https://github.com/naveen-seth updated https://github.com/llvm/llvm-project/pull/169964
>From 99dd142baa2f3fd4d68feadd57718633ffd7c88f Mon Sep 17 00:00:00 2001 From: Naveen Seth Hanig <[email protected]> Date: Fri, 28 Nov 2025 23:01:01 +0100 Subject: [PATCH 1/2] [clang][DependencyScanning] Remove dependency on clangDriver from clangDependencyScanning This follows PR #169962 and removes the dependency on clangDriver from clangDependencyScanning. DependencyScanningWorker now only supports -cc1 command line inputs, and all functionality related to driver-level commands has been moved into clangTooling (DependencyScanningTool.cpp). Because DependencyScanningWorker now only accepts -cc1 inputs, this patch enables the use of -cc1 commands with the by-name scanning API introduced in #164345. This is part of a broader effort to support driver-managed builds for compilations using C++ named modules and/or Clang modules. It is required for linking the dependency scanning tooling against the driver without introducing cyclic dependencies, which would otherwise cause build failures when dynamic linking is enabled. The RFC for this change can be found here: https://discourse.llvm.org/t/rfc-new-clangoptions-library-remove-dependency-on-clangdriver-from-clangfrontend-and-flangfrontend/88773?u=naveen-seth --- .../DependencyScannerImpl.h | 39 +-- .../DependencyScanningWorker.h | 96 +++--- .../clang/Tooling/DependencyScanningTool.h | 9 +- clang/lib/DependencyScanning/CMakeLists.txt | 2 - .../DependencyScannerImpl.cpp | 147 +-------- .../DependencyScanningWorker.cpp | 168 +++------- clang/lib/Tooling/CMakeLists.txt | 1 + clang/lib/Tooling/DependencyScanningTool.cpp | 288 ++++++++++++++++-- .../ClangScanDeps/modules-full-by-mod-name.c | 38 ++- .../modules-full-by-mult-mod-names.c | 37 ++- clang/test/ClangScanDeps/tu-buffer.c | 11 + clang/tools/clang-scan-deps/ClangScanDeps.cpp | 6 +- .../DependencyScanningWorkerTest.cpp | 25 +- 13 files changed, 478 insertions(+), 389 deletions(-) diff --git a/clang/include/clang/DependencyScanning/DependencyScannerImpl.h b/clang/include/clang/DependencyScanning/DependencyScannerImpl.h index 750688702ec23..7eafb48943c91 100644 --- a/clang/include/clang/DependencyScanning/DependencyScannerImpl.h +++ b/clang/include/clang/DependencyScanning/DependencyScannerImpl.h @@ -16,7 +16,6 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/TextDiagnosticPrinter.h" -#include "clang/Serialization/ObjectFilePCHContainerReader.h" namespace clang { class DiagnosticConsumer; @@ -88,27 +87,10 @@ struct TextDiagnosticsPrinterWithOutput { DiagPrinter(DiagnosticsOS, *DiagOpts) {} }; -std::pair<std::unique_ptr<driver::Driver>, std::unique_ptr<driver::Compilation>> -buildCompilation(ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - llvm::BumpPtrAllocator &Alloc); - std::unique_ptr<CompilerInvocation> createCompilerInvocation(ArrayRef<std::string> CommandLine, DiagnosticsEngine &Diags); -std::pair<IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::vector<std::string>> -initVFSForTUBufferScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS, - ArrayRef<std::string> CommandLine, - StringRef WorkingDirectory, - llvm::MemoryBufferRef TUBuffer); - -std::pair<IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>, - std::vector<std::string>> -initVFSForByNameScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS, - ArrayRef<std::string> CommandLine, - StringRef WorkingDirectory, StringRef ModuleName); - bool initializeScanCompilerInstance( CompilerInstance &ScanInstance, IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, @@ -143,22 +125,11 @@ class CompilerInstanceWithContext { llvm::StringRef CWD; std::vector<std::string> CommandLine; - // Context - file systems - llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS; - // Context - Diagnostics engine. - std::unique_ptr<TextDiagnosticsPrinterWithOutput> DiagPrinterWithOS; - // DiagConsumer may points to DiagPrinterWithOS->DiagPrinter, or a custom - // DiagnosticConsumer passed in from initialize. DiagnosticConsumer *DiagConsumer = nullptr; std::unique_ptr<DignosticsEngineWithDiagOpts> DiagEngineWithCmdAndOpts; // Context - compiler invocation - // Compilation's command's arguments may be owned by Alloc when expanded from - // response files, so we need to keep Alloc alive in the context. - llvm::BumpPtrAllocator Alloc; - std::unique_ptr<clang::driver::Driver> Driver; - std::unique_ptr<clang::driver::Compilation> Compilation; std::unique_ptr<CompilerInvocation> OriginalInvocation; // Context - output options @@ -180,15 +151,13 @@ class CompilerInstanceWithContext { : Worker(Worker), CWD(CWD), CommandLine(CMD) {}; // The three methods below returns false when they fail, with the detail - // accumulated in DiagConsumer. - bool initialize(DiagnosticConsumer *DC); + // accumulated in \c DiagEngineWithDiagOpts's diagnostic consumer. + bool initialize( + std::unique_ptr<DignosticsEngineWithDiagOpts> DiagEngineWithDiagOpts, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS); bool computeDependencies(StringRef ModuleName, DependencyConsumer &Consumer, DependencyActionController &Controller); bool finalize(); - - // The method below turns the return status from the above methods - // into an llvm::Error using a default DiagnosticConsumer. - llvm::Error handleReturnStatus(bool Success); }; } // namespace dependencies } // namespace clang diff --git a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h index 9585691607ca9..e3564dfbe881c 100644 --- a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h @@ -9,9 +9,11 @@ #ifndef LLVM_CLANG_DEPENDENCYSCANNING_DEPENDENCYSCANNINGWORKER_H #define LLVM_CLANG_DEPENDENCYSCANNING_DEPENDENCYSCANNINGWORKER_H +#include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/LLVM.h" +#include "clang/DependencyScanning/DependencyScannerImpl.h" #include "clang/DependencyScanning/DependencyScanningService.h" #include "clang/DependencyScanning/ModuleDepCollector.h" #include "clang/Frontend/PCHContainerOperations.h" @@ -91,41 +93,56 @@ class DependencyScanningWorker { ~DependencyScanningWorker(); - /// Run the dependency scanning tool for a given clang driver command-line, - /// 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. + /// Run the dependency scanning tool for a given clang -cc1 command-line, + /// and report the discovered dependencies to the provided consumer. /// - /// \returns false if clang errors occurred (with diagnostics reported to + /// @return false if clang errors occurred (with diagnostics reported to /// \c DiagConsumer), true otherwise. bool computeDependencies( StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, DependencyConsumer &DepConsumer, DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer, - std::optional<llvm::MemoryBufferRef> TUBuffer = std::nullopt); + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ScanFS = nullptr); - /// Run the dependency scanning tool for a given clang driver command-line - /// for a specific translation unit via file system or memory buffer. + /// Run the dependency scanning tool for all given clang -cc1 command-lines, + /// and report the discovered dependencies to the provided consumer. /// - /// \returns A \c StringError with the diagnostic output if clang errors - /// occurred, success otherwise. - llvm::Error computeDependencies( - StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, + /// \returns false if clang errors occurred (with diagnostics reported to + /// \c Diags), true otherwise. + bool computeDependencies( + StringRef WorkingDirectory, + ArrayRef<std::vector<std::string>> CommandLines, DependencyConsumer &Consumer, DependencyActionController &Controller, - std::optional<llvm::MemoryBufferRef> TUBuffer = std::nullopt); + DiagnosticsEngine &Diags, + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ScanFS = nullptr); /// The three method below implements a new interface for by name /// dependency scanning. They together enable the dependency scanning worker /// to more effectively perform scanning for a sequence of modules /// by name when the CWD and CommandLine do not change across the queries. + /// The initialization function asks the client for a DiagnosticsConsumer + /// that it direct the diagnostics to. /// @brief Initializing the context and the compiler instance. /// @param CWD The current working directory used during the scan. /// @param CommandLine The commandline used for the scan. - /// @return Error if the initializaiton fails. - llvm::Error initializeCompilerInstanceWithContextOrError( - StringRef CWD, const std::vector<std::string> &CommandLine); + /// @return False if the initializaiton fails. + bool initializeCompilerInstanceWithContext(StringRef CWD, + ArrayRef<std::string> CommandLine, + DiagnosticConsumer &DC); + + /// @brief Initializing the context and the compiler instance. + /// @param CWD The current working directory used during the scan. + /// @param CommandLine The commandline used for the scan. + /// @param DiagEngineWithCmdAndOpts Preconfigured diagnostics engine and + /// options associated with the cc1 command line. + /// @param FS The file system (typically an overlay) to use for this compiler + /// instance. + /// @return False if the initializaiton fails. + bool initializeCompilerInstanceWithContext( + StringRef CWD, ArrayRef<std::string> CommandLine, + std::unique_ptr<DignosticsEngineWithDiagOpts> DiagEngineWithCmdAndOpts, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS); /// @brief Performaces dependency scanning for the module whose name is /// specified. @@ -133,28 +150,15 @@ class DependencyScanningWorker { /// scanned. /// @param Consumer The dependency consumer that stores the results. /// @param Controller The controller for the dependency scanning action. - /// @return Error if the scanner incurs errors. - llvm::Error computeDependenciesByNameWithContextOrError( - StringRef ModuleName, DependencyConsumer &Consumer, - DependencyActionController &Controller); - - /// @brief Finalizes the diagnostics engine and deletes the compiler instance. - /// @return Error if errors occur during finalization. - llvm::Error finalizeCompilerInstanceWithContextOrError(); - - /// The three methods below provides the same functionality as the - /// three methods above. Instead of returning `llvm::Error`s, these - /// three methods return a flag to indicate if the call is successful. - /// The initialization function asks the client for a DiagnosticsConsumer - /// that it direct the diagnostics to. - bool initializeCompilerInstanceWithContext( - StringRef CWD, const std::vector<std::string> &CommandLine, - DiagnosticConsumer *DC = nullptr); + /// @return False if the scanner incurs errors. bool computeDependenciesByNameWithContext(StringRef ModuleName, DependencyConsumer &Consumer, DependencyActionController &Controller); - bool finalizeCompilerInstance(); + + /// @brief Finalizes the diagnostics engine and deletes the compiler instance. + /// @return False if errors occur during finalization. + bool finalizeCompilerInstanceWithContext(); llvm::vfs::FileSystem &getVFS() const { return *DepFS; } @@ -169,13 +173,21 @@ class DependencyScanningWorker { friend CompilerInstanceWithContext; std::unique_ptr<CompilerInstanceWithContext> CIWithContext; - /// Actually carries out the scan. If \c OverlayFS is provided, it must be - /// based on top of DepFS. - bool scanDependencies( - StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, - DependencyConsumer &Consumer, DependencyActionController &Controller, - DiagnosticConsumer &DC, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayFS = nullptr); + /// Private helper functions to actually carry out the scan. If \c OverlayFS + /// is provided, it must be based on top of DepFS. + bool scanDependencies(StringRef WorkingDirectory, + const std::vector<std::string> &CommandLine, + DependencyConsumer &Consumer, + DependencyActionController &Controller, + DiagnosticConsumer &DC, + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS); + + bool scanDependencies(StringRef WorkingDirectory, + ArrayRef<std::vector<std::string>> CommandLine, + DependencyConsumer &Consumer, + DependencyActionController &Controller, + DiagnosticsEngine &Diags, + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS); }; } // end namespace dependencies diff --git a/clang/include/clang/Tooling/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanningTool.h index 0ac142a3fc673..41bdcb7dc11ef 100644 --- a/clang/include/clang/Tooling/DependencyScanningTool.h +++ b/clang/include/clang/Tooling/DependencyScanningTool.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_TOOLING_DEPENDENCYSCANNINGTOOL_H #define LLVM_CLANG_TOOLING_DEPENDENCYSCANNINGTOOL_H +#include "clang/DependencyScanning/DependencyScannerImpl.h" #include "clang/DependencyScanning/DependencyScanningService.h" #include "clang/DependencyScanning/DependencyScanningUtils.h" #include "clang/DependencyScanning/DependencyScanningWorker.h" @@ -127,8 +128,8 @@ class DependencyScanningTool { /// @param CWD The current working directory used during the scan. /// @param CommandLine The commandline used for the scan. /// @return Error if the initializaiton fails. - llvm::Error initializeCompilerInstanceWithContext( - StringRef CWD, const std::vector<std::string> &CommandLine); + llvm::Error initializeCompilerInstanceWithContextOrError( + StringRef CWD, ArrayRef<std::string> CommandLine); /// @brief Computes the dependeny for the module named ModuleName. /// @param ModuleName The name of the module for which this method computes @@ -145,7 +146,7 @@ class DependencyScanningTool { /// @return An instance of \c TranslationUnitDeps if the scan is successful. /// Otherwise it returns an error. llvm::Expected<clang::dependencies::TranslationUnitDeps> - computeDependenciesByNameWithContext( + computeDependenciesByNameWithContextOrError( StringRef ModuleName, const llvm::DenseSet<clang::dependencies::ModuleID> &AlreadySeen, clang::dependencies::LookupModuleOutputCallback LookupModuleOutput); @@ -154,7 +155,7 @@ class DependencyScanningTool { /// diagnostics and deletes the compiler instance. Call this method /// once all names for a same commandline are scanned. /// @return Error if an error occured during finalization. - llvm::Error finalizeCompilerInstanceWithContext(); + llvm::Error finalizeCompilerInstanceWithContextOrError(); llvm::vfs::FileSystem &getWorkerVFS() const { return Worker.getVFS(); } diff --git a/clang/lib/DependencyScanning/CMakeLists.txt b/clang/lib/DependencyScanning/CMakeLists.txt index 2976f7c236f2e..0b46765649a2a 100644 --- a/clang/lib/DependencyScanning/CMakeLists.txt +++ b/clang/lib/DependencyScanning/CMakeLists.txt @@ -18,9 +18,7 @@ add_clang_library(clangDependencyScanning ClangDriverOptions LINK_LIBS - clangAST clangBasic - clangDriver clangFrontend clangLex clangSerialization diff --git a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp index 3ca9ce140e887..257ed8473e268 100644 --- a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp +++ b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp @@ -379,42 +379,6 @@ DignosticsEngineWithDiagOpts::DignosticsEngineWithDiagOpts( /*ShouldOwnClient=*/false); } -std::pair<std::unique_ptr<driver::Driver>, std::unique_ptr<driver::Compilation>> -dependencies::buildCompilation(ArrayRef<std::string> ArgStrs, - DiagnosticsEngine &Diags, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - llvm::BumpPtrAllocator &Alloc) { - SmallVector<const char *, 256> Argv; - Argv.reserve(ArgStrs.size()); - for (const std::string &Arg : ArgStrs) - Argv.push_back(Arg.c_str()); - - std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>( - Argv[0], llvm::sys::getDefaultTargetTriple(), Diags, - "clang LLVM compiler", FS); - Driver->setTitle("clang_based_tool"); - - bool CLMode = driver::IsClangCL( - driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1))); - - if (llvm::Error E = - driver::expandResponseFiles(Argv, CLMode, Alloc, FS.get())) { - Diags.Report(diag::err_drv_expand_response_file) - << llvm::toString(std::move(E)); - return std::make_pair(nullptr, nullptr); - } - - std::unique_ptr<driver::Compilation> Compilation( - Driver->BuildCompilation(Argv)); - if (!Compilation) - return std::make_pair(nullptr, nullptr); - - if (Compilation->containsError()) - return std::make_pair(nullptr, nullptr); - - return std::make_pair(std::move(Driver), std::move(Compilation)); -} - std::unique_ptr<CompilerInvocation> dependencies::createCompilerInvocation(ArrayRef<std::string> CommandLine, DiagnosticsEngine &Diags) { @@ -430,62 +394,6 @@ dependencies::createCompilerInvocation(ArrayRef<std::string> CommandLine, return Invocation; } -std::pair<IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::vector<std::string>> -dependencies::initVFSForTUBufferScanning( - IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS, - ArrayRef<std::string> CommandLine, StringRef WorkingDirectory, - llvm::MemoryBufferRef TUBuffer) { - // Reset what might have been modified in the previous worker invocation. - BaseFS->setCurrentWorkingDirectory(WorkingDirectory); - - IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS; - auto OverlayFS = - llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS); - auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); - InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory); - auto InputPath = TUBuffer.getBufferIdentifier(); - InMemoryFS->addFile( - InputPath, 0, llvm::MemoryBuffer::getMemBufferCopy(TUBuffer.getBuffer())); - IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS; - - OverlayFS->pushOverlay(InMemoryOverlay); - ModifiedFS = OverlayFS; - std::vector<std::string> ModifiedCommandLine(CommandLine); - ModifiedCommandLine.emplace_back(InputPath); - - return std::make_pair(ModifiedFS, ModifiedCommandLine); -} - -std::pair<IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>, - std::vector<std::string>> -dependencies::initVFSForByNameScanning( - IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS, - ArrayRef<std::string> CommandLine, StringRef WorkingDirectory, - StringRef ModuleName) { - // Reset what might have been modified in the previous worker invocation. - BaseFS->setCurrentWorkingDirectory(WorkingDirectory); - - // 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. - auto OverlayFS = - llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS); - auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); - 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("")); - IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS; - OverlayFS->pushOverlay(InMemoryOverlay); - - std::vector<std::string> ModifiedCommandLine(CommandLine); - ModifiedCommandLine.emplace_back(FakeInputPath); - - return std::make_pair(OverlayFS, ModifiedCommandLine); -} - bool dependencies::initializeScanCompilerInstance( CompilerInstance &ScanInstance, IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, @@ -713,39 +621,20 @@ bool DependencyScanningAction::runInvocation( return Result; } -bool CompilerInstanceWithContext::initialize(DiagnosticConsumer *DC) { - if (DC) { - DiagConsumer = DC; - } else { - DiagPrinterWithOS = - std::make_unique<TextDiagnosticsPrinterWithOutput>(CommandLine); - DiagConsumer = &DiagPrinterWithOS->DiagPrinter; - } - - std::tie(OverlayFS, CommandLine) = initVFSForByNameScanning( - Worker.DepFS, CommandLine, CWD, "ScanningByName"); - - DiagEngineWithCmdAndOpts = std::make_unique<DignosticsEngineWithDiagOpts>( - CommandLine, OverlayFS, *DiagConsumer); +bool CompilerInstanceWithContext::initialize( + std::unique_ptr<DignosticsEngineWithDiagOpts> DiagEngineWithDiagOpts, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) { + assert(DiagEngineWithDiagOpts && "Valid diagnostics engine required!"); + DiagEngineWithCmdAndOpts = std::move(DiagEngineWithDiagOpts); + DiagConsumer = DiagEngineWithCmdAndOpts->DiagEngine->getClient(); - std::tie(Driver, Compilation) = buildCompilation( - CommandLine, *DiagEngineWithCmdAndOpts->DiagEngine, OverlayFS, Alloc); - - if (!Compilation) - return false; + // Reset what might have been modified in the previous worker invocation. + Worker.DepFS->setCurrentWorkingDirectory(CWD); - assert(Compilation->getJobs().size() && - "Must have a job list of non-zero size"); - const driver::Command &Command = *(Compilation->getJobs().begin()); - const auto &CommandArgs = Command.getArguments(); - assert(!CommandArgs.empty() && "Cannot have a command with 0 args"); - assert(StringRef(CommandArgs[0]) == "-cc1" && "Requires a cc1 job."); - OriginalInvocation = std::make_unique<CompilerInvocation>(); - - if (!CompilerInvocation::CreateFromArgs(*OriginalInvocation, CommandArgs, - *DiagEngineWithCmdAndOpts->DiagEngine, - Command.getExecutable())) { - DiagEngineWithCmdAndOpts->DiagEngine->Report( + OriginalInvocation = createCompilerInvocation( + CommandLine, *DiagEngineWithCmdAndOpts->DiagEngine); + if (!OriginalInvocation) { + this->DiagEngineWithCmdAndOpts->DiagEngine->Report( diag::err_fe_expected_compiler_job) << llvm::join(CommandLine, " "); return false; @@ -763,7 +652,7 @@ bool CompilerInstanceWithContext::initialize(DiagnosticConsumer *DC) { auto &CI = *CIPtr; if (!initializeScanCompilerInstance( - CI, OverlayFS, DiagEngineWithCmdAndOpts->DiagEngine->getClient(), + CI, FS, DiagEngineWithCmdAndOpts->DiagEngine->getClient(), Worker.Service, Worker.DepFS)) return false; @@ -815,7 +704,7 @@ bool CompilerInstanceWithContext::computeDependencies( // file. In this case, we call BeginSourceFile to initialize. std::unique_ptr<FrontendAction> Action = std::make_unique<PreprocessOnlyAction>(); - auto InputFile = CI.getFrontendOpts().Inputs.begin(); + auto *InputFile = CI.getFrontendOpts().Inputs.begin(); bool ActionBeginSucceeded = Action->BeginSourceFile(CI, *InputFile); assert(ActionBeginSucceeded && "Action BeginSourceFile must succeed"); (void)ActionBeginSucceeded; @@ -876,11 +765,3 @@ bool CompilerInstanceWithContext::finalize() { DiagConsumer->finish(); return true; } - -llvm::Error CompilerInstanceWithContext::handleReturnStatus(bool Success) { - assert(DiagPrinterWithOS && "Must use the default DiagnosticConsumer."); - return Success ? llvm::Error::success() - : llvm::make_error<llvm::StringError>( - DiagPrinterWithOS->DiagnosticsOS.str(), - llvm::inconvertibleErrorCode()); -} diff --git a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp index 333edd4862336..04fae21556d54 100644 --- a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp @@ -7,10 +7,12 @@ //===----------------------------------------------------------------------===// #include "clang/DependencyScanning/DependencyScanningWorker.h" +#include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticFrontend.h" #include "clang/DependencyScanning/DependencyScannerImpl.h" #include "clang/Driver/Driver.h" #include "clang/Driver/Tool.h" +#include "clang/Serialization/ObjectFilePCHContainerReader.h" using namespace clang; using namespace dependencies; @@ -37,39 +39,6 @@ DependencyScanningWorker::DependencyScanningWorker( DependencyScanningWorker::~DependencyScanningWorker() = default; DependencyActionController::~DependencyActionController() = default; -llvm::Error DependencyScanningWorker::computeDependencies( - StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, - DependencyConsumer &Consumer, DependencyActionController &Controller, - std::optional<llvm::MemoryBufferRef> TUBuffer) { - // Capture the emitted diagnostics and report them to the client - // in the case of a failure. - TextDiagnosticsPrinterWithOutput DiagPrinterWithOS(CommandLine); - - if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller, - DiagPrinterWithOS.DiagPrinter, TUBuffer)) - return llvm::Error::success(); - return llvm::make_error<llvm::StringError>( - DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode()); -} - -static bool forEachDriverJob( - ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - llvm::function_ref<bool(const driver::Command &Cmd)> Callback) { - // Compilation holds a non-owning a reference to the Driver, hence we need to - // keep the Driver alive when we use Compilation. Arguments to commands may be - // owned by Alloc when expanded from response files. - llvm::BumpPtrAllocator Alloc; - auto [Driver, Compilation] = buildCompilation(ArgStrs, Diags, FS, Alloc); - if (!Compilation) - return false; - for (const driver::Command &Job : Compilation->getJobs()) { - if (!Callback(Job)) - return false; - } - return true; -} - static bool createAndRunToolInvocation( const std::vector<std::string> &CommandLine, DependencyScanningAction &Action, @@ -86,114 +55,77 @@ static bool createAndRunToolInvocation( } bool DependencyScanningWorker::scanDependencies( - StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, + StringRef WorkingDirectory, ArrayRef<std::vector<std::string>> CommandLines, DependencyConsumer &Consumer, DependencyActionController &Controller, - DiagnosticConsumer &DC, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayFS) { - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = DepFS; - if (OverlayFS) { + DiagnosticsEngine &Diags, + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) { #ifndef NDEBUG - bool SawDepFS = false; - OverlayFS->visit( - [&](llvm::vfs::FileSystem &VFS) { SawDepFS |= &VFS == DepFS.get(); }); - assert(SawDepFS && "OverlayFS not based on DepFS"); + bool SawDepFS = false; + FS->visit( + [&](llvm::vfs::FileSystem &VFS) { SawDepFS |= &VFS == DepFS.get(); }); + assert(SawDepFS && "FS not based on DepFS"); #endif - FS = std::move(OverlayFS); - } - - DignosticsEngineWithDiagOpts DiagEngineWithCmdAndOpts(CommandLine, FS, DC); DependencyScanningAction Action(Service, WorkingDirectory, Consumer, Controller, DepFS); - bool Success = false; - if (CommandLine[1] == "-cc1") { - Success = - createAndRunToolInvocation(CommandLine, Action, FS, PCHContainerOps, - *DiagEngineWithCmdAndOpts.DiagEngine); - } else { - Success = forEachDriverJob( - CommandLine, *DiagEngineWithCmdAndOpts.DiagEngine, FS, - [&](const driver::Command &Cmd) { - if (StringRef(Cmd.getCreator().getName()) != "clang") { - // Non-clang command. Just pass through to the dependency - // consumer. - Consumer.handleBuildCommand( - {Cmd.getExecutable(), - {Cmd.getArguments().begin(), Cmd.getArguments().end()}}); - return true; - } - - // Insert -cc1 command line options into Argv - std::vector<std::string> Argv; - Argv.push_back(Cmd.getExecutable()); - llvm::append_range(Argv, Cmd.getArguments()); - - // Create an invocation 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. - return createAndRunToolInvocation( - std::move(Argv), Action, FS, PCHContainerOps, - *DiagEngineWithCmdAndOpts.DiagEngine); - }); - } - - if (Success && !Action.hasScanned()) - DiagEngineWithCmdAndOpts.DiagEngine->Report( - diag::err_fe_expected_compiler_job) - << llvm::join(CommandLine, " "); + const bool Success = llvm::all_of(CommandLines, [&](const auto &Cmd) { + if (StringRef(Cmd[1]) != "-cc1") { + // Non-clang command. Just pass through to the dependency consumer. + Consumer.handleBuildCommand({Cmd.front(), {Cmd.begin() + 1, Cmd.end()}}); + return true; + } + // Create an invocation 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. + return createAndRunToolInvocation(Cmd, Action, FS, PCHContainerOps, Diags); + }); // Ensure finish() is called even if we never reached ExecuteAction(). if (!Action.hasDiagConsumerFinished()) - DC.finish(); + Diags.getClient()->finish(); return Success && Action.hasScanned(); } bool DependencyScanningWorker::computeDependencies( StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, - DependencyConsumer &Consumer, DependencyActionController &Controller, - DiagnosticConsumer &DC, std::optional<llvm::MemoryBufferRef> TUBuffer) { - if (TUBuffer) { - auto [FinalFS, FinalCommandLine] = initVFSForTUBufferScanning( - DepFS, CommandLine, WorkingDirectory, *TUBuffer); - return scanDependencies(WorkingDirectory, FinalCommandLine, Consumer, - Controller, DC, FinalFS); - } else { - DepFS->setCurrentWorkingDirectory(WorkingDirectory); - return scanDependencies(WorkingDirectory, CommandLine, Consumer, Controller, - DC); - } -} - -llvm::Error -DependencyScanningWorker::initializeCompilerInstanceWithContextOrError( - StringRef CWD, const std::vector<std::string> &CommandLine) { - bool Success = initializeCompilerInstanceWithContext(CWD, CommandLine); - return CIWithContext->handleReturnStatus(Success); + DependencyConsumer &DepConsumer, DependencyActionController &Controller, + DiagnosticConsumer &DiagConsumer, + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ScanFS) { + auto FinalFS = ScanFS == nullptr ? DepFS : ScanFS; + DignosticsEngineWithDiagOpts DiagEngineWithDiagOpts(CommandLine, FinalFS, + DiagConsumer); + return computeDependencies( + WorkingDirectory, ArrayRef<std::vector<std::string>>{CommandLine}, + DepConsumer, Controller, *DiagEngineWithDiagOpts.DiagEngine, ScanFS); } -llvm::Error -DependencyScanningWorker::computeDependenciesByNameWithContextOrError( - StringRef ModuleName, DependencyConsumer &Consumer, - DependencyActionController &Controller) { - bool Success = - computeDependenciesByNameWithContext(ModuleName, Consumer, Controller); - return CIWithContext->handleReturnStatus(Success); +bool DependencyScanningWorker::computeDependencies( + StringRef WorkingDirectory, ArrayRef<std::vector<std::string>> CommandLines, + DependencyConsumer &DepConsumer, DependencyActionController &Controller, + DiagnosticsEngine &Diags, + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ScanFS) { + auto FinalFS = ScanFS == nullptr ? DepFS : ScanFS; + return scanDependencies(WorkingDirectory, CommandLines, DepConsumer, + Controller, Diags, FinalFS); } -llvm::Error -DependencyScanningWorker::finalizeCompilerInstanceWithContextOrError() { - bool Success = finalizeCompilerInstance(); - return CIWithContext->handleReturnStatus(Success); +bool DependencyScanningWorker::initializeCompilerInstanceWithContext( + StringRef CWD, ArrayRef<std::string> CommandLine, DiagnosticConsumer &DC) { + auto DiagEngineWithCmdAndOpts = + std::make_unique<DignosticsEngineWithDiagOpts>(CommandLine, DepFS, DC); + return initializeCompilerInstanceWithContext( + CWD, CommandLine, std::move(DiagEngineWithCmdAndOpts), DepFS); } bool DependencyScanningWorker::initializeCompilerInstanceWithContext( - StringRef CWD, const std::vector<std::string> &CommandLine, - DiagnosticConsumer *DC) { + StringRef CWD, ArrayRef<std::string> CommandLine, + std::unique_ptr<DignosticsEngineWithDiagOpts> DiagEngineWithDiagOpts, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) { CIWithContext = std::make_unique<CompilerInstanceWithContext>(*this, CWD, CommandLine); - return CIWithContext->initialize(DC); + return CIWithContext->initialize(std::move(DiagEngineWithDiagOpts), FS); } bool DependencyScanningWorker::computeDependenciesByNameWithContext( @@ -203,6 +135,6 @@ bool DependencyScanningWorker::computeDependenciesByNameWithContext( return CIWithContext->computeDependencies(ModuleName, Consumer, Controller); } -bool DependencyScanningWorker::finalizeCompilerInstance() { +bool DependencyScanningWorker::finalizeCompilerInstanceWithContext() { return CIWithContext->finalize(); } diff --git a/clang/lib/Tooling/CMakeLists.txt b/clang/lib/Tooling/CMakeLists.txt index 0972ecb08437f..e39124e105cf3 100644 --- a/clang/lib/Tooling/CMakeLists.txt +++ b/clang/lib/Tooling/CMakeLists.txt @@ -41,6 +41,7 @@ add_clang_library(clangTooling clangBasic clangDependencyScanning clangDriver + clangDependencyScanning clangOptions clangFormat clangFrontend diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp index e037420f4fcf2..6cfa15d0cbe77 100644 --- a/clang/lib/Tooling/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanningTool.cpp @@ -7,7 +7,13 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/DependencyScanningTool.h" +#include "clang/Basic/DiagnosticFrontend.h" +#include "clang/DependencyScanning/DependencyScannerImpl.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Tool.h" #include "clang/Frontend/Utils.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/TargetParser/Host.h" #include <optional> using namespace clang; @@ -72,13 +78,183 @@ class MakeDependencyPrinterConsumer : public DependencyConsumer { }; } // anonymous namespace +static std::pair<std::unique_ptr<driver::Driver>, + std::unique_ptr<driver::Compilation>> +buildCompilation(ArrayRef<std::string> CommandLine, DiagnosticsEngine &Diags, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, + llvm::BumpPtrAllocator &Alloc) { + SmallVector<const char *, 256> Argv; + Argv.reserve(CommandLine.size()); + for (const std::string &Arg : CommandLine) + Argv.push_back(Arg.c_str()); + + std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>( + Argv[0], llvm::sys::getDefaultTargetTriple(), Diags, + "clang LLVM compiler", FS); + Driver->setTitle("clang_based_tool"); + + bool CLMode = driver::IsClangCL( + driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1))); + + if (llvm::Error E = + driver::expandResponseFiles(Argv, CLMode, Alloc, FS.get())) { + Diags.Report(diag::err_drv_expand_response_file) + << llvm::toString(std::move(E)); + return std::make_pair(nullptr, nullptr); + } + + std::unique_ptr<driver::Compilation> Compilation( + Driver->BuildCompilation(Argv)); + if (!Compilation) + return std::make_pair(nullptr, nullptr); + + if (Compilation->containsError()) + return std::make_pair(nullptr, nullptr); + + if (Compilation->getJobs().empty()) { + Diags.Report(diag::err_fe_expected_compiler_job) + << llvm::join(CommandLine, " "); + return std::make_pair(nullptr, nullptr); + } + + return std::make_pair(std::move(Driver), std::move(Compilation)); +} + +/// Constructs the full -cc1 command line, including executable, for the given +/// driver \c Cmd. +static std::vector<std::string> +buildCC1CommandLine(const driver::Command &Cmd) { + const auto &Args = Cmd.getArguments(); + std::vector<std::string> Out; + Out.reserve(Args.size() + 1); + Out.emplace_back(Cmd.getExecutable()); + llvm::append_range(Out, Args); + return Out; +} + +static std::pair<IntrusiveRefCntPtr<llvm::vfs::FileSystem>, + std::vector<std::string>> +initVFSForTUBufferScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS, + ArrayRef<std::string> CommandLine, + StringRef WorkingDirectory, + llvm::MemoryBufferRef TUBuffer) { + // Reset what might have been modified in the previous worker invocation. + BaseFS->setCurrentWorkingDirectory(WorkingDirectory); + + IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS; + auto OverlayFS = + llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS); + auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); + InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory); + auto InputPath = TUBuffer.getBufferIdentifier(); + InMemoryFS->addFile( + InputPath, 0, llvm::MemoryBuffer::getMemBufferCopy(TUBuffer.getBuffer())); + IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS; + + OverlayFS->pushOverlay(InMemoryOverlay); + std::vector<std::string> ModifiedCommandLine(CommandLine); + ModifiedCommandLine.emplace_back(InputPath); + + return std::make_pair(OverlayFS, ModifiedCommandLine); +} + +static std::pair<IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>, + std::vector<std::string>> +initVFSForByNameScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS, + ArrayRef<std::string> CommandLine, + StringRef WorkingDirectory, StringRef ModuleName) { + // 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. + auto OverlayFS = + llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS); + // Reset what might have been modified in the previous worker invocation. + OverlayFS->setCurrentWorkingDirectory(WorkingDirectory); + auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); + 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("")); + OverlayFS->pushOverlay(InMemoryFS); + + std::vector<std::string> ModifiedCommandLine(CommandLine); + ModifiedCommandLine.emplace_back(FakeInputPath); + + return std::make_pair(OverlayFS, ModifiedCommandLine); +} + +static llvm::Error makeErrorFromDiagnosticsOS( + TextDiagnosticsPrinterWithOutput &DiagPrinterWithOS) { + return llvm::make_error<llvm::StringError>( + DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode()); +} + +static bool computeDependenciesForDriverCommandLine( + DependencyScanningWorker &Worker, StringRef WorkingDirectory, + ArrayRef<std::string> CommandLine, DependencyConsumer &Consumer, + DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> ScanFS) { + Worker.getVFS().setCurrentWorkingDirectory(WorkingDirectory); + + DignosticsEngineWithDiagOpts DiagEngineWithDiagOpts( + CommandLine, &Worker.getVFS(), DiagConsumer); + auto &Diags = *DiagEngineWithDiagOpts.DiagEngine; + + // Compilation holds a non-owning a reference to the Driver, hence we need to + // keep the Driver alive when we use Compilation. Arguments to commands may be + // owned by Alloc when expanded from response files. + llvm::BumpPtrAllocator Alloc; + const auto [Driver, Compilation] = buildCompilation( + CommandLine, *DiagEngineWithDiagOpts.DiagEngine, &Worker.getVFS(), Alloc); + if (!Compilation) + return false; + + const auto CC1Commands = llvm::to_vector( + llvm::map_range(Compilation->getJobs(), buildCC1CommandLine)); + + return Worker.computeDependencies(WorkingDirectory, CC1Commands, Consumer, + Controller, Diags, ScanFS); +} + +static llvm::Error computeDependenciesOrError( + DependencyScanningWorker &Worker, StringRef WorkingDirectory, + ArrayRef<std::string> CommandLine, DependencyConsumer &Consumer, + DependencyActionController &Controller, + std::optional<llvm::MemoryBufferRef> TUBuffer = std::nullopt) { + auto [OverlayFS, FinalCommandLine] = [&]() { + if (TUBuffer) + return initVFSForTUBufferScanning(&Worker.getVFS(), CommandLine, + WorkingDirectory, *TUBuffer); + return std::make_pair( + IntrusiveRefCntPtr<llvm::vfs::FileSystem>(&Worker.getVFS()), + std::vector<std::string>(CommandLine.begin(), CommandLine.end())); + }(); + + TextDiagnosticsPrinterWithOutput DiagPrinterWithOS(CommandLine); + + const auto IsCC1Input = (FinalCommandLine[1] == "-cc1"); + const auto Success = + IsCC1Input + ? Worker.computeDependencies(WorkingDirectory, FinalCommandLine, + Consumer, Controller, + DiagPrinterWithOS.DiagPrinter, OverlayFS) + : computeDependenciesForDriverCommandLine( + Worker, WorkingDirectory, FinalCommandLine, Consumer, + Controller, DiagPrinterWithOS.DiagPrinter, OverlayFS); + + if (!Success) + return makeErrorFromDiagnosticsOS(DiagPrinterWithOS); + return llvm::Error::success(); +} + llvm::Expected<std::string> DependencyScanningTool::getDependencyFile( const std::vector<std::string> &CommandLine, StringRef CWD) { MakeDependencyPrinterConsumer Consumer; CallbackActionController Controller(nullptr); - auto Result = - Worker.computeDependencies(CWD, CommandLine, Consumer, Controller); - if (Result) + if (auto Result = computeDependenciesOrError(Worker, CWD, CommandLine, + Consumer, Controller)) return std::move(Result); std::string Output; Consumer.printDependencies(Output); @@ -129,9 +305,8 @@ llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile( P1689Rule Rule; P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command); P1689ActionController Controller; - auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer, - Controller); - if (Result) + if (auto Result = computeDependenciesOrError(Worker, CWD, Command.CommandLine, + Consumer, Controller)) return std::move(Result); MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath(); @@ -148,10 +323,8 @@ DependencyScanningTool::getTranslationUnitDependencies( std::optional<llvm::MemoryBufferRef> TUBuffer) { FullDependencyConsumer Consumer(AlreadySeen); CallbackActionController Controller(LookupModuleOutput); - llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer, - Controller, TUBuffer); - - if (Result) + if (auto Result = computeDependenciesOrError(Worker, CWD, CommandLine, + Consumer, Controller, TUBuffer)) return std::move(Result); return Consumer.takeTranslationUnitDeps(); } @@ -161,43 +334,92 @@ DependencyScanningTool::getModuleDependencies( StringRef ModuleName, const std::vector<std::string> &CommandLine, StringRef CWD, const llvm::DenseSet<ModuleID> &AlreadySeen, LookupModuleOutputCallback LookupModuleOutput) { - FullDependencyConsumer Consumer(AlreadySeen); - CallbackActionController Controller(LookupModuleOutput); if (auto Error = - Worker.initializeCompilerInstanceWithContextOrError(CWD, CommandLine)) - return std::move(Error); + initializeCompilerInstanceWithContextOrError(CWD, CommandLine)) + return Error; - auto Result = Worker.computeDependenciesByNameWithContextOrError( - ModuleName, Consumer, Controller); + auto Result = computeDependenciesByNameWithContextOrError( + ModuleName, AlreadySeen, LookupModuleOutput); - if (auto Error = Worker.finalizeCompilerInstanceWithContextOrError()) - return std::move(Error); + if (auto Error = finalizeCompilerInstanceWithContextOrError()) + return Error; - if (Result) - return std::move(Result); + return Result; +} - return Consumer.takeTranslationUnitDeps(); +static std::optional<std::vector<std::string>> getFirstCC1CommandLine( + ArrayRef<std::string> CommandLine, DiagnosticsEngine &Diags, + llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> ScanFS) { + // Compilation holds a non-owning a reference to the Driver, hence we need to + // keep the Driver alive when we use Compilation. Arguments to commands may be + // owned by Alloc when expanded from response files. + llvm::BumpPtrAllocator Alloc; + const auto [Driver, Compilation] = + buildCompilation(CommandLine, Diags, ScanFS, Alloc); + if (!Compilation) + return std::nullopt; + + const auto IsClangCmd = [](const driver::Command &Cmd) { + return StringRef(Cmd.getCreator().getName()) == "clang"; + }; + const auto CC1CommandLineRange = llvm::map_range( + llvm::make_filter_range(Compilation->getJobs(), IsClangCmd), + buildCC1CommandLine); + + if (CC1CommandLineRange.empty()) + return std::nullopt; + return *CC1CommandLineRange.begin(); } -llvm::Error DependencyScanningTool::initializeCompilerInstanceWithContext( - StringRef CWD, const std::vector<std::string> &CommandLine) { - return Worker.initializeCompilerInstanceWithContextOrError(CWD, CommandLine); +llvm::Error +DependencyScanningTool::initializeCompilerInstanceWithContextOrError( + StringRef CWD, ArrayRef<std::string> CommandLine) { + // For by name scanning, we allow command lines without an actual input file + // by adding an in-memory placeholder input. + auto OverlayFSAndArgs = initVFSForByNameScanning( + &Worker.getVFS(), CommandLine, CWD, "ScanningByName"); + auto &OverlayFS = OverlayFSAndArgs.first; + const auto &ModifiedCommandLine = OverlayFSAndArgs.second; + + DiagPrinterWithOS = + std::make_unique<TextDiagnosticsPrinterWithOutput>(CommandLine); + auto DiagEngineWithCmdAndOpts = + std::make_unique<DignosticsEngineWithDiagOpts>( + CommandLine, OverlayFS, DiagPrinterWithOS->DiagPrinter); + + const auto InitWithCommandLine = + [&](ArrayRef<std::string> CommandLine) -> llvm::Error { + if (Worker.initializeCompilerInstanceWithContext( + CWD, CommandLine, std::move(DiagEngineWithCmdAndOpts), OverlayFS)) + return llvm::Error::success(); + return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS); + }; + + if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") + return InitWithCommandLine(CommandLine); + + const auto MaybeFirstCC1 = getFirstCC1CommandLine( + ModifiedCommandLine, *DiagEngineWithCmdAndOpts->DiagEngine, OverlayFS); + if (!MaybeFirstCC1) + return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS); + return InitWithCommandLine(*MaybeFirstCC1); } llvm::Expected<TranslationUnitDeps> -DependencyScanningTool::computeDependenciesByNameWithContext( +DependencyScanningTool::computeDependenciesByNameWithContextOrError( StringRef ModuleName, const llvm::DenseSet<ModuleID> &AlreadySeen, LookupModuleOutputCallback LookupModuleOutput) { FullDependencyConsumer Consumer(AlreadySeen); CallbackActionController Controller(LookupModuleOutput); - llvm::Error Result = Worker.computeDependenciesByNameWithContextOrError( - ModuleName, Consumer, Controller); - if (Result) - return std::move(Result); - - return Consumer.takeTranslationUnitDeps(); + if (Worker.computeDependenciesByNameWithContext(ModuleName, Consumer, + Controller)) + return Consumer.takeTranslationUnitDeps(); + return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS); } -llvm::Error DependencyScanningTool::finalizeCompilerInstanceWithContext() { - return Worker.finalizeCompilerInstanceWithContextOrError(); +llvm::Error +DependencyScanningTool::finalizeCompilerInstanceWithContextOrError() { + if (Worker.finalizeCompilerInstanceWithContext()) + return llvm::Error::success(); + return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS); } diff --git a/clang/test/ClangScanDeps/modules-full-by-mod-name.c b/clang/test/ClangScanDeps/modules-full-by-mod-name.c index edb99636aaf25..a2a2d3c22f9dd 100644 --- a/clang/test/ClangScanDeps/modules-full-by-mod-name.c +++ b/clang/test/ClangScanDeps/modules-full-by-mod-name.c @@ -17,17 +17,53 @@ module transitive { header "transitive.h" } // This is here to verify that the "root" directory doesn't clash with name of // the "root" module. +//--- main.cpp +// empty + //--- cdb.json.template [{ "file": "", "directory": "DIR", - "command": "clang -fmodules -fmodules-cache-path=DIR/cache -I DIR -x c" + "command": "clang -fmodules -fmodules-cache-path=DIR/cache -I DIR -x c main.cpp" }] // 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 -module-names=root > %t/result.json // RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s +//--- cdb.no-input.json.template +[{ + "file": "", + "directory": "DIR", + "command": "clang -fmodules -fmodules-cache-path=DIR/cache -I DIR -x c" +}] + +// RUN: sed "s|DIR|%/t|g" %t/cdb.no-input.json.template > %t/cdb.no-input.json +// RUN: clang-scan-deps -compilation-database %t/cdb.no-input.json -format experimental-full -module-names=root > %t/result.no-input.json +// RUN: cat %t/result.no-input.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s + +//--- cdb.cc1.json.template +[{ + "file": "", + "directory": "DIR", + "command": "clang -cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -I DIR -x c main.cpp" +}] + +// RUN: sed "s|DIR|%/t|g" %t/cdb.cc1.json.template > %t/cdb.cc1.json +// RUN: clang-scan-deps -compilation-database %t/cdb.cc1.json -format experimental-full -module-names=root > %t/result.cc1.json +// RUN: cat %t/result.cc1.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s + +//--- cdb.cc1.no-input.json.template +[{ + "file": "", + "directory": "DIR", + "command": "clang -cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -I DIR -x c" +}] + +// RUN: sed "s|DIR|%/t|g" %t/cdb.cc1.no-input.json.template > %t/cdb.cc1.no-input.json +// RUN: clang-scan-deps -compilation-database %t/cdb.cc1.no-input.json -format experimental-full -module-names=root > %t/result.cc1.no-input.json +// RUN: cat %t/result.cc1.no-input.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s + // CHECK: { // CHECK-NEXT: "modules": [ // CHECK-NEXT: { diff --git a/clang/test/ClangScanDeps/modules-full-by-mult-mod-names.c b/clang/test/ClangScanDeps/modules-full-by-mult-mod-names.c index 030f7f3427810..3b7074219db84 100644 --- a/clang/test/ClangScanDeps/modules-full-by-mult-mod-names.c +++ b/clang/test/ClangScanDeps/modules-full-by-mult-mod-names.c @@ -22,17 +22,52 @@ module root1 { header "root1.h"} // This is here to verify that the "root" directory doesn't clash with name of // the "root" module. +//--- main.cpp + //--- cdb.json.template [{ "file": "", "directory": "DIR", - "command": "clang -fmodules -fmodules-cache-path=DIR/cache -I DIR -x c" + "command": "clang -fmodules -fmodules-cache-path=DIR/cache -I DIR -x c main.cpp" }] // 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 -module-names=root,root1,direct > %t/result.json // RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s +//--- cdb.no-input.json.template +[{ + "file": "", + "directory": "DIR", + "command": "clang -fmodules -fmodules-cache-path=DIR/cache -I DIR -x c" +}] + +// RUN: sed "s|DIR|%/t|g" %t/cdb.no-input.json.template > %t/cdb.no-input.json +// RUN: clang-scan-deps -compilation-database %t/cdb.no-input.json -format experimental-full -module-names=root,root1,direct > %t/result.no-input.json +// RUN: cat %t/result.no-input.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s + +//--- cdb.cc1.json.template +[{ + "file": "", + "directory": "DIR", + "command": "clang -cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -I DIR -x c main.cpp" +}] + +// RUN: sed "s|DIR|%/t|g" %t/cdb.cc1.json.template > %t/cdb.cc1.json +// RUN: clang-scan-deps -compilation-database %t/cdb.cc1.json -format experimental-full -module-names=root,root1,direct > %t/result.cc1.json +// RUN: cat %t/result.cc1.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s + +//--- cdb.cc1.no-input.json.template +[{ + "file": "", + "directory": "DIR", + "command": "clang -cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -I DIR -x c" +}] + +// RUN: sed "s|DIR|%/t|g" %t/cdb.cc1.no-input.json.template > %t/cdb.cc1.no-input.json +// RUN: clang-scan-deps -compilation-database %t/cdb.cc1.no-input.json -format experimental-full -module-names=root,root1,direct > %t/result.cc1.no-input.json +// RUN: cat %t/result.cc1.no-input.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s + // CHECK: { // CHECK-NEXT: "modules": [ // CHECK-NEXT: { diff --git a/clang/test/ClangScanDeps/tu-buffer.c b/clang/test/ClangScanDeps/tu-buffer.c index b450b13ff434b..ce3b037174c26 100644 --- a/clang/test/ClangScanDeps/tu-buffer.c +++ b/clang/test/ClangScanDeps/tu-buffer.c @@ -37,6 +37,17 @@ module addition { header "addition.h" } // RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full -tu-buffer-path %t/tu.c > %t/result.json // RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s --check-prefix=CHECK +//--- cdb.cc1.json.template +[{ + "file": "", + "directory": "DIR", + "command": "clang -cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -I DIR -x c -emit-obj" +}] + +// RUN: sed "s|DIR|%/t|g" %t/cdb.cc1.json.template > %t/cdb.cc1.json +// RUN: clang-scan-deps -compilation-database %t/cdb.cc1.json -format experimental-full -tu-buffer-path %t/tu.c > %t/result.cc1.json +// RUN: cat %t/result.cc1.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s --check-prefix=CHECK + // CHECK: { // CHECK-NEXT: "modules": [ // CHECK-NEXT: { diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp index 6a2acb0d4f20e..cc54614d04299 100644 --- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -1106,7 +1106,7 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) { HadErrors = true; } else { if (llvm::Error Err = - WorkerTool.initializeCompilerInstanceWithContext( + WorkerTool.initializeCompilerInstanceWithContextOrError( CWD, Input->CommandLine)) { handleErrorWithInfoString( "Compiler instance with context setup error", std::move(Err), @@ -1117,7 +1117,7 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) { for (auto N : Names) { auto MaybeModuleDepsGraph = - WorkerTool.computeDependenciesByNameWithContext( + WorkerTool.computeDependenciesByNameWithContextOrError( N, AlreadySeenModules, LookupOutput); if (handleModuleResult(N, MaybeModuleDepsGraph, *FD, LocalIndex, DependencyOS, Errs)) { @@ -1127,7 +1127,7 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) { } if (llvm::Error Err = - WorkerTool.finalizeCompilerInstanceWithContext()) { + WorkerTool.finalizeCompilerInstanceWithContextOrError()) { handleErrorWithInfoString( "Compiler instance with context finialization error", std::move(Err), DependencyOS, Errs); diff --git a/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp b/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp index e6a5684b10cc9..ca51d5ea982d9 100644 --- a/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp +++ b/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp @@ -46,13 +46,9 @@ TEST(DependencyScanner, ScanDepsWithDiagConsumer) { { // Check that a successful scan calls DiagConsumer.finish(). - std::vector<std::string> Args = {"clang", - "-target", - "x86_64-apple-macosx10.7", - "-c", - "test.cpp", - "-o" - "test.cpp.o"}; + std::vector<std::string> Args = { + "clang", "-cc1", "-triple", "x86_64-apple-macosx10.7.0", + "-emit-obj", "test.cpp", "-o", "test.cpp.o"}; EnsureFinishedConsumer DiagConsumer; bool Success = Worker.computeDependencies(CWD, Args, DC, AC, DiagConsumer); @@ -65,7 +61,7 @@ TEST(DependencyScanner, ScanDepsWithDiagConsumer) { { // Check that an invalid command-line, which never enters the scanning // action calls DiagConsumer.finish(). - std::vector<std::string> Args = {"clang", "-invalid-arg"}; + std::vector<std::string> Args = {"clang", "-cc1", "-invalid-arg"}; EnsureFinishedConsumer DiagConsumer; bool Success = Worker.computeDependencies(CWD, Args, DC, AC, DiagConsumer); @@ -77,15 +73,10 @@ TEST(DependencyScanner, ScanDepsWithDiagConsumer) { { // Check that a valid command line that produces no scanning jobs calls // DiagConsumer.finish(). - std::vector<std::string> Args = {"clang", - "-target", - "x86_64-apple-macosx10.7", - "-c", - "-x", - "assembler", - "test.s", - "-o" - "test.cpp.o"}; + std::vector<std::string> Args = { + "clang", "-cc1", "-triple", "x86_64-apple-macosx10.7.0", + "-emit-obj", "-x", "assembler", "test.s", + "-o", "test.cpp.o"}; EnsureFinishedConsumer DiagConsumer; bool Success = Worker.computeDependencies(CWD, Args, DC, AC, DiagConsumer); >From b0a4102da285761c09e3e5676e0640ff6676f82a Mon Sep 17 00:00:00 2001 From: Naveen Seth Hanig <[email protected]> Date: Thu, 4 Dec 2025 02:13:56 +0100 Subject: [PATCH 2/2] Fix small doxygen comment issue caused during rebase --- .../include/clang/DependencyScanning/DependencyScanningWorker.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h index e3564dfbe881c..862e2f51890db 100644 --- a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h @@ -96,7 +96,7 @@ class DependencyScanningWorker { /// Run the dependency scanning tool for a given clang -cc1 command-line, /// and report the discovered dependencies to the provided consumer. /// - /// @return false if clang errors occurred (with diagnostics reported to + /// \return false if clang errors occurred (with diagnostics reported to /// \c DiagConsumer), true otherwise. bool computeDependencies( StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
