int parsePrep(rpmSpec spec) { int nextPart, res, rc; StringBuf sb; char **lines; ARGV_t saveLines = NULL; if (spec->prep != NULL) { rpmlog(RPMLOG_ERR, _("line %d: second %%prep\n"), spec->lineNum); return PART_ERROR; } spec->prep = newStringBuf(); /* There are no options to %prep */ if ((rc = readLine(spec, STRIP_NOTHING)) > 0) { return PART_NONE; } else if (rc < 0) { return PART_ERROR; } sb = newStringBuf(); while (! (nextPart = isPart(spec->line))) { /* Need to expand the macros inline. That way we */ /* can give good line number information on error. */ appendStringBuf(sb, spec->line); if ((rc = readLine(spec, STRIP_NOTHING)) > 0) { nextPart = PART_NONE; break; } else if (rc < 0) { goto exit; } } saveLines = argvSplitString(getStringBuf(sb), "\n", ARGV_NONE); for (lines = saveLines; *lines; lines++) { res = 0; if (rstreqn(*lines, "%setup", sizeof("%setup")-1)) { res = doSetupMacro(spec, *lines); } else if (rstreqn(*lines, "%patch", sizeof("%patch")-1)) { res = doPatchMacro(spec, *lines); } else { appendLineStringBuf(spec->prep, *lines); } if (res && !spec->force) { /* fixup from RPMRC_FAIL do*Macro() codes for now */ nextPart = PART_ERROR; goto exit; } } res = nextPart; exit: argvFree(saveLines); sb = freeStringBuf(sb); return nextPart; }
static rpmRC addFileToTag(rpmSpec spec, const char * file, Header h, rpmTagVal tag, int append) { StringBuf sb = NULL; char buf[BUFSIZ]; char * fn; FILE * f; rpmRC rc = RPMRC_FAIL; /* assume failure */ /* no script file is not an error */ if (file == NULL) return RPMRC_OK; fn = rpmGetPath("%{_builddir}/%{?buildsubdir:%{buildsubdir}/}", file, NULL); f = fopen(fn, "r"); if (f == NULL) { rpmlog(RPMLOG_ERR,_("Could not open %s file: %s\n"), rpmTagGetName(tag), file); goto exit; } sb = newStringBuf(); if (append) { const char *s = headerGetString(h, tag); if (s) { appendLineStringBuf(sb, s); headerDel(h, tag); } } while (fgets(buf, sizeof(buf), f)) { char *expanded; if (rpmExpandMacros(spec->macros, buf, &expanded, 0) < 0) { rpmlog(RPMLOG_ERR, _("%s: line: %s\n"), fn, buf); goto exit; } appendStringBuf(sb, expanded); free(expanded); } headerPutString(h, tag, getStringBuf(sb)); rc = RPMRC_OK; exit: if (f) fclose(f); free(fn); freeStringBuf(sb); return rc; }
static int addFileToTag(rpmSpec spec, const char * file, Header h, rpmTagVal tag) { StringBuf sb = newStringBuf(); const char *s = headerGetString(h, tag); if (s) { appendLineStringBuf(sb, s); (void) headerDel(h, tag); } if ((sb = addFileToTagAux(spec, file, sb)) == NULL) return 1; headerPutString(h, tag, getStringBuf(sb)); sb = freeStringBuf(sb); return 0; }
static int addFileToTag(rpmSpec spec, const char * file, Header h, rpmTag tag) { StringBuf sb = newStringBuf(); const char *s; struct rpmtd_s td; headerGet(h, tag, &td, HEADERGET_MINMEM); if ((s = rpmtdGetString(&td))) { appendLineStringBuf(sb, s); rpmtdFreeData(&td); (void) headerDel(h, tag); } if ((sb = addFileToTagAux(spec, file, sb)) == NULL) return 1; headerPutString(h, tag, getStringBuf(sb)); sb = freeStringBuf(sb); return 0; }
static rpmSpec parseSpec(const char *specFile, rpmSpecFlags flags, const char *buildRoot, int recursing) { int parsePart = PART_PREAMBLE; int initialPackage = 1; rpmSpec spec; /* Set up a new Spec structure with no packages. */ spec = newSpec(); spec->specFile = rpmGetPath(specFile, NULL); pushOFI(spec, spec->specFile); /* If buildRoot not specified, use default %{buildroot} */ if (buildRoot) { spec->buildRoot = xstrdup(buildRoot); } else { spec->buildRoot = rpmGetPath("%{?buildroot:%{buildroot}}", NULL); } addMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC); addMacro(NULL, "_licensedir", NULL, "%{_defaultlicensedir}", RMIL_SPEC); spec->recursing = recursing; spec->flags = flags; /* All the parse*() functions expect to have a line pre-read */ /* in the spec's line buffer. Except for parsePreamble(), */ /* which handles the initial entry into a spec file. */ while (parsePart != PART_NONE) { int goterror = 0; switch (parsePart) { case PART_ERROR: /* fallthrough */ default: goterror = 1; break; case PART_PREAMBLE: parsePart = parsePreamble(spec, initialPackage); initialPackage = 0; break; case PART_PREP: parsePart = parsePrep(spec); break; case PART_BUILD: case PART_INSTALL: case PART_CHECK: case PART_CLEAN: parsePart = parseBuildInstallClean(spec, parsePart); break; case PART_CHANGELOG: parsePart = parseChangelog(spec); break; case PART_DESCRIPTION: parsePart = parseDescription(spec); break; case PART_PRE: case PART_POST: case PART_PREUN: case PART_POSTUN: case PART_PRETRANS: case PART_POSTTRANS: case PART_VERIFYSCRIPT: case PART_TRIGGERPREIN: case PART_TRIGGERIN: case PART_TRIGGERUN: case PART_TRIGGERPOSTUN: case PART_FILETRIGGERIN: case PART_FILETRIGGERUN: case PART_FILETRIGGERPOSTUN: case PART_TRANSFILETRIGGERIN: case PART_TRANSFILETRIGGERUN: case PART_TRANSFILETRIGGERPOSTUN: parsePart = parseScript(spec, parsePart); break; case PART_FILES: parsePart = parseFiles(spec); break; case PART_POLICIES: parsePart = parsePolicies(spec); break; case PART_NONE: /* XXX avoid gcc whining */ case PART_LAST: case PART_BUILDARCHITECTURES: break; } if (goterror || parsePart >= PART_LAST) { goto errxit; } if (parsePart == PART_BUILDARCHITECTURES) { int index; int x; closeSpec(spec); spec->BASpecs = xcalloc(spec->BACount, sizeof(*spec->BASpecs)); index = 0; if (spec->BANames != NULL) for (x = 0; x < spec->BACount; x++) { /* Skip if not arch is not compatible. */ if (!rpmMachineScore(RPM_MACHTABLE_BUILDARCH, spec->BANames[x])) continue; addMacro(NULL, "_target_cpu", NULL, spec->BANames[x], RMIL_RPMRC); spec->BASpecs[index] = parseSpec(specFile, flags, buildRoot, 1); if (spec->BASpecs[index] == NULL) { spec->BACount = index; goto errxit; } delMacro(NULL, "_target_cpu"); index++; } spec->BACount = index; if (! index) { rpmlog(RPMLOG_ERR, _("No compatible architectures found for build\n")); goto errxit; } /* * Return the 1st child's fully parsed Spec structure. * The restart of the parse when encountering BuildArch * causes problems for "rpm -q --specfile". This is * still a hack because there may be more than 1 arch * specified (unlikely but possible.) There's also the * further problem that the macro context, particularly * %{_target_cpu}, disagrees with the info in the header. */ if (spec->BACount >= 1) { rpmSpec nspec = spec->BASpecs[0]; spec->BASpecs = _free(spec->BASpecs); rpmSpecFree(spec); spec = nspec; } goto exit; } } if (spec->clean == NULL) { char *body = rpmExpand("%{?buildroot: %{__rm} -rf %{buildroot}}", NULL); spec->clean = newStringBuf(); appendLineStringBuf(spec->clean, body); free(body); } /* Check for description in each package */ for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next) { if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) { rpmlog(RPMLOG_ERR, _("Package has no %%description: %s\n"), headerGetString(pkg->header, RPMTAG_NAME)); goto errxit; } } /* Add arch, os and platform, self-provides etc for each package */ addTargets(spec->packages); /* Check for encoding in each package unless disabled */ if (!(spec->flags & RPMSPEC_NOUTF8)) { int badenc = 0; for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next) { if (checkForEncoding(pkg->header, 0) != RPMRC_OK) { badenc = 1; } } if (badenc) goto errxit; } closeSpec(spec); exit: /* Assemble source header from parsed components */ initSourceHeader(spec); return spec; errxit: rpmSpecFree(spec); return NULL; }
int parseSpec(rpmts ts, const char *specFile, const char *rootDir, const char *buildRoot, int recursing, const char *passPhrase, const char *cookie, int anyarch, int force) { rpmParseState parsePart = PART_PREAMBLE; int initialPackage = 1; Package pkg; rpmSpec spec; /* Set up a new Spec structure with no packages. */ spec = newSpec(); spec->specFile = rpmGetPath(specFile, NULL); spec->fileStack = newOpenFileInfo(); spec->fileStack->fileName = xstrdup(spec->specFile); /* If buildRoot not specified, use default %{buildroot} */ if (buildRoot) { spec->buildRoot = xstrdup(buildRoot); } else { spec->buildRoot = rpmGetPath("%{?buildroot:%{buildroot}}", NULL); } addMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC); spec->recursing = recursing; spec->anyarch = anyarch; spec->force = force; if (rootDir) spec->rootDir = xstrdup(rootDir); if (passPhrase) spec->passPhrase = xstrdup(passPhrase); if (cookie) spec->cookie = xstrdup(cookie); spec->timeCheck = rpmExpandNumeric("%{_timecheck}"); /* All the parse*() functions expect to have a line pre-read */ /* in the spec's line buffer. Except for parsePreamble(), */ /* which handles the initial entry into a spec file. */ while (parsePart != PART_NONE) { int goterror = 0; switch (parsePart) { /* XXX Trap unexpected RPMRC_FAIL returns for now */ case RPMRC_FAIL: rpmlog(RPMLOG_ERR, "FIXME: got RPMRC_FAIL from spec parse\n"); abort(); case PART_ERROR: /* fallthrough */ default: goterror = 1; break; case PART_PREAMBLE: parsePart = parsePreamble(spec, initialPackage); initialPackage = 0; break; case PART_PREP: parsePart = parsePrep(spec); break; case PART_BUILD: case PART_INSTALL: case PART_CHECK: case PART_CLEAN: parsePart = parseBuildInstallClean(spec, parsePart); break; case PART_CHANGELOG: parsePart = parseChangelog(spec); break; case PART_DESCRIPTION: parsePart = parseDescription(spec); break; case PART_PRE: case PART_POST: case PART_PREUN: case PART_POSTUN: case PART_PRETRANS: case PART_POSTTRANS: case PART_VERIFYSCRIPT: case PART_TRIGGERPREIN: case PART_TRIGGERIN: case PART_TRIGGERUN: case PART_TRIGGERPOSTUN: parsePart = parseScript(spec, parsePart); break; case PART_FILES: parsePart = parseFiles(spec); break; case PART_NONE: /* XXX avoid gcc whining */ case PART_LAST: case PART_BUILDARCHITECTURES: break; } if (goterror || parsePart >= PART_LAST) { goto errxit; } if (parsePart == PART_BUILDARCHITECTURES) { int index; int x; closeSpec(spec); spec->BASpecs = xcalloc(spec->BACount, sizeof(*spec->BASpecs)); index = 0; if (spec->BANames != NULL) for (x = 0; x < spec->BACount; x++) { /* Skip if not arch is not compatible. */ if (!rpmMachineScore(RPM_MACHTABLE_BUILDARCH, spec->BANames[x])) continue; addMacro(NULL, "_target_cpu", NULL, spec->BANames[x], RMIL_RPMRC); spec->BASpecs[index] = NULL; if (parseSpec(ts, specFile, spec->rootDir, buildRoot, 1, passPhrase, cookie, anyarch, force) || (spec->BASpecs[index] = rpmtsSetSpec(ts, NULL)) == NULL) { spec->BACount = index; goto errxit; } delMacro(NULL, "_target_cpu"); index++; } spec->BACount = index; if (! index) { rpmlog(RPMLOG_ERR, _("No compatible architectures found for build\n")); goto errxit; } /* * Return the 1st child's fully parsed Spec structure. * The restart of the parse when encountering BuildArch * causes problems for "rpm -q --specfile". This is * still a hack because there may be more than 1 arch * specified (unlikely but possible.) There's also the * further problem that the macro context, particularly * %{_target_cpu}, disagrees with the info in the header. */ if (spec->BACount >= 1) { rpmSpec nspec = spec->BASpecs[0]; spec->BASpecs = _free(spec->BASpecs); spec = freeSpec(spec); spec = nspec; } (void) rpmtsSetSpec(ts, spec); return 0; } } if (spec->clean == NULL) { char *body = rpmExpand("%{?buildroot: %{__rm} -rf %{buildroot}}", NULL); spec->clean = newStringBuf(); appendLineStringBuf(spec->clean, body); free(body); } /* Check for description in each package and add arch and os */ { char *platform = rpmExpand("%{_target_platform}", NULL); char *arch = rpmExpand("%{_target_cpu}", NULL); char *os = rpmExpand("%{_target_os}", NULL); for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) { if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) { rpmlog(RPMLOG_ERR, _("Package has no %%description: %s\n"), headerGetString(pkg->header, RPMTAG_NAME)); goto errxit; } headerPutString(pkg->header, RPMTAG_OS, os); /* noarch subpackages already have arch set here, leave it alone */ if (!headerIsEntry(pkg->header, RPMTAG_ARCH)) { headerPutString(pkg->header, RPMTAG_ARCH, arch); } headerPutString(pkg->header, RPMTAG_PLATFORM, platform); pkg->ds = rpmdsThis(pkg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL); } platform = _free(platform); arch = _free(arch); os = _free(os); } closeSpec(spec); (void) rpmtsSetSpec(ts, spec); return 0; errxit: spec = freeSpec(spec); return PART_ERROR; }
/** * Parse %patch line. * This supports too many crazy syntaxes: * - %patchN is equal to %patch -P<N> * - -P<N> -P<N+1>... can be used to apply several patch on a single line * - Any trailing arguments are treated as patch numbers * - Any combination of the above, except unless at least one -P is specified, * %patch is treated as %patch -P0 so that "%patch 1" is actually * equal to "%patch -P0 -P1". * * @param spec build info * @param line current line from spec file * @return RPMRC_OK on success */ static rpmRC doPatchMacro(rpmSpec spec, const char *line) { char *opt_b, *opt_P, *opt_d; char *buf = NULL; int opt_p, opt_R, opt_E, opt_F; int argc, c; const char **argv = NULL; ARGV_t patch, patchnums = NULL; rpmRC rc = RPMRC_FAIL; /* assume failure */ struct poptOption const patchOpts[] = { { NULL, 'P', POPT_ARG_STRING, &opt_P, 'P', NULL, NULL }, { NULL, 'p', POPT_ARG_INT, &opt_p, 'p', NULL, NULL }, { NULL, 'R', POPT_ARG_NONE, &opt_R, 'R', NULL, NULL }, { NULL, 'E', POPT_ARG_NONE, &opt_E, 'E', NULL, NULL }, { NULL, 'b', POPT_ARG_STRING, &opt_b, 'b', NULL, NULL }, { NULL, 'z', POPT_ARG_STRING, &opt_b, 'z', NULL, NULL }, { NULL, 'F', POPT_ARG_INT, &opt_F, 'F', NULL, NULL }, { NULL, 'd', POPT_ARG_STRING, &opt_d, 'd', NULL, NULL }, { NULL, 0, 0, NULL, 0, NULL, NULL } }; poptContext optCon = NULL; opt_p = opt_R = opt_E = 0; opt_F = rpmExpandNumeric("%{_default_patch_fuzz}"); /* get default fuzz factor for %patch */ opt_b = opt_d = NULL; /* Convert %patchN to %patch -PN to simplify further processing */ if (! strchr(" \t\n", line[6])) { rasprintf(&buf, "%%patch -P %s", line + 6); } else { if (strstr(line+6, " -P") == NULL) rasprintf(&buf, "%%patch -P %d %s", INT_MAX, line + 6); /* INT_MAX denotes not numbered %patch */ else buf = xstrdup(line); /* it is not numberless patch because -P is present */ } poptParseArgvString(buf, &argc, &argv); free(buf); /* * Grab all -P<N> numbers for later processing. Stored as strings * at this point so we only have to worry about conversion in one place. */ optCon = poptGetContext(NULL, argc, argv, patchOpts, 0); while ((c = poptGetNextOpt(optCon)) > 0) { switch (c) { case 'P': { char *arg = poptGetOptArg(optCon); if (arg) { argvAdd(&patchnums, arg); free(arg); } break; } default: break; } } if (c < -1) { rpmlog(RPMLOG_ERR, _("%s: %s: %s\n"), poptStrerror(c), poptBadOption(optCon, POPT_BADOPTION_NOALIAS), line); goto exit; } /* Any trailing arguments are treated as patch numbers */ argvAppend(&patchnums, (ARGV_const_t) poptGetArgs(optCon)); /* Convert to number, generate patch command and append to %prep script */ for (patch = patchnums; *patch; patch++) { uint32_t pnum; char *s; if (parseUnsignedNum(*patch, &pnum)) { rpmlog(RPMLOG_ERR, _("Invalid patch number %s: %s\n"), *patch, line); goto exit; } s = doPatch(spec, pnum, opt_p, opt_b, opt_R, opt_E, opt_F, opt_d); if (s == NULL) { goto exit; } appendLineStringBuf(spec->prep, s); free(s); } rc = RPMRC_OK; exit: argvFree(patchnums); free(argv); poptFreeContext(optCon); return rc; }
/** * Parse %setup macro. * @todo FIXME: Option -q broken when not immediately after %setup. * @param spec build info * @param line current line from spec file * @return RPMRC_OK on success */ static int doSetupMacro(rpmSpec spec, const char *line) { char *buf = NULL; StringBuf before = newStringBuf(); StringBuf after = newStringBuf(); poptContext optCon = NULL; int argc; const char ** argv = NULL; int arg; const char * optArg; int xx; rpmRC rc = RPMRC_FAIL; uint32_t num; int leaveDirs = 0, skipDefaultAction = 0; int createDir = 0, quietly = 0; const char * dirName = NULL; struct poptOption optionsTable[] = { { NULL, 'a', POPT_ARG_STRING, NULL, 'a', NULL, NULL}, { NULL, 'b', POPT_ARG_STRING, NULL, 'b', NULL, NULL}, { NULL, 'c', 0, &createDir, 0, NULL, NULL}, { NULL, 'D', 0, &leaveDirs, 0, NULL, NULL}, { NULL, 'n', POPT_ARG_STRING, &dirName, 0, NULL, NULL}, { NULL, 'T', 0, &skipDefaultAction, 0, NULL, NULL}, { NULL, 'q', 0, &quietly, 0, NULL, NULL}, { 0, 0, 0, 0, 0, NULL, NULL} }; if ((xx = poptParseArgvString(line, &argc, &argv))) { rpmlog(RPMLOG_ERR, _("Error parsing %%setup: %s\n"), poptStrerror(xx)); goto exit; } optCon = poptGetContext(NULL, argc, argv, optionsTable, 0); while ((arg = poptGetNextOpt(optCon)) > 0) { optArg = poptGetOptArg(optCon); /* We only parse -a and -b here */ if (parseUnsignedNum(optArg, &num)) { rpmlog(RPMLOG_ERR, _("line %d: Bad arg to %%setup: %s\n"), spec->lineNum, (optArg ? optArg : "???")); goto exit; } { char *chptr = doUntar(spec, num, quietly); if (chptr == NULL) goto exit; appendLineStringBuf((arg == 'a' ? after : before), chptr); free(chptr); } } if (arg < -1) { rpmlog(RPMLOG_ERR, _("line %d: Bad %%setup option %s: %s\n"), spec->lineNum, poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(arg)); goto exit; } if (dirName) { spec->buildSubdir = xstrdup(dirName); } else { rasprintf(&spec->buildSubdir, "%s-%s", headerGetString(spec->packages->header, RPMTAG_NAME), headerGetString(spec->packages->header, RPMTAG_VERSION)); } addMacro(spec->macros, "buildsubdir", NULL, spec->buildSubdir, RMIL_SPEC); /* cd to the build dir */ { char * buildDir = rpmGenPath(spec->rootDir, "%{_builddir}", ""); rasprintf(&buf, "cd '%s'", buildDir); appendLineStringBuf(spec->prep, buf); free(buf); free(buildDir); } /* delete any old sources */ if (!leaveDirs) { rasprintf(&buf, "rm -rf '%s'", spec->buildSubdir); appendLineStringBuf(spec->prep, buf); free(buf); } /* if necessary, create and cd into the proper dir */ if (createDir) { rasprintf(&buf, RPM_MKDIR_P " %s\ncd '%s'", spec->buildSubdir, spec->buildSubdir); appendLineStringBuf(spec->prep, buf); free(buf); } /* do the default action */ if (!createDir && !skipDefaultAction) { char *chptr = doUntar(spec, 0, quietly); if (!chptr) goto exit; appendLineStringBuf(spec->prep, chptr); free(chptr); } appendStringBuf(spec->prep, getStringBuf(before)); if (!createDir) { rasprintf(&buf, "cd '%s'", spec->buildSubdir); appendLineStringBuf(spec->prep, buf); free(buf); } if (createDir && !skipDefaultAction) { char *chptr = doUntar(spec, 0, quietly); if (chptr == NULL) goto exit; appendLineStringBuf(spec->prep, chptr); free(chptr); } appendStringBuf(spec->prep, getStringBuf(after)); /* Fix the permissions of the setup build tree */ { char *fix = rpmExpand("%{_fixperms} .", NULL); if (fix && *fix != '%') { appendLineStringBuf(spec->prep, fix); } free(fix); } rc = RPMRC_OK; exit: freeStringBuf(before); freeStringBuf(after); poptFreeContext(optCon); free(argv); return rc; }