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