[clang] 17c2948 - [clang-scan-deps] Add an API for clang dependency scanner to perform
Akira Hatanaka via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 9 08:53:09 PDT 2021
Author: Akira Hatanaka
Date: 2021-09-09T08:52:50-07:00
New Revision: 17c2948d04431e94376e8d7883b5d89fbe705b5e
URL: https://github.com/llvm/llvm-project/commit/17c2948d04431e94376e8d7883b5d89fbe705b5e
DIFF: https://github.com/llvm/llvm-project/commit/17c2948d04431e94376e8d7883b5d89fbe705b5e.diff
LOG: [clang-scan-deps] Add an API for clang dependency scanner to perform
module lookup by name alone
This removes the need to create a fake source file that imports a
module.
rdar://64538073
Differential Revision: https://reviews.llvm.org/D109485
Added:
clang/test/ClangScanDeps/Inputs/modules_cdb_by_mod_name.json
clang/test/ClangScanDeps/Inputs/modules_cdb_clangcl_by_mod_name.json
clang/test/ClangScanDeps/modules-full-by-mod-name.cpp
Modified:
clang/include/clang/Frontend/FrontendActions.h
clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
clang/lib/Frontend/FrontendActions.cpp
clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
clang/tools/clang-scan-deps/ClangScanDeps.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Frontend/FrontendActions.h b/clang/include/clang/Frontend/FrontendActions.h
index ff8d4417eaa49..545a7e842c4ff 100644
--- a/clang/include/clang/Frontend/FrontendActions.h
+++ b/clang/include/clang/Frontend/FrontendActions.h
@@ -299,6 +299,15 @@ class PrintPreprocessedAction : public PreprocessorFrontendAction {
bool hasPCHSupport() const override { return true; }
};
+class GetDependenciesByModuleNameAction : public PreprocessOnlyAction {
+ StringRef ModuleName;
+ void ExecuteAction() override;
+
+public:
+ GetDependenciesByModuleNameAction(StringRef ModuleName)
+ : ModuleName(ModuleName) {}
+};
+
} // end namespace clang
#endif
diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
index f88dc472c80b1..c26b6e91d90ce 100644
--- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
+++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
@@ -77,16 +77,18 @@ class DependencyScanningTool {
/// Print out the dependency information into a string using the dependency
/// file format that is specified in the options (-MD is the default) and
- /// return it.
+ /// return it. If \p ModuleName isn't empty, this function returns the
+ /// dependency information of module \p ModuleName.
///
/// \returns A \c StringError with the diagnostic output if clang errors
/// occurred, dependency file contents otherwise.
llvm::Expected<std::string>
getDependencyFile(const tooling::CompilationDatabase &Compilations,
- StringRef CWD);
+ StringRef CWD, llvm::Optional<StringRef> ModuleName = None);
/// Collect the full module dependency graph for the input, ignoring any
- /// modules which have already been seen.
+ /// modules which have already been seen. If \p ModuleName isn't empty, this
+ /// function returns the full dependency information of module \p ModuleName.
///
/// \param AlreadySeen This stores modules which have previously been
/// reported. Use the same instance for all calls to this
@@ -98,7 +100,8 @@ class DependencyScanningTool {
/// occurred, \c FullDependencies otherwise.
llvm::Expected<FullDependenciesResult>
getFullDependencies(const tooling::CompilationDatabase &Compilations,
- StringRef CWD, const llvm::StringSet<> &AlreadySeen);
+ StringRef CWD, const llvm::StringSet<> &AlreadySeen,
+ llvm::Optional<StringRef> ModuleName = None);
private:
DependencyScanningWorker Worker;
diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
index c6f7d239b8eb5..3ae3bcda72392 100644
--- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
+++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
@@ -76,14 +76,16 @@ class DependencyScanningWorker {
/// Run the dependency scanning tool for a given clang driver invocation (as
/// specified for the given Input in the CDB), and report the discovered
- /// dependencies to the provided consumer.
+ /// dependencies to the provided consumer. If \p ModuleName isn't empty, this
+ /// function reports the dependencies of module \p ModuleName.
///
/// \returns A \c StringError with the diagnostic output if clang errors
/// occurred, success otherwise.
llvm::Error computeDependencies(const std::string &Input,
StringRef WorkingDirectory,
const CompilationDatabase &CDB,
- DependencyConsumer &Consumer);
+ DependencyConsumer &Consumer,
+ llvm::Optional<StringRef> ModuleName = None);
private:
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index c6ebbdc8c04e1..b5544afa9f248 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -993,3 +993,17 @@ void PrintDependencyDirectivesSourceMinimizerAction::ExecuteAction() {
}
llvm::outs() << Output;
}
+
+void GetDependenciesByModuleNameAction::ExecuteAction() {
+ CompilerInstance &CI = getCompilerInstance();
+ Preprocessor &PP = CI.getPreprocessor();
+ SourceManager &SM = PP.getSourceManager();
+ FileID MainFileID = SM.getMainFileID();
+ SourceLocation FileStart = SM.getLocForStartOfFile(MainFileID);
+ SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
+ IdentifierInfo *ModuleID = PP.getIdentifierInfo(ModuleName);
+ Path.push_back(std::make_pair(ModuleID, FileStart));
+ auto ModResult = CI.loadModule(FileStart, Path, Module::Hidden, false);
+ PPCallbacks *CB = PP.getPPCallbacks();
+ CB->moduleImport(SourceLocation(), Path, ModResult);
+}
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
index 43b6a0c95f047..a7969b358d9cc 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
@@ -50,7 +50,8 @@ DependencyScanningTool::DependencyScanningTool(
: Worker(Service) {}
llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
- const tooling::CompilationDatabase &Compilations, StringRef CWD) {
+ const tooling::CompilationDatabase &Compilations, StringRef CWD,
+ llvm::Optional<StringRef> ModuleName) {
/// Prints out all of the gathered dependencies into a string.
class MakeDependencyPrinterConsumer : public DependencyConsumer {
public:
@@ -112,7 +113,8 @@ llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
std::string Input = Compilations.getAllCompileCommands().front().Filename;
MakeDependencyPrinterConsumer Consumer;
- auto Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer);
+ auto Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer,
+ ModuleName);
if (Result)
return std::move(Result);
std::string Output;
@@ -123,7 +125,8 @@ llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
llvm::Expected<FullDependenciesResult>
DependencyScanningTool::getFullDependencies(
const tooling::CompilationDatabase &Compilations, StringRef CWD,
- const llvm::StringSet<> &AlreadySeen) {
+ const llvm::StringSet<> &AlreadySeen,
+ llvm::Optional<StringRef> ModuleName) {
class FullDependencyPrinterConsumer : public DependencyConsumer {
public:
FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen)
@@ -196,8 +199,8 @@ DependencyScanningTool::getFullDependencies(
std::string Input = Compilations.getAllCompileCommands().front().Filename;
FullDependencyPrinterConsumer Consumer(AlreadySeen);
- llvm::Error Result =
- Worker.computeDependencies(Input, CWD, Compilations, Consumer);
+ llvm::Error Result = Worker.computeDependencies(Input, CWD, Compilations,
+ Consumer, ModuleName);
if (Result)
return std::move(Result);
return Consumer.getFullDependencies();
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
index d651ff23b387a..10385ab356692 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -141,10 +141,11 @@ class DependencyScanningAction : public tooling::ToolAction {
StringRef WorkingDirectory, DependencyConsumer &Consumer,
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings,
- ScanningOutputFormat Format)
+ ScanningOutputFormat Format, llvm::Optional<StringRef> ModuleName = None,
+ llvm::Optional<llvm::MemoryBufferRef> FakeMemBuffer = None)
: WorkingDirectory(WorkingDirectory), Consumer(Consumer),
- DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings),
- Format(Format) {}
+ DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings), Format(Format),
+ ModuleName(ModuleName), FakeMemBuffer(FakeMemBuffer) {}
bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
FileManager *FileMgr,
@@ -214,6 +215,16 @@ class DependencyScanningAction : public tooling::ToolAction {
.ExcludedConditionalDirectiveSkipMappings = PPSkipMappings;
}
+ if (ModuleName.hasValue()) {
+ SmallString<128> FullPath(*ModuleName);
+ llvm::sys::fs::make_absolute(WorkingDirectory, FullPath);
+ SourceManager &SrcMgr = Compiler.getSourceManager();
+ FileMgr->getVirtualFile(FullPath.c_str(), FakeMemBuffer->getBufferSize(),
+ 0);
+ FileID MainFileID = SrcMgr.createFileID(*FakeMemBuffer);
+ SrcMgr.setMainFileID(MainFileID);
+ }
+
// Create the dependency collector that will collect the produced
// dependencies.
//
@@ -249,7 +260,13 @@ class DependencyScanningAction : public tooling::ToolAction {
// the impact of strict context hashing.
Compiler.getHeaderSearchOpts().ModulesStrictContextHash = true;
- auto Action = std::make_unique<ReadPCHAndPreprocessAction>();
+ std::unique_ptr<FrontendAction> Action;
+
+ if (ModuleName.hasValue())
+ Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
+ else
+ Action = std::make_unique<ReadPCHAndPreprocessAction>();
+
const bool Result = Compiler.ExecuteAction(*Action);
if (!DepFS)
FileMgr->clearStatCache();
@@ -262,6 +279,8 @@ class DependencyScanningAction : public tooling::ToolAction {
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings;
ScanningOutputFormat Format;
+ llvm::Optional<StringRef> ModuleName;
+ llvm::Optional<llvm::MemoryBufferRef> FakeMemBuffer;
};
} // end anonymous namespace
@@ -307,19 +326,42 @@ static llvm::Error runWithDiags(
llvm::Error DependencyScanningWorker::computeDependencies(
const std::string &Input, StringRef WorkingDirectory,
- const CompilationDatabase &CDB, DependencyConsumer &Consumer) {
+ const CompilationDatabase &CDB, DependencyConsumer &Consumer,
+ llvm::Optional<StringRef> ModuleName) {
RealFS->setCurrentWorkingDirectory(WorkingDirectory);
+ std::unique_ptr<llvm::MemoryBuffer> FakeMemBuffer =
+ ModuleName.hasValue() ? llvm::MemoryBuffer::getMemBuffer(" ") : nullptr;
return runWithDiags(DiagOpts.get(), [&](DiagnosticConsumer &DC) {
/// Create the tool that uses the underlying file system to ensure that any
/// file system requests that are made by the driver do not go through the
/// dependency scanning filesystem.
- tooling::ClangTool Tool(CDB, Input, PCHContainerOps, RealFS, Files);
+ SmallString<128> FullPath;
+ tooling::ClangTool Tool(CDB,
+ ModuleName.hasValue() ? ModuleName->str() : Input,
+ PCHContainerOps, RealFS, Files);
Tool.clearArgumentsAdjusters();
Tool.setRestoreWorkingDir(false);
Tool.setPrintErrorMessage(false);
Tool.setDiagnosticConsumer(&DC);
- DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS,
- PPSkipMappings.get(), Format);
+ DependencyScanningAction Action(
+ WorkingDirectory, Consumer, DepFS, PPSkipMappings.get(), Format,
+ ModuleName,
+ FakeMemBuffer
+ ? llvm::Optional<llvm::MemoryBufferRef>(*FakeMemBuffer.get())
+ : None);
+
+ if (ModuleName.hasValue()) {
+ Tool.mapVirtualFile(*ModuleName, FakeMemBuffer->getBuffer());
+ FullPath = *ModuleName;
+ llvm::sys::fs::make_absolute(WorkingDirectory, FullPath);
+ Tool.appendArgumentsAdjuster(
+ [&](const tooling::CommandLineArguments &Args, StringRef FileName) {
+ tooling::CommandLineArguments AdjustedArgs(Args);
+ AdjustedArgs.push_back(std::string(FullPath));
+ return AdjustedArgs;
+ });
+ }
+
return !Tool.run(&Action);
});
}
diff --git a/clang/test/ClangScanDeps/Inputs/modules_cdb_by_mod_name.json b/clang/test/ClangScanDeps/Inputs/modules_cdb_by_mod_name.json
new file mode 100644
index 0000000000000..f95749f11be2a
--- /dev/null
+++ b/clang/test/ClangScanDeps/Inputs/modules_cdb_by_mod_name.json
@@ -0,0 +1,12 @@
+[
+{
+ "directory": "DIR",
+ "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++",
+ "file": ""
+},
+{
+ "directory": "DIR",
+ "command": "clang -E -IInputs -fmodules -fcxx-modules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps -x c++",
+ "file": ""
+},
+]
diff --git a/clang/test/ClangScanDeps/Inputs/modules_cdb_clangcl_by_mod_name.json b/clang/test/ClangScanDeps/Inputs/modules_cdb_clangcl_by_mod_name.json
new file mode 100644
index 0000000000000..ef44985a8f915
--- /dev/null
+++ b/clang/test/ClangScanDeps/Inputs/modules_cdb_clangcl_by_mod_name.json
@@ -0,0 +1,12 @@
+[
+{
+ "directory": "DIR",
+ "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++ --",
+ "file": ""
+},
+{
+ "directory": "DIR",
+ "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++ --",
+ "file": ""
+},
+]
diff --git a/clang/test/ClangScanDeps/modules-full-by-mod-name.cpp b/clang/test/ClangScanDeps/modules-full-by-mod-name.cpp
new file mode 100644
index 0000000000000..cecc76840e1d5
--- /dev/null
+++ b/clang/test/ClangScanDeps/modules-full-by-mod-name.cpp
@@ -0,0 +1,79 @@
+// RUN: rm -rf %t.dir
+// RUN: rm -rf %t.cdb
+// RUN: mkdir -p %t.dir
+// RUN: cp %s %t.dir/modules_cdb_input.cpp
+// RUN: cp %s %t.dir/modules_cdb_input2.cpp
+// RUN: mkdir %t.dir/Inputs
+// RUN: cp %S/Inputs/header.h %t.dir/Inputs/header.h
+// RUN: cp %S/Inputs/header2.h %t.dir/Inputs/header2.h
+// RUN: cp %S/Inputs/module.modulemap %t.dir/Inputs/module.modulemap
+// RUN: sed -e "s|DIR|%/t.dir|g" %S/Inputs/modules_cdb_by_mod_name.json > %t.cdb
+// RUN: sed -e "s|DIR|%/t.dir|g" %S/Inputs/modules_cdb_clangcl_by_mod_name.json > %t_clangcl.cdb
+//
+// RUN: echo %t.dir > %t.result
+// RUN: clang-scan-deps -compilation-database %t.cdb -j 4 -format experimental-full \
+// RUN: -mode preprocess-minimized-sources -module-name=header1 >> %t.result
+// RUN: cat %t.result | sed 's:\\\\\?:/:g' | FileCheck --check-prefixes=CHECK %s
+//
+// RUN: echo %t.dir > %t_clangcl.result
+// RUN: clang-scan-deps -compilation-database %t_clangcl.cdb -j 4 -format experimental-full \
+// RUN: -mode preprocess-minimized-sources -module-name=header1 >> %t_clangcl.result
+// RUN: cat %t_clangcl.result | sed 's:\\\\\?:/:g' | FileCheck --check-prefixes=CHECK %s
+
+// CHECK: [[PREFIX:.*]]
+// CHECK-NEXT: {
+// CHECK-NEXT: "modules": [
+// CHECK-NEXT: {
+// CHECK-NEXT: "clang-module-deps": [
+// CHECK-NEXT: {
+// CHECK-NEXT: "context-hash": "[[HASH_H2_DINCLUDE:[A-Z0-9]+]]",
+// CHECK-NEXT: "module-name": "header2"
+// CHECK-NEXT: }
+// CHECK-NEXT: ],
+// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/Inputs/module.modulemap",
+// CHECK-NEXT: "command-line": [
+// CHECK-NEXT: "-cc1"
+// CHECK: "-emit-module"
+// CHECK: "-fmodule-name=header1"
+// CHECK: "-fno-implicit-modules"
+// CHECK: ],
+// CHECK-NEXT: "context-hash": "[[HASH_H1_DINCLUDE:[A-Z0-9]+]]",
+// CHECK-NEXT: "file-deps": [
+// CHECK-NEXT: "[[PREFIX]]/Inputs/header.h",
+// CHECK-NEXT: "[[PREFIX]]/Inputs/module.modulemap"
+// CHECK-NEXT: ],
+// CHECK-NEXT: "name": "header1"
+// CHECK-NEXT: },
+// CHECK-NEXT: {
+// CHECK-NEXT: "clang-module-deps": [],
+// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/Inputs/module.modulemap",
+// CHECK-NEXT: "command-line": [
+// CHECK-NEXT: "-cc1",
+// CHECK: "-emit-module",
+// CHECK: "-fmodule-name=header1",
+// CHECK: "-fno-implicit-modules",
+// CHECK: ],
+// CHECK-NEXT: "context-hash": "[[HASH_H1:[A-Z0-9]+]]",
+// CHECK-NEXT: "file-deps": [
+// CHECK-NEXT: "[[PREFIX]]/Inputs/header.h",
+// CHECK-NEXT: "[[PREFIX]]/Inputs/module.modulemap"
+// CHECK-NEXT: ],
+// CHECK-NEXT: "name": "header1"
+// CHECK-NEXT: },
+// CHECK-NEXT: {
+// CHECK-NEXT: "clang-module-deps": [],
+// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/Inputs/module.modulemap",
+// CHECK-NEXT: "command-line": [
+// CHECK-NEXT: "-cc1",
+// CHECK: "-emit-module",
+// CHECK: "-fmodule-name=header2",
+// CHECK: "-fno-implicit-modules",
+// CHECK: ],
+// CHECK-NEXT: "context-hash": "[[HASH_H2_DINCLUDE]]",
+// CHECK-NEXT: "file-deps": [
+// CHECK-NEXT: "[[PREFIX]]/Inputs/header2.h",
+// CHECK-NEXT: "[[PREFIX]]/Inputs/module.modulemap"
+// CHECK-NEXT: ],
+// CHECK-NEXT: "name": "header2"
+// CHECK-NEXT: }
+// CHECK-NEXT: ],
diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
index 92b9bdd83e396..d3630c94b5bf2 100644
--- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -194,6 +194,11 @@ llvm::cl::opt<bool> SkipExcludedPPRanges(
"until reaching the end directive."),
llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory));
+llvm::cl::opt<std::string> ModuleName(
+ "module-name", llvm::cl::Optional,
+ llvm::cl::desc("the module of which the dependencies are to be computed"),
+ llvm::cl::cat(DependencyScannerCategory));
+
llvm::cl::opt<bool> Verbose("v", llvm::cl::Optional,
llvm::cl::desc("Use verbose output."),
llvm::cl::init(false),
@@ -544,13 +549,20 @@ int main(int argc, const char **argv) {
}
// Run the tool on it.
if (Format == ScanningOutputFormat::Make) {
- auto MaybeFile = WorkerTools[I]->getDependencyFile(*Input, CWD);
+ auto MaybeFile = WorkerTools[I]->getDependencyFile(
+ *Input, CWD,
+ ModuleName.empty()
+ ? None
+ : llvm::Optional<StringRef>(ModuleName.c_str()));
if (handleMakeDependencyToolResult(Filename, MaybeFile, DependencyOS,
Errs))
HadErrors = true;
} else {
auto MaybeFullDeps = WorkerTools[I]->getFullDependencies(
- *Input, CWD, AlreadySeenModules);
+ *Input, CWD, AlreadySeenModules,
+ ModuleName.empty()
+ ? None
+ : llvm::Optional<StringRef>(ModuleName.c_str()));
if (handleFullDependencyToolResult(Filename, MaybeFullDeps, FD,
LocalIndex, DependencyOS, Errs))
HadErrors = true;
More information about the cfe-commits
mailing list