Skip to content

Conversation

@hjyamauchi
Copy link
Contributor

Explicit module builds currently fail on Windows because direct-clang-cc1-module-build emit-pcm commands take overlaid system module map files as inputs but miss the clang VFS overlay. This change adds the overlay and fixes explicit module builds on Windows.

Explicit module builds currently fail on Windows because
direct-clang-cc1-module-build emit-pcm commands take overlaid system
module map files as inputs but miss the clang VFS overlay. This change
adds the overlay and fixes explicit module builds on Windows.
@hjyamauchi
Copy link
Contributor Author

@swift-ci please test

@owenv
Copy link
Contributor

owenv commented Nov 5, 2025

nice, I expect this might let us remove some of the Windows skips in swiftlang/swift-build#861, I was looking at some failures there due to a missing VFS post-scanning

@hjyamauchi hjyamauchi marked this pull request as ready for review November 6, 2025 17:26
CDP->startDiagnosticCapture();
}

void CompilerInstance::setUpVFSForDirectClangCC1EmitPCM() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this not in setUpVirtualFileSystemOverlays?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason of the location of the setUpVFSForDirectClangCC1EmitPCM call was because calling applyClangInvocationMapping would require the ASTContext (after setUpASTContextIfNeeded) but needs to before inputs are read (before setUpInputs()). If ASTContext could be set up before setUpVirtualFileSystemOverlays (which I haven't attempted), setUpVFSForDirectClangCC1EmitPCM could have been in setUpVirtualFileSystemOverlays.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think applyCalngInvocationMapping can probably get away with Invocation + DiagnosticEngine + an allocator instead of ASTContext.

SourceMgr.getFileSystem();
ClangInvocationFileMapping FileMapping = applyClangInvocationMapping(
*Context, nullptr, VFS, /*suppressDiagnostic=*/false);
if (!FileMapping.redirectedFiles.empty()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is correct for caching. Caching build always uses direct clang cc1 mode but the input is include-tree, which doesn't have any notion of VFS anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I understand generally that in the caching build, inputs are all loaded into CAS (include-trees) by the depscan command and then read from include-trees only by the subsequent build commands. But I am not sure how that maps into the code here or I'm under-informed about the CAS case as I have started looking into (swift) CAS builds yet.

For a caching build, I imagine, direct clang cc1 mode commands receive an an include tree via flags and setUpVirtualFileSystemOverlays will set that up so input module map file (such as vcruntime.modulemap) will be read from the include tree?

For non-caching builds, we'd need something that enables VFS. so perhaps we need to move setUpVFSForDirectClangCC1EmitPCM to setUpVirtualFileSystemOverlays and only conditionally run for non caching builds? But that doesn't match with the following comment "swift-frontend should not touch any inputs and apply any clang argument transformation when compiling".

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds correct.

IncludeTree already contains everything needed inside the CAS, including files, module maps, and all the VFS overlay has been flattened into lookup content so never pass a VFS to include tree build.

// RUN: %empty-directory(%t)

// Test that the -direct-clang-cc1-module-build is able to create a module from the VC runtime with an overlaid modulemap file
// RUN: %swift_frontend_plain -frontend -emit-pcm -module-name SAL -o %t/SAL.pcm -direct-clang-cc1-module-build "%vctoolsinstalldir\include\module.modulemap" -sdk %S/Inputs/WinSDK -Xcc -fsystem-module -Xcc -emit-module -Xcc -isystem -Xcc "%vctoolsinstalldir\include" -Xcc -cc1 -Xcc -fmodules -Xcc -fmodules-cache-path=%t/module-cache
Copy link
Contributor

Choose a reason for hiding this comment

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

This test is very insufficient.

The explicit module build is always a combination of scanner and swift-frontend. There are few different ways to tackle this problem, and I don't think this is the correct way to fix.

The concept of direct cc1 mode is that swift-frontend should not touch any inputs and apply any clang argument transformation when compiling. Everything should be done by dependency scanner. It is dependency scanner's job to construct tasks that contains the correct VFS for swift-frontend to use. I would prefer the fix to be done that way.

For test case, we have to test the combination of scanner and swift-frontend, otherwise putting a bunch of clang cc1 arguments in the tests doesn't really test what we want.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree about the test and the overall point; but, one thing that makes this trickier is that these particular VFS overlays and the files they point to (.modulemaps) are conjured up completely in-memory by the compiler.

For compilation, this is done in ClangImporter::create by calling applyClangInvocationMapping, and the scanner does something similar and calls into applyClangInvocationMapping when setting up the Clang scanner filesystem.

So now the scanner constructs invocations that assume that this virtual filesystem is present, but the CC1 task doesn't assemble the same in-memory filesystem during execution, if I understand this correctly.

Caching seems to make this particularly tricky - I don't think I have any ideas on how to fit this into include trees.

Copy link
Contributor

Choose a reason for hiding this comment

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

Include tree abstract that away already. It just needs to be seen by scanner and you don't need to pass it or add that during compilation.

If the in memory overlay is needed for non-cache build, it is fine. The overlay need not be there for caching build and please add a test case for caching (if that works, if not, we can hold that off for now).

Copy link
Contributor Author

@hjyamauchi hjyamauchi Nov 6, 2025

Choose a reason for hiding this comment

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

The concept of direct cc1 mode is that swift-frontend should not touch any inputs and apply any clang argument transformation when compiling. Everything should be done by dependency scanner. It is dependency scanner's job to construct tasks that contains the correct VFS for swift-frontend to use. I would prefer the fix to be done that way.

So it sounds like instead of setUpVFSForDirectClangCC1EmitPCM, the vfs overlay somehow needs to be passed to the build (emit-pcm) command probably via flags. I will think about this and come back.

I see that we need the depscan and the build commands in the test.

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