1# shellcheck shell=bash
2
3_dotnetIsSolution() {
4 dotnet sln ${1:+"$1"} list 2>/dev/null
5}
6
7dotnetConfigurePhase() {
8 echo "Executing dotnetConfigureHook"
9
10 runHook preConfigure
11
12 local -a projectFiles flags runtimeIds
13 concatTo projectFiles dotnetProjectFiles dotnetTestProjectFiles
14 concatTo flags dotnetFlags dotnetRestoreFlags
15 concatTo runtimeIds dotnetRuntimeIds
16
17 if [[ -z ${enableParallelBuilding-} ]]; then
18 flags+=(--disable-parallel)
19 fi
20
21 if [[ -v dotnetSelfContainedBuild ]]; then
22 if [[ -n $dotnetSelfContainedBuild ]]; then
23 flags+=("-p:SelfContained=true")
24 else
25 flags+=("-p:SelfContained=false")
26 fi
27 fi
28
29 dotnetRestore() {
30 local -r projectFile="${1-}"
31 for runtimeId in "${runtimeIds[@]}"; do
32 dotnet restore ${1+"$projectFile"} \
33 -p:ContinuousIntegrationBuild=true \
34 -p:Deterministic=true \
35 -p:NuGetAudit=false \
36 --runtime "$runtimeId" \
37 "${flags[@]}"
38 done
39 }
40
41 if [[ -f .config/dotnet-tools.json || -f dotnet-tools.json ]]; then
42 dotnet tool restore
43 fi
44
45 # dotnetGlobalTool is set in buildDotnetGlobalTool to patch dependencies but
46 # avoid other project-specific logic. This is a hack, but the old behavior
47 # is worse as it relied on a bug: setting projectFile to an empty string
48 # made the hooks actually skip all project-specific logic. It’s hard to keep
49 # backwards compatibility with this odd behavior now since we are using
50 # arrays, so instead we just pass a variable to indicate that we don’t have
51 # projects.
52 if [[ -z ${dotnetGlobalTool-} ]]; then
53 if (( ${#projectFiles[@]} == 0 )); then
54 dotnetRestore
55 fi
56
57 local projectFile
58 for projectFile in "${projectFiles[@]}"; do
59 dotnetRestore "$projectFile"
60 done
61 fi
62
63 runHook postConfigure
64
65 echo "Finished dotnetConfigureHook"
66}
67
68if [[ -z "${dontDotnetConfigure-}" && -z "${configurePhase-}" ]]; then
69 configurePhase=dotnetConfigurePhase
70fi
71
72dotnetBuildPhase() {
73 echo "Executing dotnetBuildHook"
74
75 runHook preBuild
76
77 local -r dotnetBuildType="${dotnetBuildType-Release}"
78
79 local -a projectFiles flags runtimeIds
80 concatTo projectFiles dotnetProjectFiles dotnetTestProjectFiles
81 concatTo flags dotnetFlags dotnetBuildFlags
82 concatTo runtimeIds dotnetRuntimeIds
83
84 if [[ -n "${enableParallelBuilding-}" ]]; then
85 local -r maxCpuFlag="$NIX_BUILD_CORES"
86 local -r parallelBuildFlag="true"
87 else
88 local -r maxCpuFlag="1"
89 local -r parallelBuildFlag="false"
90 fi
91
92 if [[ -v dotnetSelfContainedBuild ]]; then
93 if [[ -n $dotnetSelfContainedBuild ]]; then
94 flags+=("-p:SelfContained=true")
95 else
96 flags+=("-p:SelfContained=false")
97 fi
98 fi
99
100 if [[ -n ${dotnetUseAppHost-} ]]; then
101 flags+=("-p:UseAppHost=true")
102 fi
103
104 if [[ -n ${version-} ]]; then
105 flags+=("-p:InformationalVersion=$version")
106 fi
107
108 if [[ -n ${versionForDotnet-} ]]; then
109 flags+=("-p:Version=$versionForDotnet")
110 fi
111
112 dotnetBuild() {
113 local -r projectFile="${1-}"
114
115 local useRuntime=
116 _dotnetIsSolution "$projectFile" || useRuntime=1
117
118 for runtimeId in "${runtimeIds[@]}"; do
119 local runtimeIdFlags=()
120 if [[ -n $useRuntime ]]; then
121 runtimeIdFlags+=("--runtime" "$runtimeId")
122 fi
123
124 dotnet build ${1+"$projectFile"} \
125 -maxcpucount:"$maxCpuFlag" \
126 -p:BuildInParallel="$parallelBuildFlag" \
127 -p:ContinuousIntegrationBuild=true \
128 -p:Deterministic=true \
129 -p:OverwriteReadOnlyFiles=true \
130 --configuration "$dotnetBuildType" \
131 --no-restore \
132 "${runtimeIdFlags[@]}" \
133 "${flags[@]}"
134 done
135 }
136
137 if (( ${#projectFiles[@]} == 0 )); then
138 dotnetBuild
139 fi
140
141 local projectFile
142 for projectFile in "${projectFiles[@]}"; do
143 dotnetBuild "$projectFile"
144 done
145
146 runHook postBuild
147
148 echo "Finished dotnetBuildHook"
149}
150
151if [[ -z ${dontDotnetBuild-} && -z ${buildPhase-} ]]; then
152 buildPhase=dotnetBuildPhase
153fi
154
155dotnetCheckPhase() {
156 echo "Executing dotnetCheckHook"
157
158 runHook preCheck
159
160 local -r dotnetBuildType="${dotnetBuildType-Release}"
161
162 local -a projectFiles testProjectFiles testFilters disabledTests flags runtimeIds runtimeDeps
163 concatTo projectFiles dotnetProjectFiles
164 concatTo testProjectFiles dotnetTestProjectFiles
165 concatTo testFilters dotnetTestFilters
166 concatTo disabledTests dotnetDisabledTests
167 concatTo flags dotnetFlags dotnetTestFlags
168 concatTo runtimeIds dotnetRuntimeIds
169 concatTo runtimeDeps dotnetRuntimeDeps
170
171 if (( ${#disabledTests[@]} > 0 )); then
172 local disabledTestsFilters=("${disabledTests[@]/#/FullyQualifiedName!=}")
173 testFilters=( "${testFilters[@]}" "${disabledTestsFilters[@]//,/%2C}" )
174 fi
175
176 if (( ${#testFilters[@]} > 0 )); then
177 local OLDIFS="$IFS" IFS='&'
178 flags+=("--filter:${testFilters[*]}")
179 IFS="$OLDIFS"
180 fi
181
182 local libraryPath="${LD_LIBRARY_PATH-}"
183 if (( ${#runtimeDeps[@]} > 0 )); then
184 local libraryPaths=("${runtimeDeps[@]/%//lib}")
185 local OLDIFS="$IFS" IFS=':'
186 libraryPath="${libraryPaths[*]}${libraryPath:+':'}$libraryPath"
187 IFS="$OLDIFS"
188 fi
189
190 if [[ -n ${enableParallelBuilding-} ]]; then
191 local -r maxCpuFlag="$NIX_BUILD_CORES"
192 else
193 local -r maxCpuFlag="1"
194 fi
195
196 local projectFile runtimeId
197 for projectFile in "${testProjectFiles[@]-${projectFiles[@]}}"; do
198 local useRuntime=
199 _dotnetIsSolution "$projectFile" || useRuntime=1
200
201 for runtimeId in "${runtimeIds[@]}"; do
202 local runtimeIdFlags=()
203 if [[ -n $useRuntime ]]; then
204 runtimeIdFlags=("--runtime" "$runtimeId")
205 fi
206
207 LD_LIBRARY_PATH=$libraryPath \
208 dotnet test "$projectFile" \
209 -maxcpucount:"$maxCpuFlag" \
210 -p:ContinuousIntegrationBuild=true \
211 -p:Deterministic=true \
212 --configuration "$dotnetBuildType" \
213 --no-restore \
214 --no-build \
215 --logger "console;verbosity=normal" \
216 "${runtimeIdFlags[@]}" \
217 "${flags[@]}"
218 done
219 done
220
221 runHook postCheck
222
223 echo "Finished dotnetCheckHook"
224}
225
226if [[ -z "${dontDotnetCheck-}" && -z "${checkPhase-}" ]]; then
227 checkPhase=dotnetCheckPhase
228fi
229
230# For compatibility, convert makeWrapperArgs to an array unless we are using
231# structured attributes. That is, we ensure that makeWrapperArgs is always an
232# array.
233# See https://github.com/NixOS/nixpkgs/blob/858f4db3048c5be3527e183470e93c1a72c5727c/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-fixup-hook.sh#L1-L3
234# and https://github.com/NixOS/nixpkgs/pull/313005#issuecomment-2175482920
235# shellcheck disable=2206
236if [[ -z $__structuredAttrs ]]; then
237 makeWrapperArgs=( ${makeWrapperArgs-} )
238fi
239
240# First argument is the executable you want to wrap,
241# the second is the destination for the wrapper.
242wrapDotnetProgram() {
243 local -r dotnetRuntime=@dotnetRuntime@
244 local -r wrapperPath=@wrapperPath@
245
246 # shellcheck disable=2016
247 local -r dotnetFromEnvScript='dotnetFromEnv() {
248 local dotnetPath
249 if command -v dotnet 2>&1 >/dev/null; then
250 dotnetPath=$(which dotnet) && \
251 dotnetPath=$(realpath "$dotnetPath") && \
252 dotnetPath=$(dirname "$dotnetPath") && \
253 export DOTNET_ROOT="$dotnetPath"
254 fi
255}
256dotnetFromEnv'
257
258 # shellcheck disable=2206
259 local -a runtimeDeps
260 concatTo runtimeDeps dotnetRuntimeDeps
261
262 local wrapperFlags=()
263 if (( ${#runtimeDeps[@]} > 0 )); then
264 local libraryPath=("${runtimeDeps[@]/%//lib}")
265 local OLDIFS="$IFS" IFS=':'
266 wrapperFlags+=("--suffix" "LD_LIBRARY_PATH" ":" "${libraryPath[*]}")
267 IFS="$OLDIFS"
268 fi
269
270 if [[ -z ${dotnetSelfContainedBuild-} ]]; then
271 if [[ -n ${useDotnetFromEnv-} ]]; then
272 # if dotnet CLI is available, set DOTNET_ROOT based on it. Otherwise set to default .NET runtime
273 wrapperFlags+=("--suffix" "PATH" ":" "$wrapperPath")
274 wrapperFlags+=("--run" "$dotnetFromEnvScript")
275 if [[ -n $dotnetRuntime ]]; then
276 wrapperFlags+=("--set-default" "DOTNET_ROOT" "$dotnetRuntime/share/dotnet")
277 wrapperFlags+=("--suffix" "PATH" ":" "$dotnetRuntime/bin")
278 fi
279 elif [[ -n $dotnetRuntime ]]; then
280 wrapperFlags+=("--set" "DOTNET_ROOT" "$dotnetRuntime/share/dotnet")
281 wrapperFlags+=("--prefix" "PATH" ":" "$dotnetRuntime/bin")
282 fi
283 fi
284
285 # shellcheck disable=2154
286 makeWrapper "$1" "$2" \
287 "${wrapperFlags[@]}" \
288 "${gappsWrapperArgs[@]}" \
289 "${makeWrapperArgs[@]}"
290
291 echo "installed wrapper to $2"
292}
293
294dotnetFixupPhase() {
295 local -r dotnetInstallPath="${dotnetInstallPath-$out/lib/$pname}"
296
297 local executable executableBasename
298
299 # check if dotnetExecutables is declared (including empty values, in which case we generate no executables)
300 # shellcheck disable=2154
301 if declare -p dotnetExecutables &>/dev/null; then
302 # shellcheck disable=2206
303 local -a executables
304 concatTo executables dotnetExecutables
305 for executable in "${executables[@]}"; do
306 executableBasename=$(basename "$executable")
307
308 local path="$dotnetInstallPath/$executable"
309
310 if test -x "$path"; then
311 wrapDotnetProgram "$path" "$out/bin/$executableBasename"
312 else
313 echo "Specified binary \"$executable\" is either not an executable or does not exist!"
314 echo "Looked in $path"
315 exit 1
316 fi
317 done
318 else
319 while IFS= read -r -d '' executable; do
320 executableBasename=$(basename "$executable")
321 wrapDotnetProgram "$executable" "$out/bin/$executableBasename" \;
322 done < <(find "$dotnetInstallPath" ! -name "*.dll" -executable -type f -print0)
323 fi
324}
325
326if [[ -z "${dontFixup-}" && -z "${dontDotnetFixup-}" ]]; then
327 appendToVar preFixupPhases dotnetFixupPhase
328fi
329
330dotnetInstallPhase() {
331 echo "Executing dotnetInstallHook"
332
333 runHook preInstall
334
335 local -r dotnetInstallPath="${dotnetInstallPath-$out/lib/$pname}"
336 local -r dotnetBuildType="${dotnetBuildType-Release}"
337
338 local -a projectFiles flags installFlags packFlags runtimeIds
339 concatTo projectFiles dotnetProjectFiles
340 concatTo flags dotnetFlags
341 concatTo installFlags dotnetInstallFlags
342 concatTo packFlags dotnetPackFlags
343 concatTo runtimeIds dotnetRuntimeIds
344
345 if [[ -v dotnetSelfContainedBuild ]]; then
346 if [[ -n $dotnetSelfContainedBuild ]]; then
347 installFlags+=("--self-contained")
348 else
349 installFlags+=("--no-self-contained")
350 # https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-self-contained
351 # Trimming is only available for self-contained build, so force disable it here
352 installFlags+=("-p:PublishTrimmed=false")
353 fi
354 fi
355
356 if [[ -n ${dotnetUseAppHost-} ]]; then
357 installFlags+=("-p:UseAppHost=true")
358 fi
359
360 if [[ -n ${enableParallelBuilding-} ]]; then
361 local -r maxCpuFlag="$NIX_BUILD_CORES"
362 else
363 local -r maxCpuFlag="1"
364 fi
365
366 dotnetPublish() {
367 local -r projectFile="${1-}"
368
369 local useRuntime=
370 _dotnetIsSolution "$projectFile" || useRuntime=1
371
372 for runtimeId in "${runtimeIds[@]}"; do
373 local runtimeIdFlags=()
374 if [[ -n $useRuntime ]]; then
375 runtimeIdFlags+=("--runtime" "$runtimeId")
376 fi
377
378 dotnet publish ${1+"$projectFile"} \
379 -maxcpucount:"$maxCpuFlag" \
380 -p:ContinuousIntegrationBuild=true \
381 -p:Deterministic=true \
382 -p:OverwriteReadOnlyFiles=true \
383 --output "$dotnetInstallPath" \
384 --configuration "$dotnetBuildType" \
385 --no-restore \
386 --no-build \
387 "${runtimeIdFlags[@]}" \
388 "${flags[@]}" \
389 "${installFlags[@]}"
390 done
391 }
392
393 dotnetPack() {
394 local -r projectFile="${1-}"
395
396 local useRuntime=
397 _dotnetIsSolution "$projectFile" || useRuntime=1
398
399 for runtimeId in "${runtimeIds[@]}"; do
400 local runtimeIdFlags=()
401 if [[ -n $useRuntime ]]; then
402 runtimeIdFlags+=("--runtime" "$runtimeId")
403 # set RuntimeIdentifier because --runtime is broken:
404 # https://github.com/dotnet/sdk/issues/13983
405 runtimeIdFlags+=(-p:RuntimeIdentifier="$runtimeId")
406 fi
407
408 dotnet pack ${1+"$projectFile"} \
409 -maxcpucount:"$maxCpuFlag" \
410 -p:ContinuousIntegrationBuild=true \
411 -p:Deterministic=true \
412 -p:OverwriteReadOnlyFiles=true \
413 --output "$out/share/nuget/source" \
414 --configuration "$dotnetBuildType" \
415 --no-restore \
416 --no-build \
417 "${runtimeIdFlags[@]}" \
418 "${flags[@]}" \
419 "${packFlags[@]}"
420 done
421 }
422
423 if (( ${#projectFiles[@]} == 0 )); then
424 dotnetPublish
425 else
426 local projectFile
427 for projectFile in "${projectFiles[@]}"; do
428 dotnetPublish "$projectFile"
429 done
430 fi
431
432 if [[ -n ${packNupkg-} ]]; then
433 if (( ${#projectFiles[@]} == 0 )); then
434 dotnetPack
435 else
436 local projectFile
437 for projectFile in "${projectFiles[@]}"; do
438 dotnetPack "$projectFile"
439 done
440 fi
441 fi
442
443 runHook postInstall
444
445 echo "Finished dotnetInstallHook"
446}
447
448if [[ -z "${dontDotnetInstall-}" && -z "${installPhase-}" ]]; then
449 installPhase=dotnetInstallPhase
450fi