Skip to content

Commit ea1bfbf

Browse files
authored
[clang][Dependency Scanning] Report What a Module Exports during Scanning (llvm#137421)
We would like to report, for a module, which direct dependencies it exports during dependency scanning. This PR implements this reporting by augmenting `ModuleDep`'s `ClangModuleDeps` variable. `ClangModuleDeps` now contains instances of `DepInfo`, which is made of a `ModuleID` and a boolean flag that indicates if a particular dependence is exported. rdar://144794793
1 parent 4ac4ad4 commit ea1bfbf

File tree

5 files changed

+216
-41
lines changed

5 files changed

+216
-41
lines changed

clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h

+22-8
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,25 @@ struct ModuleDeps {
178178
/// on, not including transitive dependencies.
179179
std::vector<PrebuiltModuleDep> PrebuiltModuleDeps;
180180

181-
/// A list of module identifiers this module directly depends on, not
182-
/// including transitive dependencies.
181+
/// This struct contains information about a single dependency.
182+
struct DepInfo {
183+
/// Identifies the dependency.
184+
ModuleID ID;
185+
186+
/// Indicates if the module that has this dependency exports it or not.
187+
bool Exported = false;
188+
189+
bool operator<(const DepInfo &Other) const {
190+
return std::tie(ID, Exported) < std::tie(Other.ID, Other.Exported);
191+
}
192+
};
193+
194+
/// A list of DepsInfo containing information about modules this module
195+
/// directly depends on, not including transitive dependencies.
183196
///
184197
/// This may include modules with a different context hash when it can be
185198
/// determined that the differences are benign for this compilation.
186-
std::vector<ModuleID> ClangModuleDeps;
199+
std::vector<ModuleDeps::DepInfo> ClangModuleDeps;
187200

188201
/// The set of libraries or frameworks to link against when
189202
/// an entity from this module is used.
@@ -270,7 +283,8 @@ class ModuleDepCollectorPP final : public PPCallbacks {
270283
llvm::DenseSet<const Module *> &AddedModules);
271284

272285
/// Add discovered module dependency for the given module.
273-
void addOneModuleDep(const Module *M, const ModuleID ID, ModuleDeps &MD);
286+
void addOneModuleDep(const Module *M, bool Exported, const ModuleID ID,
287+
ModuleDeps &MD);
274288
};
275289

276290
/// Collects modular and non-modular dependencies of the main file by attaching
@@ -352,16 +366,16 @@ class ModuleDepCollector final : public DependencyCollector {
352366

353367
/// Collect module map files for given modules.
354368
llvm::DenseSet<const FileEntry *>
355-
collectModuleMapFiles(ArrayRef<ModuleID> ClangModuleDeps) const;
369+
collectModuleMapFiles(ArrayRef<ModuleDeps::DepInfo> ClangModuleDeps) const;
356370

357371
/// Add module map files to the invocation, if needed.
358372
void addModuleMapFiles(CompilerInvocation &CI,
359-
ArrayRef<ModuleID> ClangModuleDeps) const;
373+
ArrayRef<ModuleDeps::DepInfo> ClangModuleDeps) const;
360374
/// Add module files (pcm) to the invocation, if needed.
361375
void addModuleFiles(CompilerInvocation &CI,
362-
ArrayRef<ModuleID> ClangModuleDeps) const;
376+
ArrayRef<ModuleDeps::DepInfo> ClangModuleDeps) const;
363377
void addModuleFiles(CowCompilerInvocation &CI,
364-
ArrayRef<ModuleID> ClangModuleDeps) const;
378+
ArrayRef<ModuleDeps::DepInfo> ClangModuleDeps) const;
365379

366380
/// Add paths that require looking up outputs to the given dependencies.
367381
void addOutputPaths(CowCompilerInvocation &CI, ModuleDeps &Deps);

clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp

+38-27
Original file line numberDiff line numberDiff line change
@@ -389,10 +389,10 @@ ModuleDepCollector::getInvocationAdjustedForModuleBuildWithoutOutputs(
389389
}
390390

391391
llvm::DenseSet<const FileEntry *> ModuleDepCollector::collectModuleMapFiles(
392-
ArrayRef<ModuleID> ClangModuleDeps) const {
392+
ArrayRef<ModuleDeps::DepInfo> ClangModuleDeps) const {
393393
llvm::DenseSet<const FileEntry *> ModuleMapFiles;
394-
for (const ModuleID &MID : ClangModuleDeps) {
395-
ModuleDeps *MD = ModuleDepsByID.lookup(MID);
394+
for (const auto &Info : ClangModuleDeps) {
395+
ModuleDeps *MD = ModuleDepsByID.lookup(Info.ID);
396396
assert(MD && "Inconsistent dependency info");
397397
// TODO: Track ClangModuleMapFile as `FileEntryRef`.
398398
auto FE = ScanInstance.getFileManager().getOptionalFileRef(
@@ -404,44 +404,47 @@ llvm::DenseSet<const FileEntry *> ModuleDepCollector::collectModuleMapFiles(
404404
}
405405

406406
void ModuleDepCollector::addModuleMapFiles(
407-
CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
407+
CompilerInvocation &CI,
408+
ArrayRef<ModuleDeps::DepInfo> ClangModuleDeps) const {
408409
if (Service.shouldEagerLoadModules())
409410
return; // Only pcm is needed for eager load.
410411

411-
for (const ModuleID &MID : ClangModuleDeps) {
412-
ModuleDeps *MD = ModuleDepsByID.lookup(MID);
412+
for (const auto &Info : ClangModuleDeps) {
413+
ModuleDeps *MD = ModuleDepsByID.lookup(Info.ID);
413414
assert(MD && "Inconsistent dependency info");
414415
CI.getFrontendOpts().ModuleMapFiles.push_back(MD->ClangModuleMapFile);
415416
}
416417
}
417418

418419
void ModuleDepCollector::addModuleFiles(
419-
CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
420-
for (const ModuleID &MID : ClangModuleDeps) {
421-
ModuleDeps *MD = ModuleDepsByID.lookup(MID);
420+
CompilerInvocation &CI,
421+
ArrayRef<ModuleDeps::DepInfo> ClangModuleDeps) const {
422+
for (const auto &Info : ClangModuleDeps) {
423+
ModuleDeps *MD = ModuleDepsByID.lookup(Info.ID);
422424
std::string PCMPath =
423425
Controller.lookupModuleOutput(*MD, ModuleOutputKind::ModuleFile);
424426

425427
if (Service.shouldEagerLoadModules())
426428
CI.getFrontendOpts().ModuleFiles.push_back(std::move(PCMPath));
427429
else
428430
CI.getHeaderSearchOpts().PrebuiltModuleFiles.insert(
429-
{MID.ModuleName, std::move(PCMPath)});
431+
{Info.ID.ModuleName, std::move(PCMPath)});
430432
}
431433
}
432434

433435
void ModuleDepCollector::addModuleFiles(
434-
CowCompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
435-
for (const ModuleID &MID : ClangModuleDeps) {
436-
ModuleDeps *MD = ModuleDepsByID.lookup(MID);
436+
CowCompilerInvocation &CI,
437+
ArrayRef<ModuleDeps::DepInfo> ClangModuleDeps) const {
438+
for (const auto &Info : ClangModuleDeps) {
439+
ModuleDeps *MD = ModuleDepsByID.lookup(Info.ID);
437440
std::string PCMPath =
438441
Controller.lookupModuleOutput(*MD, ModuleOutputKind::ModuleFile);
439442

440443
if (Service.shouldEagerLoadModules())
441444
CI.getMutFrontendOpts().ModuleFiles.push_back(std::move(PCMPath));
442445
else
443446
CI.getMutHeaderSearchOpts().PrebuiltModuleFiles.insert(
444-
{MID.ModuleName, std::move(PCMPath)});
447+
{Info.ID.ModuleName, std::move(PCMPath)});
445448
}
446449
}
447450

@@ -471,10 +474,10 @@ void ModuleDepCollector::applyDiscoveredDependencies(CompilerInvocation &CI) {
471474
CI.getFrontendOpts().ModuleMapFiles.emplace_back(
472475
CurrentModuleMap->getNameAsRequested());
473476

474-
SmallVector<ModuleID> DirectDeps;
477+
SmallVector<ModuleDeps::DepInfo> DirectDeps;
475478
for (const auto &KV : ModularDeps)
476479
if (DirectModularDeps.contains(KV.first))
477-
DirectDeps.push_back(KV.second->ID);
480+
DirectDeps.push_back({KV.second->ID, /* Exported = */ false});
478481

479482
// TODO: Report module maps the same way it's done for modular dependencies.
480483
addModuleMapFiles(CI, DirectDeps);
@@ -598,9 +601,9 @@ static std::string getModuleContextHash(const ModuleDeps &MD,
598601
// example, case-insensitive paths to modulemap files. Usually such a case
599602
// would indicate a missed optimization to canonicalize, but it may be
600603
// difficult to canonicalize all cases when there is a VFS.
601-
for (const auto &ID : MD.ClangModuleDeps) {
602-
HashBuilder.add(ID.ModuleName);
603-
HashBuilder.add(ID.ContextHash);
604+
for (const auto &Info : MD.ClangModuleDeps) {
605+
HashBuilder.add(Info.ID.ModuleName);
606+
HashBuilder.add(Info.ID.ContextHash);
604607
}
605608

606609
HashBuilder.add(EagerLoadModules);
@@ -924,22 +927,30 @@ void ModuleDepCollectorPP::addAllSubmoduleDeps(
924927
});
925928
}
926929

927-
void ModuleDepCollectorPP::addOneModuleDep(const Module *M, const ModuleID ID,
928-
ModuleDeps &MD) {
929-
MD.ClangModuleDeps.push_back(ID);
930+
void ModuleDepCollectorPP::addOneModuleDep(const Module *M, bool Exported,
931+
const ModuleID ID, ModuleDeps &MD) {
932+
MD.ClangModuleDeps.push_back({ID, Exported});
933+
930934
if (MD.IsInStableDirectories)
931935
MD.IsInStableDirectories = MDC.ModularDeps[M]->IsInStableDirectories;
932936
}
933937

934938
void ModuleDepCollectorPP::addModuleDep(
935939
const Module *M, ModuleDeps &MD,
936940
llvm::DenseSet<const Module *> &AddedModules) {
941+
SmallVector<Module *> ExportedModulesVector;
942+
M->getExportedModules(ExportedModulesVector);
943+
llvm::DenseSet<const Module *> ExportedModulesSet(
944+
ExportedModulesVector.begin(), ExportedModulesVector.end());
937945
for (const Module *Import : M->Imports) {
938-
if (Import->getTopLevelModule() != M->getTopLevelModule() &&
946+
const Module *ImportedTopLevelModule = Import->getTopLevelModule();
947+
if (ImportedTopLevelModule != M->getTopLevelModule() &&
939948
!MDC.isPrebuiltModule(Import)) {
940-
if (auto ImportID = handleTopLevelModule(Import->getTopLevelModule()))
941-
if (AddedModules.insert(Import->getTopLevelModule()).second)
942-
addOneModuleDep(Import->getTopLevelModule(), *ImportID, MD);
949+
if (auto ImportID = handleTopLevelModule(ImportedTopLevelModule))
950+
if (AddedModules.insert(ImportedTopLevelModule).second) {
951+
bool Exported = ExportedModulesSet.contains(ImportedTopLevelModule);
952+
addOneModuleDep(ImportedTopLevelModule, Exported, *ImportID, MD);
953+
}
943954
}
944955
}
945956
}
@@ -963,7 +974,7 @@ void ModuleDepCollectorPP::addAffectingClangModule(
963974
!MDC.isPrebuiltModule(Affecting)) {
964975
if (auto ImportID = handleTopLevelModule(Affecting))
965976
if (AddedModules.insert(Affecting).second)
966-
addOneModuleDep(Affecting, *ImportID, MD);
977+
addOneModuleDep(Affecting, /* Exported = */ false, *ImportID, MD);
967978
}
968979
}
969980
}

clang/test/ClangScanDeps/export.c

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Test correctly reporting what a module exports during dependency scanning.
2+
// Module A depends on modules B, C and D, but only exports B and C.
3+
// Module E depends on modules B, C and D, and exports all of them.
4+
5+
// RUN: rm -rf %t
6+
// RUN: split-file %s %t
7+
// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json
8+
// RUN: clang-scan-deps -compilation-database \
9+
// RUN: %t/cdb.json -format experimental-full > %t/deps.db
10+
// RUN: cat %t/deps.db | sed 's:\\\\\?:/:g' | FileCheck %s
11+
12+
//--- cdb.json.template
13+
[
14+
{
15+
"directory": "DIR",
16+
"command": "clang -c DIR/test.c -I DIR/AH -I DIR/BH -I DIR/CH -I DIR/DH -I DIR/EH -fmodules -fmodules-cache-path=DIR/cache",
17+
"file": "DIR/test.c"
18+
},
19+
]
20+
21+
//--- AH/A.h
22+
#include "B.h"
23+
#include "C.h"
24+
#include "D.h"
25+
26+
int funcA();
27+
28+
//--- AH/module.modulemap
29+
module A {
30+
header "A.h"
31+
32+
export B
33+
export C
34+
}
35+
36+
//--- BH/B.h
37+
//--- BH/module.modulemap
38+
module B {
39+
header "B.h"
40+
}
41+
42+
//--- CH/C.h
43+
//--- CH/module.modulemap
44+
module C {
45+
header "C.h"
46+
}
47+
48+
//--- DH/D.h
49+
//--- DH/module.modulemap
50+
module D {
51+
header "D.h"
52+
}
53+
54+
//--- EH/E.h
55+
#include "B.h"
56+
#include "C.h"
57+
#include "D.h"
58+
59+
//--- EH/module.modulemap
60+
module E {
61+
header "E.h"
62+
export *
63+
}
64+
65+
//--- test.c
66+
#include "A.h"
67+
#include "E.h"
68+
69+
int test1() {
70+
return funcA();
71+
}
72+
73+
// CHECK: {
74+
// CHECK-NEXT: "modules": [
75+
// CHECK-NEXT: {
76+
// CHECK-NEXT: "clang-module-deps": [
77+
// CHECK-NEXT: {
78+
// CHECK-NEXT: "context-hash": "[[HASH_MOD_B:.*]]",
79+
// CHECK-NEXT: "module-name": "B",
80+
// CHECK-NEXT: "exported": "true"
81+
// CHECK-NEXT: },
82+
// CHECK-NEXT: {
83+
// CHECK-NEXT: "context-hash": "[[HASH_MOD_C:.*]]",
84+
// CHECK-NEXT: "module-name": "C",
85+
// CHECK-NEXT: "exported": "true"
86+
// CHECK-NEXT: },
87+
// CHECK-NEXT: {
88+
// CHECK-NEXT: "context-hash": "[[HASH_MOD_D:.*]]",
89+
// CHECK-NEXT: "module-name": "D"
90+
// CHECK-NEXT: }
91+
// CHECK-NEXT: ],
92+
// CHECK-NEXT: "clang-modulemap-file":{{.*}},
93+
// CHECK-NEXT: "command-line": [
94+
// CHECK: ],
95+
// CHECK: "name": "A"
96+
// CHECK-NEXT: }
97+
// CHECK: {
98+
// CHECK: "name": "B"
99+
// CHECK: }
100+
// CHECK: {
101+
// CHECK: "name": "C"
102+
// CHECK: }
103+
// CHECK: {
104+
// CHECK: "name": "D"
105+
// CHECK: }
106+
// CHECK: {
107+
// CHECK-NEXT: "clang-module-deps": [
108+
// CHECK-NEXT: {
109+
// CHECK-NEXT: "context-hash": "[[HASH_MOD_B]]",
110+
// CHECK-NEXT: "module-name": "B",
111+
// CHECK-NEXT: "exported": "true"
112+
// CHECK-NEXT: },
113+
// CHECK-NEXT: {
114+
// CHECK-NEXT: "context-hash": "[[HASH_MOD_C]]",
115+
// CHECK-NEXT: "module-name": "C",
116+
// CHECK-NEXT: "exported": "true"
117+
// CHECK-NEXT: },
118+
// CHECK-NEXT: {
119+
// CHECK-NEXT: "context-hash": "[[HASH_MOD_D]]",
120+
// CHECK-NEXT: "module-name": "D",
121+
// CHECK-NEXT: "exported": "true"
122+
// CHECK-NEXT: }
123+
// CHECK-NEXT: ],
124+
// CHECK-NEXT: "clang-modulemap-file":{{.*}},
125+
// CHECK-NEXT: "command-line": [
126+
// CHECK: ],
127+
// CHECK: "name": "E"
128+
// CHECK-NEXT: }
129+
// CHECK: ]
130+
// CHECK: }
131+
132+
133+

clang/test/ClangScanDeps/optimize-vfs-pch.m

+2-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@
5454
// CHECK-NEXT: "clang-module-deps": [
5555
// CHECK-NEXT: {
5656
// CHECK-NEXT: "context-hash": "{{.*}}",
57-
// CHECK-NEXT: "module-name": "E"
57+
// CHECK-NEXT: "module-name": "E",
58+
// CHECK-NEXT: "exported": "true"
5859
// CHECK-NEXT: }
5960
// CHECK-NEXT: ],
6061
// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/modules/D/module.modulemap",

clang/tools/clang-scan-deps/ClangScanDeps.cpp

+21-5
Original file line numberDiff line numberDiff line change
@@ -350,16 +350,32 @@ static auto toJSONStrings(llvm::json::OStream &JOS, Container &&Strings) {
350350
};
351351
}
352352

353+
static auto toJSONModuleID(llvm::json::OStream &JOS, StringRef ContextHash,
354+
StringRef ModuleName, bool Exported) {
355+
return JOS.object([&] {
356+
JOS.attribute("context-hash", StringRef(ContextHash));
357+
JOS.attribute("module-name", StringRef(ModuleName));
358+
if (Exported)
359+
JOS.attribute("exported", StringRef("true"));
360+
});
361+
}
362+
353363
// Technically, we don't need to sort the dependency list to get determinism.
354364
// Leaving these be will simply preserve the import order.
355365
static auto toJSONSorted(llvm::json::OStream &JOS, std::vector<ModuleID> V) {
356366
llvm::sort(V);
357367
return [&JOS, V = std::move(V)] {
358-
for (const ModuleID &MID : V)
359-
JOS.object([&] {
360-
JOS.attribute("context-hash", StringRef(MID.ContextHash));
361-
JOS.attribute("module-name", StringRef(MID.ModuleName));
362-
});
368+
for (const auto &MID : V)
369+
toJSONModuleID(JOS, MID.ContextHash, MID.ModuleName, false);
370+
};
371+
}
372+
373+
static auto toJSONSorted(llvm::json::OStream &JOS,
374+
std::vector<ModuleDeps::DepInfo> V) {
375+
llvm::sort(V);
376+
return [&JOS, V = std::move(V)] {
377+
for (const ModuleDeps::DepInfo &MID : V)
378+
toJSONModuleID(JOS, MID.ID.ContextHash, MID.ID.ModuleName, MID.Exported);
363379
};
364380
}
365381

0 commit comments

Comments
 (0)