llvm-project/clang/lib/DependencyScanning/DependencyScanningWorker.cpp
Naveen Seth Hanig b70be3dc14
[clang][DependencyScanning] Separate clangDependencyScanning and DependencyScanningTool (NFC) (#169962)
This patch is the first of two in refactoring Clang's dependency
scanning tooling to remove its dependency on clangDriver.

It separates Tooling/DependencyScanningTool.cpp from the rest of
clangDependencyScanning and moves clangDependencyScanning out of
clangTooling into its own library. No functional changes are
introduced.

The follow-up patch (#169964) will restrict clangDependencyScanning to
handling only -cc1 command line inputs and will move all functionality
related to handling driver commands into clangTooling.
(Tooling/DependencyScanningTool.cpp).

This is part of a broader effort to support driver-managed builds for
compilations using C++ named modules and/or Clang modules. It is
required for linking the dependency scanning tooling against the driver
without introducing cyclic dependencies, which would otherwise cause
build failures when dynamic linking is enabled.

The RFC for this change can be found here:

https://discourse.llvm.org/t/rfc-new-clangoptions-library-remove-dependency-on-clangdriver-from-clangfrontend-and-flangfrontend/88773?u=naveen-seth
2025-12-04 00:38:21 +01:00

209 lines
8.1 KiB
C++

//===- DependencyScanningWorker.cpp - Thread-Safe Scanning Worker ---------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/DependencyScanning/DependencyScanningWorker.h"
#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/DependencyScanning/DependencyScannerImpl.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Tool.h"
using namespace clang;
using namespace dependencies;
DependencyScanningWorker::DependencyScanningWorker(
DependencyScanningService &Service,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS)
: Service(Service) {
PCHContainerOps = std::make_shared<PCHContainerOperations>();
// We need to read object files from PCH built outside the scanner.
PCHContainerOps->registerReader(
std::make_unique<ObjectFilePCHContainerReader>());
// The scanner itself writes only raw ast files.
PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
if (Service.shouldTraceVFS())
BaseFS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(
std::move(BaseFS));
DepFS = llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(
Service.getSharedCache(), std::move(BaseFS));
}
DependencyScanningWorker::~DependencyScanningWorker() = default;
DependencyActionController::~DependencyActionController() = default;
llvm::Error DependencyScanningWorker::computeDependencies(
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
DependencyConsumer &Consumer, DependencyActionController &Controller,
std::optional<llvm::MemoryBufferRef> TUBuffer) {
// Capture the emitted diagnostics and report them to the client
// in the case of a failure.
TextDiagnosticsPrinterWithOutput DiagPrinterWithOS(CommandLine);
if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
DiagPrinterWithOS.DiagPrinter, TUBuffer))
return llvm::Error::success();
return llvm::make_error<llvm::StringError>(
DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode());
}
static bool forEachDriverJob(
ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
// Compilation holds a non-owning a reference to the Driver, hence we need to
// keep the Driver alive when we use Compilation. Arguments to commands may be
// owned by Alloc when expanded from response files.
llvm::BumpPtrAllocator Alloc;
auto [Driver, Compilation] = buildCompilation(ArgStrs, Diags, FS, Alloc);
if (!Compilation)
return false;
for (const driver::Command &Job : Compilation->getJobs()) {
if (!Callback(Job))
return false;
}
return true;
}
static bool createAndRunToolInvocation(
const std::vector<std::string> &CommandLine,
DependencyScanningAction &Action,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
DiagnosticsEngine &Diags) {
auto Invocation = createCompilerInvocation(CommandLine, Diags);
if (!Invocation)
return false;
return Action.runInvocation(CommandLine[0], std::move(Invocation),
std::move(FS), PCHContainerOps,
Diags.getClient());
}
bool DependencyScanningWorker::scanDependencies(
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
DependencyConsumer &Consumer, DependencyActionController &Controller,
DiagnosticConsumer &DC,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayFS) {
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = DepFS;
if (OverlayFS) {
#ifndef NDEBUG
bool SawDepFS = false;
OverlayFS->visit(
[&](llvm::vfs::FileSystem &VFS) { SawDepFS |= &VFS == DepFS.get(); });
assert(SawDepFS && "OverlayFS not based on DepFS");
#endif
FS = std::move(OverlayFS);
}
DignosticsEngineWithDiagOpts DiagEngineWithCmdAndOpts(CommandLine, FS, DC);
DependencyScanningAction Action(Service, WorkingDirectory, Consumer,
Controller, DepFS);
bool Success = false;
if (CommandLine[1] == "-cc1") {
Success =
createAndRunToolInvocation(CommandLine, Action, FS, PCHContainerOps,
*DiagEngineWithCmdAndOpts.DiagEngine);
} else {
Success = forEachDriverJob(
CommandLine, *DiagEngineWithCmdAndOpts.DiagEngine, FS,
[&](const driver::Command &Cmd) {
if (StringRef(Cmd.getCreator().getName()) != "clang") {
// Non-clang command. Just pass through to the dependency
// consumer.
Consumer.handleBuildCommand(
{Cmd.getExecutable(),
{Cmd.getArguments().begin(), Cmd.getArguments().end()}});
return true;
}
// Insert -cc1 command line options into Argv
std::vector<std::string> Argv;
Argv.push_back(Cmd.getExecutable());
llvm::append_range(Argv, Cmd.getArguments());
// Create an invocation that uses the underlying file
// system to ensure that any file system requests that
// are made by the driver do not go through the
// dependency scanning filesystem.
return createAndRunToolInvocation(
std::move(Argv), Action, FS, PCHContainerOps,
*DiagEngineWithCmdAndOpts.DiagEngine);
});
}
if (Success && !Action.hasScanned())
DiagEngineWithCmdAndOpts.DiagEngine->Report(
diag::err_fe_expected_compiler_job)
<< llvm::join(CommandLine, " ");
// Ensure finish() is called even if we never reached ExecuteAction().
if (!Action.hasDiagConsumerFinished())
DC.finish();
return Success && Action.hasScanned();
}
bool DependencyScanningWorker::computeDependencies(
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
DependencyConsumer &Consumer, DependencyActionController &Controller,
DiagnosticConsumer &DC, std::optional<llvm::MemoryBufferRef> TUBuffer) {
if (TUBuffer) {
auto [FinalFS, FinalCommandLine] = initVFSForTUBufferScanning(
DepFS, CommandLine, WorkingDirectory, *TUBuffer);
return scanDependencies(WorkingDirectory, FinalCommandLine, Consumer,
Controller, DC, FinalFS);
} else {
DepFS->setCurrentWorkingDirectory(WorkingDirectory);
return scanDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
DC);
}
}
llvm::Error
DependencyScanningWorker::initializeCompilerInstanceWithContextOrError(
StringRef CWD, const std::vector<std::string> &CommandLine) {
bool Success = initializeCompilerInstanceWithContext(CWD, CommandLine);
return CIWithContext->handleReturnStatus(Success);
}
llvm::Error
DependencyScanningWorker::computeDependenciesByNameWithContextOrError(
StringRef ModuleName, DependencyConsumer &Consumer,
DependencyActionController &Controller) {
bool Success =
computeDependenciesByNameWithContext(ModuleName, Consumer, Controller);
return CIWithContext->handleReturnStatus(Success);
}
llvm::Error
DependencyScanningWorker::finalizeCompilerInstanceWithContextOrError() {
bool Success = finalizeCompilerInstance();
return CIWithContext->handleReturnStatus(Success);
}
bool DependencyScanningWorker::initializeCompilerInstanceWithContext(
StringRef CWD, const std::vector<std::string> &CommandLine,
DiagnosticConsumer *DC) {
CIWithContext =
std::make_unique<CompilerInstanceWithContext>(*this, CWD, CommandLine);
return CIWithContext->initialize(DC);
}
bool DependencyScanningWorker::computeDependenciesByNameWithContext(
StringRef ModuleName, DependencyConsumer &Consumer,
DependencyActionController &Controller) {
assert(CIWithContext && "CompilerInstance with context required!");
return CIWithContext->computeDependencies(ModuleName, Consumer, Controller);
}
bool DependencyScanningWorker::finalizeCompilerInstance() {
return CIWithContext->finalize();
}