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