DotNET: Difference between revisions

From NixOS Wiki
imported>Milahu
m fixup NETSDK1013, project will build only on Windows
imported>Mdarocha
Modernize the wiki page, removing references to the old deprecated buildDotnetPackage and adding references to buildDotnetModule
Line 1: Line 1:
.NET packages can be built with <code>buildDotnetPackage</code>
.NET packages can be built with <code>buildDotnetModule</code>


More information about <code>buildDotnetModule</code> can be found in the [https://nixos.org/manual/nixpkgs/unstable/#dotnet nixpkgs manual]
Example build file:
Example build file:


<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>
{ lib
{ fetchFromGitHub
, stdenv
, buildDotnetModule
, fetchFromGitHub
, buildDotnetPackage
, dotnetPackages
, pkg-config
}:
}:


buildDotnetPackage rec {
buildDotnetModule rec {
   pname = "some_program";
   pname = "some_program";
   version = "some_version";
   version = "some_version";
Line 23: Line 20:
   };
   };


   xBuildFiles = [
   projectFile = "SomeProject/SomeProject.csproj"
    "SomeProject/SomeProject.csproj"
  ];
 
  nativeBuildInputs = [
    pkg-config
  ];
 
  buildInputs = [
  ];
 
  propagatedBuildInputs = [
  ];
 
  checkInputs = [
    dotnetPackages.NUnit
    dotnetPackages.NUnitRunners
  ];


   meta = with lib; {
   meta = with lib; {
Line 48: Line 28:
   };
   };
}
}
</syntaxHighlight>
</syntaxHighlight>


== XML namespace error ==
Note that the above package will not build the first time. After adding the above definition to `all-packages.nix`, you
can run the package-specific `fetch-deps` script, which will generate a file containing all the nuget dependencies of the
package. Build the script with <code>nix-build -A some-package.fetch-deps</code>, copy that generated file (the location will be printed by the script) and set the <code>nugetDeps</cude> attribute in <code>buildDotnetModule</code> to point to that generated file (ie. <code>nugetDeps = ./deps.nix</code>).


<blockquote>
After that the package will build normally. Remember to re-run <code>fetch-deps</code> every time the package is updated.
xbuild tool is deprecated and will be removed in future updates, use msbuild instead


<nowiki>
== Building non-.NET Core packages ==
The default XML namespace of the project must be the MSBuild XML namespace. If the project is authored in the MSBuild 2003 format, please add xmlns="http://schemas.microsoft.com/developer/msbuild/2003" to the <Project> element. If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format.
</nowiki>
</blockquote>


Fix: in <code>buildPhase</code>, replace <code>xbuild</code> with <code>msbuild</code>  
Keep in mind that building non-.NET Core projects (ie. projects that don't build using the <code>dotnet</code> CLI tool) is not well supported. For those projects, you have to work on a custom derivation or override the <code>buildDotnetModule</code> build steps.
 
<syntaxHighlight lang=nix>
{ # ...
, msbuild
}:
 
let
  arrayToShell = (a: toString (map (lib.escape (lib.stringToCharacters "\\ ';$`()|<>\t") ) a));
in
 
buildDotnetPackage rec {
  # ...
 
  msBuildFiles = [
    "SomeProject/SomeProject.csproj"
  ];
 
  nativeBuildInputs = [
    pkg-config
    msbuild
  ];
 
  msBuildFlags = [
    "/p:Configuration=Release"
  ];
 
  # based on https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/dotnet/build-dotnet-package/default.nix
  buildPhase = ''
    runHook preBuild
 
    echo Building dotNET packages...
 
    # Probably needs to be moved to fsharp
    if pkg-config FSharp.Core
    then
      export FSharpTargetsPath="$(dirname $(pkg-config FSharp.Core --variable=Libraries))/Microsoft.FSharp.Targets"
    fi
 
    ran=""
    for msBuildFile in ${arrayToShell msBuildFiles} ''${msBuildFilesExtra}
    do
      ran="yes"
      msbuild ${arrayToShell msBuildFlags} ''${msBuildFlagsArray} $msBuildFile
    done
 
    [ -z "$ran" ] && msbuild ${arrayToShell msBuildFlags} ''${msBuildFlagsArray}
 
    runHook postBuild
  '';
}
</syntaxHighlight>
 
MSBuild reference: https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-command-line-reference?view=vs-2022


== .NET location: Not found ==
== .NET location: Not found ==


set DOTNET_ROOT Environment Variable
If running a .NET-build executable you get the above error, make sure the DOTNET_ROOT environment variable is set:
 
<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>
environment.sessionVariables = {
environment.sessionVariables = {
Line 136: Line 61:


Wontfix: The project will build only on Windows.
Wontfix: The project will build only on Windows.
== Missing NuGet packages ==
Example error:
<blockquote>
<code>
<nowiki>
error : This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is ../../packages/Microsoft.Bcl.Build.1.0.21/build/Microsoft.Bcl.Build.targets.
</nowiki>
</code>
</blockquote>
These are upstream bugs
Fix: patch all <code>*.csproj</code> files, to remove all XML tags that contain the "missing file" paths, for example
<blockquote>
<code>
&lt;Import Project="..\..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" /&gt;
</code>
</blockquote>
See also https://stackoverflow.com/questions/32254439/nuget-packages-are-missing


== NativeAOT ==
== NativeAOT ==
Line 238: Line 139:


* [https://ryantm.github.io/nixpkgs/languages-frameworks/dotnet/ dotnet in the nixpkgs manual]
* [https://ryantm.github.io/nixpkgs/languages-frameworks/dotnet/ dotnet in the nixpkgs manual]
* [https://github.com/NixOS/nixpkgs/blob/master/pkgs/top-level/dotnet-packages.nix nixpkgs/pkgs/top-level/dotnet-packages.nix] &rarr; look for "SOURCE PACKAGES"
* [https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/dotnet/build-dotnet-package/default.nix buildDotnetPackage implementation]
* [https://grep.app/search?q=buildDotnetPackage&filter%5Brepo%5D%5B0%5D=NixOS/nixpkgs&filter%5Blang%5D%5B0%5D=Nix&filter%5Bpath%5D%5B0%5D=pkgs/ buildDotnetPackage references in nixpkgs]
* [https://grep.app/search?q=buildDotnetPackage&filter%5Brepo%5D%5B0%5D=NixOS/nixpkgs&filter%5Blang%5D%5B0%5D=Nix&filter%5Bpath%5D%5B0%5D=pkgs/ buildDotnetPackage references in nixpkgs]
* [https://sgt.hootr.club/molten-matter/dotnet-on-nix/ The journey of packaging a .NET app on Nix]
* [https://sgt.hootr.club/molten-matter/dotnet-on-nix/ The journey of packaging a .NET app on Nix]
* https://en.wikipedia.org/wiki/.NET_Framework
* https://en.wikipedia.org/wiki/.NET_Framework - The old, windows-only version of .NET. Newer versions (ie. .NET Core) are multiplatform.
** https://en.wikipedia.org/wiki/Mono_(software) is the deprecated open source implementation of the DotNET compiler and runtime. It has transformed into .NET Core.
* https://learn.microsoft.com/en-us/dotnet/core/introduction
** https://en.wikipedia.org/wiki/List_of_CLI_languages: C#, [[Fsharp|F#]], Visual Basic, ...
** https://en.wikipedia.org/wiki/List_of_CLI_languages: C#, [[Fsharp|F#]], Visual Basic, ...
** https://en.wikipedia.org/wiki/Mono_(software) is the open source implementation of the DotNET compiler and runtime


[[Category: Development]]
[[Category: Development]]

Revision as of 10:37, 29 May 2023

.NET packages can be built with buildDotnetModule

More information about buildDotnetModule can be found in the nixpkgs manual Example build file:

{ fetchFromGitHub
, buildDotnetModule
}:

buildDotnetModule rec {
  pname = "some_program";
  version = "some_version";

  src = fetchFromGitHub {
    owner = "some_owner";
    repo = pname;
    rev = "v${version}";
    sha256 = "";
  };

  projectFile = "SomeProject/SomeProject.csproj"

  meta = with lib; {
    homepage = "some_homepage";
    description = "some_description";
    license = licenses.mit;
  };
}

Note that the above package will not build the first time. After adding the above definition to `all-packages.nix`, you can run the package-specific `fetch-deps` script, which will generate a file containing all the nuget dependencies of the package. Build the script with nix-build -A some-package.fetch-deps, copy that generated file (the location will be printed by the script) and set the nugetDeps</cude> attribute in buildDotnetModule to point to that generated file (ie. nugetDeps = ./deps.nix).

After that the package will build normally. Remember to re-run fetch-deps every time the package is updated.

Building non-.NET Core packages

Keep in mind that building non-.NET Core projects (ie. projects that don't build using the dotnet CLI tool) is not well supported. For those projects, you have to work on a custom derivation or override the buildDotnetModule build steps.

.NET location: Not found

If running a .NET-build executable you get the above error, make sure the DOTNET_ROOT environment variable is set:

environment.sessionVariables = {
  DOTNET_ROOT = "${pkgs.dotnet-sdk}";
};

See : https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables#net-sdk-and-cli-environment-variables

TargetFramework value was not recognized

error NETSDK1013: The TargetFramework value 'net6.0-windows' was not recognized. It may be misspelled. If not, then the TargetFrameworkIdentifier and/or TargetFrameworkVersion properties must be specified explicitly.

Wontfix: The project will build only on Windows.

NativeAOT

This is relevant for NixOS only.

nix-ld is needed:

{
  programs.nix-ld.enable = true;
}

Now we will need a bunch of native dependencies. Here's an example of a shell:

with import <nixpkgs> {};
pkgs.mkShell rec {

  dotnetPkg = 
    (with dotnetCorePackages; combinePackages [
      sdk_7_0
    ]);

  deps = [
    zlib
    zlib.dev
    openssl
    dotnetPkg
  ];

  NIX_LD_LIBRARY_PATH = lib.makeLibraryPath ([
    stdenv.cc.cc
  ] ++ deps);
  NIX_LD = "${pkgs.stdenv.cc.libc_bin}/bin/ld.so";
  nativeBuildInputs = [ 
  ] ++ deps;

  shellHook = ''
    DOTNET_ROOT="${dotnetPkg}";
  '';
}

Global Tools

There is currently no mechanism to install them globally, and regular (mutable) installation does not work.

Here is a proof of concept of how .NET tools could be used declaratively.

Here's an example of using that package:

packages =                                                        
  let dotnetPkg =                                                 
    (with dotnetCorePackages; combinePackages [                   
      sdk_7_0                                                     
      sdk_6_0                                                     
    ]);                                                           
    dotnetTools = (callPackage ./dotnet-tool.nix {});     # dotnet-tool.nix is the file from the link above
  in [                                                            
    vim                                                           
    firefox                                                       
    dotnetPkg                                                     
    dotnetTools.combineTools dotnetPkg (with dotnetTools.tools; [ 
                          #  ^^^^^^^^^ here we specify the dotnet package 
                          # that will be invoked for this tool
                          # Ideally, something like dotnetPkg.withTools
                          # should be there

      fsautocomplete                                      # these are tools from dotnetTools.tools;        
      csharp-ls                                           # if a package is missing, it can be declared
      dotnet-repl                                         # manually, see the sources of dotnet-tools.nix
    ])                                                            
  ];

See also