Add a convenience library for stuff common cli-bits into
[platform/upstream/rpm.git] / rpmbuild.c
1 #include "system.h"
2 const char *__progname;
3
4 #define _AUTOHELP
5
6 #include <errno.h>
7 #include <libgen.h>
8 #include <ctype.h>
9 #include <sys/wait.h>
10 #if HAVE_MCHECK_H
11 #include <mcheck.h>
12 #endif
13
14 #include <rpm/rpmcli.h>
15 #include <rpm/rpmlib.h>                 /* RPMSIGTAG, rpmReadPackageFile .. */
16 #include <rpm/rpmbuild.h>
17 #include <rpm/rpmlog.h>
18 #include <rpm/rpmfileutil.h>
19 #include <rpm/rpmdb.h>
20 #include <rpm/rpmps.h>
21 #include <rpm/rpmts.h>
22 #include "lib/signature.h"
23 #include "cliutils.h"
24
25 #include "debug.h"
26
27 enum modes {
28     MODE_BUILD          = (1 <<  4),
29     MODE_REBUILD        = (1 <<  5),
30     MODE_RECOMPILE      = (1 <<  8),
31     MODE_TARBUILD       = (1 << 11),
32 };
33
34 static int quiet;
35
36 /* the structure describing the options we take and the defaults */
37 static struct poptOption optionsTable[] = {
38
39  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmBuildPoptTable, 0,
40         N_("Build options with [ <specfile> | <tarball> | <source package> ]:"),
41         NULL },
42
43  { "quiet", '\0', 0, &quiet, 0,                 NULL, NULL},
44
45  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
46         N_("Common options for all rpm modes and executables:"),
47         NULL },
48
49    POPT_AUTOALIAS
50    POPT_AUTOHELP
51    POPT_TABLEEND
52 };
53
54 static int checkSpec(rpmts ts, Header h)
55 {
56     rpmps ps;
57     int rc;
58
59     if (!headerIsEntry(h, RPMTAG_REQUIRENAME)
60      && !headerIsEntry(h, RPMTAG_CONFLICTNAME))
61         return 0;
62
63     rc = rpmtsAddInstallElement(ts, h, NULL, 0, NULL);
64
65     rc = rpmtsCheck(ts);
66
67     ps = rpmtsProblems(ts);
68     if (rc == 0 && rpmpsNumProblems(ps) > 0) {
69         rpmlog(RPMLOG_ERR, _("Failed build dependencies:\n"));
70         rpmpsPrint(NULL, ps);
71         rc = 1;
72     }
73     ps = rpmpsFree(ps);
74
75     /* XXX nuke the added package. */
76     rpmtsClean(ts);
77
78     return rc;
79 }
80
81 static int isSpecFile(const char * specfile)
82 {
83     char buf[256];
84     const char * s;
85     FILE * f;
86     int count;
87     int checking;
88
89     f = fopen(specfile, "r");
90     if (f == NULL || ferror(f)) {
91         rpmlog(RPMLOG_ERR, _("Unable to open spec file %s: %s\n"),
92                 specfile, strerror(errno));
93         return 0;
94     }
95     count = fread(buf, sizeof(buf[0]), sizeof(buf), f);
96     (void) fclose(f);
97
98     if (count == 0)
99         return 0;
100
101     checking = 1;
102     for (s = buf; count--; s++) {
103         switch (*s) {
104         case '\r':
105         case '\n':
106             checking = 1;
107             break;
108         case ':':
109             checking = 0;
110             break;
111         default:
112 #if 0
113             if (checking && !(isprint(*s) || isspace(*s))) return 0;
114             break;
115 #else
116             if (checking && !(isprint(*s) || isspace(*s)) && *(unsigned char *)s < 32) return 0;
117             break;
118 #endif
119         }
120     }
121     return 1;
122 }
123
124 /* 
125  * Try to find a spec from a tarball pointed to by arg. 
126  * Return absolute path to spec name on success, otherwise NULL.
127  */
128 static char * getTarSpec(const char *arg)
129 {
130     char *specFile = NULL;
131     char *specDir;
132     char *specBase;
133     char *tmpSpecFile;
134     const char **try;
135     char tarbuf[BUFSIZ];
136     int gotspec = 0, res;
137     static const char *tryspec[] = { "Specfile", "\\*.spec", NULL };
138
139     specDir = rpmGetPath("%{_specdir}", NULL);
140     tmpSpecFile = rpmGetPath("%{_specdir}/", "rpm-spec.XXXXXX", NULL);
141
142     (void) close(mkstemp(tmpSpecFile));
143
144     for (try = tryspec; *try != NULL; try++) {
145         FILE *fp;
146         char *cmd;
147
148         cmd = rpmExpand("%{uncompress: ", arg, "} | ",
149                         "%{__tar} xOvf - --wildcards ", *try,
150                         " 2>&1 > ", tmpSpecFile, NULL);
151
152         if (!(fp = popen(cmd, "r"))) {
153             rpmlog(RPMLOG_ERR, _("Failed to open tar pipe: %m\n"));
154         } else {
155             char *fok;
156             for (;;) {
157                 fok = fgets(tarbuf, sizeof(tarbuf) - 1, fp);
158                 /* tar sometimes prints "tar: Record size = 16" messages */
159                 if (!fok || strncmp(fok, "tar: ", 5) != 0)
160                     break;
161             }
162             pclose(fp);
163             gotspec = (fok != NULL) && isSpecFile(tmpSpecFile);
164         }
165
166         if (!gotspec) 
167             unlink(tmpSpecFile);
168         free(cmd);
169     }
170
171     if (!gotspec) {
172         rpmlog(RPMLOG_ERR, _("Failed to read spec file from %s\n"), arg);
173         goto exit;
174     }
175
176     specBase = basename(tarbuf);
177     /* remove trailing \n */
178     specBase[strlen(specBase)-1] = '\0';
179
180     rasprintf(&specFile, "%s/%s", specDir, specBase);
181     res = rename(tmpSpecFile, specFile);
182
183     if (res) {
184         rpmlog(RPMLOG_ERR, _("Failed to rename %s to %s: %m\n"),
185                 tmpSpecFile, specFile);
186         free(specFile);
187         specFile = NULL;
188     } else {
189         /* mkstemp() can give unnecessarily strict permissions, fixup */
190         mode_t mask;
191         umask(mask = umask(0));
192         (void) chmod(specFile, 0666 & ~mask);
193     }
194
195 exit:
196     (void) unlink(tmpSpecFile);
197     free(tmpSpecFile);
198     free(specDir);
199     return specFile;
200 }
201
202 static int buildForTarget(rpmts ts, const char * arg, BTA_t ba)
203 {
204     const char * passPhrase = ba->passPhrase;
205     const char * cookie = ba->cookie;
206     int buildAmount = ba->buildAmount;
207     char * buildRootURL = NULL;
208     char * specFile = NULL;
209     rpmSpec spec = NULL;
210     int rc = 1; /* assume failure */
211
212 #ifndef DYING
213     rpmSetTables(RPM_MACHTABLE_BUILDARCH, RPM_MACHTABLE_BUILDOS);
214 #endif
215
216     if (ba->buildRootOverride)
217         buildRootURL = rpmGenPath(NULL, ba->buildRootOverride, NULL);
218
219     /* Create build tree if necessary */
220     const char * buildtree = "%{_topdir}:%{_specdir}:%{_sourcedir}:%{_builddir}:%{_rpmdir}:%{_srcrpmdir}:%{_buildrootdir}";
221     const char * rootdir = rpmtsRootDir(ts);
222     if (rpmMkdirs(!rstreq(rootdir, "/") ? rootdir : NULL , buildtree)) {
223         goto exit;
224     }
225
226     if (ba->buildMode == 't') {
227         char *srcdir = NULL, *dir;
228
229         specFile = getTarSpec(arg);
230         if (!specFile)
231             goto exit;
232
233         /* Make the directory of the tarball %_sourcedir for this run */
234         /* dirname() may modify contents so extra hoops needed. */
235         if (*arg != '/') {
236             dir = rpmGetCwd();
237             rstrscat(&dir, "/", arg, NULL);
238         } else {
239             dir = xstrdup(arg);
240         }
241         srcdir = dirname(dir);
242         addMacro(NULL, "_sourcedir", NULL, srcdir, RMIL_TARBALL);
243         free(dir);
244     } else {
245         specFile = xstrdup(arg);
246     }
247
248     if (*specFile != '/') {
249         char *cwd = rpmGetCwd();
250         char *s = NULL;
251         rasprintf(&s, "%s/%s", cwd, arg);
252         free(cwd);
253         free(specFile);
254         specFile = s;
255     }
256
257     struct stat st;
258     if (stat(specFile, &st) < 0) {
259         rpmlog(RPMLOG_ERR, _("failed to stat %s: %m\n"), specFile);
260         goto exit;
261     }
262     if (! S_ISREG(st.st_mode)) {
263         rpmlog(RPMLOG_ERR, _("File %s is not a regular file.\n"), specFile);
264         goto exit;
265     }
266
267     /* Try to verify that the file is actually a specfile */
268     if (!isSpecFile(specFile)) {
269         rpmlog(RPMLOG_ERR,
270                 _("File %s does not appear to be a specfile.\n"), specFile);
271         goto exit;
272     }
273     
274     /* Don't parse spec if only its removal is requested */
275     if (ba->buildAmount == RPMBUILD_RMSPEC) {
276         rc = unlink(specFile);
277         goto exit;
278     }
279
280     /* Parse the spec file */
281 #define _anyarch(_f)    \
282 (((_f)&(RPMBUILD_PREP|RPMBUILD_BUILD|RPMBUILD_INSTALL|RPMBUILD_PACKAGEBINARY)) == 0)
283     if (parseSpec(ts, specFile, ba->rootdir, buildRootURL, 0, passPhrase,
284                 cookie, _anyarch(buildAmount), ba->force))
285     {
286         goto exit;
287     }
288 #undef  _anyarch
289     if ((spec = rpmtsSetSpec(ts, NULL)) == NULL) {
290         goto exit;
291     }
292
293     if ( ba->buildAmount&RPMBUILD_RMSOURCE && !(ba->buildAmount&~(RPMBUILD_RMSOURCE|RPMBUILD_RMSPEC)) ) {
294         rc = doRmSource(spec);
295         if ( rc == RPMRC_OK && ba->buildAmount&RPMBUILD_RMSPEC )
296             rc = unlink(specFile);
297         goto exit;
298     }
299
300     /* Assemble source header from parsed components */
301     initSourceHeader(spec);
302
303     /* Check build prerequisites */
304     if (!ba->noDeps && checkSpec(ts, spec->sourceHeader)) {
305         goto exit;
306     }
307
308     if (buildSpec(ts, spec, buildAmount, ba->noBuild)) {
309         goto exit;
310     }
311     
312     if (ba->buildMode == 't')
313         (void) unlink(specFile);
314     rc = 0;
315
316 exit:
317     free(specFile);
318     freeSpec(spec);
319     free(buildRootURL);
320     return rc;
321 }
322
323 static int build(rpmts ts, const char * arg, BTA_t ba, const char * rcfile)
324 {
325     char *t, *te;
326     int rc = 0;
327     char * targets = ba->targets;
328 #define buildCleanMask  (RPMBUILD_RMSOURCE|RPMBUILD_RMSPEC)
329     int cleanFlags = ba->buildAmount & buildCleanMask;
330     rpmVSFlags vsflags, ovsflags;
331
332     vsflags = rpmExpandNumeric("%{_vsflags_build}");
333     if (ba->qva_flags & VERIFY_DIGEST)
334         vsflags |= _RPMVSF_NODIGESTS;
335     if (ba->qva_flags & VERIFY_SIGNATURE)
336         vsflags |= _RPMVSF_NOSIGNATURES;
337     if (ba->qva_flags & VERIFY_HDRCHK)
338         vsflags |= RPMVSF_NOHDRCHK;
339     ovsflags = rpmtsSetVSFlags(ts, vsflags);
340
341     if (targets == NULL) {
342         rc =  buildForTarget(ts, arg, ba);
343         goto exit;
344     }
345
346     /* parse up the build operators */
347
348     printf(_("Building target platforms: %s\n"), targets);
349
350     ba->buildAmount &= ~buildCleanMask;
351     for (t = targets; *t != '\0'; t = te) {
352         char *target;
353         if ((te = strchr(t, ',')) == NULL)
354             te = t + strlen(t);
355         target = xmalloc(te-t+1);
356         strncpy(target, t, (te-t));
357         target[te-t] = '\0';
358         if (*te != '\0')
359             te++;
360         else    /* XXX Perform clean-up after last target build. */
361             ba->buildAmount |= cleanFlags;
362
363         printf(_("Building for target %s\n"), target);
364
365         /* Read in configuration for target. */
366         rpmFreeMacros(NULL);
367         rpmFreeRpmrc();
368         (void) rpmReadConfigFiles(rcfile, target);
369         free(target);
370         rc = buildForTarget(ts, arg, ba);
371         if (rc)
372             break;
373     }
374
375 exit:
376     vsflags = rpmtsSetVSFlags(ts, ovsflags);
377     /* Restore original configuration. */
378     rpmFreeMacros(NULL);
379     rpmFreeRpmrc();
380     (void) rpmReadConfigFiles(rcfile, NULL);
381
382     return rc;
383 }
384
385 int main(int argc, char *argv[])
386 {
387     rpmts ts = NULL;
388     enum modes bigMode = MODE_BUILD;
389     BTA_t ba = &rpmBTArgs;
390     char * passPhrase = "";
391
392     int arg;
393     const char *optArg;
394     const char *poptCtx = "rpmbuild";
395     const char *pkg = NULL;
396     pid_t pipeChild = 0;
397     poptContext optCon;
398     int ec = 0;
399     int status;
400     int p[2];
401         
402 #if HAVE_MCHECK_H && HAVE_MTRACE
403     mtrace();   /* Trace malloc only if MALLOC_TRACE=mtrace-output-file. */
404 #endif
405     setprogname(argv[0]);       /* Retrofit glibc __progname */
406
407     /* XXX glibc churn sanity */
408     if (__progname == NULL) {
409         if ((__progname = strrchr(argv[0], '/')) != NULL) __progname++;
410         else __progname = argv[0];
411     }
412
413 #if defined(ENABLE_NLS)
414     /* set up the correct locale */
415     (void) setlocale(LC_ALL, "" );
416
417     bindtextdomain(PACKAGE, LOCALEDIR);
418     textdomain(PACKAGE);
419 #endif
420
421     rpmSetVerbosity(RPMLOG_NOTICE);     /* XXX silly use by showrc */
422
423     /* Make a first pass through the arguments, looking for --rcfile */
424     /* We need to handle that before dealing with the rest of the arguments. */
425     /* XXX popt argv definition should be fixed instead of casting... */
426     optCon = poptGetContext(poptCtx, argc, (const char **)argv, optionsTable, 0);
427     {
428         char *poptfile = rpmGenPath(rpmConfigDir(), LIBRPMALIAS_FILENAME, NULL);
429         (void) poptReadConfigFile(optCon, poptfile);
430         free(poptfile);
431     }
432     (void) poptReadDefaultConfig(optCon, 1);
433     poptSetExecPath(optCon, rpmConfigDir(), 1);
434
435     while ((arg = poptGetNextOpt(optCon)) > 0) {
436         optArg = poptGetOptArg(optCon);
437
438         switch (arg) {
439         default:
440             fprintf(stderr, _("Internal error in argument processing (%d) :-(\n"), arg);
441             exit(EXIT_FAILURE);
442         }
443     }
444
445     if (arg < -1) {
446         fprintf(stderr, "%s: %s\n", 
447                 poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 
448                 poptStrerror(arg));
449         exit(EXIT_FAILURE);
450     }
451
452     if (argc <= 1 || poptPeekArg(optCon) == NULL) {
453         printUsage(optCon, stderr, 0);
454         exit(EXIT_FAILURE);
455     }
456
457     rpmcliConfigured();
458
459     switch (ba->buildMode) {
460     case 'b':   bigMode = MODE_BUILD;           break;
461     case 't':   bigMode = MODE_TARBUILD;        break;
462     case 'B':   bigMode = MODE_REBUILD;         break;
463     case 'C':   bigMode = MODE_RECOMPILE;       break;
464     }
465
466     if (rpmcliRootDir && rpmcliRootDir[0] != '/') {
467         argerror(_("arguments to --root (-r) must begin with a /"));
468     }
469
470     if (ba->sign) {
471         int sigTag = rpmLookupSignatureType(RPMLOOKUPSIG_QUERY);
472         switch (sigTag) {
473         case 0:
474             break;
475         case RPMSIGTAG_PGP:
476         case RPMSIGTAG_GPG:
477         case RPMSIGTAG_DSA:
478         case RPMSIGTAG_RSA:
479             passPhrase = rpmGetPassPhrase(_("Enter pass phrase: "), sigTag);
480             if (passPhrase == NULL) {
481                 fprintf(stderr, _("Pass phrase check failed\n"));
482                 ec = EXIT_FAILURE;
483                 goto exit;
484             }
485             fprintf(stderr, _("Pass phrase is good.\n"));
486             passPhrase = xstrdup(passPhrase);
487             break;
488         default:
489             fprintf(stderr, _("Invalid %%_signature spec in macro file.\n"));
490             ec = EXIT_FAILURE;
491             goto exit;
492             break;
493         }
494     } else {
495         /* Make rpmLookupSignatureType() return 0 ("none") from now on */
496         (void) rpmLookupSignatureType(RPMLOOKUPSIG_DISABLE);
497     }
498
499     /* rpmbuild is rather chatty by default */
500     rpmSetVerbosity(quiet ? RPMLOG_WARNING : RPMLOG_INFO);
501
502     if (rpmcliPipeOutput) {
503         if (pipe(p) < 0) {
504             fprintf(stderr, _("creating a pipe for --pipe failed: %m\n"));
505             goto exit;
506         }
507
508         if (!(pipeChild = fork())) {
509             (void) signal(SIGPIPE, SIG_DFL);
510             (void) close(p[1]);
511             (void) dup2(p[0], STDIN_FILENO);
512             (void) close(p[0]);
513             (void) execl("/bin/sh", "/bin/sh", "-c", rpmcliPipeOutput, NULL);
514             fprintf(stderr, _("exec failed\n"));
515         }
516
517         (void) close(p[0]);
518         (void) dup2(p[1], STDOUT_FILENO);
519         (void) close(p[1]);
520     }
521         
522     ts = rpmtsCreate();
523     (void) rpmtsSetRootDir(ts, rpmcliRootDir);
524     switch (bigMode) {
525     case MODE_REBUILD:
526     case MODE_RECOMPILE:
527         ba->buildAmount =
528             RPMBUILD_PREP | RPMBUILD_BUILD | RPMBUILD_INSTALL | RPMBUILD_CHECK;
529         if (bigMode == MODE_REBUILD) {
530             ba->buildAmount |= RPMBUILD_PACKAGEBINARY;
531             ba->buildAmount |= RPMBUILD_RMSOURCE;
532             ba->buildAmount |= RPMBUILD_RMSPEC;
533             ba->buildAmount |= RPMBUILD_CLEAN;
534             ba->buildAmount |= RPMBUILD_RMBUILD;
535         }
536
537         while ((pkg = poptGetArg(optCon))) {
538             char * specFile = NULL;
539
540             ba->cookie = NULL;
541             ec = rpmInstallSource(ts, pkg, &specFile, &ba->cookie);
542             if (ec == 0) {
543                 ba->rootdir = rpmcliRootDir;
544                 ba->passPhrase = passPhrase;
545                 ec = build(ts, specFile, ba, rpmcliRcfile);
546             }
547             ba->cookie = _free(ba->cookie);
548             specFile = _free(specFile);
549
550             if (ec)
551                 break;
552         }
553         break;
554     case MODE_BUILD:
555     case MODE_TARBUILD:
556         switch (ba->buildChar) {
557         case 'a':
558             ba->buildAmount |= RPMBUILD_PACKAGESOURCE;
559         case 'b':
560             ba->buildAmount |= RPMBUILD_PACKAGEBINARY;
561             ba->buildAmount |= RPMBUILD_CLEAN;
562             if ((ba->buildChar == 'b') && ba->shortCircuit)
563                 break;
564         case 'i':
565             ba->buildAmount |= RPMBUILD_INSTALL;
566             ba->buildAmount |= RPMBUILD_CHECK;
567             if ((ba->buildChar == 'i') && ba->shortCircuit)
568                 break;
569         case 'c':
570             ba->buildAmount |= RPMBUILD_BUILD;
571             if ((ba->buildChar == 'c') && ba->shortCircuit)
572                 break;
573         case 'p':
574             ba->buildAmount |= RPMBUILD_PREP;
575             break;
576             
577         case 'l':
578             ba->buildAmount |= RPMBUILD_FILECHECK;
579             break;
580         case 's':
581             ba->buildAmount |= RPMBUILD_PACKAGESOURCE;
582             break;
583         }
584
585         while ((pkg = poptGetArg(optCon))) {
586             ba->rootdir = rpmcliRootDir;
587             ba->passPhrase = passPhrase;
588             ba->cookie = NULL;
589             ec = build(ts, pkg, ba, rpmcliRcfile);
590             if (ec)
591                 break;
592             rpmFreeMacros(NULL);
593             (void) rpmReadConfigFiles(rpmcliRcfile, NULL);
594         }
595         break;
596     }
597
598 exit:
599
600     ts = rpmtsFree(ts);
601
602     optCon = poptFreeContext(optCon);
603     rpmFreeMacros(NULL);
604     rpmFreeMacros(rpmCLIMacroContext);
605     rpmFreeRpmrc();
606
607     if (pipeChild) {
608         (void) fclose(stdout);
609         (void) waitpid(pipeChild, &status, 0);
610     }
611
612     /* keeps memory leak checkers quiet */
613     rpmlogClose();
614
615     freeNames();
616     ba->buildRootOverride = _free(ba->buildRootOverride);
617     ba->targets = _free(ba->targets);
618
619 #if HAVE_MCHECK_H && HAVE_MTRACE
620     muntrace();   /* Trace malloc only if MALLOC_TRACE=mtrace-output-file. */
621 #endif
622
623     /* XXX Avoid exit status overflow. Status 255 is special to xargs(1) */
624     if (ec > 254) ec = 254;
625
626     return ec;
627 }