Skip to content

Commit d7a5d48

Browse files
dalexsotoCopilot
andcommitted
[build] Fix parallel make hang by disabling dotnet build servers
Parallel make (e.g. 'make all -j8', 'make world') has been hanging indefinitely at the end of the build. This is a long-standing issue (#13355) that has been patched three times (#15407, #21315, #22300) without fully fixing the root cause. The problem: when using parallel make, GNU Make uses a jobserver with pipe-based file descriptors to coordinate sub-makes. The dotnet CLI can start background build servers (MSBuild server, Roslyn/VBCSCompiler) that inherit these file descriptors but never close them. Make then waits indefinitely for those file descriptors to close, thinking there are still active jobs. The previous workaround attempted to shut down and force-kill dotnet processes after the build via a 'shutdown-build-server' target. This approach was unreliable because: - The shutdown ran from a double-colon all-hook:: rule with no prerequisites, so with -j it could execute in parallel with (or before) the actual build, killing nothing. - Build servers started by later subdirectories (e.g. tests/) after the dotnet/ shutdown were never killed. - The process-matching regex pattern might not match all server processes. The fix: disable build servers entirely via environment variables in Make.config: - DOTNET_CLI_USE_MSBUILD_SERVER=0: prevents the MSBuild server https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-server https://github.com/dotnet/msbuild/blob/main/documentation/MSBuild-Server.md - UseSharedCompilation=false: prevents the Roslyn compiler server (VBCSCompiler) dotnet/roslyn#27975 - MSBUILDDISABLENODEREUSE=1: prevents MSBuild node reuse https://github.com/dotnet/msbuild/wiki/MSBuild-Tips-&-Tricks This eliminates the root cause - no background servers means no inherited file descriptors means no hang. The shutdown-build-server target and its invocations are removed as they are no longer needed. Additionally, 'make world' now prints the installed workloads at the end of the build for visibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 809b19e commit d7a5d48

File tree

3 files changed

+11
-23
lines changed

3 files changed

+11
-23
lines changed

Make.config

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,15 @@ DOTNET_DIR=$(abspath $(TOP)/builds/downloads/$(DOTNET_INSTALL_NAME))
403403
export DOTNET_ROOT=$(DOTNET_DIR)
404404
# dotnet now is being looked up in the PATH
405405
export PATH := $(DOTNET_DIR):$(PATH)
406+
407+
# Disable build servers to prevent parallel make from hanging.
408+
# Build servers (MSBuild server, Roslyn/VBCSCompiler) inherit jobserver file
409+
# descriptors from make, and don't close them when daemonizing. This prevents
410+
# make from detecting that all jobs have finished, causing it to hang
411+
# indefinitely at the end of the build.
412+
export DOTNET_CLI_USE_MSBUILD_SERVER=0
413+
export UseSharedCompilation=false
414+
export MSBUILDDISABLENODEREUSE=1
406415
DOTNET=$(DOTNET_DIR)/dotnet
407416
DOTNET_BCL_DIR:=$(abspath $(TOP)/packages/microsoft.netcore.app.ref/$(DOTNET_BCL_VERSION)/ref/$(DOTNET_TFM))
408417
# when bumping to a new .NET version, there may be a period when some parts of .NET is still on the old .NET version, so handle that here for DOTNET_BCL_DIR

Makefile

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ world: check-system
3232
@$(MAKE) reset-versions
3333
@$(MAKE) all -j8
3434
@$(MAKE) install -j8
35+
@echo "Build is done, the following workloads were built:"
36+
@$(DOTNET) workload list
3537

3638
.PHONY: check-system
3739
check-system:
@@ -68,9 +70,6 @@ install-hook::
6870
exit 1; \
6971
fi
7072

71-
all-hook install-hook::
72-
$(Q) $(MAKE) -C dotnet shutdown-build-server
73-
7473
dotnet-install-system:
7574
$(Q) $(MAKE) -C dotnet install-system
7675

dotnet/Makefile

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -536,23 +536,3 @@ clean-local::
536536
$(Q) $(DOTNET) restore package/workaround-for-maccore-issue-2427/restore.csproj /bl:package/workaround-for-maccore-issue-2427/restore.binlog $(MSBUILD_VERBOSITY)
537537
$(Q) touch $@
538538

539-
# We need to shut down the builder server, because:
540-
# We're using parallel make, and parallel make will start a jobserver, managed by file descriptors, where these file descriptors must be closed in all subprocesses for make to realize it's done.
541-
# 'dotnet pack' might have started a build server
542-
# The build server does not close any file descriptors it may have inherited when daemonizing itself.
543-
# Thus the build server (which will still be alive after we're done building here) might have a file descriptor open which make is waiting for.
544-
# The proper fix is to fix the build server to close its file descriptors.
545-
# The intermediate working is to shut down the build server instead. An alternative solution would be to pass /p:UseSharedCompilation=false to 'dotnet pack' to disable the usage of the build server.
546-
#
547-
# The 'shutdown-build-server' is executed in a sub-make (and not as a dependency to the all-hook target),
548-
# to make sure it's executed after everything else is built in this file.
549-
all-hook::
550-
$(Q) $(MAKE) shutdown-build-server
551-
552-
shutdown-build-server:
553-
$(Q) echo "Shutting down build servers:"
554-
$(Q) $(DOTNET) build-server shutdown | sed 's/^/ /' || true
555-
$(Q) echo "Listing .NET processes still alive:"
556-
$(Q) pgrep -lf "^$(DOTNET)" | sed 's/^/ /' || true
557-
$(Q) echo "Killing the above mentioned processes."
558-
$(Q) pkill -9 -f "^$(DOTNET)" | sed 's/^/ /' || true

0 commit comments

Comments
 (0)