Skip to content

Commit 0c4b596

Browse files
[DependencyScan] Correct setup clang VFS for dependency scanning
Currently, dependency scanner is not reporting the redirecting files that are baked inside swift-frontend for platform support. This causes dependency scanner returns virtual path for those files, and swift-driver/build-system will not be able to correct validate the files on incremental build, causing incremental build to be almost clean builds. This behavior issue is caused by the dependency scanning file system layer inside clang dependency scanner that caches stats. If the redirecting files are created underneath the layer, the real path is lost. This fixes the issue by moving the redirecting files above the caching layer using `-ivfsoverlay` option. In addition to that, this commit also unifies how clang importer and clang dependency scanner initiate the VFS, making the logic much simpler.
1 parent 6b3e561 commit 0c4b596

File tree

8 files changed

+192
-177
lines changed

8 files changed

+192
-177
lines changed

include/swift/ClangImporter/ClangImporter.h

Lines changed: 27 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,18 @@ typedef llvm::PointerUnion<const clang::Decl *, const clang::MacroInfo *,
148148
const clang::Type *, const clang::Token *>
149149
ImportDiagnosticTarget;
150150

151+
/// Addition file mapping information for ClangImporter.
152+
struct ClangInvocationFileMapping {
153+
/// Mapping from a file name to an existing file path.
154+
SmallVector<std::pair<std::string, std::string>, 2> redirectedFiles;
155+
156+
/// MemoryBuffer that represents the file name and content to overload.
157+
SmallVector<std::pair<std::string, std::string>, 2> overridenFiles;
158+
159+
bool requiresBuiltinHeadersInSystemModules = false;
160+
bool needSystemVFSOverlay = false;
161+
};
162+
151163
/// Class that imports Clang modules into Swift, mapping directly
152164
/// from Clang ASTs over to Swift ASTs.
153165
class ClangImporter final : public ClangModuleLoader {
@@ -166,7 +178,7 @@ class ClangImporter final : public ClangModuleLoader {
166178
private:
167179
Implementation &Impl;
168180

169-
bool requiresBuiltinHeadersInSystemModules = false;
181+
ClangInvocationFileMapping clangFileMapping;
170182

171183
ClangImporter(ASTContext &ctx, DependencyTracker *tracker,
172184
DWARFImporterDelegate *dwarfImporterDelegate);
@@ -205,6 +217,14 @@ class ClangImporter final : public ClangModuleLoader {
205217
DWARFImporterDelegate *dwarfImporterDelegate = nullptr,
206218
bool ignoreFileMapping = false);
207219

220+
static std::string getClangSystemOverlayFile(const SearchPathOptions &Opts);
221+
222+
static llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
223+
computeClangImporterFileSystem(
224+
const ASTContext &ctx, const ClangInvocationFileMapping &fileMapping,
225+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> baseFS,
226+
bool suppressDiagnostics = false);
227+
208228
std::vector<std::string>
209229
getClangDriverArguments(ASTContext &ctx, bool ignoreClangTarget = false);
210230

@@ -531,6 +551,11 @@ class ClangImporter final : public ClangModuleLoader {
531551

532552
std::string getClangModuleHash() const;
533553

554+
/// Get clang file mapping.
555+
const ClangInvocationFileMapping &getClangFileMapping() const {
556+
return clangFileMapping;
557+
}
558+
534559
/// Get clang import creation cc1 args for swift explicit module build.
535560
std::vector<std::string> getSwiftExplicitModuleDirectCC1Args() const;
536561

@@ -883,56 +908,17 @@ std::optional<ResultConvention>
883908
getCxxRefConventionWithAttrs(const clang::Decl *decl);
884909
} // namespace importer
885910

886-
struct ClangInvocationFileMapping {
887-
/// Mapping from a file name to an existing file path.
888-
SmallVector<std::pair<std::string, std::string>, 2> redirectedFiles;
889-
890-
/// Mapping from a file name to a string of characters that represents the
891-
/// contents of the file.
892-
SmallVector<std::pair<std::string, std::string>, 1> overridenFiles;
893-
894-
bool requiresBuiltinHeadersInSystemModules;
895-
};
896-
897-
class ClangInvocationFileMappingContext {
898-
public:
899-
const LangOptions &LangOpts;
900-
SearchPathOptions &SearchPathOpts;
901-
ClangImporterOptions &ClangImporterOpts;
902-
const CASOptions &CASOpts;
903-
DiagnosticEngine &Diags;
904-
905-
ClangInvocationFileMappingContext(
906-
const LangOptions &LangOpts, SearchPathOptions &SearchPathOpts,
907-
ClangImporterOptions &ClangImporterOpts, const CASOptions &CASOpts,
908-
DiagnosticEngine &Diags)
909-
: LangOpts(LangOpts), SearchPathOpts(SearchPathOpts),
910-
ClangImporterOpts(ClangImporterOpts), CASOpts(CASOpts),
911-
Diags(Diags) {}
912-
913-
ClangInvocationFileMappingContext(const swift::ASTContext &Ctx);
914-
};
915-
916911
/// On Linux, some platform libraries (glibc, libstdc++) are not modularized.
917912
/// We inject modulemaps for those libraries into their include directories
918913
/// to allow using them from Swift.
919914
///
920915
/// `suppressDiagnostic` prevents us from emitting warning messages when we
921916
/// are unable to find headers.
922917
ClangInvocationFileMapping getClangInvocationFileMapping(
923-
const ClangInvocationFileMappingContext &ctx,
918+
const ASTContext &ctx,
924919
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs = nullptr,
925920
bool suppressDiagnostic = false);
926921

927-
/// Apply the given file mapping to the specified 'fileSystem', used
928-
/// primarily to inject modulemaps on platforms with non-modularized
929-
/// platform libraries.
930-
ClangInvocationFileMapping applyClangInvocationMapping(
931-
const ClangInvocationFileMappingContext &ctx,
932-
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> baseVFS,
933-
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> &fileSystem,
934-
bool suppressDiagnostics = false);
935-
936922
/// Information used to compute the access level of inherited C++ members.
937923
class ClangInheritanceInfo {
938924
/// The cumulative inheritance access specifier, that is used to compute the

lib/ClangImporter/ClangImporter.cpp

Lines changed: 105 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -828,10 +828,9 @@ getEmbedBitcodeInvocationArguments(std::vector<std::string> &invocationArgStrs,
828828
});
829829
}
830830

831-
void
832-
importer::addCommonInvocationArguments(
833-
std::vector<std::string> &invocationArgStrs,
834-
ASTContext &ctx, bool requiresBuiltinHeadersInSystemModules,
831+
void importer::addCommonInvocationArguments(
832+
std::vector<std::string> &invocationArgStrs, ASTContext &ctx,
833+
bool requiresBuiltinHeadersInSystemModules, bool needSystemVFSOverlay,
835834
bool ignoreClangTarget) {
836835
using ImporterImpl = ClangImporter::Implementation;
837836
llvm::Triple triple = ctx.LangOpts.Target;
@@ -1003,6 +1002,12 @@ importer::addCommonInvocationArguments(
10031002
invocationArgStrs.push_back("-Xclang");
10041003
invocationArgStrs.push_back("-fbuiltin-headers-in-system-modules");
10051004
}
1005+
1006+
if (needSystemVFSOverlay) {
1007+
invocationArgStrs.push_back("-ivfsoverlay");
1008+
invocationArgStrs.push_back(
1009+
ClangImporter::getClangSystemOverlayFile(ctx.SearchPathOpts));
1010+
}
10061011
}
10071012

10081013
bool ClangImporter::canReadPCH(StringRef PCHFilename) {
@@ -1151,6 +1156,85 @@ ClangImporter::getOrCreatePCH(const ClangImporterOptions &ImporterOptions,
11511156
return PCHFilename.value();
11521157
}
11531158

1159+
std::string
1160+
ClangImporter::getClangSystemOverlayFile(const SearchPathOptions &Opts) {
1161+
llvm::SmallString<256> overlayPath(Opts.RuntimeResourcePath);
1162+
llvm::sys::path::append(overlayPath,
1163+
Implementation::clangSystemVFSOverlayName);
1164+
return overlayPath.str().str();
1165+
}
1166+
1167+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
1168+
ClangImporter::computeClangImporterFileSystem(
1169+
const ASTContext &ctx, const ClangInvocationFileMapping &fileMapping,
1170+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> baseFS,
1171+
bool suppressDiagnostics) {
1172+
// Configure ClangImporter file system. There are two situations:
1173+
// * If caching is used, thus file system is immutable, the one immutable file
1174+
// system is shared between swift frontend and ClangImporter.
1175+
// * Otherwise, ClangImporter file system is configure from scratch from
1176+
// VFS in SourceMgr using ivfsoverlay options.
1177+
if (ctx.CASOpts.HasImmutableFileSystem)
1178+
return baseFS;
1179+
1180+
auto importerOpts = ctx.ClangImporterOpts;
1181+
auto fileSystem = baseFS;
1182+
std::unique_ptr<llvm::MemoryBuffer> redirectYAMLFile;
1183+
if (!fileMapping.redirectedFiles.empty()) {
1184+
if (importerOpts.DumpClangDiagnostics) {
1185+
llvm::errs() << "clang importer redirected file mappings:\n";
1186+
for (const auto &mapping : fileMapping.redirectedFiles) {
1187+
llvm::errs() << " mapping real file '" << mapping.second
1188+
<< "' to virtual file '" << mapping.first << "'\n";
1189+
}
1190+
llvm::errs() << "\n";
1191+
}
1192+
// Create a vfs overlay map for all redirects.
1193+
llvm::vfs::YAMLVFSWriter vfsWriter;
1194+
vfsWriter.setUseExternalNames(true);
1195+
for (const auto &mapping : fileMapping.redirectedFiles)
1196+
vfsWriter.addFileMapping(mapping.first, mapping.second);
1197+
1198+
std::string vfsYAML;
1199+
llvm::raw_string_ostream os(vfsYAML);
1200+
vfsWriter.write(os);
1201+
1202+
redirectYAMLFile = llvm::MemoryBuffer::getMemBufferCopy(
1203+
vfsYAML, getClangSystemOverlayFile(ctx.SearchPathOpts));
1204+
}
1205+
1206+
if (!fileMapping.overridenFiles.empty() || redirectYAMLFile) {
1207+
auto overridenVFS =
1208+
llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
1209+
for (auto &file : fileMapping.overridenFiles) {
1210+
if (importerOpts.DumpClangDiagnostics) {
1211+
llvm::errs() << "clang importer overriding file '" << file.first
1212+
<< "' with the following contents:\n";
1213+
llvm::errs() << file.second << "\n";
1214+
}
1215+
// Note MemoryBuffer is guaranteeed to be null-terminated.
1216+
overridenVFS->addFile(file.first, 0,
1217+
llvm::MemoryBuffer::getMemBufferCopy(file.second));
1218+
}
1219+
if (redirectYAMLFile) {
1220+
if (importerOpts.DumpClangDiagnostics) {
1221+
llvm::errs() << "clang importer overriding file for redirects'"
1222+
<< redirectYAMLFile->getBufferIdentifier()
1223+
<< "' with the following contents:\n";
1224+
llvm::errs() << redirectYAMLFile->getBuffer() << "\n";
1225+
}
1226+
std::string yamlFile = redirectYAMLFile->getBufferIdentifier().str();
1227+
overridenVFS->addFile(yamlFile, 0, std::move(redirectYAMLFile));
1228+
}
1229+
auto overlayVFS =
1230+
llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(fileSystem);
1231+
overlayVFS->pushOverlay(std::move(overridenVFS));
1232+
fileSystem = std::move(overlayVFS);
1233+
}
1234+
1235+
return fileSystem;
1236+
}
1237+
11541238
std::vector<std::string>
11551239
ClangImporter::getClangDriverArguments(ASTContext &ctx, bool ignoreClangTarget) {
11561240
assert(!ctx.ClangImporterOpts.DirectClangCC1ModuleBuild &&
@@ -1168,8 +1252,10 @@ ClangImporter::getClangDriverArguments(ASTContext &ctx, bool ignoreClangTarget)
11681252
getEmbedBitcodeInvocationArguments(invocationArgStrs, ctx);
11691253
break;
11701254
}
1171-
addCommonInvocationArguments(invocationArgStrs, ctx,
1172-
requiresBuiltinHeadersInSystemModules, ignoreClangTarget);
1255+
addCommonInvocationArguments(
1256+
invocationArgStrs, ctx,
1257+
clangFileMapping.requiresBuiltinHeadersInSystemModules,
1258+
clangFileMapping.needSystemVFSOverlay, ignoreClangTarget);
11731259
return invocationArgStrs;
11741260
}
11751261

@@ -1235,6 +1321,10 @@ std::optional<std::vector<std::string>> ClangImporter::getClangCC1Arguments(
12351321
CI->getTargetOpts().DarwinTargetVariantTriple = ctx.LangOpts.TargetVariant->str();
12361322
}
12371323

1324+
if (clangFileMapping.needSystemVFSOverlay)
1325+
CI->getHeaderSearchOpts().AddVFSOverlayFile(
1326+
getClangSystemOverlayFile(ctx.SearchPathOpts));
1327+
12381328
// Forward the index store path. That information is not passed to scanner
12391329
// and it is cached invariant so we don't want to re-scan if that changed.
12401330
CI->getFrontendOpts().IndexStorePath = ctx.ClangImporterOpts.IndexStorePath;
@@ -1348,18 +1438,16 @@ std::unique_ptr<ClangImporter> ClangImporter::create(
13481438
}
13491439
}
13501440

1351-
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
1352-
ctx.SourceMgr.getFileSystem();
1353-
1354-
ClangInvocationFileMapping fileMapping =
1355-
applyClangInvocationMapping(ctx, nullptr, VFS, ignoreFileMapping);
1356-
1357-
importer->requiresBuiltinHeadersInSystemModules =
1358-
fileMapping.requiresBuiltinHeadersInSystemModules;
1441+
importer->clangFileMapping = getClangInvocationFileMapping(
1442+
ctx, ctx.SourceMgr.getFileSystem(), ignoreFileMapping);
1443+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs =
1444+
computeClangImporterFileSystem(ctx, importer->clangFileMapping,
1445+
ctx.SourceMgr.getFileSystem(),
1446+
ignoreFileMapping);
13591447

13601448
// Create a new Clang compiler invocation.
13611449
{
1362-
if (auto ClangArgs = importer->getClangCC1Arguments(ctx, VFS))
1450+
if (auto ClangArgs = importer->getClangCC1Arguments(ctx, vfs))
13631451
importer->Impl.ClangArgs = *ClangArgs;
13641452
else
13651453
return nullptr;
@@ -1373,7 +1461,7 @@ std::unique_ptr<ClangImporter> ClangImporter::create(
13731461
llvm::errs() << "'\n";
13741462
}
13751463
importer->Impl.Invocation = createClangInvocation(
1376-
importer.get(), importerOpts, VFS, importer->Impl.ClangArgs);
1464+
importer.get(), importerOpts, vfs, importer->Impl.ClangArgs);
13771465
if (!importer->Impl.Invocation)
13781466
return nullptr;
13791467
}
@@ -1424,7 +1512,7 @@ std::unique_ptr<ClangImporter> ClangImporter::create(
14241512
auto actualDiagClient = std::make_unique<ClangDiagnosticConsumer>(
14251513
importer->Impl, instance.getDiagnosticOpts(),
14261514
importerOpts.DumpClangDiagnostics);
1427-
instance.createVirtualFileSystem(std::move(VFS), actualDiagClient.get());
1515+
instance.createVirtualFileSystem(std::move(vfs), actualDiagClient.get());
14281516
instance.createFileManager();
14291517
instance.createDiagnostics(actualDiagClient.release());
14301518

0 commit comments

Comments
 (0)