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