2 * \file build/parseSpec.c
3 * Top level dispatcher for spec file parsing.
10 #include <rpm/rpmtypes.h>
11 #include <rpm/rpmlib.h> /* RPM_MACHTABLE & related */
12 #include <rpm/rpmds.h>
13 #include <rpm/rpmts.h>
14 #include <rpm/rpmlog.h>
15 #include <rpm/rpmfileutil.h>
16 #include "build/rpmbuild_internal.h"
17 #include "build/rpmbuild_misc.h"
20 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
21 #define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; }
22 #define ISMACRO(s,m) (rstreqn((s), (m), sizeof((m))-1) && !risalpha((s)[sizeof((m))-1]))
23 #define ISMACROWITHARG(s,m) (rstreqn((s), (m), sizeof((m))-1) && (risblank((s)[sizeof((m))-1]) || !(s)[sizeof((m))-1]))
25 #define LEN_AND_STR(_tag) (sizeof(_tag)-1), (_tag)
27 typedef struct OpenFileInfo {
33 struct OpenFileInfo * next;
36 static const struct PartRec {
41 { PART_PREAMBLE, LEN_AND_STR("%package")},
42 { PART_PREP, LEN_AND_STR("%prep")},
43 { PART_BUILD, LEN_AND_STR("%build")},
44 { PART_INSTALL, LEN_AND_STR("%install")},
45 { PART_CHECK, LEN_AND_STR("%check")},
46 { PART_CLEAN, LEN_AND_STR("%clean")},
47 { PART_PREUN, LEN_AND_STR("%preun")},
48 { PART_POSTUN, LEN_AND_STR("%postun")},
49 { PART_PRETRANS, LEN_AND_STR("%pretrans")},
50 { PART_POSTTRANS, LEN_AND_STR("%posttrans")},
51 { PART_PRE, LEN_AND_STR("%pre")},
52 { PART_POST, LEN_AND_STR("%post")},
53 { PART_FILES, LEN_AND_STR("%files")},
54 { PART_CHANGELOG, LEN_AND_STR("%changelog")},
55 { PART_DESCRIPTION, LEN_AND_STR("%description")},
56 { PART_TRIGGERPOSTUN, LEN_AND_STR("%triggerpostun")},
57 { PART_TRIGGERPREIN, LEN_AND_STR("%triggerprein")},
58 { PART_TRIGGERUN, LEN_AND_STR("%triggerun")},
59 { PART_TRIGGERIN, LEN_AND_STR("%triggerin")},
60 { PART_TRIGGERIN, LEN_AND_STR("%trigger")},
61 { PART_VERIFYSCRIPT, LEN_AND_STR("%verifyscript")},
62 { PART_POLICIES, LEN_AND_STR("%sepolicy")},
66 int isPart(const char *line)
68 const struct PartRec *p;
70 for (p = partList; p->token != NULL; p++) {
72 if (rstrncasecmp(line, p->token, p->len))
75 if (c == '\0' || risspace(c))
79 return (p->token ? p->part : PART_NONE);
84 static int matchTok(const char *token, const char *line)
86 const char *b, *be = line;
87 size_t toklen = strlen(token);
90 while ( *(b = be) != '\0' ) {
96 if (toklen != (be-b) || rstrncasecmp(token, b, (be-b)))
105 void handleComments(char *s)
112 /* Push a file to spec's file stack, return the newly pushed entry */
113 static OFI_t * pushOFI(rpmSpec spec, const char *fn)
115 OFI_t *ofi = xcalloc(1, sizeof(*ofi));
118 ofi->fileName = xstrdup(fn);
120 ofi->readBuf[0] = '\0';
122 ofi->next = spec->fileStack;
124 spec->fileStack = ofi;
125 return spec->fileStack;
128 /* Pop from spec's file stack */
129 static OFI_t * popOFI(rpmSpec spec)
131 if (spec->fileStack) {
132 OFI_t * ofi = spec->fileStack;
134 spec->fileStack = ofi->next;
140 return spec->fileStack;
143 static int restoreFirstChar(rpmSpec spec)
145 /* Restore 1st char in (possible) next line */
146 if (spec->nextline != NULL && spec->nextpeekc != '\0') {
147 *spec->nextline = spec->nextpeekc;
148 spec->nextpeekc = '\0';
154 /* Return zero on success, 1 if we need to read more and -1 on errors. */
155 static int copyNextLineFromOFI(rpmSpec spec, OFI_t *ofi)
157 /* Expand next line from file into line buffer */
158 if (!(spec->nextline && *spec->nextline)) {
159 int pc = 0, bc = 0, nc = 0;
160 const char *from = ofi->readPtr;
162 while (from && *from && ch != '\n') {
163 ch = spec->lbuf[spec->lbufOff] = *from;
164 spec->lbufOff++; from++;
166 if (spec->lbufOff >= spec->lbufSize) {
167 spec->lbufSize += BUFSIZ;
168 spec->lbuf = realloc(spec->lbuf, spec->lbufSize);
171 spec->lbuf[spec->lbufOff] = '\0';
174 /* Check if we need another line before expanding the buffer. */
175 for (const char *p = spec->lbuf; *p; p++) {
179 case '\n': p++, nc = 1; break;
184 case '\n': nc = 0; break;
187 case '{': p++, bc++; break;
188 case '(': p++, pc++; break;
189 case '%': p++; break;
192 case '{': if (bc > 0) bc++; break;
193 case '}': if (bc > 0) bc--; break;
194 case '(': if (pc > 0) pc++; break;
195 case ')': if (pc > 0) pc--; break;
199 /* If it doesn't, ask for one more line. */
200 if (pc || bc || nc ) {
206 /* Don't expand macros (eg. %define) in false branch of %if clause */
207 if (spec->readStack->reading &&
208 expandMacros(spec, spec->macros, spec->lbuf, spec->lbufSize)) {
209 rpmlog(RPMLOG_ERR, _("line %d: %s\n"),
210 spec->lineNum, spec->lbuf);
213 spec->nextline = spec->lbuf;
218 static void copyNextLineFinish(rpmSpec spec, int strip)
223 /* Find next line in expanded line buffer */
224 spec->line = last = spec->nextline;
226 while (*spec->nextline && ch != '\n') {
227 ch = *spec->nextline++;
229 last = spec->nextline;
232 /* Save 1st char of next line in order to terminate current line. */
233 if (*spec->nextline != '\0') {
234 spec->nextpeekc = *spec->nextline;
235 *spec->nextline = '\0';
238 if (strip & STRIP_COMMENTS)
239 handleComments(spec->line);
241 if (strip & STRIP_TRAILINGSPACE)
245 static int readLineFromOFI(rpmSpec spec, OFI_t *ofi)
248 /* Make sure the current file is open */
249 if (ofi->fp == NULL) {
250 ofi->fp = fopen(ofi->fileName, "r");
251 if (ofi->fp == NULL) {
252 rpmlog(RPMLOG_ERR, _("Unable to open %s: %s\n"),
253 ofi->fileName, strerror(errno));
256 spec->lineNum = ofi->lineNum = 0;
259 /* Make sure we have something in the read buffer */
260 if (!(ofi->readPtr && *(ofi->readPtr))) {
261 if (!fgets(ofi->readBuf, BUFSIZ, ofi->fp)) {
262 /* EOF, remove this file from the stack */
265 /* only on last file do we signal EOF to caller */
269 /* otherwise, go back and try the read again. */
272 ofi->readPtr = ofi->readBuf;
274 spec->lineNum = ofi->lineNum;
279 #define ARGMATCH(s,token,match) \
282 char *exp = rpmExpand(token, NULL); \
283 while(*s && !risblank(*s)) s++; \
284 while(*s && risblank(*s)) s++; \
286 rpmlog(RPMLOG_ERR, _("%s:%d: Argument expected for %s\n"), ofi->fileName, ofi->lineNum, os); \
290 match = matchTok(exp, s); \
295 int readLine(rpmSpec spec, int strip)
299 struct ReadLevelEntry *rl;
300 OFI_t *ofi = spec->fileStack;
304 if (!restoreFirstChar(spec)) {
306 if ((rc = readLineFromOFI(spec, ofi)) != 0) {
307 if (spec->readStack->next) {
308 rpmlog(RPMLOG_ERR, _("line %d: Unclosed %%if\n"),
309 spec->readStack->lineNum);
311 } else if (startLine > 0) {
313 _("line %d: unclosed macro or bad line continuation\n"),
319 ofi = spec->fileStack;
321 /* Copy next file line into the spec line buffer */
322 rc = copyNextLineFromOFI(spec, ofi);
325 startLine = spec->lineNum;
332 copyNextLineFinish(spec, strip);
338 if (!spec->readStack->reading && ISMACROWITHARG(s, "%if")) {
340 } else if (ISMACROWITHARG(s, "%ifarch")) {
341 ARGMATCH(s, "%{_target_cpu}", match);
342 } else if (ISMACROWITHARG(s, "%ifnarch")) {
343 ARGMATCH(s, "%{_target_cpu}", match);
345 } else if (ISMACROWITHARG(s, "%ifos")) {
346 ARGMATCH(s, "%{_target_os}", match);
347 } else if (ISMACROWITHARG(s, "%ifnos")) {
348 ARGMATCH(s, "%{_target_os}", match);
350 } else if (ISMACROWITHARG(s, "%if")) {
352 match = parseExpressionBoolean(spec, s);
355 _("%s:%d: bad %%if condition\n"),
356 ofi->fileName, ofi->lineNum);
359 } else if (ISMACRO(s, "%else")) {
360 if (! spec->readStack->next) {
361 /* Got an else with no %if ! */
363 _("%s:%d: Got a %%else with no %%if\n"),
364 ofi->fileName, ofi->lineNum);
367 spec->readStack->reading =
368 spec->readStack->next->reading && ! spec->readStack->reading;
369 spec->line[0] = '\0';
370 } else if (ISMACRO(s, "%endif")) {
371 if (! spec->readStack->next) {
372 /* Got an end with no %if ! */
374 _("%s:%d: Got a %%endif with no %%if\n"),
375 ofi->fileName, ofi->lineNum);
378 rl = spec->readStack;
379 spec->readStack = spec->readStack->next;
381 spec->line[0] = '\0';
382 } else if (spec->readStack->reading && ISMACROWITHARG(s, "%include")) {
383 char *fileName, *endFileName, *p;
387 endFileName = fileName;
388 SKIPNONSPACE(endFileName);
391 if (*fileName == '\0' || *p != '\0') {
392 rpmlog(RPMLOG_ERR, _("%s:%d: malformed %%include statement\n"),
393 ofi->fileName, ofi->lineNum);
398 ofi = pushOFI(spec, fileName);
403 rl = xmalloc(sizeof(*rl));
404 rl->reading = spec->readStack->reading && match;
405 rl->next = spec->readStack;
406 rl->lineNum = ofi->lineNum;
407 spec->readStack = rl;
408 spec->line[0] = '\0';
411 if (! spec->readStack->reading) {
412 spec->line[0] = '\0';
415 /* Collect parsed line */
416 if (spec->parsed == NULL)
417 spec->parsed = newStringBuf();
418 appendStringBufAux(spec->parsed, spec->line,(strip & STRIP_TRAILINGSPACE));
420 /* FIX: spec->readStack->next should be dependent */
424 void closeSpec(rpmSpec spec)
426 while (popOFI(spec)) {};
429 static const rpmTagVal sourceTags[] = {
444 RPMTAG_CHANGELOGTIME,
445 RPMTAG_CHANGELOGNAME,
446 RPMTAG_CHANGELOGTEXT,
449 RPMTAG_HEADERI18NTABLE,
454 static void initSourceHeader(rpmSpec spec)
456 struct Source *srcPtr;
458 if (spec->sourceHeader)
461 spec->sourceHeader = headerNew();
462 /* Only specific tags are added to the source package header */
463 headerCopyTags(spec->packages->header, spec->sourceHeader, sourceTags);
465 /* Add the build restrictions */
467 HeaderIterator hi = headerInitIterator(spec->buildRestrictions);
469 while (headerNext(hi, &td)) {
470 if (rpmtdCount(&td) > 0) {
471 (void) headerPut(spec->sourceHeader, &td, HEADERPUT_DEFAULT);
475 headerFreeIterator(hi);
478 if (spec->BANames && spec->BACount > 0) {
479 headerPutStringArray(spec->sourceHeader, RPMTAG_BUILDARCHS,
480 spec->BANames, spec->BACount);
483 /* Add tags for sources and patches */
484 for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
485 if (srcPtr->flags & RPMBUILD_ISSOURCE) {
486 headerPutString(spec->sourceHeader, RPMTAG_SOURCE, srcPtr->source);
487 if (srcPtr->flags & RPMBUILD_ISNO) {
488 headerPutUint32(spec->sourceHeader, RPMTAG_NOSOURCE,
492 if (srcPtr->flags & RPMBUILD_ISPATCH) {
493 headerPutString(spec->sourceHeader, RPMTAG_PATCH, srcPtr->source);
494 if (srcPtr->flags & RPMBUILD_ISNO) {
495 headerPutUint32(spec->sourceHeader, RPMTAG_NOPATCH,
502 /* Add extra provides to package. */
503 static void addPackageProvides(Header h)
505 const char *arch, *name;
507 rpmsenseFlags pflags = RPMSENSE_EQUAL;
509 /* <name> = <evr> provide */
510 name = headerGetString(h, RPMTAG_NAME);
511 arch = headerGetString(h, RPMTAG_ARCH);
512 evr = headerGetAsString(h, RPMTAG_EVR);
513 headerPutString(h, RPMTAG_PROVIDENAME, name);
514 headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
515 headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
518 * <name>(<isa>) = <evr> provide
519 * FIXME: noarch needs special casing for now as BuildArch: noarch doesn't
520 * cause reading in the noarch macros :-/
522 isaprov = rpmExpand(name, "%{?_isa}", NULL);
523 if (!rstreq(arch, "noarch") && !rstreq(name, isaprov)) {
524 headerPutString(h, RPMTAG_PROVIDENAME, isaprov);
525 headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
526 headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
532 static void addTargets(Package Pkgs)
534 char *platform = rpmExpand("%{_target_platform}", NULL);
535 char *arch = rpmExpand("%{_target_cpu}", NULL);
536 char *os = rpmExpand("%{_target_os}", NULL);
537 char *optflags = rpmExpand("%{optflags}", NULL);
539 for (Package pkg = Pkgs; pkg != NULL; pkg = pkg->next) {
540 headerPutString(pkg->header, RPMTAG_OS, os);
541 /* noarch subpackages already have arch set here, leave it alone */
542 if (!headerIsEntry(pkg->header, RPMTAG_ARCH)) {
543 headerPutString(pkg->header, RPMTAG_ARCH, arch);
545 headerPutString(pkg->header, RPMTAG_PLATFORM, platform);
546 headerPutString(pkg->header, RPMTAG_OPTFLAGS, optflags);
548 pkg->ds = rpmdsThis(pkg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL);
549 addPackageProvides(pkg->header);
557 static rpmSpec parseSpec(const char *specFile, rpmSpecFlags flags,
558 const char *buildRoot, int recursing)
560 int parsePart = PART_PREAMBLE;
561 int initialPackage = 1;
564 /* Set up a new Spec structure with no packages. */
567 spec->specFile = rpmGetPath(specFile, NULL);
568 pushOFI(spec, spec->specFile);
569 /* If buildRoot not specified, use default %{buildroot} */
571 spec->buildRoot = xstrdup(buildRoot);
573 spec->buildRoot = rpmGetPath("%{?buildroot:%{buildroot}}", NULL);
575 addMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC);
576 addMacro(NULL, "_licensedir", NULL, "%{_defaultlicensedir}", RMIL_SPEC);
577 spec->recursing = recursing;
580 /* All the parse*() functions expect to have a line pre-read */
581 /* in the spec's line buffer. Except for parsePreamble(), */
582 /* which handles the initial entry into a spec file. */
584 while (parsePart != PART_NONE) {
587 case PART_ERROR: /* fallthrough */
592 parsePart = parsePreamble(spec, initialPackage);
596 parsePart = parsePrep(spec);
602 parsePart = parseBuildInstallClean(spec, parsePart);
605 parsePart = parseChangelog(spec);
607 case PART_DESCRIPTION:
608 parsePart = parseDescription(spec);
617 case PART_VERIFYSCRIPT:
618 case PART_TRIGGERPREIN:
621 case PART_TRIGGERPOSTUN:
622 parsePart = parseScript(spec, parsePart);
626 parsePart = parseFiles(spec);
630 parsePart = parsePolicies(spec);
633 case PART_NONE: /* XXX avoid gcc whining */
635 case PART_BUILDARCHITECTURES:
639 if (goterror || parsePart >= PART_LAST) {
643 if (parsePart == PART_BUILDARCHITECTURES) {
649 spec->BASpecs = xcalloc(spec->BACount, sizeof(*spec->BASpecs));
651 if (spec->BANames != NULL)
652 for (x = 0; x < spec->BACount; x++) {
654 /* Skip if not arch is not compatible. */
655 if (!rpmMachineScore(RPM_MACHTABLE_BUILDARCH, spec->BANames[x]))
657 addMacro(NULL, "_target_cpu", NULL, spec->BANames[x], RMIL_RPMRC);
658 spec->BASpecs[index] = parseSpec(specFile, flags, buildRoot, 1);
659 if (spec->BASpecs[index] == NULL) {
660 spec->BACount = index;
663 delMacro(NULL, "_target_cpu");
667 spec->BACount = index;
670 _("No compatible architectures found for build\n"));
675 * Return the 1st child's fully parsed Spec structure.
676 * The restart of the parse when encountering BuildArch
677 * causes problems for "rpm -q --specfile". This is
678 * still a hack because there may be more than 1 arch
679 * specified (unlikely but possible.) There's also the
680 * further problem that the macro context, particularly
681 * %{_target_cpu}, disagrees with the info in the header.
683 if (spec->BACount >= 1) {
684 rpmSpec nspec = spec->BASpecs[0];
685 spec->BASpecs = _free(spec->BASpecs);
694 if (spec->clean == NULL) {
695 char *body = rpmExpand("%{?buildroot: %{__rm} -rf %{buildroot}}", NULL);
696 spec->clean = newStringBuf();
697 appendLineStringBuf(spec->clean, body);
701 /* Check for description in each package */
702 for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
703 if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) {
704 rpmlog(RPMLOG_ERR, _("Package has no %%description: %s\n"),
705 headerGetString(pkg->header, RPMTAG_NAME));
710 /* Add arch, os and platform, self-provides etc for each package */
711 addTargets(spec->packages);
715 /* Assemble source header from parsed components */
716 initSourceHeader(spec);
725 rpmSpec rpmSpecParse(const char *specFile, rpmSpecFlags flags,
726 const char *buildRoot)
728 return parseSpec(specFile, flags, buildRoot, 0);