check build prerequisites before building from specfile.
[platform/upstream/rpm.git] / build.c
1 #include "system.h"
2
3 #include "build/rpmbuild.h"
4 #include "popt/popt.h"
5 #include "build.h"
6 #include "install.h"
7
8
9 static int checkSpec(Header h)
10 {
11     char *rootdir = NULL;
12     rpmdb db = NULL;
13     int mode = O_RDONLY;
14     rpmTransactionSet ts;
15     struct rpmDependencyConflict * conflicts;
16     int numConflicts;
17     int rc;
18
19     if (!headerIsEntry(h, RPMTAG_REQUIREFLAGS))
20         return 0;
21
22     if (rpmdbOpen(rootdir, &db, mode, 0644)) {
23         const char *dn;
24         dn = rpmGetPath( (rootdir ? rootdir : ""), "%{_dbpath}", NULL);
25         rpmMessage(RPMMESS_ERROR, _("cannot open %s/packages.rpm\n"), dn);
26         xfree(dn);
27         exit(EXIT_FAILURE);
28     }
29     ts = rpmtransCreateSet(db, rootdir);
30
31     rc = rpmtransAddPackage(ts, h, NULL, NULL, 0, NULL);
32
33     rc = rpmdepCheck(ts, &conflicts, &numConflicts);
34     if (rc == 0 && conflicts) {
35         rpmMessage(RPMMESS_ERROR, _("failed build prerequisites:\n"));
36         printDepProblems(stderr, conflicts, numConflicts);
37         rpmdepFreeConflicts(conflicts, numConflicts);
38         rc = 1;
39     }
40
41     if (ts)
42         rpmtransFree(ts);
43     if (db)
44         rpmdbClose(db);
45
46     return rc;
47 }
48
49 static int buildForTarget(const char *arg, struct rpmBuildArguments *ba,
50         const char *passPhrase, int fromTarball, char *cookie,
51         int force, int nodeps)
52 {
53     int buildAmount = ba->buildAmount;
54     const char *buildRoot = ba->buildRootOverride;
55     int test = ba->noBuild;
56
57     FILE *f;
58     const char * specfile;
59     int res = 0;
60     struct stat statbuf;
61     char * s;
62     int count, fd;
63     char buf[BUFSIZ];
64     Spec spec = NULL;
65
66     rpmSetTables(RPM_MACHTABLE_BUILDARCH, RPM_MACHTABLE_BUILDOS);
67
68     if (fromTarball) {
69         const char *specDir;
70         const char * tmpSpecFile;
71         char * cmd, *s;
72
73         specDir = rpmGetPath("%{_specdir}", NULL);
74
75         {   char tfn[64];
76             strcpy(tfn, "rpm-spec.XXXXXX");
77             tmpSpecFile = rpmGetPath("%{_specdir}/", mktemp(tfn), NULL);
78         }
79
80         cmd = alloca(strlen(arg) + 50 + strlen(tmpSpecFile));
81         sprintf(cmd, "gunzip < %s | tar xOvf - Specfile 2>&1 > %s",
82                         arg, tmpSpecFile);
83         if (!(f = popen(cmd, "r"))) {
84             fprintf(stderr, _("Failed to open tar pipe: %s\n"), 
85                         strerror(errno));
86             xfree(specDir);
87             xfree(tmpSpecFile);
88             return 1;
89         }
90         if ((!fgets(buf, sizeof(buf) - 1, f)) || !strchr(buf, '/')) {
91             /* Try again */
92             pclose(f);
93
94             sprintf(cmd, "gunzip < %s | tar xOvf - \\*.spec 2>&1 > %s", arg,
95                     tmpSpecFile);
96             if (!(f = popen(cmd, "r"))) {
97                 fprintf(stderr, _("Failed to open tar pipe: %s\n"), 
98                         strerror(errno));
99                 xfree(specDir);
100                 xfree(tmpSpecFile);
101                 return 1;
102             }
103             if (!fgets(buf, sizeof(buf) - 1, f)) {
104                 /* Give up */
105                 fprintf(stderr, _("Failed to read spec file from %s\n"), arg);
106                 unlink(tmpSpecFile);
107                 xfree(specDir);
108                 xfree(tmpSpecFile);
109                 return 1;
110             }
111         }
112         pclose(f);
113
114         cmd = s = buf;
115         while (*cmd) {
116             if (*cmd == '/') s = cmd + 1;
117             cmd++;
118         }
119
120         cmd = s;
121
122         /* remove trailing \n */
123         s = cmd + strlen(cmd) - 1;
124         *s = '\0';
125
126         s = alloca(strlen(specDir) + strlen(cmd) + 5);
127         sprintf(s, "%s/%s", specDir, cmd);
128         
129         if (rename(tmpSpecFile, s)) {
130             fprintf(stderr, _("Failed to rename %s to %s: %s\n"),
131                     tmpSpecFile, s, strerror(errno));
132             unlink(tmpSpecFile);
133             xfree(specDir);
134             xfree(tmpSpecFile);
135             return 1;
136         }
137
138         /* Make the directory which contains the tarball the source 
139            directory for this run */
140
141         if (*arg != '/') {
142             (void)getcwd(buf, BUFSIZ);
143             strcat(buf, "/");
144             strcat(buf, arg);
145         } else 
146             strcpy(buf, arg);
147
148         cmd = buf + strlen(buf) - 1;
149         while (*cmd != '/') cmd--;
150         *cmd = '\0';
151
152         addMacro(NULL, "_sourcedir", NULL, buf, RMIL_TARBALL);
153         xfree(specDir);
154         xfree(tmpSpecFile);
155         specfile = s;
156     } else if (arg[0] == '/') {
157         specfile = arg;
158     } else {
159         char *s = alloca(BUFSIZ);
160         (void)getcwd(s, BUFSIZ);
161         strcat(s, "/");
162         strcat(s, arg);
163         specfile = s;
164     }
165
166     stat(specfile, &statbuf);
167     if (! S_ISREG(statbuf.st_mode)) {
168         fprintf(stderr, _("File is not a regular file: %s\n"), specfile);
169         return 1;
170     }
171     
172     if ((fd = open(specfile, O_RDONLY)) < 0) {
173         fprintf(stderr, _("Unable to open spec file: %s\n"), specfile);
174         return 1;
175     }
176     count = read(fd, buf, sizeof(buf) < 128 ? sizeof(buf) : 128);
177     close(fd);
178     s = buf;
179     while(count--) {
180         if (! (isprint(*s) || isspace(*s) || (*s == 0x1b))) {
181             fprintf(stderr, _("File contains non-printable characters(%c): %s\n"), *s,
182                     specfile);
183             return 1;
184         }
185         s++;
186     }
187
188     /* Parse the spec file */
189 #define _anyarch(_f)    \
190 (((_f)&(RPMBUILD_PREP|RPMBUILD_BUILD|RPMBUILD_INSTALL|RPMBUILD_PACKAGEBINARY)) == 0)
191     if (parseSpec(&spec, specfile, buildRoot, 0, passPhrase, cookie,
192         _anyarch(buildAmount), force)) {
193             return 1;
194     }
195 #undef  _anyarch
196
197     /* Assemble source header from parsed components */
198     initSourceHeader(spec);
199
200     /* Check build prerequisites */
201     if (!nodeps && checkSpec(spec->sourceHeader)) {
202         freeSpec(spec);
203         return 1;
204     }
205
206     if (buildSpec(spec, buildAmount, test)) {
207         freeSpec(spec);
208         return 1;
209     }
210     
211     if (fromTarball) unlink(specfile);
212
213     freeSpec(spec);
214     
215     return res;
216 }
217
218 int build(const char *arg, struct rpmBuildArguments *ba, const char *passPhrase,
219           int fromTarball, char *cookie, const char * rcfile, int force,
220           int nodeps)
221 {
222     char *t, *te;
223     int rc;
224     char *targets = ba->targets;
225
226     if (targets == NULL) {
227         rc =  buildForTarget(arg, ba, passPhrase, fromTarball, cookie,
228                 force, nodeps);
229         return rc;
230     }
231
232     /* parse up the build operators */
233
234     printf("Building target platforms: %s\n", targets);
235
236     for (t = targets; *t != '\0'; t = te) {
237         char *target;
238         if ((te = strchr(t, ',')) == NULL)
239             te = t + strlen(t);
240         target = alloca(te-t+1);
241         strncpy(target, t, (te-t));
242         target[te-t] = '\0';
243         printf("Building for target %s\n", target);
244
245         rpmReadConfigFiles(rcfile, target);
246         rc = buildForTarget(arg, ba, passPhrase, fromTarball, cookie,
247                 force, nodeps);
248         if (rc)
249             return rc;
250
251         freeMacros(NULL);
252     }
253
254     return 0;
255 }
256
257 #define POPT_USECATALOG         1000
258 #define POPT_NOLANG             1001
259 #define POPT_RMSOURCE           1002
260 #define POPT_RMBUILD            1003
261 #define POPT_BUILDROOT          1004
262 #define POPT_BUILDARCH          1005
263 #define POPT_BUILDOS            1006
264 #define POPT_TARGETPLATFORM     1007
265 #define POPT_NOBUILD            1008
266 #define POPT_SHORTCIRCUIT       1009
267
268 extern int noLang;
269 static int noBuild = 0;
270 static int useCatalog = 0;
271
272 static void buildArgCallback(poptContext con, enum poptCallbackReason reason,
273                              const struct poptOption * opt, const char * arg,
274                              struct rpmBuildArguments * data)
275 {
276     switch (opt->val) {
277     case POPT_USECATALOG: data->useCatalog = 1; break;
278     case POPT_NOBUILD: data->noBuild = 1; break;
279     case POPT_NOLANG: data->noLang = 1; break;
280     case POPT_SHORTCIRCUIT: data->shortCircuit = 1; break;
281     case POPT_RMSOURCE: data->buildAmount |= RPMBUILD_RMSOURCE; break;
282     case POPT_RMBUILD: data->buildAmount |= RPMBUILD_RMBUILD; break;
283     case POPT_BUILDROOT:
284         if (data->buildRootOverride) {
285             fprintf(stderr, _("buildroot already specified"));
286             exit(EXIT_FAILURE);
287         }
288         data->buildRootOverride = strdup(arg);
289         break;
290     case POPT_BUILDARCH:
291         fprintf(stderr, _("--buildarch has been obsoleted.  Use the --target option instead.\n")); 
292         exit(EXIT_FAILURE);
293         break;
294     case POPT_BUILDOS:
295         fprintf(stderr, _("--buildos has been obsoleted.  Use the --target option instead.\n")); 
296         exit(EXIT_FAILURE);
297         break;
298     case POPT_TARGETPLATFORM:
299         if (data->targets) {
300             int len = strlen(data->targets) + strlen(arg) + 2;
301             data->targets = realloc(data->targets, len);
302             strcat(data->targets, ",");
303         } else {
304             data->targets = malloc(strlen(arg) + 1);
305             data->targets[0] = '\0';
306         }
307         strcat(data->targets, arg);
308         break;
309     }
310 }
311
312 struct poptOption rpmBuildPoptTable[] = {
313         { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA,
314                 buildArgCallback, 0, NULL, NULL },
315         { "buildarch", '\0', POPT_ARG_STRING, 0,  POPT_BUILDARCH,
316                 N_("override build architecture"), "ARCH" },
317         { "buildos", '\0', POPT_ARG_STRING, 0,  POPT_BUILDOS,
318                 N_("override build operating system"), "OS" },
319         { "buildroot", '\0', POPT_ARG_STRING, 0,  POPT_BUILDROOT,
320                 N_("override build root"), "DIRECTORY" },
321         { "clean", '\0', 0, 0, POPT_RMBUILD,
322                 N_("remove build tree when done"), NULL},
323         { "nobuild", '\0', 0, &noBuild,  POPT_NOBUILD,
324                 N_("do not execute any stages of the build"), NULL },
325         { "nolang", '\0', 0, &noLang, POPT_NOLANG,
326                 N_("do not accept I18N msgstr's from specfile"), NULL},
327         { "rmsource", '\0', 0, 0, POPT_RMSOURCE,
328                 N_("remove sources and specfile when done"), NULL},
329         { "short-circuit", '\0', 0, 0,  POPT_SHORTCIRCUIT,
330                 N_("skip straight to specified stage (only for c,i)"), NULL },
331         { "target", '\0', POPT_ARG_STRING, 0,  POPT_TARGETPLATFORM,
332                 N_("override target platform"), "CPU-VENDOR-OS" },
333         { "usecatalog", '\0', 0, &useCatalog, POPT_USECATALOG,
334                 N_("lookup I18N strings in specfile catalog"), NULL},
335         { 0, 0, 0, 0, 0,        NULL, NULL }
336 };