From 34b6791ea95a758996949734e47661392c20c900 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Tue, 27 Jun 2023 15:33:15 -0700 Subject: [PATCH] [DependencyScanning] Add ability to scan TU with multiple module imports Update Dependency scanner so it can scan the dependency of a TU with additional module imports that is not specified in the main TU. This also unifies the DependencyScanningAction for TU and module. When scanning for module dependency, it is equivalent to using an empty TU + one addition module import. --- .../include/clang/Frontend/FrontendActions.h | 12 +- .../DependencyScanningTool.h | 15 +- .../DependencyScanningWorker.h | 24 ++-- clang/lib/Frontend/FrontendActions.cpp | 35 ++--- .../DependencyScanningTool.cpp | 8 +- .../DependencyScanningWorker.cpp | 59 ++++---- clang/test/ClangScanDeps/tu-with-modules.c | 129 ++++++++++++++++++ clang/test/Index/Core/scan-deps-by-mod-name.m | 2 +- clang/tools/clang-scan-deps/ClangScanDeps.cpp | 45 ++++-- clang/tools/clang-scan-deps/Opts.td | 2 + clang/tools/libclang/CDependencies.cpp | 9 +- 11 files changed, 258 insertions(+), 82 deletions(-) create mode 100644 clang/test/ClangScanDeps/tu-with-modules.c diff --git a/clang/include/clang/Frontend/FrontendActions.h b/clang/include/clang/Frontend/FrontendActions.h index a620ddfc40447d7..1f3e1caa78e2f3a 100644 --- a/clang/include/clang/Frontend/FrontendActions.h +++ b/clang/include/clang/Frontend/FrontendActions.h @@ -34,12 +34,15 @@ class InitOnlyAction : public FrontendAction { /// Preprocessor-based frontend action that also loads PCH files. class ReadPCHAndPreprocessAction : public FrontendAction { + ArrayRef ImportModules; void ExecuteAction() override; std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override; public: + ReadPCHAndPreprocessAction(ArrayRef ImportModules) + : ImportModules(ImportModules) {} bool usesPreprocessorOnly() const override { return false; } }; @@ -319,15 +322,6 @@ 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 c747bcc3907644a..ae78e3b0690bc7a 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h @@ -174,14 +174,19 @@ class DependencyScanningTool { /// \param LookupModuleOutput This function is called to fill in /// "-fmodule-file=", "-o" and other output /// arguments for dependencies. + /// \param AdditionalModules Additional modules that need to be imported. + /// \param TUBuffer Optional memory buffer for translation unit input. If + /// TUBuffer is nullopt, the input should be included in the + /// Commandline already. /// /// \returns a \c StringError with the diagnostic output if clang errors /// occurred, \c TranslationUnitDeps otherwise. - llvm::Expected - getTranslationUnitDependencies(const std::vector &CommandLine, - StringRef CWD, - const llvm::DenseSet &AlreadySeen, - LookupModuleOutputCallback LookupModuleOutput); + llvm::Expected getTranslationUnitDependencies( + const std::vector &CommandLine, StringRef CWD, + const llvm::DenseSet &AlreadySeen, + LookupModuleOutputCallback LookupModuleOutput, + ArrayRef AdditionalModules = {}, + std::optional TUBuffer = std::nullopt); /// Given a compilation context specified via the Clang driver command-line, /// gather modular dependencies of module with the given name, and return the diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h index 52c89e962692523..551b78dfbb3688d 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h @@ -18,6 +18,7 @@ #include "llvm/CAS/CachingOnDiskFileSystem.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBufferRef.h" #include #include @@ -119,24 +120,27 @@ class DependencyScanningWorker { llvm::IntrusiveRefCntPtr FS); /// Run the dependency scanning tool for a given clang driver command-line, - /// and report the discovered dependencies to the provided consumer. If \p - /// ModuleName isn't empty, this function reports the dependencies of module - /// \p ModuleName. + /// and report the discovered dependencies to the provided consumer. If + /// TUBuffer is not nullopt, it is used as TU input for the dependency + /// scanning. Otherwise, the input should be included as part of the + /// command-line. ModuleNames is a list of additional modules to be imported + /// in addition to what is referenced in TU. /// /// \returns false if clang errors occurred (with diagnostics reported to /// \c DiagConsumer), true otherwise. - bool computeDependencies(StringRef WorkingDirectory, - const std::vector &CommandLine, - DependencyConsumer &DepConsumer, - DependencyActionController &Controller, - DiagnosticConsumer &DiagConsumer, - std::optional ModuleName = std::nullopt); + bool computeDependencies( + StringRef WorkingDirectory, const std::vector &CommandLine, + DependencyConsumer &DepConsumer, DependencyActionController &Controller, + DiagnosticConsumer &DiagConsumer, ArrayRef ModuleNames = {}, + std::optional TUBuffer = std::nullopt); + /// \returns A \c StringError with the diagnostic output if clang errors /// occurred, success otherwise. llvm::Error computeDependencies( StringRef WorkingDirectory, const std::vector &CommandLine, DependencyConsumer &Consumer, DependencyActionController &Controller, - std::optional ModuleName = std::nullopt); + ArrayRef ModuleNames = {}, + std::optional TUBuffer = std::nullopt); /// Scan from a compiler invocation. /// If \p DiagGenerationAsCompilation is true it will generate error diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index df13712c0d8ee3a..41fb0844cd94a6b 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -67,7 +67,8 @@ InitOnlyAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { void InitOnlyAction::ExecuteAction() { } -// Basically PreprocessOnlyAction::ExecuteAction. +// DependencyScanningPreprocessAction imports additional modules first, then +// preprocess the source file. void ReadPCHAndPreprocessAction::ExecuteAction() { Preprocessor &PP = getCompilerInstance().getPreprocessor(); @@ -77,6 +78,23 @@ void ReadPCHAndPreprocessAction::ExecuteAction() { Token Tok; // Start parsing the specified input file. PP.EnterMainSourceFile(); + + // Import additional modules first. + CompilerInstance &CI = getCompilerInstance(); + SourceManager &SM = PP.getSourceManager(); + FileID MainFileID = SM.getMainFileID(); + PPCallbacks *CB = PP.getPPCallbacks(); + unsigned Offset = -ImportModules.size() - 1; + for (auto Module : ImportModules) { + SourceLocation IncludeLoc = + SM.getLocForEndOfFile(MainFileID).getLocWithOffset(Offset++); + IdentifierInfo *ModuleID = PP.getIdentifierInfo(Module); + SmallVector, 2> Path; + Path.push_back(std::make_pair(ModuleID, IncludeLoc)); + auto ModResult = CI.loadModule(IncludeLoc, Path, Module::Hidden, false); + CB->moduleImport(IncludeLoc, Path, ModResult); + } + do { PP.Lex(Tok); } while (Tok.isNot(tok::eof)); @@ -1200,18 +1218,3 @@ void PrintDependencyDirectivesSourceMinimizerAction::ExecuteAction() { printDependencyDirectivesAsSource(FromFile.getBuffer(), Directives, llvm::outs()); } - -void GetDependenciesByModuleNameAction::ExecuteAction() { - CompilerInstance &CI = getCompilerInstance(); - Preprocessor &PP = CI.getPreprocessor(); - SourceManager &SM = PP.getSourceManager(); - FileID MainFileID = SM.getMainFileID(); - PP.EnterSourceFile(MainFileID, nullptr, SourceLocation()); - SourceLocation FileStart = SM.getLocForStartOfFile(MainFileID); - SmallVector, 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 123603e0dc525ed..36e2ef04321980c 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp @@ -268,11 +268,13 @@ llvm::Expected DependencyScanningTool::getTranslationUnitDependencies( const std::vector &CommandLine, StringRef CWD, const llvm::DenseSet &AlreadySeen, - LookupModuleOutputCallback LookupModuleOutput) { + LookupModuleOutputCallback LookupModuleOutput, + ArrayRef AddedModules, + std::optional TUBuffer) { FullDependencyConsumer Consumer(AlreadySeen); auto Controller = createActionController(LookupModuleOutput); - llvm::Error Result = - Worker.computeDependencies(CWD, CommandLine, Consumer, *Controller); + llvm::Error Result = Worker.computeDependencies( + CWD, CommandLine, Consumer, *Controller, AddedModules, TUBuffer); if (Result) return std::move(Result); return Consumer.takeTranslationUnitDeps(); diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp index 47936613eae1c01..6fe1659a29adaeb 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp @@ -34,6 +34,8 @@ #include "llvm/CAS/ObjectStore.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/PrefixMapper.h" #include "llvm/TargetParser/Host.h" #include @@ -400,7 +402,7 @@ class DependencyScanningAction : public tooling::ToolAction { ScanningOutputFormat Format, ScanningOptimizations OptimizeArgs, bool EagerLoadModules, bool DisableFree, bool EmitDependencyFile, bool DiagGenerationAsCompilation, const CASOptions &CASOpts, - std::optional ModuleName = std::nullopt, + ArrayRef ModuleNames = {}, raw_ostream *VerboseOS = nullptr) : WorkingDirectory(WorkingDirectory), Consumer(Consumer), Controller(Controller), DepFS(std::move(DepFS)), @@ -409,7 +411,7 @@ class DependencyScanningAction : public tooling::ToolAction { EagerLoadModules(EagerLoadModules), DisableFree(DisableFree), CASOpts(CASOpts), EmitDependencyFile(EmitDependencyFile), DiagGenerationAsCompilation(DiagGenerationAsCompilation), - ModuleName(ModuleName), VerboseOS(VerboseOS) { + ModuleNames(ModuleNames), VerboseOS(VerboseOS) { // The FullIncludeTree output format completely subsumes header search and // VFS optimizations due to how it works. Disable these optimizations so // we're not doing unneeded work. @@ -602,12 +604,8 @@ class DependencyScanningAction : public tooling::ToolAction { // Avoid some checks and module map parsing when loading PCM files. ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false; - std::unique_ptr Action; - - if (ModuleName) - Action = std::make_unique(*ModuleName); - else - Action = std::make_unique(); + std::unique_ptr Action = + std::make_unique(ModuleNames); // Normally this would be handled by GeneratePCHAction if (ScanInstance.getFrontendOpts().ProgramAction == frontend::GeneratePCH) @@ -692,7 +690,7 @@ class DependencyScanningAction : public tooling::ToolAction { const CASOptions &CASOpts; bool EmitDependencyFile = false; bool DiagGenerationAsCompilation; - std::optional ModuleName; + ArrayRef ModuleNames; std::optional ScanInstanceStorage; std::shared_ptr MDC; std::vector LastCC1Arguments; @@ -747,7 +745,8 @@ DependencyScanningWorker::getOrCreateFileManager() const { llvm::Error DependencyScanningWorker::computeDependencies( StringRef WorkingDirectory, const std::vector &CommandLine, DependencyConsumer &Consumer, DependencyActionController &Controller, - std::optional ModuleName) { + ArrayRef ModuleNames, + std::optional TUBuffer) { std::vector CLI; for (const std::string &Arg : CommandLine) CLI.push_back(Arg.c_str()); @@ -761,7 +760,7 @@ llvm::Error DependencyScanningWorker::computeDependencies( TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.release()); if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller, - DiagPrinter, ModuleName)) + DiagPrinter, ModuleNames, TUBuffer)) return llvm::Error::success(); return llvm::make_error(DiagnosticsOS.str(), llvm::inconvertibleErrorCode()); @@ -831,29 +830,41 @@ static bool createAndRunToolInvocation( bool DependencyScanningWorker::computeDependencies( StringRef WorkingDirectory, const std::vector &CommandLine, DependencyConsumer &Consumer, DependencyActionController &Controller, - DiagnosticConsumer &DC, std::optional ModuleName) { + DiagnosticConsumer &DC, ArrayRef ModuleNames, + std::optional TUBuffer) { // Reset what might have been modified in the previous worker invocation. BaseFS->setCurrentWorkingDirectory(WorkingDirectory); std::optional> ModifiedCommandLine; llvm::IntrusiveRefCntPtr ModifiedFS; - // If we're scanning based on a module name alone, we don't expect the client - // to provide us with an input file. However, the driver really wants to have - // one. Let's just make it up to make the driver happy. - if (ModuleName) { + // Create an empty buffer if module names are provided. + std::unique_ptr SrcBuffer; + if (!ModuleNames.empty() && !TUBuffer) { + SrcBuffer = llvm::MemoryBuffer::getMemBuffer(""); + TUBuffer = SrcBuffer->getMemBufferRef(); + } + + // If the TU is passed as a buffer to the dependency scanner, overlay that + // with InMemoryFS and setup the input correctly in command-line. + if (TUBuffer) { auto OverlayFS = llvm::makeIntrusiveRefCnt(BaseFS); auto InMemoryFS = llvm::makeIntrusiveRefCnt(); InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory); - SmallString<128> FakeInputPath; - // TODO: We should retry the creation if the path already exists. - llvm::sys::fs::createUniquePath(*ModuleName + "-%%%%%%%%.input", - FakeInputPath, - /*MakeAbsolute=*/false); - InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer("")); + // Use a stable name "" in root directory. + llvm::SmallString<256> FakeInputPath( + llvm::sys::path::root_path(WorkingDirectory)); + llvm::sys::path::append(FakeInputPath, ""); + + // Extend the buffer so it starts with some white space reserved for + // importing additional modules. + std::string FileContent(ModuleNames.size(), ' '); + FileContent.append(TUBuffer->getBuffer().str()); + InMemoryFS->addFile(FakeInputPath, 0, + llvm::MemoryBuffer::getMemBufferCopy(FileContent)); llvm::IntrusiveRefCntPtr InMemoryOverlay = InMemoryFS; @@ -900,7 +911,7 @@ bool DependencyScanningWorker::computeDependencies( DisableFree, /*EmitDependencyFile=*/false, /*DiagGenerationAsCompilation=*/false, getCASOpts(), - ModuleName); + ModuleNames); bool Success = false; if (FinalCommandLine[1] == "-cc1") { Success = createAndRunToolInvocation(FinalCommandLine, Action, *FileMgr, @@ -976,7 +987,7 @@ void DependencyScanningWorker::computeDependenciesFromCompilerInvocation( ScanningOptimizations::Default, /*DisableFree=*/false, EagerLoadModules, /*EmitDependencyFile=*/!DepFile.empty(), DiagGenerationAsCompilation, getCASOpts(), - /*ModuleName=*/std::nullopt, VerboseOS); + /*ModuleNames=*/{}, VerboseOS); // Ignore result; we're just collecting dependencies. // diff --git a/clang/test/ClangScanDeps/tu-with-modules.c b/clang/test/ClangScanDeps/tu-with-modules.c new file mode 100644 index 000000000000000..1b2accc3a9a22bd --- /dev/null +++ b/clang/test/ClangScanDeps/tu-with-modules.c @@ -0,0 +1,129 @@ +// UNSUPPORTED: target=powerpc64-ibm-aix{{.*}} + +// RUN: rm -rf %t +// RUN: split-file %s %t + +//--- module.modulemap +module root { header "root.h" } +module direct { header "direct.h" } +module transitive { header "transitive.h" } +module addition { header "addition.h" } +//--- root.h +#include "direct.h" +#include "root/textual.h" +//--- direct.h +#include "transitive.h" +//--- transitive.h +// empty + +//--- addition.h +// empty + +//--- tu.c +#include "root.h" + +//--- root/textual.h +// This is here to verify that the "root" directory doesn't clash with name of +// the "root" module. + +//--- cdb.json.template +[{ + "file": "", + "directory": "DIR", + "command": "clang -fmodules -fmodules-cache-path=DIR/cache -I DIR -x c -c" +}] + +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full -tu-path %t/tu.c > %t/result.json +// RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s --check-prefix=CHECK +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full -tu-path %t/tu.c -module-name addition > %t/result.json +// RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s --check-prefix=CHECK --check-prefix=ADDITION + +// CHECK: { +// CHECK-NEXT: "modules": [ +// ADDITION-NEXT: { +// ADDITION-NEXT: "clang-module-deps": [], +// ADDITION-NEXT: "clang-modulemap-file": "[[PREFIX]]/module.modulemap", +// ADDITION-NEXT: "command-line": [ +// ADDITION: ], +// ADDITION-NEXT: "context-hash": "{{.*}}", +// ADDITION-NEXT: "file-deps": [ +// ADDITION-NEXT: "[[PREFIX]]/module.modulemap" +// ADDITION-NEXT: "[[PREFIX]]/addition.h" +// ADDITION-NEXT: ], +// ADDITION: "name": "addition" +// ADDITION-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "transitive" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/direct.h" +// CHECK-NEXT: ], +// CHECK: "name": "direct" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "direct" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/root.h" +// CHECK-NEXT: "[[PREFIX]]/root/textual.h" +// CHECK-NEXT: ], +// CHECK: "name": "root" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/transitive.h" +// CHECK-NEXT: ], +// CHECK: "name": "transitive" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "translation-units": [ +// CHECK-NEXT: { +// CHECK-NEXT: "commands": [ +// CHECK-NEXT: { +// CHECK-NEXT: "clang-context-hash": "{{.*}}", +// CHECK-NEXT: "clang-module-deps": [ +// ADDITION-NEXT: { +// ADDITION-NEXT: "context-hash": "{{.*}}", +// ADDITION-NEXT: "module-name": "addition" +// ADDITION-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "root" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "command-line": [ +// CHECK: ], +// CHECK: "file-deps": [ +// CHECK-NEXT: +// CHECK-NEXT: ], +// CHECK-NEXT: "input-file": "" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } diff --git a/clang/test/Index/Core/scan-deps-by-mod-name.m b/clang/test/Index/Core/scan-deps-by-mod-name.m index 114b059355dff35..ceb8a3e42af7d3e 100644 --- a/clang/test/Index/Core/scan-deps-by-mod-name.m +++ b/clang/test/Index/Core/scan-deps-by-mod-name.m @@ -27,5 +27,5 @@ // CHECK-NEXT: module-deps: // CHECK-NEXT: ModA:[[HASH_MOD_A]] // CHECK-NEXT: file-deps: -// CHECK-NEXT: {{.*}}ModA-{{.*}}.input +// CHECK-NEXT: // CHECK-NEXT: build-args: -cc1 {{.*}} -fmodule-file={{(ModA=)?}}{{.*}}ModA_{{.*}}.pcm diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp index c9a9f05b927e746..572734b0ec908ca 100644 --- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -29,6 +29,7 @@ #include "llvm/Support/Format.h" #include "llvm/Support/JSON.h" #include "llvm/Support/LLVMDriver.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Program.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" @@ -36,6 +37,7 @@ #include "llvm/Support/Threading.h" #include "llvm/Support/Timer.h" #include "llvm/TargetParser/Host.h" +#include #include #include #include @@ -89,8 +91,9 @@ static std::string ModuleFilesDir; static bool EagerLoadModules; static unsigned NumThreads = 0; static std::string CompilationDB; -static std::string ModuleName; +static std::vector ModuleNames; static std::vector ModuleDepTargets; +static std::string TranslationUnitFile; static bool DeprecatedDriverCommand; static ResourceDirRecipeKind ResourceDirRecipe; static bool Verbose; @@ -215,11 +218,14 @@ static void ParseArgs(int argc, char **argv) { CompilationDB = A->getValue(); if (const llvm::opt::Arg *A = Args.getLastArg(OPT_module_name_EQ)) - ModuleName = A->getValue(); + ModuleNames.emplace_back(A->getValue()); for (const llvm::opt::Arg *A : Args.filtered(OPT_dependency_target_EQ)) ModuleDepTargets.emplace_back(A->getValue()); + if (const llvm::opt::Arg *A = Args.getLastArg(OPT_tu_path_EQ)) + TranslationUnitFile = A->getValue(); + DeprecatedDriverCommand = Args.hasArg(OPT_deprecated_driver_command); if (const llvm::opt::Arg *A = Args.getLastArg(OPT_resource_dir_recipe_EQ)) { @@ -1266,10 +1272,11 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) { return {}; }; + bool IsScanModule = TranslationUnitFile.empty() && ModuleNames.size() == 1; if (Format == ScanningOutputFormat::Full || Format == ScanningOutputFormat::FullTree || Format == ScanningOutputFormat::FullIncludeTree) - FD.emplace(ModuleName.empty() ? Inputs.size() : 0); + FD.emplace(!IsScanModule ? Inputs.size() : 0); struct DepTreeResult { size_t Index; @@ -1290,7 +1297,7 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) { if (Format == ScanningOutputFormat::Full || Format == ScanningOutputFormat::FullTree) - FD.emplace(ModuleName.empty() ? Inputs.size() : 0); + FD.emplace(!IsScanModule ? Inputs.size() : 0); std::atomic NumStatusCalls = 0; std::atomic NumOpenFileForReadCalls = 0; @@ -1312,10 +1319,6 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) { std::string Filename = std::move(Input->Filename); std::string CWD = std::move(Input->Directory); - std::optional MaybeModuleName; - if (!ModuleName.empty()) - MaybeModuleName = ModuleName; - std::string OutputDir(ModuleFilesDir); if (OutputDir.empty()) OutputDir = getModuleCachePath(Input->CommandLine); @@ -1382,16 +1385,34 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) { MakeformatOS, Errs)) HadErrors = true; } - } else if (MaybeModuleName) { + } else if (IsScanModule) { auto MaybeModuleDepsGraph = WorkerTool.getModuleDependencies( - *MaybeModuleName, Input->CommandLine, CWD, AlreadySeenModules, + ModuleNames.front(), Input->CommandLine, CWD, AlreadySeenModules, LookupOutput); - if (handleModuleResult(*MaybeModuleName, MaybeModuleDepsGraph, *FD, + if (handleModuleResult(ModuleNames.front(), MaybeModuleDepsGraph, *FD, LocalIndex, DependencyOS, Errs)) HadErrors = true; } else { + std::unique_ptr TU; + std::optional TUBuffer; + if (!TranslationUnitFile.empty()) { + auto MaybeTU = llvm::MemoryBuffer::getFile(TranslationUnitFile); + if (!MaybeTU) { + llvm::errs() << "cannot open input translation unit: " + << MaybeTU.getError().message() << "\n"; + HadErrors = true; + continue; + } + TU = std::move(*MaybeTU); + TUBuffer = TU->getMemBufferRef(); + } + std::vector ImportModules; + llvm::for_each(ModuleNames, [&ImportModules](const std::string &name) { + ImportModules.push_back(name); + }); auto MaybeTUDeps = WorkerTool.getTranslationUnitDependencies( - Input->CommandLine, CWD, AlreadySeenModules, LookupOutput); + Input->CommandLine, CWD, AlreadySeenModules, LookupOutput, + ImportModules, TUBuffer); if (handleTranslationUnitResult(Filename, MaybeTUDeps, *FD, LocalIndex, DependencyOS, Errs)) HadErrors = true; diff --git a/clang/tools/clang-scan-deps/Opts.td b/clang/tools/clang-scan-deps/Opts.td index cc479c12c965d56..03374a7d7182f07 100644 --- a/clang/tools/clang-scan-deps/Opts.td +++ b/clang/tools/clang-scan-deps/Opts.td @@ -29,6 +29,8 @@ defm compilation_database : Eq<"compilation-database", "Compilation database">; defm module_name : Eq<"module-name", "the module of which the dependencies are to be computed">; defm dependency_target : Eq<"dependency-target", "The names of dependency targets for the dependency file">; +defm tu_path: Eq<"tu-path", "The path to the translation unit for depscan. Not compatible with -module-name">; + def deprecated_driver_command : F<"deprecated-driver-command", "use a single driver command to build the tu (deprecated)">; defm resource_dir_recipe : Eq<"resource-dir-recipe", "How to produce missing '-resource-dir' argument">; diff --git a/clang/tools/libclang/CDependencies.cpp b/clang/tools/libclang/CDependencies.cpp index 11c85aae34c8a56..b28024a1149f939 100644 --- a/clang/tools/libclang/CDependencies.cpp +++ b/clang/tools/libclang/CDependencies.cpp @@ -23,6 +23,7 @@ #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/CAS/CASProvidingFileSystem.h" #include "llvm/CAS/CachingOnDiskFileSystem.h" #include "llvm/Support/Process.h" @@ -241,15 +242,19 @@ static CXErrorCode getFullDependencies(DependencyScanningWorker *Worker, assert(HasDiagConsumer ^ HasError && "Both DiagConsumer and Error provided"); #endif + llvm::SmallVector ModuleNames; + if (ModuleName) + ModuleNames.push_back(*ModuleName); + if (DiagConsumer) { bool Result = Worker->computeDependencies(WorkingDirectory, Compilation, DepConsumer, - *Controller, *DiagConsumer, ModuleName); + *Controller, *DiagConsumer, ModuleNames); if (!Result) return CXError_Failure; } else if (Error) { auto Result = Worker->computeDependencies( - WorkingDirectory, Compilation, DepConsumer, *Controller, ModuleName); + WorkingDirectory, Compilation, DepConsumer, *Controller, ModuleNames); if (Result) { *Error = cxstring::createDup(llvm::toString(std::move(Result))); return CXError_Failure;