DotNET: Difference between revisions

Lostmsu (talk | contribs)
create systemd service from an ASP.NET-based package
DHCP (talk | contribs)
m update links
 
(12 intermediate revisions by 8 users not shown)
Line 1: Line 1:
From [[Wikipedia:.NET|Wikipedia]]
<blockquote>
The .NET platform (formerly named .NET Core) is a free and open-source, managed computer software framework for Windows, Linux, and macOS operating systems. It is a cross-platform successor to the .NET Framework. The project is mainly developed by Microsoft employees by way of the .NET Foundation and is today released under an MIT License.
</blockquote>
== NativeAOT ==
This is relevant for NixOS only.
[https://github.com/nix-community/nix-ld nix-ld] is needed:
<syntaxHighlight lang=nix>
{
  programs.nix-ld.enable = true;
}
</syntaxHighlight>
Now we will need a bunch of native dependencies. Here's an example of a shell:
<syntaxHighlight lang=nix>
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}";
  '';
}
</syntaxHighlight>
== Global Tools ==
Local installation of .NET global tools is fully supported and preferred when possible - more info [https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools#install-a-local-tool in the Microsoft docs].
For globally installing .NET tools, search if they are available as Nix packages - they are packaged as any other normal
.NET binary, using <code>buildDotnetModule</code>. For .NET tools with no source available, or those hard to build from source, <code>buildDotnetGlobalTool</code> is available. See [https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/dotnet.section.md#dotnet-global-tools-dotnet-global-tools dotnet nixpkgs manual] for more info.
Note that Nix-packaged .NET tools use a special wrapper (toggled by <code>useDotnetFromEnv</code> option in <code>buildDotnetModule</code>) that automatically picks up .NET install from the user environment. If you want to use a
different SDK version with a Nix-packaged .NET tools than the default, make sure the <code>dotnet</code> CLI of your wanted SDK version is installed and available.
== Packaging ==
.NET packages can be built with <code>buildDotnetModule</code>
.NET packages can be built with <code>buildDotnetModule</code>


Line 4: Line 64:
Example build file:
Example build file:


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix" line="1" start="1">
{ fetchFromGitHub
{
, dotnetCorePackages
  buildDotnetModule,
, buildDotnetModule
  dotnetCorePackages,
}:
}:


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


   src = fetchFromGitHub {
   src = ./.;
    owner = "some_owner";
    repo = pname;
    rev = "v${version}";
    hash = ""; # use e.g. `nix-prefetch-git`
  };


   projectFile = "SomeProject/SomeProject.csproj";
   projectFile = "Hello/Hello.csproj";
   dotnet-sdk = dotnetCorePackages.sdk_8_0;
   dotnet-sdk = dotnetCorePackages.sdk_8_0;
   dotnet-runtime = dotnetCorePackages.runtime_8_0;
   dotnet-runtime = dotnetCorePackages.runtime_8_0;
   nugetDeps = ./deps.nix; # to generate, set to `""`, then `nix-build -A fetch-deps && ./result`
   nugetDeps = ./deps.json;
 
  meta = with lib; {
    homepage = "some_homepage";
    description = "some_description";
    license = licenses.mit;
  };
}
}


</syntaxhighlight>
</syntaxhighlight>


If the <code>fetch-deps</code> script isn't working for whatever reason, you can manually run <code>nuget-to-nix</code>:
If the <code>fetch-deps</code> script isn't working for whatever reason, you can manually run <code>nuget-to-json</code>:
<syntaxhighlight lang="sh">
<syntaxhighlight lang="shell-session">
dotnet restore --packages=packageDir ./SomeProject.csproj
$ dotnet restore --packages=packageDir ./SomeProject.csproj
nuget-to-nix packageDir >deps.nix
$ nuget-to-json packageDir > deps.json
rm -r packageDir
$ rm -r packageDir
</syntaxhighlight>
</syntaxhighlight>


Remember to build and run the <code>fetch-deps</code> script after NuGet packages are updated, or building the derivation will fail.
Remember to build and run the <code>fetch-deps</code> script after NuGet packages are updated, or building the derivation will fail.


== Building non-.NET Core packages ==
=== Building non-.NET Core packages ===


Keep in mind that building projects which don't use the .NET SDK (formerly the .NET Core SDK) and its <code>dotnet</code> CLI tool isn't supported.
Keep in mind that building projects which don't use the .NET SDK (formerly the .NET Core SDK) and its <code>dotnet</code> CLI tool isn't supported.
Line 52: Line 101:
Just remember to add `mono` to `buildInputs` and generate a wrapper script in `postInstall`.
Just remember to add `mono` to `buildInputs` and generate a wrapper script in `postInstall`.


== Packaging ASP.NET projects ==
=== Packaging ASP.NET projects ===


Currently building ASP.NET project as Nix package produces a website that does not work correctly out of the box because the executable can not find `wwwroot`, so all the static assets won't load with 404.
Currently building ASP.NET project as Nix package produces a website that does not work correctly out of the box because the executable can not find <code>wwwroot</code>, so all the static assets won't load with 404.


> Request finished HTTP/2 GET https://my.app/css/site.css - 404 0
<blockquote>
Request finished HTTP/2 GET https://my.app/css/site.css - 404 0
</blockquote>


The situation can be fixed by setting `WEBROOT` environment variable to the package path.
The situation can be fixed by setting <code>WEBROOT</code> environment variable to the package path.


An example of systemd + ASP.NET 8 service:
An example of systemd + ASP.NET 8 service:


```nix
<syntaxhighlight lang="nix">
# myapp package needs to be imported; and added to `environment.systemPackages`
# myapp package needs to be imported; and added to `environment.systemPackages`
# it is used below
# the variable myapp is used below


systemd.services.my-app = {
systemd.services.my-app = {
Line 73: Line 124:
   wants = [ "network-online.target" ];
   wants = [ "network-online.target" ];
   serviceConfig = {
   serviceConfig = {
     AmbientCapabilities = "CAP_NET_BIND_SERVICE"; # allow binding to privileged ports - when you want to expose Kestrel directly without reverse proxy
     # allow binding to privileged ports - when you want to expose Kestrel directly without reverse proxy
    AmbientCapabilities = "CAP_NET_BIND_SERVICE";
     User = "myapp"; # must be created using users.users.myapp = { isSystemUser = true; group = "myapp"; };
     User = "myapp"; # must be created using users.users.myapp = { isSystemUser = true; group = "myapp"; };
     Group = "myapp"; # must be created using users.groups.myapp = {};
     Group = "myapp"; # must be created using users.groups.myapp = {};
Line 110: Line 162:
};
};


```
</syntaxhighlight>
[ACME](https://wiki.nixos.org/wiki/ACME)


== .NET location: Not found ==
See also: setting up SSL certificates using [[ACME]]


If running a .NET-build executable you get the above error, make sure the DOTNET_ROOT environment variable is set:
=== Packaging Test projects ===
<syntaxHighlight lang=nix>
environment.sessionVariables = {
  DOTNET_ROOT = "${pkgs.dotnet-sdk}";
};
</syntaxHighlight>


See : https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables#net-sdk-and-cli-environment-variables
If you use MSBuild SDKs for test projects, then you have to manually copy of the Nuget package to pkg folder, since MSBuild SDK are not saved during `dotnet restore --packages pkg` process.


== TargetFramework value was not recognized ==
== Examples ==


<blockquote>
=== Running Rider with dotnet & PowerShell ===
Rider has better compatibility when run in FHS mode


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.
Rider package<syntaxhighlight lang="nix">
pkgs.jetbrains.rider
</syntaxhighlight>rider-fhs.nix<syntaxhighlight lang="nix">
{ pkgs ? import <nixpkgs> {} }:


</blockquote>
(pkgs.buildFHSEnv {
  name = "rider-env";
  targetPkgs = pkgs: (with pkgs; [
    dotnetCorePackages.dotnet_8.sdk
    dotnetCorePackages.dotnet_8.aspnetcore
    powershell
  ]);
  multiPkgs = pkgs: (with pkgs; [
  ]);
  runScript = "nohup rider &";
}).env
</syntaxhighlight>


Wontfix: The project will build only on Windows.
<syntaxhighlight lang="nix">
nix-shell ./rider-fhs.nix
</syntaxhighlight>


== NativeAOT ==
This can be added as an alias to your shell if you update the reference to an absolute address, such as location within your home directory.


This is relevant for NixOS only.
<syntaxhighlight>
run-rider = "nix-shell ~/nix/rider-fhs.nix";
</syntaxhighlight>


[https://github.com/Mic92/nix-ld nix-ld] is needed:
=== Multi-SDK installation with local workload installation enabled ===
<syntaxHighlight lang=nix>
{
  programs.nix-ld.enable = true;
}
</syntaxHighlight>


Now we will need a bunch of native dependencies. Here's an example of a shell:
By default, workload installation will fail on NixOS, as dotnet will attempt to save it to $DOTNET_ROOT, which is inside the read-only Nix store.
<syntaxHighlight lang=nix>
with import <nixpkgs> {};
pkgs.mkShell rec {


  dotnetPkg =
Please visit the [https://discourse.nixos.org/t/dotnet-maui-workload/20370/10 forum] for an example of a multi-SDK installation with workload changed to install to home directory.
    (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 = ''
== Troubleshooting ==
    DOTNET_ROOT="${dotnetPkg}";
  '';
}
</syntaxHighlight>


== Global Tools ==
=== .NET location: Not found ===


Local installation of .NET global tools is fully supported and preferred when possible - more info [https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools#install-a-local-tool in the Microsoft docs].
If running a .NET-build executable you get the above error, make sure the DOTNET_ROOT environment variable is set:
<syntaxhighlight lang="nix">
environment.sessionVariables = {
  DOTNET_ROOT = "${pkgs.dotnet-sdk}/share/dotnet/";
};
</syntaxhighlight>


For globally installing .NET tools, search if they are available as Nix packages - they are packaged as any other normal
See : https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables#net-sdk-and-cli-environment-variables
.NET binary, using <code>buildDotnetModule</code>. For .NET tools with no source available, or those hard to build from source, <code>buildDotnetGlobalTool</code> is available. See [https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/dotnet.section.md#dotnet-global-tools-dotnet-global-tools dotnet nixpkgs manual] for more info.


Note that Nix-packaged .NET tools use a special wrapper (toggled by <code>useDotnetFromEnv</code> option in <code>buildDotnetModule</code>) that automatically picks up .NET install from the user environment. If you want to use a
=== TargetFramework value was not recognized ===
different SDK version with a Nix-packaged .NET tools than the default, make sure the <code>dotnet</code> CLI of your wanted SDK version is installed and available.


<blockquote>


== Example: Running Rider with dotnet & PowerShell ==
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.


Rider package
</blockquote>


<syntaxHighlight lang=nix>
Wontfix: The project will build only on Windows.
pkgs.jetbrains.rider
</syntaxHighlight>


dotnet.nix
=== Unable to find package ===


<syntaxHighlight lang=nix>
<blockquote>
with import <nixpkgs> {};


mkShell {
error NU1101: Unable to find package runtime.any.System.Collections. No packages exist with this id in source(s): nugetSource
  name = "dotnet-env";
  packages = [
    (with dotnetCorePackages; combinePackages [
      sdk_6_0
      sdk_7_0
      sdk_8_0
    ])
    powershell
  ];
}
</syntaxHighlight>


To execute Rider
</blockquote>


<syntaxHighlight lang=bash>
Unsure what specific situations cause this, probably has something to do with .NET Standard libraries.
nix-shell ./dotnet.nix --run 'nohup rider &'
</syntaxHighlight>


This can be added as an alias to your shell if you update the reference to an absolute address, such as location within your home directory. e.g. `~/nix/dotnet.nix`
The workaround is modifying the bits that generate nuget-deps.nix:


== Example: multi-SDK installation with local workload installation enabled ==
<syntaxhighlight lang="sh">
dotnet restore --packages=packageDir --use-current-runtime ./SomeProject.csproj
nuget-to-nix packageDir >deps.nix
rm -r packageDir
</syntaxhighlight>


By default, workload installation will fail on NixOS, as dotnet will attempt to save it to $DOTNET_ROOT, which is inside the read-only Nix store.
The new parameter <code>--use-current-runtime</code> requires .NET SDK 8+. I believe what it does is explicitly adding packages missing in this runtime vs .NET Standard to packageDir.


Please visit the [https://discourse.nixos.org/t/dotnet-maui-workload/20370/10 forum] for an example of a multi-SDK installation with workload changed to install to home directory.
If this still does not work, it might indicate a good time to update target frameworks and dependencies.


== See also ==
== See also ==
Line 233: Line 263:
* [https://www.reddit.com/r/NixOS_dotnet NixOS.NET community on Reddit]
* [https://www.reddit.com/r/NixOS_dotnet NixOS.NET community on Reddit]
* [https://discord.gg/pTpq7Qfs NixOS.NET community on Discord]
* [https://discord.gg/pTpq7Qfs NixOS.NET community on Discord]
* [https://sgt.hootr.club/molten-matter/dotnet-on-nix/ The journey of packaging a .NET app on Nix]
* [https://sgt.hootr.club/blog/dotnet-on-nix/ The journey of packaging a .NET app on Nix]
* https://en.wikipedia.org/wiki/.NET_Framework - The old, windows-only version of .NET. Newer versions (ie. .NET Core) are multiplatform.
* [[Wikipedia:.NET Framework|.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 open source reimplementation of .NET Framework. Its runtime/JIT has been merged into .NET Core, and now it only receives bugfixes.
** [[Wikipedia:Mono (software)|Mono]] is the open source reimplementation of .NET Framework. Its runtime/JIT has been merged into .NET Core, and now it only receives bugfixes.
* https://learn.microsoft.com/en-us/dotnet/core/introduction
* https://learn.microsoft.com/en-us/dotnet/core/introduction


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