Dumb thinko breaking tar builds from relative paths (rhbz#456321)
[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 <libgen.h>
8
9 #include <rpm/rpmcli.h>
10 #include <rpm/rpmtag.h>
11 #include <rpm/rpmlib.h>         /* rpmrc, MACHTABLE .. */
12 #include <rpm/rpmbuild.h>
13
14 #include <rpm/rpmps.h>
15 #include <rpm/rpmte.h>
16 #include <rpm/rpmts.h>
17 #include <rpm/rpmfileutil.h>
18 #include <rpm/rpmlog.h>
19
20 #include "build.h"
21 #include "debug.h"
22
23 /**
24  */
25 static int checkSpec(rpmts ts, Header h)
26 {
27     rpmps ps;
28     int rc;
29
30     if (!headerIsEntry(h, RPMTAG_REQUIRENAME)
31      && !headerIsEntry(h, RPMTAG_CONFLICTNAME))
32         return 0;
33
34     rc = rpmtsAddInstallElement(ts, h, NULL, 0, NULL);
35
36     rc = rpmtsCheck(ts);
37
38     ps = rpmtsProblems(ts);
39     if (rc == 0 && rpmpsNumProblems(ps) > 0) {
40         rpmlog(RPMLOG_ERR, _("Failed build dependencies:\n"));
41         rpmpsPrint(NULL, ps);
42         rc = 1;
43     }
44     ps = rpmpsFree(ps);
45
46     /* XXX nuke the added package. */
47     rpmtsClean(ts);
48
49     return rc;
50 }
51
52 /**
53  */
54 static int isSpecFile(const char * specfile)
55 {
56     char buf[256];
57     const char * s;
58     FILE * f;
59     int count;
60     int checking;
61
62     f = fopen(specfile, "r");
63     if (f == NULL || ferror(f)) {
64         rpmlog(RPMLOG_ERR, _("Unable to open spec file %s: %s\n"),
65                 specfile, strerror(errno));
66         return 0;
67     }
68     count = fread(buf, sizeof(buf[0]), sizeof(buf), f);
69     (void) fclose(f);
70
71     if (count == 0)
72         return 0;
73
74     checking = 1;
75     for (s = buf; count--; s++) {
76         switch (*s) {
77         case '\r':
78         case '\n':
79             checking = 1;
80             break;
81         case ':':
82             checking = 0;
83             break;
84         default:
85 #if 0
86             if (checking && !(isprint(*s) || isspace(*s))) return 0;
87             break;
88 #else
89             if (checking && !(isprint(*s) || isspace(*s)) && *(unsigned char *)s < 32) return 0;
90             break;
91 #endif
92         }
93     }
94     return 1;
95 }
96
97 /* 
98  * Try to find a spec from a tarball pointed to by arg. 
99  * Return absolute path to spec name on success, otherwise NULL.
100  */
101 static char * getTarSpec(const char *arg)
102 {
103     char *specFile = NULL;
104     char *specDir;
105     char *specBase;
106     char *tmpSpecFile;
107     const char **try;
108     char tarbuf[BUFSIZ];
109     int gotspec = 0, res;
110     static const char *tryspec[] = { "Specfile", "\\*.spec", NULL };
111
112     specDir = rpmGetPath("%{_specdir}", NULL);
113     tmpSpecFile = rpmGetPath("%{_specdir}/", "rpm-spec.XXXXXX", NULL);
114
115     (void) close(mkstemp(tmpSpecFile));
116
117     for (try = tryspec; *try != NULL; try++) {
118         FILE *fp;
119         char *cmd;
120
121         cmd = rpmExpand("%{uncompress: ", arg, "} | ",
122                         "%{__tar} xOvf - --wildcards ", *try,
123                         " 2>&1 > ", tmpSpecFile, NULL);
124
125         if (!(fp = popen(cmd, "r"))) {
126             rpmlog(RPMLOG_ERR, _("Failed to open tar pipe: %m\n"));
127         } else {
128             char *fok = fgets(tarbuf, sizeof(tarbuf) - 1, fp);
129             pclose(fp);
130             gotspec = (fok != NULL) && isSpecFile(tmpSpecFile);
131         }
132
133         if (!gotspec) 
134             unlink(tmpSpecFile);
135         free(cmd);
136     }
137
138     if (!gotspec) {
139         rpmlog(RPMLOG_ERR, _("Failed to read spec file from %s\n"), arg);
140         goto exit;
141     }
142
143     specBase = basename(tarbuf);
144     /* remove trailing \n */
145     specBase[strlen(specBase)-1] = '\0';
146
147     rasprintf(&specFile, "%s/%s", specDir, specBase);
148     res = rename(tmpSpecFile, specFile);
149
150     if (res) {
151         rpmlog(RPMLOG_ERR, _("Failed to rename %s to %s: %m\n"),
152                 tmpSpecFile, specFile);
153         free(specFile);
154         specFile = NULL;
155     } else {
156         /* mkstemp() can give unnecessarily strict permissions, fixup */
157         mode_t mask;
158         umask(mask = umask(0));
159         (void) chmod(specFile, 0666 & ~mask);
160     }
161
162 exit:
163     (void) unlink(tmpSpecFile);
164     free(tmpSpecFile);
165     free(specDir);
166     return specFile;
167 }
168
169 /**
170  */
171 static int buildForTarget(rpmts ts, const char * arg, BTA_t ba)
172 {
173     const char * passPhrase = ba->passPhrase;
174     const char * cookie = ba->cookie;
175     int buildAmount = ba->buildAmount;
176     char * buildRootURL = NULL;
177     char * specFile = NULL;
178     rpmSpec spec = NULL;
179     int rc = 1; /* assume failure */
180
181 #ifndef DYING
182     rpmSetTables(RPM_MACHTABLE_BUILDARCH, RPM_MACHTABLE_BUILDOS);
183 #endif
184
185     if (ba->buildRootOverride)
186         buildRootURL = rpmGenPath(NULL, ba->buildRootOverride, NULL);
187
188     if (ba->buildMode == 't') {
189         char *srcdir = NULL, *dir;
190
191         specFile = getTarSpec(arg);
192         if (!specFile)
193             goto exit;
194
195         /* Make the directory of the tarball %_sourcedir for this run */
196         /* dirname() may modify contents so extra hoops needed. */
197         if (*arg != '/') {
198             dir = rpmGetCwd();
199             rstrscat(&dir, "/", arg, NULL);
200         } else {
201             dir = xstrdup(arg);
202         }
203         srcdir = dirname(dir);
204         addMacro(NULL, "_sourcedir", NULL, srcdir, RMIL_TARBALL);
205         free(dir);
206     } else {
207         specFile = xstrdup(arg);
208     }
209
210     if (*specFile != '/') {
211         char *cwd = rpmGetCwd();
212         char *s = NULL;
213         rasprintf(&s, "%s/%s", cwd, arg);
214         free(specFile);
215         specFile = s;
216     }
217
218     struct stat st;
219     if (stat(specFile, &st) < 0) {
220         rpmlog(RPMLOG_ERR, _("failed to stat %s: %m\n"), specFile);
221         goto exit;
222     }
223     if (! S_ISREG(st.st_mode)) {
224         rpmlog(RPMLOG_ERR, _("File %s is not a regular file.\n"), specFile);
225         goto exit;
226     }
227
228     /* Try to verify that the file is actually a specfile */
229     if (!isSpecFile(specFile)) {
230         rpmlog(RPMLOG_ERR,
231                 _("File %s does not appear to be a specfile.\n"), specFile);
232         goto exit;
233     }
234     
235     /* Parse the spec file */
236 #define _anyarch(_f)    \
237 (((_f)&(RPMBUILD_PREP|RPMBUILD_BUILD|RPMBUILD_INSTALL|RPMBUILD_PACKAGEBINARY)) == 0)
238     if (parseSpec(ts, specFile, ba->rootdir, buildRootURL, 0, passPhrase,
239                 cookie, _anyarch(buildAmount), ba->force))
240     {
241         goto exit;
242     }
243 #undef  _anyarch
244     if ((spec = rpmtsSetSpec(ts, NULL)) == NULL) {
245         goto exit;
246     }
247
248     /* Assemble source header from parsed components */
249     initSourceHeader(spec);
250
251     /* Check build prerequisites */
252     if (!ba->noDeps && checkSpec(ts, spec->sourceHeader)) {
253         goto exit;
254     }
255
256     if (buildSpec(ts, spec, buildAmount, ba->noBuild)) {
257         goto exit;
258     }
259     
260     if (ba->buildMode == 't')
261         (void) unlink(specFile);
262     rc = 0;
263
264 exit:
265     free(specFile);
266     freeSpec(spec);
267     free(buildRootURL);
268     return rc;
269 }
270
271 int build(rpmts ts, const char * arg, BTA_t ba, const char * rcfile)
272 {
273     char *t, *te;
274     int rc = 0;
275     char * targets = ba->targets;
276 #define buildCleanMask  (RPMBUILD_RMSOURCE|RPMBUILD_RMSPEC)
277     int cleanFlags = ba->buildAmount & buildCleanMask;
278     rpmVSFlags vsflags, ovsflags;
279
280     vsflags = rpmExpandNumeric("%{_vsflags_build}");
281     if (ba->qva_flags & VERIFY_DIGEST)
282         vsflags |= _RPMVSF_NODIGESTS;
283     if (ba->qva_flags & VERIFY_SIGNATURE)
284         vsflags |= _RPMVSF_NOSIGNATURES;
285     if (ba->qva_flags & VERIFY_HDRCHK)
286         vsflags |= RPMVSF_NOHDRCHK;
287     ovsflags = rpmtsSetVSFlags(ts, vsflags);
288
289     if (targets == NULL) {
290         rc =  buildForTarget(ts, arg, ba);
291         goto exit;
292     }
293
294     /* parse up the build operators */
295
296     printf(_("Building target platforms: %s\n"), targets);
297
298     ba->buildAmount &= ~buildCleanMask;
299     for (t = targets; *t != '\0'; t = te) {
300         char *target;
301         if ((te = strchr(t, ',')) == NULL)
302             te = t + strlen(t);
303         target = xmalloc(te-t+1);
304         strncpy(target, t, (te-t));
305         target[te-t] = '\0';
306         if (*te != '\0')
307             te++;
308         else    /* XXX Perform clean-up after last target build. */
309             ba->buildAmount |= cleanFlags;
310
311         printf(_("Building for target %s\n"), target);
312
313         /* Read in configuration for target. */
314         rpmFreeMacros(NULL);
315         rpmFreeRpmrc();
316         (void) rpmReadConfigFiles(rcfile, target);
317         free(target);
318         rc = buildForTarget(ts, arg, ba);
319         if (rc)
320             break;
321     }
322
323 exit:
324     vsflags = rpmtsSetVSFlags(ts, ovsflags);
325     /* Restore original configuration. */
326     rpmFreeMacros(NULL);
327     rpmFreeRpmrc();
328     (void) rpmReadConfigFiles(rcfile, NULL);
329
330     return rc;
331 }