Packaging a .NET Project (balatro-mobile-maker)

It’s the first time I package a .NET project and it’s this application right here: https://github.com/blake502/balatro-mobile-maker
I followed the wiki’s page about .NET and copied the example derivation and went from there:

{
  lib,
  fetchFromGitHub,
  buildDotnetModule,
  dotnetCorePackages,
}:
buildDotnetModule {
  pname = "balatro-mobile-maker";
  version = "0.8.3";

  src = fetchFromGitHub {
    owner = "blake502";
    repo = "balatro-mobile-maker";
    rev = "9fd41b536841d19a82fa834d563c12118d685106";
    hash = "sha256-bV3iDpQ+JoNrKCeimxRL24OePeC1wjxk5I7U636IDvQ=";
  };

  projectFile = "balatro-mobile-maker/balatro-mobile-maker.csproj";
  dotnet-sdk = dotnetCorePackages.sdk_8_0;
  dotnet-runtime = dotnetCorePackages.runtime_8_0;
  nugetDeps = ./deps.nix;

  meta = with lib; {
    homepage = "https://github.com/blake502/balatro-mobile-maker";
    description = "Create a mobile Balatro app from your Steam version of Balatro";
    license = licenses.mit;
  };
}

I also ran the block of code that generates the ./deps.nix file (I also noticed that nuget-to-nix doesn’t work in flake dev shells for some reason…?)

dotnet restore --packages=packageDir balatro-mobile-maker/balatro-mobile-maker.csproj
nix run nixpkgs#nuget-to-nix -- packageDir > deps.nix
rm -r packageDir

Lastly I made a quick flake calling the package and ran nix build, however I get this error:

       last 6 log lines:
       > Running phase: unpackPhase
       > unpacking source archive /nix/store/idyyj1lwjwpqfp7m9rr52v606m4qxg39-source
       > source root is source
       > Running phase: patchPhase
       > Running phase: configureNuget
       > ln: failed to create symbolic link '/build/nuget.9Myeoe/fallback/microsoft.net.illink.tasks/8.0.11': File exists
       For full logs, run 'nix log /nix/store/rjfk4wyy4ar31najkj4z811hsqjsir33-balatro-mobile-maker-beta-0.8.3.drv'.

I tried changing the .NET SDK and Runtime versions to 9.0, but had trouble with creating the deps.nix file; as the dotnet restore command appeared to set wrong versions to some dependencies. Here’s the deps.nix file it generated:

{ fetchNuGet }: [
  (fetchNuGet { pname = "Microsoft.AspNetCore.App.Ref"; version = "8.0.11"; hash = "sha256-dXJ1h1xyeI+lzdoNiYtmLBzkQnHKZcWSksjuo70yp5k="; })
  (fetchNuGet { pname = "Microsoft.NET.ILLink.Tasks"; version = "8.0.11"; hash = "sha256-szAnroFmCOKpUsq8JuwZvFujB63Tw1gp1AjvKzhWa3A="; })
  (fetchNuGet { pname = "Microsoft.NETCore.App.Host.linux-x64"; version = "8.0.11"; hash = "sha256-brt8CP11GH1hidrkYbAou8mMQ6kr2eStr/oqesK6AnY="; })
  (fetchNuGet { pname = "Microsoft.NETCore.App.Ref"; version = "8.0.11"; hash = "sha256-lo6MAnvFQ1DBDh+9qdxzOJMgACsvFjj2e5bhreJ4v0I="; })
  (fetchNuGet { pname = "SharpZipLib"; version = "1.4.2"; hash = "sha256-/giVqikworG2XKqfN9uLyjUSXr35zBuZ2FX2r8X/WUY="; })
]

Here’s the error I got running nix build with this configuration:

       last 19 log lines:
       > Running phase: unpackPhase
       > unpacking source archive /nix/store/idyyj1lwjwpqfp7m9rr52v606m4qxg39-source
       > source root is source
       > Running phase: patchPhase
       > Running phase: configureNuget
       > The template "NuGet Config" was created successfully.
       >
       > Processing post-creation actions...
       >
       >
       > Running phase: updateAutotoolsGnuConfigScriptsPhase
       > Running phase: configurePhase
       > Executing dotnetConfigureHook
       >   Determining projects to restore...
       > /build/source/balatro-mobile-maker/balatro-mobile-maker.csproj : error NU1102: Unable to find package Microsoft.NETCore.App.Runtime.linux-x64 with version (= 8.0.11)
       > /build/source/balatro-mobile-maker/balatro-mobile-maker.csproj : error NU1102:   - Found 1 version(s) in _nix [ Nearest version: 9.0.0 ]
       > /build/source/balatro-mobile-maker/balatro-mobile-maker.csproj : error NU1102: Unable to find package Microsoft.AspNetCore.App.Runtime.linux-x64 with version (= 8.0.11)
       > /build/source/balatro-mobile-maker/balatro-mobile-maker.csproj : error NU1102:   - Found 1 version(s) in _nix [ Nearest version: 9.0.0 ]
       >   Failed to restore /build/source/balatro-mobile-maker/balatro-mobile-maker.csproj (in 717 ms).
       For full logs, run 'nix log /nix/store/32sdza1qds2f49wvp7f6ym6602s9q1r8-balatro-mobile-maker-0.8.3.drv'.

.NET 7 is marked as “insecure” so I didn’t try it.

I have no experience with .NET apps so I don’t really know how to troubleshoot this bug any more.

I initially tried that, but I got this error:

~> nix-build -A balatro-mobile-maker.fetch-deps
error: cannot evaluate a function that has an argument without a value ('lib')
       Nix attempted to evaluate a function as a top level expression; in
       this case it must have its arguments supplied either by default
       values, or passed explicitly with '--arg' or '--argstr'. See
       https://nixos.org/manual/nix/stable/language/constructs.html#functions.
       at /home/cap/tmp/balatro-mobile-maker/default.nix:2:3:
            1| {
            2|   lib,
             |   ^
            3|   fetchFromGitHub,

The Nix builder doesn’t know where lib, fetchFromGitHub and all the rest of the inputs come from, so we need to pass them ourselves. To do this, we need to import nixpkgs and call the derivation with callPackage:

$ nix-build -E 'with import <nixpkgs> {}; (callPackage ./default.nix {}).fetch-deps'
$ ./result ./deps.json

To organize things more, you can put the package in another file and call that, instead:

# default.nix
let
  pkgs = import <nixpkgs> { };
in
{
  balatro-mobile-maker = pkgs.callPackage ./balatro-mobile-maker.nix { };
}
$ nix-build . -A balatro-mobile-maker.fetch-deps
$ ./result ./deps.json
$ nix-build . -A balatro-mobile-maker
$ ls ./result/bin
balatro-mobile-maker

If you want to automatically add the executable to the PATH, you can use a nix shell:

# shell.nix
let
  pkgs = import <nixpkgs> { };
  balatro-mobile-maker = pkgs.callPackage ./balatro-mobile-maker.nix { };
in
pkgs.mkShell {
  packages = [
    balatro-mobile-maker
  ];
}
$ nix-shell
$ which balatro-mobile-maker 
/nix/store/xmsvzcf87swzqxp6dijyj1qwqxw38cm9-balatro-mobile-maker-0.8.3/bin/balatro-mobile-maker

If you use flakes, you can do the same thing as we did above, like this:

# flake.nix
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
  };

  outputs =
    inputs@{ self, nixpkgs, ... }:
    let
      system = "x86_64-linux";
      pkgs = nixpkgs.legacyPackages.${system};
    in
    {
      packages.balatro-mobile-maker = pkgs.callPackage ./balatro-mobile-maker.nix { };
      packages.default = self.packages.balatro-mobile-maker;

      devShells.${system}.default = pkgs.mkShell {
        packages = [
          self.packages.balatro-mobile-maker
        ];
      };
    };
}
$ nix build .#packages.default.fetch-deps
$ ./result ./deps.json
$ nix build .#packages.default
$ nix develop

Just don’t forget to stage the files beforehand.

PS: If you’re a Nix beginner, I highly recommend you follow the guides in Tutorials — nix.dev documentation as they’re very helpful.

1 Like