nixpkgs/pkgs/build-support/vm/default.nix
Varun Biniwale c73f751b5d vmTools: update Ubuntu 26.04 commit hashes
Not sure why they changed but they (hopefully) shouldn't change again.
2026-05-27 09:33:20 -07:00

1647 lines
50 KiB
Nix

{
lib,
stdenv,
buildPackages,
bash,
bashInteractive,
busybox,
coreutils,
cpio,
dpkg,
e2fsprogs,
fetchurl,
glibc,
kmod,
linux,
makeInitrd,
makeModulesClosure,
mtdutils,
rpm,
runCommand,
util-linux,
virtiofsd,
writeScript,
writeText,
xz,
zstd,
# ----------------------------
# The following arguments form the "interface" of `pkgs.vmTools`.
# Note that `img` is a real package, but is set to this default in `all-packages.nix`.
# ----------------------------
customQemu ? null,
kernel ? linux,
img ? stdenv.hostPlatform.linux-kernel.target,
storeDir ? builtins.storeDir,
rootModules ? [
"virtio_pci"
"virtio_mmio"
"virtio_blk"
"virtio_balloon"
"virtio_rng"
"ext4"
"virtiofs"
"crc32c"
],
}:
let
qemu-common = import ../../../nixos/lib/qemu-common.nix { inherit lib stdenv; };
qemu = buildPackages.qemu_kvm;
modulesClosure = makeModulesClosure {
kernel = lib.getOutput "modules" kernel;
inherit rootModules;
firmware = kernel;
};
hd = "vda"; # either "sda" or "vda"
initrdUtils =
runCommand "initrd-utils"
{
nativeBuildInputs = [ buildPackages.nukeReferences ];
allowedReferences = [
"out"
modulesClosure
]; # prevent accidents like glibc being included in the initrd
}
''
mkdir -p $out/bin
mkdir -p $out/lib
# Copy what we need from Glibc.
cp -p \
${stdenv.cc.libc}/lib/ld-*.so.? \
${stdenv.cc.libc}/lib/libc.so.* \
${stdenv.cc.libc}/lib/libm.so.* \
${stdenv.cc.libc}/lib/libresolv.so.* \
${stdenv.cc.libc}/lib/libpthread.so.* \
${zstd.out}/lib/libzstd.so.* \
${xz.out}/lib/liblzma.so.* \
$out/lib
# Copy BusyBox.
cp -pd ${busybox}/bin/* $out/bin
cp -pd ${kmod}/bin/* $out/bin
# Run patchelf to make the programs refer to the copied libraries.
for i in $out/bin/* $out/lib/*; do if ! test -L $i; then nuke-refs $i; fi; done
for i in $out/bin/*; do
if [ -f "$i" -a ! -L "$i" ]; then
echo "patching $i..."
patchelf --set-interpreter $out/lib/ld-*.so.? --set-rpath $out/lib $i || true
fi
done
find $out/lib -type f \! -name 'ld*.so.?' | while read i; do
echo "patching $i..."
patchelf --set-rpath $out/lib $i
done
''; # */
stage1Init = writeScript "vm-run-stage1" ''
#! ${initrdUtils}/bin/ash -e
export PATH=${initrdUtils}/bin
mkdir /etc
echo -n > /etc/fstab
mount -t proc none /proc
mount -t sysfs none /sys
echo 2 > /proc/sys/vm/panic_on_oom
for o in $(cat /proc/cmdline); do
case $o in
mountDisk=*)
mountDisk=''${mountDisk#mountDisk=}
;;
command=*)
set -- $(IFS==; echo $o)
command=$2
;;
esac
done
echo "loading kernel modules..."
for i in $(cat ${modulesClosure}/insmod-list); do
insmod $i || echo "warning: unable to load $i"
done
mount -t devtmpfs devtmpfs /dev
ln -s /proc/self/fd /dev/fd
ln -s /proc/self/fd/0 /dev/stdin
ln -s /proc/self/fd/1 /dev/stdout
ln -s /proc/self/fd/2 /dev/stderr
ifconfig lo up
mkdir /fs
if test -z "$mountDisk"; then
mount -t tmpfs none /fs
elif [[ -e "$mountDisk" ]]; then
mount "$mountDisk" /fs
else
mount /dev/${hd} /fs
fi
mkdir -p /fs/dev
mount -o bind /dev /fs/dev
mkdir -p /fs/dev/shm /fs/dev/pts
mount -t tmpfs -o "mode=1777" none /fs/dev/shm
mount -t devpts none /fs/dev/pts
echo "mounting Nix store..."
mkdir -p /fs${storeDir}
mount -t virtiofs store /fs${storeDir}
mkdir -p /fs/tmp /fs/run /fs/var
mount -t tmpfs -o "mode=1777" none /fs/tmp
mount -t tmpfs -o "mode=755" none /fs/run
ln -sfn /run /fs/var/run
echo "mounting host's temporary directory..."
mkdir -p /fs/tmp/xchg
mount -t virtiofs xchg /fs/tmp/xchg
mkdir -p /fs/proc
mount -t proc none /fs/proc
mkdir -p /fs/sys
mount -t sysfs none /fs/sys
mkdir -p /fs/etc
ln -sf /proc/mounts /fs/etc/mtab
echo "127.0.0.1 localhost" > /fs/etc/hosts
# Ensures tools requiring /etc/passwd will work (e.g. nix)
if [ ! -e /fs/etc/passwd ]; then
echo "root:x:0:0:System administrator:/root:/bin/sh" > /fs/etc/passwd
fi
echo "starting stage 2 ($command)"
exec switch_root /fs $command
'';
initrd = makeInitrd {
contents = [
{
object = stage1Init;
symlink = "/init";
}
];
};
stage2Init = writeScript "vm-run-stage2" ''
#! ${bash}/bin/sh
set -euo pipefail
source /tmp/xchg/saved-env
if [ -f /tmp/xchg/.attrs.sh ]; then
source /tmp/xchg/.attrs.sh
export NIX_ATTRS_JSON_FILE=/tmp/xchg/.attrs.json
export NIX_ATTRS_SH_FILE=/tmp/xchg/.attrs.sh
fi
export NIX_STORE=${storeDir}
export NIX_BUILD_TOP=/tmp
export TMPDIR=/tmp
export PATH=/empty
cd "$NIX_BUILD_TOP"
source $stdenv/setup
if ! test -e /bin/sh; then
${coreutils}/bin/mkdir -p /bin
${coreutils}/bin/ln -s ${bash}/bin/sh /bin/sh
fi
# Set up automatic kernel module loading.
export MODULE_DIR=${lib.getOutput "modules" kernel}/lib/modules/
${coreutils}/bin/cat <<EOF > /run/modprobe
#! ${bash}/bin/sh
export MODULE_DIR=$MODULE_DIR
exec ${kmod}/bin/modprobe "\$@"
EOF
${coreutils}/bin/chmod 755 /run/modprobe
echo /run/modprobe > /proc/sys/kernel/modprobe
# For debugging: if this is the second time this image is run,
# then don't start the build again, but instead drop the user into
# an interactive shell.
if test -n "$origBuilder" -a ! -e /.debug; then
exec < /dev/null
${coreutils}/bin/touch /.debug
declare -a argsArray=()
concatTo argsArray origArgs
"$origBuilder" "''${argsArray[@]}"
echo $? > /tmp/xchg/in-vm-exit
${busybox}/bin/mount -o remount,ro dummy /
${busybox}/bin/poweroff -f
else
export PATH=/bin:/usr/bin:${coreutils}/bin
echo "Starting interactive shell..."
echo "(To run the original builder: \$origBuilder \$origArgs)"
exec ${busybox}/bin/setsid ${bashInteractive}/bin/bash < /dev/${qemu-common.qemuSerialDevice} &> /dev/${qemu-common.qemuSerialDevice}
fi
'';
qemuCommandLinux = ''
${if (customQemu != null) then customQemu else (qemu-common.qemuBinary qemu)} \
-nographic -no-reboot \
-device virtio-rng-pci \
-chardev socket,id=store,path=virtio-store.sock \
-device vhost-user-fs-pci,chardev=store,tag=store \
-chardev socket,id=xchg,path=virtio-xchg.sock \
-device vhost-user-fs-pci,chardev=xchg,tag=xchg \
''${diskImage:+-drive file=$diskImage,if=virtio,cache=unsafe,werror=report} \
-kernel ${kernel}/${img} \
-initrd ${initrd}/initrd \
-append "console=${qemu-common.qemuSerialDevice} panic=1 command=${stage2Init} mountDisk=$mountDisk loglevel=4" \
$QEMU_OPTS
'';
vmRunCommand =
qemuCommand:
writeText "vm-run" ''
${coreutils}/bin/mkdir xchg
export > xchg/saved-env
if [ -f "''${NIX_ATTRS_SH_FILE-}" ]; then
${coreutils}/bin/cp $NIX_ATTRS_JSON_FILE $NIX_ATTRS_SH_FILE xchg
source "$NIX_ATTRS_SH_FILE"
fi
source $stdenv/setup
eval "$preVM"
if [ "$enableParallelBuilding" = 1 ]; then
QEMU_NR_VCPUS=0
if [ ''${NIX_BUILD_CORES:-0} = 0 ]; then
QEMU_NR_VCPUS="$(nproc)"
else
QEMU_NR_VCPUS="$NIX_BUILD_CORES"
fi
# qemu only supports 255 vCPUs (see error from `qemu-system-x86_64 -smp 256`)
if [ "$QEMU_NR_VCPUS" -gt 255 ]; then
QEMU_NR_VCPUS=255
fi
QEMU_OPTS+=" -smp cpus=$QEMU_NR_VCPUS"
fi
# Write the command to start the VM to a file so that the user can
# debug inside the VM if the build fails (when Nix is called with
# the -K option to preserve the temporary build directory).
${coreutils}/bin/cat > ./run-vm <<EOF
#! ${bash}/bin/sh
''${diskImage:+diskImage=$diskImage}
# GitHub Actions runners seems to not allow installing seccomp filter: https://github.com/rcambrj/nix-pi-loader/issues/1#issuecomment-2605497516
# Since we are running in a sandbox already, the difference between seccomp and none is minimal
${virtiofsd}/bin/virtiofsd --xattr --socket-path virtio-store.sock --sandbox none --seccomp none --shared-dir "${storeDir}" &
${virtiofsd}/bin/virtiofsd --xattr --socket-path virtio-xchg.sock --sandbox none --seccomp none --shared-dir xchg &
# Wait until virtiofsd has created these sockets to avoid race condition.
until [[ -e virtio-store.sock ]]; do ${coreutils}/bin/sleep 0.1; done
until [[ -e virtio-xchg.sock ]]; do ${coreutils}/bin/sleep 0.1; done
${qemuCommand}
EOF
${coreutils}/bin/chmod +x ./run-vm
source ./run-vm
if ! test -e xchg/in-vm-exit; then
echo "Virtual machine didn't produce an exit code."
exit 1
fi
exitCode="$(${coreutils}/bin/cat xchg/in-vm-exit)"
if [ "$exitCode" != "0" ]; then
exit "$exitCode"
fi
eval "$postVM"
'';
# A bash script fragment that produces a disk image at `destination`.
createEmptyImage =
{
# Disk image size in MiB (1024*1024 bytes)
size,
# Name that will be written to ${destination}/nix-support/full-name
fullName,
# Where to write the image files, defaulting to $out
destination ? "$out",
}:
''
mkdir -p ${destination}
diskImage=${destination}/disk-image.qcow2
${qemu}/bin/qemu-img create -f qcow2 $diskImage "${toString size}M"
mkdir ${destination}/nix-support
echo "${fullName}" > ${destination}/nix-support/full-name
'';
defaultCreateRootFS = ''
mkdir /mnt
${e2fsprogs}/bin/mkfs.ext4 /dev/${hd}
${util-linux}/bin/mount -t ext4 /dev/${hd} /mnt
if test -e /mnt/.debug; then
exec ${bash}/bin/sh
fi
touch /mnt/.debug
mkdir /mnt/proc /mnt/dev /mnt/sys
'';
/*
Run a derivation in a Linux virtual machine (using Qemu/KVM). By
default, there is no disk image; the root filesystem is a tmpfs,
and the nix store is shared with the host (via the 9P protocol).
Thus, any pure Nix derivation should run unmodified, e.g. the
call
runInLinuxVM patchelf
will build the derivation `patchelf' inside a VM. The attribute
`preVM' can optionally contain a shell command to be evaluated
*before* the VM is started (i.e., on the host). The attribute
`memSize' specifies the memory size of the VM in MiB (1024*1024
bytes), defaulting to 512. The attribute `diskImage' can
optionally specify a file system image to be attached to /dev/sda.
(Note that currently we expect the image to contain a filesystem,
not a full disk image with a partition table etc.)
If the build fails and Nix is run with the `-K' option, a script
`run-vm' will be left behind in the temporary build directory
that allows you to boot into the VM and debug it interactively.
*/
runInLinuxVM =
drv:
lib.overrideDerivation drv (
{
memSize ? 512,
QEMU_OPTS ? "",
args,
builder,
...
}:
{
requiredSystemFeatures = [ "kvm" ];
builder = "${bash}/bin/sh";
args = [
"-e"
(vmRunCommand qemuCommandLinux)
];
origArgs = args;
origBuilder = builder;
env.QEMU_OPTS = "${QEMU_OPTS} -m ${toString memSize} -object memory-backend-memfd,id=mem,size=${toString memSize}M,share=on -machine memory-backend=mem";
__structuredAttrs = true;
}
);
extractFs =
{
file,
fs ? null,
}:
runInLinuxVM (
stdenv.mkDerivation {
name = "extract-file";
buildInputs = [ util-linux ];
buildCommand = ''
ln -s ${kernel}/lib /lib
${kmod}/bin/modprobe loop
${kmod}/bin/modprobe ext4
${kmod}/bin/modprobe hfs
${kmod}/bin/modprobe hfsplus
${kmod}/bin/modprobe squashfs
${kmod}/bin/modprobe iso9660
${kmod}/bin/modprobe ufs
${kmod}/bin/modprobe cramfs
mkdir -p $out
mkdir -p tmp
mount -o loop,ro,ufstype=44bsd ${lib.optionalString (fs != null) "-t ${fs} "}${file} tmp ||
mount -o loop,ro ${lib.optionalString (fs != null) "-t ${fs} "}${file} tmp
cp -Rv tmp/* $out/ || exit 0
'';
}
);
extractMTDfs =
{
file,
fs ? null,
}:
runInLinuxVM (
stdenv.mkDerivation {
name = "extract-file-mtd";
buildInputs = [
util-linux
mtdutils
];
buildCommand = ''
ln -s ${kernel}/lib /lib
${kmod}/bin/modprobe mtd
${kmod}/bin/modprobe mtdram total_size=131072
${kmod}/bin/modprobe mtdchar
${kmod}/bin/modprobe mtdblock
${kmod}/bin/modprobe jffs2
${kmod}/bin/modprobe zlib
mkdir -p $out
mkdir -p tmp
dd if=${file} of=/dev/mtd0
mount ${lib.optionalString (fs != null) "-t ${fs} "}/dev/mtdblock0 tmp
cp -R tmp/* $out/
'';
}
);
/*
Like runInLinuxVM, but run the build not using the stdenv from
the Nix store, but using the tools provided by /bin, /usr/bin
etc. from the specified filesystem image, which typically is a
filesystem containing a non-NixOS Linux distribution.
*/
runInLinuxImage =
drv:
runInLinuxVM (
lib.overrideDerivation drv (attrs: {
mountDisk = attrs.mountDisk or true;
/*
Mount `image' as the root FS, but use a temporary copy-on-write
image since we don't want to (and can't) write to `image'.
*/
preVM = ''
diskImage=$(pwd)/disk-image.qcow2
origImage=${attrs.diskImage}
if test -d "$origImage"; then origImage="$origImage/disk-image.qcow2"; fi
${qemu}/bin/qemu-img create -F ${attrs.diskImageFormat} -b "$origImage" -f qcow2 $diskImage
'';
/*
Inside the VM, run the stdenv setup script normally, but at the
very end set $PATH and $SHELL to the `native' paths for the
distribution inside the VM.
*/
postHook = ''
PATH=/usr/bin:/bin:/usr/sbin:/sbin
SHELL=/bin/sh
eval "$origPostHook"
'';
origPostHook = lib.optionalString (attrs ? postHook) attrs.postHook;
# Don't run Nix-specific build steps like patchelf.
fixupPhase = "true";
})
);
/*
Create a filesystem image of the specified size and fill it with
a set of RPM packages.
*/
fillDiskWithRPMs =
{
size ? 4096,
rpms,
name,
fullName,
preInstall ? "",
postInstall ? "",
runScripts ? true,
createRootFS ? defaultCreateRootFS,
QEMU_OPTS ? "",
memSize ? 512,
unifiedSystemDir ? false,
}:
runInLinuxVM (
stdenv.mkDerivation {
inherit
name
preInstall
postInstall
rpms
QEMU_OPTS
memSize
;
preVM = createEmptyImage { inherit size fullName; };
buildCommand = ''
${createRootFS}
chroot=$(type -tP chroot)
# Make the Nix store available in /mnt, because that's where the RPMs live.
mkdir -p /mnt${storeDir}
${util-linux}/bin/mount -o bind ${storeDir} /mnt${storeDir}
# Some programs may require devices in /dev to be available (e.g. /dev/random)
${util-linux}/bin/mount -o bind /dev /mnt/dev
# Newer distributions like Fedora 18 require /lib etc. to be
# symlinked to /usr.
${lib.optionalString unifiedSystemDir ''
mkdir -p /mnt/usr/bin /mnt/usr/lib /mnt/usr/lib64
ln -s /usr/bin /mnt/bin
ln -s /usr/bin /mnt/sbin
ln -s /usr/bin /mnt/usr/sbin
ln -s /usr/lib /mnt/lib
ln -s /usr/lib64 /mnt/lib64
${util-linux}/bin/mount -t proc none /mnt/proc
''}
echo "unpacking RPMs..."
set +o pipefail
for i in "''${rpms[@]}"; do
echo "$i..."
${rpm}/bin/rpm2cpio "$i" | chroot /mnt ${cpio}/bin/cpio -i --make-directories --unconditional
done
eval "$preInstall"
echo "initialising RPM DB..."
PATH=/usr/bin:/bin:/usr/sbin:/sbin $chroot /mnt \
ldconfig -v || true
PATH=/usr/bin:/bin:/usr/sbin:/sbin $chroot /mnt \
rpm --initdb
${util-linux}/bin/mount -o bind /tmp /mnt/tmp
echo "installing RPMs..."
PATH=/usr/bin:/bin:/usr/sbin:/sbin $chroot /mnt \
rpm -iv --nosignature ${lib.optionalString (!runScripts) "--noscripts"} "''${rpms[@]}"
echo "running post-install script..."
eval "$postInstall"
rm /mnt/.debug
${util-linux}/bin/umount /mnt${storeDir} /mnt/tmp /mnt/dev ${lib.optionalString unifiedSystemDir "/mnt/proc"}
${util-linux}/bin/umount /mnt
'';
passthru = { inherit fullName; };
}
);
/*
Generate a script that can be used to run an interactive session
in the given image.
*/
makeImageTestScript =
image:
writeScript "image-test" ''
#! ${bash}/bin/sh
if test -z "$1"; then
echo "Syntax: $0 <copy-on-write-temp-file>"
exit 1
fi
diskImage="$1"
if ! test -e "$diskImage"; then
${qemu}/bin/qemu-img create -b ${image}/disk-image.qcow2 -f qcow2 -F qcow2 "$diskImage"
fi
export TMPDIR=$(mktemp -d)
export out=/dummy
export origBuilder=
export origArgs=
mkdir $TMPDIR/xchg
export > $TMPDIR/xchg/saved-env
mountDisk=1
${qemuCommandLinux}
'';
/*
Build RPM packages from the tarball `src' in the Linux
distribution installed in the filesystem `diskImage'. The
tarball must contain an RPM specfile.
*/
buildRPM =
attrs:
runInLinuxImage (
stdenv.mkDerivation (
{
prePhases = [
"prepareImagePhase"
"sysInfoPhase"
];
dontConfigure = true;
outDir = "rpms/${attrs.diskImage.name}";
prepareImagePhase = ''
if test -n "$extraRPMs"; then
for rpmdir in $extraRPMs ; do
rpm -iv $(ls $rpmdir/rpms/*/*.rpm | grep -v 'src\.rpm' | sort | head -1)
done
fi
'';
sysInfoPhase = ''
echo "System/kernel: $(uname -a)"
if test -e /etc/fedora-release; then echo "Fedora release: $(cat /etc/fedora-release)"; fi
if test -e /etc/SuSE-release; then echo "SUSE release: $(cat /etc/SuSE-release)"; fi
echo "installed RPM packages"
rpm -qa --qf "%{Name}-%{Version}-%{Release} (%{Arch}; %{Distribution}; %{Vendor})\n"
'';
buildPhase = ''
eval "$preBuild"
srcName="$(rpmspec --srpm -q --qf '%{source}' *.spec)"
cp "$src" "$srcName" # `ln' doesn't work always work: RPM requires that the file is owned by root
export HOME=/tmp/home
mkdir $HOME
rpmout=/tmp/rpmout
mkdir $rpmout $rpmout/SPECS $rpmout/BUILD $rpmout/RPMS $rpmout/SRPMS
echo "%_topdir $rpmout" >> $HOME/.rpmmacros
if [ `uname -m` = i686 ]; then extra="--target i686-linux"; fi
rpmbuild -vv $extra -ta "$srcName"
eval "$postBuild"
'';
installPhase = ''
eval "$preInstall"
mkdir -p $out/$outDir
find $rpmout -name "*.rpm" -exec cp {} $out/$outDir \;
for i in $out/$outDir/*.rpm; do
echo "Generated RPM/SRPM: $i"
rpm -qip $i
done
eval "$postInstall"
''; # */
}
// attrs
)
);
/*
Create a filesystem image of the specified size and fill it with
a set of Debian packages. `debs' must be a list of list of
.deb files, namely, the Debian packages grouped together into
strongly connected components. See deb/deb-closure.nix.
*/
fillDiskWithDebs =
{
size ? 4096,
debs,
name,
fullName,
postInstall ? null,
createRootFS ? defaultCreateRootFS,
QEMU_OPTS ? "",
memSize ? 512,
...
}@args:
runInLinuxVM (
stdenv.mkDerivation (
{
inherit
name
postInstall
QEMU_OPTS
memSize
;
debsFlat = lib.flatten debs;
debsGrouped = map toString debs;
preVM = createEmptyImage { inherit size fullName; };
buildCommand = ''
${createRootFS}
PATH=$PATH:${
lib.makeBinPath [
dpkg
glibc
xz
]
}
# Unpack the .debs. We do this to prevent pre-install scripts
# (which have lots of circular dependencies) from barfing.
echo "unpacking Debs..."
for deb in "''${debsFlat[@]}"; do
echo "$deb..."
dpkg-deb --extract "$deb" /mnt
done
# Make the Nix store available in /mnt, because that's where the .debs live.
mkdir -p /mnt/inst${storeDir}
${util-linux}/bin/mount -o bind ${storeDir} /mnt/inst${storeDir}
${util-linux}/bin/mount -o bind /proc /mnt/proc
${util-linux}/bin/mount -o bind /dev /mnt/dev
# Misc. files/directories assumed by various packages.
echo "initialising Dpkg DB..."
touch /mnt/etc/shells
touch /mnt/var/lib/dpkg/status
touch /mnt/var/lib/dpkg/available
touch /mnt/var/lib/dpkg/diversions
# Now install the .debs. This is basically just to register
# them with dpkg and to make their pre/post-install scripts
# run.
echo "installing Debs..."
export DEBIAN_FRONTEND=noninteractive
for component in "''${debsGrouped[@]}"; do
echo
echo ">>> INSTALLING COMPONENT: $component"
debs=
for i in $component; do
debs="$debs /inst/$i";
done
chroot=$(type -tP chroot)
# Create a fake start-stop-daemon script, as done in debootstrap.
mv "/mnt/sbin/start-stop-daemon" "/mnt/sbin/start-stop-daemon.REAL"
echo "#!/bin/true" > "/mnt/sbin/start-stop-daemon"
chmod 755 "/mnt/sbin/start-stop-daemon"
PATH=/usr/bin:/bin:/usr/sbin:/sbin $chroot /mnt \
/usr/bin/dpkg --install --force-all $debs < /dev/null || true
# Move the real start-stop-daemon back into its place.
mv "/mnt/sbin/start-stop-daemon.REAL" "/mnt/sbin/start-stop-daemon"
done
echo "running post-install script..."
eval "$postInstall"
rm /mnt/.debug
${util-linux}/bin/umount /mnt/inst${storeDir}
${util-linux}/bin/umount /mnt/proc
${util-linux}/bin/umount /mnt/dev
${util-linux}/bin/umount /mnt
'';
passthru = { inherit fullName; };
}
// args
)
);
/*
Generate a Nix expression containing fetchurl calls for the
closure of a set of top-level RPM packages from the
`primary.xml.gz' file of a Fedora or openSUSE distribution.
*/
rpmClosureGenerator =
{
name,
packagesLists,
urlPrefixes,
packages,
archs ? [ ],
}:
assert (builtins.length packagesLists) == (builtins.length urlPrefixes);
runCommand "${name}.nix"
{
nativeBuildInputs = [
buildPackages.perl
buildPackages.perlPackages.URI
buildPackages.perlPackages.XMLSimple
buildPackages.zstd
];
inherit archs;
}
''
${lib.concatImapStrings (i: pl: ''
echo "decompressing ${pl}..."
case ${pl} in
*.zst)
zstd -d < ${pl} > ./packages_${toString i}.xml
;;
*.xz | *.lzma)
xz -d < ${pl} > ./packages_${toString i}.xml
;;
*.bz2)
bunzip2 < ${pl} > ./packages_${toString i}.xml
;;
*.gz)
gunzip < ${pl} > ./packages_${toString i}.xml
;;
*)
cp ${pl} ./packages_${toString i}.xml
;;
esac
'') packagesLists}
perl -w ${rpm/rpm-closure.pl} \
${
lib.concatImapStrings (i: pl: "./packages_${toString i}.xml ${pl.snd} ") (
lib.zipLists packagesLists urlPrefixes
)
} \
${toString packages} > $out
'';
/*
Helper function that combines rpmClosureGenerator and
fillDiskWithRPMs to generate a disk image from a set of package
names.
*/
makeImageFromRPMDist =
{
name,
fullName,
size ? 4096,
urlPrefix ? "",
urlPrefixes ? [ urlPrefix ],
packagesList ? "",
packagesLists ? [ packagesList ],
packages,
extraPackages ? [ ],
preInstall ? "",
postInstall ? "",
archs ? [
"noarch"
"i386"
],
runScripts ? true,
createRootFS ? defaultCreateRootFS,
QEMU_OPTS ? "",
memSize ? 512,
unifiedSystemDir ? false,
}:
fillDiskWithRPMs {
inherit
name
fullName
size
preInstall
postInstall
runScripts
createRootFS
unifiedSystemDir
QEMU_OPTS
memSize
;
rpms = import (rpmClosureGenerator {
inherit
name
packagesLists
urlPrefixes
archs
;
packages = packages ++ extraPackages;
}) { inherit fetchurl; };
};
/*
Like `rpmClosureGenerator', but now for Debian/Ubuntu releases
(i.e. generate a closure from a Packages.bz2 file).
*/
debClosureGenerator =
{
name,
packagesLists,
urlPrefix,
packages,
}:
runCommand "${name}.nix"
{
nativeBuildInputs = [
buildPackages.perl
buildPackages.dpkg
buildPackages.nixfmt
];
}
''
for i in ${toString packagesLists}; do
echo "adding $i..."
case $i in
*.xz | *.lzma)
xz -d < $i >> ./Packages
;;
*.bz2)
bunzip2 < $i >> ./Packages
;;
*.gz)
gzip -dc < $i >> ./Packages
;;
esac
done
perl -w ${deb/deb-closure.pl} \
./Packages ${urlPrefix} ${toString packages} > $out
nixfmt $out
'';
/*
Helper function that combines debClosureGenerator and
fillDiskWithDebs to generate a disk image from a set of package
names.
*/
makeImageFromDebDist =
{
name,
fullName,
size ? 4096,
urlPrefix,
packagesList ? "",
packagesLists ? [ packagesList ],
packages,
extraPackages ? [ ],
postInstall ? "",
extraDebs ? [ ],
createRootFS ? defaultCreateRootFS,
QEMU_OPTS ? "",
memSize ? 512,
...
}@args:
let
expr = debClosureGenerator {
inherit name packagesLists urlPrefix;
packages = packages ++ extraPackages;
};
in
(fillDiskWithDebs (
{
inherit
name
fullName
size
postInstall
createRootFS
QEMU_OPTS
memSize
;
debs = import expr { inherit fetchurl; } ++ extraDebs;
}
// args
))
// {
inherit expr;
};
# The set of supported RPM-based distributions.
rpmDistros = {
fedora42x86_64 = {
name = "fedora-42-x86_64";
fullName = "Fedora 42 (x86_64)";
packagesList = fetchurl {
url = "https://dl.fedoraproject.org/pub/fedora/linux/releases/42/Everything/x86_64/os/repodata/cd483b35df017d68b73a878a392bbf666a43d75db54c386e4720bc369eb5c3a3-primary.xml.zst";
hash = "sha256-zUg7Nd8BfWi3OoeKOSu/ZmpD1121TDhuRyC8Np61w6M=";
};
urlPrefix = "https://dl.fedoraproject.org/pub/fedora/linux/releases/42/Everything/x86_64/os";
archs = [
"noarch"
"x86_64"
];
packages = commonFedoraPackages;
unifiedSystemDir = true;
};
fedora43x86_64 = {
name = "fedora-43-x86_64";
fullName = "Fedora 43 (x86_64)";
packagesList = fetchurl {
url = "https://dl.fedoraproject.org/pub/fedora/linux/releases/43/Everything/x86_64/os/repodata/fffa3e9f63fffd3d21b8ea5e9bb0fe349a7ed1d4e09777a618cec93a2bcc305f-primary.xml.zst";
hash = "sha256-//o+n2P//T0huOpem7D+NJp+0dTgl3emGM7JOivMMF8=";
};
urlPrefix = "https://dl.fedoraproject.org/pub/fedora/linux/releases/43/Everything/x86_64/os";
archs = [
"noarch"
"x86_64"
];
packages = commonFedoraPackages ++ [ "gpgverify" ];
unifiedSystemDir = true;
};
# Rocky Linux's /pub/rocky/9/ URL is rolling and changes with each minor release. We use the
# vault instead, which provides stable URLs for specific minor versions.
rocky9x86_64 = {
name = "rocky-9.6-x86_64";
fullName = "Rocky Linux 9.6 (x86_64)";
packagesLists = [
(fetchurl {
url = "https://dl.rockylinux.org/vault/rocky/9.6/BaseOS/x86_64/os/repodata/9965e429a90787a87a07eed62872d046411fb7dded524b96d74c4ce1eade327a-primary.xml.gz";
hash = "sha256-mWXkKakHh6h6B+7WKHLQRkEft93tUkuW10xM4ereMno=";
})
(fetchurl {
url = "https://dl.rockylinux.org/vault/rocky/9.6/AppStream/x86_64/os/repodata/8cc9f795679c3365c06b6135f685ebf4188a5863a5f52f09f8cabd4f09c4dfa1-primary.xml.gz";
hash = "sha256-jMn3lWecM2XAa2E19oXr9BiKWGOl9S8J+Mq9TwnE36E=";
})
];
urlPrefixes = [
"https://dl.rockylinux.org/vault/rocky/9.6/BaseOS/x86_64/os"
"https://dl.rockylinux.org/vault/rocky/9.6/AppStream/x86_64/os"
];
archs = [
"noarch"
"x86_64"
];
packages = commonRockyPackages ++ [
"annobin"
];
unifiedSystemDir = true;
};
# Rocky Linux's /pub/rocky/10/ URL is rolling and changes with each minor release. We use the
# vault instead, which provides stable URLs for specific minor versions.
rocky10x86_64 = {
name = "rocky-10.0-x86_64";
fullName = "Rocky Linux 10.0 (x86_64)";
packagesLists = [
(fetchurl {
url = "https://dl.rockylinux.org/vault/rocky/10.0/BaseOS/x86_64/os/repodata/484d5c43cdb1058dd1328a6b891f45c85f1cb2620c528f2ef423d4b9feb9e2f0-primary.xml.gz";
hash = "sha256-SE1cQ82xBY3RMopriR9FyF8csmIMUo8u9CPUuf654vA=";
})
(fetchurl {
url = "https://dl.rockylinux.org/vault/rocky/10.0/AppStream/x86_64/os/repodata/32c93064142d89f3f19c11e92642c5abd8368418f7ab3f3bdd752e4afa9b5b23-primary.xml.gz";
hash = "sha256-MskwZBQtifPxnBHpJkLFq9g2hBj3qz873XUuSvqbWyM=";
})
];
urlPrefixes = [
"https://dl.rockylinux.org/vault/rocky/10.0/BaseOS/x86_64/os"
"https://dl.rockylinux.org/vault/rocky/10.0/AppStream/x86_64/os"
];
archs = [
"noarch"
"x86_64"
];
packages = commonRockyPackages ++ [
"annobin-plugin-gcc"
];
unifiedSystemDir = true;
};
# AlmaLinux's repo.almalinux.org URLs are rolling and change with each minor release.
# We use vault.almalinux.org instead, which provides stable URLs for specific versions.
alma9x86_64 = {
name = "alma-9.6-x86_64";
fullName = "AlmaLinux 9.6 (x86_64)";
packagesLists = [
(fetchurl {
url = "https://vault.almalinux.org/9.6/BaseOS/x86_64/os/repodata/26d6cf944c86ef850773e61919e892a375ff10bb2254003e1d71673db9900b07-primary.xml.gz";
hash = "sha256-JtbPlEyG74UHc+YZGeiSo3X/ELsiVAA+HXFnPbmQCwc=";
})
(fetchurl {
url = "https://vault.almalinux.org/9.6/AppStream/x86_64/os/repodata/afb5d18b78d819d826d3d0e32ba439da7b9e0fd91d726dd833366496b1b8ca20-primary.xml.gz";
hash = "sha256-r7XRi3jYGdgm09DjK6Q52nueD9kdcm3YMzZklrG4yiA=";
})
];
urlPrefixes = [
"https://vault.almalinux.org/9.6/BaseOS/x86_64/os"
"https://vault.almalinux.org/9.6/AppStream/x86_64/os"
];
archs = [
"noarch"
"x86_64"
];
packages = commonAlmaPackages ++ [
"annobin"
];
unifiedSystemDir = true;
};
alma10x86_64 = {
name = "alma-10.0-x86_64";
fullName = "AlmaLinux 10.0 (x86_64)";
packagesLists = [
(fetchurl {
url = "https://vault.almalinux.org/10.0/BaseOS/x86_64/os/repodata/4d88695fa7ccb6298897fa9682ac1ded4628df342ffe08312846225e4469e3e4-primary.xml.gz";
hash = "sha256-TYhpX6fMtimIl/qWgqwd7UYo3zQv/ggxKEYiXkRp4+Q=";
})
(fetchurl {
url = "https://vault.almalinux.org/10.0/AppStream/x86_64/os/repodata/11ac32065bae6f2c2451803458690fc550e79f93a4ea9f438930f0c228964791-primary.xml.gz";
hash = "sha256-EawyBluubywkUYA0WGkPxVDnn5Ok6p9DiTDwwiiWR5E=";
})
];
urlPrefixes = [
"https://vault.almalinux.org/10.0/BaseOS/x86_64/os"
"https://vault.almalinux.org/10.0/AppStream/x86_64/os"
];
archs = [
"noarch"
"x86_64"
];
packages = commonAlmaPackages ++ [
"annobin-plugin-gcc"
];
unifiedSystemDir = true;
};
# Oracle provides versioned URLs for baseos (e.g., OL9/7/baseos/base/) but not for appstream.
# We can't mix versioned baseos with rolling appstream due to package version dependencies,
# so we use rolling URLs for both. These may need hash updates when Oracle releases new versions.
oracle9x86_64 = {
name = "oracle-9-x86_64";
fullName = "Oracle Linux 9 (x86_64)";
packagesLists = [
(fetchurl {
url = "https://yum.oracle.com/repo/OracleLinux/OL9/baseos/latest/x86_64/repodata/bc292d67f73fc606db1872d5ba8804da06a514efe64523247035f0d3b678fb63-primary.xml.gz";
hash = "sha256-vCktZ/c/xgbbGHLVuogE2galFO/mRSMkcDXw07Z4+2M=";
})
(fetchurl {
url = "https://yum.oracle.com/repo/OracleLinux/OL9/appstream/x86_64/repodata/6fabacadf7cdf22cbb21dc296f58e6b852d5b8ec9a927e214231477ef90083f9-primary.xml.gz";
hash = "sha256-b6usrffN8iy7Idwpb1jmuFLVuOyakn4hQjFHfvkAg/k=";
})
];
urlPrefixes = [
"https://yum.oracle.com/repo/OracleLinux/OL9/baseos/latest/x86_64"
"https://yum.oracle.com/repo/OracleLinux/OL9/appstream/x86_64"
];
archs = [
"noarch"
"x86_64"
];
packages = commonOraclePackages ++ [
"annobin"
];
unifiedSystemDir = true;
};
# Amazon Linux 2023 uses GUID-based URLs that don't allow directory listing.
# To update: The GUID corresponds to a specific AL2023 release version. You can find the
# current GUID by either:
# 1. Running an AL2023 container: `docker run -it amazonlinux:2023 dnf repolist -v`
# and extracting the GUID from the Repo-baseurl field
# 2. Checking https://github.com/docker-library/repo-info/blob/master/repos/amazonlinux/local/latest.md
# which tracks the repository URLs from the official Docker image
# Release notes: https://docs.aws.amazon.com/linux/al2023/release-notes/relnotes.html
amazon2023x86_64 = {
name = "amazon-2023-x86_64";
fullName = "Amazon Linux 2023 (x86_64)";
packagesList = fetchurl {
url = "https://cdn.amazonlinux.com/al2023/core/guids/6fa961924efb4835a7e8de43c89726dca28a5cf5906f891262d8f78a31ea3aaf/x86_64/repodata/primary.xml.gz";
hash = "sha256-Ezdsc8a2aOIbyXvQ/nyanWe1fl089VgtfegaPcu2oo4=";
};
urlPrefix = "https://cdn.amazonlinux.com/al2023/core/guids/6fa961924efb4835a7e8de43c89726dca28a5cf5906f891262d8f78a31ea3aaf/x86_64";
archs = [
"noarch"
"x86_64"
];
packages = commonAmazonPackages ++ [
"annobin-plugin-gcc"
];
unifiedSystemDir = true;
};
};
# The set of supported Dpkg-based distributions.
debDistros = {
# Ubuntu's snapshot service returns the same data for 22.04 regardless of the timestamp in the
# URL. The hashes don't change between mirror://ubuntu and snapshot.ubuntu.com, so this is fine.
ubuntu2204i386 = {
name = "ubuntu-22.04-jammy-i386";
fullName = "Ubuntu 22.04 Jammy (i386)";
packagesLists = [
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/jammy/main/binary-i386/Packages.xz";
hash = "sha256-iZBmwT0ep4v+V3sayybbOgZBOFFZwPGpOKtmuLMMVPQ=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/jammy/universe/binary-i386/Packages.xz";
hash = "sha256-DO2LdpZ9rDDBhWj2gvDWd0TJJVZHxKsYTKTi6GXjm1E=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/jammy-updates/main/binary-i386/Packages.xz";
hash = "sha256-g95BtOoMxacZEHMBbcMes4a1P9HKf/QGOMOPr+OKayo=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/jammy-updates/universe/binary-i386/Packages.xz";
hash = "sha256-VbazaDDJKSUyQchGmw5f+FYAr4PIXWZJSBF0WVC5j+0=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/jammy-security/main/binary-i386/Packages.xz";
hash = "sha256-SkP4PqjUAbEMtktR5WQm/3jQl9O0T2VOVTP9QIYIVkQ=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/jammy-security/universe/binary-i386/Packages.xz";
hash = "sha256-citjk8LAGSRlXgOXgf3oe9vBCUC6/DJGhRJl/3ppN9c=";
})
];
urlPrefix = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z";
packages = commonDebPackages ++ [
"diffutils"
"libc-bin"
];
};
ubuntu2204x86_64 = {
name = "ubuntu-22.04-jammy-amd64";
fullName = "Ubuntu 22.04 Jammy (amd64)";
packagesLists = [
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/jammy/main/binary-amd64/Packages.xz";
hash = "sha256-N8tX8VVMv6ccWinun/7hipqMF4K7BWjgh0t/9M6PnBE=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/jammy/universe/binary-amd64/Packages.xz";
hash = "sha256-0pyyTJP+xfQyVXBrzn60bUd5lSA52MaKwbsUpvNlXOI=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/jammy-updates/main/binary-amd64/Packages.xz";
hash = "sha256-I57YuLZ458RljXfp1xFxqQLGNJh9uu8kQC0hc88XZro=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/jammy-updates/universe/binary-amd64/Packages.xz";
hash = "sha256-ZXobWMi7tkakZ89GoyKpiRhRxMRXud0DOerSfzz5CPE=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/jammy-security/main/binary-amd64/Packages.xz";
hash = "sha256-cifTPY1iyckkaLd7dp+VPRlF0viWKrWXhM8HVWaMuUw=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/jammy-security/universe/binary-amd64/Packages.xz";
hash = "sha256-LTSOGbzkv0KrF2JM6oVT1Ml2KQkySXMbKNMBb9AyfQM=";
})
];
urlPrefix = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z";
packages = commonDebPackages ++ [
"diffutils"
"libc-bin"
];
};
ubuntu2404x86_64 = {
name = "ubuntu-24.04-noble-amd64";
fullName = "Ubuntu 24.04 Noble (amd64)";
packagesLists = [
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/noble/main/binary-amd64/Packages.xz";
hash = "sha256-KmoZnhAxpcJ5yzRmRtWUmT81scA91KgqqgMjmA3ZJFE=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/noble/universe/binary-amd64/Packages.xz";
hash = "sha256-upBX+huRQ4zIodJoCNAMhTif4QHQwUliVN+XI2QFWZo=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/noble-updates/main/binary-amd64/Packages.xz";
hash = "sha256-leBJ29a2C2qdIPdjSSuwkHKUSq8GEC9L0DgdxHWZ55s=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/noble-updates/universe/binary-amd64/Packages.xz";
hash = "sha256-CWYA0A4ytptWdClW3ACdIH4hKscblDh5OgxExP4VdJA=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/noble-security/main/binary-amd64/Packages.xz";
hash = "sha256-TYs8ugCYqzOleH2OebdrpB8E68PfxB+7sRb+PlfANEo=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z/dists/noble-security/universe/binary-amd64/Packages.xz";
hash = "sha256-bK9R8CUjLQ1V4GP7/KqZooSnKHF5+T5SuBs0butC82M=";
})
];
urlPrefix = "https://snapshot.ubuntu.com/ubuntu/20260101T000000Z";
packages = commonDebPackages ++ [
"diffutils"
"libc-bin"
];
};
ubuntu2604x86_64 = {
name = "ubuntu-26.04-resolute-amd64";
fullName = "Ubuntu 26.04 Resolute (amd64)";
packagesLists = [
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260515T222303Z/dists/resolute/main/binary-amd64/Packages.xz";
hash = "sha256-7ZrEHLJj767MWgagdC3FZXDi+1/5TE8uSy+9zd1zzyQ=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260515T222303Z/dists/resolute/universe/binary-amd64/Packages.xz";
hash = "sha256-FYe+htZtOFQjJSFeDhCfdb1pXI8k15Os4nYgOKatWB4=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260515T222303Z/dists/resolute-updates/main/binary-amd64/Packages.xz";
hash = "sha256-xaUdPgtH3jCgTJXYUbksMHvzt6jj6YfdzSAb+91tQNw=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260515T222303Z/dists/resolute-updates/universe/binary-amd64/Packages.xz";
hash = "sha256-gXEKlgpgyrcnIhYwz1vxypFNX50EMbwhmidbDvUruKc=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260515T222303Z/dists/resolute-security/main/binary-amd64/Packages.xz";
hash = "sha256-tzAvbwp+/6snpL8TtbtTx2kEL2f+XfGAwDCl/r6ka6Y=";
})
(fetchurl {
url = "https://snapshot.ubuntu.com/ubuntu/20260515T222303Z/dists/resolute-security/universe/binary-amd64/Packages.xz";
hash = "sha256-gXEKlgpgyrcnIhYwz1vxypFNX50EMbwhmidbDvUruKc=";
})
];
urlPrefix = "https://snapshot.ubuntu.com/ubuntu/20260515T222303Z";
packages = commonDebPackages ++ [
"diffutils"
"libc-bin"
];
};
debian11i386 = {
name = "debian-11.11-bullseye-i386";
fullName = "Debian 11.11 Bullseye (i386)";
packagesList = fetchurl {
url = "https://snapshot.debian.org/archive/debian/20260105T082626Z/dists/bullseye/main/binary-i386/Packages.xz";
hash = "sha256-kUg1VBUO6co/5bKloxncta49191oCeF05Hm399+UuDA=";
};
urlPrefix = "https://snapshot.debian.org/archive/debian/20260105T082626Z";
packages = commonDebianPackages;
};
debian11x86_64 = {
name = "debian-11.11-bullseye-amd64";
fullName = "Debian 11.11 Bullseye (amd64)";
packagesList = fetchurl {
url = "https://snapshot.debian.org/archive/debian/20260105T082626Z/dists/bullseye/main/binary-amd64/Packages.xz";
hash = "sha256-HDQFREKX6thkcRwY5kvOSBDbY7SDQKL52BGC2fI1rXE=";
};
urlPrefix = "https://snapshot.debian.org/archive/debian/20260105T082626Z";
packages = commonDebianPackages;
};
debian12i386 = {
name = "debian-12.12-bookworm-i386";
fullName = "Debian 12.12 Bookworm (i386)";
packagesLists = [
(fetchurl {
url = "https://snapshot.debian.org/archive/debian/20260105T082626Z/dists/bookworm/main/binary-i386/Packages.xz";
hash = "sha256-nIijsNoHUYkrL6eiwN4FCLHnJy/Bv/RMvnbMIHvieVI=";
})
(fetchurl {
url = "https://snapshot.debian.org/archive/debian/20260105T082626Z/dists/bookworm-backports/main/binary-i386/Packages.xz";
hash = "sha256-/ja7+DNIKc2ZUIXiocTjLbaD2EPsfeyZcd5ndEMapp4=";
})
];
urlPrefix = "https://snapshot.debian.org/archive/debian/20260105T082626Z";
packages = commonDebianPackages;
};
debian12x86_64 = {
name = "debian-12.12-bookworm-amd64";
fullName = "Debian 12.12 Bookworm (amd64)";
packagesLists = [
(fetchurl {
url = "https://snapshot.debian.org/archive/debian/20260105T082626Z/dists/bookworm/main/binary-amd64/Packages.xz";
hash = "sha256-PfjQeu3tXmXZhH7foSD6WyFrvY4PfwSN/v5pBeShIBE=";
})
(fetchurl {
url = "https://snapshot.debian.org/archive/debian/20260105T082626Z/dists/bookworm-backports/main/binary-amd64/Packages.xz";
hash = "sha256-S3NSvw1kX2zxzMh+WYhY58VUR7iLrTEIuXwwSK6itIs=";
})
];
urlPrefix = "https://snapshot.debian.org/archive/debian/20260105T082626Z";
packages = commonDebianPackages;
};
debian13i386 = {
name = "debian-13.2-trixie-i386";
fullName = "Debian 13.2 Trixie (i386)";
packagesLists = [
(fetchurl {
url = "https://snapshot.debian.org/archive/debian/20260105T082626Z/dists/trixie/main/binary-i386/Packages.xz";
hash = "sha256-9zozvFZoWiv3wNe9rb+kPwSOgc5G5f4zmNpdoet5A78=";
})
(fetchurl {
url = "https://snapshot.debian.org/archive/debian/20260105T082626Z/dists/trixie-backports/main/binary-i386/Packages.xz";
hash = "sha256-hEBAQ73Jnv8zp9YvNXWLEObyrSlQNBNBj/XoofJL7eI=";
})
];
urlPrefix = "https://snapshot.debian.org/archive/debian/20260105T082626Z";
packages = commonDebianPackages;
};
debian13x86_64 = {
name = "debian-13.2-trixie-amd64";
fullName = "Debian 13.2 Trixie (amd64)";
packagesLists = [
(fetchurl {
url = "https://snapshot.debian.org/archive/debian/20260105T082626Z/dists/trixie/main/binary-amd64/Packages.xz";
hash = "sha256-g7f+tKljUXAC4gxJfzSC8+j0GbiwRZjonv25tYuvxtU=";
})
(fetchurl {
url = "https://snapshot.debian.org/archive/debian/20260105T082626Z/dists/trixie-backports/main/binary-amd64/Packages.xz";
hash = "sha256-9OoR36FsyK7MQMLHLFMRJ9O11WKq9JCfGwnprpztxNw=";
})
];
urlPrefix = "https://snapshot.debian.org/archive/debian/20260105T082626Z";
packages = commonDebianPackages;
};
};
# Base packages for all RHEL-family distros (Fedora, Rocky, Alma, etc.)
baseRHELFamilyPackages = [
"autoconf"
"automake"
"basesystem"
"bzip2"
"curl"
"diffutils"
"findutils"
"gawk"
"gcc-c++"
"glibc-gconv-extra"
"gzip"
"make"
"patch"
"perl"
"rpm"
"rpm-build"
"tar"
"unzip"
];
commonFedoraPackages = baseRHELFamilyPackages ++ [
"annobin-plugin-gcc"
"fedora-release"
"gcc-plugin-annobin"
"pkgconf-pkg-config"
];
commonRockyPackages = baseRHELFamilyPackages ++ [
"gcc-plugin-annobin"
"pkgconf"
"rocky-release"
];
commonAlmaPackages = baseRHELFamilyPackages ++ [
"almalinux-release"
"gcc-plugin-annobin"
"pkgconf"
];
commonOraclePackages = baseRHELFamilyPackages ++ [
"gcc-plugin-annobin"
"oraclelinux-release"
"pkgconf"
];
commonAmazonPackages = baseRHELFamilyPackages ++ [
"gcc-plugin-annobin"
"pkgconf"
"system-release"
];
# Common packages for openSUSE images.
commonOpenSUSEPackages = [
"aaa_base"
"autoconf"
"automake"
"bzip2"
"curl"
"diffutils"
"findutils"
"gawk"
"gcc-c++"
"gzip"
"make"
"patch"
"perl"
"pkg-config"
"rpm"
"tar"
"unzip"
"util-linux"
"gnu-getopt"
];
# Common packages for Debian/Ubuntu images.
commonDebPackages = [
"base-passwd"
"dpkg"
"libc6-dev"
"perl"
"bash"
"dash"
"gzip"
"bzip2"
"tar"
"grep"
"mawk"
"sed"
"findutils"
"g++"
"make"
"curl"
"patch"
"locales"
"coreutils"
# Needed by checkinstall:
"util-linux"
"file"
"dpkg-dev"
"pkg-config"
# Needed because it provides /etc/login.defs, whose absence causes
# the "passwd" post-installs script to fail.
"login"
"passwd"
];
commonDebianPackages = commonDebPackages ++ [
"sysvinit"
"diff"
];
/*
A set of functions that build the Linux distributions specified
in `rpmDistros' and `debDistros'. For instance,
`diskImageFuns.ubuntu1004x86_64 { }' builds an Ubuntu 10.04 disk
image containing the default packages specified above. Overrides
of the default image parameters can be given. In particular,
`extraPackages' specifies the names of additional packages from
the distribution that should be included in the image; `packages'
allows the entire set of packages to be overridden; and `size'
sets the size of the disk in MiB (1024*1024 bytes). E.g.,
`diskImageFuns.ubuntu1004x86_64 { extraPackages = ["firefox"];
size = 8192; }' builds an 8 GiB image containing Firefox in
addition to the default packages.
*/
diskImageFuns =
(lib.mapAttrs (
name: as: as2:
makeImageFromRPMDist (as // as2)
) rpmDistros)
// (lib.mapAttrs (
name: as: as2:
makeImageFromDebDist (as // as2)
) debDistros);
# Shorthand for `diskImageFuns.<attr> { extraPackages = ... }'.
diskImageExtraFuns = lib.mapAttrs (
name: f: extraPackages:
f { inherit extraPackages; }
) diskImageFuns;
/*
Default disk images generated from the `rpmDistros' and
`debDistros' sets.
*/
diskImages = lib.mapAttrs (name: f: f { }) diskImageFuns;
in
{
inherit
buildRPM
commonDebPackages
commonDebianPackages
commonFedoraPackages
commonOpenSUSEPackages
createEmptyImage
debClosureGenerator
debDistros
defaultCreateRootFS
diskImageExtraFuns
diskImageFuns
diskImages
extractFs
extractMTDfs
fillDiskWithDebs
fillDiskWithRPMs
hd
initrd
initrdUtils
makeImageFromDebDist
makeImageFromRPMDist
makeImageTestScript
modulesClosure
qemu
qemuCommandLinux
rpmClosureGenerator
rpmDistros
runInLinuxImage
runInLinuxVM
stage1Init
stage2Init
vmRunCommand
;
}