[clang][DependencyScanning] Remove dependency on clangDriver from clangDependencyScanning (#172347)

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.

---------

Co-authored-by: Jan Svoboda <jan@svoboda.ai>
This commit is contained in:
Naveen Seth Hanig 2026-01-05 23:56:32 +01:00 committed by GitHub
parent 4bca00d56b
commit 02451f54d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 290 additions and 233 deletions

View File

@ -89,28 +89,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::OverlayFileSystem>,
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);
void initializeScanCompilerInstance(
CompilerInstance &ScanInstance,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,

View File

@ -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,19 +93,39 @@ public:
~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 worker for the given frontend command-line,
/// and report the discovered dependencies to the provided consumer.
///
/// \returns false if clang errors occurred (with diagnostics reported to
/// 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 any errors occurred (with diagnostics reported to
/// \c DiagConsumer), true otherwise.
bool computeDependencies(
StringRef WorkingDirectory, ArrayRef<std::string> CommandLine,
DependencyConsumer &DepConsumer, DependencyActionController &Controller,
DiagnosticConsumer &DiagConsumer,
std::optional<llvm::MemoryBufferRef> TUBuffer = std::nullopt);
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS =
nullptr);
/// 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
/// 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 any errors occurred (with diagnostics reported to
/// \c DiagConsumer), true otherwise.
bool computeDependencies(
StringRef WorkingDirectory, ArrayRef<ArrayRef<std::string>> CommandLines,
DependencyConsumer &DepConsumer, DependencyActionController &Controller,
DiagnosticConsumer &DiagConsumer,
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS =
nullptr);
/// The three method below implements a new interface for by name
/// dependency scanning. They together enable the dependency scanning worker
@ -162,16 +182,21 @@ private:
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, ArrayRef<std::string> CommandLine,
DependencyConsumer &Consumer, DependencyActionController &Controller,
DiagnosticConsumer &DC,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayFS = nullptr);
};
std::pair<IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>,
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);
} // end namespace dependencies
} // end namespace clang

View File

@ -175,6 +175,25 @@ private:
DiagPrinterWithOS;
};
/// Run the dependency scanning worker for the given driver or frontend
/// 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 any errors occurred (with diagnostics reported to
/// \c DiagConsumer), true otherwise.
bool computeDependencies(
dependencies::DependencyScanningWorker &Worker, StringRef WorkingDirectory,
ArrayRef<std::string> CommandLine,
dependencies::DependencyConsumer &Consumer,
dependencies::DependencyActionController &Controller,
DiagnosticConsumer &DiagConsumer,
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS = nullptr);
} // end namespace tooling
} // end namespace clang

View File

@ -20,7 +20,6 @@ add_clang_library(clangDependencyScanning
LINK_LIBS
clangAST
clangBasic
clangDriver
clangFrontend
clangLex
clangSerialization

View File

@ -379,42 +379,6 @@ DiagnosticsEngineWithDiagOpts::DiagnosticsEngineWithDiagOpts(
/*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,61 +394,6 @@ dependencies::createCompilerInvocation(ArrayRef<std::string> CommandLine,
return Invocation;
}
std::pair<IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>,
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);
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);
}
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);
}
void dependencies::initializeScanCompilerInstance(
CompilerInstance &ScanInstance,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,

View File

@ -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,24 +41,6 @@ DependencyScanningWorker::DependencyScanningWorker(
DependencyScanningWorker::~DependencyScanningWorker() = default;
DependencyActionController::~DependencyActionController() = default;
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(
ArrayRef<std::string> CommandLine, DependencyScanningAction &Action,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
@ -72,12 +55,22 @@ static bool createAndRunToolInvocation(
Diags.getClient());
}
bool DependencyScanningWorker::scanDependencies(
bool DependencyScanningWorker::computeDependencies(
StringRef WorkingDirectory, ArrayRef<std::string> CommandLine,
DependencyConsumer &Consumer, DependencyActionController &Controller,
DiagnosticConsumer &DC,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayFS) {
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = DepFS;
DependencyConsumer &DepConsumer, DependencyActionController &Controller,
DiagnosticConsumer &DiagConsumer,
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) {
return computeDependencies(WorkingDirectory,
ArrayRef<ArrayRef<std::string>>(CommandLine),
DepConsumer, Controller, DiagConsumer, OverlayFS);
}
bool DependencyScanningWorker::computeDependencies(
StringRef WorkingDirectory, ArrayRef<ArrayRef<std::string>> CommandLines,
DependencyConsumer &DepConsumer, DependencyActionController &Controller,
DiagnosticConsumer &DiagConsumer,
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) {
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = nullptr;
if (OverlayFS) {
#ifndef NDEBUG
bool SawDepFS = false;
@ -86,73 +79,39 @@ 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,
DependencyScanningAction Action(Service, WorkingDirectory, DepConsumer,
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;
}
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.
DepConsumer.handleBuildCommand(
{Cmd.front(), {Cmd.begin() + 1, Cmd.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());
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.
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, " ");
// 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();
DiagConsumer.finish();
return Success && Action.hasScanned();
}
bool DependencyScanningWorker::computeDependencies(
StringRef WorkingDirectory, ArrayRef<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);
}
DepFS->setCurrentWorkingDirectory(WorkingDirectory);
return scanDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
DC);
}
bool DependencyScanningWorker::initializeCompilerInstanceWithContext(
StringRef CWD, ArrayRef<std::string> CommandLine, DiagnosticConsumer &DC) {
auto [OverlayFS, ModifiedCommandLine] =
@ -184,3 +143,58 @@ bool DependencyScanningWorker::computeDependenciesByNameWithContext(
bool DependencyScanningWorker::finalizeCompilerInstanceWithContext() {
return CIWithContext->finalize();
}
std::pair<IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>,
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);
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);
}
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);
}

View File

@ -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 <optional>
using namespace clang;
@ -74,14 +79,123 @@ protected:
};
} // anonymous namespace
static 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) {
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);
if (Compilation->getJobs().empty()) {
Diags.Report(diag::err_fe_expected_compiler_job)
<< llvm::join(ArgStrs, " ");
return std::make_pair(nullptr, nullptr);
}
return std::make_pair(std::move(Driver), std::move(Compilation));
}
/// Constructs the full frontend command line, including executable, for the
/// given driver \c Cmd.
static SmallVector<std::string, 0>
buildCC1CommandLine(const driver::Command &Cmd) {
const auto &Args = Cmd.getArguments();
SmallVector<std::string, 0> 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<std::string> CommandLine, DependencyConsumer &Consumer,
DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer,
IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) {
IntrusiveRefCntPtr<llvm::vfs::FileSystem> 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;
auto DiagEngineWithDiagOpts =
DiagnosticsEngineWithDiagOpts(CommandLine, FS, DiagConsumer);
const auto [Driver, Compilation] = buildCompilation(
CommandLine, *DiagEngineWithDiagOpts.DiagEngine, FS, Alloc);
if (!Compilation)
return false;
SmallVector<SmallVector<std::string, 0>> FrontendCommandLines;
for (const auto &Cmd : Compilation->getJobs())
FrontendCommandLines.push_back(buildCC1CommandLine(Cmd));
SmallVector<ArrayRef<std::string>> FrontendCommandLinesView(
FrontendCommandLines.begin(), FrontendCommandLines.end());
return Worker.computeDependencies(WorkingDirectory, FrontendCommandLinesView,
Consumer, Controller, DiagConsumer,
OverlayFS);
}
static llvm::Error makeErrorFromDiagnosticsOS(
TextDiagnosticsPrinterWithOutput &DiagPrinterWithOS) {
return llvm::make_error<llvm::StringError>(
DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode());
}
bool tooling::computeDependencies(
DependencyScanningWorker &Worker, StringRef WorkingDirectory,
ArrayRef<std::string> CommandLine, DependencyConsumer &Consumer,
DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer,
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) {
const auto IsCC1Input = (CommandLine.size() >= 2 && CommandLine[1] == "-cc1");
return IsCC1Input ? Worker.computeDependencies(WorkingDirectory, CommandLine,
Consumer, Controller,
DiagConsumer, OverlayFS)
: computeDependenciesForDriverCommandLine(
Worker, WorkingDirectory, CommandLine, Consumer,
Controller, DiagConsumer, OverlayFS);
}
std::optional<std::string>
DependencyScanningTool::getDependencyFile(ArrayRef<std::string> CommandLine,
StringRef CWD,
DiagnosticConsumer &DiagConsumer) {
MakeDependencyPrinterConsumer DepConsumer;
CallbackActionController Controller(nullptr);
if (!Worker.computeDependencies(CWD, CommandLine, DepConsumer, Controller,
DiagConsumer))
if (!computeDependencies(Worker, CWD, CommandLine, DepConsumer, Controller,
DiagConsumer))
return std::nullopt;
std::string Output;
DepConsumer.printDependencies(Output);
@ -132,8 +246,8 @@ std::optional<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
P1689Rule Rule;
P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command);
P1689ActionController Controller;
if (!Worker.computeDependencies(CWD, Command.CommandLine, Consumer,
Controller, DiagConsumer))
if (!computeDependencies(Worker, CWD, Command.CommandLine, Consumer,
Controller, DiagConsumer))
return std::nullopt;
MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath();
@ -152,8 +266,19 @@ DependencyScanningTool::getTranslationUnitDependencies(
FullDependencyConsumer Consumer(AlreadySeen);
CallbackActionController Controller(LookupModuleOutput);
if (!Worker.computeDependencies(CWD, CommandLine, Consumer, Controller,
DiagConsumer, TUBuffer))
// 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<llvm::vfs::OverlayFileSystem> OverlayFS = nullptr;
std::vector<std::string> CommandLineWithTUBufferInput;
if (TUBuffer) {
std::tie(OverlayFS, CommandLineWithTUBufferInput) =
initVFSForTUBufferScanning(&Worker.getVFS(), CommandLine, CWD,
*TUBuffer);
CommandLine = CommandLineWithTUBufferInput;
}
if (!computeDependencies(Worker, CWD, CommandLine, Consumer, Controller,
DiagConsumer, OverlayFS))
return std::nullopt;
return Consumer.takeTranslationUnitDeps();
}
@ -176,19 +301,7 @@ DependencyScanningTool::getModuleDependencies(
return Result;
}
/// 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::optional<std::vector<std::string>> getFirstCC1CommandLine(
static std::optional<SmallVector<std::string, 0>> 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
@ -210,12 +323,6 @@ static std::optional<std::vector<std::string>> getFirstCC1CommandLine(
return std::nullopt;
}
static llvm::Error makeErrorFromDiagnosticsOS(
TextDiagnosticsPrinterWithOutput &DiagPrinterWithOS) {
return llvm::make_error<llvm::StringError>(
DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode());
}
bool DependencyScanningTool::initializeWorkerCIWithContextFromCommandline(
DependencyScanningWorker &Worker, StringRef CWD,
ArrayRef<std::string> CommandLine, DiagnosticConsumer &DC) {

View File

@ -47,9 +47,10 @@ TEST(DependencyScanner, ScanDepsWithDiagConsumer) {
{
// Check that a successful scan calls DiagConsumer.finish().
std::vector<std::string> 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<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);
@ -78,9 +79,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",
"-cc1",
"-triple",
"x86_64-apple-macosx10.7",
"-c",
"-emit-obj",
"-x",
"assembler",
"test.s",