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