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