[clang] [clang][DependencyScanning] Track modules that resolve from sysroot. (PR #130634)
Cyndy Ishida via cfe-commits
cfe-commits at lists.llvm.org
Fri Mar 14 18:13:06 PDT 2025
https://github.com/cyndyishida updated https://github.com/llvm/llvm-project/pull/130634
>From aca254a154489fda68292f6d06a866ae7011a7f6 Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Mon, 10 Mar 2025 09:06:32 -0700
Subject: [PATCH 1/4] [clang][DependencyScanning] Track modules that resolve
from sysroot.
That patch tracks whether all the file & module dependencies of a module
resolve to a sysroot location. This information will later be queried by
build systems for determining where to store the accompanying pcms.
---
.../DependencyScanning/ModuleDepCollector.h | 7 ++
.../DependencyScanning/ModuleDepCollector.cpp | 25 +++-
clang/test/ClangScanDeps/modules-in-sysroot.c | 107 ++++++++++++++++++
clang/tools/clang-scan-deps/ClangScanDeps.cpp | 2 +
4 files changed, 139 insertions(+), 2 deletions(-)
create mode 100644 clang/test/ClangScanDeps/modules-in-sysroot.c
diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
index 20fb4de6a2a73..6187f0168e6d9 100644
--- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
+++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
@@ -114,6 +114,10 @@ struct ModuleDeps {
/// Whether this is a "system" module.
bool IsSystem;
+ /// Whether this is a module where it's dependencies resolve within the
+ /// sysroot.
+ bool IsInSysroot;
+
/// The path to the modulemap file which defines this module.
///
/// This can be used to explicitly build this module. This file will
@@ -219,6 +223,9 @@ class ModuleDepCollectorPP final : public PPCallbacks {
llvm::DenseSet<const Module *> &AddedModules);
void addAffectingClangModule(const Module *M, ModuleDeps &MD,
llvm::DenseSet<const Module *> &AddedModules);
+
+ /// Add discovered module dependency for the given module.
+ void addClangModule(const Module *M, const ModuleID ID, ModuleDeps &MD);
};
/// Collects modular and non-modular dependencies of the main file by attaching
diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
index 36b75c1016cd8..86eda34472cf0 100644
--- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -698,6 +698,15 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
MD.ID.ModuleName = M->getFullModuleName();
MD.IsSystem = M->IsSystem;
+
+ // Start off with the assumption that this module is in the sysroot when there
+ // is a sysroot provided. As more dependencies are discovered, check if those
+ // come from the provided sysroot.
+ const StringRef CurrSysroot = MDC.ScanInstance.getHeaderSearchOpts().Sysroot;
+ MD.IsInSysroot =
+ !CurrSysroot.empty() &&
+ (llvm::sys::path::root_directory(CurrSysroot) != CurrSysroot);
+
// For modules which use export_as link name, the linked product that of the
// corresponding export_as-named module.
if (!M->UseExportAsModuleLinkName)
@@ -739,6 +748,11 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
MDC.ScanInstance.getASTReader()->visitInputFileInfos(
*MF, /*IncludeSystem=*/true,
[&](const serialization::InputFileInfo &IFI, bool IsSystem) {
+ auto FullFilePath = ASTReader::ResolveImportedPath(
+ PathBuf, IFI.UnresolvedImportedFilename, MF->BaseDirectory);
+ if (MD.IsInSysroot)
+ MD.IsInSysroot = FullFilePath->starts_with(CurrSysroot);
+ PathBuf.resize_for_overwrite(256);
if (!(IFI.TopLevel && IFI.ModuleMap))
return;
if (IFI.UnresolvedImportedFilenameAsRequested.ends_with(
@@ -835,6 +849,13 @@ void ModuleDepCollectorPP::addAllSubmoduleDeps(
});
}
+void ModuleDepCollectorPP::addClangModule(const Module *M, const ModuleID ID,
+ ModuleDeps &MD) {
+ MD.ClangModuleDeps.push_back(ID);
+ if (MD.IsInSysroot)
+ MD.IsInSysroot = MDC.ModularDeps[M]->IsInSysroot;
+}
+
void ModuleDepCollectorPP::addModuleDep(
const Module *M, ModuleDeps &MD,
llvm::DenseSet<const Module *> &AddedModules) {
@@ -843,7 +864,7 @@ void ModuleDepCollectorPP::addModuleDep(
!MDC.isPrebuiltModule(Import)) {
if (auto ImportID = handleTopLevelModule(Import->getTopLevelModule()))
if (AddedModules.insert(Import->getTopLevelModule()).second)
- MD.ClangModuleDeps.push_back(*ImportID);
+ addClangModule(Import->getTopLevelModule(), *ImportID, MD);
}
}
}
@@ -867,7 +888,7 @@ void ModuleDepCollectorPP::addAffectingClangModule(
!MDC.isPrebuiltModule(Affecting)) {
if (auto ImportID = handleTopLevelModule(Affecting))
if (AddedModules.insert(Affecting).second)
- MD.ClangModuleDeps.push_back(*ImportID);
+ addClangModule(Affecting, *ImportID, MD);
}
}
}
diff --git a/clang/test/ClangScanDeps/modules-in-sysroot.c b/clang/test/ClangScanDeps/modules-in-sysroot.c
new file mode 100644
index 0000000000000..d96aa69c0e8f4
--- /dev/null
+++ b/clang/test/ClangScanDeps/modules-in-sysroot.c
@@ -0,0 +1,107 @@
+// This test verifies modules that are entirely comprised from sysroot inputs are captured in
+// dependency information.
+
+// The first compilation verifies that transitive dependencies on non-sysroot input are captured.
+// The second compilation verifies that external paths are resolved when a vfsoverlay is applied when considering sysroot-ness.
+
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s|DIR|%/t|g" %t/compile-commands.json.in > %t/compile-commands.json
+// RUN: sed -e "s|DIR|%/t|g" %t/overlay.json.template > %t/overlay.json
+// RUN: clang-scan-deps -compilation-database %t/compile-commands.json \
+// RUN: -j 1 -format experimental-full > %t/deps.db
+// RUN: cat %t/deps.db | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t
+
+// CHECK: "modules": [
+// CHECK-NEXT: {
+// CHECK: "is-in-sysroot": true,
+// CHECK: "name": "A"
+
+// Verify that there are no more occurances of sysroot.
+// CHECK-NOT: "is-in-sysroot"
+
+// CHECK: "name": "A"
+// CHECK: "USE_VFS"
+// CHECK: "name": "B"
+// CHECK: "name": "C"
+// CHECK: "name": "D"
+// CHECK: "name": "NotInSDK"
+
+//--- compile-commands.json.in
+[
+{
+ "directory": "DIR",
+ "command": "clang -c DIR/client.m -isysroot DIR/MacOSX.sdk -I DIR/BuildDir -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps",
+ "file": "DIR/client.m"
+},
+{
+ "directory": "DIR",
+ "command": "clang -c DIR/client.m -isysroot DIR/MacOSX.sdk -ivfsoverlay DIR/overlay.json -DUSE_VFS -I DIR/BuildDir -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps",
+ "file": "DIR/client.m"
+}
+]
+
+//--- overlay.json.template
+{
+ "version": 0,
+ "case-sensitive": "false",
+ "roots": [
+ {
+ "external-contents": "DIR/local/A/A_vfs.h",
+ "name": "DIR/MacOSX.sdk/usr/include/A/A_vfs.h",
+ "type": "file"
+ }
+ ]
+}
+
+//--- MacOSX.sdk/usr/include/A/module.modulemap
+module A {
+ umbrella "."
+}
+
+//--- MacOSX.sdk/usr/include/A/A.h
+#ifdef USE_VFS
+#include <A/A_vfs.h>
+#endif
+typedef int A_t;
+
+//--- local/A/A_vfs.h
+typedef int typeFromVFS;
+
+//--- MacOSX.sdk/usr/include/B/module.modulemap
+module B [system] {
+ umbrella "."
+}
+
+//--- MacOSX.sdk/usr/include/B/B.h
+#include <C/C.h>
+typedef int B_t;
+
+//--- MacOSX.sdk/usr/include/C/module.modulemap
+module C [system] {
+ umbrella "."
+}
+
+//--- MacOSX.sdk/usr/include/C/C.h
+#include <D/D.h>
+
+//--- MacOSX.sdk/usr/include/D/module.modulemap
+module D [system] {
+ umbrella "."
+}
+
+// Simulate a header that will be resolved in a local directory, from a sysroot header.
+//--- MacOSX.sdk/usr/include/D/D.h
+#include <HeaderNotFoundInSDK.h>
+
+//--- BuildDir/module.modulemap
+module NotInSDK [system] {
+ umbrella "."
+}
+
+//--- BuildDir/HeaderNotFoundInSDK.h
+typedef int local_t;
+
+//--- client.m
+#include <A/A.h>
+#include <B/B.h>
diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
index 3bdeb461e4bfa..f5946b30fb84d 100644
--- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -471,6 +471,8 @@ class FullDeps {
for (auto &&ModID : ModuleIDs) {
auto &MD = Modules[ModID];
JOS.object([&] {
+ if (MD.IsInSysroot)
+ JOS.attribute("is-in-sysroot", MD.IsInSysroot);
JOS.attributeArray("clang-module-deps",
toJSONSorted(JOS, MD.ClangModuleDeps));
JOS.attribute("clang-modulemap-file",
>From fd4abaa6c8097694fe1fad2510beb6b11653f01f Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Mon, 10 Mar 2025 13:39:42 -0700
Subject: [PATCH 2/4] Add explicit search paths into SDK as they aren't passed
with non-darwin targets
---
clang/test/ClangScanDeps/modules-in-sysroot.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/clang/test/ClangScanDeps/modules-in-sysroot.c b/clang/test/ClangScanDeps/modules-in-sysroot.c
index d96aa69c0e8f4..2ecf9c293b23d 100644
--- a/clang/test/ClangScanDeps/modules-in-sysroot.c
+++ b/clang/test/ClangScanDeps/modules-in-sysroot.c
@@ -4,6 +4,7 @@
// The first compilation verifies that transitive dependencies on non-sysroot input are captured.
// The second compilation verifies that external paths are resolved when a vfsoverlay is applied when considering sysroot-ness.
+// REQUIRES: shell
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s|DIR|%/t|g" %t/compile-commands.json.in > %t/compile-commands.json
@@ -31,13 +32,13 @@
[
{
"directory": "DIR",
- "command": "clang -c DIR/client.m -isysroot DIR/MacOSX.sdk -I DIR/BuildDir -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps",
- "file": "DIR/client.m"
+ "command": "clang -c DIR/client.c -isysroot DIR/MacOSX.sdk -IDIR/MacOSX.sdk/usr/include -IDIR/BuildDir -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps",
+ "file": "DIR/client.c"
},
{
"directory": "DIR",
- "command": "clang -c DIR/client.m -isysroot DIR/MacOSX.sdk -ivfsoverlay DIR/overlay.json -DUSE_VFS -I DIR/BuildDir -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps",
- "file": "DIR/client.m"
+ "command": "clang -c DIR/client.c -isysroot DIR/MacOSX.sdk -IDIR/MacOSX.sdk/usr/include -ivfsoverlay DIR/overlay.json -DUSE_VFS -IDIR/BuildDir -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps",
+ "file": "DIR/client.c"
}
]
@@ -102,6 +103,6 @@ module NotInSDK [system] {
//--- BuildDir/HeaderNotFoundInSDK.h
typedef int local_t;
-//--- client.m
+//--- client.c
#include <A/A.h>
#include <B/B.h>
>From 032d7f84a0bf3647dba8633993ed1cbf1653b0bc Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Thu, 13 Mar 2025 23:27:12 -0700
Subject: [PATCH 3/4] Address review comments except for prebuilt module
dependencies
---
.../Tooling/DependencyScanning/ModuleDepCollector.h | 8 ++++++--
.../Tooling/DependencyScanning/ModuleDepCollector.cpp | 9 +++++----
2 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
index 6187f0168e6d9..b0eed8a85a9c6 100644
--- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
+++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
@@ -114,8 +114,12 @@ struct ModuleDeps {
/// Whether this is a "system" module.
bool IsSystem;
- /// Whether this is a module where it's dependencies resolve within the
- /// sysroot.
+ /// Whether this module is fully composed of file & module inputs from the
+ /// sysroot. External paths, as opposed to virtual file paths, are always used
+ /// for computing this value.
+ ///
+ /// This attribute is useful for identifying modules that are unlikely to
+ /// change under an active development and build cycle.
bool IsInSysroot;
/// The path to the modulemap file which defines this module.
diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
index 86eda34472cf0..2f9939d4ac3c8 100644
--- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -748,11 +748,12 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
MDC.ScanInstance.getASTReader()->visitInputFileInfos(
*MF, /*IncludeSystem=*/true,
[&](const serialization::InputFileInfo &IFI, bool IsSystem) {
- auto FullFilePath = ASTReader::ResolveImportedPath(
- PathBuf, IFI.UnresolvedImportedFilename, MF->BaseDirectory);
- if (MD.IsInSysroot)
+ if (MD.IsInSysroot) {
+ auto FullFilePath = ASTReader::ResolveImportedPath(
+ PathBuf, IFI.UnresolvedImportedFilename, MF->BaseDirectory);
MD.IsInSysroot = FullFilePath->starts_with(CurrSysroot);
- PathBuf.resize_for_overwrite(256);
+ PathBuf.resize_for_overwrite(256);
+ }
if (!(IFI.TopLevel && IFI.ModuleMap))
return;
if (IFI.UnresolvedImportedFilenameAsRequested.ends_with(
>From 175e9ce016dbcef19de98bfdc5d82118fca72592 Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Fri, 14 Mar 2025 18:00:16 -0700
Subject: [PATCH 4/4] Update IsInSysroot -> IsShareable
* We need to track more than just sysroot, so start with sysroot and
resource directory as a starting point, but allow the ability to
capture more as they are discovered in the future.
* Check a subset of the compiler invocation of module compilations for
determining if a module can be shared.
* When a module has a prebuilt module dependency, consider is not
shareable for now.
---
.../DependencyScanning/ModuleDepCollector.h | 20 +--
.../DependencyScanning/ModuleDepCollector.cpp | 86 ++++++++++---
...-in-sysroot.c => modules-in-shared-dirs.c} | 37 +++---
.../prebuilt-modules-in-shared-dirs.c | 114 ++++++++++++++++++
clang/tools/clang-scan-deps/ClangScanDeps.cpp | 4 +-
5 files changed, 219 insertions(+), 42 deletions(-)
rename clang/test/ClangScanDeps/{modules-in-sysroot.c => modules-in-shared-dirs.c} (63%)
create mode 100644 clang/test/ClangScanDeps/prebuilt-modules-in-shared-dirs.c
diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
index b0eed8a85a9c6..cab4c6771666e 100644
--- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
+++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
@@ -114,13 +114,13 @@ struct ModuleDeps {
/// Whether this is a "system" module.
bool IsSystem;
- /// Whether this module is fully composed of file & module inputs from the
- /// sysroot. External paths, as opposed to virtual file paths, are always used
- /// for computing this value.
+ /// Whether this module is fully composed of file & module inputs from
+ /// locations likely to stay the same across the active development and build
+ /// cycle. For example, when all those input paths only resolve in Sysroot.
///
- /// This attribute is useful for identifying modules that are unlikely to
- /// change under an active development and build cycle.
- bool IsInSysroot;
+ /// External paths, as opposed to virtual file paths, are always used
+ /// for computing this value.
+ bool IsShareable;
/// The path to the modulemap file which defines this module.
///
@@ -229,7 +229,7 @@ class ModuleDepCollectorPP final : public PPCallbacks {
llvm::DenseSet<const Module *> &AddedModules);
/// Add discovered module dependency for the given module.
- void addClangModule(const Module *M, const ModuleID ID, ModuleDeps &MD);
+ void addOneModuleDep(const Module *M, const ModuleID ID, ModuleDeps &MD);
};
/// Collects modular and non-modular dependencies of the main file by attaching
@@ -331,6 +331,12 @@ void resetBenignCodeGenOptions(frontend::ActionKind ProgramAction,
const LangOptions &LangOpts,
CodeGenOptions &CGOpts);
+/// Determine if \c Input can be resolved within a shared location.
+///
+/// \param Directories Paths known to be in a shared location. e.g. Sysroot.
+/// \param Input Path to evaluate.
+bool isPathInSharedDir(ArrayRef<StringRef> Directories, const StringRef Input);
+
} // end namespace dependencies
} // end namespace tooling
} // end namespace clang
diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
index 2f9939d4ac3c8..05ca7718cb99e 100644
--- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -157,6 +157,32 @@ static void optimizeCWD(CowCompilerInvocation &BuildInvocation, StringRef CWD) {
}
}
+/// Check a subset of invocation options to determine whether the current
+/// context can safely be considered as shareable.
+static bool areOptionsInSharedDir(CowCompilerInvocation &BuildInvocation,
+ const ArrayRef<StringRef> SharedDirs) {
+ const auto &HSOpts = BuildInvocation.getHeaderSearchOpts();
+ if (!isPathInSharedDir(SharedDirs, HSOpts.Sysroot))
+ return false;
+
+ if (!isPathInSharedDir(SharedDirs, HSOpts.ResourceDir))
+ return false;
+
+ for (const auto &Entry : HSOpts.UserEntries) {
+ if (!Entry.IgnoreSysRoot)
+ continue;
+ if (!isPathInSharedDir(SharedDirs, Entry.Path))
+ return false;
+ }
+
+ for (const auto &SysPrefix : HSOpts.SystemHeaderPrefixes) {
+ if (!isPathInSharedDir(SharedDirs, SysPrefix.Prefix))
+ return false;
+ }
+
+ return true;
+}
+
static std::vector<std::string> splitString(std::string S, char Separator) {
SmallVector<StringRef> Segments;
StringRef(S).split(Segments, Separator, /*MaxSplit=*/-1, /*KeepEmpty=*/false);
@@ -212,6 +238,25 @@ void dependencies::resetBenignCodeGenOptions(frontend::ActionKind ProgramAction,
}
}
+bool dependencies::isPathInSharedDir(ArrayRef<StringRef> Directories,
+ const StringRef Input) {
+ auto PathStartsWith = [](StringRef Prefix, StringRef Path) {
+ auto PrefixIt = llvm::sys::path::begin(Prefix),
+ PrefixEnd = llvm::sys::path::end(Prefix);
+ for (auto PathIt = llvm::sys::path::begin(Path),
+ PathEnd = llvm::sys::path::end(Path);
+ PrefixIt != PrefixEnd && PathIt != PathEnd; ++PrefixIt, ++PathIt) {
+ if (*PrefixIt != *PathIt)
+ return false;
+ }
+ return PrefixIt == PrefixEnd;
+ };
+
+ return any_of(Directories, [&](StringRef Dir) {
+ return !Dir.empty() && PathStartsWith(Dir, Input);
+ });
+}
+
static CowCompilerInvocation
makeCommonInvocationForModuleBuild(CompilerInvocation CI) {
CI.resetNonModularOptions();
@@ -699,13 +744,15 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
MD.ID.ModuleName = M->getFullModuleName();
MD.IsSystem = M->IsSystem;
- // Start off with the assumption that this module is in the sysroot when there
+ // Start off with the assumption that this module is shareable when there
// is a sysroot provided. As more dependencies are discovered, check if those
- // come from the provided sysroot.
- const StringRef CurrSysroot = MDC.ScanInstance.getHeaderSearchOpts().Sysroot;
- MD.IsInSysroot =
- !CurrSysroot.empty() &&
- (llvm::sys::path::root_directory(CurrSysroot) != CurrSysroot);
+ // come from the provided shared directories.
+ const llvm::SmallVector<StringRef> SharedDirs = {
+ MDC.ScanInstance.getHeaderSearchOpts().Sysroot,
+ MDC.ScanInstance.getHeaderSearchOpts().ResourceDir};
+ MD.IsShareable =
+ !SharedDirs[0].empty() &&
+ (llvm::sys::path::root_directory(SharedDirs[0]) != SharedDirs[0]);
// For modules which use export_as link name, the linked product that of the
// corresponding export_as-named module.
@@ -748,10 +795,10 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
MDC.ScanInstance.getASTReader()->visitInputFileInfos(
*MF, /*IncludeSystem=*/true,
[&](const serialization::InputFileInfo &IFI, bool IsSystem) {
- if (MD.IsInSysroot) {
+ if (MD.IsShareable) {
auto FullFilePath = ASTReader::ResolveImportedPath(
PathBuf, IFI.UnresolvedImportedFilename, MF->BaseDirectory);
- MD.IsInSysroot = FullFilePath->starts_with(CurrSysroot);
+ MD.IsShareable = isPathInSharedDir(SharedDirs, *FullFilePath);
PathBuf.resize_for_overwrite(256);
}
if (!(IFI.TopLevel && IFI.ModuleMap))
@@ -795,6 +842,10 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
}
});
+ // Check provided input paths from the invocation for determining IsShareable.
+ if (MD.IsShareable)
+ MD.IsShareable = areOptionsInSharedDir(CI, SharedDirs);
+
MDC.associateWithContextHash(CI, IgnoreCWD, MD);
// Finish the compiler invocation. Requires dependencies and the context hash.
@@ -836,8 +887,13 @@ void ModuleDepCollectorPP::addModulePrebuiltDeps(
for (const Module *Import : M->Imports)
if (Import->getTopLevelModule() != M->getTopLevelModule())
if (MDC.isPrebuiltModule(Import->getTopLevelModule()))
- if (SeenSubmodules.insert(Import->getTopLevelModule()).second)
+ if (SeenSubmodules.insert(Import->getTopLevelModule()).second) {
MD.PrebuiltModuleDeps.emplace_back(Import->getTopLevelModule());
+ // Conservatively consider the module as not shareable,
+ // as transitive dependencies from the prebuilt module have not been
+ // determined.
+ MD.IsShareable = false;
+ }
}
void ModuleDepCollectorPP::addAllSubmoduleDeps(
@@ -850,11 +906,11 @@ void ModuleDepCollectorPP::addAllSubmoduleDeps(
});
}
-void ModuleDepCollectorPP::addClangModule(const Module *M, const ModuleID ID,
- ModuleDeps &MD) {
+void ModuleDepCollectorPP::addOneModuleDep(const Module *M, const ModuleID ID,
+ ModuleDeps &MD) {
MD.ClangModuleDeps.push_back(ID);
- if (MD.IsInSysroot)
- MD.IsInSysroot = MDC.ModularDeps[M]->IsInSysroot;
+ if (MD.IsShareable)
+ MD.IsShareable = MDC.ModularDeps[M]->IsShareable;
}
void ModuleDepCollectorPP::addModuleDep(
@@ -865,7 +921,7 @@ void ModuleDepCollectorPP::addModuleDep(
!MDC.isPrebuiltModule(Import)) {
if (auto ImportID = handleTopLevelModule(Import->getTopLevelModule()))
if (AddedModules.insert(Import->getTopLevelModule()).second)
- addClangModule(Import->getTopLevelModule(), *ImportID, MD);
+ addOneModuleDep(Import->getTopLevelModule(), *ImportID, MD);
}
}
}
@@ -889,7 +945,7 @@ void ModuleDepCollectorPP::addAffectingClangModule(
!MDC.isPrebuiltModule(Affecting)) {
if (auto ImportID = handleTopLevelModule(Affecting))
if (AddedModules.insert(Affecting).second)
- addClangModule(Affecting, *ImportID, MD);
+ addOneModuleDep(Affecting, *ImportID, MD);
}
}
}
diff --git a/clang/test/ClangScanDeps/modules-in-sysroot.c b/clang/test/ClangScanDeps/modules-in-shared-dirs.c
similarity index 63%
rename from clang/test/ClangScanDeps/modules-in-sysroot.c
rename to clang/test/ClangScanDeps/modules-in-shared-dirs.c
index 2ecf9c293b23d..bce927f866268 100644
--- a/clang/test/ClangScanDeps/modules-in-sysroot.c
+++ b/clang/test/ClangScanDeps/modules-in-shared-dirs.c
@@ -1,8 +1,9 @@
-// This test verifies modules that are entirely comprised from sysroot inputs are captured in
+// This test verifies modules that are entirely comprised from shared directory inputs are captured in
// dependency information.
-// The first compilation verifies that transitive dependencies on non-sysroot input are captured.
-// The second compilation verifies that external paths are resolved when a vfsoverlay is applied when considering sysroot-ness.
+// The first compilation verifies that transitive dependencies on local input are captured.
+// The second compilation verifies that external paths are resolved when a
+// vfsoverlay for determining is-shareable.
// REQUIRES: shell
// RUN: rm -rf %t
@@ -15,11 +16,11 @@
// CHECK: "modules": [
// CHECK-NEXT: {
-// CHECK: "is-in-sysroot": true,
+// CHECK: "is-shareable": true,
// CHECK: "name": "A"
// Verify that there are no more occurances of sysroot.
-// CHECK-NOT: "is-in-sysroot"
+// CHECK-NOT: "is-shareable"
// CHECK: "name": "A"
// CHECK: "USE_VFS"
@@ -32,12 +33,12 @@
[
{
"directory": "DIR",
- "command": "clang -c DIR/client.c -isysroot DIR/MacOSX.sdk -IDIR/MacOSX.sdk/usr/include -IDIR/BuildDir -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps",
+ "command": "clang -c DIR/client.c -isysroot DIR/Sysroot -IDIR/Sysroot/usr/include -IDIR/BuildDir -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps",
"file": "DIR/client.c"
},
{
"directory": "DIR",
- "command": "clang -c DIR/client.c -isysroot DIR/MacOSX.sdk -IDIR/MacOSX.sdk/usr/include -ivfsoverlay DIR/overlay.json -DUSE_VFS -IDIR/BuildDir -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps",
+ "command": "clang -c DIR/client.c -isysroot DIR/Sysroot -IDIR/Sysroot/usr/include -ivfsoverlay DIR/overlay.json -DUSE_VFS -IDIR/BuildDir -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps",
"file": "DIR/client.c"
}
]
@@ -48,51 +49,51 @@
"case-sensitive": "false",
"roots": [
{
- "external-contents": "DIR/local/A/A_vfs.h",
- "name": "DIR/MacOSX.sdk/usr/include/A/A_vfs.h",
+ "external-contents": "DIR/SysrootButNotReally/A/A_vfs.h",
+ "name": "DIR/Sysroot/usr/include/A/A_vfs.h",
"type": "file"
}
]
}
-//--- MacOSX.sdk/usr/include/A/module.modulemap
+//--- Sysroot/usr/include/A/module.modulemap
module A {
umbrella "."
}
-//--- MacOSX.sdk/usr/include/A/A.h
+//--- Sysroot/usr/include/A/A.h
#ifdef USE_VFS
#include <A/A_vfs.h>
#endif
typedef int A_t;
-//--- local/A/A_vfs.h
+//--- SysrootButNotReally/A/A_vfs.h
typedef int typeFromVFS;
-//--- MacOSX.sdk/usr/include/B/module.modulemap
+//--- Sysroot/usr/include/B/module.modulemap
module B [system] {
umbrella "."
}
-//--- MacOSX.sdk/usr/include/B/B.h
+//--- Sysroot/usr/include/B/B.h
#include <C/C.h>
typedef int B_t;
-//--- MacOSX.sdk/usr/include/C/module.modulemap
+//--- Sysroot/usr/include/C/module.modulemap
module C [system] {
umbrella "."
}
-//--- MacOSX.sdk/usr/include/C/C.h
+//--- Sysroot/usr/include/C/C.h
#include <D/D.h>
-//--- MacOSX.sdk/usr/include/D/module.modulemap
+//--- Sysroot/usr/include/D/module.modulemap
module D [system] {
umbrella "."
}
// Simulate a header that will be resolved in a local directory, from a sysroot header.
-//--- MacOSX.sdk/usr/include/D/D.h
+//--- Sysroot/usr/include/D/D.h
#include <HeaderNotFoundInSDK.h>
//--- BuildDir/module.modulemap
diff --git a/clang/test/ClangScanDeps/prebuilt-modules-in-shared-dirs.c b/clang/test/ClangScanDeps/prebuilt-modules-in-shared-dirs.c
new file mode 100644
index 0000000000000..dc133bb376850
--- /dev/null
+++ b/clang/test/ClangScanDeps/prebuilt-modules-in-shared-dirs.c
@@ -0,0 +1,114 @@
+/// This test validates that modules that depend on prebuilt modules resolve `is-shareable` as false.
+
+// REQUIRES: shell
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s|DIR|%/t|g" %t/overlay.json.template > %t/overlay.json
+// RUN: sed -e "s|DIR|%/t|g" %t/compile-pch.json.in > %t/compile-pch.json
+// RUN: clang-scan-deps -compilation-database %t/compile-pch.json \
+// RUN: -j 1 -format experimental-full > %t/deps_pch.db
+// RUN: %clang -x c-header -c %t/prebuild.h -isysroot %t/MacOSX.sdk \
+// RUN: -I%t/BuildDir -ivfsoverlay %t/overlay.json \
+// RUN: -I %t/MacOSX.sdk/usr/include -fmodules -fmodules-cache-path=%t/module-cache \
+// RUN: -fimplicit-module-maps -o %t/prebuild.pch
+// RUN: sed -e "s|DIR|%/t|g" %t/compile-commands.json.in > %t/compile-commands.json
+// RUN: clang-scan-deps -compilation-database %t/compile-commands.json \
+// RUN: -j 1 -format experimental-full > %t/deps.db
+// RUN: cat %t/deps_pch.db | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t --check-prefix PCH_DEP
+// RUN: cat %t/deps.db | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t
+
+// PCH_DEP: "is-shareable": true
+// PCH_DEP: "name": "A"
+
+// Verify is-shareable is not in any module dependencies, as they all depend on prebuilt modules.
+// CHECK-NOT: "is-shareable"
+
+//--- compile-pch.json.in
+[
+{
+ "directory": "DIR",
+ "command": "clang -x c-header -c DIR/prebuild.h -isysroot DIR/MacOSX.sdk -IDIR/BuildDir -ivfsoverlay DIR/overlay.json -IDIR/MacOSX.sdk/usr/include -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps -o DIR/prebuild.pch",
+ "file": "DIR/prebuild.h"
+}
+]
+
+//--- compile-commands.json.in
+[
+{
+ "directory": "DIR",
+ "command": "clang -c DIR/client.c -isysroot DIR/MacOSX.sdk -IDIR/BuildDir -ivfsoverlay DIR/overlay.json -IDIR/MacOSX.sdk/usr/include -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps -include-pch DIR/prebuild.pch",
+ "file": "DIR/client.c"
+}
+]
+
+//--- overlay.json.template
+{
+ "version": 0,
+ "case-sensitive": "false",
+ "roots": [
+ {
+ "external-contents": "DIR/BuildDir/B_vfs.h",
+ "name": "DIR/MacOSX.sdk/usr/include/B/B_vfs.h",
+ "type": "file"
+ }
+ ]
+}
+
+//--- MacOSX.sdk/usr/include/A/module.modulemap
+module A [system] {
+ umbrella "."
+}
+
+//--- MacOSX.sdk/usr/include/A/A.h
+typedef int A_type;
+
+//--- MacOSX.sdk/usr/include/B/module.modulemap
+module B [system] {
+ umbrella "."
+}
+
+//--- MacOSX.sdk/usr/include/B/B.h
+#include <B/B_vfs.h>
+
+//--- BuildDir/B_vfs.h
+typedef int local_t;
+
+//--- MacOSX.sdk/usr/include/sys/sys.h
+typedef int sys_t_m;
+
+//--- MacOSX.sdk/usr/include/sys/module.modulemap
+module sys [system] {
+ umbrella "."
+}
+
+//--- MacOSX.sdk/usr/include/B_transitive/B.h
+#include <B/B.h>
+
+//--- MacOSX.sdk/usr/include/B_transitive/module.modulemap
+module B_transitive [system] {
+ umbrella "."
+}
+
+//--- MacOSX.sdk/usr/include/C/module.modulemap
+module C [system] {
+ umbrella "."
+}
+
+//--- MacOSX.sdk/usr/include/C/C.h
+#include <B_transitive/B.h>
+
+
+//--- MacOSX.sdk/usr/include/D/module.modulemap
+module D [system] {
+ umbrella "."
+}
+
+//--- MacOSX.sdk/usr/include/D/D.h
+#include <C/C.h>
+
+//--- prebuild.h
+#include <A/A.h>
+#include <C/C.h> // This dependency transitively depends on a local header.
+
+//--- client.c
+#include <D/D.h> // This dependency transitively depends on a local header.
diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
index f5946b30fb84d..53f77e59999cf 100644
--- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -471,8 +471,8 @@ class FullDeps {
for (auto &&ModID : ModuleIDs) {
auto &MD = Modules[ModID];
JOS.object([&] {
- if (MD.IsInSysroot)
- JOS.attribute("is-in-sysroot", MD.IsInSysroot);
+ if (MD.IsShareable)
+ JOS.attribute("is-shareable", MD.IsShareable);
JOS.attributeArray("clang-module-deps",
toJSONSorted(JOS, MD.ClangModuleDeps));
JOS.attribute("clang-modulemap-file",
More information about the cfe-commits
mailing list