- opaque (well mostly) rpmTransactionSet using methods.
[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 "rpmte.h"
11 #include "rpmts.h"
12
13 #include "build.h"
14 #include "debug.h"
15
16 /*@access rpmTransactionSet @*/ /* XXX compared with NULL @*/
17 /*@access rpmdb @*/             /* XXX compared with NULL @*/
18 /*@access FD_t @*/              /* XXX compared with NULL @*/
19
20 /**
21  */
22 static int checkSpec(rpmTransactionSet ts, Header h)
23         /*@globals fileSystem, internalState @*/
24         /*@modifies ts, h, fileSystem, internalState @*/
25 {
26     rpmProblemSet ps;
27     int rc;
28
29     if (!headerIsEntry(h, RPMTAG_REQUIRENAME)
30      && !headerIsEntry(h, RPMTAG_CONFLICTNAME))
31         return 0;
32
33     rc = rpmtsAddPackage(ts, h, NULL, 0, NULL);
34
35     rc = rpmtsCheck(ts);
36
37     ps = rpmtsGetProblems(ts);
38     if (rc == 0 && ps) {
39         rpmMessage(RPMMESS_ERROR, _("Failed build dependencies:\n"));
40         printDepProblems(stderr, ps);
41         rc = 1;
42     }
43     ps = rpmProblemSetFree(ps);
44
45     /* XXX nuke the added package. */
46     rpmtsClean(ts);
47
48     return rc;
49 }
50
51 /*
52  * Kurwa, durni ameryka?ce sobe zawsze my?l?, ?e ca?y ?wiat mówi po
53  * angielsku...
54  */
55 /* XXX this is still a dumb test but at least it's i18n aware */
56 /**
57  */
58 static int isSpecFile(const char * specfile)
59         /*@globals fileSystem @*/
60         /*@modifies fileSystem @*/
61 {
62     char buf[256];
63     const char * s;
64     FD_t fd;
65     int count;
66     int checking;
67
68     fd = Fopen(specfile, "r.ufdio");
69     if (fd == NULL || Ferror(fd)) {
70         rpmError(RPMERR_OPEN, _("Unable to open spec file %s: %s\n"),
71                 specfile, Fstrerror(fd));
72         return 0;
73     }
74     count = Fread(buf, sizeof(buf[0]), sizeof(buf), fd);
75     (void) Fclose(fd);
76
77     checking = 1;
78     for (s = buf; count--; s++) {
79         switch (*s) {
80         case '\r':
81         case '\n':
82             checking = 1;
83             /*@switchbreak@*/ break;
84         case ':':
85             checking = 0;
86             /*@switchbreak@*/ break;
87         default:
88             if (checking && !(isprint(*s) || isspace(*s))) return 0;
89             /*@switchbreak@*/ break;
90         }
91     }
92     return 1;
93 }
94
95 /**
96  */
97 static int buildForTarget(rpmTransactionSet ts, const char * arg, BTA_t ba)
98         /*@globals rpmGlobalMacroContext,
99                 fileSystem, internalState @*/
100         /*@modifies ts, rpmGlobalMacroContext,
101                 fileSystem, internalState @*/
102 {
103     const char * passPhrase = ba->passPhrase;
104     const char * cookie = ba->cookie;
105     int buildAmount = ba->buildAmount;
106     const char * buildRootURL = NULL;
107     const char * specFile;
108     const char * specURL;
109     int specut;
110     char buf[BUFSIZ];
111     Spec spec = NULL;
112     int rc;
113
114 #ifndef DYING
115     rpmSetTables(RPM_MACHTABLE_BUILDARCH, RPM_MACHTABLE_BUILDOS);
116 #endif
117
118     /*@-branchstate@*/
119     if (ba->buildRootOverride)
120         buildRootURL = rpmGenPath(NULL, ba->buildRootOverride, NULL);
121     /*@=branchstate@*/
122
123     /*@-compmempass@*/ /* FIX: static zcmds heartburn */
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     /*@=compmempass@*/
224
225     specut = urlPath(specURL, &specFile);
226     if (*specFile != '/') {
227         char *s = alloca(BUFSIZ);
228         (void)getcwd(s, BUFSIZ);
229         strcat(s, "/");
230         strcat(s, arg);
231         specURL = s;
232     }
233
234     if (specut != URL_IS_DASH) {
235         struct stat st;
236         if (Stat(specURL, &st) < 0) {
237             rpmError(RPMERR_STAT, _("failed to stat %s: %m\n"), specURL);
238             rc = 1;
239             goto exit;
240         }
241         if (! S_ISREG(st.st_mode)) {
242             rpmError(RPMERR_NOTREG, _("File %s is not a regular file.\n"),
243                 specURL);
244             rc = 1;
245             goto exit;
246         }
247
248         /* Try to verify that the file is actually a specfile */
249         if (!isSpecFile(specURL)) {
250             rpmError(RPMERR_BADSPEC,
251                 _("File %s does not appear to be a specfile.\n"), specURL);
252             rc = 1;
253             goto exit;
254         }
255     }
256     
257     /* Parse the spec file */
258 #define _anyarch(_f)    \
259 (((_f)&(RPMBUILD_PREP|RPMBUILD_BUILD|RPMBUILD_INSTALL|RPMBUILD_PACKAGEBINARY)) == 0)
260     if (parseSpec(&spec, specURL, ba->rootdir, buildRootURL, 0, passPhrase,
261                 cookie, _anyarch(buildAmount), ba->force)) {
262         rc = 1;
263         goto exit;
264     }
265 #undef  _anyarch
266
267     /* Assemble source header from parsed components */
268     initSourceHeader(spec);
269
270     /* Check build prerequisites */
271     if (!ba->noDeps && checkSpec(ts, spec->sourceHeader)) {
272         rc = 1;
273         goto exit;
274     }
275
276     if (buildSpec(spec, buildAmount, ba->noBuild)) {
277         rc = 1;
278         goto exit;
279     }
280     
281     if (ba->buildMode == 't')
282         (void) Unlink(specURL);
283     rc = 0;
284
285 exit:
286     spec = freeSpec(spec);
287     buildRootURL = _free(buildRootURL);
288     return rc;
289 }
290
291 int build(rpmTransactionSet ts, const char * arg, BTA_t ba, 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(ts, arg, ba);
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(ts, arg, ba);
327         if (rc)
328             break;
329     }
330
331 exit:
332     /* Restore original configuration. */
333     rpmFreeMacros(NULL);
334     (void) rpmReadConfigFiles(rcfile, NULL);
335
336     return rc;
337 }