Skip to content

Enable unity build repo-wide via WSL_UNITY_BATCH_SIZE - #41441

Merged
ggarzia-MSFT merged 7 commits into
masterfrom
user/ggarzia/unity-build
Sep 2, 2026
Merged

Enable unity build repo-wide via WSL_UNITY_BATCH_SIZE#41441
ggarzia-MSFT merged 7 commits into
masterfrom
user/ggarzia/unity-build

Conversation

@ggarzia-MSFT

@ggarzia-MSFT ggarzia-MSFT commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary of the Pull Request

Enables CMake unity (jumbo) builds across the entire repo, driven by the existing WSL_UNITY_BATCH_SIZE knob. Previously only two targets (wslclib and wsltests) opted in individually; this replaces those per-target opt-ins with a single global setting and fixes the fallout that surfaced once every target was batched.

The checked-in default remains 0 (unity build disabled), so local developer builds are unchanged unless the knob is set explicitly.

PR Checklist

  • Closes: Link to issue #xxx
  • Communication: I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected
  • Tests: Added/updated if needed and all pass
  • Localization: All end user facing strings can be localized
  • Dev docs: Added/updated if needed
  • Documentation updated: If checked, please file a pull request on our docs repo and link it here: #xxx

Detailed Description of the Pull Request / Additional comments

Enabling unity build

The root CMakeLists.txt now sets CMAKE_UNITY_BUILD, CMAKE_UNITY_BUILD_MODE and CMAKE_UNITY_BUILD_BATCH_SIZE when WSL_UNITY_BATCH_SIZE is greater than zero. The block is placed after the FetchContent_MakeAvailable calls, since these variables only initialize the UNITY_BUILD property of targets created afterwards — this keeps GSL, nlohmann, yaml-cpp and boost building exactly as they do today.

The per-target opt-in blocks in src/windows/wslc/CMakeLists.txt and test/windows/CMakeLists.txt are removed in favor of the global setting.

test/windows/wslc/CMakeLists.txt also carried a set_source_files_properties call applying /Yuprecomp.h and SKIP_UNITY_BUILD_INCLUSION to the WSLC test sources. Both were no-ops: source file properties are scoped to the directory that sets them, and wsltests is defined in the parent directory, so neither ever reached the target. Those sources have therefore always been batched along with the rest of wsltests, and removing the call changes no build behavior — the precompiled header comes from target_precompile_headers(wsltests REUSE_FROM common).

Opt-outs

Three categories of source needed to stay out of the batched translation units:

  • MIDL proxy/stub targets (wslserviceproxystub, wsldevicehostproxystub, wslinstallerproxystub) are marked UNITY_BUILD OFF. Each generated _p.c declares its own MIDL_TYPE_FORMAT_STRING struct with a different size, so merging them produces redefinition errors.
  • Files carrying per-source compile flags — currently just WSLCUserSettings.cpp, which needs a yaml-cpp include directory and YAML_CPP_STATIC_DEFINE — are marked SKIP_UNITY_BUILD_INCLUSION. CMake does not exclude these automatically: the original source is marked excluded from build and its flags are silently dropped.
  • The generated module.g.cpp in the C++/WinRT projection is marked SKIP_UNITY_BUILD_INCLUSION. It mixes classic COM and C++/WinRT headers, so sharing a translation unit with a projection source that has using namespace winrt::Windows::Foundation; at global scope makes IUnknown ambiguous. The remaining 22 projection sources still batch normally.

Name collision in wslcsession

One genuine unity-build conflict was found. WSLCContainer.cpp had a global using wsl::windows::service::wslc::WSLCPortMapping; that collided with the IDL-generated global ::WSLCPortMapping from wslc.h. Once WSLCContainer.cpp and WSLCVirtualMachine.cpp landed in the same translation unit, ::WSLCPortMapping silently changed meaning and VMPortMapping::FromWSLCPortMapping no longer matched its header declaration.

The fix removes the global using-declaration and fully qualifies the two affected return types. Only return types needed qualification: for out-of-line member definitions the return type is looked up at namespace scope, whereas parameter types are looked up in class scope.

Validation Steps Performed

  • Full clean rebuild (cmake . -DWSL_UNITY_BATCH_SIZE=4 followed by cmake --build . --clean-first -- -m) on x64 Debug, matching the batch size used in CI. Zero errors; all 18 binaries produced, including wsl.exe, wslservice.exe, wslc.exe, wslcsdk.dll, wslinstaller.exe and wsltests.dll.
  • Verified the default path is unaffected: with WSL_UNITY_BATCH_SIZE left at its default of 0, no unity sources are generated and every target compiles per-file as before.
  • Ran clang-format 19.1.5 over the changed sources; no diff.

Why the link changes are here

Enabling unity batching changes static-library granularity, which pulls in link
dependencies that per-file builds never selected.

common is a static library, so at the default of WSL_UNITY_BATCH_SIZE=0 the linker
picks archive members one source file at a time. Only install.cpp, WslClient.cpp and
wslutil.cpp call MSI, and only install.cpp calls WinTrust, so targets such as
wslrelay and wslhost never pull those members in — dumpbin /imports on a default
build shows no msi.dll or WINTRUST.dll imports for them.

Batching merges those files into an object that also holds members these targets do need,
so the references come along:

  • common gains a PUBLIC link line for HCS, MSI, VirtDisk and configfile.
    COMMON_LINK_LIBRARIES carries neither MSI nor HCS, so propagating from common is
    what keeps every consumer from restating them.
  • Five targets gain delayimp.lib and /DELAYLOAD:msi.dll /DELAYLOAD:WINTRUST.dll, so
    the newly reachable references do not become eager startup imports of DLLs those
    binaries never call into. This matches wsl, wslg and wslcsdk, which already use
    the same pair.

Neither change is gated on WSL_UNITY_BATCH_SIZE because the delay-load is inert at
batch 0 — there are no imports from those DLLs to defer — and the PUBLIC line only
restates dependencies common genuinely has.

Copilot AI lite review requested due to automatic review settings August 25, 2026 22:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enables repo-wide CMake unity (jumbo) builds gated by the existing WSL_UNITY_BATCH_SIZE option, replacing prior per-target opt-ins. It also adds targeted unity opt-outs and resolves a unity-induced type-name collision in wslcsession.

Changes:

  • Enable unity build globally (for targets created after dependency FetchContent), controlled by WSL_UNITY_BATCH_SIZE.
  • Add opt-outs for generated MIDL proxy/stub targets and specific per-source-flag/generated sources that are incompatible with unity builds.
  • Fix WSLCContainer.cpp return-type lookup ambiguity by removing a global using and fully qualifying affected return types; add delay-load linker settings consistently across several Windows targets.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated no comments.

Show a summary per file
File Description
CMakeLists.txt Enables global unity build configuration when WSL_UNITY_BATCH_SIZE > 0, after FetchContent dependencies are instantiated.
src/windows/common/CMakeLists.txt Propagates common’s link dependencies via PUBLIC and excludes WSLCUserSettings.cpp from unity build due to per-source settings.
src/windows/libwsl/CMakeLists.txt Adds delay-load support (delayimp + /DELAYLOAD) for MSI/WinTrust.
src/windows/service/stub/CMakeLists.txt Disables unity build for the MIDL proxy/stub target to avoid generated-symbol collisions.
src/windows/wsldevicehoststub/CMakeLists.txt Disables unity build for the MIDL proxy/stub target to avoid generated-symbol collisions.
src/windows/wslinstaller/stub/CMakeLists.txt Disables unity build for the MIDL proxy/stub target to avoid generated-symbol collisions.
src/windows/wslc/CMakeLists.txt Removes per-target unity opt-in; adds delay-load linker settings to wslc.exe.
src/windows/WslcSDK/winrt/CMakeLists.txt Excludes generated module.g.cpp from unity build due to COM/C++/WinRT header interaction.
src/windows/wslcsession/CMakeLists.txt Adds delay-load linker settings to wslcsession.
src/windows/wslcsession/WSLCContainer.cpp Resolves unity-build-induced WSLCPortMapping name collision by removing global using and fully qualifying return types.
src/windows/wslhost/CMakeLists.txt Adds delay-load linker settings to wslhost.
src/windows/wslrelay/CMakeLists.txt Adds delay-load linker settings to wslrelay.
test/windows/CMakeLists.txt Removes per-target unity opt-in for wsltests (now governed by global setting).
test/windows/wslc/CMakeLists.txt Excludes WSLC test sources from unity build because they rely on per-source PCH compile flags.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@ggarzia-MSFT
ggarzia-MSFT marked this pull request as ready for review August 25, 2026 23:16
@ggarzia-MSFT
ggarzia-MSFT requested review from a team as code owners August 25, 2026 23:16
Copilot AI review requested due to automatic review settings August 26, 2026 17:49
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 33 out of 33 changed files in this pull request and generated no new comments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 26, 2026 18:04
@ggarzia-MSFT
ggarzia-MSFT force-pushed the user/ggarzia/unity-build branch from a8b8ff1 to ab9c4c7 Compare August 26, 2026 18:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 33 out of 33 changed files in this pull request and generated 3 comments.

Comment thread src/windows/common/CMakeLists.txt
Comment thread src/windows/common/WslClient.cpp
Comment thread src/windows/wslc/core/TableOutput.h Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 26, 2026 18:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 33 out of 33 changed files in this pull request and generated no new comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

test/windows/wslc/CMakeLists.txt:22

  • SKIP_UNITY_BUILD_INCLUSION ON is being applied to ${WSLC_TEST_SOURCES} (all *.cpp under test/windows/wslc), which opts the entire WSLC test suite out of unity builds. That undermines the repo-wide unity build goal; if only specific files need special per-source flags, restrict the skip to just those files (or rely on target_precompile_headers(wsltests REUSE_FROM common) at the parent level).
set_source_files_properties(${WSLC_TEST_SOURCES}
    PROPERTIES
    COMPILE_FLAGS "/Yuprecomp.h"
    SKIP_UNITY_BUILD_INCLUSION ON
)

…build

# Conflicts:
#	src/windows/common/string.hpp
#	src/windows/wslc/tasks/ImageTasks.cpp
#	src/windows/wslc/tasks/VolumeTasks.cpp
Copilot AI review requested due to automatic review settings August 28, 2026 22:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 1 comment.

Comment thread test/windows/wslc/CMakeLists.txt Outdated

@ranm-msft ranm-msft left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unity wiring itself looks right to me - setting the CMAKE_UNITY_BUILD* variables after FetchContent_MakeAvailable is the correct place, and the collision fixes (qualifying socket::CancellableAccept / string::IsEqual, ::socket, hoisting the duplicate InitializeFileOffset and the containerd timeouts into headers) all read as genuine merged-TU problems rather than workarounds. Two questions before I sign off.

1. The unconditional link changes aren't covered by the description. common gains a PUBLIC link line for HCS/MSI/VirtDisk/configfile, and five targets gain delayimp.lib + /DELAYLOAD:msi.dll /DELAYLOAD:WINTRUST.dll. Neither is gated on WSL_UNITY_BATCH_SIZE.

I assume this is unity fallout: batching common coarsens static-library object granularity, so pulling one symbol now drags in MSI/WinTrust references that previously lived in unselected archive members, and the delay-load keeps those from becoming eager startup imports. If that's the reasoning, could it go in the description? As written it reads as unrelated scope.

Worth caveating the "local developer builds are unchanged" line too - delay-loading WINTRUST.dll moves a load-time failure to a first-call exception in the default (non-unity) build as well.

2. Coverage inverts. .pipelines/build-job.yml passes -DWSL_UNITY_BATCH_SIZE=4, so after this PR every first-party target is built by CI only in unity mode, while the checked-in default of 0 that local devs get has no CI leg. Previously most targets were still compiled per-file in CI. Is a non-unity leg worth keeping, or is manual validation of the default path considered enough?

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 31, 2026 15:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated no new comments.

@ggarzia-MSFT

ggarzia-MSFT commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

ranm-msft

  1. yes this is unity fallout, updated the description
  2. Yes manual builds on dev machines are enough validation. There is no case I am aware of where a unity build will succeed and a non unity build will fail.

@ranm-msft ranm-msft left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified the cleanup - this is a genuine no-op. wsltests is created in test/windows/CMakeLists.txt, and source file properties are only visible to targets created in the same directory scope, so the /Yuprecomp.h and SKIP_UNITY_BUILD_INCLUSION properties set in the child directory never reached it. Calling target_sources() from the child doesn't change that. PCH is unaffected either way, since target_precompile_headers(wsltests REUSE_FROM common) is target-level. Worth noting for future readers that this doesn't newly opt those files into batching - they were already eligible. If per-source flags are ever genuinely needed there, set_source_files_properties(... TARGET_DIRECTORY wsltests ...) is the escape hatch.

Thanks for the description rewrite on the link changes - the archive-member granularity explanation plus the dumpbin /imports check fully answers my first point.

Two small follow-ups:

  1. The "Opt-outs" bullet still lists "the /Yuprecomp.h source under test/windows/wslc" as marked SKIP_UNITY_BUILD_INCLUSION. That half is stale as of this commit.

  2. On the non-unity leg: there is a counterexample in the other direction. A .cpp missing a direct include can compile inside a batch because an earlier neighbour in the same translation unit already included the header, then fail when compiled on its own; using directives and macro state can leak the same way. So unity success doesn't imply non-unity success, and the checked-in default of 0 is the configuration CI never builds. Happy to treat a second leg as a cost tradeoff - just flagging that the failure direction does exist.

Approving - nothing here blocks.

@ggarzia-MSFT

Copy link
Copy Markdown
Contributor Author

Verified the cleanup - this is a genuine no-op. wsltests is created in test/windows/CMakeLists.txt, and source file properties are only visible to targets created in the same directory scope, so the /Yuprecomp.h and SKIP_UNITY_BUILD_INCLUSION properties set in the child directory never reached it. Calling target_sources() from the child doesn't change that. PCH is unaffected either way, since target_precompile_headers(wsltests REUSE_FROM common) is target-level. Worth noting for future readers that this doesn't newly opt those files into batching - they were already eligible. If per-source flags are ever genuinely needed there, set_source_files_properties(... TARGET_DIRECTORY wsltests ...) is the escape hatch.

Thanks for the description rewrite on the link changes - the archive-member granularity explanation plus the dumpbin /imports check fully answers my first point.

Two small follow-ups:

  1. The "Opt-outs" bullet still lists "the /Yuprecomp.h source under test/windows/wslc" as marked SKIP_UNITY_BUILD_INCLUSION. That half is stale as of this commit.
  2. On the non-unity leg: there is a counterexample in the other direction. A .cpp missing a direct include can compile inside a batch because an earlier neighbour in the same translation unit already included the header, then fail when compiled on its own; using directives and macro state can leak the same way. So unity success doesn't imply non-unity success, and the checked-in default of 0 is the configuration CI never builds. Happy to treat a second leg as a cost tradeoff - just flagging that the failure direction does exist.

Approving - nothing here blocks.

  1. Fixed
  2. Good point, that failure direction does exist. However, given that we have not had an issue of this kind happen yet, and that devs will be manually verifying non unity builds, the risk is low. Happy to add this later if it becomes an issue

Copilot AI review requested due to automatic review settings September 2, 2026 01:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

common now links configfile before the configfile target is defined (subdir order), which can break targeted/incremental builds due to missing CMake target dependencies.

Review details

Suppressed comments (1)

src/windows/common/CMakeLists.txt:165

  • common is now linked PUBLIC to configfile, but configfile is added as a subproject after src/windows/common in the root CMakeLists.txt (see add_subdirectory(src/windows/common) before add_subdirectory(src/shared/configfile)). In that ordering, configfile will be treated as a plain library name rather than a CMake target, so consumers may not get a proper target dependency and targeted builds (e.g., building only wslcsession) can fail because configfile.lib isn't built first.
# common calls into HCS, HNS, the IP helper API, the VHD APIs, MSI, WinTrust and
# the config file parser, so anything linking common needs those libraries too.
# PUBLIC propagates them instead of making each consumer restate them.
target_link_libraries(common PUBLIC ${HCS_LINK_LIBRARIES} ${MSI_LINK_LIBRARIES} VirtDisk.lib configfile)

  • Files reviewed: 30/30 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@ggarzia-MSFT
ggarzia-MSFT merged commit 7117e32 into master Sep 2, 2026
12 checks passed
@ggarzia-MSFT
ggarzia-MSFT deleted the user/ggarzia/unity-build branch September 2, 2026 18:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants