mirror of
https://github.com/intel/llvm.git
synced 2026-01-14 20:10:50 +08:00
This is the second 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 by-name scanning interface in DependencyScanningWorker to accept only -cc1 command lines directly and moves the logic for handling driver-style command lines into DependencyScanningTool in clangTooling. Support for -cc1 command lines in by-name scanning is introduced in this patch. The next patch will update the remaining parts of DependencyScanningWorker to operate only on -cc1 command lines, allowing its dependency on clangDriver to be removed. https://github.com/llvm/llvm-project/pull/169964#pullrequestreview-3545879529
276 lines
9.9 KiB
C++
276 lines
9.9 KiB
C++
//===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===//
|
|
//
|
|
// 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/Tooling/DependencyScanningTool.h"
|
|
#include "clang/Basic/DiagnosticFrontend.h"
|
|
#include "clang/DependencyScanning/DependencyScannerImpl.h"
|
|
#include "clang/Driver/Tool.h"
|
|
#include "clang/Frontend/Utils.h"
|
|
#include <optional>
|
|
|
|
using namespace clang;
|
|
using namespace tooling;
|
|
using namespace dependencies;
|
|
|
|
DependencyScanningTool::DependencyScanningTool(
|
|
DependencyScanningService &Service,
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
|
|
: Worker(Service, std::move(FS)) {}
|
|
|
|
namespace {
|
|
/// Prints out all of the gathered dependencies into a string.
|
|
class MakeDependencyPrinterConsumer : public DependencyConsumer {
|
|
public:
|
|
void handleBuildCommand(Command) override {}
|
|
|
|
void
|
|
handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
|
|
this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
|
|
}
|
|
|
|
void handleFileDependency(StringRef File) override {
|
|
Dependencies.push_back(std::string(File));
|
|
}
|
|
|
|
// These are ignored for the make format as it can't support the full
|
|
// set of deps, and handleFileDependency handles enough for implicitly
|
|
// built modules to work.
|
|
void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {}
|
|
void handleModuleDependency(ModuleDeps MD) override {}
|
|
void handleDirectModuleDependency(ModuleID ID) override {}
|
|
void handleVisibleModule(std::string ModuleName) override {}
|
|
void handleContextHash(std::string Hash) override {}
|
|
|
|
void printDependencies(std::string &S) {
|
|
assert(Opts && "Handled dependency output options.");
|
|
|
|
class DependencyPrinter : public DependencyFileGenerator {
|
|
public:
|
|
DependencyPrinter(DependencyOutputOptions &Opts,
|
|
ArrayRef<std::string> Dependencies)
|
|
: DependencyFileGenerator(Opts) {
|
|
for (const auto &Dep : Dependencies)
|
|
addDependency(Dep);
|
|
}
|
|
|
|
void printDependencies(std::string &S) {
|
|
llvm::raw_string_ostream OS(S);
|
|
outputDependencyFile(OS);
|
|
}
|
|
};
|
|
|
|
DependencyPrinter Generator(*Opts, Dependencies);
|
|
Generator.printDependencies(S);
|
|
}
|
|
|
|
protected:
|
|
std::unique_ptr<DependencyOutputOptions> Opts;
|
|
std::vector<std::string> Dependencies;
|
|
};
|
|
} // anonymous namespace
|
|
|
|
llvm::Expected<std::string>
|
|
DependencyScanningTool::getDependencyFile(ArrayRef<std::string> CommandLine,
|
|
StringRef CWD) {
|
|
MakeDependencyPrinterConsumer Consumer;
|
|
CallbackActionController Controller(nullptr);
|
|
auto Result =
|
|
Worker.computeDependencies(CWD, CommandLine, Consumer, Controller);
|
|
if (Result)
|
|
return std::move(Result);
|
|
std::string Output;
|
|
Consumer.printDependencies(Output);
|
|
return Output;
|
|
}
|
|
|
|
llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
|
|
const CompileCommand &Command, StringRef CWD, std::string &MakeformatOutput,
|
|
std::string &MakeformatOutputPath) {
|
|
class P1689ModuleDependencyPrinterConsumer
|
|
: public MakeDependencyPrinterConsumer {
|
|
public:
|
|
P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule,
|
|
const CompileCommand &Command)
|
|
: Filename(Command.Filename), Rule(Rule) {
|
|
Rule.PrimaryOutput = Command.Output;
|
|
}
|
|
|
|
void handleProvidedAndRequiredStdCXXModules(
|
|
std::optional<P1689ModuleInfo> Provided,
|
|
std::vector<P1689ModuleInfo> Requires) override {
|
|
Rule.Provides = Provided;
|
|
if (Rule.Provides)
|
|
Rule.Provides->SourcePath = Filename.str();
|
|
Rule.Requires = Requires;
|
|
}
|
|
|
|
StringRef getMakeFormatDependencyOutputPath() {
|
|
if (Opts->OutputFormat != DependencyOutputFormat::Make)
|
|
return {};
|
|
return Opts->OutputFile;
|
|
}
|
|
|
|
private:
|
|
StringRef Filename;
|
|
P1689Rule &Rule;
|
|
};
|
|
|
|
class P1689ActionController : public DependencyActionController {
|
|
public:
|
|
// The lookupModuleOutput is for clang modules. P1689 format don't need it.
|
|
std::string lookupModuleOutput(const ModuleDeps &,
|
|
ModuleOutputKind Kind) override {
|
|
return "";
|
|
}
|
|
};
|
|
|
|
P1689Rule Rule;
|
|
P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command);
|
|
P1689ActionController Controller;
|
|
auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer,
|
|
Controller);
|
|
if (Result)
|
|
return std::move(Result);
|
|
|
|
MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath();
|
|
if (!MakeformatOutputPath.empty())
|
|
Consumer.printDependencies(MakeformatOutput);
|
|
return Rule;
|
|
}
|
|
|
|
llvm::Expected<TranslationUnitDeps>
|
|
DependencyScanningTool::getTranslationUnitDependencies(
|
|
ArrayRef<std::string> CommandLine, StringRef CWD,
|
|
const llvm::DenseSet<ModuleID> &AlreadySeen,
|
|
LookupModuleOutputCallback LookupModuleOutput,
|
|
std::optional<llvm::MemoryBufferRef> TUBuffer) {
|
|
FullDependencyConsumer Consumer(AlreadySeen);
|
|
CallbackActionController Controller(LookupModuleOutput);
|
|
llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer,
|
|
Controller, TUBuffer);
|
|
|
|
if (Result)
|
|
return std::move(Result);
|
|
return Consumer.takeTranslationUnitDeps();
|
|
}
|
|
|
|
llvm::Expected<TranslationUnitDeps>
|
|
DependencyScanningTool::getModuleDependencies(
|
|
StringRef ModuleName, ArrayRef<std::string> CommandLine, StringRef CWD,
|
|
const llvm::DenseSet<ModuleID> &AlreadySeen,
|
|
LookupModuleOutputCallback LookupModuleOutput) {
|
|
if (auto Error =
|
|
initializeCompilerInstanceWithContextOrError(CWD, CommandLine))
|
|
return Error;
|
|
|
|
auto Result = computeDependenciesByNameWithContextOrError(
|
|
ModuleName, AlreadySeen, LookupModuleOutput);
|
|
|
|
if (auto Error = finalizeCompilerInstanceWithContextOrError())
|
|
return Error;
|
|
|
|
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(
|
|
ArrayRef<std::string> CommandLine, DiagnosticsEngine &Diags,
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> ScanFS) {
|
|
// Compilation holds a non-owning a reference to the Driver, hence we need to
|
|
// keep the Driver alive when we use Compilation. Arguments to commands may be
|
|
// owned by Alloc when expanded from response files.
|
|
llvm::BumpPtrAllocator Alloc;
|
|
const auto [Driver, Compilation] =
|
|
buildCompilation(CommandLine, Diags, ScanFS, Alloc);
|
|
if (!Compilation)
|
|
return std::nullopt;
|
|
|
|
const auto IsClangCmd = [](const driver::Command &Cmd) {
|
|
return StringRef(Cmd.getCreator().getName()) == "clang";
|
|
};
|
|
|
|
const auto &Jobs = Compilation->getJobs();
|
|
if (const auto It = llvm::find_if(Jobs, IsClangCmd); It != Jobs.end())
|
|
return buildCC1CommandLine(*It);
|
|
return std::nullopt;
|
|
}
|
|
|
|
static llvm::Error makeErrorFromDiagnosticsOS(
|
|
TextDiagnosticsPrinterWithOutput &DiagPrinterWithOS) {
|
|
return llvm::make_error<llvm::StringError>(
|
|
DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode());
|
|
}
|
|
|
|
llvm::Error
|
|
DependencyScanningTool::initializeCompilerInstanceWithContextOrError(
|
|
StringRef CWD, ArrayRef<std::string> CommandLine) {
|
|
DiagPrinterWithOS =
|
|
std::make_unique<TextDiagnosticsPrinterWithOutput>(CommandLine);
|
|
|
|
if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {
|
|
// The input command line is already a -cc1 invocation; initialize the
|
|
// compiler instance directly from it.
|
|
if (Worker.initializeCompilerInstanceWithContext(
|
|
CWD, CommandLine, DiagPrinterWithOS->DiagPrinter))
|
|
return llvm::Error::success();
|
|
return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS);
|
|
}
|
|
|
|
// The input command line is either a driver-style command line, or
|
|
// ill-formed. In this case, we will first call the Driver to build a -cc1
|
|
// command line for this compilation or diagnose any ill-formed input.
|
|
auto OverlayFSAndArgs = initVFSForByNameScanning(
|
|
&Worker.getVFS(), CommandLine, CWD, "ScanningByName");
|
|
auto &OverlayFS = OverlayFSAndArgs.first;
|
|
const auto &ModifiedCommandLine = OverlayFSAndArgs.second;
|
|
|
|
auto DiagEngineWithCmdAndOpts =
|
|
std::make_unique<DiagnosticsEngineWithDiagOpts>(
|
|
ModifiedCommandLine, OverlayFS, DiagPrinterWithOS->DiagPrinter);
|
|
|
|
const auto MaybeFirstCC1 = getFirstCC1CommandLine(
|
|
ModifiedCommandLine, *DiagEngineWithCmdAndOpts->DiagEngine, OverlayFS);
|
|
if (!MaybeFirstCC1)
|
|
return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS);
|
|
|
|
if (Worker.initializeCompilerInstanceWithContext(
|
|
CWD, *MaybeFirstCC1, std::move(DiagEngineWithCmdAndOpts), OverlayFS))
|
|
return llvm::Error::success();
|
|
return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS);
|
|
}
|
|
|
|
llvm::Expected<TranslationUnitDeps>
|
|
DependencyScanningTool::computeDependenciesByNameWithContextOrError(
|
|
StringRef ModuleName, const llvm::DenseSet<ModuleID> &AlreadySeen,
|
|
LookupModuleOutputCallback LookupModuleOutput) {
|
|
FullDependencyConsumer Consumer(AlreadySeen);
|
|
CallbackActionController Controller(LookupModuleOutput);
|
|
if (Worker.computeDependenciesByNameWithContext(ModuleName, Consumer,
|
|
Controller))
|
|
return Consumer.takeTranslationUnitDeps();
|
|
return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS);
|
|
}
|
|
|
|
llvm::Error
|
|
DependencyScanningTool::finalizeCompilerInstanceWithContextOrError() {
|
|
if (Worker.finalizeCompilerInstanceWithContext())
|
|
return llvm::Error::success();
|
|
return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS);
|
|
}
|