pytrainer: unpin python 3.10
[NixPkgs.git] / pkgs / build-support / vm / default.nix
blob7ba67af5cf5a1bae8eea5f4ad477b6fea9340732
1 { lib
2 , pkgs
3 , customQemu ? null
4 , kernel ? pkgs.linux
5 , img ? pkgs.stdenv.hostPlatform.linux-kernel.target
6 , storeDir ? builtins.storeDir
7 , rootModules ?
8     [ "virtio_pci" "virtio_mmio" "virtio_blk" "virtio_balloon" "virtio_rng" "ext4" "unix" "9p" "9pnet_virtio" "crc32c_generic" ]
9       ++ pkgs.lib.optional pkgs.stdenv.hostPlatform.isx86 "rtc_cmos"
12 let
13   inherit (pkgs) bash bashInteractive busybox cpio coreutils e2fsprogs fetchurl kmod rpm
14     stdenv util-linux
15     buildPackages writeScript writeText runCommand;
17 rec {
18   qemu-common = import ../../../nixos/lib/qemu-common.nix { inherit lib pkgs; };
20   qemu = buildPackages.qemu_kvm;
22   modulesClosure = pkgs.makeModulesClosure {
23     inherit kernel rootModules;
24     firmware = kernel;
25   };
28   hd = "vda"; # either "sda" or "vda"
30   initrdUtils = runCommand "initrd-utils"
31     { nativeBuildInputs = [ buildPackages.nukeReferences ];
32       allowedReferences = [ "out" modulesClosure ]; # prevent accidents like glibc being included in the initrd
33     }
34     ''
35       mkdir -p $out/bin
36       mkdir -p $out/lib
38       # Copy what we need from Glibc.
39       cp -p \
40         ${pkgs.stdenv.cc.libc}/lib/ld-*.so.? \
41         ${pkgs.stdenv.cc.libc}/lib/libc.so.* \
42         ${pkgs.stdenv.cc.libc}/lib/libm.so.* \
43         ${pkgs.stdenv.cc.libc}/lib/libresolv.so.* \
44         ${pkgs.stdenv.cc.libc}/lib/libpthread.so.* \
45         ${pkgs.zstd.out}/lib/libzstd.so.* \
46         ${pkgs.xz.out}/lib/liblzma.so.* \
47         $out/lib
49       # Copy BusyBox.
50       cp -pd ${pkgs.busybox}/bin/* $out/bin
51       cp -pd ${pkgs.kmod}/bin/* $out/bin
53       # Run patchelf to make the programs refer to the copied libraries.
54       for i in $out/bin/* $out/lib/*; do if ! test -L $i; then nuke-refs $i; fi; done
56       for i in $out/bin/*; do
57           if [ -f "$i" -a ! -L "$i" ]; then
58               echo "patching $i..."
59               patchelf --set-interpreter $out/lib/ld-*.so.? --set-rpath $out/lib $i || true
60           fi
61       done
63       find $out/lib -type f \! -name 'ld*.so.?' | while read i; do
64         echo "patching $i..."
65         patchelf --set-rpath $out/lib $i
66       done
67     ''; # */
70   stage1Init = writeScript "vm-run-stage1" ''
71     #! ${initrdUtils}/bin/ash -e
73     export PATH=${initrdUtils}/bin
75     mkdir /etc
76     echo -n > /etc/fstab
78     mount -t proc none /proc
79     mount -t sysfs none /sys
81     echo 2 > /proc/sys/vm/panic_on_oom
83     for o in $(cat /proc/cmdline); do
84       case $o in
85         mountDisk=*)
86           mountDisk=''${mountDisk#mountDisk=}
87           ;;
88         command=*)
89           set -- $(IFS==; echo $o)
90           command=$2
91           ;;
92         out=*)
93           set -- $(IFS==; echo $o)
94           export out=$2
95           ;;
96       esac
97     done
99     echo "loading kernel modules..."
100     for i in $(cat ${modulesClosure}/insmod-list); do
101       insmod $i || echo "warning: unable to load $i"
102     done
104     mount -t devtmpfs devtmpfs /dev
105     ln -s /proc/self/fd /dev/fd
106     ln -s /proc/self/fd/0 /dev/stdin
107     ln -s /proc/self/fd/1 /dev/stdout
108     ln -s /proc/self/fd/2 /dev/stderr
110     ifconfig lo up
112     mkdir /fs
114     if test -z "$mountDisk"; then
115       mount -t tmpfs none /fs
116     elif [[ -e "$mountDisk" ]]; then
117       mount "$mountDisk" /fs
118     else
119       mount /dev/${hd} /fs
120     fi
122     mkdir -p /fs/dev
123     mount -o bind /dev /fs/dev
125     mkdir -p /fs/dev/shm /fs/dev/pts
126     mount -t tmpfs -o "mode=1777" none /fs/dev/shm
127     mount -t devpts none /fs/dev/pts
129     echo "mounting Nix store..."
130     mkdir -p /fs${storeDir}
131     mount -t 9p store /fs${storeDir} -o trans=virtio,version=9p2000.L,cache=loose,msize=131072
133     mkdir -p /fs/tmp /fs/run /fs/var
134     mount -t tmpfs -o "mode=1777" none /fs/tmp
135     mount -t tmpfs -o "mode=755" none /fs/run
136     ln -sfn /run /fs/var/run
138     echo "mounting host's temporary directory..."
139     mkdir -p /fs/tmp/xchg
140     mount -t 9p xchg /fs/tmp/xchg -o trans=virtio,version=9p2000.L,msize=131072
142     mkdir -p /fs/proc
143     mount -t proc none /fs/proc
145     mkdir -p /fs/sys
146     mount -t sysfs none /fs/sys
148     mkdir -p /fs/etc
149     ln -sf /proc/mounts /fs/etc/mtab
150     echo "127.0.0.1 localhost" > /fs/etc/hosts
151     # Ensures tools requiring /etc/passwd will work (e.g. nix)
152     if [ ! -e /fs/etc/passwd ]; then
153       echo "root:x:0:0:System administrator:/root:/bin/sh" > /fs/etc/passwd
154     fi
156     echo "starting stage 2 ($command)"
157     exec switch_root /fs $command $out
158   '';
161   initrd = pkgs.makeInitrd {
162     contents = [
163       { object = stage1Init;
164         symlink = "/init";
165       }
166     ];
167   };
170   stage2Init = writeScript "vm-run-stage2" ''
171     #! ${bash}/bin/sh
172     source /tmp/xchg/saved-env
174     # Set the system time from the hardware clock.  Works around an
175     # apparent KVM > 1.5.2 bug.
176     ${util-linux}/bin/hwclock -s
178     export NIX_STORE=${storeDir}
179     export NIX_BUILD_TOP=/tmp
180     export TMPDIR=/tmp
181     export PATH=/empty
182     out="$1"
183     cd "$NIX_BUILD_TOP"
185     if ! test -e /bin/sh; then
186       ${coreutils}/bin/mkdir -p /bin
187       ${coreutils}/bin/ln -s ${bash}/bin/sh /bin/sh
188     fi
190     # Set up automatic kernel module loading.
191     export MODULE_DIR=${kernel}/lib/modules/
192     ${coreutils}/bin/cat <<EOF > /run/modprobe
193     #! ${bash}/bin/sh
194     export MODULE_DIR=$MODULE_DIR
195     exec ${kmod}/bin/modprobe "\$@"
196     EOF
197     ${coreutils}/bin/chmod 755 /run/modprobe
198     echo /run/modprobe > /proc/sys/kernel/modprobe
200     # For debugging: if this is the second time this image is run,
201     # then don't start the build again, but instead drop the user into
202     # an interactive shell.
203     if test -n "$origBuilder" -a ! -e /.debug; then
204       exec < /dev/null
205       ${coreutils}/bin/touch /.debug
206       $origBuilder $origArgs
207       echo $? > /tmp/xchg/in-vm-exit
209       ${busybox}/bin/mount -o remount,ro dummy /
211       ${busybox}/bin/poweroff -f
212     else
213       export PATH=/bin:/usr/bin:${coreutils}/bin
214       echo "Starting interactive shell..."
215       echo "(To run the original builder: \$origBuilder \$origArgs)"
216       exec ${busybox}/bin/setsid ${bashInteractive}/bin/bash < /dev/${qemu-common.qemuSerialDevice} &> /dev/${qemu-common.qemuSerialDevice}
217     fi
218   '';
221   qemuCommandLinux = ''
222     ${if (customQemu != null) then customQemu else (qemu-common.qemuBinary qemu)} \
223       -nographic -no-reboot \
224       -device virtio-rng-pci \
225       -virtfs local,path=${storeDir},security_model=none,mount_tag=store \
226       -virtfs local,path=$TMPDIR/xchg,security_model=none,mount_tag=xchg \
227       ''${diskImage:+-drive file=$diskImage,if=virtio,cache=unsafe,werror=report} \
228       -kernel ${kernel}/${img} \
229       -initrd ${initrd}/initrd \
230       -append "console=${qemu-common.qemuSerialDevice} panic=1 command=${stage2Init} out=$out mountDisk=$mountDisk loglevel=4" \
231       $QEMU_OPTS
232   '';
235   vmRunCommand = qemuCommand: writeText "vm-run" ''
236     export > saved-env
238     PATH=${coreutils}/bin
239     mkdir xchg
240     mv saved-env xchg/
242     eval "$preVM"
244     if [ "$enableParallelBuilding" = 1 ]; then
245       if [ ''${NIX_BUILD_CORES:-0} = 0 ]; then
246         QEMU_OPTS+=" -smp cpus=$(nproc)"
247       else
248         QEMU_OPTS+=" -smp cpus=$NIX_BUILD_CORES"
249       fi
250     fi
252     # Write the command to start the VM to a file so that the user can
253     # debug inside the VM if the build fails (when Nix is called with
254     # the -K option to preserve the temporary build directory).
255     cat > ./run-vm <<EOF
256     #! ${bash}/bin/sh
257     ''${diskImage:+diskImage=$diskImage}
258     TMPDIR=$TMPDIR
259     cd $TMPDIR
260     ${qemuCommand}
261     EOF
263     mkdir -p -m 0700 $out
265     chmod +x ./run-vm
266     source ./run-vm
268     if ! test -e xchg/in-vm-exit; then
269       echo "Virtual machine didn't produce an exit code."
270       exit 1
271     fi
273     exitCode="$(cat xchg/in-vm-exit)"
274     if [ "$exitCode" != "0" ]; then
275       exit "$exitCode"
276     fi
278     eval "$postVM"
279   '';
281   /*
282     A bash script fragment that produces a disk image at `destination`.
283    */
284   createEmptyImage = {
285     # Disk image size in MiB
286     size,
287     # Name that will be written to ${destination}/nix-support/full-name
288     fullName,
289     # Where to write the image files, defaulting to $out
290     destination ? "$out"
291   }: ''
292     mkdir -p ${destination}
293     diskImage=${destination}/disk-image.qcow2
294     ${qemu}/bin/qemu-img create -f qcow2 $diskImage "${toString size}M"
296     mkdir ${destination}/nix-support
297     echo "${fullName}" > ${destination}/nix-support/full-name
298   '';
301   defaultCreateRootFS = ''
302     mkdir /mnt
303     ${e2fsprogs}/bin/mkfs.ext4 /dev/${hd}
304     ${util-linux}/bin/mount -t ext4 /dev/${hd} /mnt
306     if test -e /mnt/.debug; then
307       exec ${bash}/bin/sh
308     fi
309     touch /mnt/.debug
311     mkdir /mnt/proc /mnt/dev /mnt/sys
312   '';
315   /* Run a derivation in a Linux virtual machine (using Qemu/KVM).  By
316      default, there is no disk image; the root filesystem is a tmpfs,
317      and the nix store is shared with the host (via the 9P protocol).
318      Thus, any pure Nix derivation should run unmodified, e.g. the
319      call
321        runInLinuxVM patchelf
323      will build the derivation `patchelf' inside a VM.  The attribute
324      `preVM' can optionally contain a shell command to be evaluated
325      *before* the VM is started (i.e., on the host).  The attribute
326      `memSize' specifies the memory size of the VM in megabytes,
327      defaulting to 512.  The attribute `diskImage' can optionally
328      specify a file system image to be attached to /dev/sda.  (Note
329      that currently we expect the image to contain a filesystem, not a
330      full disk image with a partition table etc.)
332      If the build fails and Nix is run with the `-K' option, a script
333      `run-vm' will be left behind in the temporary build directory
334      that allows you to boot into the VM and debug it interactively. */
336   runInLinuxVM = drv: lib.overrideDerivation drv ({ memSize ? 512, QEMU_OPTS ? "", args, builder, ... }: {
337     requiredSystemFeatures = [ "kvm" ];
338     builder = "${bash}/bin/sh";
339     args = ["-e" (vmRunCommand qemuCommandLinux)];
340     origArgs = args;
341     origBuilder = builder;
342     QEMU_OPTS = "${QEMU_OPTS} -m ${toString memSize}";
343     passAsFile = []; # HACK fix - see https://github.com/NixOS/nixpkgs/issues/16742
344   });
347   extractFs = {file, fs ? null} :
348     runInLinuxVM (
349     stdenv.mkDerivation {
350       name = "extract-file";
351       buildInputs = [ util-linux ];
352       buildCommand = ''
353         ln -s ${kernel}/lib /lib
354         ${kmod}/bin/modprobe loop
355         ${kmod}/bin/modprobe ext4
356         ${kmod}/bin/modprobe hfs
357         ${kmod}/bin/modprobe hfsplus
358         ${kmod}/bin/modprobe squashfs
359         ${kmod}/bin/modprobe iso9660
360         ${kmod}/bin/modprobe ufs
361         ${kmod}/bin/modprobe cramfs
363         mkdir -p $out
364         mkdir -p tmp
365         mount -o loop,ro,ufstype=44bsd ${lib.optionalString (fs != null) "-t ${fs} "}${file} tmp ||
366           mount -o loop,ro ${lib.optionalString (fs != null) "-t ${fs} "}${file} tmp
367         cp -Rv tmp/* $out/ || exit 0
368       '';
369     });
372   extractMTDfs = {file, fs ? null} :
373     runInLinuxVM (
374     stdenv.mkDerivation {
375       name = "extract-file-mtd";
376       buildInputs = [ pkgs.util-linux pkgs.mtdutils ];
377       buildCommand = ''
378         ln -s ${kernel}/lib /lib
379         ${kmod}/bin/modprobe mtd
380         ${kmod}/bin/modprobe mtdram total_size=131072
381         ${kmod}/bin/modprobe mtdchar
382         ${kmod}/bin/modprobe mtdblock
383         ${kmod}/bin/modprobe jffs2
384         ${kmod}/bin/modprobe zlib
386         mkdir -p $out
387         mkdir -p tmp
389         dd if=${file} of=/dev/mtd0
390         mount ${lib.optionalString (fs != null) "-t ${fs} "}/dev/mtdblock0 tmp
392         cp -R tmp/* $out/
393       '';
394     });
397   /* Like runInLinuxVM, but run the build not using the stdenv from
398      the Nix store, but using the tools provided by /bin, /usr/bin
399      etc. from the specified filesystem image, which typically is a
400      filesystem containing a non-NixOS Linux distribution. */
402   runInLinuxImage = drv: runInLinuxVM (lib.overrideDerivation drv (attrs: {
403     mountDisk = attrs.mountDisk or true;
405     /* Mount `image' as the root FS, but use a temporary copy-on-write
406        image since we don't want to (and can't) write to `image'. */
407     preVM = ''
408       diskImage=$(pwd)/disk-image.qcow2
409       origImage=${attrs.diskImage}
410       if test -d "$origImage"; then origImage="$origImage/disk-image.qcow2"; fi
411       ${qemu}/bin/qemu-img create -F ${attrs.diskImageFormat} -b "$origImage" -f qcow2 $diskImage
412     '';
414     /* Inside the VM, run the stdenv setup script normally, but at the
415        very end set $PATH and $SHELL to the `native' paths for the
416        distribution inside the VM. */
417     postHook = ''
418       PATH=/usr/bin:/bin:/usr/sbin:/sbin
419       SHELL=/bin/sh
420       eval "$origPostHook"
421     '';
423     origPostHook = lib.optionalString (attrs ? postHook) attrs.postHook;
425     /* Don't run Nix-specific build steps like patchelf. */
426     fixupPhase = "true";
427   }));
430   /* Create a filesystem image of the specified size and fill it with
431      a set of RPM packages. */
433   fillDiskWithRPMs =
434     { size ? 4096, rpms, name, fullName, preInstall ? "", postInstall ? ""
435     , runScripts ? true, createRootFS ? defaultCreateRootFS
436     , QEMU_OPTS ? "", memSize ? 512
437     , unifiedSystemDir ? false
438     }:
440     runInLinuxVM (stdenv.mkDerivation {
441       inherit name preInstall postInstall rpms QEMU_OPTS memSize;
442       preVM = createEmptyImage {inherit size fullName;};
444       buildCommand = ''
445         ${createRootFS}
447         chroot=$(type -tP chroot)
449         # Make the Nix store available in /mnt, because that's where the RPMs live.
450         mkdir -p /mnt${storeDir}
451         ${util-linux}/bin/mount -o bind ${storeDir} /mnt${storeDir}
453         # Newer distributions like Fedora 18 require /lib etc. to be
454         # symlinked to /usr.
455         ${lib.optionalString unifiedSystemDir ''
456           mkdir -p /mnt/usr/bin /mnt/usr/sbin /mnt/usr/lib /mnt/usr/lib64
457           ln -s /usr/bin /mnt/bin
458           ln -s /usr/sbin /mnt/sbin
459           ln -s /usr/lib /mnt/lib
460           ln -s /usr/lib64 /mnt/lib64
461           ${util-linux}/bin/mount -t proc none /mnt/proc
462         ''}
464         echo "unpacking RPMs..."
465         set +o pipefail
466         for i in $rpms; do
467             echo "$i..."
468             ${rpm}/bin/rpm2cpio "$i" | chroot /mnt ${cpio}/bin/cpio -i --make-directories --unconditional
469         done
471         eval "$preInstall"
473         echo "initialising RPM DB..."
474         PATH=/usr/bin:/bin:/usr/sbin:/sbin $chroot /mnt \
475           ldconfig -v || true
476         PATH=/usr/bin:/bin:/usr/sbin:/sbin $chroot /mnt \
477           rpm --initdb
479         ${util-linux}/bin/mount -o bind /tmp /mnt/tmp
481         echo "installing RPMs..."
482         PATH=/usr/bin:/bin:/usr/sbin:/sbin $chroot /mnt \
483           rpm -iv --nosignature ${lib.optionalString (!runScripts) "--noscripts"} $rpms
485         echo "running post-install script..."
486         eval "$postInstall"
488         rm /mnt/.debug
490         ${util-linux}/bin/umount /mnt${storeDir} /mnt/tmp ${lib.optionalString unifiedSystemDir "/mnt/proc"}
491         ${util-linux}/bin/umount /mnt
492       '';
494       passthru = { inherit fullName; };
495     });
498   /* Generate a script that can be used to run an interactive session
499      in the given image. */
501   makeImageTestScript = image: writeScript "image-test" ''
502     #! ${bash}/bin/sh
503     if test -z "$1"; then
504       echo "Syntax: $0 <copy-on-write-temp-file>"
505       exit 1
506     fi
507     diskImage="$1"
508     if ! test -e "$diskImage"; then
509       ${qemu}/bin/qemu-img create -b ${image}/disk-image.qcow2 -f qcow2 -F qcow2 "$diskImage"
510     fi
511     export TMPDIR=$(mktemp -d)
512     export out=/dummy
513     export origBuilder=
514     export origArgs=
515     mkdir $TMPDIR/xchg
516     export > $TMPDIR/xchg/saved-env
517     mountDisk=1
518     ${qemuCommandLinux}
519   '';
522   /* Build RPM packages from the tarball `src' in the Linux
523      distribution installed in the filesystem `diskImage'.  The
524      tarball must contain an RPM specfile. */
526   buildRPM = attrs: runInLinuxImage (stdenv.mkDerivation ({
527     prePhases = [ "prepareImagePhase" "sysInfoPhase" ];
528     dontConfigure = true;
530     outDir = "rpms/${attrs.diskImage.name}";
532     prepareImagePhase = ''
533       if test -n "$extraRPMs"; then
534         for rpmdir in $extraRPMs ; do
535           rpm -iv $(ls $rpmdir/rpms/*/*.rpm | grep -v 'src\.rpm' | sort | head -1)
536         done
537       fi
538     '';
540     sysInfoPhase = ''
541       echo "System/kernel: $(uname -a)"
542       if test -e /etc/fedora-release; then echo "Fedora release: $(cat /etc/fedora-release)"; fi
543       if test -e /etc/SuSE-release; then echo "SUSE release: $(cat /etc/SuSE-release)"; fi
544       echo "installed RPM packages"
545       rpm -qa --qf "%{Name}-%{Version}-%{Release} (%{Arch}; %{Distribution}; %{Vendor})\n"
546     '';
548     buildPhase = ''
549       eval "$preBuild"
551       srcName="$(rpmspec --srpm -q --qf '%{source}' *.spec)"
552       cp "$src" "$srcName" # `ln' doesn't work always work: RPM requires that the file is owned by root
554       export HOME=/tmp/home
555       mkdir $HOME
557       rpmout=/tmp/rpmout
558       mkdir $rpmout $rpmout/SPECS $rpmout/BUILD $rpmout/RPMS $rpmout/SRPMS
560       echo "%_topdir $rpmout" >> $HOME/.rpmmacros
562       if [ `uname -m` = i686 ]; then extra="--target i686-linux"; fi
563       rpmbuild -vv $extra -ta "$srcName"
565       eval "$postBuild"
566     '';
568     installPhase = ''
569       eval "$preInstall"
571       mkdir -p $out/$outDir
572       find $rpmout -name "*.rpm" -exec cp {} $out/$outDir \;
574       for i in $out/$outDir/*.rpm; do
575         echo "Generated RPM/SRPM: $i"
576         rpm -qip $i
577       done
579       eval "$postInstall"
580     ''; # */
581   } // attrs));
584   /* Create a filesystem image of the specified size and fill it with
585      a set of Debian packages.  `debs' must be a list of list of
586      .deb files, namely, the Debian packages grouped together into
587      strongly connected components.  See deb/deb-closure.nix. */
589   fillDiskWithDebs =
590     { size ? 4096, debs, name, fullName, postInstall ? null, createRootFS ? defaultCreateRootFS
591     , QEMU_OPTS ? "", memSize ? 512, ... }@args:
593     runInLinuxVM (stdenv.mkDerivation ({
594       inherit name postInstall QEMU_OPTS memSize;
596       debs = (lib.intersperse "|" debs);
598       preVM = createEmptyImage {inherit size fullName;};
600       buildCommand = ''
601         ${createRootFS}
603         PATH=$PATH:${lib.makeBinPath [ pkgs.dpkg pkgs.glibc pkgs.xz ]}
605         # Unpack the .debs.  We do this to prevent pre-install scripts
606         # (which have lots of circular dependencies) from barfing.
607         echo "unpacking Debs..."
609         for deb in $debs; do
610           if test "$deb" != "|"; then
611             echo "$deb..."
612             dpkg-deb --extract "$deb" /mnt
613           fi
614         done
616         # Make the Nix store available in /mnt, because that's where the .debs live.
617         mkdir -p /mnt/inst${storeDir}
618         ${util-linux}/bin/mount -o bind ${storeDir} /mnt/inst${storeDir}
619         ${util-linux}/bin/mount -o bind /proc /mnt/proc
620         ${util-linux}/bin/mount -o bind /dev /mnt/dev
622         # Misc. files/directories assumed by various packages.
623         echo "initialising Dpkg DB..."
624         touch /mnt/etc/shells
625         touch /mnt/var/lib/dpkg/status
626         touch /mnt/var/lib/dpkg/available
627         touch /mnt/var/lib/dpkg/diversions
629         # Now install the .debs.  This is basically just to register
630         # them with dpkg and to make their pre/post-install scripts
631         # run.
632         echo "installing Debs..."
634         export DEBIAN_FRONTEND=noninteractive
636         oldIFS="$IFS"
637         IFS="|"
638         for component in $debs; do
639           IFS="$oldIFS"
640           echo
641           echo ">>> INSTALLING COMPONENT: $component"
642           debs=
643           for i in $component; do
644             debs="$debs /inst/$i";
645           done
646           chroot=$(type -tP chroot)
648           # Create a fake start-stop-daemon script, as done in debootstrap.
649           mv "/mnt/sbin/start-stop-daemon" "/mnt/sbin/start-stop-daemon.REAL"
650           echo "#!/bin/true" > "/mnt/sbin/start-stop-daemon"
651           chmod 755 "/mnt/sbin/start-stop-daemon"
653           PATH=/usr/bin:/bin:/usr/sbin:/sbin $chroot /mnt \
654             /usr/bin/dpkg --install --force-all $debs < /dev/null || true
656           # Move the real start-stop-daemon back into its place.
657           mv "/mnt/sbin/start-stop-daemon.REAL" "/mnt/sbin/start-stop-daemon"
658         done
660         echo "running post-install script..."
661         eval "$postInstall"
663         rm /mnt/.debug
665         ${util-linux}/bin/umount /mnt/inst${storeDir}
666         ${util-linux}/bin/umount /mnt/proc
667         ${util-linux}/bin/umount /mnt/dev
668         ${util-linux}/bin/umount /mnt
669       '';
671       passthru = { inherit fullName; };
672     } // args));
675   /* Generate a Nix expression containing fetchurl calls for the
676      closure of a set of top-level RPM packages from the
677      `primary.xml.gz' file of a Fedora or openSUSE distribution. */
679   rpmClosureGenerator =
680     {name, packagesLists, urlPrefixes, packages, archs ? []}:
681     assert (builtins.length packagesLists) == (builtins.length urlPrefixes);
682     runCommand "${name}.nix" {
683       nativeBuildInputs = [ buildPackages.perl buildPackages.perlPackages.XMLSimple ];
684       inherit archs;
685     } ''
686       ${lib.concatImapStrings (i: pl: ''
687         gunzip < ${pl} > ./packages_${toString i}.xml
688       '') packagesLists}
689       perl -w ${rpm/rpm-closure.pl} \
690         ${lib.concatImapStrings (i: pl: "./packages_${toString i}.xml ${pl.snd} " ) (lib.zipLists packagesLists urlPrefixes)} \
691         ${toString packages} > $out
692     '';
695   /* Helper function that combines rpmClosureGenerator and
696      fillDiskWithRPMs to generate a disk image from a set of package
697      names. */
699   makeImageFromRPMDist =
700     { name, fullName, size ? 4096
701     , urlPrefix ? "", urlPrefixes ? [urlPrefix]
702     , packagesList ? "", packagesLists ? [packagesList]
703     , packages, extraPackages ? []
704     , preInstall ? "", postInstall ? "", archs ? ["noarch" "i386"]
705     , runScripts ? true, createRootFS ? defaultCreateRootFS
706     , QEMU_OPTS ? "", memSize ? 512
707     , unifiedSystemDir ? false }:
709     fillDiskWithRPMs {
710       inherit name fullName size preInstall postInstall runScripts createRootFS unifiedSystemDir QEMU_OPTS memSize;
711       rpms = import (rpmClosureGenerator {
712         inherit name packagesLists urlPrefixes archs;
713         packages = packages ++ extraPackages;
714       }) { inherit fetchurl; };
715     };
718   /* Like `rpmClosureGenerator', but now for Debian/Ubuntu releases
719      (i.e. generate a closure from a Packages.bz2 file). */
721   debClosureGenerator =
722     {name, packagesLists, urlPrefix, packages}:
724     runCommand "${name}.nix"
725       { nativeBuildInputs = [ buildPackages.perl buildPackages.dpkg ]; } ''
726       for i in ${toString packagesLists}; do
727         echo "adding $i..."
728         case $i in
729           *.xz | *.lzma)
730             xz -d < $i >> ./Packages
731             ;;
732           *.bz2)
733             bunzip2 < $i >> ./Packages
734             ;;
735           *.gz)
736             gzip -dc < $i >> ./Packages
737             ;;
738         esac
739       done
741       perl -w ${deb/deb-closure.pl} \
742         ./Packages ${urlPrefix} ${toString packages} > $out
743     '';
746   /* Helper function that combines debClosureGenerator and
747      fillDiskWithDebs to generate a disk image from a set of package
748      names. */
750   makeImageFromDebDist =
751     { name, fullName, size ? 4096, urlPrefix
752     , packagesList ? "", packagesLists ? [packagesList]
753     , packages, extraPackages ? [], postInstall ? ""
754     , extraDebs ? [], createRootFS ? defaultCreateRootFS
755     , QEMU_OPTS ? "", memSize ? 512, ... }@args:
757     let
758       expr = debClosureGenerator {
759         inherit name packagesLists urlPrefix;
760         packages = packages ++ extraPackages;
761       };
762     in
763       (fillDiskWithDebs ({
764         inherit name fullName size postInstall createRootFS QEMU_OPTS memSize;
765         debs = import expr {inherit fetchurl;} ++ extraDebs;
766       } // args)) // {inherit expr;};
769   /* The set of supported RPM-based distributions. */
771   rpmDistros = {
773     # Note: no i386 release for Fedora >= 26
774     fedora26x86_64 =
775       let version = "26";
776       in {
777         name = "fedora-${version}-x86_64";
778         fullName = "Fedora ${version} (x86_64)";
779         packagesList = fetchurl rec {
780           url = "mirror://fedora/linux/releases/${version}/Everything/x86_64/os/repodata/${sha256}-primary.xml.gz";
781           sha256 = "880055a50c05b20641530d09b23f64501a000b2f92fe252417c530178730a95e";
782         };
783         urlPrefix = "mirror://fedora/linux/releases/${version}/Everything/x86_64/os";
784         archs = ["noarch" "x86_64"];
785         packages = commonFedoraPackages ++ [ "cronie" "util-linux" ];
786         unifiedSystemDir = true;
787       };
789     fedora27x86_64 =
790       let version = "27";
791       in {
792         name = "fedora-${version}-x86_64";
793         fullName = "Fedora ${version} (x86_64)";
794         packagesList = fetchurl rec {
795           url = "mirror://fedora/linux/releases/${version}/Everything/x86_64/os/repodata/${sha256}-primary.xml.gz";
796           sha256 = "48986ce4583cd09825c6d437150314446f0f49fa1a1bd62dcfa1085295030fe9";
797         };
798         urlPrefix = "mirror://fedora/linux/releases/${version}/Everything/x86_64/os";
799         archs = ["noarch" "x86_64"];
800         packages = commonFedoraPackages ++ [ "cronie" "util-linux" ];
801         unifiedSystemDir = true;
802       };
804     centos6i386 =
805       let version = "6.9";
806       in rec {
807         name = "centos-${version}-i386";
808         fullName = "CentOS ${version} (i386)";
809         urlPrefix = "mirror://centos/${version}/os/i386";
810         packagesList = fetchurl rec {
811           url = "${urlPrefix}/repodata/${sha256}-primary.xml.gz";
812           sha256 = "b826a45082ef68340325c0855f3d2e5d5a4d0f77d28ba3b871791d6f14a97aeb";
813         };
814         archs = ["noarch" "i386"];
815         packages = commonCentOSPackages ++ [ "procps" ];
816       };
818     centos6x86_64 =
819       let version = "6.9";
820       in rec {
821         name = "centos-${version}-x86_64";
822         fullName = "CentOS ${version} (x86_64)";
823         urlPrefix = "mirror://centos/${version}/os/x86_64";
824         packagesList = fetchurl rec {
825           url = "${urlPrefix}/repodata/${sha256}-primary.xml.gz";
826           sha256 = "ed2b2d4ac98d774d4cd3e91467e1532f7e8b0275cfc91a0d214b532dcaf1e979";
827         };
828         archs = ["noarch" "x86_64"];
829         packages = commonCentOSPackages ++ [ "procps" ];
830       };
832     # Note: no i386 release for 7.x
833     centos7x86_64 =
834       let version = "7.4.1708";
835       in rec {
836         name = "centos-${version}-x86_64";
837         fullName = "CentOS ${version} (x86_64)";
838         urlPrefix = "mirror://centos/${version}/os/x86_64";
839         packagesList = fetchurl rec {
840           url = "${urlPrefix}/repodata/${sha256}-primary.xml.gz";
841           sha256 = "b686d3a0f337323e656d9387b9a76ce6808b26255fc3a138b1a87d3b1cb95ed5";
842         };
843         archs = ["noarch" "x86_64"];
844         packages = commonCentOSPackages ++ [ "procps-ng" ];
845       };
846   };
849   /* The set of supported Dpkg-based distributions. */
851   debDistros = {
852     ubuntu1404i386 = {
853       name = "ubuntu-14.04-trusty-i386";
854       fullName = "Ubuntu 14.04 Trusty (i386)";
855       packagesLists =
856         [ (fetchurl {
857             url = "mirror://ubuntu/dists/trusty/main/binary-i386/Packages.bz2";
858             sha256 = "1d5y3v3v079gdq45hc07ja0bjlmzqfwdwwlq0brwxi8m75k3iz7x";
859           })
860           (fetchurl {
861             url = "mirror://ubuntu/dists/trusty/universe/binary-i386/Packages.bz2";
862             sha256 = "03x9w92by320rfklrqhcl3qpwmnxds9c8ijl5zhcb21d6dcz5z1a";
863           })
864         ];
865       urlPrefix = "mirror://ubuntu";
866       packages = commonDebPackages ++ [ "diffutils" "libc-bin" ];
867     };
869     ubuntu1404x86_64 = {
870       name = "ubuntu-14.04-trusty-amd64";
871       fullName = "Ubuntu 14.04 Trusty (amd64)";
872       packagesLists =
873         [ (fetchurl {
874             url = "mirror://ubuntu/dists/trusty/main/binary-amd64/Packages.bz2";
875             sha256 = "1hhzbyqfr5i0swahwnl5gfp5l9p9hspywb1vpihr3b74p1z935bh";
876           })
877           (fetchurl {
878             url = "mirror://ubuntu/dists/trusty/universe/binary-amd64/Packages.bz2";
879             sha256 = "04560ba8s4z4v5iawknagrkn9q1nzvpn081ycmqvhh73p3p3g1jm";
880           })
881         ];
882       urlPrefix = "mirror://ubuntu";
883       packages = commonDebPackages ++ [ "diffutils" "libc-bin" ];
884     };
886     ubuntu1604i386 = {
887       name = "ubuntu-16.04-xenial-i386";
888       fullName = "Ubuntu 16.04 Xenial (i386)";
889       packagesLists =
890         [ (fetchurl {
891             url = "mirror://ubuntu/dists/xenial/main/binary-i386/Packages.xz";
892             sha256 = "13r75sp4slqy8w32y5dnr7pp7p3cfvavyr1g7gwnlkyrq4zx4ahy";
893           })
894           (fetchurl {
895             url = "mirror://ubuntu/dists/xenial/universe/binary-i386/Packages.xz";
896             sha256 = "14fid1rqm3sc0wlygcvn0yx5aljf51c2jpd4x0zxij4019316hsh";
897           })
898         ];
899       urlPrefix = "mirror://ubuntu";
900       packages = commonDebPackages ++ [ "diffutils" "libc-bin" ];
901     };
903     ubuntu1604x86_64 = {
904       name = "ubuntu-16.04-xenial-amd64";
905       fullName = "Ubuntu 16.04 Xenial (amd64)";
906       packagesLists =
907         [ (fetchurl {
908             url = "mirror://ubuntu/dists/xenial/main/binary-amd64/Packages.xz";
909             sha256 = "110qnkhjkkwm316fbig3aivm2595ydz6zskc4ld5cr8ngcrqm1bn";
910           })
911           (fetchurl {
912             url = "mirror://ubuntu/dists/xenial/universe/binary-amd64/Packages.xz";
913             sha256 = "0mm7gj491yi6q4v0n4qkbsm94s59bvqir6fk60j73w7y4la8rg68";
914           })
915         ];
916       urlPrefix = "mirror://ubuntu";
917       packages = commonDebPackages ++ [ "diffutils" "libc-bin" ];
918     };
920     ubuntu1804i386 = {
921       name = "ubuntu-18.04-bionic-i386";
922       fullName = "Ubuntu 18.04 Bionic (i386)";
923       packagesLists =
924         [ (fetchurl {
925             url = "mirror://ubuntu/dists/bionic/main/binary-i386/Packages.xz";
926             sha256 = "0f0v4131kwf7m7f8j3288rlqdxk1k3vqy74b7fcfd6jz9j8d840i";
927           })
928           (fetchurl {
929             url = "mirror://ubuntu/dists/bionic/universe/binary-i386/Packages.xz";
930             sha256 = "1v75c0dqr0wp0dqd4hnci92qqs4hll8frqdbpswadgxm5chn91bw";
931           })
932         ];
933       urlPrefix = "mirror://ubuntu";
934       packages = commonDebPackages ++ [ "diffutils" "libc-bin" ];
935     };
937     ubuntu1804x86_64 = {
938       name = "ubuntu-18.04-bionic-amd64";
939       fullName = "Ubuntu 18.04 Bionic (amd64)";
940       packagesLists =
941         [ (fetchurl {
942             url = "mirror://ubuntu/dists/bionic/main/binary-amd64/Packages.xz";
943             sha256 = "1ls81bjyvmfz6i919kszl7xks1ibrh1xqhsk6698ackndkm0wp39";
944           })
945           (fetchurl {
946             url = "mirror://ubuntu/dists/bionic/universe/binary-amd64/Packages.xz";
947             sha256 = "1832nqpn4ap95b3sj870xqayrza9in4kih9jkmjax27pq6x15v1r";
948           })
949         ];
950       urlPrefix = "mirror://ubuntu";
951       packages = commonDebPackages ++ [ "diffutils" "libc-bin" ];
952     };
954     ubuntu2004i386 = {
955       name = "ubuntu-20.04-focal-i386";
956       fullName = "Ubuntu 20.04 Focal (i386)";
957       packagesLists =
958         [ (fetchurl {
959             url = "mirror://ubuntu/dists/focal/main/binary-i386/Packages.xz";
960             sha256 = "sha256-7RAYURoN3RKYQAHpwBS9TIV6vCmpURpphyMJQmV4wLc=";
961           })
962           (fetchurl {
963             url = "mirror://ubuntu/dists/focal/universe/binary-i386/Packages.xz";
964             sha256 = "sha256-oA551xVE80volUPgkMyvzpQ1d+GhuZd4DAe7dXZnULM=";
965           })
966         ];
967       urlPrefix = "mirror://ubuntu";
968       packages = commonDebPackages ++ [ "diffutils" "libc-bin" ];
969     };
971     ubuntu2004x86_64 = {
972       name = "ubuntu-20.04-focal-amd64";
973       fullName = "Ubuntu 20.04 Focal (amd64)";
974       packagesLists =
975         [ (fetchurl {
976             url = "mirror://ubuntu/dists/focal/main/binary-amd64/Packages.xz";
977             sha256 = "sha256-d1eSH/j+7Zw5NKDJk21EG6SiOL7j6myMHfXLzUP8mGE=";
978           })
979           (fetchurl {
980             url = "mirror://ubuntu/dists/focal/universe/binary-amd64/Packages.xz";
981             sha256 = "sha256-RqdG2seJvZU3rKVNsWgLnf9RwkgVMRE1A4IZnX2WudE=";
982           })
983         ];
984       urlPrefix = "mirror://ubuntu";
985       packages = commonDebPackages ++ [ "diffutils" "libc-bin" ];
986     };
988     ubuntu2204i386 = {
989       name = "ubuntu-22.04-jammy-i386";
990       fullName = "Ubuntu 22.04 Jammy (i386)";
991       packagesLists =
992         [ (fetchurl {
993             url = "mirror://ubuntu/dists/jammy/main/binary-i386/Packages.xz";
994             sha256 = "sha256-iZBmwT0ep4v+V3sayybbOgZBOFFZwPGpOKtmuLMMVPQ=";
995           })
996           (fetchurl {
997             url = "mirror://ubuntu/dists/jammy/universe/binary-i386/Packages.xz";
998             sha256 = "sha256-DO2LdpZ9rDDBhWj2gvDWd0TJJVZHxKsYTKTi6GXjm1E=";
999           })
1000         ];
1001       urlPrefix = "mirror://ubuntu";
1002       packages = commonDebPackages ++ [ "diffutils" "libc-bin" ];
1003     };
1005     ubuntu2204x86_64 = {
1006       name = "ubuntu-22.04-jammy-amd64";
1007       fullName = "Ubuntu 22.04 Jammy (amd64)";
1008       packagesLists =
1009         [ (fetchurl {
1010             url = "mirror://ubuntu/dists/jammy/main/binary-amd64/Packages.xz";
1011             sha256 = "sha256-N8tX8VVMv6ccWinun/7hipqMF4K7BWjgh0t/9M6PnBE=";
1012           })
1013           (fetchurl {
1014             url = "mirror://ubuntu/dists/jammy/universe/binary-amd64/Packages.xz";
1015             sha256 = "sha256-0pyyTJP+xfQyVXBrzn60bUd5lSA52MaKwbsUpvNlXOI=";
1016           })
1017         ];
1018       urlPrefix = "mirror://ubuntu";
1019       packages = commonDebPackages ++ [ "diffutils" "libc-bin" ];
1020     };
1022     debian10i386 = {
1023       name = "debian-10.13-buster-i386";
1024       fullName = "Debian 10.13 Buster (i386)";
1025       packagesList = fetchurl {
1026         url = "https://snapshot.debian.org/archive/debian/20221126T084953Z/dists/buster/main/binary-i386/Packages.xz";
1027         hash = "sha256-n9JquhtZgxw3qr9BX0MQoY3ZTIHN0dit+iru3DC31UY=";
1028       };
1029       urlPrefix = "https://snapshot.debian.org/archive/debian/20221126T084953Z";
1030       packages = commonDebianPackages;
1031     };
1033     debian10x86_64 = {
1034       name = "debian-10.13-buster-amd64";
1035       fullName = "Debian 10.13 Buster (amd64)";
1036       packagesList = fetchurl {
1037         url = "https://snapshot.debian.org/archive/debian/20221126T084953Z/dists/buster/main/binary-amd64/Packages.xz";
1038         hash = "sha256-YukIIB3u87jgp9oudwklsxyKVKjSL618wFgDSXiFmjU=";
1039       };
1040       urlPrefix = "https://snapshot.debian.org/archive/debian/20221126T084953Z";
1041       packages = commonDebianPackages;
1042     };
1044     debian11i386 = {
1045       name = "debian-11.8-bullseye-i386";
1046       fullName = "Debian 11.8 Bullseye (i386)";
1047       packagesList = fetchurl {
1048         url = "https://snapshot.debian.org/archive/debian/20231124T031419Z/dists/bullseye/main/binary-i386/Packages.xz";
1049         hash = "sha256-0bKSLLPhEC7FB5D1NA2jaQP0wTe/Qp1ddiA/NDVjRaI=";
1050       };
1051       urlPrefix = "https://snapshot.debian.org/archive/debian/20231124T031419Z";
1052       packages = commonDebianPackages;
1053     };
1055     debian11x86_64 = {
1056       name = "debian-11.8-bullseye-amd64";
1057       fullName = "Debian 11.8 Bullseye (amd64)";
1058       packagesList = fetchurl {
1059         url = "https://snapshot.debian.org/archive/debian/20231124T031419Z/dists/bullseye/main/binary-amd64/Packages.xz";
1060         hash = "sha256-CYPsGgQgJZkh3JmbcAQkYDWP193qrkOADOgrMETZIeo=";
1061       };
1062       urlPrefix = "https://snapshot.debian.org/archive/debian/20231124T031419Z";
1063       packages = commonDebianPackages;
1064     };
1066     debian12i386 = {
1067       name = "debian-12.2-bookworm-i386";
1068       fullName = "Debian 12.2 Bookworm (i386)";
1069       packagesList = fetchurl {
1070         url = "https://snapshot.debian.org/archive/debian/20231124T031419Z/dists/bookworm/main/binary-i386/Packages.xz";
1071         hash = "sha256-OeN9Q2HFM3GsPNhOa4VhM7qpwT66yUNwC+6Z8SbGEeQ=";
1072       };
1073       urlPrefix = "https://snapshot.debian.org/archive/debian/20231124T031419Z";
1074       packages = commonDebianPackages;
1075     };
1077     debian12x86_64 = {
1078       name = "debian-12.2-bookworm-amd64";
1079       fullName = "Debian 12.2 Bookworm (amd64)";
1080       packagesList = fetchurl {
1081         url = "https://snapshot.debian.org/archive/debian/20231124T031419Z/dists/bookworm/main/binary-amd64/Packages.xz";
1082         hash = "sha256-SZDElRfe9BlBwDlajQB79Qdn08rv8whYoQDeVCveKVs=";
1083       };
1084       urlPrefix = "https://snapshot.debian.org/archive/debian/20231124T031419Z";
1085       packages = commonDebianPackages;
1086     };
1087   };
1090   /* Common packages for Fedora images. */
1091   commonFedoraPackages = [
1092     "autoconf"
1093     "automake"
1094     "basesystem"
1095     "bzip2"
1096     "curl"
1097     "diffutils"
1098     "fedora-release"
1099     "findutils"
1100     "gawk"
1101     "gcc-c++"
1102     "gzip"
1103     "make"
1104     "patch"
1105     "perl"
1106     "pkgconf-pkg-config"
1107     "rpm"
1108     "rpm-build"
1109     "tar"
1110     "unzip"
1111   ];
1113   commonCentOSPackages = [
1114     "autoconf"
1115     "automake"
1116     "basesystem"
1117     "bzip2"
1118     "curl"
1119     "diffutils"
1120     "centos-release"
1121     "findutils"
1122     "gawk"
1123     "gcc-c++"
1124     "gzip"
1125     "make"
1126     "patch"
1127     "perl"
1128     "pkgconfig"
1129     "rpm"
1130     "rpm-build"
1131     "tar"
1132     "unzip"
1133   ];
1135   commonRHELPackages = [
1136     "autoconf"
1137     "automake"
1138     "basesystem"
1139     "bzip2"
1140     "curl"
1141     "diffutils"
1142     "findutils"
1143     "gawk"
1144     "gcc-c++"
1145     "gzip"
1146     "make"
1147     "patch"
1148     "perl"
1149     "pkgconfig"
1150     "procps-ng"
1151     "rpm"
1152     "rpm-build"
1153     "tar"
1154     "unzip"
1155   ];
1157   /* Common packages for openSUSE images. */
1158   commonOpenSUSEPackages = [
1159     "aaa_base"
1160     "autoconf"
1161     "automake"
1162     "bzip2"
1163     "curl"
1164     "diffutils"
1165     "findutils"
1166     "gawk"
1167     "gcc-c++"
1168     "gzip"
1169     "make"
1170     "patch"
1171     "perl"
1172     "pkg-config"
1173     "rpm"
1174     "tar"
1175     "unzip"
1176     "util-linux"
1177     "gnu-getopt"
1178   ];
1181   /* Common packages for Debian/Ubuntu images. */
1182   commonDebPackages = [
1183     "base-passwd"
1184     "dpkg"
1185     "libc6-dev"
1186     "perl"
1187     "bash"
1188     "dash"
1189     "gzip"
1190     "bzip2"
1191     "tar"
1192     "grep"
1193     "mawk"
1194     "sed"
1195     "findutils"
1196     "g++"
1197     "make"
1198     "curl"
1199     "patch"
1200     "locales"
1201     "coreutils"
1202     # Needed by checkinstall:
1203     "util-linux"
1204     "file"
1205     "dpkg-dev"
1206     "pkg-config"
1207     # Needed because it provides /etc/login.defs, whose absence causes
1208     # the "passwd" post-installs script to fail.
1209     "login"
1210     "passwd"
1211   ];
1213   commonDebianPackages = commonDebPackages ++ [ "sysvinit" "diff" ];
1216   /* A set of functions that build the Linux distributions specified
1217      in `rpmDistros' and `debDistros'.  For instance,
1218      `diskImageFuns.ubuntu1004x86_64 { }' builds an Ubuntu 10.04 disk
1219      image containing the default packages specified above.  Overrides
1220      of the default image parameters can be given.  In particular,
1221      `extraPackages' specifies the names of additional packages from
1222      the distribution that should be included in the image; `packages'
1223      allows the entire set of packages to be overridden; and `size'
1224      sets the size of the disk in megabytes.  E.g.,
1225      `diskImageFuns.ubuntu1004x86_64 { extraPackages = ["firefox"];
1226      size = 8192; }' builds an 8 GiB image containing Firefox in
1227      addition to the default packages. */
1228   diskImageFuns =
1229     (lib.mapAttrs (name: as: as2: makeImageFromRPMDist (as // as2)) rpmDistros) //
1230     (lib.mapAttrs (name: as: as2: makeImageFromDebDist (as // as2)) debDistros);
1233   /* Shorthand for `diskImageFuns.<attr> { extraPackages = ... }'. */
1234   diskImageExtraFuns =
1235     lib.mapAttrs (name: f: extraPackages: f { inherit extraPackages; }) diskImageFuns;
1238   /* Default disk images generated from the `rpmDistros' and
1239      `debDistros' sets. */
1240   diskImages = lib.mapAttrs (name: f: f {}) diskImageFuns;