- generate an rpm header on the fly for imported pubkeys.
[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, rpmGlobalMacroContext, 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         /*@globals rpmGlobalMacroContext,
110                 fileSystem, internalState @*/
111         /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/
112 {
113     const char * passPhrase = ba->passPhrase;
114     char * cookie = ba->cookie;
115     int buildAmount = ba->buildAmount;
116     const char * buildRootURL = NULL;
117     const char * specFile;
118     const char * specURL;
119     int specut;
120     char buf[BUFSIZ];
121     Spec spec = NULL;
122     int rc;
123
124 #ifndef DYING
125     rpmSetTables(RPM_MACHTABLE_BUILDARCH, RPM_MACHTABLE_BUILDOS);
126 #endif
127
128     /*@-branchstate@*/
129     if (ba->buildRootOverride)
130         buildRootURL = rpmGenPath(NULL, ba->buildRootOverride, NULL);
131     /*@=branchstate@*/
132
133     if (ba->buildMode == 't') {
134         FILE *fp;
135         const char * specDir;
136         const char * tmpSpecFile;
137         char * cmd, * s;
138         rpmCompressedMagic res = COMPRESSED_OTHER;
139         /*@observer@*/ static const char *zcmds[] =
140                 { "cat", "gunzip", "bunzip2", "cat" };
141
142         specDir = rpmGetPath("%{_specdir}", NULL);
143
144         /* XXX Using mkstemp is difficult here. */
145         /* XXX FWIW, default %{_specdir} is root.root 0755 */
146         {   char tfn[64];
147             strcpy(tfn, "rpm-spec.XXXXXX");
148             /*@-unrecog@*/
149             tmpSpecFile = rpmGetPath("%{_specdir}/", mktemp(tfn), NULL);
150             /*@=unrecog@*/
151         }
152
153         (void) isCompressed(arg, &res);
154
155         cmd = alloca(strlen(arg) + 50 + strlen(tmpSpecFile));
156         sprintf(cmd, "%s < %s | tar xOvf - Specfile 2>&1 > %s",
157                         zcmds[res & 0x3], arg, tmpSpecFile);
158         if (!(fp = popen(cmd, "r"))) {
159             rpmError(RPMERR_POPEN, _("Failed to open tar pipe: %m\n"));
160             specDir = _free(specDir);
161             tmpSpecFile = _free(tmpSpecFile);
162             return 1;
163         }
164         if ((!fgets(buf, sizeof(buf) - 1, fp)) || !strchr(buf, '/')) {
165             /* Try again */
166             (void) pclose(fp);
167
168             sprintf(cmd, "%s < %s | tar xOvf - \\*.spec 2>&1 > %s",
169                     zcmds[res & 0x3], arg, tmpSpecFile);
170             if (!(fp = popen(cmd, "r"))) {
171                 rpmError(RPMERR_POPEN, _("Failed to open tar pipe: %m\n"));
172                 specDir = _free(specDir);
173                 tmpSpecFile = _free(tmpSpecFile);
174                 return 1;
175             }
176             if (!fgets(buf, sizeof(buf) - 1, fp)) {
177                 /* Give up */
178                 rpmError(RPMERR_READ, _("Failed to read spec file from %s\n"),
179                         arg);
180                 (void) unlink(tmpSpecFile);
181                 specDir = _free(specDir);
182                 tmpSpecFile = _free(tmpSpecFile);
183                 return 1;
184             }
185         }
186         (void) pclose(fp);
187
188         cmd = s = buf;
189         while (*cmd != '\0') {
190             if (*cmd == '/') s = cmd + 1;
191             cmd++;
192         }
193
194         cmd = s;
195
196         /* remove trailing \n */
197         s = cmd + strlen(cmd) - 1;
198         *s = '\0';
199
200         specURL = s = alloca(strlen(specDir) + strlen(cmd) + 5);
201         sprintf(s, "%s/%s", specDir, cmd);
202         res = rename(tmpSpecFile, s);
203         specDir = _free(specDir);
204         
205         if (res) {
206             rpmError(RPMERR_RENAME, _("Failed to rename %s to %s: %m\n"),
207                         tmpSpecFile, s);
208             (void) unlink(tmpSpecFile);
209             tmpSpecFile = _free(tmpSpecFile);
210             return 1;
211         }
212         tmpSpecFile = _free(tmpSpecFile);
213
214         /* Make the directory which contains the tarball the source 
215            directory for this run */
216
217         if (*arg != '/') {
218             (void)getcwd(buf, BUFSIZ);
219             strcat(buf, "/");
220             strcat(buf, arg);
221         } else 
222             strcpy(buf, arg);
223
224         cmd = buf + strlen(buf) - 1;
225         while (*cmd != '/') cmd--;
226         *cmd = '\0';
227
228         addMacro(NULL, "_sourcedir", NULL, buf, RMIL_TARBALL);
229     } else {
230         specURL = arg;
231     }
232
233     specut = urlPath(specURL, &specFile);
234     if (*specFile != '/') {
235         char *s = alloca(BUFSIZ);
236         (void)getcwd(s, BUFSIZ);
237         strcat(s, "/");
238         strcat(s, arg);
239         specURL = s;
240     }
241
242     if (specut != URL_IS_DASH) {
243         struct stat st;
244         if (Stat(specURL, &st) < 0) {
245             rpmError(RPMERR_STAT, _("failed to stat %s: %m\n"), specURL);
246             rc = 1;
247             goto exit;
248         }
249         if (! S_ISREG(st.st_mode)) {
250             rpmError(RPMERR_NOTREG, _("File %s is not a regular file.\n"),
251                 specURL);
252             rc = 1;
253             goto exit;
254         }
255
256         /* Try to verify that the file is actually a specfile */
257         if (!isSpecFile(specURL)) {
258             rpmError(RPMERR_BADSPEC,
259                 _("File %s does not appear to be a specfile.\n"), specURL);
260             rc = 1;
261             goto exit;
262         }
263     }
264     
265     /* Parse the spec file */
266 #define _anyarch(_f)    \
267 (((_f)&(RPMBUILD_PREP|RPMBUILD_BUILD|RPMBUILD_INSTALL|RPMBUILD_PACKAGEBINARY)) == 0)
268     if (parseSpec(&spec, specURL, ba->rootdir, buildRootURL, 0, passPhrase,
269                 cookie, _anyarch(buildAmount), ba->force)) {
270         rc = 1;
271         goto exit;
272     }
273 #undef  _anyarch
274
275     /* Assemble source header from parsed components */
276     initSourceHeader(spec);
277
278     /* Check build prerequisites */
279     if (!ba->noDeps && checkSpec(spec->sourceHeader)) {
280         rc = 1;
281         goto exit;
282     }
283
284     if (buildSpec(spec, buildAmount, ba->noBuild)) {
285         rc = 1;
286         goto exit;
287     }
288     
289     if (ba->buildMode == 't')
290         (void) Unlink(specURL);
291     rc = 0;
292
293 exit:
294     spec = freeSpec(spec);
295     buildRootURL = _free(buildRootURL);
296     return rc;
297 }
298
299 int build(const char * arg, BTA_t ba, 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);
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);
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 }