From b41e7b572ca46d906cdf976c0db1c924e63bc6ab Mon Sep 17 00:00:00 2001 From: IvarWithoutBones Date: Tue, 27 Jul 2021 00:46:18 +0200 Subject: [PATCH 1/4] buildDotnetModule: init --- .../build-dotnet-module/default.nix | 144 ++++++++++++++++++ pkgs/top-level/all-packages.nix | 1 + 2 files changed, 145 insertions(+) create mode 100644 pkgs/build-support/build-dotnet-module/default.nix diff --git a/pkgs/build-support/build-dotnet-module/default.nix b/pkgs/build-support/build-dotnet-module/default.nix new file mode 100644 index 000000000000..0c1a1f686e7e --- /dev/null +++ b/pkgs/build-support/build-dotnet-module/default.nix @@ -0,0 +1,144 @@ +{ lib, stdenv, makeWrapper, dotnetCorePackages, dotnetPackages, cacert, linkFarmFromDrvs, fetchurl }: + +{ name ? "${args.pname}-${args.version}" +, enableParallelBuilding ? true +# 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 install`. +, dotnetInstallFlags ? [] +# Flags to pass to dotnet in all phases. +, dotnetFlags ? [] + +# The binaries that should get installed to `$out/bin`, relative to `$out/lib/$pname/`. 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 +# The packages project file, which contains instructions on how to compile it. +, projectFile ? null +# The NuGet dependency file. This locks all NuGet dependency versions, as otherwise they cannot be deterministically fetched. +# This can be generated using the `nuget-to-nix` tool. +, nugetDeps ? null +# Libraries that need to be available at runtime should be passed through this. +# These get wrapped into `LD_LIBRARY_PATH`. +, runtimeDeps ? [] + +# The type of build to perform. This is passed to `dotnet` with the `--configuration` flag. Possible values are `Release`, `Debug`, etc. +, buildType ? "Release" +# The dotnet SDK to use. +, dotnet-sdk ? dotnetCorePackages.sdk_5_0 +# The dotnet runtime to use. +, dotnet-runtime ? dotnetCorePackages.net_5_0 +, ... } @ args: + +assert projectFile == null -> throw "Defining the `projectFile` attribute is required. This is usually an `.csproj`, or `.sln` file."; + +# TODO: Automatically generate a dependency file when a lockfile is present. +# This file is unfortunately almost never present, as Microsoft recommands not to push this in upstream repositories. +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."; + +let + _nugetDeps = linkFarmFromDrvs "${name}-nuget-deps" (import nugetDeps { + fetchNuGet = { name, version, sha256 }: fetchurl { + name = "nuget-${name}-${version}.nupkg"; + url = "https://www.nuget.org/api/v2/package/${name}/${version}"; + inherit sha256; + }; + }); + + package = stdenv.mkDerivation (args // { + nativeBuildInputs = args.nativeBuildInputs or [] ++ [ dotnet-sdk dotnetPackages.Nuget cacert makeWrapper ]; + + # Stripping breaks the executable + dontStrip = true; + + DOTNET_NOLOGO = true; # This disables the welcome message. + DOTNET_CLI_TELEMETRY_OPTOUT = true; + + configurePhase = args.configurePhase or '' + runHook preConfigure + + export HOME=$(mktemp -d) + + nuget sources Add -Name nixos -Source "$PWD/nixos" + nuget init "${_nugetDeps}" "$PWD/nixos" + + # This is required due to https://github.com/NuGet/Home/issues/4413. + mkdir -p $HOME/.nuget/NuGet + cp $HOME/.config/NuGet/NuGet.Config $HOME/.nuget/NuGet + + dotnet restore ${lib.escapeShellArg projectFile} \ + ${lib.optionalString (!enableParallelBuilding) "--disable-parallel"} \ + -p:ContinuousIntegrationBuild=true \ + -p:Deterministic=true \ + --source "$PWD/nixos" \ + "''${dotnetRestoreFlags[@]}" \ + "''${dotnetFlags[@]}" + + runHook postConfigure + ''; + + buildPhase = args.buildPhase or '' + runHook preBuild + + dotnet build ${lib.escapeShellArg projectFile} \ + -maxcpucount:${if enableParallelBuilding then "$NIX_BUILD_CORES" else "1"} \ + -p:BuildInParallel=${if enableParallelBuilding then "true" else "false"} \ + -p:ContinuousIntegrationBuild=true \ + -p:Deterministic=true \ + -p:Version=${args.version} \ + --configuration ${buildType} \ + --no-restore \ + "''${dotnetBuildFlags[@]}" \ + "''${dotnetFlags[@]}" + + runHook postBuild + ''; + + installPhase = args.installPhase or '' + runHook preInstall + + dotnet publish ${lib.escapeShellArg projectFile} \ + -p:ContinuousIntegrationBuild=true \ + -p:Deterministic=true \ + --output $out/lib/${args.pname} \ + --configuration ${buildType} \ + --no-build \ + --no-self-contained \ + "''${dotnetInstallFlags[@]}" \ + "''${dotnetFlags[@]}" + '' + (if executables != null then '' + for executable in ''${executables}; do + execPath="$out/lib/${args.pname}/$executable" + + if [[ -f "$execPath" && -x "$execPath" ]]; then + makeWrapper "$execPath" "$out/bin/$(basename "$executable")" \ + --set DOTNET_ROOT "${dotnet-runtime}" \ + --suffix LD_LIBRARY_PATH : "${lib.makeLibraryPath runtimeDeps}" \ + "''${gappsWrapperArgs[@]}" \ + ''${makeWrapperArgs} + else + echo "Specified binary \"$executable\" is either not an executable, or does not exist!" + exit 1 + fi + done + '' else '' + for executable in $out/lib/${args.pname}/*; do + if [[ -f "$executable" && -x "$executable" && "$executable" != *"dll"* ]]; then + makeWrapper "$executable" "$out/bin/$(basename "$executable")" \ + --set DOTNET_ROOT "${dotnet-runtime}" \ + --suffix LD_LIBRARY_PATH : "${lib.makeLibraryPath runtimeDeps}" \ + "''${gappsWrapperArgs[@]}" \ + ''${makeWrapperArgs} + fi + done + '') + '' + runHook postInstall + ''; + }); +in + package diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 2361851b5017..9b1e82673375 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -593,6 +593,7 @@ with pkgs; fetchNuGet = callPackage ../build-support/fetchnuget { }; buildDotnetPackage = callPackage ../build-support/build-dotnet-package { }; + buildDotnetModule = callPackage ../build-support/build-dotnet-module { }; nuget-to-nix = callPackage ../build-support/nuget-to-nix { }; fetchgx = callPackage ../build-support/fetchgx { }; From a87e6645dd580baac85314fceb67431293054fec Mon Sep 17 00:00:00 2001 From: IvarWithoutBones Date: Thu, 23 Sep 2021 21:40:09 +0200 Subject: [PATCH 2/4] buildDotnetModule: add documentation --- doc/languages-frameworks/dotnet.section.md | 38 ++++++++++++++++++++-- doc/languages-frameworks/index.xml | 1 + 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/doc/languages-frameworks/dotnet.section.md b/doc/languages-frameworks/dotnet.section.md index 1bcb6e45210e..738d7bad271e 100644 --- a/doc/languages-frameworks/dotnet.section.md +++ b/doc/languages-frameworks/dotnet.section.md @@ -70,6 +70,40 @@ The `dotnetCorePackages.sdk` contains both a runtime and the full sdk of a given ## Packaging a Dotnet Application {#packaging-a-dotnet-application} -Ideally, we would like to build against the sdk, then only have the dotnet runtime available in the runtime closure. +To package Dotnet applications, you can use `buildDotnetModule`. This has similar arguments to `stdenv.mkDerivation`, with the following additions: -TODO: Create closure-friendly way to package dotnet applications +* `projectFile` has to be used for specifying the dotnet project file relative to the source root. These usually have `.sln` or `.csproj` file extensions. +* `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. +* `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 `[]`. +* `runtimeDeps` is used to wrap libraries into `LD_LIBRARY_PATH`. This is how dotnet usually handles runtime dependencies. +* `buildType` is used to change the type of build. Possible values are `Release`, `Debug`, etc. By default, this is set to `Release`. +* `dotnet-sdk` is useful in cases where you need to change what dotnet SDK is being used. +* `dotnet-runtime` is useful in cases where you need to change what dotnet runtime is being used. +* `dotnetRestoreFlags` can be used to pass flags to `dotnet restore`. +* `dotnetBuildFlags` can be used to pass flags to `dotnet build`. +* `dotnetInstallFlags` can be used to pass flags to `dotnet install`. +* `dotnetFlags` can be used to pass flags to all of the above phases. + +Here is an example `default.nix`, using some of the previously discussed arguments: +```nix +{ lib, buildDotnetModule, dotnetCorePackages, ffmpeg }: + +buildDotnetModule rec { + pname = "someDotnetApplication"; + version = "0.1"; + + src = ./.; + + projectFile = "src/project.sln"; + nugetDeps = ./deps.nix; # File generated with `nuget-to-nix path/to/src > deps.nix`. + + dotnet-sdk = dotnetCorePackages.sdk_3_1; + dotnet-runtime = dotnetCorePackages.net_5_0; + dotnetFlags = [ "--runtime linux-x64" ]; + + executables = [ "foo" ]; # This wraps "$out/lib/$pname/foo" to `$out/bin/foo`. + executables = []; # Don't install any executables. + + runtimeDeps = [ ffmpeg ]; # This will wrap ffmpeg's library path into `LD_LIBRARY_PATH`. +} +``` diff --git a/doc/languages-frameworks/index.xml b/doc/languages-frameworks/index.xml index b010f27cac02..f221693e764c 100644 --- a/doc/languages-frameworks/index.xml +++ b/doc/languages-frameworks/index.xml @@ -12,6 +12,7 @@ + From 84cb383424277e1080742a94aedae1c86fe1b087 Mon Sep 17 00:00:00 2001 From: IvarWithoutBones Date: Thu, 19 Aug 2021 17:10:41 +0200 Subject: [PATCH 3/4] ryujinx: use buildDotnetModule --- pkgs/misc/emulators/ryujinx/default.nix | 92 ++++++------------------- 1 file changed, 22 insertions(+), 70 deletions(-) diff --git a/pkgs/misc/emulators/ryujinx/default.nix b/pkgs/misc/emulators/ryujinx/default.nix index 856a56f5f5eb..05c57a7fe1d7 100644 --- a/pkgs/misc/emulators/ryujinx/default.nix +++ b/pkgs/misc/emulators/ryujinx/default.nix @@ -1,11 +1,25 @@ -{ lib, stdenv, fetchFromGitHub, fetchurl, makeWrapper, makeDesktopItem, linkFarmFromDrvs -, dotnet-sdk_5, dotnetPackages, dotnetCorePackages, cacert +{ lib, buildDotnetModule, fetchFromGitHub, makeDesktopItem , libX11, libgdiplus, ffmpeg , SDL2_mixer, openal, libsoundio, sndio, pulseaudio , gtk3, gobject-introspection, gdk-pixbuf, wrapGAppsHook }: -let +buildDotnetModule rec { + pname = "ryujinx"; + version = "1.0.6954"; # Versioning is based off of the official appveyor builds: https://ci.appveyor.com/project/gdkchan/ryujinx + + src = fetchFromGitHub { + owner = "Ryujinx"; + repo = "Ryujinx"; + rev = "31cbd09a75a9d5f4814c3907a060e0961eb2bb15"; + sha256 = "00qql0wmlzs722s0igip3v0yjlqhc31jcr7nghwibcqrmx031azk"; + }; + + projectFile = "Ryujinx.sln"; + executables = [ "Ryujinx" ]; + nugetDeps = ./deps.nix; + + nativeBuildInputs = [ wrapGAppsHook gobject-introspection gdk-pixbuf ]; runtimeDeps = [ gtk3 libX11 @@ -17,81 +31,24 @@ let sndio pulseaudio ]; -in stdenv.mkDerivation rec { - pname = "ryujinx"; - version = "1.0.6954"; # Versioning is based off of the official appveyor builds: https://ci.appveyor.com/project/gdkchan/ryujinx - - src = fetchFromGitHub { - owner = "Ryujinx"; - repo = "Ryujinx"; - rev = "31cbd09a75a9d5f4814c3907a060e0961eb2bb15"; - sha256 = "00qql0wmlzs722s0igip3v0yjlqhc31jcr7nghwibcqrmx031azk"; - }; - - nativeBuildInputs = [ dotnet-sdk_5 dotnetPackages.Nuget cacert makeWrapper wrapGAppsHook gobject-introspection gdk-pixbuf ]; - - nugetDeps = linkFarmFromDrvs "${pname}-nuget-deps" (import ./deps.nix { - fetchNuGet = { name, version, sha256 }: fetchurl { - name = "nuget-${name}-${version}.nupkg"; - url = "https://www.nuget.org/api/v2/package/${name}/${version}"; - inherit sha256; - }; - }); patches = [ ./log.patch # Without this, Ryujinx attempts to write logs to the nix store. This patch makes it write to "~/.config/Ryujinx/Logs" on Linux. ]; - configurePhase = '' - runHook preConfigure - - export HOME=$(mktemp -d) - export DOTNET_CLI_TELEMETRY_OPTOUT=1 - export DOTNET_NOLOGO=1 - - nuget sources Add -Name nixos -Source "$PWD/nixos" - nuget init "$nugetDeps" "$PWD/nixos" - - # FIXME: https://github.com/NuGet/Home/issues/4413 - mkdir -p $HOME/.nuget/NuGet - cp $HOME/.config/NuGet/NuGet.Config $HOME/.nuget/NuGet - - dotnet restore --source "$PWD/nixos" Ryujinx.sln - - runHook postConfigure - ''; - - buildPhase = '' - runHook preBuild - dotnet build Ryujinx.sln \ - --no-restore \ - --configuration Release \ - -p:Version=${version} - runHook postBuild - ''; - - installPhase = '' - runHook preInstall - - dotnet publish Ryujinx.sln \ - --no-build \ - --configuration Release \ - --no-self-contained \ - --output $out/lib/ryujinx - shopt -s extglob - + preInstall = '' # TODO: fix this hack https://github.com/Ryujinx/Ryujinx/issues/2349 mkdir -p $out/lib/sndio-6 ln -s ${sndio}/lib/libsndio.so $out/lib/sndio-6/libsndio.so.6 - makeWrapper $out/lib/ryujinx/Ryujinx $out/bin/Ryujinx \ - --set DOTNET_ROOT "${dotnetCorePackages.net_5_0}" \ - --suffix LD_LIBRARY_PATH : "${builtins.concatStringsSep ":" [ (lib.makeLibraryPath runtimeDeps) "$out/lib/sndio-6" ]}" \ - ''${gappsWrapperArgs[@]} + makeWrapperArgs+=( + --suffix LD_LIBRARY_PATH : "$out/lib/sndio-6" + ) for i in 16 32 48 64 96 128 256 512 1024; do install -D ${src}/Ryujinx/Ui/Resources/Logo_Ryujinx.png $out/share/icons/hicolor/''${i}x$i/apps/ryujinx.png done + cp -r ${makeDesktopItem { desktopName = "Ryujinx"; name = "ryujinx"; @@ -101,13 +58,8 @@ in stdenv.mkDerivation rec { type = "Application"; categories = "Game;"; }}/share/applications $out/share - - runHook postInstall ''; - # Strip breaks the executable. - dontStrip = true; - meta = with lib; { description = "Experimental Nintendo Switch Emulator written in C#"; homepage = "https://ryujinx.org/"; From e93d52e69eb438ca45ab8dc3b2bead4d18ade8fd Mon Sep 17 00:00:00 2001 From: IvarWithoutBones Date: Thu, 23 Sep 2021 19:44:06 +0200 Subject: [PATCH 4/4] discordchatexporter-cli: use buildDotnetModule --- .../discordchatexporter-cli/default.nix | 76 +++---------------- 1 file changed, 12 insertions(+), 64 deletions(-) diff --git a/pkgs/tools/backup/discordchatexporter-cli/default.nix b/pkgs/tools/backup/discordchatexporter-cli/default.nix index fe7b041b1533..60437403e5a2 100644 --- a/pkgs/tools/backup/discordchatexporter-cli/default.nix +++ b/pkgs/tools/backup/discordchatexporter-cli/default.nix @@ -1,11 +1,12 @@ -{ lib, stdenv, fetchFromGitHub, fetchurl, linkFarmFromDrvs, makeWrapper, autoPatchelfHook -, dotnet-sdk_5, dotnetPackages, dotnetCorePackages, cacert +{ lib +, stdenv +, buildDotnetModule +, fetchFromGitHub +, autoPatchelfHook +, dotnetCorePackages }: -let - projectFile = "DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj"; -in -stdenv.mkDerivation rec { +buildDotnetModule rec { pname = "discordchatexporter-cli"; version = "2.30.1"; @@ -16,66 +17,13 @@ stdenv.mkDerivation rec { sha256 = "JSYIhd+DNVOKseHtWNNChECR5hKr+ntu1Yyqtnlg8rM="; }; - nativeBuildInputs = [ dotnet-sdk_5 dotnetPackages.Nuget cacert makeWrapper autoPatchelfHook ]; + projectFile = "DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj"; + dotnet-runtime = dotnetCorePackages.netcore_3_1; + nugetDeps = ./deps.nix; + + nativeBuildInputs = [ autoPatchelfHook ]; buildInputs = [ stdenv.cc.cc.lib ]; - nugetDeps = linkFarmFromDrvs "${pname}-nuget-deps" (import ./deps.nix { - fetchNuGet = { name, version, sha256 }: fetchurl { - name = "nuget-${name}-${version}.nupkg"; - url = "https://www.nuget.org/api/v2/package/${name}/${version}"; - inherit sha256; - }; - }); - - configurePhase = '' - runHook preConfigure - - export HOME=$(mktemp -d) - export DOTNET_CLI_TELEMETRY_OPTOUT=1 - export DOTNET_NOLOGO=1 - - nuget sources Add -Name nixos -Source "$PWD/nixos" - nuget init "$nugetDeps" "$PWD/nixos" - - # FIXME: https://github.com/NuGet/Home/issues/4413 - mkdir -p $HOME/.nuget/NuGet - cp $HOME/.config/NuGet/NuGet.Config $HOME/.nuget/NuGet - - dotnet restore --source "$PWD/nixos" ${projectFile} - - runHook postConfigure - ''; - - buildPhase = '' - runHook preBuild - - dotnet build ${projectFile} \ - --no-restore \ - --configuration Release \ - -p:Version=${version} - - runHook postBuild - ''; - - installPhase = '' - runHook preInstall - - dotnet publish ${projectFile} \ - --no-build \ - --configuration Release \ - --no-self-contained \ - --output $out/lib/${pname} - shopt -s extglob - - makeWrapper $out/lib/${pname}/DiscordChatExporter.Cli $out/bin/discordchatexporter-cli \ - --set DOTNET_ROOT "${dotnetCorePackages.sdk_3_1}" - - runHook postInstall - ''; - - # Strip breaks the executable. - dontStrip = true; - meta = with lib; { description = "A tool to export Discord chat logs to a file"; homepage = "https://github.com/Tyrrrz/DiscordChatExporter";