diff --git a/README.md b/README.md index 4b8e9c8..9dbd617 100644 --- a/README.md +++ b/README.md @@ -28,21 +28,14 @@ How to use Install a Windows image ----------------------- -1. Adjust demo-image.nix accordingly +1. Adjust demo-image in ``flake.nix`` accordingly 2. Run: -If in impure mode ```shell -nix-build demo-image.nix +nix build .#demo-image ./result ``` -Results in a file called c.img -If in pure mode -```shell -nix-build demo-image.nix -ls -la ./result -``` Results in a symlink to the image in the nix store @@ -52,3 +45,5 @@ Impure/pure mode Sometimes it can be useful to build the image _outside_ of the Nix sandbox for debugging purposes. For this purpose we have an attribute called `impureMode` which outputs the shell script used by Nix inside the sandbox to build the image. + +When building an image with flakes, use ``nix build .#demo-image-impure`` instead. \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..4f34ad6 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1593034146, + "narHash": "sha256-EypP7RyPq5Yv05VgsQoIkdn26KJUIgQCItHgVY1MMQE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f8248ab6d9e69ea9c07950d73d48807ec595e923", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f8248ab6d9e69ea9c07950d73d48807ec595e923", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..c52e4c1 --- /dev/null +++ b/flake.nix @@ -0,0 +1,129 @@ +{ + description = "A Nix library to create and manage virtual machines running Windows."; + inputs.nixpkgs.url = github:NixOS/nixpkgs/f8248ab6d9e69ea9c07950d73d48807ec595e923; + outputs = { self, nixpkgs }: + let + pkgs = import nixpkgs { system = "x86_64-linux"; }; + lib = pkgs.lib; + # utils + utils = (import wfvm/utils.nix { inherit pkgs; }); + # layers + layers = (import wfvm/layers { inherit pkgs; }); # end of layers + + + + # makeWindowsImage + makeWindowsImage = attrs: import wfvm/win.nix ({ inherit pkgs; } // attrs ); + + build-demo-image = { impureMode ? false }: makeWindowsImage { + # Build install script & skip building iso + + inherit impureMode; + + # Custom base iso + # windowsImage = pkgs.requireFile rec { + # name = "Win10_21H1_English_x64.iso"; + # sha256 = "1sl51lnx4r6ckh5fii7m2hi15zh8fh7cf7rjgjq9kacg8hwyh4b9"; + # message = "Get ${name} from https://www.microsoft.com/en-us/software-download/windows10ISO"; + # }; + + # impureShellCommands = [ + # "powershell.exe echo Hello" + # ]; + + # User accounts + # users = { + # artiq = { + # password = "1234"; + # # description = "Default user"; + # # displayName = "Display name"; + # groups = [ + # "Administrators" + # ]; + # }; + # }; + + # Auto login + # defaultUser = "artiq"; + + # fullName = "M-Labs"; + # organization = "m-labs"; + # administratorPassword = "12345"; + + # Imperative installation commands, to be installed incrementally + installCommands = if impureMode then [] else (with layers; [ + (collapseLayers [ + disable-autosleep + disable-autolock + disable-firewall + ]) + anaconda3 msys2 msvc msvc-ide-unbreak + ]); + + # services = { + # # Enable remote management + # WinRm = { + # Status = "Running"; + # PassThru = true; + # }; + # }; + + # License key (required) + # productKey = throw "Search the f* web" + # imageSelection = "1"; + + # Locales + # uiLanguage = "en-US"; + # inputLocale = "en-US"; + # userLocale = "en-US"; + # systemLocale = "en-US"; + }; + + in { + + # bundle dev env + devShell.x86_64-linux = pkgs.mkShell { + name = "wfvm-dev-shell"; + buildInputs = with pkgs; [ + go + ]; + shellHook = '' + unset GOPATH + ''; + }; + + inherit utils; + inherit makeWindowsImage; + inherit layers; + + demo-ssh = utils.wfvm-run { + name = "demo-ssh"; + image = build-demo-image {}; + isolateNetwork = false; + script = '' + ${pkgs.sshpass}/bin/sshpass -p1234 -- ${pkgs.openssh}/bin/ssh -p 2022 wfvm@localhost -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null + ''; + }; + + packages.x86_64-linux = { + demo-image = build-demo-image {}; + demo-image-impure = build-demo-image { impureMode = true; }; + + make-msys-packages = utils.wfvm-run { + name = "get-msys-packages"; + image = makeWindowsImage { installCommands = [ layers.msys2 ]; }; + script = '' + cat > getmsyspackages.bat << EOF + set MSYS=C:\\MSYS64 + set TOOLPREF=mingw-w64-x86_64- + set PATH=%MSYS%\usr\bin;%MSYS%\mingw64\bin;%PATH% + pacman -Sp %TOOLPREF%gcc %TOOLPREF%binutils make autoconf automake libtool texinfo > packages.txt + EOF + \${utils.win-put}/bin/win-put getmsyspackages.bat + \${utils.win-exec}/bin/win-exec getmsyspackages + \${utils.win-get}/bin/win-get packages.txt + ''; + }; + }; + }; +} \ No newline at end of file diff --git a/wfvm/autounattend.nix b/wfvm/autounattend.nix index 99b0429..abd1c16 100644 --- a/wfvm/autounattend.nix +++ b/wfvm/autounattend.nix @@ -15,7 +15,7 @@ , impureShellCommands ? [] , driveLetter ? "D:" , efi ? true -, imageSelection ? "Windows 10 Pro" +, imageSelection ? "1" , ... }: @@ -200,7 +200,7 @@ let - /IMAGE/NAME + /IMAGE/INDEX ${imageSelection} diff --git a/wfvm/bundle/default.nix b/wfvm/bundle/default.nix deleted file mode 100644 index 16a0fed..0000000 --- a/wfvm/bundle/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ pkgs }: - -pkgs.runCommandNoCC "win-bundle-installer.exe" {} '' - mkdir bundle - cd bundle - cp ${./go.mod} go.mod - cp ${./main.go} main.go - env HOME=$(mktemp -d) GOOS=windows GOARCH=amd64 ${pkgs.go}/bin/go build - mv bundle.exe $out -'' diff --git a/wfvm/bundle/shell.nix b/wfvm/bundle/shell.nix deleted file mode 100644 index 20c60ed..0000000 --- a/wfvm/bundle/shell.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ pkgs ? import {} }: - -pkgs.mkShell { - - buildInputs = [ - pkgs.go - ]; - - shellHook = '' - unset GOPATH - ''; - -} diff --git a/wfvm/default.nix b/wfvm/default.nix deleted file mode 100644 index 7846583..0000000 --- a/wfvm/default.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ pkgs }: - -{ - makeWindowsImage = attrs: import ./win.nix ({ inherit pkgs; } // attrs); - layers = (import ./layers { inherit pkgs; }); - utils = (import ./utils.nix { inherit pkgs; }); -} diff --git a/wfvm/demo-image.nix b/wfvm/demo-image.nix deleted file mode 100644 index a928de1..0000000 --- a/wfvm/demo-image.nix +++ /dev/null @@ -1,72 +0,0 @@ -{ pkgs ? import {}, impureMode ? false }: - -let - wfvm = (import ./default.nix { inherit pkgs; }); -in -wfvm.makeWindowsImage { - # Build install script & skip building iso - inherit impureMode; - - # Custom base iso - # windowsImage = pkgs.requireFile rec { - # name = "Win10_21H1_English_x64.iso"; - # sha256 = "1sl51lnx4r6ckh5fii7m2hi15zh8fh7cf7rjgjq9kacg8hwyh4b9"; - # message = "Get ${name} from https://www.microsoft.com/en-us/software-download/windows10ISO"; - # }; - - # impureShellCommands = [ - # "powershell.exe echo Hello" - # ]; - - # User accounts - # users = { - # artiq = { - # password = "1234"; - # # description = "Default user"; - # # displayName = "Display name"; - # groups = [ - # "Administrators" - # ]; - # }; - # }; - - # Auto login - # defaultUser = "artiq"; - - # fullName = "M-Labs"; - # organization = "m-labs"; - # administratorPassword = "12345"; - - # Imperative installation commands, to be installed incrementally - installCommands = - if impureMode - then [] - else with wfvm.layers; [ - (collapseLayers [ - disable-autosleep - disable-autolock - disable-firewall - ]) - anaconda3 msys2 msvc msvc-ide-unbreak - ]; - - # services = { - # # Enable remote management - # WinRm = { - # Status = "Running"; - # PassThru = true; - # }; - # }; - - # License key (required) - # productKey = throw "Search the f* web" - imageSelection = "Windows 10 Pro"; - - - # Locales - # uiLanguage = "en-US"; - # inputLocale = "en-US"; - # userLocale = "en-US"; - # systemLocale = "en-US"; - -} diff --git a/wfvm/demo-ssh.nix b/wfvm/demo-ssh.nix deleted file mode 100644 index 47c60e4..0000000 --- a/wfvm/demo-ssh.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ pkgs ? import {} }: - -let - wfvm = (import ./default.nix { inherit pkgs; }); -in - wfvm.utils.wfvm-run { - name = "demo-ssh"; - image = import ./demo-image.nix { inherit pkgs; }; - isolateNetwork = false; - script = '' - ${pkgs.sshpass}/bin/sshpass -p1234 -- ${pkgs.openssh}/bin/ssh -p 2022 wfvm@localhost -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null - ''; - } diff --git a/wfvm/layers/default.nix b/wfvm/layers/default.nix index a9068a6..ec20fd3 100644 --- a/wfvm/layers/default.nix +++ b/wfvm/layers/default.nix @@ -1,6 +1,7 @@ { pkgs }: let - wfvm = import ../. { inherit pkgs; }; + makeWindowsImage = attrs: import ../win.nix ({ inherit pkgs; } // attrs ); + utils = import ../utils.nix { inherit pkgs; }; in { anaconda3 = { @@ -72,20 +73,20 @@ in bootstrapper = pkgs.fetchurl { name = "RESTRICTDIST-vs_Community.exe"; url = "https://aka.ms/vs/16/release/vs_community.exe"; - sha256 = "0b3csxz0qsafnvc0d74ywfpralwz8chv4zf9k07akpm8lp8ycgq0"; + sha256 = "sha256-bxi8LsvNxSZshkTbhK/FEmMx84NKYB7TUNOm9sAKXS8="; }; # This touchy-feely "community" piece of trash seems deliberately crafted to break Wine, so we use the VM to run it. - download-vs = wfvm.utils.wfvm-run { + download-vs = utils.wfvm-run { name = "download-vs"; - image = wfvm.makeWindowsImage { }; + image = makeWindowsImage { }; isolateNetwork = false; script = '' ln -s ${bootstrapper} vs_Community.exe - ${wfvm.utils.win-put}/bin/win-put vs_Community.exe + ${utils.win-put}/bin/win-put vs_Community.exe rm vs_Community.exe - ${wfvm.utils.win-exec}/bin/win-exec "vs_Community.exe --quiet --norestart --layout c:\vslayout --add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended --lang en-US" - ${wfvm.utils.win-get}/bin/win-get /c:/vslayout + ${utils.win-exec}/bin/win-exec "vs_Community.exe --quiet --norestart --layout c:\vslayout --add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended --lang en-US" + ${utils.win-get}/bin/win-get /c:/vslayout ''; }; cache = pkgs.stdenv.mkDerivation { diff --git a/wfvm/layers/make_msys_packages.sh b/wfvm/layers/make_msys_packages.sh index fa8595e..ef39678 100755 --- a/wfvm/layers/make_msys_packages.sh +++ b/wfvm/layers/make_msys_packages.sh @@ -2,27 +2,7 @@ set -e -nix-build -E " -let - pkgs = import {}; - wfvm = import ../default.nix { inherit pkgs; }; -in - wfvm.utils.wfvm-run { - name = \"get-msys-packages\"; - image = wfvm.makeWindowsImage { installCommands = [ wfvm.layers.msys2 ]; }; - script = '' - cat > getmsyspackages.bat << EOF - set MSYS=C:\\MSYS64 - set TOOLPREF=mingw-w64-x86_64- - set PATH=%MSYS%\usr\bin;%MSYS%\mingw64\bin;%PATH% - pacman -Sp %TOOLPREF%gcc %TOOLPREF%binutils make autoconf automake libtool texinfo > packages.txt - EOF - \${wfvm.utils.win-put}/bin/win-put getmsyspackages.bat - \${wfvm.utils.win-exec}/bin/win-exec getmsyspackages - \${wfvm.utils.win-get}/bin/win-get packages.txt - ''; - } -" +nix build .#make-msys-packages ./result/bin/wfvm-run-get-msys-packages diff --git a/wfvm/win.nix b/wfvm/win.nix index 0e153e4..cd265f4 100644 --- a/wfvm/win.nix +++ b/wfvm/win.nix @@ -8,6 +8,7 @@ # autounattend always installs index 1, so this default is backward-compatible , imageSelection ? "Windows 10 Pro" , efi ? true +, bundleInstaller ? {} , ... }@attrs: @@ -23,6 +24,16 @@ let }; }); + # bundle + bundleInstaller = pkgs.runCommandNoCC "win-bundle-installer.exe" {} '' + mkdir bundle + cd bundle + cp ${bundle/go.mod} go.mod + cp ${bundle/main.go} main.go + env HOME=$(mktemp -d) GOOS=windows GOARCH=amd64 ${pkgs.go}/bin/go build + mv bundle.exe $out + ''; + runQemuCommand = name: command: ( pkgs.runCommandNoCC name { buildInputs = [ p7zip utils.qemu libguestfs ]; } ( @@ -36,8 +47,8 @@ let ); windowsIso = if windowsImage != null then windowsImage else pkgs.requireFile rec { - name = "Win10_21H1_English_x64.iso"; - sha256 = "1sl51lnx4r6ckh5fii7m2hi15zh8fh7cf7rjgjq9kacg8hwyh4b9"; + name = "xks67i4frg8k7rmlv5298aac0s4n5nih-RESTRICTDIST-release_svc_refresh_CLIENT_LTSC_EVAL_x64FRE_en-us.iso"; + sha256 = "0fmw30g7959bh47z8xi5pmmxq4kb0sxs1qxf51il3xy2f2py33v6"; message = "Get ${name} from https://www.microsoft.com/en-us/software-download/windows10ISO"; }; @@ -67,8 +78,6 @@ let } ); - bundleInstaller = pkgs.callPackage ./bundle {}; - # Packages required to drive installation of other packages bootstrapPkgs = runQemuCommand "bootstrap-win-pkgs.img" ''