/* RPM - Copyright (C) 1995 Red Hat Software * * build.c - routines for preparing and building the sources */ #include "miscfn.h" #include #include #include #include #include #include #include #include #include #include #include "build.h" #include "files.h" #include "header.h" #include "spec.h" #include "specP.h" #include "rpmlib.h" #include "messages.h" #include "stringbuf.h" #include "misc.h" #include "pack.h" #include "popt.h" #include "names.h" struct Script { char *name; FILE *file; }; struct Script *openScript(Spec spec, int builddir, char *name); void writeScript(struct Script *script, char *s); int execScript(struct Script *script); void freeScript(struct Script *script, int test); int execPart(Spec s, char *sb, char *name, int builddir, int test); static int doSetupMacro(Spec spec, StringBuf sb, char *line); static int doPatchMacro(Spec spec, StringBuf sb, char *line); static char *do_untar(Spec spec, int c, int quietly); static char *do_patch(Spec spec, int c, int strip, char *dashb, int reverse, int removeEmpties); int isCompressed(char *file); static void doSweep(Spec s); static int doRmSource(Spec s); char build_subdir[1024]; struct Script *openScript(Spec spec, int builddir, char *name) { struct Script *script = malloc(sizeof(struct Script)); struct PackageRec *main_package = spec->packages; char *s, * arch, * os; int fd; int_32 foo; rpmGetArchInfo(&arch, NULL); rpmGetOsInfo(&os, NULL); if (! main_package) { rpmError(RPMERR_INTERNAL, "Empty main package"); exit(RPMERR_INTERNAL); } if (makeTempFile(NULL, &script->name, &fd)) exit(1); script->file = fdopen(fd, "w"); /* Prepare the script */ fprintf(script->file, "# Script generated by rpm\n\n"); fprintf(script->file, "RPM_SOURCE_DIR=\"%s\"\n", rpmGetVar(RPMVAR_SOURCEDIR)); fprintf(script->file, "RPM_BUILD_DIR=\"%s\"\n", rpmGetVar(RPMVAR_BUILDDIR)); fprintf(script->file, "RPM_DOC_DIR=\"%s\"\n", rpmGetVar(RPMVAR_DEFAULTDOCDIR)); fprintf(script->file, "RPM_OPT_FLAGS=\"%s\"\n", rpmGetVar(RPMVAR_OPTFLAGS)); fprintf(script->file, "RPM_ARCH=\"%s\"\n", arch); fprintf(script->file, "RPM_OS=\"%s\"\n", os); if (rpmGetVar(RPMVAR_ROOT)) { fprintf(script->file, "RPM_ROOT_DIR=\"%s\"\n", rpmGetVar(RPMVAR_ROOT)); } else { fprintf(script->file, "RPM_ROOT_DIR=\"\"\n"); } if (rpmGetVar(RPMVAR_BUILDROOT)) { fprintf(script->file, "RPM_BUILD_ROOT=\"%s\"\n", rpmGetVar(RPMVAR_BUILDROOT)); } else { fprintf(script->file, "RPM_BUILD_ROOT=\"\"\n"); } fprintf(script->file, "RPM_PACKAGE_NAME=\"%s\"\n", spec->name); headerGetEntry(main_package->header, RPMTAG_VERSION, &foo, (void **)&s, &foo); fprintf(script->file, "RPM_PACKAGE_VERSION=\"%s\"\n", s); headerGetEntry(main_package->header, RPMTAG_RELEASE, &foo, (void **)&s, &foo); fprintf(script->file, "RPM_PACKAGE_RELEASE=\"%s\"\n", s); if (rpmIsVerbose()) { fprintf(script->file, "set -x\n\n"); } else { fprintf(script->file, "exec > /dev/null\n\n"); } /* Set the umask to a known value */ fprintf(script->file, "umask 022\n"); fprintf(script->file, "\necho Executing: %s\n", name); fprintf(script->file, "cd %s\n\n", rpmGetVar(RPMVAR_BUILDDIR)); if (builddir) { /* Need to cd to the actual build directory. */ /* Note that this means we have to parse the */ /* %prep section even if we aren't using it. */ fprintf(script->file, "cd %s\n\n", build_subdir); } return script; } void writeScript(struct Script *script, char *s) { fprintf(script->file, "%s", s); } int execScript(struct Script *script) { int pid; int status; writeScript(script, "\nexit 0;\n"); fclose(script->file); script->file = NULL; chmod(script->name, 0600); if (!(pid = fork())) { execl("/bin/sh", "/bin/sh", "-e", script->name, script->name, NULL); rpmError(RPMERR_SCRIPT, "Exec failed"); _exit(RPMERR_SCRIPT); } wait(&status); if (! WIFEXITED(status) || WEXITSTATUS(status)) { rpmError(RPMERR_SCRIPT, "Bad exit status"); exit(RPMERR_SCRIPT); } return 0; } void freeScript(struct Script *script, int test) { if (script->file) fclose(script->file); if (! test) unlink(script->name); free(script->name); free(script); } int execPart(Spec s, char *sb, char *name, int builddir, int test) { struct Script *script; rpmMessage(RPMMESS_DEBUG, "RUNNING: %s\n", name); script = openScript(s, builddir, name); writeScript(script, sb); if (!test) { execScript(script); } freeScript(script, test); return 0; } static void doSweep(Spec s) { char buf[1024]; if (strcmp(build_subdir, ".")) { struct Script *script; script = openScript(s, 0, "sweep"); sprintf(buf, "rm -rf %s\n", build_subdir); writeScript(script, buf); execScript(script); freeScript(script, 0); } } static int doRmSource(Spec s) { char filename[1024]; struct sources *source; struct PackageRec *package; /* spec file */ sprintf(filename, "%s%s", rpmGetVar(RPMVAR_SPECDIR), strrchr(s->specfile, '/')); unlink(filename); /* sources and patches */ source = s->sources; while (source) { sprintf(filename, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), source->source); unlink(filename); source = source->next; } /* icons */ package = s->packages; while (package) { if (package->icon) { sprintf(filename, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), package->icon); unlink(filename); } package = package->next; } return 0; } static int doSetupMacro(Spec spec, StringBuf sb, char *line) { char *version; int leaveDirs = 0, skipDefaultAction = 0; int createDir = 0, quietly = 0; char * dirName = NULL; char buf[1024]; StringBuf before; StringBuf after; poptContext optCon; int argc; char ** argv; int arg; char * optArg; char * chptr; int rc; int num; struct poptOption optionsTable[] = { { NULL, 'a', POPT_ARG_STRING, NULL, 'a' }, { NULL, 'b', POPT_ARG_STRING, NULL, 'b' }, { NULL, 'c', 0, &createDir, 0 }, { NULL, 'D', 0, &leaveDirs, 0 }, { NULL, 'n', POPT_ARG_STRING, &dirName, 0 }, { NULL, 'T', 0, &skipDefaultAction, 0 }, { NULL, 'q', 0, &quietly, 0 }, }; if ((rc = poptParseArgvString(line, &argc, &argv))) { rpmError(RPMERR_BADSPEC, "Error parsing %%setup: %s", poptStrerror(rc)); return RPMERR_BADSPEC; } before = newStringBuf(); after = newStringBuf(); optCon = poptGetContext(NULL, argc, argv, optionsTable, 0); while ((arg = poptGetNextOpt(optCon)) > 0) { optArg = poptGetOptArg(optCon); /* We only parse -a and -b here */ num = strtoul(optArg, &chptr, 10); if ((*chptr) || (chptr == optArg) || (num == ULONG_MAX)) { rpmError(RPMERR_BADSPEC, "Bad arg to %%setup %c: %s", num, optArg); free(argv); freeStringBuf(before); freeStringBuf(after); poptFreeContext(optCon); return(RPMERR_BADSPEC); } chptr = do_untar(spec, num, quietly); if (!chptr) return 1; if (arg == 'a') appendLineStringBuf(after, chptr); else appendLineStringBuf(before, chptr); } if (arg < -1) { rpmError(RPMERR_BADSPEC, "Bad %%setup option %s: %s", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(arg)); free(argv); freeStringBuf(before); freeStringBuf(after); poptFreeContext(optCon); return(RPMERR_BADSPEC); } if (dirName) { strcpy(build_subdir, dirName); } else { strcpy(build_subdir, spec->name); strcat(build_subdir, "-"); /* We should already have a version field */ headerGetEntry(spec->packages->header, RPMTAG_VERSION, NULL, (void *) &version, NULL); strcat(build_subdir, version); } free(argv); poptFreeContext(optCon); /* cd to the build dir */ sprintf(buf, "cd %s", rpmGetVar(RPMVAR_BUILDDIR)); appendLineStringBuf(sb, buf); /* delete any old sources */ if (!leaveDirs) { sprintf(buf, "rm -rf %s", build_subdir); appendLineStringBuf(sb, buf); } /* if necessary, create and cd into the proper dir */ if (createDir) { sprintf(buf, "mkdir -p %s\ncd %s", build_subdir, build_subdir); appendLineStringBuf(sb, buf); } /* do the default action */ if (!createDir && !skipDefaultAction) { chptr = do_untar(spec, 0, quietly); if (!chptr) return 1; appendLineStringBuf(sb, chptr); } appendStringBuf(sb, getStringBuf(before)); freeStringBuf(before); if (!createDir) { sprintf(buf, "cd %s", build_subdir); appendLineStringBuf(sb, buf); } if (createDir && !skipDefaultAction) { chptr = do_untar(spec, 0, quietly); if (!chptr) return 1; appendLineStringBuf(sb, chptr); } appendStringBuf(sb, getStringBuf(after)); freeStringBuf(after); /* clean up permissions etc */ if (!geteuid()) { appendLineStringBuf(sb, "chown -R root ."); appendLineStringBuf(sb, "chgrp -R root ."); } if (rpmGetVar(RPMVAR_FIXPERMS)) { appendStringBuf(sb, "chmod -R "); appendStringBuf(sb, rpmGetVar(RPMVAR_FIXPERMS)); appendLineStringBuf(sb, " ."); } return 0; } int isCompressed(char *file) { int fd; unsigned char magic[4]; fd = open(file, O_RDONLY); read(fd, magic, 4); close(fd); if (((magic[0] == 0037) && (magic[1] == 0213)) || /* gzip */ ((magic[0] == 0037) && (magic[1] == 0236)) || /* old gzip */ ((magic[0] == 0037) && (magic[1] == 0036)) || /* pack */ ((magic[0] == 0037) && (magic[1] == 0240)) || /* SCO lzh */ ((magic[0] == 0037) && (magic[1] == 0235)) || /* compress */ ((magic[0] == 0120) && (magic[1] == 0113) && (magic[2] == 0003) && (magic[3] == 0004)) /* pkzip */ ) { return 1; } return 0; } static char *do_untar(Spec spec, int c, int quietly) { static char buf[1024]; char file[1024]; char *s, *taropts; struct sources *sp; s = NULL; sp = spec->sources; while (sp) { if ((sp->ispatch == 0) && (sp->num == c)) { s = sp->source; break; } sp = sp->next; } if (! s) { rpmError(RPMERR_BADSPEC, "No source number %d", c); return NULL; } sprintf(file, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), s); taropts = (rpmIsVerbose() && !quietly ? "-xvvf" : "-xf"); if (isCompressed(file)) { sprintf(buf, "%s -dc %s | tar %s -\n" "if [ $? -ne 0 ]; then\n" " exit $?\n" "fi", rpmGetVar(RPMVAR_GZIPBIN), file, taropts); } else { sprintf(buf, "tar %s %s", taropts, file); } return buf; } static char *do_patch(Spec spec, int c, int strip, char *db, int reverse, int removeEmpties) { static char buf[1024]; char file[1024]; char args[1024]; char *s; struct sources *sp; s = NULL; sp = spec->sources; while (sp) { if ((sp->ispatch == 1) && (sp->num == c)) { s = sp->source; break; } sp = sp->next; } if (! s) { rpmError(RPMERR_BADSPEC, "No patch number %d", c); return NULL; } sprintf(file, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), s); args[0] = '\0'; if (db) { strcat(args, "-b "); strcat(args, db); } if (reverse) { strcat(args, " -R"); } if (removeEmpties) { strcat(args, " -E"); } if (isCompressed(file)) { sprintf(buf, "echo \"Patch #%d:\"\n" "%s -dc %s | patch -p%d %s -s\n" "if [ $? -ne 0 ]; then\n" " exit $?\n" "fi", c, rpmGetVar(RPMVAR_GZIPBIN), file, strip, args); } else { sprintf(buf, "echo \"Patch #%d:\"\n" "patch -p%d %s -s < %s", c, strip, args, file); } return buf; } static int doPatchMacro(Spec spec, StringBuf sb, char *line) { char *opt_b; int opt_P, opt_p, opt_R, opt_E; char *s, *s1; char buf[1024]; int patch_nums[1024]; /* XXX - we can only handle 1024 patches! */ int patch_index, x; opt_P = opt_p = opt_R = opt_E = 0; opt_b = NULL; patch_index = 0; if (! strchr(" \t\n", line[6])) { /* %patchN */ sprintf(buf, "%%patch -P %s", line + 6); } else { strcpy(buf, line); } strtok(buf, " \t\n"); /* remove %patch */ while ((s = strtok(NULL, " \t\n"))) { if (!strcmp(s, "-P")) { opt_P = 1; } else if (!strcmp(s, "-R")) { opt_R = 1; } else if (!strcmp(s, "-E")) { opt_E = 1; } else if (!strcmp(s, "-b")) { /* orig suffix */ opt_b = strtok(NULL, " \t\n"); if (! opt_b) { rpmError(RPMERR_BADSPEC, "Need arg to %%patch -b"); return(RPMERR_BADSPEC); } } else if (!strncmp(s, "-p", 2)) { /* unfortunately, we must support -pX */ if (! strchr(" \t\n", s[2])) { s = s + 2; } else { s = strtok(NULL, " \t\n"); if (! s) { rpmError(RPMERR_BADSPEC, "Need arg to %%patch -p"); return(RPMERR_BADSPEC); } } s1 = NULL; opt_p = strtoul(s, &s1, 10); if ((*s1) || (s1 == s) || (opt_p == ULONG_MAX)) { rpmError(RPMERR_BADSPEC, "Bad arg to %%patch -p: %s", s); return(RPMERR_BADSPEC); } } else { /* Must be a patch num */ if (patch_index == 1024) { rpmError(RPMERR_BADSPEC, "Too many patches!"); return(RPMERR_BADSPEC); } s1 = NULL; patch_nums[patch_index] = strtoul(s, &s1, 10); if ((*s1) || (s1 == s) || (patch_nums[patch_index] == ULONG_MAX)) { rpmError(RPMERR_BADSPEC, "Bad arg to %%patch: %s", s); return(RPMERR_BADSPEC); } patch_index++; } } /* All args processed */ if (! opt_P) { s = do_patch(spec, 0, opt_p, opt_b, opt_R, opt_E); if (! s) { return 1; } appendLineStringBuf(sb, s); } x = 0; while (x < patch_index) { s = do_patch(spec, patch_nums[x], opt_p, opt_b, opt_R, opt_E); if (! s) { return 1; } appendLineStringBuf(sb, s); x++; } return 0; } static int checkSources(Spec s) { struct sources *source; struct PackageRec *package; char buf[1024]; /* Check that we can access all the sources */ source = s->sources; while (source) { sprintf(buf, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), source->source); if (access(buf, R_OK)) { rpmError(RPMERR_BADSPEC, "missing source or patch: %s", buf); return RPMERR_BADSPEC; } source = source->next; } /* ... and icons */ package = s->packages; while (package) { if (package->icon) { sprintf(buf, "%s/%s", rpmGetVar(RPMVAR_SOURCEDIR), package->icon); if (access(buf, R_OK)) { rpmError(RPMERR_BADSPEC, "missing icon: %s", buf); return RPMERR_BADSPEC; } } package = package->next; } return 0; } int execPrep(Spec s, int really_exec, int test) { char **lines, **lines1, *p; StringBuf out; int res; if (checkSources(s)) { return 1; } out = newStringBuf(); p = getStringBuf(s->prep); lines = splitString(p, strlen(p), '\n'); lines1 = lines; while (*lines) { if (! strncmp(*lines, "%setup", 6)) { if (doSetupMacro(s, out, *lines)) { return 1; } } else if (! strncmp(*lines, "%patch", 6)) { if (doPatchMacro(s, out, *lines)) { return 1; } } else { appendLineStringBuf(out, *lines); } lines++; } freeSplitString(lines1); res = 0; if (really_exec) { res = execPart(s, getStringBuf(out), "%prep", 0, test); } freeStringBuf(out); return res; } int execBuild(Spec s, int test) { return execPart(s, getStringBuf(s->build), "%build", 1, test); } int execInstall(Spec s, int test) { int res; if ((res = execPart(s, getStringBuf(s->install), "%install", 1, test))) { return res; } if ((res = finish_filelists(s))) { return res; } return execPart(s, getStringBuf(s->doc), "special doc", 1, test); } int execClean(Spec s) { return execPart(s, getStringBuf(s->clean), "%clean", 1, 0); } int verifyList(Spec s) { int res; if ((res = finish_filelists(s))) { return res; } return packageBinaries(s, NULL, PACK_NOPACKAGE); } int doBuild(Spec s, int flags, char *passPhrase) { int test; test = flags & RPMBUILD_TEST; strcpy(build_subdir, "."); if (s->buildArch) { rpmSetMachine(s->buildArch, NULL); } /* We always need to parse the %prep section */ if (execPrep(s, (flags & RPMBUILD_PREP), test)) { return 1; } if (flags & RPMBUILD_LIST) return verifyList(s); if (flags & RPMBUILD_BUILD) { if (execBuild(s, test)) { return 1; } } if (flags & RPMBUILD_INSTALL) { if (execInstall(s, test)) { return 1; } } if (test) { return 0; } markBuildTime(); if (flags & RPMBUILD_BINARY) { if (packageBinaries(s, passPhrase, PACK_PACKAGE)) { return 1; } if (execClean(s)) { return 1; } } if (flags & RPMBUILD_SOURCE) { if (packageSource(s, passPhrase)) { return 1; } } if (flags & RPMBUILD_SWEEP) { doSweep(s); } if (flags & RPMBUILD_RMSOURCE) { doRmSource(s); } return 0; }