If you want to build an RPM from source and install it within a mkosi
image, you can do that with mkosi itself without using mock. The steps
required are as follows:
BuildRequires dependencies in the build overlayBuildRequires dependencies in the build overlayrpmbuildIn the following examples, we’ll use mkosi itself and its Fedora RPM spec as an example.
To keep things snappy, we execute the first 3 steps in a prepare
script so that they’re cached on subsequent runs of mkosi if the
Incremental= setting is enabled.
First, we need access to the upstream sources and the RPM spec and
related files. These can be mounted into the current working directory
when running mkosi scripts by using the BuildSources= setting. For
example, in mkosi.local.conf, we could have the following settings:
[Build]
BuildSources=../mkosi:mkosi
../fedora/mkosi:mkosi/rpm
BuildSourcesEphemeral=yes
Which instructs mkosi to mount the local version of the mkosi upstream
repository at ../mkosi to mkosi in the current working directory
when running mkosi. The Fedora RPM spec is mounted at mkosi/rpm.
We enable the BuildSourcesEphemeral= option as rpmbuild will write
quite a few files to the source directory as part of building the rpm
which we don’t want to remain there after the build finishes.
We use rpmspec and rpmbuild, but these do not really support running
from outside of the image that the RPM is being built in, so we have to
make sure they’re available inside the image by adding the following to
mkosi.conf:
[Content]
Packages=rpm-build
# If you don't want rpm-build in the final image.
RemovePackages=rpm-build
The prepare script mkosi.prepare then looks as follows:
#!/bin/sh
set -e
if [ "$1" = "final" ]; then
exit 0
fi
mkosi-chroot \
env --chdir=mkosi \
rpmspec \
--query \
--buildrequires \
--define "_topdir /var/tmp" \
--define "_sourcedir $PWD/mkosi/rpm" \
rpm/mkosi.spec |
sort --unique |
tee /tmp/buildrequires |
xargs --delimiter '\n' mkosi-install
until mkosi-chroot \
env --chdir=mkosi \
rpmbuild \
-bd \
--noprep \
--build-in-place \
--define "_topdir /var/tmp" \
--define "_sourcedir $PWD/mkosi/rpm" \
--define "_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" \
rpm/mkosi.spec
do
EXIT_STATUS=$?
if [ $EXIT_STATUS -ne 11 ]; then
exit $EXIT_STATUS
fi
mkosi-chroot \
rpm \
--query \
--package \
--requires \
/var/tmp/SRPMS/mkosi-*.buildreqs.nosrc.rpm |
grep --invert-match '^rpmlib(' |
sort --unique >/tmp/dynamic-buildrequires
sort /tmp/buildrequires /tmp/dynamic-buildrequires |
uniq --unique |
tee --append /tmp/buildrequires |
xargs --delimiter '\n' mkosi-install
done
To install non-dynamic dependencies, we use rpmspec. What’s important
is to set _sourcedir to the directory containing the RPM sources for
the RPM spec that we want to build. We run rpmspec inside the image to
make sure all the RPM macros have their expected values and then run
mkosi-install outside the image to install the required dependencies.
mkosi-install will invoke the package manager that’s being used to
build the image to install the given packages.
We always set _topdir to /var/tmp to avoid polluting the image with
rpmbuild artifacts.
After installing non-dynamic BuildRequires dependencies, we have to
install the dynamic BuildRequires dependencies by running rpmbuild
-bd until it succeeds or fails with an exit code that’s not 11. After
each run of rpmbuild -bd that exits with exit code 11, there will be
an SRPM in the SRPMS subdirectory of the rpm working directory
(_topdir) of which the BuildRequires dependencies have to be
installed. We retrieve the list of BuildRequires dependencies with
rpm this time (because we’re operating on a package instead of a
spec), remove all rpmlib style dependencies which can’t be installed
and store them in a temporary file after filtering duplicates. Because
the BuildRequires dependencies from the SRPM will also contain the
non-dynamic BuildRequires dependencies, we have to filter those out as
well.
Now we have an image and build overlay with all the necessary dependencies installed to be able to build the RPM.
Next is the build script. We suffix the build script with .chroot so
that mkosi runs it entirely inside the image. In the build script, we
invoke rpmbuild -bb --build-in-place to have rpmbuild build the RPM
in place from the upstream sources. Because --build-in-place
configures _builddir to the current working directory, we change
directory to the upstream sources before invoking rpmbuild. Again,
_sourcedir has to point to the RPM spec sources. We also have to
override _rpmdir to point to the mkosi package directory (stored in
$PACKAGEDIR). The build script mkosi.build.chroot then looks as
follows:
#!/bin/sh
set -e
env --chdir=mkosi \
rpmbuild \
-bb \
--noprep \
--build-in-place \
$([ "$WITH_TESTS" = "0" ] && echo --nocheck) \
--define "_topdir /var/tmp" \
--define "_sourcedir $PWD/mkosi/rpm" \
--define "_rpmdir $PACKAGEDIR" \
${BUILDDIR:+--define} \
${BUILDDIR:+"_vpath_builddir $BUILDDIR"} \
--define "_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" \
--define "_binary_payload w.ufdio" \
--define "debug_package %{nil}" \
--define "__brp_strip %{nil}" \
--define "__brp_compress %{nil}" \
--define "__brp_mangle_shebangs %{nil}" \
--define "__brp_strip_comment_note %{nil}" \
--define "__brp_strip_static_archive %{nil}" \
rpm/mkosi.spec
The _vpath_builddir directory will be used to store out-of-tree build
artifacts for build systems that support out-of-tree builds (CMake,
Meson) so we set it to mkosi’s out-of-tree build directory in
$BUILDDIR if one is provided. This will make subsequent RPM builds
much faster as CMake or Meson will be able to do an incremental build.
Setting _binary_payload to w.ufdio disables compression to speed up
the build. We also disable debug package generation using
debug_package and various rpm build root policy scripts to speed up
the build. Note that the build root policy macros we use here are
CentOS/Fedora specific.
After the build script finishes, the produced rpms will be located in
$PACKAGEDIR. Any packages put in this directory by the build script
are added to a local package repository and become available for
installation in a post-installation script or using the
VolatilePackages= setting:
#!/bin/sh
set -e
mkosi-install mkosi
cp "$PACKAGEDIR"/*mkosi*.rpm "$OUTPUTDIR"
To run just the build script to build the rpms without actually building
an image, you can use the none output format (mkosi -t none).