Merge pull request #139222 from IvarWithoutBones/init/buildDotnet

buildDotnetModule: init

authored by

Kevin Cox and committed by
GitHub
f6e161d6 f8d52833

+216 -136
+36 -2
doc/languages-frameworks/dotnet.section.md
··· 70 70 71 71 ## Packaging a Dotnet Application {#packaging-a-dotnet-application} 72 72 73 - Ideally, we would like to build against the sdk, then only have the dotnet runtime available in the runtime closure. 73 + To package Dotnet applications, you can use `buildDotnetModule`. This has similar arguments to `stdenv.mkDerivation`, with the following additions: 74 + 75 + * `projectFile` has to be used for specifying the dotnet project file relative to the source root. These usually have `.sln` or `.csproj` file extensions. 76 + * `nugetDeps` has to be used to specify the NuGet dependency file. Unfortunately, these cannot be deterministically fetched without a lockfile. This file should be generated using `nuget-to-nix` tool, which is available in nixpkgs. 77 + * `executables` is used to specify which executables get wrapped to `$out/bin`, relative to `$out/lib/$pname`. If this is unset, all executables generated will get installed. If you do not want to install any, set this to `[]`. 78 + * `runtimeDeps` is used to wrap libraries into `LD_LIBRARY_PATH`. This is how dotnet usually handles runtime dependencies. 79 + * `buildType` is used to change the type of build. Possible values are `Release`, `Debug`, etc. By default, this is set to `Release`. 80 + * `dotnet-sdk` is useful in cases where you need to change what dotnet SDK is being used. 81 + * `dotnet-runtime` is useful in cases where you need to change what dotnet runtime is being used. 82 + * `dotnetRestoreFlags` can be used to pass flags to `dotnet restore`. 83 + * `dotnetBuildFlags` can be used to pass flags to `dotnet build`. 84 + * `dotnetInstallFlags` can be used to pass flags to `dotnet install`. 85 + * `dotnetFlags` can be used to pass flags to all of the above phases. 86 + 87 + Here is an example `default.nix`, using some of the previously discussed arguments: 88 + ```nix 89 + { lib, buildDotnetModule, dotnetCorePackages, ffmpeg }: 74 90 75 - TODO: Create closure-friendly way to package dotnet applications 91 + buildDotnetModule rec { 92 + pname = "someDotnetApplication"; 93 + version = "0.1"; 94 + 95 + src = ./.; 96 + 97 + projectFile = "src/project.sln"; 98 + nugetDeps = ./deps.nix; # File generated with `nuget-to-nix path/to/src > deps.nix`. 99 + 100 + dotnet-sdk = dotnetCorePackages.sdk_3_1; 101 + dotnet-runtime = dotnetCorePackages.net_5_0; 102 + dotnetFlags = [ "--runtime linux-x64" ]; 103 + 104 + executables = [ "foo" ]; # This wraps "$out/lib/$pname/foo" to `$out/bin/foo`. 105 + executables = []; # Don't install any executables. 106 + 107 + runtimeDeps = [ ffmpeg ]; # This will wrap ffmpeg's library path into `LD_LIBRARY_PATH`. 108 + } 109 + ```
+1
doc/languages-frameworks/index.xml
··· 12 12 <xi:include href="coq.section.xml" /> 13 13 <xi:include href="crystal.section.xml" /> 14 14 <xi:include href="dhall.section.xml" /> 15 + <xi:include href="dotnet.section.xml" /> 15 16 <xi:include href="emscripten.section.xml" /> 16 17 <xi:include href="gnome.section.xml" /> 17 18 <xi:include href="go.section.xml" />
+144
pkgs/build-support/build-dotnet-module/default.nix
··· 1 + { lib, stdenv, makeWrapper, dotnetCorePackages, dotnetPackages, cacert, linkFarmFromDrvs, fetchurl }: 2 + 3 + { name ? "${args.pname}-${args.version}" 4 + , enableParallelBuilding ? true 5 + # Flags to pass to `makeWrapper`. This is done to avoid double wrapping. 6 + , makeWrapperArgs ? [] 7 + 8 + # Flags to pass to `dotnet restore`. 9 + , dotnetRestoreFlags ? [] 10 + # Flags to pass to `dotnet build`. 11 + , dotnetBuildFlags ? [] 12 + # Flags to pass to `dotnet install`. 13 + , dotnetInstallFlags ? [] 14 + # Flags to pass to dotnet in all phases. 15 + , dotnetFlags ? [] 16 + 17 + # The binaries that should get installed to `$out/bin`, relative to `$out/lib/$pname/`. These get wrapped accordingly. 18 + # Unfortunately, dotnet has no method for doing this automatically. 19 + # If unset, all executables in the projects root will get installed. This may cause bloat! 20 + , executables ? null 21 + # The packages project file, which contains instructions on how to compile it. 22 + , projectFile ? null 23 + # The NuGet dependency file. This locks all NuGet dependency versions, as otherwise they cannot be deterministically fetched. 24 + # This can be generated using the `nuget-to-nix` tool. 25 + , nugetDeps ? null 26 + # Libraries that need to be available at runtime should be passed through this. 27 + # These get wrapped into `LD_LIBRARY_PATH`. 28 + , runtimeDeps ? [] 29 + 30 + # The type of build to perform. This is passed to `dotnet` with the `--configuration` flag. Possible values are `Release`, `Debug`, etc. 31 + , buildType ? "Release" 32 + # The dotnet SDK to use. 33 + , dotnet-sdk ? dotnetCorePackages.sdk_5_0 34 + # The dotnet runtime to use. 35 + , dotnet-runtime ? dotnetCorePackages.net_5_0 36 + , ... } @ args: 37 + 38 + assert projectFile == null -> throw "Defining the `projectFile` attribute is required. This is usually an `.csproj`, or `.sln` file."; 39 + 40 + # TODO: Automatically generate a dependency file when a lockfile is present. 41 + # This file is unfortunately almost never present, as Microsoft recommands not to push this in upstream repositories. 42 + assert nugetDeps == null -> throw "Defining the `nugetDeps` attribute is required, as to lock the NuGet dependencies. This file can be generated using the `nuget-to-nix` tool."; 43 + 44 + let 45 + _nugetDeps = linkFarmFromDrvs "${name}-nuget-deps" (import nugetDeps { 46 + fetchNuGet = { name, version, sha256 }: fetchurl { 47 + name = "nuget-${name}-${version}.nupkg"; 48 + url = "https://www.nuget.org/api/v2/package/${name}/${version}"; 49 + inherit sha256; 50 + }; 51 + }); 52 + 53 + package = stdenv.mkDerivation (args // { 54 + nativeBuildInputs = args.nativeBuildInputs or [] ++ [ dotnet-sdk dotnetPackages.Nuget cacert makeWrapper ]; 55 + 56 + # Stripping breaks the executable 57 + dontStrip = true; 58 + 59 + DOTNET_NOLOGO = true; # This disables the welcome message. 60 + DOTNET_CLI_TELEMETRY_OPTOUT = true; 61 + 62 + configurePhase = args.configurePhase or '' 63 + runHook preConfigure 64 + 65 + export HOME=$(mktemp -d) 66 + 67 + nuget sources Add -Name nixos -Source "$PWD/nixos" 68 + nuget init "${_nugetDeps}" "$PWD/nixos" 69 + 70 + # This is required due to https://github.com/NuGet/Home/issues/4413. 71 + mkdir -p $HOME/.nuget/NuGet 72 + cp $HOME/.config/NuGet/NuGet.Config $HOME/.nuget/NuGet 73 + 74 + dotnet restore ${lib.escapeShellArg projectFile} \ 75 + ${lib.optionalString (!enableParallelBuilding) "--disable-parallel"} \ 76 + -p:ContinuousIntegrationBuild=true \ 77 + -p:Deterministic=true \ 78 + --source "$PWD/nixos" \ 79 + "''${dotnetRestoreFlags[@]}" \ 80 + "''${dotnetFlags[@]}" 81 + 82 + runHook postConfigure 83 + ''; 84 + 85 + buildPhase = args.buildPhase or '' 86 + runHook preBuild 87 + 88 + dotnet build ${lib.escapeShellArg projectFile} \ 89 + -maxcpucount:${if enableParallelBuilding then "$NIX_BUILD_CORES" else "1"} \ 90 + -p:BuildInParallel=${if enableParallelBuilding then "true" else "false"} \ 91 + -p:ContinuousIntegrationBuild=true \ 92 + -p:Deterministic=true \ 93 + -p:Version=${args.version} \ 94 + --configuration ${buildType} \ 95 + --no-restore \ 96 + "''${dotnetBuildFlags[@]}" \ 97 + "''${dotnetFlags[@]}" 98 + 99 + runHook postBuild 100 + ''; 101 + 102 + installPhase = args.installPhase or '' 103 + runHook preInstall 104 + 105 + dotnet publish ${lib.escapeShellArg projectFile} \ 106 + -p:ContinuousIntegrationBuild=true \ 107 + -p:Deterministic=true \ 108 + --output $out/lib/${args.pname} \ 109 + --configuration ${buildType} \ 110 + --no-build \ 111 + --no-self-contained \ 112 + "''${dotnetInstallFlags[@]}" \ 113 + "''${dotnetFlags[@]}" 114 + '' + (if executables != null then '' 115 + for executable in ''${executables}; do 116 + execPath="$out/lib/${args.pname}/$executable" 117 + 118 + if [[ -f "$execPath" && -x "$execPath" ]]; then 119 + makeWrapper "$execPath" "$out/bin/$(basename "$executable")" \ 120 + --set DOTNET_ROOT "${dotnet-runtime}" \ 121 + --suffix LD_LIBRARY_PATH : "${lib.makeLibraryPath runtimeDeps}" \ 122 + "''${gappsWrapperArgs[@]}" \ 123 + ''${makeWrapperArgs} 124 + else 125 + echo "Specified binary \"$executable\" is either not an executable, or does not exist!" 126 + exit 1 127 + fi 128 + done 129 + '' else '' 130 + for executable in $out/lib/${args.pname}/*; do 131 + if [[ -f "$executable" && -x "$executable" && "$executable" != *"dll"* ]]; then 132 + makeWrapper "$executable" "$out/bin/$(basename "$executable")" \ 133 + --set DOTNET_ROOT "${dotnet-runtime}" \ 134 + --suffix LD_LIBRARY_PATH : "${lib.makeLibraryPath runtimeDeps}" \ 135 + "''${gappsWrapperArgs[@]}" \ 136 + ''${makeWrapperArgs} 137 + fi 138 + done 139 + '') + '' 140 + runHook postInstall 141 + ''; 142 + }); 143 + in 144 + package
+22 -70
pkgs/misc/emulators/ryujinx/default.nix
··· 1 - { lib, stdenv, fetchFromGitHub, fetchurl, makeWrapper, makeDesktopItem, linkFarmFromDrvs 2 - , dotnet-sdk_5, dotnetPackages, dotnetCorePackages, cacert 1 + { lib, buildDotnetModule, fetchFromGitHub, makeDesktopItem 3 2 , libX11, libgdiplus, ffmpeg 4 3 , SDL2_mixer, openal, libsoundio, sndio, pulseaudio 5 4 , gtk3, gobject-introspection, gdk-pixbuf, wrapGAppsHook 6 5 }: 7 6 8 - let 9 - runtimeDeps = [ 10 - gtk3 11 - libX11 12 - libgdiplus 13 - ffmpeg 14 - SDL2_mixer 15 - openal 16 - libsoundio 17 - sndio 18 - pulseaudio 19 - ]; 20 - in stdenv.mkDerivation rec { 7 + buildDotnetModule rec { 21 8 pname = "ryujinx"; 22 9 version = "1.0.7058"; # Versioning is based off of the official appveyor builds: https://ci.appveyor.com/project/gdkchan/ryujinx 23 10 ··· 28 15 sha256 = "1lsg4v15x8i43pwkgn4y8d2m95m6w7izwm4zhspnq8r2lv18lqb2"; 29 16 }; 30 17 31 - nativeBuildInputs = [ dotnet-sdk_5 dotnetPackages.Nuget cacert makeWrapper wrapGAppsHook gobject-introspection gdk-pixbuf ]; 18 + projectFile = "Ryujinx.sln"; 19 + executables = [ "Ryujinx" ]; 20 + nugetDeps = ./deps.nix; 32 21 33 - nugetDeps = linkFarmFromDrvs "${pname}-nuget-deps" (import ./deps.nix { 34 - fetchNuGet = { name, version, sha256 }: fetchurl { 35 - name = "nuget-${name}-${version}.nupkg"; 36 - url = "https://www.nuget.org/api/v2/package/${name}/${version}"; 37 - inherit sha256; 38 - }; 39 - }); 22 + nativeBuildInputs = [ wrapGAppsHook gobject-introspection gdk-pixbuf ]; 23 + runtimeDeps = [ 24 + gtk3 25 + libX11 26 + libgdiplus 27 + ffmpeg 28 + SDL2_mixer 29 + openal 30 + libsoundio 31 + sndio 32 + pulseaudio 33 + ]; 40 34 41 35 patches = [ 42 36 ./log.patch # Without this, Ryujinx attempts to write logs to the nix store. This patch makes it write to "~/.config/Ryujinx/Logs" on Linux. 43 37 ]; 44 38 45 - configurePhase = '' 46 - runHook preConfigure 47 - 48 - export HOME=$(mktemp -d) 49 - export DOTNET_CLI_TELEMETRY_OPTOUT=1 50 - export DOTNET_NOLOGO=1 51 - 52 - nuget sources Add -Name nixos -Source "$PWD/nixos" 53 - nuget init "$nugetDeps" "$PWD/nixos" 54 - 55 - # FIXME: https://github.com/NuGet/Home/issues/4413 56 - mkdir -p $HOME/.nuget/NuGet 57 - cp $HOME/.config/NuGet/NuGet.Config $HOME/.nuget/NuGet 58 - 59 - dotnet restore --source "$PWD/nixos" Ryujinx.sln 60 - 61 - runHook postConfigure 62 - ''; 63 - 64 - buildPhase = '' 65 - runHook preBuild 66 - dotnet build Ryujinx.sln \ 67 - --no-restore \ 68 - --configuration Release \ 69 - -p:Version=${version} 70 - runHook postBuild 71 - ''; 72 - 73 - installPhase = '' 74 - runHook preInstall 75 - 76 - dotnet publish Ryujinx.sln \ 77 - --no-build \ 78 - --configuration Release \ 79 - --no-self-contained \ 80 - --output $out/lib/ryujinx 81 - shopt -s extglob 82 - 39 + preInstall = '' 83 40 # TODO: fix this hack https://github.com/Ryujinx/Ryujinx/issues/2349 84 41 mkdir -p $out/lib/sndio-6 85 42 ln -s ${sndio}/lib/libsndio.so $out/lib/sndio-6/libsndio.so.6 86 43 87 - makeWrapper $out/lib/ryujinx/Ryujinx $out/bin/Ryujinx \ 88 - --set DOTNET_ROOT "${dotnetCorePackages.net_5_0}" \ 89 - --suffix LD_LIBRARY_PATH : "${builtins.concatStringsSep ":" [ (lib.makeLibraryPath runtimeDeps) "$out/lib/sndio-6" ]}" \ 90 - ''${gappsWrapperArgs[@]} 44 + makeWrapperArgs+=( 45 + --suffix LD_LIBRARY_PATH : "$out/lib/sndio-6" 46 + ) 91 47 92 48 for i in 16 32 48 64 96 128 256 512 1024; do 93 49 install -D ${src}/Ryujinx/Ui/Resources/Logo_Ryujinx.png $out/share/icons/hicolor/''${i}x$i/apps/ryujinx.png 94 50 done 51 + 95 52 cp -r ${makeDesktopItem { 96 53 desktopName = "Ryujinx"; 97 54 name = "ryujinx"; ··· 101 58 type = "Application"; 102 59 categories = "Game;"; 103 60 }}/share/applications $out/share 104 - 105 - runHook postInstall 106 61 ''; 107 - 108 - # Strip breaks the executable. 109 - dontStrip = true; 110 62 111 63 meta = with lib; { 112 64 description = "Experimental Nintendo Switch Emulator written in C#";
+12 -64
pkgs/tools/backup/discordchatexporter-cli/default.nix
··· 1 - { lib, stdenv, fetchFromGitHub, fetchurl, linkFarmFromDrvs, makeWrapper, autoPatchelfHook 2 - , dotnet-sdk_5, dotnetPackages, dotnetCorePackages, cacert 1 + { lib 2 + , stdenv 3 + , buildDotnetModule 4 + , fetchFromGitHub 5 + , autoPatchelfHook 6 + , dotnetCorePackages 3 7 }: 4 8 5 - let 6 - projectFile = "DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj"; 7 - in 8 - stdenv.mkDerivation rec { 9 + buildDotnetModule rec { 9 10 pname = "discordchatexporter-cli"; 10 11 version = "2.30.1"; 11 12 ··· 16 17 sha256 = "JSYIhd+DNVOKseHtWNNChECR5hKr+ntu1Yyqtnlg8rM="; 17 18 }; 18 19 19 - nativeBuildInputs = [ dotnet-sdk_5 dotnetPackages.Nuget cacert makeWrapper autoPatchelfHook ]; 20 - buildInputs = [ stdenv.cc.cc.lib ]; 21 - 22 - nugetDeps = linkFarmFromDrvs "${pname}-nuget-deps" (import ./deps.nix { 23 - fetchNuGet = { name, version, sha256 }: fetchurl { 24 - name = "nuget-${name}-${version}.nupkg"; 25 - url = "https://www.nuget.org/api/v2/package/${name}/${version}"; 26 - inherit sha256; 27 - }; 28 - }); 29 - 30 - configurePhase = '' 31 - runHook preConfigure 32 - 33 - export HOME=$(mktemp -d) 34 - export DOTNET_CLI_TELEMETRY_OPTOUT=1 35 - export DOTNET_NOLOGO=1 36 - 37 - nuget sources Add -Name nixos -Source "$PWD/nixos" 38 - nuget init "$nugetDeps" "$PWD/nixos" 39 - 40 - # FIXME: https://github.com/NuGet/Home/issues/4413 41 - mkdir -p $HOME/.nuget/NuGet 42 - cp $HOME/.config/NuGet/NuGet.Config $HOME/.nuget/NuGet 43 - 44 - dotnet restore --source "$PWD/nixos" ${projectFile} 45 - 46 - runHook postConfigure 47 - ''; 48 - 49 - buildPhase = '' 50 - runHook preBuild 51 - 52 - dotnet build ${projectFile} \ 53 - --no-restore \ 54 - --configuration Release \ 55 - -p:Version=${version} 56 - 57 - runHook postBuild 58 - ''; 59 - 60 - installPhase = '' 61 - runHook preInstall 62 - 63 - dotnet publish ${projectFile} \ 64 - --no-build \ 65 - --configuration Release \ 66 - --no-self-contained \ 67 - --output $out/lib/${pname} 68 - shopt -s extglob 20 + projectFile = "DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj"; 21 + dotnet-runtime = dotnetCorePackages.netcore_3_1; 22 + nugetDeps = ./deps.nix; 69 23 70 - makeWrapper $out/lib/${pname}/DiscordChatExporter.Cli $out/bin/discordchatexporter-cli \ 71 - --set DOTNET_ROOT "${dotnetCorePackages.sdk_3_1}" 72 - 73 - runHook postInstall 74 - ''; 75 - 76 - # Strip breaks the executable. 77 - dontStrip = true; 24 + nativeBuildInputs = [ autoPatchelfHook ]; 25 + buildInputs = [ stdenv.cc.cc.lib ]; 78 26 79 27 meta = with lib; { 80 28 description = "A tool to export Discord chat logs to a file";
+1
pkgs/top-level/all-packages.nix
··· 605 605 606 606 fetchNuGet = callPackage ../build-support/fetchnuget { }; 607 607 buildDotnetPackage = callPackage ../build-support/build-dotnet-package { }; 608 + buildDotnetModule = callPackage ../build-support/build-dotnet-module { }; 608 609 nuget-to-nix = callPackage ../build-support/nuget-to-nix { }; 609 610 610 611 fetchgx = callPackage ../build-support/fetchgx { };