Skip to content

Commit 3aaaf33

Browse files
committed
[clang][DependencyScanning] Track dependencies from prebuilt modules to
determine IsInStableDir When a module is being scanned, it can depend on modules that have already been built from a pch dependency. When this happens, the pcm files are reused for the module dependencies. When this is the case, check if input files recorded from the PCMs come from the provided stable directories transitively, since the scanner will not have access to the full set of file dependencies from prebuilt modules.
1 parent 600eeed commit 3aaaf33

File tree

7 files changed

+237
-71
lines changed

7 files changed

+237
-71
lines changed

clang/include/clang/Serialization/ASTReader.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,17 @@ class ASTReaderListener {
237237
return true;
238238
}
239239

240+
/// Overloaded member function of \c visitInputFile that should
241+
/// be defined when the input file contains both the virtual and external
242+
/// paths, for example when deserializing input files from AST files.
243+
///
244+
/// \returns true to continue receiving the next input file, false to stop.
245+
virtual bool visitInputFile(StringRef FilenameAsRequested, StringRef Filename,
246+
bool isSystem, bool isOverridden,
247+
bool isExplicitModule) {
248+
return true;
249+
}
250+
240251
/// Returns true if this \c ASTReaderListener wants to receive the
241252
/// imports of the AST file via \c visitImport, false otherwise.
242253
virtual bool needsImportVisitation() const { return false; }

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

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ namespace dependencies {
3333

3434
class DependencyActionController;
3535
class DependencyConsumer;
36+
class PrebuiltModuleASTAttrs;
3637

3738
/// Modular dependency that has already been built prior to the dependency scan.
3839
struct PrebuiltModuleDep {
@@ -46,6 +47,47 @@ struct PrebuiltModuleDep {
4647
ModuleMapFile(M->PresumedModuleMapFile) {}
4748
};
4849

50+
/// Attributes loaded from AST files of prebuilt modules collected prior to
51+
/// ModuleDepCollector creation.
52+
using PrebuiltModulesAttrsMap = llvm::StringMap<PrebuiltModuleASTAttrs>;
53+
class PrebuiltModuleASTAttrs {
54+
public:
55+
/// When a module is discovered to not be in stable directories, traverse &
56+
/// update all modules that depend on it.
57+
void
58+
updateDependentsNotInStableDirs(PrebuiltModulesAttrsMap &PrebuiltModulesMap);
59+
60+
/// Read-only access to whether the module is made up of dependencies in
61+
/// stable directories.
62+
bool isInStableDir() const { return IsInStableDirs; }
63+
64+
/// Read-only access to vfs map files.
65+
const llvm::StringSet<> &getVFS() const { return VFSMap; }
66+
67+
/// Update the VFSMap to the one discovered from serializing the AST file.
68+
void setVFS(llvm::StringSet<> &&VFS) { VFSMap = std::move(VFS); }
69+
70+
/// Add a direct dependent module file, so it can be updated if the current
71+
/// module is from stable directores.
72+
void addDependent(StringRef ModuleFile) {
73+
ModuleFileDependents.insert(ModuleFile);
74+
}
75+
76+
/// Update whether the prebuilt module resolves entirely in a stable
77+
/// directories.
78+
void setInStableDir(bool V = false) {
79+
// Cannot reset attribute once it's false.
80+
if (!IsInStableDirs)
81+
return;
82+
IsInStableDirs = V;
83+
}
84+
85+
private:
86+
llvm::StringSet<> VFSMap;
87+
bool IsInStableDirs = true;
88+
std::set<StringRef> ModuleFileDependents;
89+
};
90+
4991
/// This is used to identify a specific module.
5092
struct ModuleID {
5193
/// The name of the module. This may include `:` for C++20 module partitions,
@@ -171,8 +213,6 @@ struct ModuleDeps {
171213
BuildInfo;
172214
};
173215

174-
using PrebuiltModuleVFSMapT = llvm::StringMap<llvm::StringSet<>>;
175-
176216
class ModuleDepCollector;
177217

178218
/// Callback that records textual includes and direct modular includes/imports
@@ -242,7 +282,7 @@ class ModuleDepCollector final : public DependencyCollector {
242282
CompilerInstance &ScanInstance, DependencyConsumer &C,
243283
DependencyActionController &Controller,
244284
CompilerInvocation OriginalCI,
245-
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap);
285+
const PrebuiltModulesAttrsMap PrebuiltModulesASTMap);
246286

247287
void attachToPreprocessor(Preprocessor &PP) override;
248288
void attachToASTReader(ASTReader &R) override;
@@ -262,8 +302,9 @@ class ModuleDepCollector final : public DependencyCollector {
262302
DependencyConsumer &Consumer;
263303
/// Callbacks for computing dependency information.
264304
DependencyActionController &Controller;
265-
/// Mapping from prebuilt AST files to their sorted list of VFS overlay files.
266-
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;
305+
/// Mapping from prebuilt AST filepaths to their attributes referenced during
306+
/// dependency collecting.
307+
const PrebuiltModulesAttrsMap PrebuiltModulesASTMap;
267308
/// Path to the main source file.
268309
std::string MainFile;
269310
/// Hash identifying the compilation conditions of the current TU.
@@ -339,6 +380,14 @@ void resetBenignCodeGenOptions(frontend::ActionKind ProgramAction,
339380
bool isPathInStableDir(const ArrayRef<StringRef> Directories,
340381
const StringRef Input);
341382

383+
/// Determine if options collected from a module's
384+
/// compilation can safely be considered as stable.
385+
///
386+
/// \param Directories Paths known to be in a stable location. e.g. Sysroot.
387+
/// \param HSOpts Header search options derived from the compiler invocation.
388+
bool areOptionsInStableDir(const ArrayRef<StringRef> Directories,
389+
const HeaderSearchOptions &HSOpts);
390+
342391
} // end namespace dependencies
343392
} // end namespace tooling
344393
} // end namespace clang

clang/lib/Frontend/FrontendActions.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -777,10 +777,11 @@ namespace {
777777
/// Indicates that the AST file contains particular input file.
778778
///
779779
/// \returns true to continue receiving the next input file, false to stop.
780-
bool visitInputFile(StringRef Filename, bool isSystem,
781-
bool isOverridden, bool isExplicitModule) override {
780+
bool visitInputFile(StringRef FilenameAsRequested, StringRef Filename,
781+
bool isSystem, bool isOverridden,
782+
bool isExplicitModule) override {
782783

783-
Out.indent(2) << "Input file: " << Filename;
784+
Out.indent(2) << "Input file: " << FilenameAsRequested;
784785

785786
if (isSystem || isOverridden || isExplicitModule) {
786787
Out << " [";

clang/lib/Serialization/ASTReader.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5835,9 +5835,13 @@ bool ASTReader::readASTFileControlBlock(
58355835
break;
58365836
case INPUT_FILE:
58375837
bool Overridden = static_cast<bool>(Record[3]);
5838+
size_t FilenameLen = ModuleDir.size() + Record[7] + 1;
58385839
auto Filename = ResolveImportedPath(PathBuf, Blob, ModuleDir);
5840+
StringRef FilenameAsRequested = Filename->substr(0, FilenameLen);
5841+
StringRef ExternalFilename = Filename->substr(FilenameLen);
58395842
shouldContinue = Listener.visitInputFile(
5840-
*Filename, isSystemFile, Overridden, /*IsExplicitModule=*/false);
5843+
FilenameAsRequested, ExternalFilename, isSystemFile, Overridden,
5844+
/*IsExplicitModule=*/false);
58415845
break;
58425846
}
58435847
if (!shouldContinue)

clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

Lines changed: 93 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -90,63 +90,135 @@ static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
9090

9191
using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
9292

93-
/// A listener that collects the imported modules and optionally the input
94-
/// files.
93+
/// A listener that collects the imported modules and the input
94+
/// files. While visiting, collect vfsoverlays and file inputs that determine
95+
/// whether prebuilt modules fully resolve in stable directories.
9596
class PrebuiltModuleListener : public ASTReaderListener {
9697
public:
9798
PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,
9899
llvm::SmallVector<std::string> &NewModuleFiles,
99-
PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
100+
PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
100101
const HeaderSearchOptions &HSOpts,
101-
const LangOptions &LangOpts, DiagnosticsEngine &Diags)
102+
const LangOptions &LangOpts, DiagnosticsEngine &Diags,
103+
const llvm::SmallVector<StringRef> &StableDirs)
102104
: PrebuiltModuleFiles(PrebuiltModuleFiles),
103105
NewModuleFiles(NewModuleFiles),
104-
PrebuiltModuleVFSMap(PrebuiltModuleVFSMap), ExistingHSOpts(HSOpts),
105-
ExistingLangOpts(LangOpts), Diags(Diags) {}
106+
PrebuiltModulesASTMap(PrebuiltModulesASTMap), ExistingHSOpts(HSOpts),
107+
ExistingLangOpts(LangOpts), Diags(Diags), StableDirs(StableDirs) {}
106108

107109
bool needsImportVisitation() const override { return true; }
110+
bool needsInputFileVisitation() override { return true; }
111+
bool needsSystemInputFileVisitation() override { return true; }
108112

113+
/// Accumulate the modules are transitively depended on by the initial
114+
/// prebuilt module.
109115
void visitImport(StringRef ModuleName, StringRef Filename) override {
110116
if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
111117
NewModuleFiles.push_back(Filename.str());
118+
119+
if (PrebuiltModulesASTMap.try_emplace(Filename).second)
120+
PrebuiltModulesASTMap[Filename].setInStableDir(!StableDirs.empty());
121+
122+
if (auto It = PrebuiltModulesASTMap.find(CurrentFile);
123+
It != PrebuiltModulesASTMap.end() && CurrentFile != Filename)
124+
PrebuiltModulesASTMap[Filename].addDependent(It->getKey());
125+
}
126+
127+
/// For each input file discovered, check whether it's external path is in a
128+
/// stable directory. Traversal is stopped if the current module is not
129+
/// considered stable.
130+
bool visitInputFile(StringRef FilenameAsRequested, StringRef ExternalFilename,
131+
bool isSystem, bool isOverridden,
132+
bool isExplicitModule) override {
133+
if (StableDirs.empty())
134+
return false;
135+
if (!PrebuiltModulesASTMap.contains(CurrentFile) ||
136+
!PrebuiltModulesASTMap[CurrentFile].isInStableDir())
137+
return false;
138+
139+
const StringRef FileToUse =
140+
ExternalFilename.empty() ? FilenameAsRequested : ExternalFilename;
141+
142+
PrebuiltModulesASTMap[CurrentFile].setInStableDir(
143+
isPathInStableDir(StableDirs, FileToUse));
144+
return PrebuiltModulesASTMap[CurrentFile].isInStableDir();
112145
}
113146

147+
/// Update which module that is being actively traversed.
114148
void visitModuleFile(StringRef Filename,
115149
serialization::ModuleKind Kind) override {
150+
// If the CurrentFile is not
151+
// considered stable, update any of it's transitive dependents.
152+
if (PrebuiltModulesASTMap.contains(CurrentFile) &&
153+
!PrebuiltModulesASTMap[CurrentFile].isInStableDir())
154+
PrebuiltModulesASTMap[CurrentFile].updateDependentsNotInStableDirs(
155+
PrebuiltModulesASTMap);
116156
CurrentFile = Filename;
117157
}
118158

159+
/// Check the header search options for a given module when considering
160+
/// if the module comes from stable directories.
161+
bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
162+
StringRef ModuleFilename,
163+
StringRef SpecificModuleCachePath,
164+
bool Complain) override {
165+
if (PrebuiltModulesASTMap.try_emplace(CurrentFile).second)
166+
PrebuiltModulesASTMap[CurrentFile].setInStableDir(!StableDirs.empty());
167+
168+
if (PrebuiltModulesASTMap[CurrentFile].isInStableDir())
169+
PrebuiltModulesASTMap[CurrentFile].setInStableDir(
170+
areOptionsInStableDir(StableDirs, HSOpts));
171+
172+
return false;
173+
}
174+
175+
/// Accumulate vfsoverlays used to build these prebuilt modules.
119176
bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
120177
bool Complain) override {
121-
std::vector<std::string> VFSOverlayFiles = HSOpts.VFSOverlayFiles;
122-
PrebuiltModuleVFSMap.try_emplace(CurrentFile, llvm::from_range,
123-
VFSOverlayFiles);
178+
179+
if (PrebuiltModulesASTMap.try_emplace(CurrentFile).second)
180+
PrebuiltModulesASTMap[CurrentFile].setInStableDir(!StableDirs.empty());
181+
182+
PrebuiltModulesASTMap[CurrentFile].setVFS(
183+
llvm::StringSet<>(llvm::from_range, HSOpts.VFSOverlayFiles));
184+
124185
return checkHeaderSearchPaths(
125186
HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts);
126187
}
127188

128189
private:
129190
PrebuiltModuleFilesT &PrebuiltModuleFiles;
130191
llvm::SmallVector<std::string> &NewModuleFiles;
131-
PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap;
192+
PrebuiltModulesAttrsMap &PrebuiltModulesASTMap;
132193
const HeaderSearchOptions &ExistingHSOpts;
133194
const LangOptions &ExistingLangOpts;
134195
DiagnosticsEngine &Diags;
135196
std::string CurrentFile;
197+
const llvm::SmallVector<StringRef> &StableDirs;
136198
};
137199

138200
/// Visit the given prebuilt module and collect all of the modules it
139201
/// transitively imports and contributing input files.
140202
static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
141203
CompilerInstance &CI,
142204
PrebuiltModuleFilesT &ModuleFiles,
143-
PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
205+
PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
144206
DiagnosticsEngine &Diags) {
207+
208+
// Gather the set of stable directories to use as transitive dependencies are
209+
// discovered.
210+
llvm::SmallVector<StringRef> StableDirs;
211+
std::string SysrootToUse(CI.getHeaderSearchOpts().Sysroot);
212+
if (!SysrootToUse.empty() &&
213+
(llvm::sys::path::root_directory(SysrootToUse) != SysrootToUse))
214+
StableDirs = {SysrootToUse, CI.getHeaderSearchOpts().ResourceDir};
215+
145216
// List of module files to be processed.
146217
llvm::SmallVector<std::string> Worklist;
147-
PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModuleVFSMap,
218+
219+
PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModulesASTMap,
148220
CI.getHeaderSearchOpts(), CI.getLangOpts(),
149-
Diags);
221+
Diags, StableDirs);
150222

151223
Listener.visitModuleFile(PrebuiltModuleFilename,
152224
serialization::MK_ExplicitModule);
@@ -371,16 +443,18 @@ class DependencyScanningAction : public tooling::ToolAction {
371443
auto *FileMgr = ScanInstance.createFileManager(FS);
372444
ScanInstance.createSourceManager(*FileMgr);
373445

374-
// Store the list of prebuilt module files into header search options. This
375-
// will prevent the implicit build to create duplicate modules and will
376-
// force reuse of the existing prebuilt module files instead.
377-
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;
446+
// Store a mapping of prebuilt module files and their properties like header
447+
// search options. This will prevent the implicit build to create duplicate
448+
// modules and will force reuse of the existing prebuilt module files
449+
// instead.
450+
PrebuiltModulesAttrsMap PrebuiltModulesASTMap;
451+
378452
if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
379453
if (visitPrebuiltModule(
380454
ScanInstance.getPreprocessorOpts().ImplicitPCHInclude,
381455
ScanInstance,
382456
ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,
383-
PrebuiltModuleVFSMap, ScanInstance.getDiagnostics()))
457+
PrebuiltModulesASTMap, ScanInstance.getDiagnostics()))
384458
return false;
385459

386460
// Create the dependency collector that will collect the produced
@@ -410,7 +484,7 @@ class DependencyScanningAction : public tooling::ToolAction {
410484
case ScanningOutputFormat::Full:
411485
MDC = std::make_shared<ModuleDepCollector>(
412486
Service, std::move(Opts), ScanInstance, Consumer, Controller,
413-
OriginalInvocation, std::move(PrebuiltModuleVFSMap));
487+
OriginalInvocation, std::move(PrebuiltModulesASTMap));
414488
ScanInstance.addDependencyCollector(MDC);
415489
break;
416490
}

0 commit comments

Comments
 (0)