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