Skip to content

Commit b30aab1

Browse files
committed
Add an API for clang dependency scanner to perform module lookup by name alone (#3127)
* [clang][clang-scan-deps] Add an experimental C API for returning the list of module and file dependencies of a module for a particular compiler invocation rdar://64538073 * Address review comments - Define a subclass of FrontendAction and call loadModule there. - Remove the API for creating a worker just for clang_experimental_DependencyScannerWorkerByModName_getFileDependencies_v1. - Add the capability to get the dependencies based on the module name to clang-scan-deps. * Address review comments - Create a fake virtual source file instead of asking users to create one and pass it to the command line. - Remove the "translation-units" output from test case modules-full-by-mod-name.cpp. The information isn't useful as the source file name isn't passed on the command line in this case. - Inherit from `PreprocessOnlyAction`. * Address review comments * Address review comments - Rebase. - Rename functions. - Remove unneeded test cases. * Address review comments - Remove member `ModuleName` and thread it through functions. Remove member `FakeMemBuffer` too and make it a variable inside `computeDependencies`. - Use `MemoryBufferRef` instead of `MemoryBuffer *`. - Use `StringRef` instead of `const char *`. * Address review comments - Use Optional<String> as the type of ModuleName. - Add comments explaining what happens if the passed ModuleName isn't empty. (cherry picked from commit 2a3e8df) Conflicts: clang/tools/libclang/libclang.map
1 parent e04284c commit b30aab1

File tree

15 files changed

+374
-34
lines changed

15 files changed

+374
-34
lines changed

clang/include/clang-c/Dependencies.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,17 @@ clang_experimental_DependencyScannerWorker_getFileDependencies_v1(
222222
CXDependencyScannerWorker Worker, int argc, const char *const *argv,
223223
const char *WorkingDirectory, CXModuleDiscoveredCallback *MDC,
224224
void *Context, CXString *error);
225+
226+
/**
227+
* Same as \c clang_experimental_DependencyScannerWorker_getFileDependencies_v1,
228+
* but get the dependencies by module name alone.
229+
*/
230+
CINDEX_LINKAGE CXFileDependencies *
231+
clang_experimental_DependencyScannerWorker_getDependenciesByModuleName_v0(
232+
CXDependencyScannerWorker Worker, int argc, const char *const *argv,
233+
const char *ModuleName, const char *WorkingDirectory,
234+
CXModuleDiscoveredCallback *MDC, void *Context, CXString *error);
235+
225236
/**
226237
* @}
227238
*/

clang/include/clang/Frontend/FrontendActions.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,15 @@ class PrintPreprocessedAction : public PreprocessorFrontendAction {
299299
bool hasPCHSupport() const override { return true; }
300300
};
301301

302+
class GetDependenciesByModuleNameAction : public PreprocessOnlyAction {
303+
StringRef ModuleName;
304+
void ExecuteAction() override;
305+
306+
public:
307+
GetDependenciesByModuleNameAction(StringRef ModuleName)
308+
: ModuleName(ModuleName) {}
309+
};
310+
302311
} // end namespace clang
303312

304313
#endif

clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,18 @@ class DependencyScanningTool {
7777

7878
/// Print out the dependency information into a string using the dependency
7979
/// file format that is specified in the options (-MD is the default) and
80-
/// return it.
80+
/// return it. If \p ModuleName isn't empty, this function returns the
81+
/// dependency information of module \p ModuleName.
8182
///
8283
/// \returns A \c StringError with the diagnostic output if clang errors
8384
/// occurred, dependency file contents otherwise.
8485
llvm::Expected<std::string>
8586
getDependencyFile(const tooling::CompilationDatabase &Compilations,
86-
StringRef CWD);
87+
StringRef CWD, llvm::Optional<StringRef> ModuleName = None);
8788

8889
/// Collect the full module dependency graph for the input, ignoring any
89-
/// modules which have already been seen.
90+
/// modules which have already been seen. If \p ModuleName isn't empty, this
91+
/// function returns the full dependency information of module \p ModuleName.
9092
///
9193
/// \param AlreadySeen This stores modules which have previously been
9294
/// reported. Use the same instance for all calls to this
@@ -98,7 +100,8 @@ class DependencyScanningTool {
98100
/// occurred, \c FullDependencies otherwise.
99101
llvm::Expected<FullDependenciesResult>
100102
getFullDependencies(const tooling::CompilationDatabase &Compilations,
101-
StringRef CWD, const llvm::StringSet<> &AlreadySeen);
103+
StringRef CWD, const llvm::StringSet<> &AlreadySeen,
104+
llvm::Optional<StringRef> ModuleName = None);
102105

103106
private:
104107
DependencyScanningWorker Worker;

clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,25 +75,29 @@ class DependencyScanningWorker {
7575
DependencyScanningWorker(DependencyScanningService &Service);
7676

7777
/// Run the dependency scanning tool for a given clang driver invocation, and
78-
/// report the discovered dependencies to the provided consumer.
78+
/// report the discovered dependencies to the provided consumer. If
79+
/// \p ModuleName isn't empty, this function reports the dependencies of
80+
/// module \p ModuleName.
7981
///
8082
/// \returns A \c StringError with the diagnostic output if clang errors
8183
/// occurred, success otherwise.
82-
llvm::Error
83-
computeDependenciesForClangInvocation(StringRef WorkingDirectory,
84-
ArrayRef<std::string> Arguments,
85-
DependencyConsumer &Consumer);
84+
llvm::Error computeDependenciesForClangInvocation(
85+
StringRef WorkingDirectory, ArrayRef<std::string> Arguments,
86+
DependencyConsumer &Consumer,
87+
llvm::Optional<StringRef> ModuleName = None);
8688

8789
/// Run the dependency scanning tool for a given clang driver invocation (as
8890
/// specified for the given Input in the CDB), and report the discovered
89-
/// dependencies to the provided consumer.
91+
/// dependencies to the provided consumer. If \p ModuleName isn't empty, this
92+
/// function reports the dependencies of module \p ModuleName.
9093
///
9194
/// \returns A \c StringError with the diagnostic output if clang errors
9295
/// occurred, success otherwise.
9396
llvm::Error computeDependencies(const std::string &Input,
9497
StringRef WorkingDirectory,
9598
const CompilationDatabase &CDB,
96-
DependencyConsumer &Consumer);
99+
DependencyConsumer &Consumer,
100+
llvm::Optional<StringRef> ModuleName = None);
97101

98102
ScanningOutputFormat getFormat() const { return Format; }
99103

clang/lib/Frontend/FrontendActions.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,3 +993,17 @@ void PrintDependencyDirectivesSourceMinimizerAction::ExecuteAction() {
993993
}
994994
llvm::outs() << Output;
995995
}
996+
997+
void GetDependenciesByModuleNameAction::ExecuteAction() {
998+
CompilerInstance &CI = getCompilerInstance();
999+
Preprocessor &PP = CI.getPreprocessor();
1000+
SourceManager &SM = PP.getSourceManager();
1001+
FileID MainFileID = SM.getMainFileID();
1002+
SourceLocation FileStart = SM.getLocForStartOfFile(MainFileID);
1003+
SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
1004+
IdentifierInfo *ModuleID = PP.getIdentifierInfo(ModuleName);
1005+
Path.push_back(std::make_pair(ModuleID, FileStart));
1006+
auto ModResult = CI.loadModule(FileStart, Path, Module::Hidden, false);
1007+
PPCallbacks *CB = PP.getPPCallbacks();
1008+
CB->moduleImport(SourceLocation(), Path, ModResult);
1009+
}

clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ DependencyScanningTool::DependencyScanningTool(
5050
: Worker(Service) {}
5151

5252
llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
53-
const tooling::CompilationDatabase &Compilations, StringRef CWD) {
53+
const tooling::CompilationDatabase &Compilations, StringRef CWD,
54+
llvm::Optional<StringRef> ModuleName) {
5455
/// Prints out all of the gathered dependencies into a string.
5556
class MakeDependencyPrinterConsumer : public DependencyConsumer {
5657
public:
@@ -112,7 +113,8 @@ llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
112113
std::string Input = Compilations.getAllCompileCommands().front().Filename;
113114

114115
MakeDependencyPrinterConsumer Consumer;
115-
auto Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer);
116+
auto Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer,
117+
ModuleName);
116118
if (Result)
117119
return std::move(Result);
118120
std::string Output;
@@ -123,7 +125,8 @@ llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
123125
llvm::Expected<FullDependenciesResult>
124126
DependencyScanningTool::getFullDependencies(
125127
const tooling::CompilationDatabase &Compilations, StringRef CWD,
126-
const llvm::StringSet<> &AlreadySeen) {
128+
const llvm::StringSet<> &AlreadySeen,
129+
llvm::Optional<StringRef> ModuleName) {
127130
class FullDependencyPrinterConsumer : public DependencyConsumer {
128131
public:
129132
FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen)
@@ -196,8 +199,8 @@ DependencyScanningTool::getFullDependencies(
196199
std::string Input = Compilations.getAllCompileCommands().front().Filename;
197200

198201
FullDependencyPrinterConsumer Consumer(AlreadySeen);
199-
llvm::Error Result =
200-
Worker.computeDependencies(Input, CWD, Compilations, Consumer);
202+
llvm::Error Result = Worker.computeDependencies(Input, CWD, Compilations,
203+
Consumer, ModuleName);
201204
if (Result)
202205
return std::move(Result);
203206
return Consumer.getFullDependencies();

clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,11 @@ class DependencyScanningAction : public tooling::ToolAction {
141141
StringRef WorkingDirectory, DependencyConsumer &Consumer,
142142
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
143143
ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings,
144-
ScanningOutputFormat Format)
144+
ScanningOutputFormat Format, llvm::Optional<StringRef> ModuleName = None,
145+
llvm::Optional<llvm::MemoryBufferRef> FakeMemBuffer = None)
145146
: WorkingDirectory(WorkingDirectory), Consumer(Consumer),
146-
DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings),
147-
Format(Format) {}
147+
DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings), Format(Format),
148+
ModuleName(ModuleName), FakeMemBuffer(FakeMemBuffer) {}
148149

149150
bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
150151
FileManager *FileMgr,
@@ -214,6 +215,16 @@ class DependencyScanningAction : public tooling::ToolAction {
214215
.ExcludedConditionalDirectiveSkipMappings = PPSkipMappings;
215216
}
216217

218+
if (ModuleName.hasValue()) {
219+
SmallString<128> FullPath(*ModuleName);
220+
llvm::sys::fs::make_absolute(WorkingDirectory, FullPath);
221+
SourceManager &SrcMgr = Compiler.getSourceManager();
222+
FileMgr->getVirtualFile(FullPath.c_str(), FakeMemBuffer->getBufferSize(),
223+
0);
224+
FileID MainFileID = SrcMgr.createFileID(*FakeMemBuffer);
225+
SrcMgr.setMainFileID(MainFileID);
226+
}
227+
217228
// Create the dependency collector that will collect the produced
218229
// dependencies.
219230
//
@@ -249,7 +260,13 @@ class DependencyScanningAction : public tooling::ToolAction {
249260
// the impact of strict context hashing.
250261
Compiler.getHeaderSearchOpts().ModulesStrictContextHash = true;
251262

252-
auto Action = std::make_unique<ReadPCHAndPreprocessAction>();
263+
std::unique_ptr<FrontendAction> Action;
264+
265+
if (ModuleName.hasValue())
266+
Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
267+
else
268+
Action = std::make_unique<ReadPCHAndPreprocessAction>();
269+
253270
const bool Result = Compiler.ExecuteAction(*Action);
254271
if (!DepFS)
255272
FileMgr->clearStatCache();
@@ -262,6 +279,8 @@ class DependencyScanningAction : public tooling::ToolAction {
262279
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
263280
ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings;
264281
ScanningOutputFormat Format;
282+
llvm::Optional<StringRef> ModuleName;
283+
llvm::Optional<llvm::MemoryBufferRef> FakeMemBuffer;
265284
};
266285

267286
} // end anonymous namespace
@@ -307,29 +326,53 @@ static llvm::Error runWithDiags(
307326

308327
llvm::Error DependencyScanningWorker::computeDependencies(
309328
const std::string &Input, StringRef WorkingDirectory,
310-
const CompilationDatabase &CDB, DependencyConsumer &Consumer) {
329+
const CompilationDatabase &CDB, DependencyConsumer &Consumer,
330+
llvm::Optional<StringRef> ModuleName) {
311331
RealFS->setCurrentWorkingDirectory(WorkingDirectory);
332+
std::unique_ptr<llvm::MemoryBuffer> FakeMemBuffer =
333+
ModuleName.hasValue() ? llvm::MemoryBuffer::getMemBuffer(" ") : nullptr;
312334
return runWithDiags(DiagOpts.get(), [&](DiagnosticConsumer &DC) {
313335
/// Create the tool that uses the underlying file system to ensure that any
314336
/// file system requests that are made by the driver do not go through the
315337
/// dependency scanning filesystem.
316-
tooling::ClangTool Tool(CDB, Input, PCHContainerOps, RealFS, Files);
338+
SmallString<128> FullPath;
339+
tooling::ClangTool Tool(CDB,
340+
ModuleName.hasValue() ? ModuleName->str() : Input,
341+
PCHContainerOps, RealFS, Files);
317342
Tool.clearArgumentsAdjusters();
318343
Tool.setRestoreWorkingDir(false);
319344
Tool.setPrintErrorMessage(false);
320345
Tool.setDiagnosticConsumer(&DC);
321-
DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS,
322-
PPSkipMappings.get(), Format);
346+
DependencyScanningAction Action(
347+
WorkingDirectory, Consumer, DepFS, PPSkipMappings.get(), Format,
348+
ModuleName,
349+
FakeMemBuffer
350+
? llvm::Optional<llvm::MemoryBufferRef>(*FakeMemBuffer.get())
351+
: None);
352+
353+
if (ModuleName.hasValue()) {
354+
Tool.mapVirtualFile(*ModuleName, FakeMemBuffer->getBuffer());
355+
FullPath = *ModuleName;
356+
llvm::sys::fs::make_absolute(WorkingDirectory, FullPath);
357+
Tool.appendArgumentsAdjuster(
358+
[&](const tooling::CommandLineArguments &Args, StringRef FileName) {
359+
tooling::CommandLineArguments AdjustedArgs(Args);
360+
AdjustedArgs.push_back(std::string(FullPath));
361+
return AdjustedArgs;
362+
});
363+
}
364+
323365
return !Tool.run(&Action);
324366
});
325367
}
326368

327369
llvm::Error DependencyScanningWorker::computeDependenciesForClangInvocation(
328370
StringRef WorkingDirectory, ArrayRef<std::string> Arguments,
329-
DependencyConsumer &Consumer) {
371+
DependencyConsumer &Consumer, llvm::Optional<StringRef> ModuleName) {
330372
std::string Input("dependency-scanner-fake-input-file");
331373
StringRef Output("dependency-scanner-fake-output-file");
332374
SingleCommandCompilationDatabase CDB(
333375
CompileCommand(WorkingDirectory, Input, Arguments, Output));
334-
return computeDependencies(Input, WorkingDirectory, CDB, Consumer);
376+
return computeDependencies(Input, WorkingDirectory, CDB, Consumer,
377+
ModuleName);
335378
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[
2+
{
3+
"directory": "DIR",
4+
"command": "clang -E -IInputs -D INCLUDE_HEADER2 -MD -MF DIR/modules_cdb2.d -fmodules -fcxx-modules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps -gmodules -x c++",
5+
"file": ""
6+
},
7+
{
8+
"directory": "DIR",
9+
"command": "clang -E -IInputs -fmodules -fcxx-modules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps -x c++",
10+
"file": ""
11+
},
12+
]
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[
2+
{
3+
"directory": "DIR",
4+
"command": "clang-cl /E /IInputs /D INCLUDE_HEADER2 /clang:-MD /clang:-MF /clang:DIR/modules_cdb2_clangcl.d /clang:-fmodules /clang:-fcxx-modules /clang:-fmodules-cache-path=DIR/module-cache_clangcl /clang:-fimplicit-modules /clang:-fimplicit-module-maps /clang:-x /clang:c++ --",
5+
"file": ""
6+
},
7+
{
8+
"directory": "DIR",
9+
"command": "clang-cl /E /IInputs /clang:-fmodules /clang:-fcxx-modules /clang:-fmodules-cache-path=DIR/module-cache_clangcl /clang:-fimplicit-modules /clang:-fimplicit-module-maps /clang:-x /clang:c++ --",
10+
"file": ""
11+
},
12+
]
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// RUN: rm -rf %t.dir
2+
// RUN: rm -rf %t.cdb
3+
// RUN: mkdir -p %t.dir
4+
// RUN: cp %s %t.dir/modules_cdb_input.cpp
5+
// RUN: cp %s %t.dir/modules_cdb_input2.cpp
6+
// RUN: mkdir %t.dir/Inputs
7+
// RUN: cp %S/Inputs/header.h %t.dir/Inputs/header.h
8+
// RUN: cp %S/Inputs/header2.h %t.dir/Inputs/header2.h
9+
// RUN: cp %S/Inputs/module.modulemap %t.dir/Inputs/module.modulemap
10+
// RUN: sed -e "s|DIR|%/t.dir|g" %S/Inputs/modules_cdb_by_mod_name.json > %t.cdb
11+
// RUN: sed -e "s|DIR|%/t.dir|g" %S/Inputs/modules_cdb_clangcl_by_mod_name.json > %t_clangcl.cdb
12+
//
13+
// RUN: echo %t.dir > %t.result
14+
// RUN: clang-scan-deps -compilation-database %t.cdb -j 4 -format experimental-full \
15+
// RUN: -mode preprocess-minimized-sources -module-name=header1 >> %t.result
16+
// RUN: cat %t.result | sed 's:\\\\\?:/:g' | FileCheck --check-prefixes=CHECK %s
17+
//
18+
// RUN: echo %t.dir > %t_clangcl.result
19+
// RUN: clang-scan-deps -compilation-database %t_clangcl.cdb -j 4 -format experimental-full \
20+
// RUN: -mode preprocess-minimized-sources -module-name=header1 >> %t_clangcl.result
21+
// RUN: cat %t_clangcl.result | sed 's:\\\\\?:/:g' | FileCheck --check-prefixes=CHECK %s
22+
23+
// CHECK: [[PREFIX:.*]]
24+
// CHECK-NEXT: {
25+
// CHECK-NEXT: "modules": [
26+
// CHECK-NEXT: {
27+
// CHECK-NEXT: "clang-module-deps": [
28+
// CHECK-NEXT: {
29+
// CHECK-NEXT: "context-hash": "[[HASH_H2_DINCLUDE:[A-Z0-9]+]]",
30+
// CHECK-NEXT: "module-name": "header2"
31+
// CHECK-NEXT: }
32+
// CHECK-NEXT: ],
33+
// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/Inputs/module.modulemap",
34+
// CHECK-NEXT: "command-line": [
35+
// CHECK-NEXT: "-cc1"
36+
// CHECK: "-emit-module"
37+
// CHECK: "-fmodule-name=header1"
38+
// CHECK: "-fno-implicit-modules"
39+
// CHECK: ],
40+
// CHECK-NEXT: "context-hash": "[[HASH_H1_DINCLUDE:[A-Z0-9]+]]",
41+
// CHECK-NEXT: "file-deps": [
42+
// CHECK-NEXT: "[[PREFIX]]/Inputs/header.h",
43+
// CHECK-NEXT: "[[PREFIX]]/Inputs/module.modulemap"
44+
// CHECK-NEXT: ],
45+
// CHECK-NEXT: "name": "header1"
46+
// CHECK-NEXT: },
47+
// CHECK-NEXT: {
48+
// CHECK-NEXT: "clang-module-deps": [],
49+
// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/Inputs/module.modulemap",
50+
// CHECK-NEXT: "command-line": [
51+
// CHECK-NEXT: "-cc1",
52+
// CHECK: "-emit-module",
53+
// CHECK: "-fmodule-name=header1",
54+
// CHECK: "-fno-implicit-modules",
55+
// CHECK: ],
56+
// CHECK-NEXT: "context-hash": "[[HASH_H1:[A-Z0-9]+]]",
57+
// CHECK-NEXT: "file-deps": [
58+
// CHECK-NEXT: "[[PREFIX]]/Inputs/header.h",
59+
// CHECK-NEXT: "[[PREFIX]]/Inputs/module.modulemap"
60+
// CHECK-NEXT: ],
61+
// CHECK-NEXT: "name": "header1"
62+
// CHECK-NEXT: },
63+
// CHECK-NEXT: {
64+
// CHECK-NEXT: "clang-module-deps": [],
65+
// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/Inputs/module.modulemap",
66+
// CHECK-NEXT: "command-line": [
67+
// CHECK-NEXT: "-cc1",
68+
// CHECK: "-emit-module",
69+
// CHECK: "-fmodule-name=header2",
70+
// CHECK: "-fno-implicit-modules",
71+
// CHECK: ],
72+
// CHECK-NEXT: "context-hash": "[[HASH_H2_DINCLUDE]]",
73+
// CHECK-NEXT: "file-deps": [
74+
// CHECK-NEXT: "[[PREFIX]]/Inputs/header2.h",
75+
// CHECK-NEXT: "[[PREFIX]]/Inputs/module.modulemap"
76+
// CHECK-NEXT: ],
77+
// CHECK-NEXT: "name": "header2"
78+
// CHECK-NEXT: }
79+
// CHECK-NEXT: ],

0 commit comments

Comments
 (0)