Skip to content

rpm: Install BuildRequires from the spec - #397

Merged
martinpitt merged 7 commits into
amutable:mainfrom
daandemeyer:in-place-buildrequires
Nov 12, 2026
Merged

martinpitt merged 7 commits into
amutable:mainfrom
daandemeyer:in-place-buildrequires

Conversation

@daandemeyer

@daandemeyer daandemeyer commented Nov 10, 2026 •

Copy link
Copy Markdown
Member

A hand-written rpm.package, or an in-place build, only gets the BuildRequires that its BUCK file lists.
systemd lists 66 of them by hand, and the list goes stale whenever the pin moves.

Let's run rpmbuild -bd on the spec in the buildroot with the same options as the build, and install the
BuildRequires that it reports missing as an extra layer. rpm runs %generate_buildrequires only when the
static BuildRequires are installed, so a spec with the script lists those in build_requires. Imported
packages keep using their recorded BuildRequires, since rpm.branch() derives their buildroot_packages from
them.

@daandemeyer
daandemeyer force-pushed the in-place-buildrequires branch from 728907f to 2d19e78 Compare November 10, 2026 12:25
@daandemeyer daandemeyer changed the title rpm: Install the BuildRequires of the spec that a build parses rpm: Install BuildRequires from the spec Nov 10, 2026
@daandemeyer
daandemeyer force-pushed the in-place-buildrequires branch from 2d19e78 to 70f2928 Compare November 10, 2026 12:39
@martinpitt

Copy link
Copy Markdown
Member

For the first round, bot-to-bot review: pr-in-place-buildrequires-review.md

Sorry, in a long meeting, not much human review on that report -- can do in ~ 1 h.

@daandemeyer
daandemeyer force-pushed the in-place-buildrequires branch from 70f2928 to bbe21b6 Compare November 12, 2026 08:15

@martinpitt martinpitt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH, my gut doesn't like this. It's a lot of brittle complexity that doesn't fit into a human brain any more, all for the relatively corner use case of building a local image with a temporarily mounted systemd tree.

This does not and cannot replace the rpm imports with its srcpkg.json, as regardless of how expensive/powerful we make this machinery, it cannot feed back into the build graph (buck just doesn't work that way, which is why we introduced the "record package metadata in json files" concept in the first place). So that code path has to remain.

AFAIUI, our primary use case/motivation is to build against a local systemd, right? That doesn't use %generate_buildrequires, but supporting that is responsible for a lot of complexity here, mostlythe dynamic action loop from bbe21b6 and the PackageInstaller split from 734bb08. With no generators, the query could also become rpmspec -q --buildrequires that runs in the buildroot.

Claude estimates that about 400 lines. Could we drop that and refuse local packages with %generate_buildrequires? Feels too much of a YAGNI case to me. (As an old Debianite, I openly admit that I hate that concept: it makes reasoning about build graphs unpredictable and hard, and building packages a PITA).

This isn't a veto, just some venting -- you stared at the requirements for this for a long time, and I trust you to know whether it's worth it.

round 2 bot findings

Comment thread package_system/rpm/plan.py Resolved
Comment thread package_system/rpm/plan_test.py Resolved
Comment thread examples/package/rpm/BUCK Resolved
build.py stages the spec and builds the rpmbuild command line inline. The
BuildRequires query needs to do the same with the same spec fields, so
let's move both into rpmbuild.py. Pinned packaging comes in as a directory
in sources, and a strict_subpackages flag replaces the packaging field.
The copy into SOURCES runs outside the chroot and follows symlinks, so we
now refuse a symlink in the path of the spec or of a source, and at the top
of a source directory.

Signed-off-by: Daan De Meyer <daan@amutable.com>
We write the frozen copy of an in-place spec over the spec in the checkout,
so that rpm finds the files that the spec includes. rpm resolves %include
from its working directory and not from the spec, so the copy doesn't need
to be there. Let's write the copy to SPECS like any other spec. Staging
then never writes into the checkout.

Signed-off-by: Daan De Meyer <daan@amutable.com>
Nothing reads the BuildRequires of the spec that a build uses, so the
buildroot only gets what the BUCK file lists. Let's add a driver that
stages the spec like build.py, runs rpmbuild -bd in the buildroot with the
same arguments as the build, and writes the BuildRequires that the
buildroot lacks. Unlike -br, -bd doesn't check that the Source and Patch
files exist, and an in-place build doesn't need the Source archive.

Signed-off-by: Daan De Meyer <daan@amutable.com>
rpm.branch() takes buildroot_packages from the BuildRequires of all
architectures, but build_requires only has those of the current one. A
package can then end up with buildroot_packages and nothing to install,
and the solve fails with "a solve needs at least one install spec". Let's
skip the solve when there's nothing to install.

Signed-off-by: Daan De Meyer <daan@amutable.com>
undelivered() checks that each install spec ended up in the transaction.
resolve_pkg_spec() doesn't evaluate rich dependencies, so for (A if B) it
reports A as missing when B isn't installed. Let's skip rich dependencies
in that check. The solve itself still fails when it can't satisfy one.

Signed-off-by: Daan De Meyer <daan@amutable.com>
A build with in_place_spec or in_place_packaging only gets the
BuildRequires that the BUCK file lists. systemd lists 66 of them by hand,
and the list goes stale whenever the pin moves. Let's query the spec and
install the missing ones as an extra layer. The solve reads them from the
query's JSON file and installs nothing when the list is empty. Imported
packages keep their recorded BuildRequires, because rpm.branch() derives
buildroot_packages from them.

Signed-off-by: Daan De Meyer <daan@amutable.com>
Only an in-place build queries its spec. A hand-written rpm.package that
builds from an archive still only installs the build_requires from its
BUCK file, which goes out of sync with the spec. Let's query the spec for
every build of a package that wasn't imported.

Signed-off-by: Daan De Meyer <daan@amutable.com>
@daandemeyer
daandemeyer force-pushed the in-place-buildrequires branch from bbe21b6 to 0a5a077 Compare November 12, 2026 13:16
@daandemeyer

Copy link
Copy Markdown
Member Author

@martinpitt I dropped the dynamic buildrequires stuff again. Note that this wasn't just for local builds of systemd. I think any project using tine should be able to build one or two rpms locally without having to pull in the entire importer without having to list all buildrequires one by one. I dislike the concept as well but unfortunately it's a thing rpm supports :/. Anyway, dropped for now, can bring this up again when I actually have a use case for it.

@martinpitt martinpitt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack, thanks! I'll close my eyes on the other minor issues, let's go!

@martinpitt
martinpitt merged commit 5927c17 into amutable:main Nov 12, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants