about summary refs log tree commit diff
path: root/pkgs/development/compilers/dotnet/common.nix
blob: 49f3e3be4d779b59e5ae21a03866a15adf45360d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# TODO: switch to stdenvNoCC
{ stdenv
, lib
, writeText
, testers
, runCommand
, expect
, curl
, installShellFiles
}: type: args: stdenv.mkDerivation (finalAttrs: args // {
  doInstallCheck = true;

  # TODO: this should probably be postInstallCheck
  # TODO: send output to /dev/null
  installCheckPhase = args.installCheckPhase or "" + ''
    $out/bin/dotnet --info
  '';

  # TODO: move this to sdk section?
  setupHook = writeText "dotnet-setup-hook" (''
    if [ ! -w "$HOME" ]; then
      export HOME=$(mktemp -d) # Dotnet expects a writable home directory for its configuration files
    fi

    export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 # Dont try to expand NuGetFallbackFolder to disk
    export DOTNET_NOLOGO=1 # Disables the welcome message
    export DOTNET_CLI_TELEMETRY_OPTOUT=1
    export DOTNET_SKIP_WORKLOAD_INTEGRITY_CHECK=1 # Skip integrity check on first run, which fails due to read-only directory
  '' + args.setupHook or "");

  nativeBuildInputs = (args.nativeBuildInputs or []) ++ [ installShellFiles ];

  postInstall = ''
    # completions snippets taken from https://learn.microsoft.com/en-us/dotnet/core/tools/enable-tab-autocomplete
    installShellCompletion --cmd dotnet \
      --bash ${./completions/dotnet.bash} \
      --zsh ${./completions/dotnet.zsh} \
      --fish ${./completions/dotnet.fish}
  '';

  passthru = {
    tests = let
      mkDotnetTest =
        {
          name,
          template,
          usePackageSource ? false,
          build,
          # TODO: use correct runtimes instead of sdk
          runtime ? finalAttrs.finalPackage,
          runInputs ? [],
          run ? null,
        }:
        let
          built = runCommand "dotnet-test-${name}" { buildInputs = [ finalAttrs.finalPackage ]; } (''
            HOME=$PWD/.home
            dotnet new nugetconfig
            dotnet nuget disable source nuget
          '' + lib.optionalString usePackageSource ''
            dotnet nuget add source ${finalAttrs.finalPackage.packages}
          '' + ''
            dotnet new ${template} -n test -o .
          '' + build);
        in
          if run == null
            then built
          else
            runCommand "${built.name}-run" { src = built; nativeBuildInputs = runInputs; } (
              lib.optionalString (runtime != null) ''
                # TODO: use runtime here
                export DOTNET_ROOT=${runtime}
              '' + run);

      # Setting LANG to something other than 'C' forces the runtime to search
      # for ICU, which will be required in most user environments.
      checkConsoleOutput = command: ''
        output="$(LANG=C.UTF-8 ${command})"
        # yes, older SDKs omit the comma
        [[ "$output" =~ Hello,?\ World! ]] && touch "$out"
      '';

    in {
      version = testers.testVersion ({
        package = finalAttrs.finalPackage;
      } // lib.optionalAttrs (type != "sdk") {
        command = "dotnet --info";
      });
    }
    // lib.optionalAttrs (type == "sdk") {
      console = mkDotnetTest {
        name = "console";
        template = "console";
        build = checkConsoleOutput "dotnet run";
      };

      publish = mkDotnetTest {
        name = "publish";
        template = "console";
        build = "dotnet publish -o $out";
        run = checkConsoleOutput "$src/test";
      };

      self-contained = mkDotnetTest {
        name = "self-contained";
        template = "console";
        usePackageSource = true;
        build = "dotnet publish --use-current-runtime --sc -o $out";
        runtime = null;
        run = checkConsoleOutput "$src/test";
      };

      single-file = mkDotnetTest {
        name = "single-file";
        template = "console";
        usePackageSource = true;
        build = "dotnet publish --use-current-runtime -p:PublishSingleFile=true -o $out";
        runtime = null;
        run = checkConsoleOutput "$src/test";
      };

      web = mkDotnetTest {
        name = "web";
        template = "web";
        build = "dotnet publish -o $out";
        runInputs = [ expect curl ];
        run = ''
          expect <<"EOF"
            set status 1
            spawn $env(src)/test
            expect_before default abort
            expect -re {Now listening on: ([^\r]+)\r} {
              set url $expect_out(1,string)
            }
            expect "Application started. Press Ctrl+C to shut down."
            set output [exec curl -sSf $url]
            if {$output != "Hello World!"} {
              send_error "Unexpected output: $output\n"
              exit 1
            }
            send \x03
            catch wait result
            exit [lindex $result 3]
          EOF
          touch $out
        '';
      };
    } // args.passthru.tests or {};
  } // args.passthru or {};
})