Skip to content

Commit

Permalink
Add Dynamic Spec generation
Browse files Browse the repository at this point in the history
Read in *.specpart files from %{specpartsdir} aka $RPM_SPECPARTS_DIR
after the %install script. This allows the build process to add sub
packages to the build. In the future more changes to the packages may be
possible - like amending package declarations.

The %{specpartsdir} is created by the %setup pseudo macro inside of the
buildsubdir. It's presence allows build scripts to check if this feature
is supported in the running rpmbuild instance.
  • Loading branch information
ffesti committed Nov 4, 2022
1 parent 05e784b commit ab78270
Show file tree
Hide file tree
Showing 13 changed files with 200 additions and 3 deletions.
4 changes: 4 additions & 0 deletions build/build.c
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,10 @@ static rpmRC buildSpec(rpmts ts, BTA_t buildArgs, rpmSpec spec, int what)
getStringBuf(spec->install), test, sbp)))
goto exit;

if (((what & RPMBUILD_INSTALL) || (what & RPMBUILD_PACKAGEBINARY)) &&
(rc = parseGeneratedSpecs(spec)))
goto exit;

if ((what & RPMBUILD_CHECK) &&
(rc = doScript(spec, RPMBUILD_CHECK, "%check",
getStringBuf(spec->check), test, sbp)))
Expand Down
9 changes: 9 additions & 0 deletions build/parsePrep.c
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,15 @@ static int doSetupMacro(rpmSpec spec, const char *line)
free(buf);
}

/* mkdir for dynamic specparts */
buf = rpmExpand("%{__mkdir} SPECPARTS", NULL);
appendBuf(spec, buf, 1);
free(buf);

buf = rpmGenPath("%{u2p:%{_builddir}}", "%{buildsubdir}", "SPECPARTS");
rpmPushMacro(spec->macros, "specpartsdir", NULL, buf, RMIL_SPEC);
free(buf);

appendBuf(spec, getStringBuf(after), 0);

/* Fix the permissions of the setup build tree */
Expand Down
29 changes: 28 additions & 1 deletion build/parseSpec.c
Original file line number Diff line number Diff line change
Expand Up @@ -1083,7 +1083,8 @@ static rpmSpec parseSpecSection(rpmSpec spec, int secondary)
return spec;

errxit:
rpmSpecFree(spec);
if (!secondary)
rpmSpecFree(spec);
return NULL;
}

Expand Down Expand Up @@ -1156,3 +1157,29 @@ rpmSpec rpmSpecParse(const char *specFile, rpmSpecFlags flags,
{
return parseSpec(specFile, flags, buildRoot, 0);
}

rpmRC parseGeneratedSpecs(rpmSpec spec)
{
ARGV_t argv = NULL;
int argc = 0;
int i;
rpmRC rc = RPMRC_OK;

char * specPattern = rpmGenPath("%{specpartsdir}", NULL, "*.specpart");
if (rpmGlob(specPattern, &argc, &argv) == 0) {
for (i = 0; i < argc; i++) {
rpmlog(RPMLOG_NOTICE, "Reading %s\n", argv[i]);
pushOFI(spec, argv[i]);
snprintf(spec->fileStack->readBuf, spec->fileStack->readBufLen,
"# Spec part read from %s\n\n", argv[i]);
if (!parseSpecSection(spec, 1)) {
rpmlog(RPMLOG_ERR, "parsing failed\n");
rc = RPMRC_FAIL;
break;
}
}
argvFree(argv);
}
free(specPattern);
return rc;
}
9 changes: 9 additions & 0 deletions build/rpmbuild_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,15 @@ RPM_GNUC_INTERNAL
rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char * field, rpmTagVal tagN,
int index, rpmsenseFlags tagflags, addReqProvFunction cb, void *cbdata);

/** \ingroup rpmbuild
* Parse spec piece generated during build
*
* @param spec spec file control structure
* @return RPMRC_OK on success
*/
RPM_GNUC_INTERNAL
rpmRC parseGeneratedSpecs(rpmSpec spec);

/** \ingroup rpmbuild
* Run a build script, assembled from spec file scriptlet section.
*
Expand Down
1 change: 1 addition & 0 deletions docs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ set(refman
manual/dependencies.md
manual/dependency_generators.md
manual/devel_documentation.md
manual/dynamic_specs.md
manual/file_triggers.md
manual/format.md
manual/hregions.md
Expand Down
1 change: 1 addition & 0 deletions docs/manual/buildprocess.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ title: rpm.org - Package Build Process
* %conf
* %build
* %install
* Read [dynamic spec parts](dynamic_specs.md)
* %check - if present
* Process files
* Turn %files lines into actual files (evaluate globs)
Expand Down
27 changes: 27 additions & 0 deletions docs/manual/dynamic_specs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
layout: default
title: rpm.org - Package Build Process
---
# Dynamic Spec Generation

Since rpm 4.19 RPM supports parsing dynamically generated specs. This
allows the build scripts (**%build** or **%install**) to create parts
of the spec file. The parts are read in and parsed after **%install**
and before **%check**. Because of this they obviously can't contain
the build scripts but are intended to create sub packages based on the
build results.

The files need to be placed in the **%{specpartsdir}** (also available
as **$RPM_SPECPARTS_DIR** in the build scripts) and have a
**.specpart** postfix. The directory is created by **%setup** in the
**buildsubdir**. Scripts must fail if it is not present. They should
give an error message that dynamic spec generation is not supported on
the given RPM version if the directory is not found or switch to an
alternative that does not require the feature.

Generally the specparts should be generated by separate scripts and not
directly from the build scripts themselves. This can be done for
testing but one needs to be careful that the spec syntax is not
already parsed when the spec file is read. Avoid Spec directives or
sections starting right at the beginning of the line as they will be
interpreted right away.
2 changes: 1 addition & 1 deletion docs/manual/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ title: rpm.org - RPM Reference Manual
* [Conditional Builds](conditionalbuilds.md)
* [Relocatable Packages](relocatable.md)
* [Multiple build areas](multiplebuilds.md)

* [Dynamic Spec Generation](dynamic_specs.md)

## Developer Information

Expand Down
3 changes: 2 additions & 1 deletion macros.in
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,8 @@ package or when debugging this package.\
RPM_ARCH=\"%{_arch}\"\
RPM_OS=\"%{_os}\"\
RPM_BUILD_NCPUS=\"%{_smp_build_ncpus}\"\
export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS RPM_BUILD_NCPUS\
RPM_SPECPARTS_DIR=\"%{u2p:%{specpartsdir}}\"\
export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS RPM_BUILD_NCPUS RPM_SPECPARTS_DIR\
RPM_DOC_DIR=\"%{_docdir}\"\
export RPM_DOC_DIR\
RPM_PACKAGE_NAME=\"%{NAME}\"\
Expand Down
37 changes: 37 additions & 0 deletions tests/data/SPECS/dynamic-fail.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Summary: dynamic hello -- hello, world rpm
Name: dynamic
Version: 1.0
Release: 1
Group: Utilities
License: GPL
Distribution: RPM test suite.
URL: http://rpm.org
BuildArch: noarch

%description
Simple rpm demonstration.

%prep
%setup -q -T -c

%build
echo "Q: Why?\nA: Because we can!" > FAQ

%install
mkdir -p $RPM_BUILD_ROOT/usr/local/bin
echo " " > $RPM_BUILD_ROOT/usr/local/bin/hello


echo "%package docs" >> %{specpartsdir}/docs.specpart
echo "Summary: Documentation for dynamic spec" >> %{specpartsdir}/docs.specpart
echo "BuildArch: noarch" >> %{specpartsdir}/docs.specpart
echo "%files docs" >> %{specpartsdir}/docs.specpart
echo "%doc FAQ" >> %{specpartsdir}/docs.specpart

%files
%defattr(-,root,root)
%attr(0751,root,root) /usr/local/bin/hello

%changelog
* Mon Oct 24 2022 Florian Festi <ffesti@redhat.com>
- create.
39 changes: 39 additions & 0 deletions tests/data/SPECS/dynamic.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Summary: dynamic hello -- hello, world rpm
Name: dynamic
Version: 1.0
Release: 1
Group: Utilities
License: GPL
Distribution: RPM test suite.
URL: http://rpm.org
BuildArch: noarch

%description
Simple rpm demonstration.

%prep
%setup -q -T -c

%build
echo "Q: Why?\nA: Because we can!" > FAQ

%install
mkdir -p $RPM_BUILD_ROOT/usr/local/bin
echo " " > $RPM_BUILD_ROOT/usr/local/bin/hello


echo "%package docs" >> %{specpartsdir}/docs.specpart
echo "Summary: Documentation for dynamic spec" >> %{specpartsdir}/docs.specpart
echo "BuildArch: noarch" >> %{specpartsdir}/docs.specpart
echo "%description docs" >> %{specpartsdir}/docs.specpart
echo "Test for dynamically generated spec files" >> %{specpartsdir}/docs.specpart
echo "%files docs" >> %{specpartsdir}/docs.specpart
echo "%doc FAQ" >> %{specpartsdir}/docs.specpart

%files
%defattr(-,root,root)
%attr(0751,root,root) /usr/local/bin/hello

%changelog
* Mon Oct 24 2022 Florian Festi <ffesti@redhat.com>
- create.
41 changes: 41 additions & 0 deletions tests/rpmbuild.at
Original file line number Diff line number Diff line change
Expand Up @@ -2272,3 +2272,44 @@ runroot rpmbuild \
],
[ignore])
AT_CLEANUP

# ------------------------------
# Check if dynamic spec generation works
AT_SETUP([rpmbuild with dynamic spec generation])
AT_KEYWORDS([build])
RPMDB_INIT
AT_CHECK([

runroot rpmbuild -ba /data/SPECS/dynamic.spec
],
[0],
[ignore],
[ignore])

AT_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
],
[])
AT_CLEANUP

# ------------------------------
# Check failing dynamic spec generation
AT_SETUP([rpmbuild with dynamic spec generation fail])
AT_KEYWORDS([build])
RPMDB_INIT
AT_CHECK([

runroot rpmbuild -ba /data/SPECS/dynamic-fail.spec
],
[1],
[ignore],
[ignore])

AT_CLEANUP
1 change: 1 addition & 0 deletions tests/rpmspec.at
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ if [ $STATUS -ne 0 ]; then
exit $STATUS
fi
cd 'hello-1.0'
/usr/bin/mkdir SPECPARTS
/usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
echo "Patch #0 (hello-1.0-modernize.patch):"
/usr/bin/patch --no-backup-if-mismatch -f -p1 -b --suffix .modernize --fuzz=0 < /build/SOURCES/hello-1.0-modernize.patch
Expand Down

0 comments on commit ab78270

Please sign in to comment.