dotfiles/nix-conf/system/nixos-wsl/modules/interop.nix

92 lines
3.1 KiB
Nix
Raw Permalink Normal View History

2022-08-30 14:29:44 +00:00
{
2023-06-18 00:46:31 +00:00
lib,
pkgs,
config,
...
}:
with builtins;
with lib; {
2022-08-30 14:29:44 +00:00
imports = [
2023-06-18 00:46:31 +00:00
(mkRenamedOptionModule ["wsl" "compatibility" "interopPreserveArgvZero"] ["wsl" "interop" "preserveArgvZero"])
2022-08-30 14:29:44 +00:00
];
options.wsl.interop = with types; {
register = mkOption {
type = bool;
default = false; # Use the existing registration by default
description = "Explicitly register the binfmt_misc handler for Windows executables";
};
includePath = mkOption {
type = bool;
default = true;
description = "Include Windows PATH in WSL PATH";
};
preserveArgvZero = mkOption {
type = nullOr bool;
default = null;
description = ''
Register binfmt interpreter for Windows executables with 'preserves argv[0]' flag.
Default (null): autodetect, at some performance cost.
To avoid the performance cost, set this to true for WSL Preview 0.58 and up,
or to false for any older versions, including pre-Microsoft Store and Windows 10.
'';
};
};
2023-06-18 00:46:31 +00:00
config = let
cfg = config.wsl.interop;
in
2022-08-30 14:29:44 +00:00
mkIf config.wsl.enable {
boot.binfmt.registrations = mkIf cfg.register {
2023-06-18 00:46:31 +00:00
WSLInterop = let
compat = cfg.preserveArgvZero;
2022-08-30 14:29:44 +00:00
2023-06-18 00:46:31 +00:00
# WSL Preview 0.58 and up registers the /init binfmt interp for Windows executable
# with the "preserve argv[0]" flag, so if you run `./foo.exe`, the interp gets invoked
# as `/init foo.exe ./foo.exe`.
# argv[0] --^ ^-- actual path
#
# Older versions expect to be called without the argv[0] bit, simply as `/init ./foo.exe`.
#
# We detect that by running `/init /known-not-existing-path.exe` and checking the exit code:
# the new style interp expects at least two arguments, so exits with exit code 1,
# presumably meaning "parsing error"; the old style interp attempts to actually run
# the executable, fails to find it, and exits with 255.
compatWrapper = pkgs.writeShellScript "nixos-wsl-binfmt-hack" ''
/init /nixos-wsl-does-not-exist.exe
[ $? -eq 255 ] && shift
exec /init "$@"
'';
2022-08-30 14:29:44 +00:00
2023-06-18 00:46:31 +00:00
# use the autodetect hack if unset, otherwise call /init directly
interpreter =
if compat == null
then compatWrapper
else "/init";
2022-08-30 14:29:44 +00:00
2023-06-18 00:46:31 +00:00
# enable for the wrapper and autodetect hack
preserveArgvZero =
if compat == false
then false
else true;
in {
magicOrExtension = "MZ";
fixBinary = true;
wrapInterpreterInShell = false;
inherit interpreter preserveArgvZero;
};
2022-08-30 14:29:44 +00:00
};
# Include Windows %PATH% in Linux $PATH.
environment.extraInit = mkIf cfg.includePath ''PATH="$PATH:$WSLPATH"'';
2023-06-18 00:46:31 +00:00
warnings = let
registrations = config.boot.binfmt.registrations;
in
2022-08-30 14:29:44 +00:00
optional (!(registrations ? WSLInterop) && (length (attrNames config.boot.binfmt.registrations)) != 0) "Having any binfmt registrations without re-registering WSLInterop (wsl.interop.register) will break running .exe files from WSL2";
};
}