dotnet: split setup hooks into wrapper for runtime/sdk

This commit is contained in:
David McFarland 2024-09-25 15:54:03 -03:00
parent 6bed24773e
commit ddd08e404f
15 changed files with 276 additions and 261 deletions

View File

@ -719,6 +719,11 @@
- `python3Packages.nose` has been removed, as it has been deprecated and unmaintained for almost a decade and does not work on Python 3.12.
Please switch to `pytest` or another test runner/framework.
- `dotnet-sdk`, `dotnet-runtime`, and all other dotnet packages now use a
wrapper package containing `bin/dotnet`, build hooks, etc. If you need to
reference the underlying dotnet distribution (DOTNET_ROOT) you should use e.g.
`dotnet-runtime.unwrapped`.
## Other Notable Changes {#sec-release-24.11-notable-changes}
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->

View File

@ -52,7 +52,8 @@
{
name = "dotnet-fixup-hook";
substitutions = {
dotnetRuntime = dotnet-runtime;
# this is used for DOTNET_ROOT, so we need unwrapped
dotnetRuntime = dotnet-runtime.unwrapped;
wrapperPath = lib.makeBinPath [ which coreutils ];
};
}

View File

@ -32,7 +32,7 @@
let
dyalogHome = "$out/lib/dyalog";
makeWrapperArgs = lib.optional dotnetSupport "--set DOTNET_ROOT ${dotnet-sdk_8}";
makeWrapperArgs = lib.optional dotnetSupport "--set DOTNET_ROOT ${dotnet-sdk_8.unwrapped}";
licenseUrl = "https://www.dyalog.com/uploads/documents/Developer_Software_Licence.pdf";

View File

@ -72,7 +72,7 @@ mkPackage rec {
# The provided libhostfxr.dylib is for x86_64-darwin, so we remove it
rm artifacts/mono-msbuild/SdkResolvers/Microsoft.DotNet.MSBuildSdkResolver/libhostfxr.dylib
ln -s $(find ${dotnet-sdk} -name libhostfxr${sharedLibrary}) artifacts/mono-msbuild/SdkResolvers/Microsoft.DotNet.MSBuildSdkResolver/
ln -s $(find ${dotnet-sdk.unwrapped} -name libhostfxr${sharedLibrary}) artifacts/mono-msbuild/SdkResolvers/Microsoft.DotNet.MSBuildSdkResolver/
# overwrite the file
echo "#!${stdenv.shell}" > eng/common/dotnet-install.sh
@ -82,7 +82,7 @@ mkPackage rec {
echo "#!${stdenv.shell}" > mono/build/extract_and_copy_hostfxr.sh
mkdir -p mono/dotnet-overlay/msbuild-bin
cp ${dotnet-sdk}/sdk/*/{Microsoft.NETCoreSdk.BundledVersions.props,RuntimeIdentifierGraph.json} mono/dotnet-overlay/msbuild-bin
cp ${dotnet-sdk.unwrapped}/sdk/*/{Microsoft.NETCoreSdk.BundledVersions.props,RuntimeIdentifierGraph.json} mono/dotnet-overlay/msbuild-bin
# DisableNerdbankVersioning https://gitter.im/Microsoft/msbuild/archives/2018/06/27?at=5b33dbc4ce3b0f268d489bfa
# TODO there are some (many?) failing tests
@ -99,7 +99,7 @@ mkPackage rec {
--set-default MONO_GC_PARAMS "nursery-size=64m" \
--add-flags "$out/lib/mono/msbuild/15.0/bin/MSBuild.dll"
ln -s $(find ${dotnet-sdk} -name libhostfxr${sharedLibrary}) $out/lib/mono/msbuild/Current/bin/SdkResolvers/Microsoft.DotNet.MSBuildSdkResolver/
ln -s $(find ${dotnet-sdk.unwrapped} -name libhostfxr${sharedLibrary}) $out/lib/mono/msbuild/Current/bin/SdkResolvers/Microsoft.DotNet.MSBuildSdkResolver/
'';
doInstallCheck = true;

View File

@ -36,7 +36,7 @@ let
cmakeFlags = [
"-DCORECLR_DIR=${coreclr-src}/src/coreclr"
"-DDOTNET_DIR=${dotnet-sdk}"
"-DDOTNET_DIR=${dotnet-sdk.unwrapped}"
"-DBUILD_MANAGED=0"
];
};

View File

@ -90,7 +90,7 @@ buildPythonApplication rec {
VIRTUALENV_NO_DOWNLOAD=1 PRE_COMMIT_NO_CONCURRENCY=1 LANG=en_US.UTF-8
# Resolve `.NET location: Not found` errors for dotnet tests
export DOTNET_ROOT="${dotnet-sdk}"
export DOTNET_ROOT="${dotnet-sdk.unwrapped}"
export HOME=$(mktemp -d)

View File

@ -57,7 +57,7 @@ let
sdk = ".NET SDK ${version}";
};
mkCommon = callPackage ./common.nix { };
mkWrapper = callPackage ./wrapper.nix { };
hostRid = systemToDotnetRid stdenv.hostPlatform.system;
targetRid = systemToDotnetRid stdenv.targetPlatform.system;
@ -91,7 +91,7 @@ let
);
in
mkCommon type rec {
mkWrapper type (stdenv.mkDerivation rec {
inherit pname version;
# Some of these dependencies are `dlopen()`ed.
@ -222,4 +222,4 @@ mkCommon type rec {
binaryNativeCode
];
};
}
})

View File

@ -20,7 +20,7 @@ assert lib.assertMsg ((builtins.length dotnetPackages) > 0) ''
];`'';
buildEnv {
name = "dotnet-core-combined";
paths = dotnetPackages;
paths = map (x: x.unwrapped) dotnetPackages;
pathsToLink = [
"/host"
"/packs"
@ -31,10 +31,10 @@ buildEnv {
];
ignoreCollisions = true;
postBuild = ''
cp -R ${cli}/{dotnet,share,nix-support} $out/
mkdir $out/bin
ln -s $out/dotnet $out/bin/dotnet
cp -R "${cli.unwrapped}"/dotnet $out/
cp -R "${cli}"/nix-support "$out"/
mkdir "$out"/bin
ln -s "$out"/dotnet "$out"/bin/dotnet
'';
passthru = {
inherit (cli) icu;

View File

@ -1,240 +0,0 @@
{
stdenv,
stdenvNoCC,
lib,
writeText,
testers,
runCommand,
runCommandWith,
expect,
curl,
installShellFiles,
callPackage,
zlib,
swiftPackages,
darwin,
icu,
lndir,
substituteAll,
nugetPackageHook,
xmlstarlet,
}:
type: args:
stdenvNoCC.mkDerivation (
finalAttrs:
args
// {
doInstallCheck = true;
# TODO: this should probably be postInstallCheck
# TODO: send output to /dev/null
installCheckPhase =
args.installCheckPhase or ""
+ ''
$out/bin/dotnet --info
'';
setupHooks =
args.setupHooks or [ ]
++ [
./dotnet-setup-hook.sh
]
++ lib.optional (type == "sdk") (substituteAll {
src = ./dotnet-sdk-setup-hook.sh;
inherit lndir xmlstarlet;
});
propagatedBuildInputs =
(args.propagatedBuildInputs or [ ])
++ lib.optional (type == "sdk") nugetPackageHook;
nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [ installShellFiles ];
postInstall = ''
# completions snippets taken from https://learn.microsoft.com/en-us/dotnet/core/tools/enable-tab-autocomplete
installShellCompletion --cmd dotnet \
--bash ${./completions/dotnet.bash} \
--zsh ${./completions/dotnet.zsh} \
--fish ${./completions/dotnet.fish}
'';
passthru = {
tests =
let
mkDotnetTest =
{
name,
stdenv ? stdenvNoCC,
template,
usePackageSource ? false,
build,
buildInputs ? [ ],
# TODO: use correct runtimes instead of sdk
runtime ? finalAttrs.finalPackage,
runInputs ? [ ],
run ? null,
runAllowNetworking ? false,
}:
let
sdk = finalAttrs.finalPackage;
built = stdenv.mkDerivation {
name = "dotnet-test-${name}";
buildInputs = [ sdk ] ++ buildInputs ++ lib.optional (usePackageSource) sdk.packages;
# make sure ICU works in a sandbox
propagatedSandboxProfile = toString sdk.__propagatedSandboxProfile;
unpackPhase = ''
mkdir test
cd test
dotnet new ${template} -o . --no-restore
'';
buildPhase = build;
dontPatchELF = true;
};
in
if run == null then
built
else
runCommand "${built.name}-run"
(
{
src = built;
nativeBuildInputs = [ built ] ++ runInputs;
passthru = {
inherit built;
};
}
// lib.optionalAttrs (stdenv.hostPlatform.isDarwin && runAllowNetworking) {
sandboxProfile = ''
(allow network-inbound (local ip))
(allow mach-lookup (global-name "com.apple.FSEvents"))
'';
__darwinAllowLocalNetworking = true;
}
)
(
lib.optionalString (runtime != null) ''
# TODO: use runtime here
export DOTNET_ROOT=${runtime}
''
+ run
);
# Setting LANG to something other than 'C' forces the runtime to search
# for ICU, which will be required in most user environments.
checkConsoleOutput = command: ''
output="$(LANG=C.UTF-8 ${command})"
# yes, older SDKs omit the comma
[[ "$output" =~ Hello,?\ World! ]] && touch "$out"
'';
in
{
version = testers.testVersion (
{
package = finalAttrs.finalPackage;
}
// lib.optionalAttrs (type != "sdk") {
command = "dotnet --info";
}
);
}
// lib.optionalAttrs (type == "sdk") (
{
console = mkDotnetTest {
name = "console";
template = "console";
build = checkConsoleOutput "dotnet run";
};
publish = mkDotnetTest {
name = "publish";
template = "console";
build = "dotnet publish -o $out/bin";
run = checkConsoleOutput "$src/bin/test";
};
self-contained = mkDotnetTest {
name = "self-contained";
template = "console";
usePackageSource = true;
build = "dotnet publish --use-current-runtime --sc -o $out";
runtime = null;
run = checkConsoleOutput "$src/test";
};
single-file = mkDotnetTest {
name = "single-file";
template = "console";
usePackageSource = true;
build = "dotnet publish --use-current-runtime -p:PublishSingleFile=true -o $out/bin";
runtime = null;
run = checkConsoleOutput "$src/bin/test";
};
web = mkDotnetTest {
name = "web";
template = "web";
build = "dotnet publish -o $out/bin";
runInputs = [
expect
curl
];
run = ''
expect <<"EOF"
set status 1
spawn $env(src)/bin/test
proc abort { } { exit 2 }
expect_before default abort
expect -re {Now listening on: ([^\r]+)\r} {
set url $expect_out(1,string)
}
expect "Application started. Press Ctrl+C to shut down."
set output [exec curl -sSf $url]
if {$output != "Hello World!"} {
send_error "Unexpected output: $output\n"
exit 1
}
send \x03
expect_before timeout abort
expect eof
catch wait result
exit [lindex $result 3]
EOF
touch $out
'';
runAllowNetworking = true;
};
}
// lib.optionalAttrs finalAttrs.finalPackage.hasILCompiler {
aot = mkDotnetTest {
name = "aot";
stdenv = if stdenv.hostPlatform.isDarwin then swiftPackages.stdenv else stdenv;
template = "console";
usePackageSource = true;
buildInputs =
[
zlib
]
++ lib.optional stdenv.hostPlatform.isDarwin (
with darwin;
with apple_sdk.frameworks;
[
swiftPackages.swift
Foundation
CryptoKit
GSS
ICU
]
);
build = ''
dotnet restore -p:PublishAot=true
dotnet publish -p:PublishAot=true -o $out/bin
'';
runtime = null;
run = checkConsoleOutput "$src/bin/test";
};
}
)
// args.passthru.tests or { };
} // args.passthru or { };
}
)

View File

@ -26,7 +26,7 @@ let
tarballHash
depsFile
;
bootstrapSdk = (buildDotnetSdk bootstrapSdkFile).sdk.overrideAttrs (old: {
bootstrapSdk = (buildDotnetSdk bootstrapSdkFile).sdk.unwrapped.overrideAttrs (old: {
passthru = old.passthru or { } // {
artifacts = stdenvNoCC.mkDerivation rec {
name = lib.nameFromURL artifactsUrl ".tar.gz";

View File

@ -12,7 +12,8 @@
}:
let
mkCommon = callPackage ./common.nix { };
mkWrapper = callPackage ./wrapper.nix { };
mkCommon = type: args: mkWrapper type (stdenvNoCC.mkDerivation args);
inherit (vmr) targetRid releaseManifest;
# TODO: do this properly

View File

@ -23,7 +23,7 @@ let
vmr =
(mkVMR {
inherit releaseManifestFile tarballHash;
bootstrapSdk = stage0.sdk;
bootstrapSdk = stage0.sdk.unwrapped;
}).overrideAttrs
(old: {
passthru = old.passthru or { } // {

View File

@ -100,7 +100,7 @@ stdenv.mkDerivation rec {
buildInputs =
[
# this gets copied into the tree, but we still want the hooks to run
# this gets copied into the tree, but we still need the sandbox profile
bootstrapSdk
# the propagated build inputs in llvm.dev break swift compilation
llvm.out
@ -364,6 +364,7 @@ stdenv.mkDerivation rec {
cp -Tr ${bootstrapSdk} .dotnet
chmod -R +w .dotnet
export HOME=$(mktemp -d)
${prepScript} $prepFlags
runHook postConfigure

View File

@ -0,0 +1,247 @@
{
stdenv,
stdenvNoCC,
lib,
writeText,
testers,
runCommand,
runCommandWith,
expect,
curl,
installShellFiles,
callPackage,
zlib,
swiftPackages,
darwin,
icu,
lndir,
substituteAll,
nugetPackageHook,
xmlstarlet,
}:
type: unwrapped:
stdenvNoCC.mkDerivation (finalAttrs: {
pname = "${unwrapped.pname}-wrapped";
inherit (unwrapped) version meta;
src = unwrapped;
dontUnpack = true;
doInstallCheck = true;
installPhase = ''
runHook preInstall
mkdir -p "$out"/bin
ln -s "$src"/bin/* "$out"/bin
runHook postInstall
'';
installCheckPhase = ''
runHook preInstallCheck
$out/bin/dotnet --info
runHook postInstallCheck
'';
setupHooks =
[
./dotnet-setup-hook.sh
]
++ lib.optional (type == "sdk") (substituteAll {
src = ./dotnet-sdk-setup-hook.sh;
inherit lndir xmlstarlet;
});
propagatedSandboxProfile = toString unwrapped.__propagatedSandboxProfile;
propagatedBuildInputs = lib.optional (type == "sdk") nugetPackageHook;
nativeBuildInputs = [ installShellFiles ];
postInstall = ''
# completions snippets taken from https://learn.microsoft.com/en-us/dotnet/core/tools/enable-tab-autocomplete
installShellCompletion --cmd dotnet \
--bash ${./completions/dotnet.bash} \
--zsh ${./completions/dotnet.zsh} \
--fish ${./completions/dotnet.fish}
'';
passthru = unwrapped.passthru // {
inherit unwrapped;
tests =
let
mkDotnetTest =
{
name,
stdenv ? stdenvNoCC,
template,
usePackageSource ? false,
build,
buildInputs ? [ ],
# TODO: use correct runtimes instead of sdk
runtime ? finalAttrs.finalPackage,
runInputs ? [ ],
run ? null,
runAllowNetworking ? false,
}:
let
sdk = finalAttrs.finalPackage;
built = stdenv.mkDerivation {
name = "dotnet-test-${name}";
buildInputs = [ sdk ] ++ buildInputs ++ lib.optional (usePackageSource) sdk.packages;
# make sure ICU works in a sandbox
propagatedSandboxProfile = toString sdk.__propagatedSandboxProfile;
unpackPhase = ''
mkdir test
cd test
dotnet new ${template} -o . --no-restore
'';
buildPhase = build;
dontPatchELF = true;
};
in
if run == null then
built
else
runCommand "${built.name}-run"
(
{
src = built;
nativeBuildInputs = [ built ] ++ runInputs;
passthru = {
inherit built;
};
}
// lib.optionalAttrs (stdenv.hostPlatform.isDarwin && runAllowNetworking) {
sandboxProfile = ''
(allow network-inbound (local ip))
(allow mach-lookup (global-name "com.apple.FSEvents"))
'';
__darwinAllowLocalNetworking = true;
}
)
(
lib.optionalString (runtime != null) ''
# TODO: use runtime here
export DOTNET_ROOT=${runtime.unwrapped}
''
+ run
);
# Setting LANG to something other than 'C' forces the runtime to search
# for ICU, which will be required in most user environments.
checkConsoleOutput = command: ''
output="$(LANG=C.UTF-8 ${command})"
# yes, older SDKs omit the comma
[[ "$output" =~ Hello,?\ World! ]] && touch "$out"
'';
in
unwrapped.passthru.tests or { }
// {
version = testers.testVersion (
{
package = finalAttrs.finalPackage;
}
// lib.optionalAttrs (type != "sdk") {
command = "dotnet --info";
}
);
}
// lib.optionalAttrs (type == "sdk") (
{
console = mkDotnetTest {
name = "console";
template = "console";
build = checkConsoleOutput "dotnet run";
};
publish = mkDotnetTest {
name = "publish";
template = "console";
build = "dotnet publish -o $out/bin";
run = checkConsoleOutput "$src/bin/test";
};
self-contained = mkDotnetTest {
name = "self-contained";
template = "console";
usePackageSource = true;
build = "dotnet publish --use-current-runtime --sc -o $out";
runtime = null;
run = checkConsoleOutput "$src/test";
};
single-file = mkDotnetTest {
name = "single-file";
template = "console";
usePackageSource = true;
build = "dotnet publish --use-current-runtime -p:PublishSingleFile=true -o $out/bin";
runtime = null;
run = checkConsoleOutput "$src/bin/test";
};
web = mkDotnetTest {
name = "web";
template = "web";
build = "dotnet publish -o $out/bin";
runInputs = [
expect
curl
];
run = ''
expect <<"EOF"
set status 1
spawn $env(src)/bin/test
proc abort { } { exit 2 }
expect_before default abort
expect -re {Now listening on: ([^\r]+)\r} {
set url $expect_out(1,string)
}
expect "Application started. Press Ctrl+C to shut down."
set output [exec curl -sSf $url]
if {$output != "Hello World!"} {
send_error "Unexpected output: $output\n"
exit 1
}
send \x03
expect_before timeout abort
expect eof
catch wait result
exit [lindex $result 3]
EOF
touch $out
'';
runAllowNetworking = true;
};
}
// lib.optionalAttrs finalAttrs.finalPackage.hasILCompiler {
aot = mkDotnetTest {
name = "aot";
stdenv = if stdenv.hostPlatform.isDarwin then swiftPackages.stdenv else stdenv;
template = "console";
usePackageSource = true;
buildInputs =
[
zlib
]
++ lib.optional stdenv.hostPlatform.isDarwin (
with darwin;
with apple_sdk.frameworks;
[
swiftPackages.swift
Foundation
CryptoKit
GSS
ICU
]
);
build = ''
dotnet restore -p:PublishAot=true
dotnet publish -p:PublishAot=true -o $out/bin
'';
runtime = null;
run = checkConsoleOutput "$src/bin/test";
};
}
);
};
})

View File

@ -21,7 +21,7 @@ let
removeReferencesTo
];
postFixup = (oldAttrs.postFixup or "") + ''
remove-references-to -t ${dotnet-runtime} "$out/bin/Application"
remove-references-to -t ${dotnet-runtime.unwrapped} "$out/bin/Application"
'';
});
@ -46,7 +46,7 @@ in
use-dotnet-root-env = testers.testEqualContents {
assertion = "buildDotnetModule uses DOTNET_ROOT from environment in wrapper";
expected = runtimeVersionFile;
actual = runCommand "use-dotnet-from-env-root-test" { env.DOTNET_ROOT = dotnet-runtime; } ''
actual = runCommand "use-dotnet-from-env-root-test" { env.DOTNET_ROOT = dotnet-runtime.unwrapped; } ''
${appWithoutFallback}/bin/Application >"$out"
'';
};