531 lines
14 KiB
C
531 lines
14 KiB
C
/** \ingroup rpmbuild
|
|
* \file build/parsePrep.c
|
|
* Parse %prep section from spec file.
|
|
*/
|
|
|
|
#include "system.h"
|
|
|
|
#include <errno.h>
|
|
|
|
#include <rpm/header.h>
|
|
#include <rpm/rpmlog.h>
|
|
#include <rpm/rpmfileutil.h>
|
|
#include "build/rpmbuild_internal.h"
|
|
#include "build/rpmbuild_misc.h"
|
|
#include "lib/rpmug.h"
|
|
#include "debug.h"
|
|
|
|
/**
|
|
* Check that file owner and group are known.
|
|
* @param urlfn file url
|
|
* @return RPMRC_OK on success
|
|
*/
|
|
static rpmRC checkOwners(const char * urlfn)
|
|
{
|
|
struct stat sb;
|
|
|
|
if (lstat(urlfn, &sb)) {
|
|
rpmlog(RPMLOG_ERR, _("Bad source: %s: %s\n"),
|
|
urlfn, strerror(errno));
|
|
return RPMRC_FAIL;
|
|
}
|
|
if (!rpmugUname(sb.st_uid) || !rpmugGname(sb.st_gid)) {
|
|
rpmlog(RPMLOG_ERR, _("Bad owner/group: %s\n"), urlfn);
|
|
return RPMRC_FAIL;
|
|
}
|
|
|
|
return RPMRC_OK;
|
|
}
|
|
|
|
/**
|
|
* Expand %patchN macro into %prep scriptlet.
|
|
* @param spec build info
|
|
* @param c patch index
|
|
* @param strip patch level (i.e. patch -p argument)
|
|
* @param db saved file suffix (i.e. patch --suffix argument)
|
|
* @param reverse include -R?
|
|
* @param removeEmpties include -E?
|
|
* @param fuzz fuzz factor, fuzz<0 means no fuzz set
|
|
* @param dir dir to change to (i.e. patch -d argument)
|
|
* @return expanded %patch macro (NULL on error)
|
|
*/
|
|
|
|
static char *doPatch(rpmSpec spec, uint32_t c, int strip, const char *db,
|
|
int reverse, int removeEmpties, int fuzz, const char *dir)
|
|
{
|
|
char *fn = NULL;
|
|
char *buf = NULL;
|
|
char *arg_backup = NULL;
|
|
char *arg_fuzz = NULL;
|
|
char *arg_dir = NULL;
|
|
char *args = NULL;
|
|
char *arg_patch_flags = rpmExpand("%{?_default_patch_flags}", NULL);
|
|
struct Source *sp;
|
|
char *patchcmd;
|
|
|
|
for (sp = spec->sources; sp != NULL; sp = sp->next) {
|
|
if ((sp->flags & RPMBUILD_ISPATCH) && (sp->num == c)) {
|
|
break;
|
|
}
|
|
}
|
|
if (sp == NULL) {
|
|
if (c != INT_MAX) {
|
|
rpmlog(RPMLOG_ERR, _("No patch number %u\n"), c);
|
|
} else {
|
|
rpmlog(RPMLOG_ERR, _("%%patch without corresponding \"Patch:\" tag\n"));
|
|
}
|
|
goto exit;
|
|
}
|
|
|
|
fn = rpmGetPath("%{_sourcedir}/", sp->source, NULL);
|
|
|
|
/* On non-build parse's, file cannot be stat'd or read. */
|
|
if ((spec->flags & RPMSPEC_FORCE) || checkOwners(fn)) goto exit;
|
|
|
|
if (db) {
|
|
rasprintf(&arg_backup,
|
|
#if HAVE_OLDPATCH_21 == 0
|
|
"-b "
|
|
#endif
|
|
"--suffix %s", db);
|
|
} else arg_backup = xstrdup("");
|
|
|
|
if (dir) {
|
|
rasprintf(&arg_dir, " -d %s", dir);
|
|
} else arg_dir = xstrdup("");
|
|
|
|
if (fuzz >= 0) {
|
|
rasprintf(&arg_fuzz, " --fuzz=%d", fuzz);
|
|
} else arg_fuzz = xstrdup("");
|
|
|
|
rasprintf(&args, "%s -p%d %s%s%s%s%s", arg_patch_flags, strip, arg_backup, arg_fuzz, arg_dir,
|
|
reverse ? " -R" : "",
|
|
removeEmpties ? " -E" : "");
|
|
|
|
patchcmd = rpmExpand("%{uncompress: ", fn, "} | %{__patch} ", args, NULL);
|
|
|
|
free(arg_fuzz);
|
|
free(arg_dir);
|
|
free(arg_backup);
|
|
free(args);
|
|
|
|
if (c != INT_MAX) {
|
|
rasprintf(&buf, "echo \"Patch #%u (%s):\"\n"
|
|
"%s\n",
|
|
c, basename(fn), patchcmd);
|
|
} else {
|
|
rasprintf(&buf, "echo \"Patch (%s):\"\n"
|
|
"%s\n",
|
|
basename(fn), patchcmd);
|
|
}
|
|
free(patchcmd);
|
|
|
|
exit:
|
|
free(arg_patch_flags);
|
|
free(fn);
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* Expand %setup macro into %prep scriptlet.
|
|
* @param spec build info
|
|
* @param c source index
|
|
* @param quietly should -vv be omitted from tar?
|
|
* @return expanded %setup macro (NULL on error)
|
|
*/
|
|
static char *doUntar(rpmSpec spec, uint32_t c, int quietly)
|
|
{
|
|
char *fn = NULL;
|
|
char *buf = NULL;
|
|
char *tar = NULL;
|
|
const char *taropts = ((rpmIsVerbose() && !quietly) ? "-xvvf" : "-xf");
|
|
struct Source *sp;
|
|
rpmCompressedMagic compressed = COMPRESSED_NOT;
|
|
|
|
for (sp = spec->sources; sp != NULL; sp = sp->next) {
|
|
if ((sp->flags & RPMBUILD_ISSOURCE) && (sp->num == c)) {
|
|
break;
|
|
}
|
|
}
|
|
if (sp == NULL) {
|
|
if (c) {
|
|
rpmlog(RPMLOG_ERR, _("No source number %u\n"), c);
|
|
} else {
|
|
rpmlog(RPMLOG_ERR, _("No \"Source:\" tag in the spec file\n"));
|
|
}
|
|
goto exit;
|
|
}
|
|
|
|
fn = rpmGetPath("%{_sourcedir}/", sp->source, NULL);
|
|
|
|
/* XXX On non-build parse's, file cannot be stat'd or read */
|
|
if (!(spec->flags & RPMSPEC_FORCE) && (rpmFileIsCompressed(fn, &compressed) || checkOwners(fn))) {
|
|
goto exit;
|
|
}
|
|
|
|
tar = rpmGetPath("%{__tar}", NULL);
|
|
if (compressed != COMPRESSED_NOT) {
|
|
char *zipper, *t = NULL;
|
|
int needtar = 1;
|
|
|
|
switch (compressed) {
|
|
case COMPRESSED_NOT: /* XXX can't happen */
|
|
case COMPRESSED_OTHER:
|
|
t = "%{__gzip} -dc";
|
|
break;
|
|
case COMPRESSED_BZIP2:
|
|
t = "%{__bzip2} -dc";
|
|
break;
|
|
case COMPRESSED_ZIP:
|
|
if (rpmIsVerbose() && !quietly)
|
|
t = "%{__unzip}";
|
|
else
|
|
t = "%{__unzip} -qq";
|
|
needtar = 0;
|
|
break;
|
|
case COMPRESSED_LZMA:
|
|
case COMPRESSED_XZ:
|
|
t = "%{__xz} -dc";
|
|
break;
|
|
case COMPRESSED_LZIP:
|
|
t = "%{__lzip} -dc";
|
|
break;
|
|
case COMPRESSED_LRZIP:
|
|
t = "%{__lrzip} -dqo-";
|
|
break;
|
|
case COMPRESSED_7ZIP:
|
|
t = "%{__7zip} x";
|
|
needtar = 0;
|
|
break;
|
|
}
|
|
zipper = rpmGetPath(t, NULL);
|
|
if (needtar) {
|
|
rasprintf(&buf, "%s '%s' | %s %s - \n"
|
|
"STATUS=$?\n"
|
|
"if [ $STATUS -ne 0 ]; then\n"
|
|
" exit $STATUS\n"
|
|
"fi", zipper, fn, tar, taropts);
|
|
} else {
|
|
rasprintf(&buf, "%s '%s'\n"
|
|
"STATUS=$?\n"
|
|
"if [ $STATUS -ne 0 ]; then\n"
|
|
" exit $STATUS\n"
|
|
"fi", zipper, fn);
|
|
}
|
|
free(zipper);
|
|
} else {
|
|
rasprintf(&buf, "%s %s %s", tar, taropts, fn);
|
|
}
|
|
|
|
exit:
|
|
free(fn);
|
|
free(tar);
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
buf = rpmExpand("%{__mkdir_p} ", spec->buildSubdir, "\n",
|
|
"cd '", spec->buildSubdir, "'", NULL);
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
int parsePrep(rpmSpec spec)
|
|
{
|
|
int nextPart, rc, res = PART_ERROR;
|
|
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;
|
|
}
|
|
|
|
while (! (nextPart = isPart(spec->line))) {
|
|
/* Need to expand the macros inline. That way we */
|
|
/* can give good line number information on error. */
|
|
argvAdd(&saveLines, spec->line);
|
|
if ((rc = readLine(spec, STRIP_NOTHING)) > 0) {
|
|
nextPart = PART_NONE;
|
|
break;
|
|
} else if (rc < 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
for (ARGV_const_t lines = saveLines; lines && *lines; lines++) {
|
|
rc = RPMRC_OK;
|
|
if (rstreqn(*lines, "%setup", sizeof("%setup")-1)) {
|
|
rc = doSetupMacro(spec, *lines);
|
|
} else if (rstreqn(*lines, "%patch", sizeof("%patch")-1)) {
|
|
rc = doPatchMacro(spec, *lines);
|
|
} else {
|
|
appendStringBuf(spec->prep, *lines);
|
|
}
|
|
if (rc != RPMRC_OK && !(spec->flags & RPMSPEC_FORCE)) {
|
|
goto exit;
|
|
}
|
|
}
|
|
res = nextPart;
|
|
|
|
exit:
|
|
argvFree(saveLines);
|
|
|
|
return res;
|
|
}
|