From c7a6ab6f0a1b4693bf2be35c2a147a8d3fb4443e Mon Sep 17 00:00:00 2001 From: Naveen Seth Hanig Date: Mon, 15 Dec 2025 19:23:52 +0100 Subject: [PATCH 01/10] [clang][DependencyScanning] Remove dependency on clangDriver from clangDependencyScanning This is the final patch in a series that removes the dependency of clangDependencyScanning on clangDriver, splitting the work from #169964 into smaller changes (see comment linked below). This patch updates the remaining parts of the scanning interface in DependencyScanningWorker to only operate on -cc1 / driver job command lines. This follows #171238, which applied this change to the by-name scanning API. 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. --- .../DependencyScannerImpl.h | 18 -- .../DependencyScanningWorker.h | 58 ++++-- clang/lib/DependencyScanning/CMakeLists.txt | 1 - .../DependencyScannerImpl.cpp | 91 --------- .../DependencyScanningWorker.cpp | 181 +++++++++--------- clang/lib/Tooling/DependencyScanningTool.cpp | 153 ++++++++++++--- .../DependencyScanningWorkerTest.cpp | 12 +- 7 files changed, 271 insertions(+), 243 deletions(-) diff --git a/clang/include/clang/DependencyScanning/DependencyScannerImpl.h b/clang/include/clang/DependencyScanning/DependencyScannerImpl.h index f43c7f70183fd..231873375a264 100644 --- a/clang/include/clang/DependencyScanning/DependencyScannerImpl.h +++ b/clang/include/clang/DependencyScanning/DependencyScannerImpl.h @@ -89,28 +89,10 @@ struct TextDiagnosticsPrinterWithOutput { DiagPrinter(DiagnosticsOS, *DiagOpts) {} }; -std::pair, std::unique_ptr> -buildCompilation(ArrayRef ArgStrs, DiagnosticsEngine &Diags, - IntrusiveRefCntPtr FS, - llvm::BumpPtrAllocator &Alloc); - std::unique_ptr createCompilerInvocation(ArrayRef CommandLine, DiagnosticsEngine &Diags); -std::pair, - std::vector> -initVFSForTUBufferScanning(IntrusiveRefCntPtr BaseFS, - ArrayRef CommandLine, - StringRef WorkingDirectory, - llvm::MemoryBufferRef TUBuffer); - -std::pair, - std::vector> -initVFSForByNameScanning(IntrusiveRefCntPtr BaseFS, - ArrayRef CommandLine, - StringRef WorkingDirectory, StringRef ModuleName); - bool initializeScanCompilerInstance( CompilerInstance &ScanInstance, IntrusiveRefCntPtr FS, diff --git a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h index 489fba4ed3f6b..35f289cf12eb0 100644 --- a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h @@ -16,7 +16,7 @@ #include "clang/DependencyScanning/DependencyScanningService.h" #include "clang/DependencyScanning/ModuleDepCollector.h" #include "clang/Frontend/PCHContainerOperations.h" -#include "llvm/Support/Error.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBufferRef.h" #include "llvm/Support/VirtualFileSystem.h" @@ -93,11 +93,13 @@ 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 the given driver job command-line, + /// and report the discovered dependencies to the provided consumer. + /// + /// OverlayFS should be based on the Worker's dependency scanning file-system + /// and can be used to provide any input specified on the command-line as + /// in-memory file. If no overlay file-system is provided, the Worker's + /// dependency scanning file-system is used instead. /// /// \returns false if clang errors occurred (with diagnostics reported to /// \c DiagConsumer), true otherwise. @@ -105,17 +107,25 @@ class DependencyScanningWorker { StringRef WorkingDirectory, ArrayRef CommandLine, DependencyConsumer &DepConsumer, DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer, - std::optional TUBuffer = std::nullopt); + llvm::IntrusiveRefCntPtr OverlayFS = + 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 driver job 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, ArrayRef CommandLine, - DependencyConsumer &Consumer, DependencyActionController &Controller, - std::optional TUBuffer = std::nullopt); + /// OverlayFS should be based on the Worker's dependency scanning file-system + /// and can be used to provide any input specified on the command-line as + /// in-memory file. If no overlay file-system is provided, the Worker's + /// dependency scanning file-system is used instead. + /// + /// \returns false if clang errors occurred (with diagnostics reported to + /// \c Diags), true otherwise. + bool computeDependencies( + StringRef WorkingDirectory, ArrayRef> CommandLines, + DependencyConsumer &DepConsumer, DependencyActionController &Controller, + DiagnosticsEngine &Diags, + llvm::IntrusiveRefCntPtr OverlayFS = + nullptr); /// The three method below implements a new interface for by name /// dependency scanning. They together enable the dependency scanning worker @@ -176,12 +186,26 @@ class DependencyScanningWorker { /// Actually carries out the scan. If \c OverlayFS is provided, it must be /// based on top of DepFS. bool scanDependencies( - StringRef WorkingDirectory, ArrayRef CommandLine, + StringRef WorkingDirectory, + ArrayRef> CC1CommandLines, DependencyConsumer &Consumer, DependencyActionController &Controller, - DiagnosticConsumer &DC, + DiagnosticsEngine &Diags, IntrusiveRefCntPtr OverlayFS = nullptr); }; +std::pair, + std::vector> +initVFSForTUBufferScanning(IntrusiveRefCntPtr BaseFS, + ArrayRef CommandLine, + StringRef WorkingDirectory, + llvm::MemoryBufferRef TUBuffer); + +std::pair, + std::vector> +initVFSForByNameScanning(IntrusiveRefCntPtr BaseFS, + ArrayRef CommandLine, + StringRef WorkingDirectory, StringRef ModuleName); + } // end namespace dependencies } // end namespace clang diff --git a/clang/lib/DependencyScanning/CMakeLists.txt b/clang/lib/DependencyScanning/CMakeLists.txt index 2976f7c236f2e..4c30c3ee57cfd 100644 --- a/clang/lib/DependencyScanning/CMakeLists.txt +++ b/clang/lib/DependencyScanning/CMakeLists.txt @@ -20,7 +20,6 @@ add_clang_library(clangDependencyScanning LINK_LIBS clangAST clangBasic - clangDriver clangFrontend clangLex clangSerialization diff --git a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp index 201230d7d6a8e..7fb214eb2e630 100644 --- a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp +++ b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp @@ -379,42 +379,6 @@ DiagnosticsEngineWithDiagOpts::DiagnosticsEngineWithDiagOpts( /*ShouldOwnClient=*/false); } -std::pair, std::unique_ptr> -dependencies::buildCompilation(ArrayRef ArgStrs, - DiagnosticsEngine &Diags, - IntrusiveRefCntPtr FS, - llvm::BumpPtrAllocator &Alloc) { - SmallVector Argv; - Argv.reserve(ArgStrs.size()); - for (const std::string &Arg : ArgStrs) - Argv.push_back(Arg.c_str()); - - std::unique_ptr Driver = std::make_unique( - 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 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 dependencies::createCompilerInvocation(ArrayRef CommandLine, DiagnosticsEngine &Diags) { @@ -430,61 +394,6 @@ dependencies::createCompilerInvocation(ArrayRef CommandLine, return Invocation; } -std::pair, - std::vector> -dependencies::initVFSForTUBufferScanning( - IntrusiveRefCntPtr BaseFS, - ArrayRef CommandLine, StringRef WorkingDirectory, - llvm::MemoryBufferRef TUBuffer) { - // Reset what might have been modified in the previous worker invocation. - BaseFS->setCurrentWorkingDirectory(WorkingDirectory); - - auto OverlayFS = - llvm::makeIntrusiveRefCnt(BaseFS); - auto InMemoryFS = llvm::makeIntrusiveRefCnt(); - InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory); - auto InputPath = TUBuffer.getBufferIdentifier(); - InMemoryFS->addFile( - InputPath, 0, llvm::MemoryBuffer::getMemBufferCopy(TUBuffer.getBuffer())); - IntrusiveRefCntPtr InMemoryOverlay = InMemoryFS; - - OverlayFS->pushOverlay(InMemoryOverlay); - std::vector ModifiedCommandLine(CommandLine); - ModifiedCommandLine.emplace_back(InputPath); - - return std::make_pair(OverlayFS, ModifiedCommandLine); -} - -std::pair, - std::vector> -dependencies::initVFSForByNameScanning( - IntrusiveRefCntPtr BaseFS, - ArrayRef 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(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("")); - IntrusiveRefCntPtr InMemoryOverlay = InMemoryFS; - OverlayFS->pushOverlay(InMemoryOverlay); - - std::vector ModifiedCommandLine(CommandLine); - ModifiedCommandLine.emplace_back(FakeInputPath); - - return std::make_pair(OverlayFS, ModifiedCommandLine); -} - bool dependencies::initializeScanCompilerInstance( CompilerInstance &ScanInstance, IntrusiveRefCntPtr FS, diff --git a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp index ef16b14e7cc6e..1aa77c69a9bb5 100644 --- a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp @@ -13,6 +13,7 @@ #include "clang/Driver/Driver.h" #include "clang/Driver/Tool.h" #include "clang/Serialization/ObjectFilePCHContainerReader.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/VirtualFileSystem.h" using namespace clang; @@ -40,39 +41,6 @@ DependencyScanningWorker::DependencyScanningWorker( DependencyScanningWorker::~DependencyScanningWorker() = default; DependencyActionController::~DependencyActionController() = default; -llvm::Error DependencyScanningWorker::computeDependencies( - StringRef WorkingDirectory, ArrayRef CommandLine, - DependencyConsumer &Consumer, DependencyActionController &Controller, - std::optional 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( - DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode()); -} - -static bool forEachDriverJob( - ArrayRef ArgStrs, DiagnosticsEngine &Diags, - IntrusiveRefCntPtr FS, - llvm::function_ref 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( ArrayRef CommandLine, DependencyScanningAction &Action, IntrusiveRefCntPtr FS, @@ -88,11 +56,11 @@ static bool createAndRunToolInvocation( } bool DependencyScanningWorker::scanDependencies( - StringRef WorkingDirectory, ArrayRef CommandLine, + StringRef WorkingDirectory, ArrayRef> CC1CommandLines, DependencyConsumer &Consumer, DependencyActionController &Controller, - DiagnosticConsumer &DC, - IntrusiveRefCntPtr OverlayFS) { - IntrusiveRefCntPtr FS = DepFS; + DiagnosticsEngine &Diags, + llvm::IntrusiveRefCntPtr OverlayFS) { + IntrusiveRefCntPtr FS = nullptr; if (OverlayFS) { #ifndef NDEBUG bool SawDepFS = false; @@ -101,71 +69,55 @@ bool DependencyScanningWorker::scanDependencies( assert(SawDepFS && "OverlayFS not based on DepFS"); #endif FS = std::move(OverlayFS); + } else { + FS = DepFS; + FS->setCurrentWorkingDirectory(WorkingDirectory); } - DiagnosticsEngineWithDiagOpts 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 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(CC1CommandLines, [&](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, ArrayRef CommandLine, - DependencyConsumer &Consumer, DependencyActionController &Controller, - DiagnosticConsumer &DC, std::optional TUBuffer) { - if (TUBuffer) { - auto [FinalFS, FinalCommandLine] = initVFSForTUBufferScanning( - DepFS, CommandLine, WorkingDirectory, *TUBuffer); - return scanDependencies(WorkingDirectory, FinalCommandLine, Consumer, - Controller, DC, FinalFS); - } + DependencyConsumer &DepConsumer, DependencyActionController &Controller, + DiagnosticConsumer &DiagConsumer, + llvm::IntrusiveRefCntPtr OverlayFS) { + DiagnosticsEngineWithDiagOpts DiagEngineWithDiagOpts = + OverlayFS + ? DiagnosticsEngineWithDiagOpts(CommandLine, OverlayFS, DiagConsumer) + : DiagnosticsEngineWithDiagOpts(CommandLine, DepFS, DiagConsumer); + auto &Diags = *DiagEngineWithDiagOpts.DiagEngine; + return computeDependencies(WorkingDirectory, + ArrayRef>(CommandLine), + DepConsumer, Controller, Diags, OverlayFS); +} - DepFS->setCurrentWorkingDirectory(WorkingDirectory); - return scanDependencies(WorkingDirectory, CommandLine, Consumer, Controller, - DC); +bool DependencyScanningWorker::computeDependencies( + StringRef WorkingDirectory, ArrayRef> CommandLines, + DependencyConsumer &DepConsumer, DependencyActionController &Controller, + DiagnosticsEngine &Diags, + llvm::IntrusiveRefCntPtr OverlayFS) { + return scanDependencies(WorkingDirectory, CommandLines, DepConsumer, + Controller, Diags, OverlayFS); } bool DependencyScanningWorker::initializeCompilerInstanceWithContext( @@ -203,3 +155,58 @@ bool DependencyScanningWorker::computeDependenciesByNameWithContext( bool DependencyScanningWorker::finalizeCompilerInstanceWithContext() { return CIWithContext->finalize(); } + +std::pair, + std::vector> +dependencies::initVFSForTUBufferScanning( + IntrusiveRefCntPtr BaseFS, + ArrayRef CommandLine, StringRef WorkingDirectory, + llvm::MemoryBufferRef TUBuffer) { + // Reset what might have been modified in the previous worker invocation. + BaseFS->setCurrentWorkingDirectory(WorkingDirectory); + + auto OverlayFS = + llvm::makeIntrusiveRefCnt(BaseFS); + auto InMemoryFS = llvm::makeIntrusiveRefCnt(); + InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory); + auto InputPath = TUBuffer.getBufferIdentifier(); + InMemoryFS->addFile( + InputPath, 0, llvm::MemoryBuffer::getMemBufferCopy(TUBuffer.getBuffer())); + IntrusiveRefCntPtr InMemoryOverlay = InMemoryFS; + + OverlayFS->pushOverlay(InMemoryOverlay); + std::vector ModifiedCommandLine(CommandLine); + ModifiedCommandLine.emplace_back(InputPath); + + return std::make_pair(OverlayFS, ModifiedCommandLine); +} + +std::pair, + std::vector> +dependencies::initVFSForByNameScanning( + IntrusiveRefCntPtr BaseFS, + ArrayRef 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(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("")); + IntrusiveRefCntPtr InMemoryOverlay = InMemoryFS; + OverlayFS->pushOverlay(InMemoryOverlay); + + std::vector ModifiedCommandLine(CommandLine); + ModifiedCommandLine.emplace_back(FakeInputPath); + + return std::make_pair(OverlayFS, ModifiedCommandLine); +} diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp index 74cc6af3551f8..1ca81fc9a0d83 100644 --- a/clang/lib/Tooling/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanningTool.cpp @@ -7,10 +7,15 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/DependencyScanningTool.h" +#include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticFrontend.h" #include "clang/DependencyScanning/DependencyScannerImpl.h" #include "clang/Driver/Tool.h" #include "clang/Frontend/Utils.h" +#include "llvm/ADT/SmallVectorExtras.h" +#include "llvm/ADT/iterator.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/TargetParser/Host.h" #include using namespace clang; @@ -74,13 +79,131 @@ class MakeDependencyPrinterConsumer : public DependencyConsumer { }; } // anonymous namespace +static std::pair, + std::unique_ptr> +buildCompilation(ArrayRef CommandLine, DiagnosticsEngine &Diags, + IntrusiveRefCntPtr FS, + llvm::BumpPtrAllocator &Alloc) { + auto Argv = llvm::map_to_vector<256>( + CommandLine, [](const auto &Arg) { return Arg.c_str(); }); + + std::unique_ptr Driver = std::make_unique( + 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 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 +buildCC1CommandLine(const driver::Command &Cmd) { + const auto &Args = Cmd.getArguments(); + std::vector Out; + Out.reserve(Args.size() + 1); + Out.emplace_back(Cmd.getExecutable()); + llvm::append_range(Out, Args); + return Out; +} + +static bool computeDependenciesForDriverCommandLine( + DependencyScanningWorker &Worker, StringRef WorkingDirectory, + ArrayRef CommandLine, DependencyConsumer &Consumer, + DependencyActionController &Controller, DiagnosticsEngine &Diags, + IntrusiveRefCntPtr OverlayFS) { + Worker.getVFS().setCurrentWorkingDirectory(WorkingDirectory); + + // 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, &Worker.getVFS(), Alloc); + if (!Compilation) + return false; + + const auto CC1CommandLines = + llvm::map_to_vector(Compilation->getJobs(), buildCC1CommandLine); + const auto CC1CommandLinesView = llvm::map_to_vector( + CC1CommandLines, [](const auto &CC1Cmds) { return ArrayRef(CC1Cmds); }); + + return Worker.computeDependencies(WorkingDirectory, CC1CommandLinesView, + Consumer, Controller, Diags, OverlayFS); +} + +static llvm::Error makeErrorFromDiagnosticsOS( + TextDiagnosticsPrinterWithOutput &DiagPrinterWithOS) { + return llvm::make_error( + DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode()); +} + +static llvm::Error computeDependenciesOrError( + DependencyScanningWorker &Worker, StringRef WorkingDirectory, + ArrayRef CommandLine, DependencyConsumer &Consumer, + DependencyActionController &Controller, + std::optional TUBuffer = std::nullopt) { + // If we are scanning from a TUBuffer, create an overlay filesystem with the + // input as an in-memory file and add it to the command line. + IntrusiveRefCntPtr OverlayFS = nullptr; + std::vector CommandLineWithTUBufferInput; + if (TUBuffer) { + std::tie(OverlayFS, CommandLineWithTUBufferInput) = + initVFSForTUBufferScanning(&Worker.getVFS(), CommandLine, + WorkingDirectory, *TUBuffer); + CommandLine = CommandLineWithTUBufferInput; + } + + TextDiagnosticsPrinterWithOutput DiagPrinterWithOS(CommandLine); + DiagnosticsEngineWithDiagOpts DiagEngineWithDiagOpts( + CommandLine, &Worker.getVFS(), DiagPrinterWithOS.DiagPrinter); + auto &Diags = *DiagEngineWithDiagOpts.DiagEngine; + + const auto IsCC1Input = (CommandLine.size() >= 2 && CommandLine[1] == "-cc1"); + const auto Success = + IsCC1Input + ? Worker.computeDependencies(WorkingDirectory, CommandLine, Consumer, + Controller, Diags, OverlayFS) + : computeDependenciesForDriverCommandLine( + Worker, WorkingDirectory, CommandLine, Consumer, Controller, + Diags, OverlayFS); + + if (!Success) + return makeErrorFromDiagnosticsOS(DiagPrinterWithOS); + return llvm::Error::success(); +} + llvm::Expected DependencyScanningTool::getDependencyFile(ArrayRef CommandLine, StringRef CWD) { MakeDependencyPrinterConsumer Consumer; CallbackActionController Controller(nullptr); - auto Result = - Worker.computeDependencies(CWD, CommandLine, Consumer, Controller); + auto Result = computeDependenciesOrError(Worker, CWD, CommandLine, Consumer, + Controller); if (Result) return std::move(Result); std::string Output; @@ -132,8 +255,8 @@ llvm::Expected DependencyScanningTool::getP1689ModuleDependencyFile( P1689Rule Rule; P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command); P1689ActionController Controller; - auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer, - Controller); + auto Result = computeDependenciesOrError(Worker, CWD, Command.CommandLine, + Consumer, Controller); if (Result) return std::move(Result); @@ -151,8 +274,8 @@ DependencyScanningTool::getTranslationUnitDependencies( std::optional TUBuffer) { FullDependencyConsumer Consumer(AlreadySeen); CallbackActionController Controller(LookupModuleOutput); - llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer, - Controller, TUBuffer); + llvm::Error Result = computeDependenciesOrError( + Worker, CWD, CommandLine, Consumer, Controller, TUBuffer); if (Result) return std::move(Result); @@ -177,18 +300,6 @@ DependencyScanningTool::getModuleDependencies( return Result; } -/// Constructs the full -cc1 command line, including executable, for the given -/// driver \c Cmd. -static std::vector -buildCC1CommandLine(const driver::Command &Cmd) { - const auto &Args = Cmd.getArguments(); - std::vector Out; - Out.reserve(Args.size() + 1); - Out.emplace_back(Cmd.getExecutable()); - llvm::append_range(Out, Args); - return Out; -} - static std::optional> getFirstCC1CommandLine( ArrayRef CommandLine, DiagnosticsEngine &Diags, llvm::IntrusiveRefCntPtr ScanFS) { @@ -211,12 +322,6 @@ static std::optional> getFirstCC1CommandLine( return std::nullopt; } -static llvm::Error makeErrorFromDiagnosticsOS( - TextDiagnosticsPrinterWithOutput &DiagPrinterWithOS) { - return llvm::make_error( - DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode()); -} - llvm::Error DependencyScanningTool::initializeCompilerInstanceWithContextOrError( StringRef CWD, ArrayRef CommandLine) { diff --git a/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp b/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp index e6a5684b10cc9..872c7effde3d3 100644 --- a/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp +++ b/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp @@ -47,9 +47,10 @@ TEST(DependencyScanner, ScanDepsWithDiagConsumer) { { // Check that a successful scan calls DiagConsumer.finish(). std::vector Args = {"clang", - "-target", + "-cc1", + "-triple", "x86_64-apple-macosx10.7", - "-c", + "-emit-obj", "test.cpp", "-o" "test.cpp.o"}; @@ -65,7 +66,7 @@ TEST(DependencyScanner, ScanDepsWithDiagConsumer) { { // Check that an invalid command-line, which never enters the scanning // action calls DiagConsumer.finish(). - std::vector Args = {"clang", "-invalid-arg"}; + std::vector Args = {"clang", "-cc1", "-invalid-arg"}; EnsureFinishedConsumer DiagConsumer; bool Success = Worker.computeDependencies(CWD, Args, DC, AC, DiagConsumer); @@ -78,9 +79,10 @@ TEST(DependencyScanner, ScanDepsWithDiagConsumer) { // Check that a valid command line that produces no scanning jobs calls // DiagConsumer.finish(). std::vector Args = {"clang", - "-target", + "-cc1", + "-triple", "x86_64-apple-macosx10.7", - "-c", + "-emit-obj", "-x", "assembler", "test.s", From d5dae83a72114fc04df4d4a3d9a43bf6eb7a036c Mon Sep 17 00:00:00 2001 From: Naveen Seth Hanig Date: Mon, 15 Dec 2025 21:17:35 +0100 Subject: [PATCH 02/10] Apply suggestions from code review Co-authored-by: Jan Svoboda --- .../clang/DependencyScanning/DependencyScanningWorker.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h index 35f289cf12eb0..f46e2b2236604 100644 --- a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h @@ -93,7 +93,7 @@ class DependencyScanningWorker { ~DependencyScanningWorker(); - /// Run the dependency scanning tool for the given driver job command-line, + /// Run the dependency scanning worker for the given frontend command-line, /// and report the discovered dependencies to the provided consumer. /// /// OverlayFS should be based on the Worker's dependency scanning file-system @@ -110,7 +110,7 @@ class DependencyScanningWorker { llvm::IntrusiveRefCntPtr OverlayFS = nullptr); - /// Run the dependency scanning tool for all given driver job command-lines, + /// Run the dependency scanning tool for all given frontend command-lines, /// and report the discovered dependencies to the provided consumer. /// /// OverlayFS should be based on the Worker's dependency scanning file-system From af9ae17496f8f9aa0a7d2c5cecf4518969da1fbe Mon Sep 17 00:00:00 2001 From: Naveen Seth Hanig Date: Tue, 16 Dec 2025 19:18:08 +0100 Subject: [PATCH 03/10] Revert buildCompilation changes --- clang/lib/Tooling/DependencyScanningTool.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp index 9adf2422ae840..37bc72d890682 100644 --- a/clang/lib/Tooling/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanningTool.cpp @@ -81,11 +81,13 @@ class MakeDependencyPrinterConsumer : public DependencyConsumer { static std::pair, std::unique_ptr> -buildCompilation(ArrayRef CommandLine, DiagnosticsEngine &Diags, +buildCompilation(ArrayRef ArgStrs, DiagnosticsEngine &Diags, IntrusiveRefCntPtr FS, llvm::BumpPtrAllocator &Alloc) { - auto Argv = llvm::map_to_vector<256>( - CommandLine, [](const auto &Arg) { return Arg.c_str(); }); + SmallVector Argv; + Argv.reserve(ArgStrs.size()); + for (const std::string &Arg : ArgStrs) + Argv.push_back(Arg.c_str()); std::unique_ptr Driver = std::make_unique( Argv[0], llvm::sys::getDefaultTargetTriple(), Diags, @@ -112,7 +114,7 @@ buildCompilation(ArrayRef CommandLine, DiagnosticsEngine &Diags, if (Compilation->getJobs().empty()) { Diags.Report(diag::err_fe_expected_compiler_job) - << llvm::join(CommandLine, " "); + << llvm::join(ArgStrs, " "); return std::make_pair(nullptr, nullptr); } From 401ee99764897b727b014f0b6c2c44e3e74cbd02 Mon Sep 17 00:00:00 2001 From: Naveen Seth Hanig Date: Tue, 16 Dec 2025 19:39:30 +0100 Subject: [PATCH 04/10] Move away from map_to_vector, prefer SmallVector Now that we are using ArrayRef consistently in the computeDependencies function signatures, we can also use SmallVector over std::vector as per https://llvm.org/docs/ProgrammersManual.html#llvm-adt-smallvector-h --- clang/lib/Tooling/DependencyScanningTool.cpp | 25 ++++++++++---------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp index 37bc72d890682..1aaa46c5266b1 100644 --- a/clang/lib/Tooling/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanningTool.cpp @@ -121,12 +121,12 @@ buildCompilation(ArrayRef ArgStrs, DiagnosticsEngine &Diags, 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 -buildCC1CommandLine(const driver::Command &Cmd) { +/// Constructs the full frontend command line, including executable, for the +/// given driver \c Cmd. +static SmallVector +buildFrontendCommandLine(const driver::Command &Cmd) { const auto &Args = Cmd.getArguments(); - std::vector Out; + SmallVector Out; Out.reserve(Args.size() + 1); Out.emplace_back(Cmd.getExecutable()); llvm::append_range(Out, Args); @@ -149,12 +149,13 @@ static bool computeDependenciesForDriverCommandLine( if (!Compilation) return false; - const auto CC1CommandLines = - llvm::map_to_vector(Compilation->getJobs(), buildCC1CommandLine); - const auto CC1CommandLinesView = llvm::map_to_vector( - CC1CommandLines, [](const auto &CC1Cmds) { return ArrayRef(CC1Cmds); }); + SmallVector> FrontendCommandLines; + for (const auto &Cmd : Compilation->getJobs()) + FrontendCommandLines.push_back(buildFrontendCommandLine(Cmd)); + SmallVector> FrontendCommandLinesView( + FrontendCommandLines.begin(), FrontendCommandLines.end()); - return Worker.computeDependencies(WorkingDirectory, CC1CommandLinesView, + return Worker.computeDependencies(WorkingDirectory, FrontendCommandLinesView, Consumer, Controller, Diags, OverlayFS); } @@ -296,7 +297,7 @@ DependencyScanningTool::getModuleDependencies( return Result; } -static std::optional> getFirstCC1CommandLine( +static std::optional> getFirstCC1CommandLine( ArrayRef CommandLine, DiagnosticsEngine &Diags, llvm::IntrusiveRefCntPtr ScanFS) { // Compilation holds a non-owning a reference to the Driver, hence we need to @@ -314,7 +315,7 @@ static std::optional> getFirstCC1CommandLine( const auto &Jobs = Compilation->getJobs(); if (const auto It = llvm::find_if(Jobs, IsClangCmd); It != Jobs.end()) - return buildCC1CommandLine(*It); + return buildFrontendCommandLine(*It); return std::nullopt; } From d2e53d67099e94e272d16ec1ee58e76050323c2f Mon Sep 17 00:00:00 2001 From: Naveen Seth Hanig Date: Tue, 16 Dec 2025 19:49:40 +0100 Subject: [PATCH 05/10] Inline scanDependencies --- .../DependencyScanningWorker.h | 9 ---- .../DependencyScanningWorker.cpp | 54 ++++++++----------- 2 files changed, 23 insertions(+), 40 deletions(-) diff --git a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h index f46e2b2236604..25f03501119a1 100644 --- a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h @@ -182,15 +182,6 @@ class DependencyScanningWorker { friend CompilerInstanceWithContext; std::unique_ptr CIWithContext; - - /// Actually carries out the scan. If \c OverlayFS is provided, it must be - /// based on top of DepFS. - bool scanDependencies( - StringRef WorkingDirectory, - ArrayRef> CC1CommandLines, - DependencyConsumer &Consumer, DependencyActionController &Controller, - DiagnosticsEngine &Diags, - IntrusiveRefCntPtr OverlayFS = nullptr); }; std::pair, diff --git a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp index 58cd28395bf81..089e792990859 100644 --- a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp @@ -55,11 +55,26 @@ static bool createAndRunToolInvocation( Diags.getClient()); } -bool DependencyScanningWorker::scanDependencies( - StringRef WorkingDirectory, ArrayRef> CC1CommandLines, - DependencyConsumer &Consumer, DependencyActionController &Controller, +bool DependencyScanningWorker::computeDependencies( + StringRef WorkingDirectory, ArrayRef CommandLine, + DependencyConsumer &DepConsumer, DependencyActionController &Controller, + DiagnosticConsumer &DiagConsumer, + llvm::IntrusiveRefCntPtr OverlayFS) { + DiagnosticsEngineWithDiagOpts DiagEngineWithDiagOpts = + OverlayFS + ? DiagnosticsEngineWithDiagOpts(CommandLine, OverlayFS, DiagConsumer) + : DiagnosticsEngineWithDiagOpts(CommandLine, DepFS, DiagConsumer); + auto &Diags = *DiagEngineWithDiagOpts.DiagEngine; + return computeDependencies(WorkingDirectory, + ArrayRef>(CommandLine), + DepConsumer, Controller, Diags, OverlayFS); +} + +bool DependencyScanningWorker::computeDependencies( + StringRef WorkingDirectory, ArrayRef> CommandLines, + DependencyConsumer &DepConsumer, DependencyActionController &Controller, DiagnosticsEngine &Diags, - llvm::IntrusiveRefCntPtr OverlayFS) { + llvm::IntrusiveRefCntPtr OverlayFS) { IntrusiveRefCntPtr FS = nullptr; if (OverlayFS) { #ifndef NDEBUG @@ -74,13 +89,14 @@ bool DependencyScanningWorker::scanDependencies( FS->setCurrentWorkingDirectory(WorkingDirectory); } - DependencyScanningAction Action(Service, WorkingDirectory, Consumer, + DependencyScanningAction Action(Service, WorkingDirectory, DepConsumer, Controller, DepFS); - const bool Success = llvm::all_of(CC1CommandLines, [&](const auto &Cmd) { + 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()}}); + DepConsumer.handleBuildCommand( + {Cmd.front(), {Cmd.begin() + 1, Cmd.end()}}); return true; } // Create an invocation that uses the underlying file system to ensure that @@ -96,30 +112,6 @@ bool DependencyScanningWorker::scanDependencies( return Success && Action.hasScanned(); } -bool DependencyScanningWorker::computeDependencies( - StringRef WorkingDirectory, ArrayRef CommandLine, - DependencyConsumer &DepConsumer, DependencyActionController &Controller, - DiagnosticConsumer &DiagConsumer, - llvm::IntrusiveRefCntPtr OverlayFS) { - DiagnosticsEngineWithDiagOpts DiagEngineWithDiagOpts = - OverlayFS - ? DiagnosticsEngineWithDiagOpts(CommandLine, OverlayFS, DiagConsumer) - : DiagnosticsEngineWithDiagOpts(CommandLine, DepFS, DiagConsumer); - auto &Diags = *DiagEngineWithDiagOpts.DiagEngine; - return computeDependencies(WorkingDirectory, - ArrayRef>(CommandLine), - DepConsumer, Controller, Diags, OverlayFS); -} - -bool DependencyScanningWorker::computeDependencies( - StringRef WorkingDirectory, ArrayRef> CommandLines, - DependencyConsumer &DepConsumer, DependencyActionController &Controller, - DiagnosticsEngine &Diags, - llvm::IntrusiveRefCntPtr OverlayFS) { - return scanDependencies(WorkingDirectory, CommandLines, DepConsumer, - Controller, Diags, OverlayFS); -} - bool DependencyScanningWorker::initializeCompilerInstanceWithContext( StringRef CWD, ArrayRef CommandLine, DiagnosticConsumer &DC) { auto [OverlayFS, ModifiedCommandLine] = From bd8b27248565a1f57db471ddcd602e64f32c5882 Mon Sep 17 00:00:00 2001 From: Naveen Seth Hanig Date: Tue, 16 Dec 2025 19:53:24 +0100 Subject: [PATCH 06/10] auto use --- clang/lib/DependencyScanning/DependencyScanningWorker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp index 089e792990859..355217c61cbdc 100644 --- a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp @@ -60,7 +60,7 @@ bool DependencyScanningWorker::computeDependencies( DependencyConsumer &DepConsumer, DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer, llvm::IntrusiveRefCntPtr OverlayFS) { - DiagnosticsEngineWithDiagOpts DiagEngineWithDiagOpts = + auto DiagEngineWithDiagOpts = OverlayFS ? DiagnosticsEngineWithDiagOpts(CommandLine, OverlayFS, DiagConsumer) : DiagnosticsEngineWithDiagOpts(CommandLine, DepFS, DiagConsumer); From 0bface72b78ca9e8bfb16a4a74560957af575c99 Mon Sep 17 00:00:00 2001 From: Naveen Seth Hanig Date: Tue, 16 Dec 2025 20:16:21 +0100 Subject: [PATCH 07/10] Align computeDependencies signatures, use new DiagnosticEngine for each frontend invocation. --- .../DependencyScanningWorker.h | 4 +-- .../DependencyScanningWorker.cpp | 16 ++++----- clang/lib/Tooling/DependencyScanningTool.cpp | 36 ++++++++++--------- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h index 25f03501119a1..75a84e378b5fa 100644 --- a/clang/include/clang/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/DependencyScanning/DependencyScanningWorker.h @@ -119,11 +119,11 @@ class DependencyScanningWorker { /// dependency scanning file-system is used instead. /// /// \returns false if clang errors occurred (with diagnostics reported to - /// \c Diags), true otherwise. + /// \c DiagConsumer), true otherwise. bool computeDependencies( StringRef WorkingDirectory, ArrayRef> CommandLines, DependencyConsumer &DepConsumer, DependencyActionController &Controller, - DiagnosticsEngine &Diags, + DiagnosticConsumer &DiagConsumer, llvm::IntrusiveRefCntPtr OverlayFS = nullptr); diff --git a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp index 355217c61cbdc..266660cdfab28 100644 --- a/clang/lib/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/DependencyScanning/DependencyScanningWorker.cpp @@ -60,20 +60,15 @@ bool DependencyScanningWorker::computeDependencies( DependencyConsumer &DepConsumer, DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer, llvm::IntrusiveRefCntPtr OverlayFS) { - auto DiagEngineWithDiagOpts = - OverlayFS - ? DiagnosticsEngineWithDiagOpts(CommandLine, OverlayFS, DiagConsumer) - : DiagnosticsEngineWithDiagOpts(CommandLine, DepFS, DiagConsumer); - auto &Diags = *DiagEngineWithDiagOpts.DiagEngine; return computeDependencies(WorkingDirectory, ArrayRef>(CommandLine), - DepConsumer, Controller, Diags, OverlayFS); + DepConsumer, Controller, DiagConsumer, OverlayFS); } bool DependencyScanningWorker::computeDependencies( StringRef WorkingDirectory, ArrayRef> CommandLines, DependencyConsumer &DepConsumer, DependencyActionController &Controller, - DiagnosticsEngine &Diags, + DiagnosticConsumer &DiagConsumer, llvm::IntrusiveRefCntPtr OverlayFS) { IntrusiveRefCntPtr FS = nullptr; if (OverlayFS) { @@ -99,6 +94,11 @@ bool DependencyScanningWorker::computeDependencies( {Cmd.front(), {Cmd.begin() + 1, Cmd.end()}}); return true; } + + auto DiagEngineWithDiagOpts = + DiagnosticsEngineWithDiagOpts(Cmd, OverlayFS, DiagConsumer); + auto &Diags = *DiagEngineWithDiagOpts.DiagEngine; + // 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. @@ -107,7 +107,7 @@ bool DependencyScanningWorker::computeDependencies( // Ensure finish() is called even if we never reached ExecuteAction(). if (!Action.hasDiagConsumerFinished()) - Diags.getClient()->finish(); + DiagConsumer.finish(); return Success && Action.hasScanned(); } diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp index 1aaa46c5266b1..079630e15d1d0 100644 --- a/clang/lib/Tooling/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanningTool.cpp @@ -136,16 +136,24 @@ buildFrontendCommandLine(const driver::Command &Cmd) { static bool computeDependenciesForDriverCommandLine( DependencyScanningWorker &Worker, StringRef WorkingDirectory, ArrayRef CommandLine, DependencyConsumer &Consumer, - DependencyActionController &Controller, DiagnosticsEngine &Diags, + DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer, IntrusiveRefCntPtr OverlayFS) { - Worker.getVFS().setCurrentWorkingDirectory(WorkingDirectory); + IntrusiveRefCntPtr FS = nullptr; + if (OverlayFS) { + FS = OverlayFS; + } else { + FS = &Worker.getVFS(); + FS->setCurrentWorkingDirectory(WorkingDirectory); + } // 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, &Worker.getVFS(), Alloc); + auto DiagEngineWithDiagOpts = + DiagnosticsEngineWithDiagOpts(CommandLine, FS, DiagConsumer); + const auto [Driver, Compilation] = buildCompilation( + CommandLine, *DiagEngineWithDiagOpts.DiagEngine, FS, Alloc); if (!Compilation) return false; @@ -156,7 +164,8 @@ static bool computeDependenciesForDriverCommandLine( FrontendCommandLines.begin(), FrontendCommandLines.end()); return Worker.computeDependencies(WorkingDirectory, FrontendCommandLinesView, - Consumer, Controller, Diags, OverlayFS); + Consumer, Controller, DiagConsumer, + OverlayFS); } static llvm::Error makeErrorFromDiagnosticsOS( @@ -181,18 +190,13 @@ static bool computeDependencies( CommandLine = CommandLineWithTUBufferInput; } - DiagnosticsEngineWithDiagOpts DiagEngineWithDiagOpts( - CommandLine, &Worker.getVFS(), DiagConsumer); - auto &Diags = *DiagEngineWithDiagOpts.DiagEngine; - const auto IsCC1Input = (CommandLine.size() >= 2 && CommandLine[1] == "-cc1"); - return - IsCC1Input - ? Worker.computeDependencies(WorkingDirectory, CommandLine, Consumer, - Controller, Diags, OverlayFS) - : computeDependenciesForDriverCommandLine( - Worker, WorkingDirectory, CommandLine, Consumer, Controller, - Diags, OverlayFS); + return IsCC1Input ? Worker.computeDependencies(WorkingDirectory, CommandLine, + Consumer, Controller, + DiagConsumer, OverlayFS) + : computeDependenciesForDriverCommandLine( + Worker, WorkingDirectory, CommandLine, Consumer, + Controller, DiagConsumer, OverlayFS); } std::optional From aa5a43430b544eabf852e140567e3749256a1a49 Mon Sep 17 00:00:00 2001 From: Naveen Seth Hanig Date: Tue, 16 Dec 2025 20:24:38 +0100 Subject: [PATCH 08/10] Formatting --- clang/lib/Tooling/DependencyScanningTool.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp index 079630e15d1d0..361c7b360f83f 100644 --- a/clang/lib/Tooling/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanningTool.cpp @@ -206,7 +206,7 @@ DependencyScanningTool::getDependencyFile(ArrayRef CommandLine, MakeDependencyPrinterConsumer DepConsumer; CallbackActionController Controller(nullptr); if (!computeDependencies(Worker, CWD, CommandLine, DepConsumer, Controller, - DiagConsumer)) + DiagConsumer)) return std::nullopt; std::string Output; DepConsumer.printDependencies(Output); @@ -278,7 +278,7 @@ DependencyScanningTool::getTranslationUnitDependencies( CallbackActionController Controller(LookupModuleOutput); if (!computeDependencies(Worker, CWD, CommandLine, Consumer, Controller, - DiagConsumer, TUBuffer)) + DiagConsumer, TUBuffer)) return std::nullopt; return Consumer.takeTranslationUnitDeps(); } From 4e1e9e5e57e1956d9b27133e1a83627ddb40e543 Mon Sep 17 00:00:00 2001 From: Naveen Seth Hanig Date: Tue, 16 Dec 2025 22:10:28 +0100 Subject: [PATCH 09/10] Move TUBuffer handling to getTranslationUnitDependencies --- clang/lib/Tooling/DependencyScanningTool.cpp | 27 ++++++++++---------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp index 361c7b360f83f..062821e8242e7 100644 --- a/clang/lib/Tooling/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanningTool.cpp @@ -178,18 +178,8 @@ static bool computeDependencies( DependencyScanningWorker &Worker, StringRef WorkingDirectory, ArrayRef CommandLine, DependencyConsumer &Consumer, DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer, - std::optional TUBuffer = std::nullopt) { - // If we are scanning from a TUBuffer, create an overlay filesystem with the - // input as an in-memory file and add it to the command line. - IntrusiveRefCntPtr OverlayFS = nullptr; - std::vector CommandLineWithTUBufferInput; - if (TUBuffer) { - std::tie(OverlayFS, CommandLineWithTUBufferInput) = - initVFSForTUBufferScanning(&Worker.getVFS(), CommandLine, - WorkingDirectory, *TUBuffer); - CommandLine = CommandLineWithTUBufferInput; - } - + llvm::IntrusiveRefCntPtr OverlayFS = + nullptr) { const auto IsCC1Input = (CommandLine.size() >= 2 && CommandLine[1] == "-cc1"); return IsCC1Input ? Worker.computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller, @@ -277,8 +267,19 @@ DependencyScanningTool::getTranslationUnitDependencies( FullDependencyConsumer Consumer(AlreadySeen); CallbackActionController Controller(LookupModuleOutput); + // If we are scanning from a TUBuffer, create an overlay filesystem with the + // input as an in-memory file and add it to the command line. + IntrusiveRefCntPtr OverlayFS = nullptr; + std::vector CommandLineWithTUBufferInput; + if (TUBuffer) { + std::tie(OverlayFS, CommandLineWithTUBufferInput) = + initVFSForTUBufferScanning(&Worker.getVFS(), CommandLine, CWD, + *TUBuffer); + CommandLine = CommandLineWithTUBufferInput; + } + if (!computeDependencies(Worker, CWD, CommandLine, Consumer, Controller, - DiagConsumer, TUBuffer)) + DiagConsumer, OverlayFS)) return std::nullopt; return Consumer.takeTranslationUnitDeps(); } From b19b2ea21525fa322498346fc02e72f67f661921 Mon Sep 17 00:00:00 2001 From: Naveen Seth Hanig Date: Tue, 16 Dec 2025 22:10:54 +0100 Subject: [PATCH 10/10] Undo renaming to buildFrontendCommandLine --- clang/lib/Tooling/DependencyScanningTool.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp index 062821e8242e7..e61289f4a45d0 100644 --- a/clang/lib/Tooling/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanningTool.cpp @@ -124,7 +124,7 @@ buildCompilation(ArrayRef ArgStrs, DiagnosticsEngine &Diags, /// Constructs the full frontend command line, including executable, for the /// given driver \c Cmd. static SmallVector -buildFrontendCommandLine(const driver::Command &Cmd) { +buildCC1CommandLine(const driver::Command &Cmd) { const auto &Args = Cmd.getArguments(); SmallVector Out; Out.reserve(Args.size() + 1); @@ -159,7 +159,7 @@ static bool computeDependenciesForDriverCommandLine( SmallVector> FrontendCommandLines; for (const auto &Cmd : Compilation->getJobs()) - FrontendCommandLines.push_back(buildFrontendCommandLine(Cmd)); + FrontendCommandLines.push_back(buildCC1CommandLine(Cmd)); SmallVector> FrontendCommandLinesView( FrontendCommandLines.begin(), FrontendCommandLines.end()); @@ -320,7 +320,7 @@ static std::optional> getFirstCC1CommandLine( const auto &Jobs = Compilation->getJobs(); if (const auto It = llvm::find_if(Jobs, IsClangCmd); It != Jobs.end()) - return buildFrontendCommandLine(*It); + return buildCC1CommandLine(*It); return std::nullopt; }