{ config, lib, options, pkgs, ... }: let packageName = "custom-nixos-vm"; mergedSystemOption = with options.system; lib.mergeDefinitions build.loc build.type (lib.lists.forEach ( builtins.filter (item: !(lib.path.hasPrefix ./. (/. + item.file))) build.definitionsWithLocations) (item: { inherit (item) file value; })); vanillaVM = mergedSystemOption.mergedValue.vm; in { options = let vmVariantOptions = { virtualisation = { runAsRoot = lib.mkOption { type = lib.types.bool; default = false; }; sharedHostKeys = lib.mkOption { type = lib.types.bool; default = false; }; virt-viewer = lib.mkOption { type = lib.types.bool; default = false; }; qemu = { runInBackground = lib.mkOption { type = lib.types.bool; default = false; }; spice = { enable = lib.mkEnableOption "spice"; bindAddress = lib.mkOption { type = lib.types.str; default = "127.0.0.1"; }; port = lib.mkOption { type = lib.types.port; default = 5900; }; }; }; }; }; in { virtualisation = { vmVariant = vmVariantOptions; vmVariantWithBootLoader = vmVariantOptions; }; }; config = { virtualisation = let extendVMConfig = vmVariant: { boot.loader.efi.efiSysMountPoint = lib.mkVMOverride "/boot"; virtualisation = { runAsRoot = lib.mkIf vmVariant.virtualisation.sharedHostKeys true; qemu = { spice.enable = lib.mkIf vmVariant.virtualisation.virt-viewer true; runInBackground = lib.mkIf vmVariant.virtualisation.virt-viewer true; options = with { inherit (vmVariant.virtualisation.qemu) spice; }; ( lib.optional (spice.enable) ("-spice " + ( lib.concatStringsSep "," [ "addr=${lib.escapeShellArg spice.bindAddress}" "port=${toString spice.port}" "disable-ticketing=on" ]))); }; sharedDirectories = lib.optionalAttrs (vmVariant.virtualisation.sharedHostKeys) { hostKeys = let path = "/etc/ssh"; in { source = path; target = path; }; }; }; }; virtualisation = config.virtualisation; in { vmVariant = extendVMConfig virtualisation.vmVariant; vmVariantWithBootLoader = extendVMConfig virtualisation.vmVariantWithBootLoader; }; # Replace native `qemu` with `remote-viewer` system.build = { vm = lib.mkForce ( ( vm: if (vm.name == packageName) then vm else let prefix = lib.concatStringsSep " " ( lib.optionals config.virtualisation.runAsRoot ( ["sudo"] ++ lib.optional config.virtualisation.qemu.runInBackground "-b")); suffix = lib.concatStringsSep " " ( lib.optional (!config.virtualisation.runAsRoot && config.virtualisation.qemu.runInBackground) "&"); wrapped = pkgs.writeShellApplication { name = "run-${config.system.name}-vm"; text = lib.strings.concatLines ( [ "${prefix} ${vm}/bin/run-${config.system.name}-vm ${suffix}" ] ++ ( let spice = config.virtualisation.qemu.spice; in ( lib.optionals config.virtualisation.virt-viewer [ "${pkgs.virt-viewer}/bin/remote-viewer spice://${lib.escapeShellArg spice.bindAddress}:${toString spice.port}" "kill %1" ]))); }; in pkgs.symlinkJoin { name = packageName; paths = [ wrapped ]; }) vanillaVM); }; }; }