From 77a13aa9add226603b5225df6fb01168bc13cad3 Mon Sep 17 00:00:00 2001 From: Matt Sturgeon Date: Thu, 1 Aug 2024 13:58:28 +0100 Subject: [PATCH 1/2] buildDotnetModule: format with nixfmt --- .../dotnet/build-dotnet-module/default.nix | 337 ++++++++++-------- 1 file changed, 184 insertions(+), 153 deletions(-) diff --git a/pkgs/build-support/dotnet/build-dotnet-module/default.nix b/pkgs/build-support/dotnet/build-dotnet-module/default.nix index 2ba801231f38..6cdd4f5e40db 100644 --- a/pkgs/build-support/dotnet/build-dotnet-module/default.nix +++ b/pkgs/build-support/dotnet/build-dotnet-module/default.nix @@ -1,52 +1,54 @@ -{ lib -, runtimeShell -, stdenvNoCC -, callPackage -, substituteAll -, writeShellScript -, makeWrapper -, dotnetCorePackages -, mkNugetDeps -, nuget-to-nix -, cacert -, unzip -, yq -, nix +{ + lib, + runtimeShell, + stdenvNoCC, + callPackage, + substituteAll, + writeShellScript, + makeWrapper, + dotnetCorePackages, + mkNugetDeps, + nuget-to-nix, + cacert, + unzip, + yq, + nix, }: -{ name ? "${_args.pname}-${_args.version}" -, pname ? name -, enableParallelBuilding ? true -, doCheck ? false +{ + name ? "${_args.pname}-${_args.version}", + pname ? name, + enableParallelBuilding ? true, + doCheck ? false, # Flags to pass to `makeWrapper`. This is done to avoid double wrapping. -, makeWrapperArgs ? [ ] + makeWrapperArgs ? [ ], # Flags to pass to `dotnet restore`. -, dotnetRestoreFlags ? [ ] + dotnetRestoreFlags ? [ ], # Flags to pass to `dotnet build`. -, dotnetBuildFlags ? [ ] + dotnetBuildFlags ? [ ], # Flags to pass to `dotnet test`, if running tests is enabled. -, dotnetTestFlags ? [ ] + dotnetTestFlags ? [ ], # Flags to pass to `dotnet install`. -, dotnetInstallFlags ? [ ] + dotnetInstallFlags ? [ ], # Flags to pass to `dotnet pack`. -, dotnetPackFlags ? [ ] + dotnetPackFlags ? [ ], # Flags to pass to dotnet in all phases. -, dotnetFlags ? [ ] + dotnetFlags ? [ ], # The path to publish the project to. When unset, the directory "$out/lib/$pname" is used. -, installPath ? null + installPath ? null, # The binaries that should get installed to `$out/bin`, relative to `$installPath/`. These get wrapped accordingly. # Unfortunately, dotnet has no method for doing this automatically. # If unset, all executables in the projects root will get installed. This may cause bloat! -, executables ? null + executables ? null, # Packs a project as a `nupkg`, and installs it to `$out/share`. If set to `true`, the derivation can be used as a dependency for another dotnet project by adding it to `projectReferences`. -, packNupkg ? false + packNupkg ? false, # The packages project file, which contains instructions on how to compile it. This can be an array of multiple project files as well. -, projectFile ? null + projectFile ? null, # The NuGet dependency file. This locks all NuGet dependency versions, as otherwise they cannot be deterministically fetched. # This can be generated by running the `passthru.fetch-deps` script. -, nugetDeps ? null + nugetDeps ? null, # A list of derivations containing nupkg packages for local project references. # Referenced derivations can be built with `buildDotnetModule` with `packNupkg=true` flag. # Since we are sharing them as nugets they must be added to csproj/fsproj files as `PackageReference` as well. @@ -55,168 +57,197 @@ # To enable discovery through `projectReferences` you would need to add a line: # # -, projectReferences ? [ ] + projectReferences ? [ ], # Libraries that need to be available at runtime should be passed through this. # These get wrapped into `LD_LIBRARY_PATH`. -, runtimeDeps ? [ ] + runtimeDeps ? [ ], # The dotnet runtime ID. If null, fetch-deps will gather dependencies for all # platforms in meta.platforms which are supported by the sdk. -, runtimeId ? null + runtimeId ? null, # Tests to disable. This gets passed to `dotnet test --filter "FullyQualifiedName!={}"`, to ensure compatibility with all frameworks. # See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test#filter-option-details for more details. -, 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. # It gets restored and build, but not installed. You may need to regenerate your nuget lockfile after setting this. -, testProjectFile ? null + 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" + buildType ? "Release", # If set to true, builds the application as a self-contained - removing the runtime dependency on dotnet -, selfContainedBuild ? false + selfContainedBuild ? false, # Whether to use an alternative wrapper, that executes the application DLL using the dotnet runtime from the user environment. `dotnet-runtime` is provided as a default in case no .NET is installed # This is useful for .NET tools and applications that may need to run under different .NET runtimes -, useDotnetFromEnv ? false + useDotnetFromEnv ? false, # Whether to explicitly enable UseAppHost when building. This is redundant if useDotnetFromEnv is enabled -, useAppHost ? true + useAppHost ? true, # The dotnet SDK to use. -, dotnet-sdk ? dotnetCorePackages.sdk_6_0 + dotnet-sdk ? dotnetCorePackages.sdk_6_0, # The dotnet runtime to use. -, dotnet-runtime ? dotnetCorePackages.runtime_6_0 -, ... -} @ _args: + dotnet-runtime ? dotnetCorePackages.runtime_6_0, + ... +}@_args: let args = removeAttrs _args [ "nugetDeps" ]; - projectFiles = - lib.optionals (projectFile != null) (lib.toList projectFile); - testProjectFiles = - lib.optionals (testProjectFile != null) (lib.toList testProjectFile); + 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; + 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 dotnet-runtime; - }) dotnetConfigureHook dotnetBuildHook dotnetCheckHook dotnetInstallHook dotnetFixupHook; - - _nugetDeps = - if (nugetDeps != null) then - if lib.isDerivation nugetDeps - then nugetDeps - else mkNugetDeps { - inherit name; - sourceFile = nugetDeps; - } - else throw "Defining the `nugetDeps` attribute is required, as to lock the NuGet dependencies. This file can be generated by running the `passthru.fetch-deps` script."; - - nugetDepsFile = _nugetDeps.sourceFile; - - inherit (dotnetCorePackages) systemToDotnetRid; -in -stdenvNoCC.mkDerivation (finalAttrs: args // { - dotnetInstallPath = installPath; - dotnetExecutables = executables; - dotnetBuildType = buildType; - dotnetProjectFiles = projectFiles; - dotnetTestProjectFiles = testProjectFiles; - dotnetDisabledTests = disabledTests; - dotnetRuntimeIds = lib.singleton ( - if runtimeId != null - then runtimeId - else systemToDotnetRid stdenvNoCC.hostPlatform.system); - dotnetRuntimeDeps = map lib.getLib runtimeDeps; - dotnetSelfContainedBuild = selfContainedBuild; - dotnetUseAppHost = useAppHost; - inherit useDotnetFromEnv; - - nativeBuildInputs = args.nativeBuildInputs or [ ] ++ [ + inherit (callPackage ./hooks { inherit dotnet-sdk dotnet-runtime; }) dotnetConfigureHook dotnetBuildHook dotnetCheckHook dotnetInstallHook dotnetFixupHook + ; - cacert - makeWrapper - dotnet-sdk - unzip - yq - ]; + _nugetDeps = + if (nugetDeps != null) then + if lib.isDerivation nugetDeps then + nugetDeps + else + mkNugetDeps { + inherit name; + sourceFile = nugetDeps; + } + else + throw "Defining the `nugetDeps` attribute is required, as to lock the NuGet dependencies. This file can be generated by running the `passthru.fetch-deps` script."; - buildInputs = args.buildInputs or [] ++ [ - dotnet-sdk.packages - _nugetDeps - ] ++ projectReferences; + nugetDepsFile = _nugetDeps.sourceFile; - # Parse the version attr into a format acceptable for the Version msbuild property - # The actual version attr is saved in InformationalVersion, which accepts an arbitrary string - versionForDotnet = if !(lib.hasAttr "version" args) || args.version == null - then null else let - components = lib.pipe args.version [ - lib.splitVersion - (lib.filter (x: (lib.strings.match "[0-9]+" x) != null)) - (lib.filter (x: (lib.toIntBase10 x) < 65535)) # one version component in dotnet has to fit in 16 bits + inherit (dotnetCorePackages) systemToDotnetRid; +in +stdenvNoCC.mkDerivation ( + finalAttrs: + args + // { + dotnetInstallPath = installPath; + dotnetExecutables = executables; + dotnetBuildType = buildType; + dotnetProjectFiles = projectFiles; + dotnetTestProjectFiles = testProjectFiles; + dotnetDisabledTests = disabledTests; + dotnetRuntimeIds = lib.singleton ( + if runtimeId != null then runtimeId else systemToDotnetRid stdenvNoCC.hostPlatform.system + ); + dotnetRuntimeDeps = map lib.getLib runtimeDeps; + dotnetSelfContainedBuild = selfContainedBuild; + dotnetUseAppHost = useAppHost; + inherit useDotnetFromEnv; + + nativeBuildInputs = args.nativeBuildInputs or [ ] ++ [ + dotnetConfigureHook + dotnetBuildHook + dotnetCheckHook + dotnetInstallHook + dotnetFixupHook + + cacert + makeWrapper + dotnet-sdk + unzip + yq ]; - in if (lib.length components) == 0 - then null - else lib.concatStringsSep "." ((lib.take 4 components) - ++ (if (lib.length components) < 4 - then lib.replicate (4 - (lib.length components)) "0" - else [ ])); - makeWrapperArgs = args.makeWrapperArgs or [ ] ++ [ - "--prefix" "LD_LIBRARY_PATH" ":" "${dotnet-sdk.icu}/lib" - ]; + buildInputs = + args.buildInputs or [ ] + ++ [ + dotnet-sdk.packages + _nugetDeps + ] + ++ projectReferences; - # Stripping breaks the executable - dontStrip = args.dontStrip or true; + # Parse the version attr into a format acceptable for the Version msbuild property + # The actual version attr is saved in InformationalVersion, which accepts an arbitrary string + versionForDotnet = + if !(lib.hasAttr "version" args) || args.version == null then + null + else + let + components = lib.pipe args.version [ + lib.splitVersion + (lib.filter (x: (lib.strings.match "[0-9]+" x) != null)) + (lib.filter (x: (lib.toIntBase10 x) < 65535)) # one version component in dotnet has to fit in 16 bits + ]; + in + if (lib.length components) == 0 then + null + else + lib.concatStringsSep "." ( + (lib.take 4 components) + ++ (if (lib.length components) < 4 then lib.replicate (4 - (lib.length components)) "0" else [ ]) + ); - # gappsWrapperArgs gets included when wrapping for dotnet, as to avoid double wrapping - dontWrapGApps = args.dontWrapGApps or true; + makeWrapperArgs = args.makeWrapperArgs or [ ] ++ [ + "--prefix" + "LD_LIBRARY_PATH" + ":" + "${dotnet-sdk.icu}/lib" + ]; - # propagate the runtime sandbox profile since the contents apply to published - # executables - propagatedSandboxProfile = toString dotnet-runtime.__propagatedSandboxProfile; + # Stripping breaks the executable + dontStrip = args.dontStrip or true; - passthru = { - nugetDeps = _nugetDeps; - } // lib.optionalAttrs (!lib.isDerivation nugetDeps) { - fetch-deps = let - pkg = finalAttrs.finalPackage.overrideAttrs (old: { - buildInputs = lib.remove _nugetDeps old.buildInputs; - keepNugetConfig = true; - } // lib.optionalAttrs (runtimeId == null) { - dotnetRuntimeIds = map (system: systemToDotnetRid system) platforms; - }); + # gappsWrapperArgs gets included when wrapping for dotnet, as to avoid double wrapping + dontWrapGApps = args.dontWrapGApps or true; - drv = builtins.unsafeDiscardOutputDependency pkg.drvPath; + # propagate the runtime sandbox profile since the contents apply to published + # executables + propagatedSandboxProfile = toString dotnet-runtime.__propagatedSandboxProfile; - innerScript = substituteAll { - src = ./fetch-deps.sh; - isExecutable = true; - defaultDepsFile = - # Wire in the nugetDeps file such that running the script with no args - # runs it agains the correct deps file by default. - # Note that toString is necessary here as it results in the path at - # eval time (i.e. to the file in your local Nixpkgs checkout) rather - # than the Nix store path of the path after it's been imported. - if lib.isPath nugetDepsFile - && !lib.hasPrefix "${builtins.storeDir}/" (toString nugetDepsFile) then - toString nugetDepsFile - else - ''$(mktemp -t "${pname}-deps-XXXXXX.nix")''; - nugetToNix = (nuget-to-nix.override { inherit dotnet-sdk; }); - }; + passthru = + { + nugetDeps = _nugetDeps; + } + // lib.optionalAttrs (!lib.isDerivation nugetDeps) { + fetch-deps = + let + pkg = finalAttrs.finalPackage.overrideAttrs ( + old: + { + buildInputs = lib.remove _nugetDeps old.buildInputs; + keepNugetConfig = true; + } + // lib.optionalAttrs (runtimeId == null) { + dotnetRuntimeIds = map (system: systemToDotnetRid system) platforms; + } + ); - in writeShellScript "${name}-fetch-deps" '' - NIX_BUILD_SHELL="${runtimeShell}" exec ${nix}/bin/nix-shell \ - --pure --run 'source "${innerScript}"' "${drv}" - ''; - } // args.passthru or { }; + drv = builtins.unsafeDiscardOutputDependency pkg.drvPath; - meta = (args.meta or { }) // { inherit platforms; }; -}) + innerScript = substituteAll { + src = ./fetch-deps.sh; + isExecutable = true; + defaultDepsFile = + # Wire in the nugetDeps file such that running the script with no args + # runs it agains the correct deps file by default. + # Note that toString is necessary here as it results in the path at + # eval time (i.e. to the file in your local Nixpkgs checkout) rather + # than the Nix store path of the path after it's been imported. + if lib.isPath nugetDepsFile && !lib.hasPrefix "${builtins.storeDir}/" (toString nugetDepsFile) then + toString nugetDepsFile + else + ''$(mktemp -t "${pname}-deps-XXXXXX.nix")''; + nugetToNix = (nuget-to-nix.override { inherit dotnet-sdk; }); + }; + + in + writeShellScript "${name}-fetch-deps" '' + NIX_BUILD_SHELL="${runtimeShell}" exec ${nix}/bin/nix-shell \ + --pure --run 'source "${innerScript}"' "${drv}" + ''; + } + // args.passthru or { }; + + meta = (args.meta or { }) // { + inherit platforms; + }; + } +) From 79d26048de8e908826c2a1acb60188df69f19112 Mon Sep 17 00:00:00 2001 From: Matt Sturgeon Date: Thu, 1 Aug 2024 01:18:03 +0100 Subject: [PATCH 2/2] buildDotnetModule: add `finalAttrs` support Allow users to pass arguments to `buildDotnetModule` in the form: ```nix buildDotnetModule (finalAttrs: { # Args }) ``` Exposing the behaviour of the underlying `mkDerivation` and allowing packages to be defined in a recursive way that works correctly even when the package is overridden, e.g. using `overrideAttrs`. Added some simple test cases that piggyback on the existing `structured-attrs` test. --- .../dotnet/build-dotnet-module/default.nix | 439 +++++++++--------- pkgs/test/dotnet/default.nix | 1 + pkgs/test/dotnet/final-attrs/default.nix | 76 +++ 3 files changed, 300 insertions(+), 216 deletions(-) create mode 100644 pkgs/test/dotnet/final-attrs/default.nix diff --git a/pkgs/build-support/dotnet/build-dotnet-module/default.nix b/pkgs/build-support/dotnet/build-dotnet-module/default.nix index 6cdd4f5e40db..c8ad6a174ce5 100644 --- a/pkgs/build-support/dotnet/build-dotnet-module/default.nix +++ b/pkgs/build-support/dotnet/build-dotnet-module/default.nix @@ -14,240 +14,247 @@ yq, nix, }: - -{ - name ? "${_args.pname}-${_args.version}", - pname ? name, - enableParallelBuilding ? true, - doCheck ? false, - # Flags to pass to `makeWrapper`. This is done to avoid double wrapping. - makeWrapperArgs ? [ ], - - # Flags to pass to `dotnet restore`. - dotnetRestoreFlags ? [ ], - # Flags to pass to `dotnet build`. - dotnetBuildFlags ? [ ], - # Flags to pass to `dotnet test`, if running tests is enabled. - dotnetTestFlags ? [ ], - # Flags to pass to `dotnet install`. - dotnetInstallFlags ? [ ], - # Flags to pass to `dotnet pack`. - dotnetPackFlags ? [ ], - # Flags to pass to dotnet in all phases. - dotnetFlags ? [ ], - - # The path to publish the project to. When unset, the directory "$out/lib/$pname" is used. - installPath ? null, - # The binaries that should get installed to `$out/bin`, relative to `$installPath/`. These get wrapped accordingly. - # Unfortunately, dotnet has no method for doing this automatically. - # If unset, all executables in the projects root will get installed. This may cause bloat! - executables ? null, - # Packs a project as a `nupkg`, and installs it to `$out/share`. If set to `true`, the derivation can be used as a dependency for another dotnet project by adding it to `projectReferences`. - packNupkg ? false, - # The packages project file, which contains instructions on how to compile it. This can be an array of multiple project files as well. - projectFile ? null, - # The NuGet dependency file. This locks all NuGet dependency versions, as otherwise they cannot be deterministically fetched. - # This can be generated by running the `passthru.fetch-deps` script. - nugetDeps ? null, - # A list of derivations containing nupkg packages for local project references. - # Referenced derivations can be built with `buildDotnetModule` with `packNupkg=true` flag. - # Since we are sharing them as nugets they must be added to csproj/fsproj files as `PackageReference` as well. - # For example, your project has a local dependency: - # - # To enable discovery through `projectReferences` you would need to add a line: - # - # - projectReferences ? [ ], - # Libraries that need to be available at runtime should be passed through this. - # These get wrapped into `LD_LIBRARY_PATH`. - runtimeDeps ? [ ], - # The dotnet runtime ID. If null, fetch-deps will gather dependencies for all - # platforms in meta.platforms which are supported by the sdk. - runtimeId ? null, - - # Tests to disable. This gets passed to `dotnet test --filter "FullyQualifiedName!={}"`, to ensure compatibility with all frameworks. - # See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test#filter-option-details for more details. - 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 ? null, - - # The type of build to perform. This is passed to `dotnet` with the `--configuration` flag. Possible values are `Release`, `Debug`, etc. - buildType ? "Release", - # If set to true, builds the application as a self-contained - removing the runtime dependency on dotnet - selfContainedBuild ? false, - # Whether to use an alternative wrapper, that executes the application DLL using the dotnet runtime from the user environment. `dotnet-runtime` is provided as a default in case no .NET is installed - # This is useful for .NET tools and applications that may need to run under different .NET runtimes - useDotnetFromEnv ? false, - # Whether to explicitly enable UseAppHost when building. This is redundant if useDotnetFromEnv is enabled - useAppHost ? true, - # The dotnet SDK to use. - dotnet-sdk ? dotnetCorePackages.sdk_6_0, - # The dotnet runtime to use. - dotnet-runtime ? dotnetCorePackages.runtime_6_0, - ... -}@_args: - let - args = removeAttrs _args [ "nugetDeps" ]; + transformArgs = + finalAttrs: + { + name ? "${args.pname}-${args.version}", + pname ? name, + enableParallelBuilding ? true, + doCheck ? false, + # Flags to pass to `makeWrapper`. This is done to avoid double wrapping. + makeWrapperArgs ? [ ], - projectFiles = lib.optionals (projectFile != null) (lib.toList projectFile); - testProjectFiles = lib.optionals (testProjectFile != null) (lib.toList testProjectFile); + # Flags to pass to `dotnet restore`. + dotnetRestoreFlags ? [ ], + # Flags to pass to `dotnet build`. + dotnetBuildFlags ? [ ], + # Flags to pass to `dotnet test`, if running tests is enabled. + dotnetTestFlags ? [ ], + # Flags to pass to `dotnet install`. + dotnetInstallFlags ? [ ], + # Flags to pass to `dotnet pack`. + dotnetPackFlags ? [ ], + # Flags to pass to dotnet in all phases. + dotnetFlags ? [ ], - platforms = - if args ? meta.platforms then - lib.intersectLists args.meta.platforms dotnet-sdk.meta.platforms - else - dotnet-sdk.meta.platforms; + # The path to publish the project to. When unset, the directory "$out/lib/$pname" is used. + installPath ? null, + # The binaries that should get installed to `$out/bin`, relative to `$installPath/`. These get wrapped accordingly. + # Unfortunately, dotnet has no method for doing this automatically. + # If unset, all executables in the projects root will get installed. This may cause bloat! + executables ? null, + # Packs a project as a `nupkg`, and installs it to `$out/share`. If set to `true`, the derivation can be used as a dependency for another dotnet project by adding it to `projectReferences`. + packNupkg ? false, + # The packages project file, which contains instructions on how to compile it. This can be an array of multiple project files as well. + projectFile ? null, + # The NuGet dependency file. This locks all NuGet dependency versions, as otherwise they cannot be deterministically fetched. + # This can be generated by running the `passthru.fetch-deps` script. + nugetDeps ? null, + # A list of derivations containing nupkg packages for local project references. + # Referenced derivations can be built with `buildDotnetModule` with `packNupkg=true` flag. + # Since we are sharing them as nugets they must be added to csproj/fsproj files as `PackageReference` as well. + # For example, your project has a local dependency: + # + # To enable discovery through `projectReferences` you would need to add a line: + # + # + projectReferences ? [ ], + # Libraries that need to be available at runtime should be passed through this. + # These get wrapped into `LD_LIBRARY_PATH`. + runtimeDeps ? [ ], + # The dotnet runtime ID. If null, fetch-deps will gather dependencies for all + # platforms in meta.platforms which are supported by the sdk. + runtimeId ? null, - inherit (callPackage ./hooks { inherit dotnet-sdk dotnet-runtime; }) - dotnetConfigureHook - dotnetBuildHook - dotnetCheckHook - dotnetInstallHook - dotnetFixupHook - ; + # Tests to disable. This gets passed to `dotnet test --filter "FullyQualifiedName!={}"`, to ensure compatibility with all frameworks. + # See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test#filter-option-details for more details. + 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 ? null, - _nugetDeps = - if (nugetDeps != null) then - if lib.isDerivation nugetDeps then - nugetDeps - else - mkNugetDeps { - inherit name; - sourceFile = nugetDeps; - } - else - throw "Defining the `nugetDeps` attribute is required, as to lock the NuGet dependencies. This file can be generated by running the `passthru.fetch-deps` script."; + # The type of build to perform. This is passed to `dotnet` with the `--configuration` flag. Possible values are `Release`, `Debug`, etc. + buildType ? "Release", + # If set to true, builds the application as a self-contained - removing the runtime dependency on dotnet + selfContainedBuild ? false, + # Whether to use an alternative wrapper, that executes the application DLL using the dotnet runtime from the user environment. `dotnet-runtime` is provided as a default in case no .NET is installed + # This is useful for .NET tools and applications that may need to run under different .NET runtimes + useDotnetFromEnv ? false, + # Whether to explicitly enable UseAppHost when building. This is redundant if useDotnetFromEnv is enabled + useAppHost ? true, + # The dotnet SDK to use. + dotnet-sdk ? dotnetCorePackages.sdk_6_0, + # The dotnet runtime to use. + dotnet-runtime ? dotnetCorePackages.runtime_6_0, + ... + }@args: + let + projectFiles = lib.optionals (projectFile != null) (lib.toList projectFile); + testProjectFiles = lib.optionals (testProjectFile != null) (lib.toList testProjectFile); - nugetDepsFile = _nugetDeps.sourceFile; + platforms = + if args ? meta.platforms then + lib.intersectLists args.meta.platforms dotnet-sdk.meta.platforms + else + dotnet-sdk.meta.platforms; - inherit (dotnetCorePackages) systemToDotnetRid; -in -stdenvNoCC.mkDerivation ( - finalAttrs: - args - // { - dotnetInstallPath = installPath; - dotnetExecutables = executables; - dotnetBuildType = buildType; - dotnetProjectFiles = projectFiles; - dotnetTestProjectFiles = testProjectFiles; - dotnetDisabledTests = disabledTests; - dotnetRuntimeIds = lib.singleton ( - if runtimeId != null then runtimeId else systemToDotnetRid stdenvNoCC.hostPlatform.system - ); - dotnetRuntimeDeps = map lib.getLib runtimeDeps; - dotnetSelfContainedBuild = selfContainedBuild; - dotnetUseAppHost = useAppHost; - inherit useDotnetFromEnv; + inherit (callPackage ./hooks { inherit dotnet-sdk dotnet-runtime; }) + dotnetConfigureHook + dotnetBuildHook + dotnetCheckHook + dotnetInstallHook + dotnetFixupHook + ; - nativeBuildInputs = args.nativeBuildInputs or [ ] ++ [ - dotnetConfigureHook - dotnetBuildHook - dotnetCheckHook - dotnetInstallHook - dotnetFixupHook + _nugetDeps = + if (nugetDeps != null) then + if lib.isDerivation nugetDeps then + nugetDeps + else + mkNugetDeps { + inherit name; + sourceFile = nugetDeps; + } + else + throw "Defining the `nugetDeps` attribute is required, as to lock the NuGet dependencies. This file can be generated by running the `passthru.fetch-deps` script."; - cacert - makeWrapper - dotnet-sdk - unzip - yq - ]; + nugetDepsFile = _nugetDeps.sourceFile; - buildInputs = - args.buildInputs or [ ] - ++ [ - dotnet-sdk.packages - _nugetDeps - ] - ++ projectReferences; + inherit (dotnetCorePackages) systemToDotnetRid; + in + # Not all args need to be passed through to mkDerivation + # TODO: We should probably filter out even more attrs + removeAttrs args [ "nugetDeps" ] + // { + dotnetInstallPath = installPath; + dotnetExecutables = executables; + dotnetBuildType = buildType; + dotnetProjectFiles = projectFiles; + dotnetTestProjectFiles = testProjectFiles; + dotnetDisabledTests = disabledTests; + dotnetRuntimeIds = lib.singleton ( + if runtimeId != null then runtimeId else systemToDotnetRid stdenvNoCC.hostPlatform.system + ); + dotnetRuntimeDeps = map lib.getLib runtimeDeps; + dotnetSelfContainedBuild = selfContainedBuild; + dotnetUseAppHost = useAppHost; + inherit useDotnetFromEnv; - # Parse the version attr into a format acceptable for the Version msbuild property - # The actual version attr is saved in InformationalVersion, which accepts an arbitrary string - versionForDotnet = - if !(lib.hasAttr "version" args) || args.version == null then - null - else - let - components = lib.pipe args.version [ - lib.splitVersion - (lib.filter (x: (lib.strings.match "[0-9]+" x) != null)) - (lib.filter (x: (lib.toIntBase10 x) < 65535)) # one version component in dotnet has to fit in 16 bits - ]; - in - if (lib.length components) == 0 then + nativeBuildInputs = args.nativeBuildInputs or [ ] ++ [ + dotnetConfigureHook + dotnetBuildHook + dotnetCheckHook + dotnetInstallHook + dotnetFixupHook + + cacert + makeWrapper + dotnet-sdk + unzip + yq + ]; + + buildInputs = + args.buildInputs or [ ] + ++ [ + dotnet-sdk.packages + _nugetDeps + ] + ++ projectReferences; + + # Parse the version attr into a format acceptable for the Version msbuild property + # The actual version attr is saved in InformationalVersion, which accepts an arbitrary string + versionForDotnet = + if !(lib.hasAttr "version" args) || args.version == null then null else - lib.concatStringsSep "." ( - (lib.take 4 components) - ++ (if (lib.length components) < 4 then lib.replicate (4 - (lib.length components)) "0" else [ ]) - ); - - makeWrapperArgs = args.makeWrapperArgs or [ ] ++ [ - "--prefix" - "LD_LIBRARY_PATH" - ":" - "${dotnet-sdk.icu}/lib" - ]; - - # Stripping breaks the executable - dontStrip = args.dontStrip or true; - - # gappsWrapperArgs gets included when wrapping for dotnet, as to avoid double wrapping - dontWrapGApps = args.dontWrapGApps or true; - - # propagate the runtime sandbox profile since the contents apply to published - # executables - propagatedSandboxProfile = toString dotnet-runtime.__propagatedSandboxProfile; - - passthru = - { - nugetDeps = _nugetDeps; - } - // lib.optionalAttrs (!lib.isDerivation nugetDeps) { - fetch-deps = let - pkg = finalAttrs.finalPackage.overrideAttrs ( - old: - { - buildInputs = lib.remove _nugetDeps old.buildInputs; - keepNugetConfig = true; - } - // lib.optionalAttrs (runtimeId == null) { - dotnetRuntimeIds = map (system: systemToDotnetRid system) platforms; - } + components = lib.pipe args.version [ + lib.splitVersion + (lib.filter (x: (lib.strings.match "[0-9]+" x) != null)) + (lib.filter (x: (lib.toIntBase10 x) < 65535)) # one version component in dotnet has to fit in 16 bits + ]; + in + if (lib.length components) == 0 then + null + else + lib.concatStringsSep "." ( + (lib.take 4 components) + ++ (if (lib.length components) < 4 then lib.replicate (4 - (lib.length components)) "0" else [ ]) ); - drv = builtins.unsafeDiscardOutputDependency pkg.drvPath; + makeWrapperArgs = args.makeWrapperArgs or [ ] ++ [ + "--prefix" + "LD_LIBRARY_PATH" + ":" + "${dotnet-sdk.icu}/lib" + ]; - innerScript = substituteAll { - src = ./fetch-deps.sh; - isExecutable = true; - defaultDepsFile = - # Wire in the nugetDeps file such that running the script with no args - # runs it agains the correct deps file by default. - # Note that toString is necessary here as it results in the path at - # eval time (i.e. to the file in your local Nixpkgs checkout) rather - # than the Nix store path of the path after it's been imported. - if lib.isPath nugetDepsFile && !lib.hasPrefix "${builtins.storeDir}/" (toString nugetDepsFile) then - toString nugetDepsFile - else - ''$(mktemp -t "${pname}-deps-XXXXXX.nix")''; - nugetToNix = (nuget-to-nix.override { inherit dotnet-sdk; }); - }; + # Stripping breaks the executable + dontStrip = args.dontStrip or true; - in - writeShellScript "${name}-fetch-deps" '' - NIX_BUILD_SHELL="${runtimeShell}" exec ${nix}/bin/nix-shell \ - --pure --run 'source "${innerScript}"' "${drv}" - ''; - } - // args.passthru or { }; + # gappsWrapperArgs gets included when wrapping for dotnet, as to avoid double wrapping + dontWrapGApps = args.dontWrapGApps or true; - meta = (args.meta or { }) // { - inherit platforms; + # propagate the runtime sandbox profile since the contents apply to published + # executables + propagatedSandboxProfile = toString dotnet-runtime.__propagatedSandboxProfile; + + passthru = + { + nugetDeps = _nugetDeps; + } + // lib.optionalAttrs (!lib.isDerivation nugetDeps) { + fetch-deps = + let + pkg = finalAttrs.finalPackage.overrideAttrs ( + old: + { + buildInputs = lib.remove _nugetDeps old.buildInputs; + keepNugetConfig = true; + } + // lib.optionalAttrs (runtimeId == null) { + dotnetRuntimeIds = map (system: systemToDotnetRid system) platforms; + } + ); + + drv = builtins.unsafeDiscardOutputDependency pkg.drvPath; + + innerScript = substituteAll { + src = ./fetch-deps.sh; + isExecutable = true; + defaultDepsFile = + # Wire in the nugetDeps file such that running the script with no args + # runs it agains the correct deps file by default. + # Note that toString is necessary here as it results in the path at + # eval time (i.e. to the file in your local Nixpkgs checkout) rather + # than the Nix store path of the path after it's been imported. + if lib.isPath nugetDepsFile && !lib.hasPrefix "${builtins.storeDir}/" (toString nugetDepsFile) then + toString nugetDepsFile + else + ''$(mktemp -t "${pname}-deps-XXXXXX.nix")''; + nugetToNix = (nuget-to-nix.override { inherit dotnet-sdk; }); + }; + + in + writeShellScript "${name}-fetch-deps" '' + NIX_BUILD_SHELL="${runtimeShell}" exec ${nix}/bin/nix-shell \ + --pure --run 'source "${innerScript}"' "${drv}" + ''; + } + // args.passthru or { }; + + meta = (args.meta or { }) // { + inherit platforms; + }; }; - } +in +fnOrAttrs: +stdenvNoCC.mkDerivation ( + finalAttrs: + let + args = if lib.isFunction fnOrAttrs then fnOrAttrs (args // finalAttrs) else fnOrAttrs; + in + transformArgs finalAttrs args ) diff --git a/pkgs/test/dotnet/default.nix b/pkgs/test/dotnet/default.nix index d70850c05fdb..72c2f5a64def 100644 --- a/pkgs/test/dotnet/default.nix +++ b/pkgs/test/dotnet/default.nix @@ -4,4 +4,5 @@ project-references = callPackage ./project-references { }; use-dotnet-from-env = lib.recurseIntoAttrs (callPackage ./use-dotnet-from-env { }); structured-attrs = lib.recurseIntoAttrs (callPackage ./structured-attrs { }); + final-attrs = lib.recurseIntoAttrs (callPackage ./final-attrs { }); } diff --git a/pkgs/test/dotnet/final-attrs/default.nix b/pkgs/test/dotnet/final-attrs/default.nix new file mode 100644 index 000000000000..cfc476361157 --- /dev/null +++ b/pkgs/test/dotnet/final-attrs/default.nix @@ -0,0 +1,76 @@ +{ + lib, + dotnet-sdk, + buildPackages, # buildDotnetModule + testers, + runCommand, +}: +let + copyrightString = "Original Copyright"; + originalCopyright = builtins.toFile "original-copyright.txt" copyrightString; + overridenCopyright = builtins.toFile "overridden-copyright.txt" ( + copyrightString + " with override!" + ); + + inherit (buildPackages) buildDotnetModule; + + app-recursive = buildDotnetModule (finalAttrs: { + name = "final-attrs-rec-test-application"; + src = ../structured-attrs/src; + nugetDeps = ../structured-attrs/nuget-deps.nix; + dotnetFlags = [ "--property:Copyright=${finalAttrs.passthru.copyrightString}" ]; + env.TargetFramework = "net${lib.versions.majorMinor (lib.getVersion dotnet-sdk)}"; + __structuredAttrs = true; + passthru = { + inherit copyrightString; + }; + }); + + app-const = buildDotnetModule { + name = "final-attrs-const-test-application"; + src = ../structured-attrs/src; + nugetDeps = ../structured-attrs/nuget-deps.nix; + dotnetFlags = [ "--property:Copyright=${copyrightString}" ]; + env.TargetFramework = "net${lib.versions.majorMinor (lib.getVersion dotnet-sdk)}"; + __structuredAttrs = true; + passthru = { + inherit copyrightString; + }; + }; + + override = + app: + app.overrideAttrs (previousAttrs: { + passthru = previousAttrs.passthru // { + copyrightString = previousAttrs.passthru.copyrightString + " with override!"; + }; + }); + + run = + name: app: + runCommand name { } '' + ${app}/bin/Application >"$out" + ''; +in +{ + check-output = testers.testEqualContents { + assertion = "buildDotnetModule produces the expected output when called with a recursive function"; + expected = originalCopyright; + actual = run "dotnet-final-attrs-test-rec-output" app-recursive; + }; + output-matches-const = testers.testEqualContents { + assertion = "buildDotnetModule produces the same output when called with attrs or a recursive function"; + expected = run "dotnet-final-attrs-test-const" app-const; + actual = run "dotnet-final-attrs-test-rec" app-recursive; + }; + override-has-no-effect = testers.testEqualContents { + assertion = "buildDotnetModule produces the expected output when called with a recursive function"; + expected = originalCopyright; + actual = run "dotnet-final-attrs-test-override-const-output" (override app-const); + }; + override-modifies-output = testers.testEqualContents { + assertion = "buildDotnetModule produces the expected output when called with a recursive function"; + expected = overridenCopyright; + actual = run "dotnet-final-attrs-test-override-rec-output" (override app-recursive); + }; +}