Skip to content

Add an API for clang dependency scanner to perform module lookup by name alone #3127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Sep 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions clang/include/clang-c/Dependencies.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,17 @@ clang_experimental_DependencyScannerWorker_getFileDependencies_v1(
CXDependencyScannerWorker Worker, int argc, const char *const *argv,
const char *WorkingDirectory, CXModuleDiscoveredCallback *MDC,
void *Context, CXString *error);

/**
* Same as \c clang_experimental_DependencyScannerWorker_getFileDependencies_v1,
* but get the dependencies by module name alone.
*/
CINDEX_LINKAGE CXFileDependencies *
clang_experimental_DependencyScannerWorker_getDependenciesByModuleName_v0(
CXDependencyScannerWorker Worker, int argc, const char *const *argv,
const char *ModuleName, const char *WorkingDirectory,
CXModuleDiscoveredCallback *MDC, void *Context, CXString *error);

/**
* @}
*/
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/Frontend/FrontendActions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,25 +75,29 @@ class DependencyScanningWorker {
DependencyScanningWorker(DependencyScanningService &Service);

/// Run the dependency scanning tool for a given clang driver invocation, and
/// report the discovered dependencies to the provided consumer.
/// report the discovered 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
computeDependenciesForClangInvocation(StringRef WorkingDirectory,
ArrayRef<std::string> Arguments,
DependencyConsumer &Consumer);
llvm::Error computeDependenciesForClangInvocation(
StringRef WorkingDirectory, ArrayRef<std::string> Arguments,
DependencyConsumer &Consumer,
llvm::Optional<StringRef> ModuleName = None);

/// 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);

ScanningOutputFormat getFormat() const { return Format; }

Expand Down
14 changes: 14 additions & 0 deletions clang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
13 changes: 8 additions & 5 deletions clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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();
Expand Down
63 changes: 53 additions & 10 deletions clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
//
Expand Down Expand Up @@ -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();
Expand All @@ -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
Expand Down Expand Up @@ -307,29 +326,53 @@ 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);
});
}

llvm::Error DependencyScanningWorker::computeDependenciesForClangInvocation(
StringRef WorkingDirectory, ArrayRef<std::string> Arguments,
DependencyConsumer &Consumer) {
DependencyConsumer &Consumer, llvm::Optional<StringRef> ModuleName) {
std::string Input("dependency-scanner-fake-input-file");
StringRef Output("dependency-scanner-fake-output-file");
SingleCommandCompilationDatabase CDB(
CompileCommand(WorkingDirectory, Input, Arguments, Output));
return computeDependencies(Input, WorkingDirectory, CDB, Consumer);
return computeDependencies(Input, WorkingDirectory, CDB, Consumer,
ModuleName);
}
12 changes: 12 additions & 0 deletions clang/test/ClangScanDeps/Inputs/modules_cdb_by_mod_name.json
Original file line number Diff line number Diff line change
@@ -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": ""
},
]
Original file line number Diff line number Diff line change
@@ -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": ""
},
]
79 changes: 79 additions & 0 deletions clang/test/ClangScanDeps/modules-full-by-mod-name.cpp
Original file line number Diff line number Diff line change
@@ -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: ],
Loading