about summary refs log tree commit diff
path: root/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/default.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/default.nix')
-rw-r--r--nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/default.nix333
1 files changed, 333 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/default.nix b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/default.nix
new file mode 100644
index 000000000000..7b88b16064bc
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/default.nix
@@ -0,0 +1,333 @@
+{ lib
+, stdenvNoCC
+, callPackage
+, writeShellScript
+, srcOnly
+, linkFarmFromDrvs
+, symlinkJoin
+, makeWrapper
+, dotnetCorePackages
+, mkNugetSource
+, mkNugetDeps
+, nuget-to-nix
+, cacert
+, coreutils
+, runtimeShellPackage
+}:
+
+{ 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:
+  #     <ProjectReference Include="../foo/bar.fsproj" />
+  # To enable discovery through `projectReferences` you would need to add a line:
+  #     <ProjectReference Include="../foo/bar.fsproj" />
+  #     <PackageReference Include="bar" Version="*" Condition=" '$(ContinuousIntegrationBuild)'=='true' "/>
+, 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 enabledz
+, 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);
+
+  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;
+
+  localDeps =
+    if (projectReferences != [ ])
+    then linkFarmFromDrvs "${name}-project-references" projectReferences
+    else 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.";
+
+  # contains the actual package dependencies
+  dependenciesSource = mkNugetSource {
+    name = "${name}-dependencies-source";
+    description = "Nuget source with the dependencies for ${name}";
+    deps = [ _nugetDeps ] ++ lib.optional (localDeps != null) localDeps;
+  };
+
+  # this contains all the nuget packages that are implicitly referenced by the dotnet
+  # build system. having them as separate deps allows us to avoid having to regenerate
+  # a packages dependencies when the dotnet-sdk version changes
+  sdkDeps = lib.lists.flatten [ dotnet-sdk.packages ];
+
+  sdkSource = let
+    version = dotnet-sdk.version or (lib.concatStringsSep "-" dotnet-sdk.versions);
+  in mkNugetSource {
+    name = "dotnet-sdk-${version}-source";
+    deps = sdkDeps;
+  };
+
+  nuget-source = symlinkJoin {
+    name = "${name}-nuget-source";
+    paths = [ dependenciesSource sdkSource ];
+  };
+
+  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
+    dotnetCheckHook
+    dotnetInstallHook
+    dotnetFixupHook
+
+    cacert
+    makeWrapper
+    dotnet-sdk
+  ];
+
+  # 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 [ ]));
+
+  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 = {
+    inherit nuget-source;
+  } // lib.optionalAttrs (!lib.isDerivation nugetDeps) {
+    fetch-deps =
+      let
+        flags = dotnetFlags ++ dotnetRestoreFlags;
+        runtimeIds =
+          if runtimeId != null
+          then [ runtimeId ]
+          else map (system: dotnetCorePackages.systemToDotnetRid system) platforms;
+        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")'';
+      in
+      writeShellScript "fetch-${pname}-deps" ''
+        set -euo pipefail
+
+        export PATH="${lib.makeBinPath [ coreutils runtimeShellPackage dotnet-sdk (nuget-to-nix.override { inherit dotnet-sdk; }) ]}"
+
+        for arg in "$@"; do
+            case "$arg" in
+                --keep-sources|-k)
+                    keepSources=1
+                    shift
+                    ;;
+                --help|-h)
+                    echo "usage: $0 [--keep-sources] [--help] <output path>"
+                    echo "    <output path>   The path to write the lockfile to. A temporary file is used if this is not set"
+                    echo "    --keep-sources  Dont remove temporary directories upon exit, useful for debugging"
+                    echo "    --help          Show this help message"
+                    exit
+                    ;;
+            esac
+        done
+
+        if [[ ''${TMPDIR:-} == /run/user/* ]]; then
+           # /run/user is usually a tmpfs in RAM, which may be too small
+           # to store all downloaded dotnet packages
+           unset TMPDIR
+        fi
+
+        export tmp=$(mktemp -td "deps-${pname}-XXXXXX")
+        HOME=$tmp/home
+
+        exitTrap() {
+            test -n "''${ranTrap-}" && return
+            ranTrap=1
+
+            if test -n "''${keepSources-}"; then
+                echo -e "Path to the source: $tmp/src\nPath to the fake home: $tmp/home"
+            else
+                rm -rf "$tmp"
+            fi
+
+            # Since mktemp is used this will be empty if the script didnt succesfully complete
+            if ! test -s "$depsFile"; then
+              rm -rf "$depsFile"
+            fi
+        }
+
+        trap exitTrap EXIT INT TERM
+
+        dotnetRestore() {
+            local -r project="''${1-}"
+            local -r rid="$2"
+
+            dotnet restore ''${project-} \
+                -p:ContinuousIntegrationBuild=true \
+                -p:Deterministic=true \
+                --packages "$tmp/nuget_pkgs" \
+                --runtime "$rid" \
+                --no-cache \
+                --force \
+                ${lib.optionalString (!enableParallelBuilding) "--disable-parallel"} \
+                ${lib.escapeShellArgs flags}
+        }
+
+        declare -a projectFiles=( ${lib.escapeShellArgs projectFiles} )
+        declare -a testProjectFiles=( ${lib.escapeShellArgs testProjectFiles} )
+
+        export DOTNET_NOLOGO=1
+        export DOTNET_CLI_TELEMETRY_OPTOUT=1
+
+        depsFile=$(realpath "''${1:-${defaultDepsFile}}")
+        echo Will write lockfile to "$depsFile"
+        mkdir -p "$tmp/nuget_pkgs"
+
+        storeSrc="${srcOnly args}"
+        src=$tmp/src
+        cp -rT "$storeSrc" "$src"
+        chmod -R +w "$src"
+
+        cd "$src"
+        echo "Restoring project..."
+
+        ${dotnet-sdk}/bin/dotnet tool restore
+        cp -r $HOME/.nuget/packages/* $tmp/nuget_pkgs || true
+
+        for rid in "${lib.concatStringsSep "\" \"" runtimeIds}"; do
+            (( ''${#projectFiles[@]} == 0 )) && dotnetRestore "" "$rid"
+
+            for project in ''${projectFiles[@]-} ''${testProjectFiles[@]-}; do
+                dotnetRestore "$project" "$rid"
+            done
+        done
+        # Second copy, makes sure packages restored by ie. paket are included
+        cp -r $HOME/.nuget/packages/* $tmp/nuget_pkgs || true
+
+        echo "Succesfully restored project"
+
+        echo "Writing lockfile..."
+
+        excluded_sources="${lib.concatStringsSep " " sdkDeps}"
+        for excluded_source in ''${excluded_sources[@]}; do
+          ls "$excluded_source" >> "$tmp/excluded_list"
+        done
+        tmpFile="$tmp"/deps.nix
+        echo -e "# This file was automatically generated by passthru.fetch-deps.\n# Please dont edit it manually, your changes might get overwritten!\n" > "$tmpFile"
+        nuget-to-nix "$tmp/nuget_pkgs" "$tmp/excluded_list" >> "$tmpFile"
+        mv "$tmpFile" "$depsFile"
+        echo "Succesfully wrote lockfile to $depsFile"
+      '';
+  } // args.passthru or { };
+
+  meta = (args.meta or { }) // { inherit platforms; };
+})