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