diff options
Diffstat (limited to 'nixpkgs/pkgs/build-support/vm')
-rw-r--r-- | nixpkgs/pkgs/build-support/vm/deb/deb-closure.pl | 180 | ||||
-rw-r--r-- | nixpkgs/pkgs/build-support/vm/default.nix | 1241 | ||||
-rw-r--r-- | nixpkgs/pkgs/build-support/vm/rpm/rpm-closure.pl | 184 | ||||
-rw-r--r-- | nixpkgs/pkgs/build-support/vm/test.nix | 58 |
4 files changed, 1663 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/build-support/vm/deb/deb-closure.pl b/nixpkgs/pkgs/build-support/vm/deb/deb-closure.pl new file mode 100644 index 000000000000..2d331e18dfeb --- /dev/null +++ b/nixpkgs/pkgs/build-support/vm/deb/deb-closure.pl @@ -0,0 +1,180 @@ +use strict; +use Dpkg::Control; +use Dpkg::Deps; +use File::Basename; + +my $packagesFile = shift @ARGV; +my $urlPrefix = shift @ARGV; +my @toplevelPkgs = @ARGV; + + +my %packages; + + +# Parse the Packages file. +open PACKAGES, "<$packagesFile" or die; + +while (1) { + my $cdata = Dpkg::Control->new(type => CTRL_INFO_PKG); + last if not $cdata->parse(\*PACKAGES, $packagesFile); + die unless defined $cdata->{Package}; + #print STDERR $cdata->{Package}, "\n"; + $packages{$cdata->{Package}} = $cdata; +} + +close PACKAGES; + + +# Flatten a Dpkg::Deps dependency value into a list of package names. +sub getDeps { + my $deps = shift; + #print "$deps\n"; + if ($deps->isa('Dpkg::Deps::AND')) { + my @res = (); + foreach my $dep ($deps->get_deps()) { + push @res, getDeps($dep); + } + return @res; + } elsif ($deps->isa('Dpkg::Deps::OR')) { + # Arbitrarily pick the first alternative. + return getDeps(($deps->get_deps())[0]); + } elsif ($deps->isa('Dpkg::Deps::Simple')) { + return ($deps->{package}); + } else { + die "unknown dep type"; + } +} + + +# Process the "Provides" and "Replaces" fields to be able to resolve +# virtual dependencies. +my %provides; + +foreach my $cdata (sort {$a->{Package} cmp $b->{Package}} (values %packages)) { + if (defined $cdata->{Provides}) { + my @provides = getDeps(Dpkg::Deps::deps_parse($cdata->{Provides})); + foreach my $name (@provides) { + #die "conflicting provide: $name\n" if defined $provides{$name}; + #warn "provide by $cdata->{Package} conflicts with package with the same name: $name\n"; + next if defined $packages{$name}; + $provides{$name} = $cdata->{Package}; + } + } + # Treat "Replaces" like "Provides". + if (defined $cdata->{Replaces}) { + my @replaces = getDeps(Dpkg::Deps::deps_parse($cdata->{Replaces})); + foreach my $name (@replaces) { + next if defined $packages{$name}; + $provides{$name} = $cdata->{Package}; + } + } +} + + +# Determine the closure of a package. +my %donePkgs; +my %depsUsed; +my @order = (); + +sub closePackage { + my $pkgName = shift; + print STDERR ">>> $pkgName\n"; + my $cdata = $packages{$pkgName}; + + if (!defined $cdata) { + die "unknown (virtual) package $pkgName" + unless defined $provides{$pkgName}; + print STDERR "virtual $pkgName: using $provides{$pkgName}\n"; + $pkgName = $provides{$pkgName}; + $cdata = $packages{$pkgName}; + } + + die "unknown package $pkgName" unless defined $cdata; + return if defined $donePkgs{$pkgName}; + $donePkgs{$pkgName} = 1; + + if (defined $cdata->{Provides}) { + foreach my $name (getDeps(Dpkg::Deps::deps_parse($cdata->{Provides}))) { + $provides{$name} = $cdata->{Package}; + } + } + + my @depNames = (); + + if (defined $cdata->{Depends}) { + print STDERR " $pkgName: $cdata->{Depends}\n"; + my $deps = Dpkg::Deps::deps_parse($cdata->{Depends}); + die unless defined $deps; + push @depNames, getDeps($deps); + } + + if (defined $cdata->{'Pre-Depends'}) { + print STDERR " $pkgName: $cdata->{'Pre-Depends'}\n"; + my $deps = Dpkg::Deps::deps_parse($cdata->{'Pre-Depends'}); + die unless defined $deps; + push @depNames, getDeps($deps); + } + + foreach my $depName (@depNames) { + closePackage($depName); + } + + push @order, $pkgName; + $depsUsed{$pkgName} = \@depNames; +} + +foreach my $pkgName (@toplevelPkgs) { + closePackage $pkgName; +} + + +# Generate the output Nix expression. +print "# This is a generated file. Do not modify!\n"; +print "# Following are the Debian packages constituting the closure of: @toplevelPkgs\n\n"; +print "{fetchurl}:\n\n"; +print "[\n\n"; + +# Output the packages in strongly connected components. +my %done; +my %forward; +my $newComponent = 1; +foreach my $pkgName (@order) { + $done{$pkgName} = 1; + my $cdata = $packages{$pkgName}; + my @deps = @{$depsUsed{$pkgName}}; + foreach my $dep (@deps) { + $dep = $provides{$dep} if defined $provides{$dep}; + $forward{$dep} = 1 unless defined $done{$dep}; + } + delete $forward{$pkgName}; + + print " [\n\n" if $newComponent; + $newComponent = 0; + + my $origName = basename $cdata->{Filename}; + my $cleanedName = $origName; + $cleanedName =~ s/~//g; + + print " (fetchurl {\n"; + print " url = \"$urlPrefix/$cdata->{Filename}\";\n"; + print " sha256 = \"$cdata->{SHA256}\";\n"; + print " name = \"$cleanedName\";\n" if $cleanedName ne $origName; + print " })\n"; + print "\n"; + + if (keys %forward == 0) { + print " ]\n\n"; + $newComponent = 1; + } +} + +foreach my $pkgName (@order) { + my $cdata = $packages{$pkgName}; +} + +print "]\n"; + +if ($newComponent != 1) { + print STDERR "argh: ", keys %forward, "\n"; + exit 1; +} diff --git a/nixpkgs/pkgs/build-support/vm/default.nix b/nixpkgs/pkgs/build-support/vm/default.nix new file mode 100644 index 000000000000..871f81bb5d69 --- /dev/null +++ b/nixpkgs/pkgs/build-support/vm/default.nix @@ -0,0 +1,1241 @@ +{ lib +, pkgs +, kernel ? pkgs.linux +, img ? pkgs.stdenv.hostPlatform.linux-kernel.target +, storeDir ? builtins.storeDir +, rootModules ? + [ "virtio_pci" "virtio_mmio" "virtio_blk" "virtio_balloon" "virtio_rng" "ext4" "unix" "9p" "9pnet_virtio" "crc32c_generic" ] + ++ pkgs.lib.optional pkgs.stdenv.hostPlatform.isx86 "rtc_cmos" +}: + +let + inherit (pkgs) bash bashInteractive busybox cpio coreutils e2fsprogs fetchurl kmod rpm + stdenv util-linux + buildPackages writeScript writeText runCommand; +in +rec { + qemu-common = import ../../../nixos/lib/qemu-common.nix { inherit lib pkgs; }; + + qemu = buildPackages.qemu_kvm; + + modulesClosure = pkgs.makeModulesClosure { + inherit kernel 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 \ + ${pkgs.stdenv.cc.libc}/lib/ld-*.so.? \ + ${pkgs.stdenv.cc.libc}/lib/libc.so.* \ + ${pkgs.stdenv.cc.libc}/lib/libm.so.* \ + ${pkgs.stdenv.cc.libc}/lib/libresolv.so.* \ + ${pkgs.stdenv.cc.libc}/lib/libpthread.so.* \ + ${pkgs.zstd.out}/lib/libzstd.so.* \ + ${pkgs.xz.out}/lib/liblzma.so.* \ + $out/lib + + # Copy BusyBox. + cp -pd ${pkgs.busybox}/bin/* $out/bin + cp -pd ${pkgs.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 + ;; + out=*) + set -- $(IFS==; echo $o) + export out=$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 9p store /fs${storeDir} -o trans=virtio,version=9p2000.L,cache=loose,msize=131072 + + 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 9p xchg /fs/tmp/xchg -o trans=virtio,version=9p2000.L,msize=131072 + + 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 $out + ''; + + + initrd = pkgs.makeInitrd { + contents = [ + { object = stage1Init; + symlink = "/init"; + } + ]; + }; + + + stage2Init = writeScript "vm-run-stage2" '' + #! ${bash}/bin/sh + source /tmp/xchg/saved-env + + # Set the system time from the hardware clock. Works around an + # apparent KVM > 1.5.2 bug. + ${util-linux}/bin/hwclock -s + + export NIX_STORE=${storeDir} + export NIX_BUILD_TOP=/tmp + export TMPDIR=/tmp + export PATH=/empty + out="$1" + cd "$NIX_BUILD_TOP" + + 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=${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 + $origBuilder $origArgs + 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 = '' + ${qemu-common.qemuBinary qemu} \ + -nographic -no-reboot \ + -device virtio-rng-pci \ + -virtfs local,path=${storeDir},security_model=none,mount_tag=store \ + -virtfs local,path=$TMPDIR/xchg,security_model=none,mount_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} out=$out mountDisk=$mountDisk loglevel=4" \ + $QEMU_OPTS + ''; + + + vmRunCommand = qemuCommand: writeText "vm-run" '' + export > saved-env + + PATH=${coreutils}/bin + mkdir xchg + mv saved-env xchg/ + + eval "$preVM" + + if [ "$enableParallelBuilding" = 1 ]; then + if [ ''${NIX_BUILD_CORES:-0} = 0 ]; then + QEMU_OPTS+=" -smp cpus=$(nproc)" + else + QEMU_OPTS+=" -smp cpus=$NIX_BUILD_CORES" + fi + 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). + cat > ./run-vm <<EOF + #! ${bash}/bin/sh + ''${diskImage:+diskImage=$diskImage} + TMPDIR=$TMPDIR + cd $TMPDIR + ${qemuCommand} + EOF + + mkdir -p -m 0700 $out + + 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="$(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 + 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 megabytes, + 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; + QEMU_OPTS = "${QEMU_OPTS} -m ${toString memSize}"; + passAsFile = []; # HACK fix - see https://github.com/NixOS/nixpkgs/issues/16742 + }); + + + 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 = [ pkgs.util-linux pkgs.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} + + # Newer distributions like Fedora 18 require /lib etc. to be + # symlinked to /usr. + ${lib.optionalString unifiedSystemDir '' + mkdir -p /mnt/usr/bin /mnt/usr/sbin /mnt/usr/lib /mnt/usr/lib64 + ln -s /usr/bin /mnt/bin + ln -s /usr/sbin /mnt/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 ${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; + + debs = (lib.intersperse "|" debs); + + preVM = createEmptyImage {inherit size fullName;}; + + buildCommand = '' + ${createRootFS} + + PATH=$PATH:${lib.makeBinPath [ pkgs.dpkg pkgs.glibc pkgs.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 $debs; do + if test "$deb" != "|"; then + echo "$deb..." + dpkg-deb --extract "$deb" /mnt + fi + 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 + + oldIFS="$IFS" + IFS="|" + for component in $debs; do + IFS="$oldIFS" + 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.XMLSimple ]; + inherit archs; + } '' + ${lib.concatImapStrings (i: pl: '' + gunzip < ${pl} > ./packages_${toString i}.xml + '') 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 ]; } '' + 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 + ''; + + + /* 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 = { + + # Note: no i386 release for Fedora >= 26 + fedora26x86_64 = + let version = "26"; + in { + name = "fedora-${version}-x86_64"; + fullName = "Fedora ${version} (x86_64)"; + packagesList = fetchurl rec { + url = "mirror://fedora/linux/releases/${version}/Everything/x86_64/os/repodata/${sha256}-primary.xml.gz"; + sha256 = "880055a50c05b20641530d09b23f64501a000b2f92fe252417c530178730a95e"; + }; + urlPrefix = "mirror://fedora/linux/releases/${version}/Everything/x86_64/os"; + archs = ["noarch" "x86_64"]; + packages = commonFedoraPackages ++ [ "cronie" "util-linux" ]; + unifiedSystemDir = true; + }; + + fedora27x86_64 = + let version = "27"; + in { + name = "fedora-${version}-x86_64"; + fullName = "Fedora ${version} (x86_64)"; + packagesList = fetchurl rec { + url = "mirror://fedora/linux/releases/${version}/Everything/x86_64/os/repodata/${sha256}-primary.xml.gz"; + sha256 = "48986ce4583cd09825c6d437150314446f0f49fa1a1bd62dcfa1085295030fe9"; + }; + urlPrefix = "mirror://fedora/linux/releases/${version}/Everything/x86_64/os"; + archs = ["noarch" "x86_64"]; + packages = commonFedoraPackages ++ [ "cronie" "util-linux" ]; + unifiedSystemDir = true; + }; + + centos6i386 = + let version = "6.9"; + in rec { + name = "centos-${version}-i386"; + fullName = "CentOS ${version} (i386)"; + urlPrefix = "mirror://centos/${version}/os/i386"; + packagesList = fetchurl rec { + url = "${urlPrefix}/repodata/${sha256}-primary.xml.gz"; + sha256 = "b826a45082ef68340325c0855f3d2e5d5a4d0f77d28ba3b871791d6f14a97aeb"; + }; + archs = ["noarch" "i386"]; + packages = commonCentOSPackages ++ [ "procps" ]; + }; + + centos6x86_64 = + let version = "6.9"; + in rec { + name = "centos-${version}-x86_64"; + fullName = "CentOS ${version} (x86_64)"; + urlPrefix = "mirror://centos/${version}/os/x86_64"; + packagesList = fetchurl rec { + url = "${urlPrefix}/repodata/${sha256}-primary.xml.gz"; + sha256 = "ed2b2d4ac98d774d4cd3e91467e1532f7e8b0275cfc91a0d214b532dcaf1e979"; + }; + archs = ["noarch" "x86_64"]; + packages = commonCentOSPackages ++ [ "procps" ]; + }; + + # Note: no i386 release for 7.x + centos7x86_64 = + let version = "7.4.1708"; + in rec { + name = "centos-${version}-x86_64"; + fullName = "CentOS ${version} (x86_64)"; + urlPrefix = "mirror://centos/${version}/os/x86_64"; + packagesList = fetchurl rec { + url = "${urlPrefix}/repodata/${sha256}-primary.xml.gz"; + sha256 = "b686d3a0f337323e656d9387b9a76ce6808b26255fc3a138b1a87d3b1cb95ed5"; + }; + archs = ["noarch" "x86_64"]; + packages = commonCentOSPackages ++ [ "procps-ng" ]; + }; + }; + + + /* The set of supported Dpkg-based distributions. */ + + debDistros = { + ubuntu1404i386 = { + name = "ubuntu-14.04-trusty-i386"; + fullName = "Ubuntu 14.04 Trusty (i386)"; + packagesLists = + [ (fetchurl { + url = "mirror://ubuntu/dists/trusty/main/binary-i386/Packages.bz2"; + sha256 = "1d5y3v3v079gdq45hc07ja0bjlmzqfwdwwlq0brwxi8m75k3iz7x"; + }) + (fetchurl { + url = "mirror://ubuntu/dists/trusty/universe/binary-i386/Packages.bz2"; + sha256 = "03x9w92by320rfklrqhcl3qpwmnxds9c8ijl5zhcb21d6dcz5z1a"; + }) + ]; + urlPrefix = "mirror://ubuntu"; + packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; + }; + + ubuntu1404x86_64 = { + name = "ubuntu-14.04-trusty-amd64"; + fullName = "Ubuntu 14.04 Trusty (amd64)"; + packagesLists = + [ (fetchurl { + url = "mirror://ubuntu/dists/trusty/main/binary-amd64/Packages.bz2"; + sha256 = "1hhzbyqfr5i0swahwnl5gfp5l9p9hspywb1vpihr3b74p1z935bh"; + }) + (fetchurl { + url = "mirror://ubuntu/dists/trusty/universe/binary-amd64/Packages.bz2"; + sha256 = "04560ba8s4z4v5iawknagrkn9q1nzvpn081ycmqvhh73p3p3g1jm"; + }) + ]; + urlPrefix = "mirror://ubuntu"; + packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; + }; + + ubuntu1604i386 = { + name = "ubuntu-16.04-xenial-i386"; + fullName = "Ubuntu 16.04 Xenial (i386)"; + packagesLists = + [ (fetchurl { + url = "mirror://ubuntu/dists/xenial/main/binary-i386/Packages.xz"; + sha256 = "13r75sp4slqy8w32y5dnr7pp7p3cfvavyr1g7gwnlkyrq4zx4ahy"; + }) + (fetchurl { + url = "mirror://ubuntu/dists/xenial/universe/binary-i386/Packages.xz"; + sha256 = "14fid1rqm3sc0wlygcvn0yx5aljf51c2jpd4x0zxij4019316hsh"; + }) + ]; + urlPrefix = "mirror://ubuntu"; + packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; + }; + + ubuntu1604x86_64 = { + name = "ubuntu-16.04-xenial-amd64"; + fullName = "Ubuntu 16.04 Xenial (amd64)"; + packagesLists = + [ (fetchurl { + url = "mirror://ubuntu/dists/xenial/main/binary-amd64/Packages.xz"; + sha256 = "110qnkhjkkwm316fbig3aivm2595ydz6zskc4ld5cr8ngcrqm1bn"; + }) + (fetchurl { + url = "mirror://ubuntu/dists/xenial/universe/binary-amd64/Packages.xz"; + sha256 = "0mm7gj491yi6q4v0n4qkbsm94s59bvqir6fk60j73w7y4la8rg68"; + }) + ]; + urlPrefix = "mirror://ubuntu"; + packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; + }; + + ubuntu1804i386 = { + name = "ubuntu-18.04-bionic-i386"; + fullName = "Ubuntu 18.04 Bionic (i386)"; + packagesLists = + [ (fetchurl { + url = "mirror://ubuntu/dists/bionic/main/binary-i386/Packages.xz"; + sha256 = "0f0v4131kwf7m7f8j3288rlqdxk1k3vqy74b7fcfd6jz9j8d840i"; + }) + (fetchurl { + url = "mirror://ubuntu/dists/bionic/universe/binary-i386/Packages.xz"; + sha256 = "1v75c0dqr0wp0dqd4hnci92qqs4hll8frqdbpswadgxm5chn91bw"; + }) + ]; + urlPrefix = "mirror://ubuntu"; + packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; + }; + + ubuntu1804x86_64 = { + name = "ubuntu-18.04-bionic-amd64"; + fullName = "Ubuntu 18.04 Bionic (amd64)"; + packagesLists = + [ (fetchurl { + url = "mirror://ubuntu/dists/bionic/main/binary-amd64/Packages.xz"; + sha256 = "1ls81bjyvmfz6i919kszl7xks1ibrh1xqhsk6698ackndkm0wp39"; + }) + (fetchurl { + url = "mirror://ubuntu/dists/bionic/universe/binary-amd64/Packages.xz"; + sha256 = "1832nqpn4ap95b3sj870xqayrza9in4kih9jkmjax27pq6x15v1r"; + }) + ]; + urlPrefix = "mirror://ubuntu"; + packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; + }; + + ubuntu2004i386 = { + name = "ubuntu-20.04-focal-i386"; + fullName = "Ubuntu 20.04 Focal (i386)"; + packagesLists = + [ (fetchurl { + url = "mirror://ubuntu/dists/focal/main/binary-i386/Packages.xz"; + sha256 = "sha256-7RAYURoN3RKYQAHpwBS9TIV6vCmpURpphyMJQmV4wLc="; + }) + (fetchurl { + url = "mirror://ubuntu/dists/focal/universe/binary-i386/Packages.xz"; + sha256 = "sha256-oA551xVE80volUPgkMyvzpQ1d+GhuZd4DAe7dXZnULM="; + }) + ]; + urlPrefix = "mirror://ubuntu"; + packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; + }; + + ubuntu2004x86_64 = { + name = "ubuntu-20.04-focal-amd64"; + fullName = "Ubuntu 20.04 Focal (amd64)"; + packagesLists = + [ (fetchurl { + url = "mirror://ubuntu/dists/focal/main/binary-amd64/Packages.xz"; + sha256 = "sha256-d1eSH/j+7Zw5NKDJk21EG6SiOL7j6myMHfXLzUP8mGE="; + }) + (fetchurl { + url = "mirror://ubuntu/dists/focal/universe/binary-amd64/Packages.xz"; + sha256 = "sha256-RqdG2seJvZU3rKVNsWgLnf9RwkgVMRE1A4IZnX2WudE="; + }) + ]; + urlPrefix = "mirror://ubuntu"; + packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; + }; + + ubuntu2204i386 = { + name = "ubuntu-22.04-jammy-i386"; + fullName = "Ubuntu 22.04 Jammy (i386)"; + packagesLists = + [ (fetchurl { + url = "mirror://ubuntu/dists/jammy/main/binary-i386/Packages.xz"; + sha256 = "sha256-iZBmwT0ep4v+V3sayybbOgZBOFFZwPGpOKtmuLMMVPQ="; + }) + (fetchurl { + url = "mirror://ubuntu/dists/jammy/universe/binary-i386/Packages.xz"; + sha256 = "sha256-DO2LdpZ9rDDBhWj2gvDWd0TJJVZHxKsYTKTi6GXjm1E="; + }) + ]; + urlPrefix = "mirror://ubuntu"; + packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; + }; + + ubuntu2204x86_64 = { + name = "ubuntu-22.04-jammy-amd64"; + fullName = "Ubuntu 22.04 Jammy (amd64)"; + packagesLists = + [ (fetchurl { + url = "mirror://ubuntu/dists/jammy/main/binary-amd64/Packages.xz"; + sha256 = "sha256-N8tX8VVMv6ccWinun/7hipqMF4K7BWjgh0t/9M6PnBE="; + }) + (fetchurl { + url = "mirror://ubuntu/dists/jammy/universe/binary-amd64/Packages.xz"; + sha256 = "sha256-0pyyTJP+xfQyVXBrzn60bUd5lSA52MaKwbsUpvNlXOI="; + }) + ]; + urlPrefix = "mirror://ubuntu"; + packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; + }; + + debian10i386 = { + name = "debian-10.13-buster-i386"; + fullName = "Debian 10.13 Buster (i386)"; + packagesList = fetchurl { + url = "https://snapshot.debian.org/archive/debian/20221126T084953Z/dists/buster/main/binary-i386/Packages.xz"; + hash = "sha256-n9JquhtZgxw3qr9BX0MQoY3ZTIHN0dit+iru3DC31UY="; + }; + urlPrefix = "https://snapshot.debian.org/archive/debian/20221126T084953Z"; + packages = commonDebianPackages; + }; + + debian10x86_64 = { + name = "debian-10.13-buster-amd64"; + fullName = "Debian 10.13 Buster (amd64)"; + packagesList = fetchurl { + url = "https://snapshot.debian.org/archive/debian/20221126T084953Z/dists/buster/main/binary-amd64/Packages.xz"; + hash = "sha256-YukIIB3u87jgp9oudwklsxyKVKjSL618wFgDSXiFmjU="; + }; + urlPrefix = "https://snapshot.debian.org/archive/debian/20221126T084953Z"; + packages = commonDebianPackages; + }; + + debian11i386 = { + name = "debian-11.8-bullseye-i386"; + fullName = "Debian 11.8 Bullseye (i386)"; + packagesList = fetchurl { + url = "https://snapshot.debian.org/archive/debian/20231124T031419Z/dists/bullseye/main/binary-i386/Packages.xz"; + hash = "sha256-0bKSLLPhEC7FB5D1NA2jaQP0wTe/Qp1ddiA/NDVjRaI="; + }; + urlPrefix = "https://snapshot.debian.org/archive/debian/20231124T031419Z"; + packages = commonDebianPackages; + }; + + debian11x86_64 = { + name = "debian-11.8-bullseye-amd64"; + fullName = "Debian 11.8 Bullseye (amd64)"; + packagesList = fetchurl { + url = "https://snapshot.debian.org/archive/debian/20231124T031419Z/dists/bullseye/main/binary-amd64/Packages.xz"; + hash = "sha256-CYPsGgQgJZkh3JmbcAQkYDWP193qrkOADOgrMETZIeo="; + }; + urlPrefix = "https://snapshot.debian.org/archive/debian/20231124T031419Z"; + packages = commonDebianPackages; + }; + + debian12i386 = { + name = "debian-12.2-bookworm-i386"; + fullName = "Debian 12.2 Bookworm (i386)"; + packagesList = fetchurl { + url = "https://snapshot.debian.org/archive/debian/20231124T031419Z/dists/bookworm/main/binary-i386/Packages.xz"; + hash = "sha256-OeN9Q2HFM3GsPNhOa4VhM7qpwT66yUNwC+6Z8SbGEeQ="; + }; + urlPrefix = "https://snapshot.debian.org/archive/debian/20231124T031419Z"; + packages = commonDebianPackages; + }; + + debian12x86_64 = { + name = "debian-12.2-bookworm-amd64"; + fullName = "Debian 12.2 Bookworm (amd64)"; + packagesList = fetchurl { + url = "https://snapshot.debian.org/archive/debian/20231124T031419Z/dists/bookworm/main/binary-amd64/Packages.xz"; + hash = "sha256-SZDElRfe9BlBwDlajQB79Qdn08rv8whYoQDeVCveKVs="; + }; + urlPrefix = "https://snapshot.debian.org/archive/debian/20231124T031419Z"; + packages = commonDebianPackages; + }; + }; + + + /* Common packages for Fedora images. */ + commonFedoraPackages = [ + "autoconf" + "automake" + "basesystem" + "bzip2" + "curl" + "diffutils" + "fedora-release" + "findutils" + "gawk" + "gcc-c++" + "gzip" + "make" + "patch" + "perl" + "pkgconf-pkg-config" + "rpm" + "rpm-build" + "tar" + "unzip" + ]; + + commonCentOSPackages = [ + "autoconf" + "automake" + "basesystem" + "bzip2" + "curl" + "diffutils" + "centos-release" + "findutils" + "gawk" + "gcc-c++" + "gzip" + "make" + "patch" + "perl" + "pkgconfig" + "rpm" + "rpm-build" + "tar" + "unzip" + ]; + + commonRHELPackages = [ + "autoconf" + "automake" + "basesystem" + "bzip2" + "curl" + "diffutils" + "findutils" + "gawk" + "gcc-c++" + "gzip" + "make" + "patch" + "perl" + "pkgconfig" + "procps-ng" + "rpm" + "rpm-build" + "tar" + "unzip" + ]; + + /* 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 megabytes. 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; + +} diff --git a/nixpkgs/pkgs/build-support/vm/rpm/rpm-closure.pl b/nixpkgs/pkgs/build-support/vm/rpm/rpm-closure.pl new file mode 100644 index 000000000000..6442cd91a957 --- /dev/null +++ b/nixpkgs/pkgs/build-support/vm/rpm/rpm-closure.pl @@ -0,0 +1,184 @@ +use strict; +use XML::Simple; +use List::Util qw(min); + +my @packagesFiles = (); +my @urlPrefixes = (); + +# rpm-closure.pl (<package-file> <url-prefix>)+ <toplevel-pkg>+ + +while(-f $ARGV[0]) { + my $packagesFile = shift @ARGV; + my $urlPrefix = shift @ARGV; + push(@packagesFiles, $packagesFile); + push(@urlPrefixes, $urlPrefix); +} + + +sub rpmvercmp { + my ($version1, $version2) = @_; + my @vercmps1 = split /\./, $version1; + my @vercmps2 = split /\./, $version2; + my $l1 = scalar(@vercmps1); + my $l2 = scalar(@vercmps2); + my $l = min($l1, $l2); + + for(my $i=0; $i<$l; $i++) { + my $v1 = $vercmps1[$i]; + my $v2 = $vercmps2[$i]; + + if($v1 =~ /^[0-9]*$/ && $v2 =~ /^[0-9]*$/) { + if ( int($v1) > int($v2) ) { + return 1; + } + elsif ( int($v1) < int($v2) ) { + return -1; + } + } else { + if ( $v1 gt $v2 ) { + return 1; + } + elsif ( $v1 lt $v2 ) { + return -1; + } + } + } + if($l1 == $l2) { + return 0; + } elsif ($l1 > $l2) { + return 1; + } elsif ($l1 < $l2) { + return -1; + } +} + +my @toplevelPkgs = @ARGV; + +my @archs = split ' ', ($ENV{'archs'} or ""); + +my %pkgs; +for (my $i = 0; $i < scalar(@packagesFiles); $i++) { + my $packagesFile = $packagesFiles[$i]; + print STDERR "parsing packages in $packagesFile...\n"; + + my $xml = XMLin($packagesFile, ForceArray => ['package', 'rpm:entry', 'file'], KeyAttr => []) or die; + + print STDERR "$packagesFile contains $xml->{packages} packages\n"; + + foreach my $pkg (@{$xml->{'package'}}) { + if (scalar @archs > 0) { + my $arch = $pkg->{arch}; + my $found = 0; + foreach my $a (@archs) { $found = 1 if $arch eq $a; } + next if !$found; + } + if (defined $pkgs{$pkg->{name}}) { + my $earlierPkg = $pkgs{$pkg->{name}}; + print STDERR "WARNING: duplicate occurrence of package $pkg->{name}\n"; + # <version epoch="0" ver="1.28.0" rel="2.el6"/> + my $cmp = rpmvercmp($pkg->{'version'}->{ver}, $earlierPkg->{'version'}->{ver}); + if ($cmp > 0 || ($cmp == 0 && rpmvercmp($pkg->{'version'}->{rel}, $earlierPkg->{'version'}->{rel})>0)) { + print STDERR "WARNING: replaced package $pkg->{name} (".$earlierPkg->{'version'}->{ver}." ".$earlierPkg->{'version'}->{rel}.") with newer one (".$pkg->{'version'}->{ver}." ".$pkg->{'version'}->{rel}.")\n"; + $pkg->{urlPrefix} = $urlPrefixes[$i]; + $pkgs{$pkg->{name}} = $pkg; + } + next; + } + $pkg->{urlPrefix} = $urlPrefixes[$i]; + $pkgs{$pkg->{name}} = $pkg; + } +} + +my %provides; +PKG: foreach my $pkgName (sort(keys %pkgs)) { + #print STDERR "looking at $pkgName\n"; + my $pkg = $pkgs{$pkgName}; + + # Skip packages that conflict with a required package. + my $conflicts = $pkg->{format}->{'rpm:conflicts'}->{'rpm:entry'} // []; + foreach my $conflict (@{$conflicts}) { + next if $conflict->{flags} // "" eq "LT" || $conflict->{flags} // "" eq "LE"; + #print STDERR " $pkgName conflicts with $conflict->{name}\n"; + if (grep { $_ eq $conflict->{name} } @toplevelPkgs) { + print STDERR "skipping package $pkgName because it conflicts with a required package\n"; + next PKG; + } + } + + my $provides = $pkg->{format}->{'rpm:provides'}->{'rpm:entry'} or die; + foreach my $req (@{$provides}) { + #print STDERR " $pkgName provides $req->{name}\n"; + #die "multiple provides for $req->{name}" if defined $provides{$req->{name}}; + $provides{$req->{name}} = $pkgName; + } + + if (defined $pkg->{format}->{file}) { + foreach my $file (@{$pkg->{format}->{file}}) { + #print STDERR " provides file $file\n"; + $provides{$file} = $pkgName; + } + } +} + + +my %donePkgs; +my @needed = (); + +sub closePackage { + my $pkgName = shift; + + return if defined $donePkgs{$pkgName}; + $donePkgs{$pkgName} = 1; + + print STDERR ">>> $pkgName\n"; + + my $pkg = $pkgs{$pkgName} or die "package $pkgName doesn't exist"; + + my $requires = $pkg->{format}->{'rpm:requires'}->{'rpm:entry'} || []; + + my @deps = (); + foreach my $req (@{$requires}) { + next if $req->{name} =~ /^rpmlib\(/; + #print STDERR " needs $req->{name}\n"; + my $provider = $provides{$req->{name}}; + if (!defined $provider) { + print STDERR " WARNING: no provider for $req->{name}\n"; + next; + } + #print STDERR " satisfied by $provider\n"; + push @deps, $provider; + } + + closePackage($_) foreach @deps; + + push @needed, $pkgName; +} + + +foreach my $pkgName (@toplevelPkgs) { + closePackage $pkgName; +} + + +# Generate the output Nix expression. +print "# This is a generated file. Do not modify!\n"; +print "# Following are the RPM packages constituting the closure of: @toplevelPkgs\n\n"; +print "{fetchurl}:\n\n"; +print "[\n\n"; + +foreach my $pkgName (@needed) { + my $pkg = $pkgs{$pkgName}; + print " (fetchurl {\n"; + print " url = $pkg->{urlPrefix}/$pkg->{location}->{href};\n"; + if ($pkg->{checksum}->{type} eq "sha") { + print " sha1 = \"$pkg->{checksum}->{content}\";\n"; + } elsif ($pkg->{checksum}->{type} eq "sha256") { + print " sha256 = \"$pkg->{checksum}->{content}\";\n"; + } else { + die "unsupported hash type"; + } + print " })\n"; + print "\n"; +} + +print "]\n"; diff --git a/nixpkgs/pkgs/build-support/vm/test.nix b/nixpkgs/pkgs/build-support/vm/test.nix new file mode 100644 index 000000000000..50dbfeb750be --- /dev/null +++ b/nixpkgs/pkgs/build-support/vm/test.nix @@ -0,0 +1,58 @@ +let + pkgs = import ../../.. { }; + + inherit (pkgs) + hello + patchelf + pcmanfm + stdenv + ; + + inherit (pkgs.vmTools) + buildRPM + diskImages + makeImageTestScript + runInLinuxImage + runInLinuxVM + ; +in + +{ + + + # Run the PatchELF derivation in a VM. + buildPatchelfInVM = runInLinuxVM patchelf; + + buildHelloInVM = runInLinuxVM hello; + + buildPcmanrmInVM = runInLinuxVM (pcmanfm.overrideAttrs (old: { + # goes out-of-memory with many cores + enableParallelBuilding = false; + })); + + testRPMImage = makeImageTestScript diskImages.fedora27x86_64; + + + buildPatchelfRPM = buildRPM { + name = "patchelf-rpm"; + src = patchelf.src; + diskImage = diskImages.fedora27x86_64; + diskImageFormat = "qcow2"; + }; + + + testUbuntuImage = makeImageTestScript diskImages.ubuntu1804i386; + + + buildInDebian = runInLinuxImage (stdenv.mkDerivation { + name = "deb-compile"; + src = patchelf.src; + diskImage = diskImages.ubuntu1804i386; + diskImageFormat = "qcow2"; + memSize = 512; + postHook = '' + dpkg-query --list + ''; + }); + +} |