211 lines · cpp
1//===- DependencyScanningWorker.cpp - clang-scan-deps worker --------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"10#include "DependencyScannerImpl.h"11#include "clang/Basic/DiagnosticFrontend.h"12#include "clang/Driver/Driver.h"13#include "clang/Driver/Tool.h"14 15using namespace clang;16using namespace tooling;17using namespace dependencies;18 19DependencyScanningWorker::DependencyScanningWorker(20 DependencyScanningService &Service,21 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)22 : Service(Service) {23 PCHContainerOps = std::make_shared<PCHContainerOperations>();24 // We need to read object files from PCH built outside the scanner.25 PCHContainerOps->registerReader(26 std::make_unique<ObjectFilePCHContainerReader>());27 // The scanner itself writes only raw ast files.28 PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());29 30 if (Service.shouldTraceVFS())31 FS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(std::move(FS));32 33 switch (Service.getMode()) {34 case ScanningMode::DependencyDirectivesScan:35 DepFS = llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(36 Service.getSharedCache(), FS);37 BaseFS = DepFS;38 break;39 case ScanningMode::CanonicalPreprocessing:40 DepFS = nullptr;41 BaseFS = FS;42 break;43 }44}45 46DependencyScanningWorker::~DependencyScanningWorker() = default;47DependencyActionController::~DependencyActionController() = default;48 49llvm::Error DependencyScanningWorker::computeDependencies(50 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,51 DependencyConsumer &Consumer, DependencyActionController &Controller,52 std::optional<llvm::MemoryBufferRef> TUBuffer) {53 // Capture the emitted diagnostics and report them to the client54 // in the case of a failure.55 TextDiagnosticsPrinterWithOutput DiagPrinterWithOS(CommandLine);56 57 if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,58 DiagPrinterWithOS.DiagPrinter, TUBuffer))59 return llvm::Error::success();60 return llvm::make_error<llvm::StringError>(61 DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode());62}63 64static bool forEachDriverJob(65 ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags,66 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,67 llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {68 // Compilation holds a non-owning a reference to the Driver, hence we need to69 // keep the Driver alive when we use Compilation. Arguments to commands may be70 // owned by Alloc when expanded from response files.71 llvm::BumpPtrAllocator Alloc;72 auto [Driver, Compilation] = buildCompilation(ArgStrs, Diags, FS, Alloc);73 if (!Compilation)74 return false;75 for (const driver::Command &Job : Compilation->getJobs()) {76 if (!Callback(Job))77 return false;78 }79 return true;80}81 82static bool createAndRunToolInvocation(83 const std::vector<std::string> &CommandLine,84 DependencyScanningAction &Action,85 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,86 std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,87 DiagnosticsEngine &Diags, DependencyConsumer &Consumer) {88 auto Invocation = createCompilerInvocation(CommandLine, Diags);89 if (!Invocation)90 return false;91 92 if (!Action.runInvocation(std::move(Invocation), std::move(FS),93 PCHContainerOps, Diags.getClient()))94 return false;95 96 std::vector<std::string> Args = Action.takeLastCC1Arguments();97 Consumer.handleBuildCommand({CommandLine[0], std::move(Args)});98 return true;99}100 101bool DependencyScanningWorker::scanDependencies(102 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,103 DependencyConsumer &Consumer, DependencyActionController &Controller,104 DiagnosticConsumer &DC,105 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {106 DignosticsEngineWithDiagOpts DiagEngineWithCmdAndOpts(CommandLine, FS, DC);107 DependencyScanningAction Action(Service, WorkingDirectory, Consumer,108 Controller, DepFS);109 110 bool Success = false;111 if (CommandLine[1] == "-cc1") {112 Success = createAndRunToolInvocation(113 CommandLine, Action, FS, PCHContainerOps,114 *DiagEngineWithCmdAndOpts.DiagEngine, Consumer);115 } else {116 Success = forEachDriverJob(117 CommandLine, *DiagEngineWithCmdAndOpts.DiagEngine, FS,118 [&](const driver::Command &Cmd) {119 if (StringRef(Cmd.getCreator().getName()) != "clang") {120 // Non-clang command. Just pass through to the dependency121 // consumer.122 Consumer.handleBuildCommand(123 {Cmd.getExecutable(),124 {Cmd.getArguments().begin(), Cmd.getArguments().end()}});125 return true;126 }127 128 // Insert -cc1 comand line options into Argv129 std::vector<std::string> Argv;130 Argv.push_back(Cmd.getExecutable());131 llvm::append_range(Argv, Cmd.getArguments());132 133 // Create an invocation that uses the underlying file134 // system to ensure that any file system requests that135 // are made by the driver do not go through the136 // dependency scanning filesystem.137 return createAndRunToolInvocation(138 std::move(Argv), Action, FS, PCHContainerOps,139 *DiagEngineWithCmdAndOpts.DiagEngine, Consumer);140 });141 }142 143 if (Success && !Action.hasScanned())144 DiagEngineWithCmdAndOpts.DiagEngine->Report(145 diag::err_fe_expected_compiler_job)146 << llvm::join(CommandLine, " ");147 148 // Ensure finish() is called even if we never reached ExecuteAction().149 if (!Action.hasDiagConsumerFinished())150 DC.finish();151 152 return Success && Action.hasScanned();153}154 155bool DependencyScanningWorker::computeDependencies(156 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,157 DependencyConsumer &Consumer, DependencyActionController &Controller,158 DiagnosticConsumer &DC, std::optional<llvm::MemoryBufferRef> TUBuffer) {159 if (TUBuffer) {160 auto [FinalFS, FinalCommandLine] = initVFSForTUBufferScanning(161 BaseFS, CommandLine, WorkingDirectory, *TUBuffer);162 return scanDependencies(WorkingDirectory, FinalCommandLine, Consumer,163 Controller, DC, FinalFS);164 } else {165 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);166 return scanDependencies(WorkingDirectory, CommandLine, Consumer, Controller,167 DC, BaseFS);168 }169}170 171llvm::Error172DependencyScanningWorker::initializeCompilerInstanceWithContextOrError(173 StringRef CWD, const std::vector<std::string> &CommandLine) {174 bool Success = initializeCompilerInstanceWithContext(CWD, CommandLine);175 return CIWithContext->handleReturnStatus(Success);176}177 178llvm::Error179DependencyScanningWorker::computeDependenciesByNameWithContextOrError(180 StringRef ModuleName, DependencyConsumer &Consumer,181 DependencyActionController &Controller) {182 bool Success =183 computeDependenciesByNameWithContext(ModuleName, Consumer, Controller);184 return CIWithContext->handleReturnStatus(Success);185}186 187llvm::Error188DependencyScanningWorker::finalizeCompilerInstanceWithContextOrError() {189 bool Success = finalizeCompilerInstance();190 return CIWithContext->handleReturnStatus(Success);191}192 193bool DependencyScanningWorker::initializeCompilerInstanceWithContext(194 StringRef CWD, const std::vector<std::string> &CommandLine,195 DiagnosticConsumer *DC) {196 CIWithContext =197 std::make_unique<CompilerInstanceWithContext>(*this, CWD, CommandLine);198 return CIWithContext->initialize(DC);199}200 201bool DependencyScanningWorker::computeDependenciesByNameWithContext(202 StringRef ModuleName, DependencyConsumer &Consumer,203 DependencyActionController &Controller) {204 assert(CIWithContext && "CompilerInstance with context required!");205 return CIWithContext->computeDependencies(ModuleName, Consumer, Controller);206}207 208bool DependencyScanningWorker::finalizeCompilerInstance() {209 return CIWithContext->finalize();210}211