diff options
author | Ivan Trubach <mr.trubach@icloud.com> | 2024-05-16 02:10:39 +0300 |
---|---|---|
committer | Ivan Trubach <mr.trubach@icloud.com> | 2024-05-29 08:45:25 +0300 |
commit | f7d8046fb83cf0bb4e0a8ad179dee89bcbe0335d (patch) | |
tree | 9b69ac9e898c2c948da20b47fbeab9fdbea5cfa8 | |
parent | 22a5fc0518c6b323d98b052ae6a14fd010808d1f (diff) |
buildDotnetModule: fix structured attributes support
This change refactors internal hooks used by buildDotnetModule to support derivations with structured attributes. Note that this changes variable names that the internal hooks expect.
19 files changed, 440 insertions, 176 deletions
diff --git a/pkgs/applications/misc/ArchiSteamFarm/default.nix b/pkgs/applications/misc/ArchiSteamFarm/default.nix index 7c334fb804558..fc3335ca3a927 100644 --- a/pkgs/applications/misc/ArchiSteamFarm/default.nix +++ b/pkgs/applications/misc/ArchiSteamFarm/default.nix @@ -41,7 +41,7 @@ buildDotnetModule rec { doCheck = true; preBuild = '' - export projectFile=(ArchiSteamFarm) + dotnetProjectFiles=(ArchiSteamFarm) ''; preInstall = '' diff --git a/pkgs/build-support/dotnet/build-dotnet-global-tool/default.nix b/pkgs/build-support/dotnet/build-dotnet-global-tool/default.nix index 16cf029ca3451..7ae9cfc9f6618 100644 --- a/pkgs/build-support/dotnet/build-dotnet-global-tool/default.nix +++ b/pkgs/build-support/dotnet/build-dotnet-global-tool/default.nix @@ -28,7 +28,7 @@ buildDotnetModule (args // { ] ++ (nugetDeps fetchNuGet); }; - projectFile = ""; + dotnetGlobalTool = true; useDotnetFromEnv = true; diff --git a/pkgs/build-support/dotnet/build-dotnet-module/default.nix b/pkgs/build-support/dotnet/build-dotnet-module/default.nix index e2ddee48cc006..6a4f9129cd44d 100644 --- a/pkgs/build-support/dotnet/build-dotnet-module/default.nix +++ b/pkgs/build-support/dotnet/build-dotnet-module/default.nix @@ -69,7 +69,7 @@ , 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. # 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. , buildType ? "Release" @@ -88,17 +88,18 @@ } @ args: let + projectFiles = + lib.optionals (projectFile != null) (lib.toList projectFile); + testProjectFiles = + lib.optionals (testProjectFile != null) (lib.toList testProjectFile); + platforms = if args ? meta.platforms then lib.intersectLists args.meta.platforms dotnet-sdk.meta.platforms else dotnet-sdk.meta.platforms; inherit (callPackage ./hooks { - inherit dotnet-sdk disabledTests nuget-source dotnet-runtime runtimeDeps buildType; - runtimeId = - if runtimeId != null - then runtimeId - else dotnetCorePackages.systemToDotnetRid stdenvNoCC.targetPlatform.system; + inherit dotnet-sdk dotnet-runtime; }) dotnetConfigureHook dotnetBuildHook dotnetCheckHook dotnetInstallHook dotnetFixupHook; localDeps = @@ -143,6 +144,19 @@ let nugetDepsFile = _nugetDeps.sourceFile; in 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 [ ] ++ [ dotnetConfigureHook dotnetBuildHook @@ -172,7 +186,7 @@ stdenvNoCC.mkDerivation (args // { else [ ])); makeWrapperArgs = args.makeWrapperArgs or [ ] ++ [ - "--prefix LD_LIBRARY_PATH : ${dotnet-sdk.icu}/lib" + "--prefix" "LD_LIBRARY_PATH" ":" "${dotnet-sdk.icu}/lib" ]; # Stripping breaks the executable @@ -181,8 +195,6 @@ stdenvNoCC.mkDerivation (args // { # gappsWrapperArgs gets included when wrapping for dotnet, as to avoid double wrapping dontWrapGApps = args.dontWrapGApps or true; - inherit selfContainedBuild useAppHost useDotnetFromEnv; - # propagate the runtime sandbox profile since the contents apply to published # executables propagatedSandboxProfile = toString dotnet-runtime.__propagatedSandboxProfile; @@ -267,11 +279,11 @@ stdenvNoCC.mkDerivation (args // { --no-cache \ --force \ ${lib.optionalString (!enableParallelBuilding) "--disable-parallel"} \ - ${lib.optionalString (flags != []) (toString flags)} + ${lib.escapeShellArgs flags} } - declare -a projectFiles=( ${toString (lib.toList projectFile)} ) - declare -a testProjectFiles=( ${toString (lib.toList testProjectFile)} ) + declare -a projectFiles=( ${lib.escapeShellArgs projectFiles} ) + declare -a testProjectFiles=( ${lib.escapeShellArgs testProjectFiles} ) export DOTNET_NOLOGO=1 export DOTNET_CLI_TELEMETRY_OPTOUT=1 diff --git a/pkgs/build-support/dotnet/build-dotnet-module/hooks/default.nix b/pkgs/build-support/dotnet/build-dotnet-module/hooks/default.nix index 44091604f5c2c..b9c51a743c6a6 100644 --- a/pkgs/build-support/dotnet/build-dotnet-module/hooks/default.nix +++ b/pkgs/build-support/dotnet/build-dotnet-module/hooks/default.nix @@ -4,28 +4,21 @@ , coreutils , zlib , openssl -, callPackage , makeSetupHook -, makeWrapper +, dotnetCorePackages + # Passed from ../default.nix , dotnet-sdk -, disabledTests -, nuget-source , dotnet-runtime -, runtimeDeps -, buildType -, runtimeId }: -assert (builtins.isString runtimeId); - let - libraryPath = lib.makeLibraryPath runtimeDeps; + runtimeId = dotnetCorePackages.systemToDotnetRid stdenv.hostPlatform.system; in { dotnetConfigureHook = makeSetupHook { name = "dotnet-configure-hook"; substitutions = { - nugetSource = nuget-source; + runtimeId = lib.escapeShellArg runtimeId; dynamicLinker = "${stdenv.cc}/nix-support/dynamic-linker"; libPath = lib.makeLibraryPath [ stdenv.cc.cc.lib @@ -34,7 +27,6 @@ in zlib openssl ]; - inherit runtimeId; }; } ./dotnet-configure-hook.sh; @@ -43,7 +35,7 @@ in { name = "dotnet-build-hook"; substitutions = { - inherit buildType runtimeId; + runtimeId = lib.escapeShellArg runtimeId; }; } ./dotnet-build-hook.sh; @@ -52,15 +44,7 @@ in { name = "dotnet-check-hook"; substitutions = { - inherit buildType runtimeId libraryPath; - 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}" - ); + runtimeId = lib.escapeShellArg runtimeId; }; } ./dotnet-check-hook.sh; @@ -69,7 +53,7 @@ in { name = "dotnet-install-hook"; substitutions = { - inherit buildType runtimeId; + runtimeId = lib.escapeShellArg runtimeId; }; } ./dotnet-install-hook.sh; @@ -79,11 +63,7 @@ in name = "dotnet-fixup-hook"; substitutions = { dotnetRuntime = dotnet-runtime; - runtimeDeps = libraryPath; - shell = stdenv.shell; - which = "${which}/bin/which"; - dirname = "${coreutils}/bin/dirname"; - realpath = "${coreutils}/bin/realpath"; + wrapperPath = lib.makeBinPath [ which coreutils ]; }; } ./dotnet-fixup-hook.sh; diff --git a/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-build-hook.sh b/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-build-hook.sh index 798109291f92a..f209861f79b15 100644 --- a/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-build-hook.sh +++ b/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-build-hook.sh @@ -1,12 +1,25 @@ -# inherit arguments from derivation -dotnetBuildFlags=( ${dotnetBuildFlags[@]-} ) - dotnetBuildHook() { echo "Executing dotnetBuildHook" 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 parallelBuildFlag="true" else @@ -14,50 +27,53 @@ dotnetBuildHook() { local -r parallelBuildFlag="false" fi - if [ "${selfContainedBuild-}" ]; then - dotnetBuildFlags+=("-p:SelfContained=true") + if [[ -n ${dotnetSelfContainedBuild-} ]]; then + dotnetBuildFlagsArray+=("-p:SelfContained=true") else - dotnetBuildFlags+=("-p:SelfContained=false") + dotnetBuildFlagsArray+=("-p:SelfContained=false") fi - if [ "${useAppHost-}" ]; then - dotnetBuildFlags+=("-p:UseAppHost=true") + if [[ -n ${dotnetUseAppHost-} ]]; then + dotnetBuildFlagsArray+=("-p:UseAppHost=true") fi - local versionFlags=() - if [ "${version-}" ]; then - versionFlags+=("-p:InformationalVersion=${version-}") + local versionFlagsArray=() + if [[ -n ${version-} ]]; then + versionFlagsArray+=("-p:InformationalVersion=$version") fi - if [ "${versionForDotnet-}" ]; then - versionFlags+=("-p:Version=${versionForDotnet-}") + if [[ -n ${versionForDotnet-} ]]; then + versionFlagsArray+=("-p:Version=$versionForDotnet") fi dotnetBuild() { - local -r project="${1-}" + local -r projectFile="${1-}" - runtimeIdFlags=() - if [[ "$project" == *.csproj ]] || [ "${selfContainedBuild-}" ]; then - runtimeIdFlags+=("--runtime @runtimeId@") + local runtimeIdFlagsArray=() + if [[ $projectFile == *.csproj || -n ${dotnetSelfContainedBuild-} ]]; then + runtimeIdFlagsArray+=("--runtime" "$dotnetRuntimeId") fi - dotnet build ${project-} \ - -maxcpucount:$maxCpuFlag \ - -p:BuildInParallel=$parallelBuildFlag \ + dotnet build ${1+"$projectFile"} \ + -maxcpucount:"$maxCpuFlag" \ + -p:BuildInParallel="$parallelBuildFlag" \ -p:ContinuousIntegrationBuild=true \ -p:Deterministic=true \ - --configuration "@buildType@" \ + --configuration "$dotnetBuildType" \ --no-restore \ - ${versionFlags[@]} \ - ${runtimeIdFlags[@]} \ - ${dotnetBuildFlags[@]} \ - ${dotnetFlags[@]} + "${versionFlagsArray[@]}" \ + "${runtimeIdFlagsArray[@]}" \ + "${dotnetBuildFlagsArray[@]}" \ + "${dotnetFlagsArray[@]}" } - (( "${#projectFile[@]}" == 0 )) && dotnetBuild + if (( ${#dotnetProjectFilesArray[@]} == 0 )); then + dotnetBuild + fi - for project in ${projectFile[@]} ${testProjectFile[@]-}; do - dotnetBuild "$project" + local projectFile + for projectFile in "${dotnetProjectFilesArray[@]}" "${dotnetTestProjectFilesArray[@]}"; do + dotnetBuild "$projectFile" done runHook postBuild @@ -65,6 +81,6 @@ dotnetBuildHook() { echo "Finished dotnetBuildHook" } -if [[ -z "${dontDotnetBuild-}" && -z "${buildPhase-}" ]]; then +if [[ -z ${dontDotnetBuild-} && -z ${buildPhase-} ]]; then buildPhase=dotnetBuildHook fi diff --git a/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-check-hook.sh b/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-check-hook.sh index f19bf9f620feb..c91251f4f1807 100644 --- a/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-check-hook.sh +++ b/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-check-hook.sh @@ -1,39 +1,65 @@ -# inherit arguments from derivation -dotnetTestFlags=( ${dotnetTestFlags[@]-} ) - dotnetCheckHook() { echo "Executing dotnetCheckHook" runHook preCheck - if [ "${disabledTests-}" ]; then - local -r disabledTestsFlag="--filter @disabledTests@" + 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 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 + + 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 [ "${enableParallelBuilding-}" ]; then + if [[ -n ${enableParallelBuilding-} ]]; then local -r maxCpuFlag="$NIX_BUILD_CORES" else local -r maxCpuFlag="1" fi - for project in ${testProjectFile[@]-${projectFile[@]}}; do - runtimeIdFlags=() - if [[ "$project" == *.csproj ]]; then - runtimeIdFlags=("--runtime @runtimeId@") + local projectFile + for projectFile in "${dotnetTestProjectFilesArray[@]-${dotnetProjectFilesArray[@]}}"; do + local runtimeIdFlagsArray=() + if [[ $projectFile == *.csproj ]]; then + runtimeIdFlagsArray=("--runtime" "$dotnetRuntimeId") fi - LD_LIBRARY_PATH="@libraryPath@" \ - dotnet test "$project" \ - -maxcpucount:$maxCpuFlag \ + LD_LIBRARY_PATH=$libraryPath \ + dotnet test "$projectFile" \ + -maxcpucount:"$maxCpuFlag" \ -p:ContinuousIntegrationBuild=true \ -p:Deterministic=true \ - --configuration "@buildType@" \ + --configuration "$dotnetBuildType" \ --no-build \ --logger "console;verbosity=normal" \ - ${disabledTestsFlag-} \ - ${runtimeIdFlags[@]} \ - "${dotnetTestFlags[@]}" \ - "${dotnetFlags[@]}" + "${runtimeIdFlagsArray[@]}" \ + "${dotnetTestFlagsArray[@]}" \ + "${dotnetFlagsArray[@]}" done runHook postCheck diff --git a/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-configure-hook.sh b/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-configure-hook.sh index 3eb0d4e1f2309..12fa348699865 100644 --- a/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-configure-hook.sh +++ b/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-configure-hook.sh @@ -1,63 +1,103 @@ -declare -a projectFile testProjectFile - -# Inherit arguments from derivation -dotnetFlags=( ${dotnetFlags[@]-} ) -dotnetRestoreFlags=( ${dotnetRestoreFlags[@]-} ) - dotnetConfigureHook() { echo "Executing dotnetConfigureHook" 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" fi dotnetRestore() { - local -r project="${1-}" - dotnet restore ${project-} \ + local -r projectFile="${1-}" + dotnet restore ${1+"$projectFile"} \ -p:ContinuousIntegrationBuild=true \ -p:Deterministic=true \ - --runtime "@runtimeId@" \ - --source "@nugetSource@/lib" \ + --runtime "$dotnetRuntimeId" \ + --source "$nugetSource/lib" \ ${parallelFlag-} \ - ${dotnetRestoreFlags[@]} \ - ${dotnetFlags[@]} + "${dotnetRestoreFlagsArray[@]}" \ + "${dotnetFlagsArray[@]}" } # Generate a NuGet.config file to make sure everything, # 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"?> <configuration> <packageSources> <clear /> - <add key="nugetSource" value="@nugetSource@/lib" /> + <add key="nugetSource" value="$nugetSourceXMLQuoted/lib" /> </packageSources> </configuration> EOF - # Patch paket.dependencies and paket.lock (if found) to use the proper source. This ensures - # paket restore works correctly - # We use + instead of / in sed to avoid problems with slashes - find -name paket.dependencies -exec sed -i 's+source .*+source @nugetSource@/lib+g' {} \; - find -name paket.lock -exec sed -i 's+remote:.*+remote: @nugetSource@/lib+g' {} \; - - dotnet tool restore --add-source "@nugetSource@/lib" - - (( "${#projectFile[@]}" == 0 )) && dotnetRestore + # Patch paket.dependencies and paket.lock (if found) to use the proper + # source. This ensures paket restore works correctly. Note that the + # nugetSourceSedQuoted abomination below safely escapes nugetSource string + # for use as a sed replacement string to avoid issues with slashes and other + # 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" + + # 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. It’s 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 don’t have + # projects. + if [[ -z ${dotnetGlobalTool-} ]]; then + if (( ${#dotnetProjectFilesArray[@]} == 0 )); then + dotnetRestore + fi - for project in ${projectFile[@]} ${testProjectFile[@]-}; do - dotnetRestore "$project" - done + local projectFile + for projectFile in "${dotnetProjectFilesArray[@]}" "${dotnetTestProjectFilesArray[@]}"; do + dotnetRestore "$projectFile" + done + fi echo "Fixing up native binaries..." # Find all native binaries and nuget libraries, and fix them up, # 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 if patchelf --print-interpreter "$binary" >/dev/null 2>/dev/null; then 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 fixes dotnet-built binaries like crossgen2 @@ -68,7 +108,7 @@ EOF --add-needed libssl.so \ "$binary" - patchelf --set-rpath "@libPath@" "$binary" + patchelf --set-rpath "$libPath" "$binary" fi done diff --git a/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-fixup-hook.sh b/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-fixup-hook.sh index e3671728af35e..f9aba29a43555 100644 --- a/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-fixup-hook.sh +++ b/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-fixup-hook.sh @@ -1,28 +1,55 @@ -# Inherit arguments from the derivation -declare -a derivationMakeWrapperArgs="( ${makeWrapperArgs-} )" -makeWrapperArgs=( "${derivationMakeWrapperArgs[@]}" ) - # First argument is the executable you want to wrap, # the second is the destination for the wrapper. wrapDotnetProgram() { - local dotnetRootFlags=() + local -r dotnetRuntime=@dotnetRuntime@ + local -r wrapperPath=@wrapperPath@ + + local -r dotnetFromEnvScript='dotnetFromEnv() { + 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 - if [ ! "${selfContainedBuild-}" ]; then - if [ "${useDotnetFromEnv-}" ]; then + 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 - dotnetRootFlags+=("--run" 'command -v dotnet &>/dev/null && export DOTNET_ROOT="$(@dirname@ "$(@realpath@ "$(@which@ dotnet)")")" || export DOTNET_ROOT="@dotnetRuntime@"') - dotnetRootFlags+=("--suffix" "PATH" ":" "@dotnetRuntime@/bin") + dotnetRootFlagsArray+=("--suffix" "PATH" ":" "$wrapperPath") + dotnetRootFlagsArray+=("--run" "$dotnetFromEnvScript") + dotnetRootFlagsArray+=("--set-default" "DOTNET_ROOT" "$dotnetRuntime") + dotnetRootFlagsArray+=("--suffix" "PATH" ":" "$dotnetRuntime/bin") else - dotnetRootFlags+=("--set" "DOTNET_ROOT" "@dotnetRuntime@") - dotnetRootFlags+=("--prefix" "PATH" ":" "@dotnetRuntime@/bin") + dotnetRootFlagsArray+=("--set" "DOTNET_ROOT" "$dotnetRuntime") + dotnetRootFlagsArray+=("--prefix" "PATH" ":" "$dotnetRuntime/bin") fi fi makeWrapper "$1" "$2" \ - --suffix "LD_LIBRARY_PATH" : "@runtimeDeps@" \ - "${dotnetRootFlags[@]}" \ + "${dotnetRuntimeDepsFlags[@]}" \ + "${dotnetRootFlagsArray[@]}" \ "${gappsWrapperArgs[@]}" \ - "${makeWrapperArgs[@]}" + "${makeWrapperArgsArray[@]}" echo "installed wrapper to "$2"" } @@ -30,13 +57,24 @@ wrapDotnetProgram() { dotnetFixupHook() { echo "Executing dotnetFixupPhase" - # check if executables is declared (including empty values, in which case we generate no executables) - if declare -p executables &>/dev/null; then - for executable in ${executables[@]}; do - path="${installPath-$out/lib/$pname}/$executable" + local -r dotnetInstallPath="${dotnetInstallPath-$out/lib/$pname}" + + local executable executableBasename + + # 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 - wrapDotnetProgram "$path" "$out/bin/$(basename "$executable")" + wrapDotnetProgram "$path" "$out/bin/$executableBasename" else echo "Specified binary \"$executable\" is either not an executable or does not exist!" echo "Looked in $path" @@ -45,8 +83,9 @@ dotnetFixupHook() { done else while IFS= read -d '' executable; do - wrapDotnetProgram "$executable" "$out/bin/$(basename "$executable")" \; - done < <(find "${installPath-$out/lib/$pname}" ! -name "*.dll" -executable -type f -print0) + executableBasename=$(basename "$executable") + wrapDotnetProgram "$executable" "$out/bin/$executableBasename" \; + done < <(find "$dotnetInstallPath" ! -name "*.dll" -executable -type f -print0) fi echo "Finished dotnetFixupPhase" diff --git a/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-install-hook.sh b/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-install-hook.sh index ed754d8ffcad9..4d9b3c502c354 100644 --- a/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-install-hook.sh +++ b/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-install-hook.sh @@ -1,70 +1,86 @@ -# inherit arguments from derivation -dotnetInstallFlags=( ${dotnetInstallFlags[@]-} ) - dotnetInstallHook() { echo "Executing dotnetInstallHook" runHook preInstall - if [ "${selfContainedBuild-}" ]; then - dotnetInstallFlags+=("--self-contained") + local -r hostRuntimeId=@runtimeId@ + 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 + local dotnetProjectFilesArray=($dotnetProjectFiles) + local dotnetFlagsArray=($dotnetFlags) + local dotnetInstallFlagsArray=($dotnetInstallFlags) + local dotnetPackFlagsArray=($dotnetPackFlags) + fi + + if [[ -n ${dotnetSelfContainedBuild-} ]]; then + dotnetInstallFlagsArray+=("--self-contained") else - dotnetInstallFlags+=("--no-self-contained") + 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 - dotnetInstallFlags+=("-p:PublishTrimmed=false") + dotnetInstallFlagsArray+=("-p:PublishTrimmed=false") fi - if [ "${useAppHost-}" ]; then - dotnetInstallFlags+=("-p:UseAppHost=true") + if [[ -n ${dotnetUseAppHost-} ]]; then + dotnetInstallFlagsArray+=("-p:UseAppHost=true") fi dotnetPublish() { - local -r project="${1-}" + local -r projectFile="${1-}" - runtimeIdFlags=() - if [[ "$project" == *.csproj ]] || [ "${selfContainedBuild-}" ]; then - runtimeIdFlags+=("--runtime @runtimeId@") + runtimeIdFlagsArray=() + if [[ $projectFile == *.csproj || -n ${dotnetSelfContainedBuild-} ]]; then + runtimeIdFlagsArray+=("--runtime" "$dotnetRuntimeId") fi - dotnet publish ${project-} \ + dotnet publish ${1+"$projectFile"} \ -p:ContinuousIntegrationBuild=true \ -p:Deterministic=true \ - --output "${installPath-$out/lib/$pname}" \ - --configuration "@buildType@" \ + --output "$dotnetInstallPath" \ + --configuration "$dotnetBuildType" \ --no-build \ - ${runtimeIdFlags[@]} \ - ${dotnetInstallFlags[@]} \ - ${dotnetFlags[@]} + "${runtimeIdFlagsArray[@]}" \ + "${dotnetInstallFlagsArray[@]}" \ + "${dotnetFlagsArray[@]}" } dotnetPack() { - local -r project="${1-}" - dotnet pack ${project-} \ + local -r projectFile="${1-}" + dotnet pack ${1+"$projectFile"} \ -p:ContinuousIntegrationBuild=true \ -p:Deterministic=true \ --output "$out/share" \ - --configuration "@buildType@" \ + --configuration "$dotnetBuildType" \ --no-build \ - --runtime "@runtimeId@" \ - ${dotnetPackFlags[@]} \ - ${dotnetFlags[@]} + --runtime "$dotnetRuntimeId" \ + "${dotnetPackFlagsArray[@]}" \ + "${dotnetFlagsArray[@]}" } - if (( "${#projectFile[@]}" == 0 )); then + if (( ${#dotnetProjectFilesArray[@]} == 0 )); then dotnetPublish else - for project in ${projectFile[@]}; do - dotnetPublish "$project" + local projectFile + for projectFile in "${dotnetProjectFilesArray[@]}"; do + dotnetPublish "$projectFile" done fi - if [[ "${packNupkg-}" ]]; then - if (( "${#projectFile[@]}" == 0 )); then + if [[ -n ${packNupkg-} ]]; then + if (( ${#dotnetProjectFilesArray[@]} == 0 )); then dotnetPack else - for project in ${projectFile[@]}; do - dotnetPack "$project" + local projectFile + for projectFile in "${dotnetProjectFilesArray[@]}"; do + dotnetPack "$projectFile" done fi fi diff --git a/pkgs/test/dotnet/default.nix b/pkgs/test/dotnet/default.nix index 7592b09d76e3c..d70850c05fdb0 100644 --- a/pkgs/test/dotnet/default.nix +++ b/pkgs/test/dotnet/default.nix @@ -1,5 +1,7 @@ -{ callPackage }: +{ lib, callPackage }: { project-references = callPackage ./project-references { }; + use-dotnet-from-env = lib.recurseIntoAttrs (callPackage ./use-dotnet-from-env { }); + structured-attrs = lib.recurseIntoAttrs (callPackage ./structured-attrs { }); } diff --git a/pkgs/test/dotnet/project-references/default.nix b/pkgs/test/dotnet/project-references/default.nix index f40b9196c2091..4ac5cf2ad82e6 100644 --- a/pkgs/test/dotnet/project-references/default.nix +++ b/pkgs/test/dotnet/project-references/default.nix @@ -4,11 +4,13 @@ { lib , dotnet-sdk -, buildDotnetModule +, buildPackages # buildDotnetModule , runCommand }: let + inherit (buildPackages) buildDotnetModule; + nugetDeps = ./nuget-deps.nix; # Specify the TargetFramework via an environment variable so that we don't @@ -18,7 +20,8 @@ let library = buildDotnetModule { name = "project-references-test-library"; src = ./library; - inherit nugetDeps TargetFramework; + inherit nugetDeps; + env.TargetFramework = TargetFramework; packNupkg = true; }; @@ -26,7 +29,8 @@ let application = buildDotnetModule { name = "project-references-test-application"; src = ./application; - inherit nugetDeps TargetFramework; + inherit nugetDeps; + env.TargetFramework = TargetFramework; projectReferences = [ library ]; }; diff --git a/pkgs/test/dotnet/structured-attrs/default.nix b/pkgs/test/dotnet/structured-attrs/default.nix new file mode 100644 index 0000000000000..cf96fef8dbdcc --- /dev/null +++ b/pkgs/test/dotnet/structured-attrs/default.nix @@ -0,0 +1,36 @@ +{ lib +, dotnet-sdk +, buildPackages # buildDotnetModule +, testers +, runCommand +}: +let + # Note: without structured attributes, we can’t 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" + ''; + }; +} diff --git a/pkgs/test/dotnet/structured-attrs/nuget-deps.nix b/pkgs/test/dotnet/structured-attrs/nuget-deps.nix new file mode 100644 index 0000000000000..f3a17967e25c8 --- /dev/null +++ b/pkgs/test/dotnet/structured-attrs/nuget-deps.nix @@ -0,0 +1,5 @@ +# This file was automatically generated by passthru.fetch-deps. +# Please dont edit it manually, your changes might get overwritten! + +{ fetchNuGet }: [ +] diff --git a/pkgs/test/dotnet/structured-attrs/src/Application.cs b/pkgs/test/dotnet/structured-attrs/src/Application.cs new file mode 100644 index 0000000000000..3bc548105c2b4 --- /dev/null +++ b/pkgs/test/dotnet/structured-attrs/src/Application.cs @@ -0,0 +1,10 @@ +using System; +using System.Reflection; + +Console.Write( + ( + (AssemblyCopyrightAttribute)Assembly + .GetExecutingAssembly() + .GetCustomAttributes(typeof(AssemblyCopyrightAttribute), true)[0] + ).Copyright +); diff --git a/pkgs/test/dotnet/structured-attrs/src/Application.csproj b/pkgs/test/dotnet/structured-attrs/src/Application.csproj new file mode 100644 index 0000000000000..decaa6d961aab --- /dev/null +++ b/pkgs/test/dotnet/structured-attrs/src/Application.csproj @@ -0,0 +1,5 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <OutputType>exe</OutputType> + </PropertyGroup> +</Project> diff --git a/pkgs/test/dotnet/use-dotnet-from-env/default.nix b/pkgs/test/dotnet/use-dotnet-from-env/default.nix new file mode 100644 index 0000000000000..711a98eb0c29d --- /dev/null +++ b/pkgs/test/dotnet/use-dotnet-from-env/default.nix @@ -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" + ''; + }; +} diff --git a/pkgs/test/dotnet/use-dotnet-from-env/nuget-deps.nix b/pkgs/test/dotnet/use-dotnet-from-env/nuget-deps.nix new file mode 100644 index 0000000000000..f3a17967e25c8 --- /dev/null +++ b/pkgs/test/dotnet/use-dotnet-from-env/nuget-deps.nix @@ -0,0 +1,5 @@ +# This file was automatically generated by passthru.fetch-deps. +# Please dont edit it manually, your changes might get overwritten! + +{ fetchNuGet }: [ +] diff --git a/pkgs/test/dotnet/use-dotnet-from-env/src/Application.cs b/pkgs/test/dotnet/use-dotnet-from-env/src/Application.cs new file mode 100644 index 0000000000000..5efc571ca9a3e --- /dev/null +++ b/pkgs/test/dotnet/use-dotnet-from-env/src/Application.cs @@ -0,0 +1,3 @@ +using System; + +Console.Write(Environment.Version.ToString()); diff --git a/pkgs/test/dotnet/use-dotnet-from-env/src/Application.csproj b/pkgs/test/dotnet/use-dotnet-from-env/src/Application.csproj new file mode 100644 index 0000000000000..decaa6d961aab --- /dev/null +++ b/pkgs/test/dotnet/use-dotnet-from-env/src/Application.csproj @@ -0,0 +1,5 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <OutputType>exe</OutputType> + </PropertyGroup> +</Project> |