diff --git a/build/parsePreamble.c b/build/parsePreamble.c index de205d5f8e..8d1d85464b 100644 --- a/build/parsePreamble.c +++ b/build/parsePreamble.c @@ -48,6 +48,15 @@ static const rpmTagVal copyTagsDuringParse[] = { 0 }; +/** + */ +static const rpmTagVal requiredTagsForBuild[] = { + RPMTAG_NAME, + RPMTAG_VERSION, + RPMTAG_RELEASE, + 0 +}; + /** */ static const rpmTagVal requiredTags[] = { @@ -415,7 +424,7 @@ static inline char * findLastChar(char * s) /** */ -static int isMemberInEntry(Header h, const char *name, rpmTagVal tag) +int isMemberInEntry(Header h, const char *name, rpmTagVal tag) { struct rpmtd_s td; int found = 0; @@ -437,7 +446,7 @@ static int isMemberInEntry(Header h, const char *name, rpmTagVal tag) /** */ -static rpmRC checkForValidArchitectures(rpmSpec spec) +rpmRC checkForValidArchitectures(rpmSpec spec) { char *arch = rpmExpand("%{_target_cpu}", NULL); char *os = rpmExpand("%{_target_os}", NULL); @@ -483,7 +492,7 @@ static rpmRC checkForValidArchitectures(rpmSpec spec) * @param NVR package name-version-release * @return RPMRC_OK if OK */ -static int checkForRequired(Header h, const char * NVR) +int checkForRequired(Header h) { int res = RPMRC_OK; const rpmTagVal * p; @@ -492,7 +501,30 @@ static int checkForRequired(Header h, const char * NVR) if (!headerIsEntry(h, *p)) { rpmlog(RPMLOG_ERR, _("%s field must be present in package: %s\n"), - rpmTagGetName(*p), NVR); + rpmTagGetName(*p), headerGetString(h, RPMTAG_NAME)); + res = RPMRC_FAIL; + } + } + + return res; +} + +/** + * Check that required tags are present in header. + * @param h header + * @param NVR package name-version-release + * @return RPMRC_OK if OK + */ +static int checkForRequiredForBuild(Header h) +{ + int res = RPMRC_OK; + const rpmTagVal * p; + + for (p = requiredTagsForBuild; *p != 0; p++) { + if (!headerIsEntry(h, *p)) { + rpmlog(RPMLOG_ERR, + _("%s field must be present before build in package: %s\n"), + rpmTagGetName(*p), headerGetString(h, RPMTAG_NAME)); res = RPMRC_FAIL; } } @@ -506,7 +538,7 @@ static int checkForRequired(Header h, const char * NVR) * @param NVR package name-version-release * @return RPMRC_OK if OK */ -static int checkForDuplicates(Header h, const char * NVR) +int checkForDuplicates(Header h) { int res = RPMRC_OK; rpmTagVal tag, lastTag = RPMTAG_NOT_FOUND; @@ -515,7 +547,7 @@ static int checkForDuplicates(Header h, const char * NVR) while ((tag = headerNextTag(hi)) != RPMTAG_NOT_FOUND) { if (tag == lastTag) { rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"), - rpmTagGetName(tag), NVR); + rpmTagGetName(tag), headerGetString(h, RPMTAG_NAME)); res = RPMRC_FAIL; } lastTag = tag; @@ -545,7 +577,7 @@ static struct optionalTag { /** */ -static void fillOutMainPackage(Header h) +void fillOutMainPackage(Header h) { const struct optionalTag *ot; @@ -1153,11 +1185,13 @@ int parsePreamble(rpmSpec spec, int initialPackage) NVR = xstrdup(name); pkg = newPackage(NVR, spec->pool, &spec->packages); headerPutString(pkg->header, RPMTAG_NAME, NVR); + } else if (spec->sourcePackage) { + NVR = xstrdup("(main package)"); + pkg = spec->packages; } else { NVR = xstrdup("(main package)"); pkg = newPackage(NULL, spec->pool, &spec->packages); spec->sourcePackage = newPackage(NULL, spec->pool, NULL); - } if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) { @@ -1212,6 +1246,10 @@ int parsePreamble(rpmSpec spec, int initialPackage) * can't be messed with by anything spec does beyond this point. */ if (initialPackage) { + if (checkForRequiredForBuild(pkg->header)) { + goto exit; + } + char *buildRoot = rpmGetPath(spec->buildRoot, NULL); free(spec->buildRoot); spec->buildRoot = buildRoot; @@ -1226,32 +1264,6 @@ int parsePreamble(rpmSpec spec, int initialPackage) } } - /* XXX Skip valid arch check if not building binary package */ - if (!(spec->flags & RPMSPEC_ANYARCH) && checkForValidArchitectures(spec)) { - goto exit; - } - - /* It is the main package */ - if (pkg == spec->packages) { - fillOutMainPackage(pkg->header); - /* Define group tag to something when group is undefined in main package*/ - if (!headerIsEntry(pkg->header, RPMTAG_GROUP)) { - headerPutString(pkg->header, RPMTAG_GROUP, "Unspecified"); - } - } - - if (checkForDuplicates(pkg->header, NVR)) { - goto exit; - } - - if (pkg != spec->packages) { - copyInheritedTags(pkg->header, spec->packages->header); - } - - if (checkForRequired(pkg->header, NVR)) { - goto exit; - } - /* if we get down here nextPart has been set to non-error */ res = nextPart; diff --git a/build/parseSpec.c b/build/parseSpec.c index 3929c0a26c..9a24d13114 100644 --- a/build/parseSpec.c +++ b/build/parseSpec.c @@ -652,6 +652,10 @@ static void initSourceHeader(rpmSpec spec) if (headerIsEntry(sourcePkg->header, RPMTAG_NAME)) return; + char *os = rpmExpand("%{_target_os}", NULL); + headerPutString(sourcePkg->header, RPMTAG_OS, os); + free(os); + /* Only specific tags are added to the source package header */ headerCopyTags(spec->packages->header, sourcePkg->header, sourceTags); @@ -694,11 +698,17 @@ static void initSourceHeader(rpmSpec spec) } } } +} + +static void finalizeSourceHeader(rpmSpec spec) +{ + /* Only specific tags are added to the source package header */ + headerCopyTags(spec->packages->header, spec->sourcePackage->header, sourceTags); /* Provide all package NEVRs that would be built */ for (Package p = spec->packages; p != NULL; p = p->next) { if (p->fileList) { - Header h = sourcePkg->header; + Header h = spec->sourcePackage->header; uint32_t dsflags = rpmdsFlags(p->ds); headerPutString(h, RPMTAG_PROVIDENAME, rpmdsN(p->ds)); headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &dsflags, 1); @@ -733,37 +743,17 @@ void addPackageProvides(Package pkg) free(evr); } -static void addTargets(Package Pkgs) +static void addArch(rpmSpec spec) { - char *platform = rpmExpand("%{_target_platform}", NULL); char *arch = rpmExpand("%{_target_cpu}", NULL); - char *os = rpmExpand("%{_target_os}", NULL); - char *optflags = rpmExpand("%{optflags}", NULL); - for (Package pkg = Pkgs; pkg != NULL; pkg = pkg->next) { - if (headerIsEntry(pkg->header, RPMTAG_OS)) { - continue; - } - headerPutString(pkg->header, RPMTAG_OS, os); + for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next) { /* noarch subpackages already have arch set here, leave it alone */ if (!headerIsEntry(pkg->header, RPMTAG_ARCH)) { headerPutString(pkg->header, RPMTAG_ARCH, arch); } - headerPutString(pkg->header, RPMTAG_PLATFORM, platform); - headerPutString(pkg->header, RPMTAG_OPTFLAGS, optflags); - - /* Add manual dependencies early for rpmspec etc to look at */ - addPackageProvides(pkg); - for (int i=0; idependencies[i], pkg->header); - } - - pkg->ds = rpmdsThis(pkg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL); } - free(platform); free(arch); - free(os); - free(optflags); } rpmRC checkForEncoding(Header h, int addtag) @@ -901,12 +891,6 @@ static rpmRC parseSpecSection(rpmSpec *specptr, int secondary) int storedParsePart; int initialPackage = 1; - if (secondary) { - initialPackage = 0; - parsePart = PART_EMPTY; - prevParsePart = PART_NONE; - } - /* All the parse*() functions expect to have a line pre-read */ /* in the spec's line buffer. Except for parsePreamble(), */ /* which handles the initial entry into a spec file. */ @@ -1053,17 +1037,8 @@ static rpmRC parseSpecSection(rpmSpec *specptr, int secondary) } } - /* Check for description in each package */ - for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next) { - if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) { - rpmlog(RPMLOG_ERR, _("Package has no %%description: %s\n"), - headerGetString(pkg->header, RPMTAG_NAME)); - goto errxit; - } - } - - /* Add arch, os and platform, self-provides etc for each package */ - addTargets(spec->packages); + /* Add arch for each package */ + addArch(spec); /* Check for encoding in each package unless disabled */ if (!(spec->flags & RPMSPEC_NOUTF8)) { @@ -1128,10 +1103,74 @@ static rpmSpec parseSpec(const char *specFile, rpmSpecFlags flags, return NULL; } +static rpmRC finalizeSpec(rpmSpec spec) +{ + rpmRC rc = RPMRC_FAIL; + + char *platform = rpmExpand("%{_target_platform}", NULL); + char *os = rpmExpand("%{_target_os}", NULL); + char *optflags = rpmExpand("%{optflags}", NULL); + + /* XXX Skip valid arch check if not building binary package */ + if (!(spec->flags & RPMSPEC_ANYARCH) && checkForValidArchitectures(spec)) { + goto exit; + } + + fillOutMainPackage(spec->packages->header); + /* Define group tag to something when group is undefined in main package*/ + if (!headerIsEntry(spec->packages->header, RPMTAG_GROUP)) { + headerPutString(spec->packages->header, RPMTAG_GROUP, "Unspecified"); + } + + for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next) { + headerPutString(pkg->header, RPMTAG_OS, os); + headerPutString(pkg->header, RPMTAG_PLATFORM, platform); + headerPutString(pkg->header, RPMTAG_OPTFLAGS, optflags); + + if (pkg != spec->packages) { + copyInheritedTags(pkg->header, spec->packages->header); + } + + /* Add manual dependencies early for rpmspec etc to look at */ + addPackageProvides(pkg); + + for (int i=0; idependencies[i], pkg->header); + } + + pkg->ds = rpmdsThis(pkg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL); + + if (checkForRequired(pkg->header)) { + goto exit; + } + if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) { + rpmlog(RPMLOG_ERR, _("Package has no %%description: %s\n"), + headerGetString(pkg->header, RPMTAG_NAME)); + goto exit; + } + if (checkForDuplicates(pkg->header)) { + goto exit; + } + } + + finalizeSourceHeader(spec); + + rc = RPMRC_OK; + exit: + free(platform); + free(os); + free(optflags); + return rc; +} + rpmSpec rpmSpecParse(const char *specFile, rpmSpecFlags flags, const char *buildRoot) { - return parseSpec(specFile, flags, buildRoot, 0); + rpmSpec spec = parseSpec(specFile, flags, buildRoot, 0); + if (spec && !(flags & RPMSPEC_NOFINALIZE)) { + finalizeSpec(spec); + } + return spec; } rpmRC parseGeneratedSpecs(rpmSpec spec) @@ -1158,5 +1197,11 @@ rpmRC parseGeneratedSpecs(rpmSpec spec) argvFree(argv); } free(specPattern); + if (!rc) { + rc = finalizeSpec(spec); + if (rc != RPMRC_OK) { + rpmlog(RPMLOG_ERR, "parsing failed\n"); + } + } return rc; } diff --git a/build/rpmbuild_internal.h b/build/rpmbuild_internal.h index 9021def2c1..ddf1ea7bdf 100644 --- a/build/rpmbuild_internal.h +++ b/build/rpmbuild_internal.h @@ -621,6 +621,25 @@ void * specLuaFini(rpmSpec spec); RPM_GNUC_INTERNAL void addLuaSource(const struct Source *p); + +RPM_GNUC_INTERNAL +int isMemberInEntry(Header h, const char *name, rpmTagVal tag); + +RPM_GNUC_INTERNAL +rpmRC checkForValidArchitectures(rpmSpec spec); + +RPM_GNUC_INTERNAL +int checkForRequired(Header h); + +RPM_GNUC_INTERNAL +int checkForDuplicates(Header h); + +RPM_GNUC_INTERNAL +void fillOutMainPackage(Header h); + +RPM_GNUC_INTERNAL +void copyInheritedTags(Header h, Header fromh); + #ifdef __cplusplus } #endif diff --git a/include/rpm/rpmspec.h b/include/rpm/rpmspec.h index 1ec3383123..a3239b844a 100644 --- a/include/rpm/rpmspec.h +++ b/include/rpm/rpmspec.h @@ -37,6 +37,7 @@ enum rpmSpecFlags_e { RPMSPEC_FORCE = (1 << 1), RPMSPEC_NOLANG = (1 << 2), RPMSPEC_NOUTF8 = (1 << 3), + RPMSPEC_NOFINALIZE = (1 << 4), }; typedef rpmFlags rpmSpecFlags; diff --git a/tests/data/SPECS/dynamic.spec b/tests/data/SPECS/dynamic.spec index 5dbaa8584e..ce31ed2828 100644 --- a/tests/data/SPECS/dynamic.spec +++ b/tests/data/SPECS/dynamic.spec @@ -1,12 +1,14 @@ -Summary: dynamic hello -- hello, world rpm Name: dynamic Version: 1.0 Release: 1 +BuildArch: noarch +%{?!FULLDYNAMIC: Group: Utilities License: GPL Distribution: RPM test suite. URL: http://rpm.org -BuildArch: noarch +Summary: dynamic hello -- hello, world rpm +} %description Simple rpm demonstration. @@ -21,6 +23,20 @@ echo "Q: Why?\nA: Because we can!" > FAQ mkdir -p $RPM_BUILD_ROOT/usr/local/bin echo " " > $RPM_BUILD_ROOT/usr/local/bin/hello +%{?FULLDYNAMIC: +echo "Group: Utilities" >> %{specpartsdir}/mainpkg.specpart +echo "License: GPL" >> %{specpartsdir}/mainpkg.specpart +echo "Distribution: RPM test suite." >> %{specpartsdir}/mainpkg.specpart +echo "URL: http://rpm.org" >> %{specpartsdir}/mainpkg.specpart +echo "Summary: dynamic hello -- hello, world rpm" >> %{specpartsdir}/mainpkg.specpart +%{?DOUBLESUMMARY: +echo "Summary: dynamic hello -- hello, world again" >> %{specpartsdir}/mainpkg.specpart +} +%{?WRONGTAG: +echo "LicenseToKill: True" >> %{specpartsdir}/mainpkg.specpart +} + +} echo "%package docs" >> %{specpartsdir}/docs.specpart %{?!FAIL:echo "Summary: Documentation for dynamic spec" >> %{specpartsdir}/docs.specpart} diff --git a/tests/rpmbuild.at b/tests/rpmbuild.at index 0d28354084..2851f2f66d 100644 --- a/tests/rpmbuild.at +++ b/tests/rpmbuild.at @@ -2406,6 +2406,32 @@ runroot rpm -ql /build/RPMS/noarch/dynamic-docs-1.0-1.noarch.rpm []) RPMTEST_CLEANUP +# ------------------------------ +# Check if dynamic spec generation works for main package, too +AT_SETUP([rpmbuild with dynamic spec generation for main package]) +AT_KEYWORDS([build]) +RPMDB_INIT +RPMTEST_CHECK([ + +runroot rpmbuild --define "_prefix /usr/local" -D "FULLDYNAMIC 1" -ba /data/SPECS/dynamic.spec +], +[0], +[ignore], +[ignore]) + +RPMTEST_CHECK([ + +runroot rpm -qp --qf "%{Summary}\n" /build/RPMS/noarch/dynamic-docs-1.0-1.noarch.rpm +runroot rpm -ql /build/RPMS/noarch/dynamic-docs-1.0-1.noarch.rpm +], +[0], +[Documentation for dynamic spec +/usr/local/share/doc/dynamic-docs-1.0 +/usr/local/share/doc/dynamic-docs-1.0/FAQ +], +[]) +RPMTEST_CLEANUP + # ------------------------------ # Check failing dynamic spec generation AT_SETUP([rpmbuild with dynamic spec generation fail]) @@ -2423,6 +2449,40 @@ error: parsing failed RPMTEST_CLEANUP +# ------------------------------ +# Check failing dynamic spec generation +AT_SETUP([rpmbuild with dynamic spec generation fail]) +AT_KEYWORDS([build]) +RPMDB_INIT +RPMTEST_CHECK([ + +runroot rpmbuild --quiet -D "FULLDYNAMIC 1" -D "DOUBLESUMMARY 1" -ba /data/SPECS/dynamic.spec +], +[0], +[], +[warning: line 6: second Summary +]) + +RPMTEST_CLEANUP + +# ------------------------------ +# Check failing dynamic spec generation +AT_SETUP([rpmbuild with dynamic spec generation fail]) +AT_KEYWORDS([build]) +RPMDB_INIT +RPMTEST_CHECK([ + +runroot rpmbuild --quiet -D "FULLDYNAMIC 1" -D "WRONGTAG 1" -ba /data/SPECS/dynamic.spec +], +[1], +[], +[error: line 6: Unknown tag: LicenseToKill: True +error: parsing failed +]) + +RPMTEST_CLEANUP + + # ------------------------------ # Check source name with space AT_SETUP([rpmbuild source name with space]) diff --git a/tools/rpmbuild.c b/tools/rpmbuild.c index 16fba7767a..724dee82ac 100644 --- a/tools/rpmbuild.c +++ b/tools/rpmbuild.c @@ -63,7 +63,7 @@ static struct rpmBuildArguments_s rpmBTArgs; extern int _fsm_debug; -static rpmSpecFlags spec_flags = 0; /*!< Bit(s) to control spec parsing. */ +static rpmSpecFlags spec_flags = RPMSPEC_NOFINALIZE; /*!< Bit(s) to control spec parsing. */ static int noDeps = 0; /*!< from --nodeps */ static int shortCircuit = 0; /*!< from --short-circuit */ static char buildMode = 0; /*!< Build mode (one of "btBC") */