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