[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:
parent
4bca00d56b
commit
02451f54d6
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -20,7 +20,6 @@ add_clang_library(clangDependencyScanning
|
||||
LINK_LIBS
|
||||
clangAST
|
||||
clangBasic
|
||||
clangDriver
|
||||
clangFrontend
|
||||
clangLex
|
||||
clangSerialization
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user