Add support for dynamic BuildRequires

Supports new %generate_buildrequires section in the spec file which is executed
after %prep. Stdout is captured and turned into BuildRequires. These are then
checked. If they cannot be fulfilled a source package is created with all
BuildRequires and the build is terminated after that.

rpmbuild has now the following new build modes -br, -tr, -rr and exits with 11
if build requirements are not met.

That means for users:
* No %generate_buildrequires
  * rpmbuild -br is equivalent to rpmbuild -bs
  * rpmbuild -br --nodeps is equivalent to rpmbuild -bs
* %generate_buildrequires
  * rpmbuild -br will check dynamic BuildRequires
    * Satisfied → src.rpm
    * Unsatisfied → buildreqs.nosrc.rpm
  * rpmbuild -br --nodeps will always generate buildreqs.nosrc.rpm

Source packages contain
Requires: rpmlib(DynamicBuildRequires) <= 4.15.0-1
if the spec contains a %generate_buildrequires section and
Provide: rpmlib(DynamicBuildRequires) = 4.15.0-1
if the results been added to the source package.

Co-authored-by: Igor Gnatenko <i.gnatenko.brain@gmail.com>
This commit is contained in:
Florian Festi 2018-10-26 10:30:47 +02:00 committed by Panu Matilainen
parent d0754b4f2c
commit 58dcfddc37
13 changed files with 299 additions and 10 deletions

View File

@ -71,6 +71,11 @@ rpmRC doScript(rpmSpec spec, rpmBuildFlags what, const char *name,
mPost = "%{__spec_prep_post}";
mCmd = "%{__spec_prep_cmd}";
break;
case RPMBUILD_BUILDREQUIRES:
mTemplate = "%{__spec_buildrequires_template}";
mPost = "%{__spec_buildrequires_post}";
mCmd = "%{__spec_buildrequires_cmd}";
break;
case RPMBUILD_BUILD:
mTemplate = "%{__spec_build_template}";
mPost = "%{__spec_build_post}";
@ -174,6 +179,52 @@ exit:
return rc;
}
static int doBuildRequires(rpmSpec spec, int test)
{
StringBuf sb_stdout = NULL;
int outc;
ARGV_t output = NULL;
int rc = 1; /* assume failure */
if (!spec->buildrequires) {
rc = RPMRC_OK;
goto exit;
}
if ((rc = doScript(spec, RPMBUILD_BUILDREQUIRES, "%generate_buildrequires",
getStringBuf(spec->buildrequires), test, &sb_stdout)))
goto exit;
/* add results to requires of the srpm */
argvSplit(&output, getStringBuf(sb_stdout), "\n\r");
outc = argvCount(output);
if (!outc) {
goto exit;
}
for (int i = 0; i < outc; i++) {
parseRCPOT(spec, spec->sourcePackage, output[i], RPMTAG_REQUIRENAME,
0, 0, addReqProvPkg, NULL);
}
rpmdsPutToHeader(
*packageDependencies(spec->sourcePackage, RPMTAG_REQUIRENAME),
spec->sourcePackage->header);
parseRCPOT(spec, spec->sourcePackage,
"rpmlib(DynamicBuildRequires) = 4.15.0-1",
RPMTAG_PROVIDENAME, 0, RPMSENSE_FIND_PROVIDES | RPMSENSE_RPMLIB,
addReqProvPkg, NULL);
rc = RPMRC_MISSINGBUILDREQUIRES;
exit:
freeStringBuf(sb_stdout);
free(output);
return rc;
}
static rpmRC doCheckBuildRequires(rpmts ts, rpmSpec spec, int test)
{
rpmRC rc = RPMRC_OK;
@ -182,9 +233,9 @@ static rpmRC doCheckBuildRequires(rpmts ts, rpmSpec spec, int test)
if (ps) {
rpmlog(RPMLOG_ERR, _("Failed build dependencies:\n"));
rpmpsPrint(NULL, ps);
}
if (ps != NULL)
rc = RPMRC_MISSINGBUILDREQUIRES;
}
rpmpsFree(ps);
return rc;
}
@ -230,6 +281,14 @@ static rpmRC buildSpec(rpmts ts, BTA_t buildArgs, rpmSpec spec, int what)
}
} else {
int didBuild = (what & (RPMBUILD_PREP|RPMBUILD_BUILD|RPMBUILD_INSTALL));
int sourceOnly = ((what & RPMBUILD_PACKAGESOURCE) &&
!(what & (RPMBUILD_BUILD|RPMBUILD_INSTALL|RPMBUILD_PACKAGEBINARY)));
if (!spec->buildrequires && sourceOnly) {
/* don't run prep if not needed for source build */
/* with(out) dynamic build requires*/
what &= ~(RPMBUILD_PREP);
}
if ((what & RPMBUILD_CHECKBUILDREQUIRES) &&
(rc = doCheckBuildRequires(ts, spec, test)))
@ -240,6 +299,29 @@ static rpmRC buildSpec(rpmts ts, BTA_t buildArgs, rpmSpec spec, int what)
getStringBuf(spec->prep), test, NULL)))
goto exit;
if (what & RPMBUILD_BUILDREQUIRES)
rc = doBuildRequires(spec, test);
if ((what & RPMBUILD_CHECKBUILDREQUIRES) &&
(rc == RPMRC_MISSINGBUILDREQUIRES))
rc = doCheckBuildRequires(ts, spec, test);
if (rc == RPMRC_MISSINGBUILDREQUIRES) {
if (what & RPMBUILD_DUMPBUILDREQUIRES) {
/* Create buildreqs package */
char *nvr = headerGetAsString(spec->packages->header, RPMTAG_NVR);
rasprintf(&spec->sourceRpmName, "%s.buildreqs.nosrc.rpm", nvr);
free(nvr);
/* free sources to not include them in the buildreqs package */
spec->sources = freeSources(spec->sources);
spec->numSources = 0;
missing_buildreqs = 1;
what = RPMBUILD_PACKAGESOURCE;
} else {
rc = RPMRC_OK;
}
} else if (rc) {
goto exit;
}
if ((what & RPMBUILD_BUILD) &&
(rc = doScript(spec, RPMBUILD_BUILD, "%build",
getStringBuf(spec->build), test, NULL)))

View File

@ -782,6 +782,10 @@ rpmRC packageSources(rpmSpec spec, char **cookie)
headerPutUint32(sourcePkg->header, RPMTAG_BUILDTIME, &(spec->buildTime), 1);
headerPutUint32(sourcePkg->header, RPMTAG_SOURCEPACKAGE, &one, 1);
if (spec->buildrequires) {
(void) rpmlibNeedsFeature(sourcePkg, "DynamicBuildRequires", "4.15.0-1");
}
/* XXX this should be %_srpmdir */
{ sourcePkg->filename = rpmGetPath("%{_srcrpmdir}/", spec->sourceRpmName,NULL);
char *pkgcheck = rpmExpand("%{?_build_pkgcheck_srpm} ", sourcePkg->filename, NULL);

View File

@ -43,6 +43,7 @@ static const struct PartRec {
} partList[] = {
{ PART_PREAMBLE, LEN_AND_STR("%package")},
{ PART_PREP, LEN_AND_STR("%prep")},
{ PART_BUILDREQUIRES, LEN_AND_STR("%generate_buildrequires")},
{ PART_BUILD, LEN_AND_STR("%build")},
{ PART_INSTALL, LEN_AND_STR("%install")},
{ PART_CHECK, LEN_AND_STR("%check")},
@ -906,6 +907,10 @@ static rpmSpec parseSpec(const char *specFile, rpmSpecFlags flags,
case PART_PREP:
parsePart = parsePrep(spec);
break;
case PART_BUILDREQUIRES:
parsePart = parseSimpleScript(spec, "%generate_buildrequires",
&(spec->buildrequires));
break;
case PART_BUILD:
parsePart = parseSimpleScript(spec, "%build", &(spec->build));
break;

View File

@ -19,9 +19,15 @@ int addReqProv(Package pkg, rpmTagVal tagN,
dsp = packageDependencies(pkg, tagN);
/* rpmlib() dependency sanity: only requires permitted, ensure sense bit */
/* rpmlib() dependency sanity:
* - Provides are permitted only for source packages
* - Otherwise only requires
* - Ensure sense bit
*/
if (rstreqn(N, "rpmlib(", sizeof("rpmlib(")-1)) {
if (tagN != RPMTAG_REQUIRENAME) return 1;
if (tagN != RPMTAG_REQUIRENAME &&
(tagN == RPMTAG_PROVIDENAME && !(Flags & RPMSENSE_RPMLIB)))
return 1;
Flags |= RPMSENSE_RPMLIB;
}

View File

@ -36,6 +36,8 @@ enum rpmBuildFlags_e {
RPMBUILD_FILE_LIST = (1 << 17), /*!< rpmSpecPkgGetSection: %files */
RPMBUILD_POLICY = (1 << 18), /*!< rpmSpecPkgGetSection: %policy */
RPMBUILD_CHECKBUILDREQUIRES = (1 << 19), /*!< Check %%buildrequires. */
RPMBUILD_BUILDREQUIRES = (1 << 20), /*!< Execute %%buildrequires. */
RPMBUILD_DUMPBUILDREQUIRES = (1 << 21), /*!< Write buildrequires.nosrc.rpm. */
RPMBUILD_NOBUILD = (1 << 31) /*!< Don't execute or package. */
};
@ -109,7 +111,9 @@ rpmds rpmSpecDS(rpmSpec spec, rpmTagVal tag);
* @param ts rpm transaction set
* @param spec spec file control structure
* @param buildArgs build arguments
* @return RPMRC_OK on success, RPMRC_MISSINGBUILDREQUIRES or 1
* @return 0 on success, 1 on build error,
* RPMRC_MISSINGBUILDREQUIRES on missing build
* requirements
*/
int rpmSpecBuild(rpmts ts, rpmSpec spec, BTA_t buildArgs);

View File

@ -140,6 +140,7 @@ struct rpmSpec_s {
rpmstrPool pool;
StringBuf prep; /*!< %prep scriptlet. */
StringBuf buildrequires; /*!< %buildrequires scriptlet. */
StringBuf build; /*!< %build scriptlet. */
StringBuf install; /*!< %install scriptlet. */
StringBuf check; /*!< %check scriptlet. */
@ -237,7 +238,8 @@ typedef enum rpmParseState_e {
PART_EMPTY = 39+PART_BASE, /*!< */
PART_PATCHLIST = 40+PART_BASE, /*!< */
PART_SOURCELIST = 41+PART_BASE, /*!< */
PART_LAST = 42+PART_BASE /*!< */
PART_BUILDREQUIRES = 42+PART_BASE, /*!< */
PART_LAST = 43+PART_BASE /*!< */
} rpmParseState;

View File

@ -11,13 +11,13 @@ rpmbuild \- Build RPM Package(s)
.PP
\fBrpmbuild\fR {\fB-ba|-bb|-bp|-bc|-bi|-bl|-bs\fR} [\fBrpmbuild-options\fR] \fB\fISPECFILE\fB\fR\fI ...\fR
\fBrpmbuild\fR {\fB-ba|-bb|-bp|-bc|-bi|-bl|-bs|-br\fR} [\fBrpmbuild-options\fR] \fB\fISPECFILE\fB\fR\fI ...\fR
\fBrpmbuild\fR {\fB-ra|-rb|-rp|-rc|-ri|-rl|-rs\fR} [\fBrpmbuild-options\fR] \fB\fISOURCEPACKAGE\fB\fR\fI ...\fR
\fBrpmbuild\fR {\fB-ra|-rb|-rp|-rc|-ri|-rl|-rs|-rr\fR} [\fBrpmbuild-options\fR] \fB\fISOURCEPACKAGE\fB\fR\fI ...\fR
\fBrpmbuild\fR {\fB-ta|-tb|-tp|-tc|-ti|-tl|-ts\fR} [\fBrpmbuild-options\fR] \fB\fITARBALL\fB\fR\fI ...\fR
\fBrpmbuild\fR {\fB-ta|-tb|-tp|-tc|-ti|-tl|-ts|-tr\fR} [\fBrpmbuild-options\fR] \fB\fITARBALL\fB\fR\fI ...\fR
@ -162,6 +162,9 @@ exists.
.TP
\fB-bs\fR
Build just the source package.
.TP
\fB-br\fR
Build just the source package - but calculate and include the dynamic build requires.
.PP
The following options may also be used:
.TP

View File

@ -1249,6 +1249,9 @@ static const struct rpmlibProvides_s rpmlibProvides[] = {
{ "rpmlib(RichDependencies)", "4.12.0-1",
( RPMSENSE_EQUAL),
N_("support for rich dependencies.") },
{ "rpmlib(DynamicBuildRequires)", "4.15.0-1",
(RPMSENSE_RPMLIB|RPMSENSE_EQUAL),
N_("support for dynamic buildrequires.") },
#ifdef HAVE_ZSTD
{ "rpmlib(PayloadIsZstd)", "5.4.18-1",
(RPMSENSE_RPMLIB|RPMSENSE_EQUAL),

View File

@ -846,6 +846,21 @@ package or when debugging this package.\
#%{__spec_prep_post}\
#%{nil}
%__spec_buildrequires_shell %{___build_shell}
%__spec_buildrequires_args %{___build_args}
%__spec_buildrequires_cmd %{___build_cmd}
%__spec_buildrequires_pre %{___build_pre}
%__spec_buildrequires_body %{___build_body}
%__spec_buildrequires_post %{___build_post}
%__spec_buildrequires_template #!%{__spec_buildrequires_shell}\
%{__spec_buildrequires_pre}\
%{nil}
#%{__spec_buildrequires_body}\
#%{__spec_buildrequires_post}\
#%{nil}
%__spec_build_shell %{___build_shell}
%__spec_build_args %{___build_args}
%__spec_build_cmd %{___build_cmd}

View File

@ -38,6 +38,7 @@ static struct rpmBuildArguments_s rpmBTArgs;
#define POPT_BL 0x626c
#define POPT_BP 0x6270
#define POPT_BS 0x6273
#define POPT_BR 0x6272
#define POPT_RA 0x4261
#define POPT_RB 0x4262
#define POPT_RC 0x4263
@ -45,6 +46,7 @@ static struct rpmBuildArguments_s rpmBTArgs;
#define POPT_RL 0x426c
#define POPT_RP 0x4270
#define POPT_RS 0x4273
#define POPT_RR 0x4272
#define POPT_TA 0x7461
#define POPT_TB 0x7462
#define POPT_TC 0x7463
@ -52,6 +54,7 @@ static struct rpmBuildArguments_s rpmBTArgs;
#define POPT_TL 0x746c
#define POPT_TP 0x7470
#define POPT_TS 0x7473
#define POPT_TR 0x7472
extern int _fsm_debug;
@ -81,6 +84,7 @@ static void buildArgCallback( poptContext con,
case POPT_BL:
case POPT_BP:
case POPT_BS:
case POPT_BR:
case POPT_RA:
/* case POPT_RB: same value as POPT_REBUILD */
case POPT_RC:
@ -88,6 +92,7 @@ static void buildArgCallback( poptContext con,
case POPT_RL:
case POPT_RP:
case POPT_RS:
case POPT_RR:
case POPT_TA:
case POPT_TB:
case POPT_TC:
@ -95,6 +100,7 @@ static void buildArgCallback( poptContext con,
case POPT_TL:
case POPT_TP:
case POPT_TS:
case POPT_TR:
if (opt->val == POPT_BS || opt->val == POPT_TS)
noDeps = 1;
if (buildMode == '\0' && buildChar == '\0') {
@ -156,6 +162,9 @@ static struct poptOption rpmBuildPoptTable[] = {
{ "bs", 0, POPT_ARGFLAG_ONEDASH, 0, POPT_BS,
N_("build source package only from <specfile>"),
N_("<specfile>") },
{ "br", 0, POPT_ARGFLAG_ONEDASH, 0, POPT_BR,
N_("build source package only from <specfile> - calculate dynamic build requires"),
N_("<specfile>") },
{ "rp", 0, POPT_ARGFLAG_ONEDASH, 0, POPT_RP,
N_("build through %prep (unpack sources and apply patches) from <source package>"),
@ -178,6 +187,9 @@ static struct poptOption rpmBuildPoptTable[] = {
{ "rs", 0, POPT_ARGFLAG_ONEDASH, 0, POPT_RS,
N_("build source package only from <source package>"),
N_("<source package>") },
{ "rr", 0, POPT_ARGFLAG_ONEDASH, 0, POPT_RR,
N_("build source package only from <source package> - calculate dynamic build requires"),
N_("<source package>") },
{ "tp", 0, POPT_ARGFLAG_ONEDASH, 0, POPT_TP,
N_("build through %prep (unpack sources and apply patches) from <tarball>"),
@ -200,7 +212,9 @@ static struct poptOption rpmBuildPoptTable[] = {
{ "ts", 0, POPT_ARGFLAG_ONEDASH, 0, POPT_TS,
N_("build source package only from <tarball>"),
N_("<tarball>") },
{ "tr", 0, POPT_ARGFLAG_ONEDASH, 0, POPT_TR,
N_("build source package only from <tarball> - calculate dynamic build requires"),
N_("<tarball>") },
{ "rebuild", '\0', 0, 0, POPT_REBUILD,
N_("build binary package from <source package>"),
N_("<source package>") },
@ -623,7 +637,9 @@ int main(int argc, char *argv[])
break;
case 'c':
ba->buildAmount |= RPMBUILD_BUILD;
ba->buildAmount |= RPMBUILD_BUILDREQUIRES;
if (!noDeps) {
ba->buildAmount |= RPMBUILD_DUMPBUILDREQUIRES;
ba->buildAmount |= RPMBUILD_CHECKBUILDREQUIRES;
}
if ((buildChar == 'c') && shortCircuit)
@ -634,6 +650,12 @@ int main(int argc, char *argv[])
case 'l':
ba->buildAmount |= RPMBUILD_FILECHECK;
break;
case 'r':
ba->buildAmount |= RPMBUILD_PREP;
ba->buildAmount |= RPMBUILD_BUILDREQUIRES;
ba->buildAmount |= RPMBUILD_DUMPBUILDREQUIRES;
if (!noDeps)
ba->buildAmount |= RPMBUILD_CHECKBUILDREQUIRES;
case 's':
ba->buildAmount |= RPMBUILD_PACKAGESOURCE;
break;

Binary file not shown.

View File

@ -0,0 +1,46 @@
Summary: Test dynamic BuildRequires
Name: buildrequires
Version: 1.0
Release: 1
Group: Utilities
License: GPL
Distribution: RPM test suite.
Source0: buildrequires-1.0.tar.gz
Prefix: /usr
%description
Simple build requires demonstration.
%prep
%setup -q
%generate_buildrequires
echo foo-bar = 2.0
cat buildrequires.txt
%build
make
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/usr/local/bin
make DESTDIR=$RPM_BUILD_ROOT install
%clean
rm -rf $RPM_BUILD_ROOT
%pre
%post
%preun
%postun
%files
%defattr(-,root,root)
%doc FAQ
#%readme README
#%license COPYING
%attr(0751,root,root) /usr/local/bin/hello

View File

@ -1406,3 +1406,100 @@ No hello.debug
],
[ignore])
AT_CLEANUP
# ------------------------------
# Check dynamic build requires
AT_SETUP([dynamic build requires rpmbuild -bs])
AT_KEYWORDS([build])
AT_CHECK([
rm -rf ${TOPDIR}
AS_MKDIR_P(${TOPDIR}/SOURCES)
cp "${abs_srcdir}"/data/SOURCES/buildrequires-1.0.tar.gz ${TOPDIR}/SOURCES
run rpmbuild \
--quiet -bs "${abs_srcdir}"/data/SPECS/buildrequires.spec
runroot rpm -qpR /build/SRPMS/buildrequires-1.0-1.src.rpm
],
[0],
[rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(DynamicBuildRequires) <= 4.15.0-1
rpmlib(FileDigests) <= 4.6.0-1
],
[ignore])
AT_CLEANUP
# ------------------------------
# Check dynamic build requires
AT_SETUP([rpmbuild -br])
AT_KEYWORDS([build])
AT_CHECK([
rm -rf ${TOPDIR}
AS_MKDIR_P(${TOPDIR}/SOURCES)
cp "${abs_srcdir}"/data/SOURCES/buildrequires-1.0.tar.gz ${TOPDIR}/SOURCES
run rpmbuild \
-br --quiet "${abs_srcdir}"/data/SPECS/buildrequires.spec
],
[11],
[],
[error: Failed build dependencies:
(bar = 3.4 or bar = 3.5) is needed by buildrequires-1.0-1.x86_64
foo > 1.3 is needed by buildrequires-1.0-1.x86_64
foo-bar = 2.0 is needed by buildrequires-1.0-1.x86_64
],
)
AT_CLEANUP
# ------------------------------
# Check dynamic build requires
AT_SETUP([rpmbuild -ba])
AT_KEYWORDS([build])
AT_CHECK([
rm -rf ${TOPDIR}
AS_MKDIR_P(${TOPDIR}/SOURCES)
cp "${abs_srcdir}"/data/SOURCES/buildrequires-1.0.tar.gz ${TOPDIR}/SOURCES
run rpmbuild \
-ba --quiet "${abs_srcdir}"/data/SPECS/buildrequires.spec
],
[11],
[],
[error: Failed build dependencies:
(bar = 3.4 or bar = 3.5) is needed by buildrequires-1.0-1.x86_64
foo > 1.3 is needed by buildrequires-1.0-1.x86_64
foo-bar = 2.0 is needed by buildrequires-1.0-1.x86_64
],
)
AT_CLEANUP
# ------------------------------
# Check dynamic build requires
AT_SETUP([rpmbuild -br --nodeps])
AT_KEYWORDS([build])
AT_CHECK([
rm -rf ${TOPDIR}
AS_MKDIR_P(${TOPDIR}/SOURCES)
cp "${abs_srcdir}"/data/SOURCES/buildrequires-1.0.tar.gz ${TOPDIR}/SOURCES
run rpmbuild \
-br --quiet --nodeps "${abs_srcdir}"/data/SPECS/buildrequires.spec
runroot rpm -qpR /build/SRPMS/buildrequires-1.0-1.buildreqs.nosrc.rpm
],
[0],
[(bar = 3.4 or bar = 3.5)
foo > 1.3
foo-bar = 2.0
rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(DynamicBuildRequires) <= 4.15.0-1
rpmlib(FileDigests) <= 4.6.0-1
rpmlib(RichDependencies) <= 4.12.0-1
],
[ignore])
AT_CLEANUP