Merge pull request #313005 from tie/dotnet-cross

buildDotnetModule: fix structured attributes support
This commit is contained in:
David McFarland 2024-06-17 09:43:51 -03:00 committed by GitHub
commit b5447275d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 440 additions and 176 deletions

View File

@ -41,7 +41,7 @@ buildDotnetModule rec {
doCheck = true; doCheck = true;
preBuild = '' preBuild = ''
export projectFile=(ArchiSteamFarm) dotnetProjectFiles=(ArchiSteamFarm)
''; '';
preInstall = '' preInstall = ''

View File

@ -28,7 +28,7 @@ buildDotnetModule (args // {
] ++ (nugetDeps fetchNuGet); ] ++ (nugetDeps fetchNuGet);
}; };
projectFile = ""; dotnetGlobalTool = true;
useDotnetFromEnv = true; useDotnetFromEnv = true;

View File

@ -69,7 +69,7 @@
, disabledTests ? [ ] , disabledTests ? [ ]
# The project file to run unit tests against. This is usually referenced in the regular project file, but sometimes it needs to be manually set. # The project file to run unit tests against. This is usually referenced in the regular project file, but sometimes it needs to be manually set.
# It gets restored and build, but not installed. You may need to regenerate your nuget lockfile after setting this. # It gets restored and build, but not installed. You may need to regenerate your nuget lockfile after setting this.
, testProjectFile ? "" , testProjectFile ? null
# The type of build to perform. This is passed to `dotnet` with the `--configuration` flag. Possible values are `Release`, `Debug`, etc. # The type of build to perform. This is passed to `dotnet` with the `--configuration` flag. Possible values are `Release`, `Debug`, etc.
, buildType ? "Release" , buildType ? "Release"
@ -88,17 +88,18 @@
} @ args: } @ args:
let let
projectFiles =
lib.optionals (projectFile != null) (lib.toList projectFile);
testProjectFiles =
lib.optionals (testProjectFile != null) (lib.toList testProjectFile);
platforms = platforms =
if args ? meta.platforms if args ? meta.platforms
then lib.intersectLists args.meta.platforms dotnet-sdk.meta.platforms then lib.intersectLists args.meta.platforms dotnet-sdk.meta.platforms
else dotnet-sdk.meta.platforms; else dotnet-sdk.meta.platforms;
inherit (callPackage ./hooks { inherit (callPackage ./hooks {
inherit dotnet-sdk disabledTests nuget-source dotnet-runtime runtimeDeps buildType; inherit dotnet-sdk dotnet-runtime;
runtimeId =
if runtimeId != null
then runtimeId
else dotnetCorePackages.systemToDotnetRid stdenvNoCC.targetPlatform.system;
}) dotnetConfigureHook dotnetBuildHook dotnetCheckHook dotnetInstallHook dotnetFixupHook; }) dotnetConfigureHook dotnetBuildHook dotnetCheckHook dotnetInstallHook dotnetFixupHook;
localDeps = localDeps =
@ -143,6 +144,19 @@ let
nugetDepsFile = _nugetDeps.sourceFile; nugetDepsFile = _nugetDeps.sourceFile;
in in
stdenvNoCC.mkDerivation (args // { stdenvNoCC.mkDerivation (args // {
dotnetInstallPath = installPath;
dotnetExecutables = executables;
dotnetBuildType = buildType;
dotnetProjectFiles = projectFiles;
dotnetTestProjectFiles = testProjectFiles;
dotnetDisabledTests = disabledTests;
dotnetRuntimeId = runtimeId;
nugetSource = nuget-source;
dotnetRuntimeDeps = map lib.getLib runtimeDeps;
dotnetSelfContainedBuild = selfContainedBuild;
dotnetUseAppHost = useAppHost;
inherit useDotnetFromEnv;
nativeBuildInputs = args.nativeBuildInputs or [ ] ++ [ nativeBuildInputs = args.nativeBuildInputs or [ ] ++ [
dotnetConfigureHook dotnetConfigureHook
dotnetBuildHook dotnetBuildHook
@ -172,7 +186,7 @@ stdenvNoCC.mkDerivation (args // {
else [ ])); else [ ]));
makeWrapperArgs = args.makeWrapperArgs or [ ] ++ [ makeWrapperArgs = args.makeWrapperArgs or [ ] ++ [
"--prefix LD_LIBRARY_PATH : ${dotnet-sdk.icu}/lib" "--prefix" "LD_LIBRARY_PATH" ":" "${dotnet-sdk.icu}/lib"
]; ];
# Stripping breaks the executable # Stripping breaks the executable
@ -181,8 +195,6 @@ stdenvNoCC.mkDerivation (args // {
# gappsWrapperArgs gets included when wrapping for dotnet, as to avoid double wrapping # gappsWrapperArgs gets included when wrapping for dotnet, as to avoid double wrapping
dontWrapGApps = args.dontWrapGApps or true; dontWrapGApps = args.dontWrapGApps or true;
inherit selfContainedBuild useAppHost useDotnetFromEnv;
# propagate the runtime sandbox profile since the contents apply to published # propagate the runtime sandbox profile since the contents apply to published
# executables # executables
propagatedSandboxProfile = toString dotnet-runtime.__propagatedSandboxProfile; propagatedSandboxProfile = toString dotnet-runtime.__propagatedSandboxProfile;
@ -267,11 +279,11 @@ stdenvNoCC.mkDerivation (args // {
--no-cache \ --no-cache \
--force \ --force \
${lib.optionalString (!enableParallelBuilding) "--disable-parallel"} \ ${lib.optionalString (!enableParallelBuilding) "--disable-parallel"} \
${lib.optionalString (flags != []) (toString flags)} ${lib.escapeShellArgs flags}
} }
declare -a projectFiles=( ${toString (lib.toList projectFile)} ) declare -a projectFiles=( ${lib.escapeShellArgs projectFiles} )
declare -a testProjectFiles=( ${toString (lib.toList testProjectFile)} ) declare -a testProjectFiles=( ${lib.escapeShellArgs testProjectFiles} )
export DOTNET_NOLOGO=1 export DOTNET_NOLOGO=1
export DOTNET_CLI_TELEMETRY_OPTOUT=1 export DOTNET_CLI_TELEMETRY_OPTOUT=1

View File

@ -4,28 +4,21 @@
, coreutils , coreutils
, zlib , zlib
, openssl , openssl
, callPackage
, makeSetupHook , makeSetupHook
, makeWrapper , dotnetCorePackages
# Passed from ../default.nix
, dotnet-sdk , dotnet-sdk
, disabledTests
, nuget-source
, dotnet-runtime , dotnet-runtime
, runtimeDeps
, buildType
, runtimeId
}: }:
assert (builtins.isString runtimeId);
let let
libraryPath = lib.makeLibraryPath runtimeDeps; runtimeId = dotnetCorePackages.systemToDotnetRid stdenv.hostPlatform.system;
in in
{ {
dotnetConfigureHook = makeSetupHook dotnetConfigureHook = makeSetupHook
{ {
name = "dotnet-configure-hook"; name = "dotnet-configure-hook";
substitutions = { substitutions = {
nugetSource = nuget-source; runtimeId = lib.escapeShellArg runtimeId;
dynamicLinker = "${stdenv.cc}/nix-support/dynamic-linker"; dynamicLinker = "${stdenv.cc}/nix-support/dynamic-linker";
libPath = lib.makeLibraryPath [ libPath = lib.makeLibraryPath [
stdenv.cc.cc.lib stdenv.cc.cc.lib
@ -34,7 +27,6 @@ in
zlib zlib
openssl openssl
]; ];
inherit runtimeId;
}; };
} }
./dotnet-configure-hook.sh; ./dotnet-configure-hook.sh;
@ -43,7 +35,7 @@ in
{ {
name = "dotnet-build-hook"; name = "dotnet-build-hook";
substitutions = { substitutions = {
inherit buildType runtimeId; runtimeId = lib.escapeShellArg runtimeId;
}; };
} }
./dotnet-build-hook.sh; ./dotnet-build-hook.sh;
@ -52,15 +44,7 @@ in
{ {
name = "dotnet-check-hook"; name = "dotnet-check-hook";
substitutions = { substitutions = {
inherit buildType runtimeId libraryPath; runtimeId = lib.escapeShellArg runtimeId;
disabledTests = lib.optionalString (disabledTests != [ ])
(
let
escapedNames = lib.lists.map (n: lib.replaceStrings [ "," ] [ "%2C" ] n) disabledTests;
filters = lib.lists.map (n: "FullyQualifiedName!=${n}") escapedNames;
in
"${lib.concatStringsSep "&" filters}"
);
}; };
} }
./dotnet-check-hook.sh; ./dotnet-check-hook.sh;
@ -69,7 +53,7 @@ in
{ {
name = "dotnet-install-hook"; name = "dotnet-install-hook";
substitutions = { substitutions = {
inherit buildType runtimeId; runtimeId = lib.escapeShellArg runtimeId;
}; };
} }
./dotnet-install-hook.sh; ./dotnet-install-hook.sh;
@ -79,11 +63,7 @@ in
name = "dotnet-fixup-hook"; name = "dotnet-fixup-hook";
substitutions = { substitutions = {
dotnetRuntime = dotnet-runtime; dotnetRuntime = dotnet-runtime;
runtimeDeps = libraryPath; wrapperPath = lib.makeBinPath [ which coreutils ];
shell = stdenv.shell;
which = "${which}/bin/which";
dirname = "${coreutils}/bin/dirname";
realpath = "${coreutils}/bin/realpath";
}; };
} }
./dotnet-fixup-hook.sh; ./dotnet-fixup-hook.sh;

View File

@ -1,12 +1,25 @@
# inherit arguments from derivation
dotnetBuildFlags=( ${dotnetBuildFlags[@]-} )
dotnetBuildHook() { dotnetBuildHook() {
echo "Executing dotnetBuildHook" echo "Executing dotnetBuildHook"
runHook preBuild runHook preBuild
if [ "${enableParallelBuilding-}" ]; then local -r hostRuntimeId=@runtimeId@
local -r dotnetBuildType="${dotnetBuildType-Release}"
local -r dotnetRuntimeId="${dotnetRuntimeId-$hostRuntimeId}"
if [[ -n $__structuredAttrs ]]; then
local dotnetProjectFilesArray=( "${dotnetProjectFiles[@]}" )
local dotnetTestProjectFilesArray=( "${dotnetTestProjectFiles[@]}" )
local dotnetFlagsArray=( "${dotnetFlags[@]}" )
local dotnetBuildFlagsArray=( "${dotnetBuildFlags[@]}" )
else
local dotnetProjectFilesArray=($dotnetProjectFiles)
local dotnetTestProjectFilesArray=($dotnetTestProjectFiles)
local dotnetFlagsArray=($dotnetFlags)
local dotnetBuildFlagsArray=($dotnetBuildFlags)
fi
if [[ -n "${enableParallelBuilding-}" ]]; then
local -r maxCpuFlag="$NIX_BUILD_CORES" local -r maxCpuFlag="$NIX_BUILD_CORES"
local -r parallelBuildFlag="true" local -r parallelBuildFlag="true"
else else
@ -14,50 +27,53 @@ dotnetBuildHook() {
local -r parallelBuildFlag="false" local -r parallelBuildFlag="false"
fi fi
if [ "${selfContainedBuild-}" ]; then if [[ -n ${dotnetSelfContainedBuild-} ]]; then
dotnetBuildFlags+=("-p:SelfContained=true") dotnetBuildFlagsArray+=("-p:SelfContained=true")
else else
dotnetBuildFlags+=("-p:SelfContained=false") dotnetBuildFlagsArray+=("-p:SelfContained=false")
fi fi
if [ "${useAppHost-}" ]; then if [[ -n ${dotnetUseAppHost-} ]]; then
dotnetBuildFlags+=("-p:UseAppHost=true") dotnetBuildFlagsArray+=("-p:UseAppHost=true")
fi fi
local versionFlags=() local versionFlagsArray=()
if [ "${version-}" ]; then if [[ -n ${version-} ]]; then
versionFlags+=("-p:InformationalVersion=${version-}") versionFlagsArray+=("-p:InformationalVersion=$version")
fi fi
if [ "${versionForDotnet-}" ]; then if [[ -n ${versionForDotnet-} ]]; then
versionFlags+=("-p:Version=${versionForDotnet-}") versionFlagsArray+=("-p:Version=$versionForDotnet")
fi fi
dotnetBuild() { dotnetBuild() {
local -r project="${1-}" local -r projectFile="${1-}"
runtimeIdFlags=() local runtimeIdFlagsArray=()
if [[ "$project" == *.csproj ]] || [ "${selfContainedBuild-}" ]; then if [[ $projectFile == *.csproj || -n ${dotnetSelfContainedBuild-} ]]; then
runtimeIdFlags+=("--runtime @runtimeId@") runtimeIdFlagsArray+=("--runtime" "$dotnetRuntimeId")
fi fi
dotnet build ${project-} \ dotnet build ${1+"$projectFile"} \
-maxcpucount:$maxCpuFlag \ -maxcpucount:"$maxCpuFlag" \
-p:BuildInParallel=$parallelBuildFlag \ -p:BuildInParallel="$parallelBuildFlag" \
-p:ContinuousIntegrationBuild=true \ -p:ContinuousIntegrationBuild=true \
-p:Deterministic=true \ -p:Deterministic=true \
--configuration "@buildType@" \ --configuration "$dotnetBuildType" \
--no-restore \ --no-restore \
${versionFlags[@]} \ "${versionFlagsArray[@]}" \
${runtimeIdFlags[@]} \ "${runtimeIdFlagsArray[@]}" \
${dotnetBuildFlags[@]} \ "${dotnetBuildFlagsArray[@]}" \
${dotnetFlags[@]} "${dotnetFlagsArray[@]}"
} }
(( "${#projectFile[@]}" == 0 )) && dotnetBuild if (( ${#dotnetProjectFilesArray[@]} == 0 )); then
dotnetBuild
fi
for project in ${projectFile[@]} ${testProjectFile[@]-}; do local projectFile
dotnetBuild "$project" for projectFile in "${dotnetProjectFilesArray[@]}" "${dotnetTestProjectFilesArray[@]}"; do
dotnetBuild "$projectFile"
done done
runHook postBuild runHook postBuild
@ -65,6 +81,6 @@ dotnetBuildHook() {
echo "Finished dotnetBuildHook" echo "Finished dotnetBuildHook"
} }
if [[ -z "${dontDotnetBuild-}" && -z "${buildPhase-}" ]]; then if [[ -z ${dontDotnetBuild-} && -z ${buildPhase-} ]]; then
buildPhase=dotnetBuildHook buildPhase=dotnetBuildHook
fi fi

View File

@ -1,39 +1,65 @@
# inherit arguments from derivation
dotnetTestFlags=( ${dotnetTestFlags[@]-} )
dotnetCheckHook() { dotnetCheckHook() {
echo "Executing dotnetCheckHook" echo "Executing dotnetCheckHook"
runHook preCheck runHook preCheck
if [ "${disabledTests-}" ]; then local -r hostRuntimeId=@runtimeId@
local -r disabledTestsFlag="--filter @disabledTests@" local -r dotnetBuildType="${dotnetBuildType-Release}"
local -r dotnetRuntimeId="${dotnetRuntimeId-$hostRuntimeId}"
if [[ -n $__structuredAttrs ]]; then
local dotnetProjectFilesArray=( "${dotnetProjectFiles[@]}" )
local dotnetTestProjectFilesArray=( "${dotnetTestProjectFiles[@]}" )
local dotnetTestFlagsArray=( "${dotnetTestFlags[@]}" )
local dotnetDisabledTestsArray=( "${dotnetDisabledTests[@]}" )
local dotnetRuntimeDepsArray=( "${dotnetRuntimeDeps[@]}" )
else
local dotnetProjectFilesArray=($dotnetProjectFiles)
local dotnetTestProjectFilesArray=($dotnetTestProjectFiles)
local dotnetTestFlagsArray=($dotnetTestFlags)
local dotnetDisabledTestsArray=($dotnetDisabledTests)
local dotnetRuntimeDepsArray=($dotnetRuntimeDeps)
fi fi
if [ "${enableParallelBuilding-}" ]; then if (( ${#dotnetDisabledTestsArray[@]} > 0 )); then
local disabledTestsFilters=("${dotnetDisabledTestsArray[@]/#/FullyQualifiedName!=}")
local OLDIFS="$IFS" IFS='&'
dotnetTestFlagsArray+=("--filter:${disabledTestsFilters[*]//,/%2C}")
IFS="$OLDIFS"
fi
local libraryPath="${LD_LIBRARY_PATH-}"
if (( ${#dotnetRuntimeDepsArray[@]} > 0 )); then
local libraryPathArray=("${dotnetRuntimeDepsArray[@]/%//lib}")
local OLDIFS="$IFS" IFS=':'
libraryPath="${libraryPathArray[*]}${libraryPath:+':'}$libraryPath"
IFS="$OLDIFS"
fi
if [[ -n ${enableParallelBuilding-} ]]; then
local -r maxCpuFlag="$NIX_BUILD_CORES" local -r maxCpuFlag="$NIX_BUILD_CORES"
else else
local -r maxCpuFlag="1" local -r maxCpuFlag="1"
fi fi
for project in ${testProjectFile[@]-${projectFile[@]}}; do local projectFile
runtimeIdFlags=() for projectFile in "${dotnetTestProjectFilesArray[@]-${dotnetProjectFilesArray[@]}}"; do
if [[ "$project" == *.csproj ]]; then local runtimeIdFlagsArray=()
runtimeIdFlags=("--runtime @runtimeId@") if [[ $projectFile == *.csproj ]]; then
runtimeIdFlagsArray=("--runtime" "$dotnetRuntimeId")
fi fi
LD_LIBRARY_PATH="@libraryPath@" \ LD_LIBRARY_PATH=$libraryPath \
dotnet test "$project" \ dotnet test "$projectFile" \
-maxcpucount:$maxCpuFlag \ -maxcpucount:"$maxCpuFlag" \
-p:ContinuousIntegrationBuild=true \ -p:ContinuousIntegrationBuild=true \
-p:Deterministic=true \ -p:Deterministic=true \
--configuration "@buildType@" \ --configuration "$dotnetBuildType" \
--no-build \ --no-build \
--logger "console;verbosity=normal" \ --logger "console;verbosity=normal" \
${disabledTestsFlag-} \ "${runtimeIdFlagsArray[@]}" \
${runtimeIdFlags[@]} \ "${dotnetTestFlagsArray[@]}" \
"${dotnetTestFlags[@]}" \ "${dotnetFlagsArray[@]}"
"${dotnetFlags[@]}"
done done
runHook postCheck runHook postCheck

View File

@ -1,63 +1,103 @@
declare -a projectFile testProjectFile
# Inherit arguments from derivation
dotnetFlags=( ${dotnetFlags[@]-} )
dotnetRestoreFlags=( ${dotnetRestoreFlags[@]-} )
dotnetConfigureHook() { dotnetConfigureHook() {
echo "Executing dotnetConfigureHook" echo "Executing dotnetConfigureHook"
runHook preConfigure runHook preConfigure
if [ -z "${enableParallelBuilding-}" ]; then if [[ -z ${nugetSource-} ]]; then
echo
echo "ERROR: no dependencies were specified"
echo 'Hint: set `nugetSource` if using these hooks individually. If this is happening with `buildDotnetModule`, please open an issue.'
echo
exit 1
fi
local nugetSourceSedQuoted="${nugetSource//[\/\\&$'\n']/\\&}"
local nugetSourceXMLQuoted="$nugetSource"
nugetSourceXMLQuoted="${nugetSource//&/\&}"
nugetSourceXMLQuoted="${nugetSourceXMLQuoted//\"/\"}"
local -r hostRuntimeId=@runtimeId@
local -r dynamicLinker=@dynamicLinker@
local -r libPath=@libPath@
local -r dotnetRuntimeId="${dotnetRuntimeId-$hostRuntimeId}"
if [[ -n $__structuredAttrs ]]; then
local dotnetProjectFilesArray=( "${dotnetProjectFiles[@]}" )
local dotnetTestProjectFilesArray=( "${dotnetTestProjectFiles[@]}" )
local dotnetFlagsArray=( "${dotnetFlags[@]}" )
local dotnetRestoreFlagsArray=( "${dotnetRestoreFlags[@]}" )
else
local dotnetProjectFilesArray=($dotnetProjectFiles)
local dotnetTestProjectFilesArray=($dotnetTestProjectFiles)
local dotnetFlagsArray=($dotnetFlags)
local dotnetRestoreFlagsArray=($dotnetRestoreFlags)
fi
if [[ -z ${enableParallelBuilding-} ]]; then
local -r parallelFlag="--disable-parallel" local -r parallelFlag="--disable-parallel"
fi fi
dotnetRestore() { dotnetRestore() {
local -r project="${1-}" local -r projectFile="${1-}"
dotnet restore ${project-} \ dotnet restore ${1+"$projectFile"} \
-p:ContinuousIntegrationBuild=true \ -p:ContinuousIntegrationBuild=true \
-p:Deterministic=true \ -p:Deterministic=true \
--runtime "@runtimeId@" \ --runtime "$dotnetRuntimeId" \
--source "@nugetSource@/lib" \ --source "$nugetSource/lib" \
${parallelFlag-} \ ${parallelFlag-} \
${dotnetRestoreFlags[@]} \ "${dotnetRestoreFlagsArray[@]}" \
${dotnetFlags[@]} "${dotnetFlagsArray[@]}"
} }
# Generate a NuGet.config file to make sure everything, # Generate a NuGet.config file to make sure everything,
# including things like <Sdk /> dependencies, is restored from the proper source # including things like <Sdk /> dependencies, is restored from the proper source
cat <<EOF > "./NuGet.config" cat >NuGet.config <<EOF
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<packageSources> <packageSources>
<clear /> <clear />
<add key="nugetSource" value="@nugetSource@/lib" /> <add key="nugetSource" value="$nugetSourceXMLQuoted/lib" />
</packageSources> </packageSources>
</configuration> </configuration>
EOF EOF
# Patch paket.dependencies and paket.lock (if found) to use the proper source. This ensures # Patch paket.dependencies and paket.lock (if found) to use the proper
# paket restore works correctly # source. This ensures paket restore works correctly. Note that the
# We use + instead of / in sed to avoid problems with slashes # nugetSourceSedQuoted abomination below safely escapes nugetSource string
find -name paket.dependencies -exec sed -i 's+source .*+source @nugetSource@/lib+g' {} \; # for use as a sed replacement string to avoid issues with slashes and other
find -name paket.lock -exec sed -i 's+remote:.*+remote: @nugetSource@/lib+g' {} \; # special characters ('&', '\\' and '\n').
find -name paket.dependencies -exec sed -i "s/source .*/source $nugetSourceSedQuoted\/lib/g" {} \;
find -name paket.lock -exec sed -i "s/remote:.*/remote: $nugetSourceSedQuoted\/lib/g" {} \;
dotnet tool restore --add-source "@nugetSource@/lib" dotnet tool restore --add-source "$nugetSource/lib"
(( "${#projectFile[@]}" == 0 )) && dotnetRestore # dotnetGlobalTool is set in buildDotnetGlobalTool to patch dependencies but
# avoid other project-specific logic. This is a hack, but the old behavior
# is worse as it relied on a bug: setting projectFile to an empty string
# made the hooks actually skip all project-specific logic. Its hard to keep
# backwards compatibility with this odd behavior now since we are using
# arrays, so instead we just pass a variable to indicate that we dont have
# projects.
if [[ -z ${dotnetGlobalTool-} ]]; then
if (( ${#dotnetProjectFilesArray[@]} == 0 )); then
dotnetRestore
fi
for project in ${projectFile[@]} ${testProjectFile[@]-}; do local projectFile
dotnetRestore "$project" for projectFile in "${dotnetProjectFilesArray[@]}" "${dotnetTestProjectFilesArray[@]}"; do
done dotnetRestore "$projectFile"
done
fi
echo "Fixing up native binaries..." echo "Fixing up native binaries..."
# Find all native binaries and nuget libraries, and fix them up, # Find all native binaries and nuget libraries, and fix them up,
# by setting the proper interpreter and rpath to some commonly used libraries # by setting the proper interpreter and rpath to some commonly used libraries
local binary
for binary in $(find "$HOME/.nuget/packages/" -type f -executable); do for binary in $(find "$HOME/.nuget/packages/" -type f -executable); do
if patchelf --print-interpreter "$binary" >/dev/null 2>/dev/null; then if patchelf --print-interpreter "$binary" >/dev/null 2>/dev/null; then
echo "Found binary: $binary, fixing it up..." echo "Found binary: $binary, fixing it up..."
patchelf --set-interpreter "$(cat "@dynamicLinker@")" "$binary" patchelf --set-interpreter "$(cat "$dynamicLinker")" "$binary"
# This makes sure that if the binary requires some specific runtime dependencies, it can find it. # This makes sure that if the binary requires some specific runtime dependencies, it can find it.
# This fixes dotnet-built binaries like crossgen2 # This fixes dotnet-built binaries like crossgen2
@ -68,7 +108,7 @@ EOF
--add-needed libssl.so \ --add-needed libssl.so \
"$binary" "$binary"
patchelf --set-rpath "@libPath@" "$binary" patchelf --set-rpath "$libPath" "$binary"
fi fi
done done

View File

@ -1,28 +1,55 @@
# Inherit arguments from the derivation
declare -a derivationMakeWrapperArgs="( ${makeWrapperArgs-} )"
makeWrapperArgs=( "${derivationMakeWrapperArgs[@]}" )
# First argument is the executable you want to wrap, # First argument is the executable you want to wrap,
# the second is the destination for the wrapper. # the second is the destination for the wrapper.
wrapDotnetProgram() { wrapDotnetProgram() {
local dotnetRootFlags=() local -r dotnetRuntime=@dotnetRuntime@
local -r wrapperPath=@wrapperPath@
if [ ! "${selfContainedBuild-}" ]; then local -r dotnetFromEnvScript='dotnetFromEnv() {
if [ "${useDotnetFromEnv-}" ]; then local dotnetPath
if command -v dotnet 2>&1 >/dev/null; then
dotnetPath=$(which dotnet) && \
dotnetPath=$(realpath "$dotnetPath") && \
dotnetPath=$(dirname "$dotnetPath") && \
export DOTNET_ROOT="$dotnetPath"
fi
}
dotnetFromEnv'
if [[ -n $__structuredAttrs ]]; then
local -r dotnetRuntimeDepsArray=( "${dotnetRuntimeDeps[@]}" )
local -r makeWrapperArgsArray=( "${makeWrapperArgs[@]}" )
else
local -r dotnetRuntimeDepsArray=($dotnetRuntimeDeps)
local -r makeWrapperArgsArray=($makeWrapperArgs)
fi
local dotnetRuntimeDepsFlags=()
if (( ${#dotnetRuntimeDepsArray[@]} > 0 )); then
local libraryPathArray=("${dotnetRuntimeDepsArray[@]/%//lib}")
local OLDIFS="$IFS" IFS=':'
dotnetRuntimeDepsFlags+=("--suffix" "LD_LIBRARY_PATH" ":" "${libraryPathArray[*]}")
IFS="$OLDIFS"
fi
local dotnetRootFlagsArray=()
if [[ -z ${dotnetSelfContainedBuild-} ]]; then
if [[ -n ${useDotnetFromEnv-} ]]; then
# if dotnet CLI is available, set DOTNET_ROOT based on it. Otherwise set to default .NET runtime # if dotnet CLI is available, set DOTNET_ROOT based on it. Otherwise set to default .NET runtime
dotnetRootFlags+=("--run" 'command -v dotnet &>/dev/null && export DOTNET_ROOT="$(@dirname@ "$(@realpath@ "$(@which@ dotnet)")")" || export DOTNET_ROOT="@dotnetRuntime@"') dotnetRootFlagsArray+=("--suffix" "PATH" ":" "$wrapperPath")
dotnetRootFlags+=("--suffix" "PATH" ":" "@dotnetRuntime@/bin") dotnetRootFlagsArray+=("--run" "$dotnetFromEnvScript")
dotnetRootFlagsArray+=("--set-default" "DOTNET_ROOT" "$dotnetRuntime")
dotnetRootFlagsArray+=("--suffix" "PATH" ":" "$dotnetRuntime/bin")
else else
dotnetRootFlags+=("--set" "DOTNET_ROOT" "@dotnetRuntime@") dotnetRootFlagsArray+=("--set" "DOTNET_ROOT" "$dotnetRuntime")
dotnetRootFlags+=("--prefix" "PATH" ":" "@dotnetRuntime@/bin") dotnetRootFlagsArray+=("--prefix" "PATH" ":" "$dotnetRuntime/bin")
fi fi
fi fi
makeWrapper "$1" "$2" \ makeWrapper "$1" "$2" \
--suffix "LD_LIBRARY_PATH" : "@runtimeDeps@" \ "${dotnetRuntimeDepsFlags[@]}" \
"${dotnetRootFlags[@]}" \ "${dotnetRootFlagsArray[@]}" \
"${gappsWrapperArgs[@]}" \ "${gappsWrapperArgs[@]}" \
"${makeWrapperArgs[@]}" "${makeWrapperArgsArray[@]}"
echo "installed wrapper to "$2"" echo "installed wrapper to "$2""
} }
@ -30,13 +57,24 @@ wrapDotnetProgram() {
dotnetFixupHook() { dotnetFixupHook() {
echo "Executing dotnetFixupPhase" echo "Executing dotnetFixupPhase"
# check if executables is declared (including empty values, in which case we generate no executables) local -r dotnetInstallPath="${dotnetInstallPath-$out/lib/$pname}"
if declare -p executables &>/dev/null; then
for executable in ${executables[@]}; do local executable executableBasename
path="${installPath-$out/lib/$pname}/$executable"
# check if dotnetExecutables is declared (including empty values, in which case we generate no executables)
if declare -p dotnetExecutables &>/dev/null; then
if [[ -n $__structuredAttrs ]]; then
local dotnetExecutablesArray=( "${dotnetExecutables[@]}" )
else
local dotnetExecutablesArray=($dotnetExecutables)
fi
for executable in "${dotnetExecutablesArray[@]}"; do
executableBasename=$(basename "$executable")
local path="$dotnetInstallPath/$executable"
if test -x "$path"; then if test -x "$path"; then
wrapDotnetProgram "$path" "$out/bin/$(basename "$executable")" wrapDotnetProgram "$path" "$out/bin/$executableBasename"
else else
echo "Specified binary \"$executable\" is either not an executable or does not exist!" echo "Specified binary \"$executable\" is either not an executable or does not exist!"
echo "Looked in $path" echo "Looked in $path"
@ -45,8 +83,9 @@ dotnetFixupHook() {
done done
else else
while IFS= read -d '' executable; do while IFS= read -d '' executable; do
wrapDotnetProgram "$executable" "$out/bin/$(basename "$executable")" \; executableBasename=$(basename "$executable")
done < <(find "${installPath-$out/lib/$pname}" ! -name "*.dll" -executable -type f -print0) wrapDotnetProgram "$executable" "$out/bin/$executableBasename" \;
done < <(find "$dotnetInstallPath" ! -name "*.dll" -executable -type f -print0)
fi fi
echo "Finished dotnetFixupPhase" echo "Finished dotnetFixupPhase"

View File

@ -1,70 +1,86 @@
# inherit arguments from derivation
dotnetInstallFlags=( ${dotnetInstallFlags[@]-} )
dotnetInstallHook() { dotnetInstallHook() {
echo "Executing dotnetInstallHook" echo "Executing dotnetInstallHook"
runHook preInstall runHook preInstall
if [ "${selfContainedBuild-}" ]; then local -r hostRuntimeId=@runtimeId@
dotnetInstallFlags+=("--self-contained") local -r dotnetInstallPath="${dotnetInstallPath-$out/lib/$pname}"
local -r dotnetBuildType="${dotnetBuildType-Release}"
local -r dotnetRuntimeId="${dotnetRuntimeId-$hostRuntimeId}"
if [[ -n $__structuredAttrs ]]; then
local dotnetProjectFilesArray=( "${dotnetProjectFiles[@]}" )
local dotnetFlagsArray=( "${dotnetFlags[@]}" )
local dotnetInstallFlagsArray=( "${dotnetInstallFlags[@]}" )
local dotnetPackFlagsArray=( "${dotnetPackFlags[@]}" )
else else
dotnetInstallFlags+=("--no-self-contained") local dotnetProjectFilesArray=($dotnetProjectFiles)
# https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-self-contained local dotnetFlagsArray=($dotnetFlags)
# Trimming is only available for self-contained build, so force disable it here local dotnetInstallFlagsArray=($dotnetInstallFlags)
dotnetInstallFlags+=("-p:PublishTrimmed=false") local dotnetPackFlagsArray=($dotnetPackFlags)
fi fi
if [ "${useAppHost-}" ]; then if [[ -n ${dotnetSelfContainedBuild-} ]]; then
dotnetInstallFlags+=("-p:UseAppHost=true") dotnetInstallFlagsArray+=("--self-contained")
else
dotnetInstallFlagsArray+=("--no-self-contained")
# https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-self-contained
# Trimming is only available for self-contained build, so force disable it here
dotnetInstallFlagsArray+=("-p:PublishTrimmed=false")
fi
if [[ -n ${dotnetUseAppHost-} ]]; then
dotnetInstallFlagsArray+=("-p:UseAppHost=true")
fi fi
dotnetPublish() { dotnetPublish() {
local -r project="${1-}" local -r projectFile="${1-}"
runtimeIdFlags=() runtimeIdFlagsArray=()
if [[ "$project" == *.csproj ]] || [ "${selfContainedBuild-}" ]; then if [[ $projectFile == *.csproj || -n ${dotnetSelfContainedBuild-} ]]; then
runtimeIdFlags+=("--runtime @runtimeId@") runtimeIdFlagsArray+=("--runtime" "$dotnetRuntimeId")
fi fi
dotnet publish ${project-} \ dotnet publish ${1+"$projectFile"} \
-p:ContinuousIntegrationBuild=true \ -p:ContinuousIntegrationBuild=true \
-p:Deterministic=true \ -p:Deterministic=true \
--output "${installPath-$out/lib/$pname}" \ --output "$dotnetInstallPath" \
--configuration "@buildType@" \ --configuration "$dotnetBuildType" \
--no-build \ --no-build \
${runtimeIdFlags[@]} \ "${runtimeIdFlagsArray[@]}" \
${dotnetInstallFlags[@]} \ "${dotnetInstallFlagsArray[@]}" \
${dotnetFlags[@]} "${dotnetFlagsArray[@]}"
} }
dotnetPack() { dotnetPack() {
local -r project="${1-}" local -r projectFile="${1-}"
dotnet pack ${project-} \ dotnet pack ${1+"$projectFile"} \
-p:ContinuousIntegrationBuild=true \ -p:ContinuousIntegrationBuild=true \
-p:Deterministic=true \ -p:Deterministic=true \
--output "$out/share" \ --output "$out/share" \
--configuration "@buildType@" \ --configuration "$dotnetBuildType" \
--no-build \ --no-build \
--runtime "@runtimeId@" \ --runtime "$dotnetRuntimeId" \
${dotnetPackFlags[@]} \ "${dotnetPackFlagsArray[@]}" \
${dotnetFlags[@]} "${dotnetFlagsArray[@]}"
} }
if (( "${#projectFile[@]}" == 0 )); then if (( ${#dotnetProjectFilesArray[@]} == 0 )); then
dotnetPublish dotnetPublish
else else
for project in ${projectFile[@]}; do local projectFile
dotnetPublish "$project" for projectFile in "${dotnetProjectFilesArray[@]}"; do
dotnetPublish "$projectFile"
done done
fi fi
if [[ "${packNupkg-}" ]]; then if [[ -n ${packNupkg-} ]]; then
if (( "${#projectFile[@]}" == 0 )); then if (( ${#dotnetProjectFilesArray[@]} == 0 )); then
dotnetPack dotnetPack
else else
for project in ${projectFile[@]}; do local projectFile
dotnetPack "$project" for projectFile in "${dotnetProjectFilesArray[@]}"; do
dotnetPack "$projectFile"
done done
fi fi
fi fi

View File

@ -1,5 +1,7 @@
{ callPackage }: { lib, callPackage }:
{ {
project-references = callPackage ./project-references { }; project-references = callPackage ./project-references { };
use-dotnet-from-env = lib.recurseIntoAttrs (callPackage ./use-dotnet-from-env { });
structured-attrs = lib.recurseIntoAttrs (callPackage ./structured-attrs { });
} }

View File

@ -4,11 +4,13 @@
{ lib { lib
, dotnet-sdk , dotnet-sdk
, buildDotnetModule , buildPackages # buildDotnetModule
, runCommand , runCommand
}: }:
let let
inherit (buildPackages) buildDotnetModule;
nugetDeps = ./nuget-deps.nix; nugetDeps = ./nuget-deps.nix;
# Specify the TargetFramework via an environment variable so that we don't # Specify the TargetFramework via an environment variable so that we don't
@ -18,7 +20,8 @@ let
library = buildDotnetModule { library = buildDotnetModule {
name = "project-references-test-library"; name = "project-references-test-library";
src = ./library; src = ./library;
inherit nugetDeps TargetFramework; inherit nugetDeps;
env.TargetFramework = TargetFramework;
packNupkg = true; packNupkg = true;
}; };
@ -26,7 +29,8 @@ let
application = buildDotnetModule { application = buildDotnetModule {
name = "project-references-test-application"; name = "project-references-test-application";
src = ./application; src = ./application;
inherit nugetDeps TargetFramework; inherit nugetDeps;
env.TargetFramework = TargetFramework;
projectReferences = [ library ]; projectReferences = [ library ];
}; };

View File

@ -0,0 +1,36 @@
{ lib
, dotnet-sdk
, buildPackages # buildDotnetModule
, testers
, runCommand
}:
let
# Note: without structured attributes, we cant use derivation arguments that
# contain spaces unambiguously because arguments are passed as space-separated
# environment variables.
copyrightString = "Public domain 🅮";
inherit (buildPackages) buildDotnetModule;
app = buildDotnetModule {
name = "structured-attrs-test-application";
src = ./src;
nugetDeps = ./nuget-deps.nix;
dotnetFlags = [ "--property:Copyright=${copyrightString}" ];
env.TargetFramework = "net${lib.versions.majorMinor (lib.getVersion dotnet-sdk)}";
__structuredAttrs = true;
};
in
{
no-structured-attrs = testers.testBuildFailure (app.overrideAttrs {
__structuredAttrs = false;
});
check-output = testers.testEqualContents {
assertion = "buildDotnetModule sets AssemblyCopyrightAttribute with structured attributes";
expected = builtins.toFile "expected-copyright.txt" copyrightString;
actual = runCommand "dotnet-structured-attrs-test" { } ''
${app}/bin/Application >"$out"
'';
};
}

View File

@ -0,0 +1,5 @@
# This file was automatically generated by passthru.fetch-deps.
# Please dont edit it manually, your changes might get overwritten!
{ fetchNuGet }: [
]

View File

@ -0,0 +1,10 @@
using System;
using System.Reflection;
Console.Write(
(
(AssemblyCopyrightAttribute)Assembly
.GetExecutingAssembly()
.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), true)[0]
).Copyright
);

View File

@ -0,0 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>exe</OutputType>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,60 @@
{ lib
, dotnet-sdk
, buildPackages # buildDotnetModule, dotnet-runtime
, testers
, runCommand
, removeReferencesTo
}:
let
inherit (buildPackages) buildDotnetModule dotnet-runtime;
app = buildDotnetModule {
name = "use-dotnet-from-env-test-application";
src = ./src;
nugetDeps = ./nuget-deps.nix;
useDotnetFromEnv = true;
env.TargetFramework = "net${lib.versions.majorMinor (lib.getVersion dotnet-sdk)}";
};
appWithoutFallback = app.overrideAttrs (oldAttrs: {
nativeBuildInputs = (oldAttrs.nativeBuildInputs or [ ]) ++ [
removeReferencesTo
];
postFixup = (oldAttrs.postFixup or "") + ''
remove-references-to -t ${dotnet-runtime} "$out/bin/Application"
'';
});
runtimeVersion = lib.getVersion dotnet-runtime;
runtimeVersionFile = builtins.toFile "dotnet-version.txt" runtimeVersion;
in
{
fallback = testers.testEqualContents {
assertion = "buildDotnetModule sets fallback DOTNET_ROOT in wrapper";
expected = runtimeVersionFile;
actual = runCommand "use-dotnet-from-env-fallback-test" { } ''
${app}/bin/Application >"$out"
'';
};
# Check that appWithoutFallback does not use fallback .NET runtime.
without-fallback = testers.testBuildFailure (runCommand "use-dotnet-from-env-without-fallback-test" { } ''
${appWithoutFallback}/bin/Application >"$out"
'');
# NB assumes that without-fallback above to passes.
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; } ''
${appWithoutFallback}/bin/Application >"$out"
'';
};
use-dotnet-path-env = testers.testEqualContents {
assertion = "buildDotnetModule uses DOTNET_ROOT from dotnet in PATH in wrapper";
expected = runtimeVersionFile;
actual = runCommand "use-dotnet-from-env-path-test" { dotnetRuntime = dotnet-runtime; } ''
PATH=$dotnetRuntime''${PATH+:}$PATH ${appWithoutFallback}/bin/Application >"$out"
'';
};
}

View File

@ -0,0 +1,5 @@
# This file was automatically generated by passthru.fetch-deps.
# Please dont edit it manually, your changes might get overwritten!
{ fetchNuGet }: [
]

View File

@ -0,0 +1,3 @@
using System;
Console.Write(Environment.Version.ToString());

View File

@ -0,0 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>exe</OutputType>
</PropertyGroup>
</Project>