rpm/build/build.c

727 lines
17 KiB
C

/* RPM - Copyright (C) 1995 Red Hat Software
*
* build.c - routines for preparing and building the sources
*/
#include "miscfn.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <limits.h>
#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;
int_32 foo;
if (! main_package) {
rpmError(RPMERR_INTERNAL, "Empty main package");
exit(RPMERR_INTERNAL);
}
script->name = tempnam(rpmGetVar(RPMVAR_TMPPATH), "rpmbuild");
script->file = fopen(script->name, "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", rpmGetArchName());
fprintf(script->file, "RPM_OS=\"%s\"\n", rpmGetOsName());
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, ".");
/* 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;
}