check for memory leaks (almost all leaks are plugged).
[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 = Fread(buf, sizeof(buf), 1, fd);
65     Fclose(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     struct stat statbuf;
95     char buf[BUFSIZ];
96     Spec spec = NULL;
97
98     rpmSetTables(RPM_MACHTABLE_BUILDARCH, RPM_MACHTABLE_BUILDOS);
99
100     if (fromTarball) {
101         const char *specDir;
102         const char * tmpSpecFile;
103         char * cmd, *s;
104         int res;
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         res = rename(tmpSpecFile, s);
162         xfree(specDir);
163         xfree(tmpSpecFile);
164         
165         if (res) {
166             fprintf(stderr, _("Failed to rename %s to %s: %s\n"),
167                     tmpSpecFile, s, strerror(errno));
168             unlink(tmpSpecFile);
169             return 1;
170         }
171
172         /* Make the directory which contains the tarball the source 
173            directory for this run */
174
175         if (*arg != '/') {
176             (void)getcwd(buf, BUFSIZ);
177             strcat(buf, "/");
178             strcat(buf, arg);
179         } else 
180             strcpy(buf, arg);
181
182         cmd = buf + strlen(buf) - 1;
183         while (*cmd != '/') cmd--;
184         *cmd = '\0';
185
186         addMacro(NULL, "_sourcedir", NULL, buf, RMIL_TARBALL);
187         specfile = s;
188     } else if (arg[0] == '/') {
189         specfile = arg;
190     } else {
191         char *s = alloca(BUFSIZ);
192         (void)getcwd(s, BUFSIZ);
193         strcat(s, "/");
194         strcat(s, arg);
195         specfile = s;
196     }
197
198     stat(specfile, &statbuf);
199     if (! S_ISREG(statbuf.st_mode)) {
200         fprintf(stderr, _("File is not a regular file: %s\n"), specfile);
201         return 1;
202     }
203
204     /* Try to verify that the file is actually a specfile */
205     if (!isSpecFile(specfile)) {
206         fprintf(stderr, _("File %s does not appear to be a specfile.\n"),
207                 specfile);
208         return 1;
209     }
210     
211     /* Parse the spec file */
212 #define _anyarch(_f)    \
213 (((_f)&(RPMBUILD_PREP|RPMBUILD_BUILD|RPMBUILD_INSTALL|RPMBUILD_PACKAGEBINARY)) == 0)
214     if (parseSpec(&spec, specfile, buildRoot, 0, passPhrase, cookie,
215         _anyarch(buildAmount), force)) {
216             return 1;
217     }
218 #undef  _anyarch
219
220     /* Assemble source header from parsed components */
221     initSourceHeader(spec);
222
223     /* Check build prerequisites */
224     if (!nodeps && checkSpec(spec->sourceHeader)) {
225         freeSpec(spec);
226         return 1;
227     }
228
229     if (buildSpec(spec, buildAmount, test)) {
230         freeSpec(spec);
231         return 1;
232     }
233     
234     if (fromTarball) unlink(specfile);
235
236     freeSpec(spec);
237     
238     return 0;
239 }
240
241 int build(const char *arg, struct rpmBuildArguments *ba, const char *passPhrase,
242           int fromTarball, char *cookie, const char * rcfile, int force,
243           int nodeps)
244 {
245     char *t, *te;
246     int rc;
247     char *targets = ba->targets;
248
249     if (targets == NULL) {
250         rc =  buildForTarget(arg, ba, passPhrase, fromTarball, cookie,
251                 force, nodeps);
252         return rc;
253     }
254
255     /* parse up the build operators */
256
257     printf(_("Building target platforms: %s\n"), targets);
258
259     for (t = targets; *t != '\0'; t = te) {
260         char *target;
261         if ((te = strchr(t, ',')) == NULL)
262             te = t + strlen(t);
263         target = alloca(te-t+1);
264         strncpy(target, t, (te-t));
265         target[te-t] = '\0';
266         printf(_("Building for target %s\n"), target);
267
268         rpmReadConfigFiles(rcfile, target);
269         rc = buildForTarget(arg, ba, passPhrase, fromTarball, cookie,
270                 force, nodeps);
271         if (rc)
272             return rc;
273
274         freeMacros(NULL);
275     }
276
277     return 0;
278 }
279
280 #define POPT_USECATALOG         1000
281 #define POPT_NOLANG             1001
282 #define POPT_RMSOURCE           1002
283 #define POPT_RMBUILD            1003
284 #define POPT_BUILDROOT          1004
285 #define POPT_BUILDARCH          1005
286 #define POPT_BUILDOS            1006
287 #define POPT_TARGETPLATFORM     1007
288 #define POPT_NOBUILD            1008
289 #define POPT_SHORTCIRCUIT       1009
290 #define POPT_RMSPEC             1010
291
292 extern int noLang;
293 static int noBuild = 0;
294 static int useCatalog = 0;
295
296 static void buildArgCallback( /*@unused@*/ poptContext con,
297         /*@unused@*/ enum poptCallbackReason reason,
298         const struct poptOption * opt, const char * arg, const void * data)
299 {
300     struct rpmBuildArguments * rba = (struct rpmBuildArguments *) data;
301
302     switch (opt->val) {
303     case POPT_USECATALOG: rba->useCatalog = 1; break;
304     case POPT_NOBUILD: rba->noBuild = 1; break;
305     case POPT_NOLANG: rba->noLang = 1; break;
306     case POPT_SHORTCIRCUIT: rba->shortCircuit = 1; break;
307     case POPT_RMSOURCE: rba->buildAmount |= RPMBUILD_RMSOURCE; break;
308     case POPT_RMSPEC: rba->buildAmount |= RPMBUILD_RMSPEC; break;
309     case POPT_RMBUILD: rba->buildAmount |= RPMBUILD_RMBUILD; break;
310     case POPT_BUILDROOT:
311         if (rba->buildRootOverride) {
312             fprintf(stderr, _("buildroot already specified"));
313             exit(EXIT_FAILURE);
314             /*@notreached@*/
315         }
316         rba->buildRootOverride = xstrdup(arg);
317         break;
318     case POPT_BUILDARCH:
319         fprintf(stderr, _("--buildarch has been obsoleted.  Use the --target option instead.\n")); 
320         exit(EXIT_FAILURE);
321         /*@notreached@*/ break;
322     case POPT_BUILDOS:
323         fprintf(stderr, _("--buildos has been obsoleted.  Use the --target option instead.\n")); 
324         exit(EXIT_FAILURE);
325         /*@notreached@*/ break;
326     case POPT_TARGETPLATFORM:
327         if (rba->targets) {
328             int len = strlen(rba->targets) + strlen(arg) + 2;
329             rba->targets = xrealloc(rba->targets, len);
330             strcat(rba->targets, ",");
331         } else {
332             rba->targets = xmalloc(strlen(arg) + 1);
333             rba->targets[0] = '\0';
334         }
335         strcat(rba->targets, arg);
336         break;
337     }
338 }
339
340 struct poptOption rpmBuildPoptTable[] = {
341         { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA,
342                 buildArgCallback, 0, NULL, NULL },
343         { "buildarch", '\0', POPT_ARG_STRING, 0,  POPT_BUILDARCH,
344                 N_("override build architecture"), "ARCH" },
345         { "buildos", '\0', POPT_ARG_STRING, 0,  POPT_BUILDOS,
346                 N_("override build operating system"), "OS" },
347         { "buildroot", '\0', POPT_ARG_STRING, 0,  POPT_BUILDROOT,
348                 N_("override build root"), "DIRECTORY" },
349         { "clean", '\0', 0, 0, POPT_RMBUILD,
350                 N_("remove build tree when done"), NULL},
351         { "nobuild", '\0', 0, &noBuild,  POPT_NOBUILD,
352                 N_("do not execute any stages of the build"), NULL },
353         { "nolang", '\0', 0, &noLang, POPT_NOLANG,
354                 N_("do not accept I18N msgstr's from specfile"), NULL},
355         { "rmsource", '\0', 0, 0, POPT_RMSOURCE,
356                 N_("remove sources when done"), NULL},
357         { "rmspec", '\0', 0, 0, POPT_RMSPEC,
358                 N_("remove specfile when done"), NULL},
359         { "short-circuit", '\0', 0, 0,  POPT_SHORTCIRCUIT,
360                 N_("skip straight to specified stage (only for c,i)"), NULL },
361         { "target", '\0', POPT_ARG_STRING, 0,  POPT_TARGETPLATFORM,
362                 N_("override target platform"), "CPU-VENDOR-OS" },
363         { "usecatalog", '\0', 0, &useCatalog, POPT_USECATALOG,
364                 N_("lookup I18N strings in specfile catalog"), NULL},
365         { 0, 0, 0, 0, 0,        NULL, NULL }
366 };