Explicit branchstate annotations.
[platform/upstream/rpm.git] / build.c
1 /** \ingroup rpmcli
2  * Parse spec file and build package.
3  */
4
5 #include "system.h"
6
7 #include <rpmcli.h>
8 #include <rpmbuild.h>
9
10 #include "build.h"
11 #include "debug.h"
12
13 /*@access rpmTransactionSet @*/ /* XXX compared with NULL @*/
14 /*@access rpmdb @*/             /* XXX compared with NULL @*/
15 /*@access FD_t @*/              /* XXX compared with NULL @*/
16
17 /**
18  */
19 static int checkSpec(Header h)
20         /*@globals rpmGlobalMacroContext, fileSystem @*/
21         /*@modifies h, fileSystem @*/
22 {
23     const char * rootdir = NULL;
24     rpmdb db = NULL;
25     int mode = O_RDONLY;
26     rpmTransactionSet ts;
27     rpmDependencyConflict conflicts;
28     int numConflicts;
29     int rc;
30
31     if (!headerIsEntry(h, RPMTAG_REQUIREFLAGS))
32         return 0;
33
34     if (rpmdbOpen(rootdir, &db, mode, 0644)) {
35         const char * dn;
36         dn = rpmGetPath( (rootdir ? rootdir : ""), "%{_dbpath}", NULL);
37         rpmError(RPMERR_OPEN, _("cannot open rpm database in %s\n"), dn);
38         dn = _free(dn);
39         exit(EXIT_FAILURE);
40     }
41     ts = rpmtransCreateSet(db, rootdir);
42
43     rc = rpmtransAddPackage(ts, h, NULL, NULL, 0, NULL);
44
45     rc = rpmdepCheck(ts, &conflicts, &numConflicts);
46     /*@-branchstate@*/
47     if (rc == 0 && conflicts) {
48         rpmMessage(RPMMESS_ERROR, _("failed build dependencies:\n"));
49         printDepProblems(stderr, conflicts, numConflicts);
50         conflicts = rpmdepFreeConflicts(conflicts, numConflicts);
51         rc = 1;
52     }
53     /*@=branchstate@*/
54
55     ts = rpmtransFree(ts);
56     if (db != NULL)
57         (void) rpmdbClose(db);
58
59     return rc;
60 }
61
62 /*
63  * Kurwa, durni ameryka?ce sobe zawsze my?l?, ?e ca?y ?wiat mówi po
64  * angielsku...
65  */
66 /* XXX this is still a dumb test but at least it's i18n aware */
67 /**
68  */
69 static int isSpecFile(const char * specfile)
70         /*@globals fileSystem @*/
71         /*@modifies fileSystem @*/
72 {
73     char buf[256];
74     const char * s;
75     FD_t fd;
76     int count;
77     int checking;
78
79     fd = Fopen(specfile, "r.ufdio");
80     if (fd == NULL || Ferror(fd)) {
81         rpmError(RPMERR_OPEN, _("Unable to open spec file %s: %s\n"),
82                 specfile, Fstrerror(fd));
83         return 0;
84     }
85     count = Fread(buf, sizeof(buf[0]), sizeof(buf), fd);
86     (void) Fclose(fd);
87
88     checking = 1;
89     for (s = buf; count--; s++) {
90         switch (*s) {
91         case '\r':
92         case '\n':
93             checking = 1;
94             /*@switchbreak@*/ break;
95         case ':':
96             checking = 0;
97             /*@switchbreak@*/ break;
98         default:
99             if (checking && !(isprint(*s) || isspace(*s))) return 0;
100             /*@switchbreak@*/ break;
101         }
102     }
103     return 1;
104 }
105
106 /**
107  */
108 static int buildForTarget(const char * arg, BTA_t ba,
109                 const char * passPhrase, char * cookie)
110         /*@globals rpmGlobalMacroContext,
111                 fileSystem, internalState @*/
112         /*@modifies fileSystem, internalState @*/
113 {
114     int buildAmount = ba->buildAmount;
115     const char * buildRootURL = NULL;
116     const char * specFile;
117     const char * specURL;
118     int specut;
119     char buf[BUFSIZ];
120     Spec spec = NULL;
121     int rc;
122
123 #ifndef DYING
124     rpmSetTables(RPM_MACHTABLE_BUILDARCH, RPM_MACHTABLE_BUILDOS);
125 #endif
126
127     /*@-branchstate@*/
128     if (ba->buildRootOverride)
129         buildRootURL = rpmGenPath(NULL, ba->buildRootOverride, NULL);
130     /*@=branchstate@*/
131
132     if (ba->buildMode == 't') {
133         FILE *fp;
134         const char * specDir;
135         const char * tmpSpecFile;
136         char * cmd, * s;
137         rpmCompressedMagic res = COMPRESSED_OTHER;
138         /*@observer@*/ static const char *zcmds[] =
139                 { "cat", "gunzip", "bunzip2", "cat" };
140
141         specDir = rpmGetPath("%{_specdir}", NULL);
142
143         /* XXX Using mkstemp is difficult here. */
144         /* XXX FWIW, default %{_specdir} is root.root 0755 */
145         {   char tfn[64];
146             strcpy(tfn, "rpm-spec.XXXXXX");
147             /*@-unrecog@*/
148             tmpSpecFile = rpmGetPath("%{_specdir}/", mktemp(tfn), NULL);
149             /*@=unrecog@*/
150         }
151
152         (void) isCompressed(arg, &res);
153
154         cmd = alloca(strlen(arg) + 50 + strlen(tmpSpecFile));
155         sprintf(cmd, "%s < %s | tar xOvf - Specfile 2>&1 > %s",
156                         zcmds[res & 0x3], arg, tmpSpecFile);
157         if (!(fp = popen(cmd, "r"))) {
158             rpmError(RPMERR_POPEN, _("Failed to open tar pipe: %m\n"));
159             specDir = _free(specDir);
160             tmpSpecFile = _free(tmpSpecFile);
161             return 1;
162         }
163         if ((!fgets(buf, sizeof(buf) - 1, fp)) || !strchr(buf, '/')) {
164             /* Try again */
165             (void) pclose(fp);
166
167             sprintf(cmd, "%s < %s | tar xOvf - \\*.spec 2>&1 > %s",
168                     zcmds[res & 0x3], arg, tmpSpecFile);
169             if (!(fp = popen(cmd, "r"))) {
170                 rpmError(RPMERR_POPEN, _("Failed to open tar pipe: %m\n"));
171                 specDir = _free(specDir);
172                 tmpSpecFile = _free(tmpSpecFile);
173                 return 1;
174             }
175             if (!fgets(buf, sizeof(buf) - 1, fp)) {
176                 /* Give up */
177                 rpmError(RPMERR_READ, _("Failed to read spec file from %s\n"),
178                         arg);
179                 (void) unlink(tmpSpecFile);
180                 specDir = _free(specDir);
181                 tmpSpecFile = _free(tmpSpecFile);
182                 return 1;
183             }
184         }
185         (void) pclose(fp);
186
187         cmd = s = buf;
188         while (*cmd != '\0') {
189             if (*cmd == '/') s = cmd + 1;
190             cmd++;
191         }
192
193         cmd = s;
194
195         /* remove trailing \n */
196         s = cmd + strlen(cmd) - 1;
197         *s = '\0';
198
199         specURL = s = alloca(strlen(specDir) + strlen(cmd) + 5);
200         sprintf(s, "%s/%s", specDir, cmd);
201         res = rename(tmpSpecFile, s);
202         specDir = _free(specDir);
203         
204         if (res) {
205             rpmError(RPMERR_RENAME, _("Failed to rename %s to %s: %m\n"),
206                         tmpSpecFile, s);
207             (void) unlink(tmpSpecFile);
208             tmpSpecFile = _free(tmpSpecFile);
209             return 1;
210         }
211         tmpSpecFile = _free(tmpSpecFile);
212
213         /* Make the directory which contains the tarball the source 
214            directory for this run */
215
216         if (*arg != '/') {
217             (void)getcwd(buf, BUFSIZ);
218             strcat(buf, "/");
219             strcat(buf, arg);
220         } else 
221             strcpy(buf, arg);
222
223         cmd = buf + strlen(buf) - 1;
224         while (*cmd != '/') cmd--;
225         *cmd = '\0';
226
227         addMacro(NULL, "_sourcedir", NULL, buf, RMIL_TARBALL);
228     } else {
229         specURL = arg;
230     }
231
232     specut = urlPath(specURL, &specFile);
233     if (*specFile != '/') {
234         char *s = alloca(BUFSIZ);
235         (void)getcwd(s, BUFSIZ);
236         strcat(s, "/");
237         strcat(s, arg);
238         specURL = s;
239     }
240
241     if (specut != URL_IS_DASH) {
242         struct stat st;
243         if (Stat(specURL, &st) < 0) {
244             rpmError(RPMERR_STAT, _("failed to stat %s: %m\n"), specURL);
245             rc = 1;
246             goto exit;
247         }
248         if (! S_ISREG(st.st_mode)) {
249             rpmError(RPMERR_NOTREG, _("File %s is not a regular file.\n"),
250                 specURL);
251             rc = 1;
252             goto exit;
253         }
254
255         /* Try to verify that the file is actually a specfile */
256         if (!isSpecFile(specURL)) {
257             rpmError(RPMERR_BADSPEC,
258                 _("File %s does not appear to be a specfile.\n"), specURL);
259             rc = 1;
260             goto exit;
261         }
262     }
263     
264     /* Parse the spec file */
265 #define _anyarch(_f)    \
266 (((_f)&(RPMBUILD_PREP|RPMBUILD_BUILD|RPMBUILD_INSTALL|RPMBUILD_PACKAGEBINARY)) == 0)
267     if (parseSpec(&spec, specURL, ba->rootdir, buildRootURL, 0, passPhrase,
268                 cookie, _anyarch(buildAmount), ba->force)) {
269         rc = 1;
270         goto exit;
271     }
272 #undef  _anyarch
273
274     /* Assemble source header from parsed components */
275     initSourceHeader(spec);
276
277     /* Check build prerequisites */
278     if (!ba->noDeps && checkSpec(spec->sourceHeader)) {
279         rc = 1;
280         goto exit;
281     }
282
283     if (buildSpec(spec, buildAmount, ba->noBuild)) {
284         rc = 1;
285         goto exit;
286     }
287     
288     if (ba->buildMode == 't')
289         (void) Unlink(specURL);
290     rc = 0;
291
292 exit:
293     spec = freeSpec(spec);
294     buildRootURL = _free(buildRootURL);
295     return rc;
296 }
297
298 int build(const char * arg, BTA_t ba,
299         const char * passPhrase, char * cookie, const char * rcfile)
300 {
301     char *t, *te;
302     int rc = 0;
303     char * targets = ba->targets;
304 #define buildCleanMask  (RPMBUILD_RMSOURCE|RPMBUILD_RMSPEC)
305     int cleanFlags = ba->buildAmount & buildCleanMask;
306
307     if (targets == NULL) {
308         rc =  buildForTarget(arg, ba, passPhrase, cookie);
309         goto exit;
310     }
311
312     /* parse up the build operators */
313
314     printf(_("Building target platforms: %s\n"), targets);
315
316     ba->buildAmount &= ~buildCleanMask;
317     for (t = targets; *t != '\0'; t = te) {
318         char *target;
319         if ((te = strchr(t, ',')) == NULL)
320             te = t + strlen(t);
321         target = alloca(te-t+1);
322         strncpy(target, t, (te-t));
323         target[te-t] = '\0';
324         if (*te != '\0')
325             te++;
326         else    /* XXX Perform clean-up after last target build. */
327             ba->buildAmount |= cleanFlags;
328
329         printf(_("Building for target %s\n"), target);
330
331         /* Read in configuration for target. */
332         rpmFreeMacros(NULL);
333         (void) rpmReadConfigFiles(rcfile, target);
334         rc = buildForTarget(arg, ba, passPhrase, cookie);
335         if (rc)
336             break;
337     }
338
339 exit:
340     /* Restore original configuration. */
341     rpmFreeMacros(NULL);
342     (void) rpmReadConfigFiles(rcfile, NULL);
343     return rc;
344 }