Path: blob/main/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
35294 views
//===- DependencyScanningWorker.cpp - clang-scan-deps worker --------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"9#include "clang/Basic/DiagnosticDriver.h"10#include "clang/Basic/DiagnosticFrontend.h"11#include "clang/Basic/DiagnosticSerialization.h"12#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"13#include "clang/Driver/Compilation.h"14#include "clang/Driver/Driver.h"15#include "clang/Driver/Job.h"16#include "clang/Driver/Tool.h"17#include "clang/Frontend/CompilerInstance.h"18#include "clang/Frontend/CompilerInvocation.h"19#include "clang/Frontend/FrontendActions.h"20#include "clang/Frontend/TextDiagnosticPrinter.h"21#include "clang/Frontend/Utils.h"22#include "clang/Lex/PreprocessorOptions.h"23#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"24#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"25#include "clang/Tooling/Tooling.h"26#include "llvm/ADT/ScopeExit.h"27#include "llvm/Support/Allocator.h"28#include "llvm/Support/Error.h"29#include "llvm/TargetParser/Host.h"30#include <optional>3132using namespace clang;33using namespace tooling;34using namespace dependencies;3536namespace {3738/// Forwards the gatherered dependencies to the consumer.39class DependencyConsumerForwarder : public DependencyFileGenerator {40public:41DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,42StringRef WorkingDirectory, DependencyConsumer &C)43: DependencyFileGenerator(*Opts), WorkingDirectory(WorkingDirectory),44Opts(std::move(Opts)), C(C) {}4546void finishedMainFile(DiagnosticsEngine &Diags) override {47C.handleDependencyOutputOpts(*Opts);48llvm::SmallString<256> CanonPath;49for (const auto &File : getDependencies()) {50CanonPath = File;51llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);52llvm::sys::fs::make_absolute(WorkingDirectory, CanonPath);53C.handleFileDependency(CanonPath);54}55}5657private:58StringRef WorkingDirectory;59std::unique_ptr<DependencyOutputOptions> Opts;60DependencyConsumer &C;61};6263static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,64const HeaderSearchOptions &ExistingHSOpts,65DiagnosticsEngine *Diags,66const LangOptions &LangOpts) {67if (LangOpts.Modules) {68if (HSOpts.VFSOverlayFiles != ExistingHSOpts.VFSOverlayFiles) {69if (Diags) {70Diags->Report(diag::warn_pch_vfsoverlay_mismatch);71auto VFSNote = [&](int Type, ArrayRef<std::string> VFSOverlays) {72if (VFSOverlays.empty()) {73Diags->Report(diag::note_pch_vfsoverlay_empty) << Type;74} else {75std::string Files = llvm::join(VFSOverlays, "\n");76Diags->Report(diag::note_pch_vfsoverlay_files) << Type << Files;77}78};79VFSNote(0, HSOpts.VFSOverlayFiles);80VFSNote(1, ExistingHSOpts.VFSOverlayFiles);81}82}83}84return false;85}8687using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);8889/// A listener that collects the imported modules and optionally the input90/// files.91class PrebuiltModuleListener : public ASTReaderListener {92public:93PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,94llvm::SmallVector<std::string> &NewModuleFiles,95PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,96const HeaderSearchOptions &HSOpts,97const LangOptions &LangOpts, DiagnosticsEngine &Diags)98: PrebuiltModuleFiles(PrebuiltModuleFiles),99NewModuleFiles(NewModuleFiles),100PrebuiltModuleVFSMap(PrebuiltModuleVFSMap), ExistingHSOpts(HSOpts),101ExistingLangOpts(LangOpts), Diags(Diags) {}102103bool needsImportVisitation() const override { return true; }104105void visitImport(StringRef ModuleName, StringRef Filename) override {106if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)107NewModuleFiles.push_back(Filename.str());108}109110void visitModuleFile(StringRef Filename,111serialization::ModuleKind Kind) override {112CurrentFile = Filename;113}114115bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,116bool Complain) override {117std::vector<std::string> VFSOverlayFiles = HSOpts.VFSOverlayFiles;118PrebuiltModuleVFSMap.insert(119{CurrentFile, llvm::StringSet<>(VFSOverlayFiles)});120return checkHeaderSearchPaths(121HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts);122}123124private:125PrebuiltModuleFilesT &PrebuiltModuleFiles;126llvm::SmallVector<std::string> &NewModuleFiles;127PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap;128const HeaderSearchOptions &ExistingHSOpts;129const LangOptions &ExistingLangOpts;130DiagnosticsEngine &Diags;131std::string CurrentFile;132};133134/// Visit the given prebuilt module and collect all of the modules it135/// transitively imports and contributing input files.136static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,137CompilerInstance &CI,138PrebuiltModuleFilesT &ModuleFiles,139PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,140DiagnosticsEngine &Diags) {141// List of module files to be processed.142llvm::SmallVector<std::string> Worklist;143PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModuleVFSMap,144CI.getHeaderSearchOpts(), CI.getLangOpts(),145Diags);146147Listener.visitModuleFile(PrebuiltModuleFilename,148serialization::MK_ExplicitModule);149if (ASTReader::readASTFileControlBlock(150PrebuiltModuleFilename, CI.getFileManager(), CI.getModuleCache(),151CI.getPCHContainerReader(),152/*FindModuleFileExtensions=*/false, Listener,153/*ValidateDiagnosticOptions=*/false, ASTReader::ARR_OutOfDate))154return true;155156while (!Worklist.empty()) {157Listener.visitModuleFile(Worklist.back(), serialization::MK_ExplicitModule);158if (ASTReader::readASTFileControlBlock(159Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(),160CI.getPCHContainerReader(),161/*FindModuleFileExtensions=*/false, Listener,162/*ValidateDiagnosticOptions=*/false))163return true;164}165return false;166}167168/// Transform arbitrary file name into an object-like file name.169static std::string makeObjFileName(StringRef FileName) {170SmallString<128> ObjFileName(FileName);171llvm::sys::path::replace_extension(ObjFileName, "o");172return std::string(ObjFileName);173}174175/// Deduce the dependency target based on the output file and input files.176static std::string177deduceDepTarget(const std::string &OutputFile,178const SmallVectorImpl<FrontendInputFile> &InputFiles) {179if (OutputFile != "-")180return OutputFile;181182if (InputFiles.empty() || !InputFiles.front().isFile())183return "clang-scan-deps\\ dependency";184185return makeObjFileName(InputFiles.front().getFile());186}187188/// Sanitize diagnostic options for dependency scan.189static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {190// Don't print 'X warnings and Y errors generated'.191DiagOpts.ShowCarets = false;192// Don't write out diagnostic file.193DiagOpts.DiagnosticSerializationFile.clear();194// Don't emit warnings except for scanning specific warnings.195// TODO: It would be useful to add a more principled way to ignore all196// warnings that come from source code. The issue is that we need to197// ignore warnings that could be surpressed by198// `#pragma clang diagnostic`, while still allowing some scanning199// warnings for things we're not ready to turn into errors yet.200// See `test/ClangScanDeps/diagnostic-pragmas.c` for an example.201llvm::erase_if(DiagOpts.Warnings, [](StringRef Warning) {202return llvm::StringSwitch<bool>(Warning)203.Cases("pch-vfs-diff", "error=pch-vfs-diff", false)204.StartsWith("no-error=", false)205.Default(true);206});207}208209// Clang implements -D and -U by splatting text into a predefines buffer. This210// allows constructs such as `-DFඞ=3 "-D F\u{0D9E} 4 3 2”` to be accepted and211// define the same macro, or adding C++ style comments before the macro name.212//213// This function checks that the first non-space characters in the macro214// obviously form an identifier that can be uniqued on without lexing. Failing215// to do this could lead to changing the final definition of a macro.216//217// We could set up a preprocessor and actually lex the name, but that's very218// heavyweight for a situation that will almost never happen in practice.219static std::optional<StringRef> getSimpleMacroName(StringRef Macro) {220StringRef Name = Macro.split("=").first.ltrim(" \t");221std::size_t I = 0;222223auto FinishName = [&]() -> std::optional<StringRef> {224StringRef SimpleName = Name.slice(0, I);225if (SimpleName.empty())226return std::nullopt;227return SimpleName;228};229230for (; I != Name.size(); ++I) {231switch (Name[I]) {232case '(': // Start of macro parameter list233case ' ': // End of macro name234case '\t':235return FinishName();236case '_':237continue;238default:239if (llvm::isAlnum(Name[I]))240continue;241return std::nullopt;242}243}244return FinishName();245}246247static void canonicalizeDefines(PreprocessorOptions &PPOpts) {248using MacroOpt = std::pair<StringRef, std::size_t>;249std::vector<MacroOpt> SimpleNames;250SimpleNames.reserve(PPOpts.Macros.size());251std::size_t Index = 0;252for (const auto &M : PPOpts.Macros) {253auto SName = getSimpleMacroName(M.first);254// Skip optimizing if we can't guarantee we can preserve relative order.255if (!SName)256return;257SimpleNames.emplace_back(*SName, Index);258++Index;259}260261llvm::stable_sort(SimpleNames, llvm::less_first());262// Keep the last instance of each macro name by going in reverse263auto NewEnd = std::unique(264SimpleNames.rbegin(), SimpleNames.rend(),265[](const MacroOpt &A, const MacroOpt &B) { return A.first == B.first; });266SimpleNames.erase(SimpleNames.begin(), NewEnd.base());267268// Apply permutation.269decltype(PPOpts.Macros) NewMacros;270NewMacros.reserve(SimpleNames.size());271for (std::size_t I = 0, E = SimpleNames.size(); I != E; ++I) {272std::size_t OriginalIndex = SimpleNames[I].second;273// We still emit undefines here as they may be undefining a predefined macro274NewMacros.push_back(std::move(PPOpts.Macros[OriginalIndex]));275}276std::swap(PPOpts.Macros, NewMacros);277}278279/// A clang tool that runs the preprocessor in a mode that's optimized for280/// dependency scanning for the given compiler invocation.281class DependencyScanningAction : public tooling::ToolAction {282public:283DependencyScanningAction(284StringRef WorkingDirectory, DependencyConsumer &Consumer,285DependencyActionController &Controller,286llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,287ScanningOutputFormat Format, ScanningOptimizations OptimizeArgs,288bool EagerLoadModules, bool DisableFree,289std::optional<StringRef> ModuleName = std::nullopt)290: WorkingDirectory(WorkingDirectory), Consumer(Consumer),291Controller(Controller), DepFS(std::move(DepFS)), Format(Format),292OptimizeArgs(OptimizeArgs), EagerLoadModules(EagerLoadModules),293DisableFree(DisableFree), ModuleName(ModuleName) {}294295bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,296FileManager *DriverFileMgr,297std::shared_ptr<PCHContainerOperations> PCHContainerOps,298DiagnosticConsumer *DiagConsumer) override {299// Make a deep copy of the original Clang invocation.300CompilerInvocation OriginalInvocation(*Invocation);301// Restore the value of DisableFree, which may be modified by Tooling.302OriginalInvocation.getFrontendOpts().DisableFree = DisableFree;303if (any(OptimizeArgs & ScanningOptimizations::Macros))304canonicalizeDefines(OriginalInvocation.getPreprocessorOpts());305306if (Scanned) {307// Scanning runs once for the first -cc1 invocation in a chain of driver308// jobs. For any dependent jobs, reuse the scanning result and just309// update the LastCC1Arguments to correspond to the new invocation.310// FIXME: to support multi-arch builds, each arch requires a separate scan311setLastCC1Arguments(std::move(OriginalInvocation));312return true;313}314315Scanned = true;316317// Create a compiler instance to handle the actual work.318ScanInstanceStorage.emplace(std::move(PCHContainerOps));319CompilerInstance &ScanInstance = *ScanInstanceStorage;320ScanInstance.setInvocation(std::move(Invocation));321322// Create the compiler's actual diagnostics engine.323sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());324ScanInstance.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);325if (!ScanInstance.hasDiagnostics())326return false;327328// Some DiagnosticConsumers require that finish() is called.329auto DiagConsumerFinisher =330llvm::make_scope_exit([DiagConsumer]() { DiagConsumer->finish(); });331332ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath =333true;334335ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;336ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;337ScanInstance.getFrontendOpts().ModulesShareFileManager = false;338ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw";339ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage =340any(OptimizeArgs & ScanningOptimizations::VFS);341342// Support for virtual file system overlays.343auto FS = createVFSFromCompilerInvocation(344ScanInstance.getInvocation(), ScanInstance.getDiagnostics(),345DriverFileMgr->getVirtualFileSystemPtr());346347// Create a new FileManager to match the invocation's FileSystemOptions.348auto *FileMgr = ScanInstance.createFileManager(FS);349ScanInstance.createSourceManager(*FileMgr);350351// Store the list of prebuilt module files into header search options. This352// will prevent the implicit build to create duplicate modules and will353// force reuse of the existing prebuilt module files instead.354PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;355if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())356if (visitPrebuiltModule(357ScanInstance.getPreprocessorOpts().ImplicitPCHInclude,358ScanInstance,359ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,360PrebuiltModuleVFSMap, ScanInstance.getDiagnostics()))361return false;362363// Use the dependency scanning optimized file system if requested to do so.364if (DepFS)365ScanInstance.getPreprocessorOpts().DependencyDirectivesForFile =366[LocalDepFS = DepFS](FileEntryRef File)367-> std::optional<ArrayRef<dependency_directives_scan::Directive>> {368if (llvm::ErrorOr<EntryRef> Entry =369LocalDepFS->getOrCreateFileSystemEntry(File.getName()))370if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))371return Entry->getDirectiveTokens();372return std::nullopt;373};374375// Create the dependency collector that will collect the produced376// dependencies.377//378// This also moves the existing dependency output options from the379// invocation to the collector. The options in the invocation are reset,380// which ensures that the compiler won't create new dependency collectors,381// and thus won't write out the extra '.d' files to disk.382auto Opts = std::make_unique<DependencyOutputOptions>();383std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts());384// We need at least one -MT equivalent for the generator of make dependency385// files to work.386if (Opts->Targets.empty())387Opts->Targets = {388deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile,389ScanInstance.getFrontendOpts().Inputs)};390Opts->IncludeSystemHeaders = true;391392switch (Format) {393case ScanningOutputFormat::Make:394ScanInstance.addDependencyCollector(395std::make_shared<DependencyConsumerForwarder>(396std::move(Opts), WorkingDirectory, Consumer));397break;398case ScanningOutputFormat::P1689:399case ScanningOutputFormat::Full:400MDC = std::make_shared<ModuleDepCollector>(401std::move(Opts), ScanInstance, Consumer, Controller,402OriginalInvocation, std::move(PrebuiltModuleVFSMap), OptimizeArgs,403EagerLoadModules, Format == ScanningOutputFormat::P1689);404ScanInstance.addDependencyCollector(MDC);405break;406}407408// Consider different header search and diagnostic options to create409// different modules. This avoids the unsound aliasing of module PCMs.410//411// TODO: Implement diagnostic bucketing to reduce the impact of strict412// context hashing.413ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;414ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true;415ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true;416ScanInstance.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings =417true;418419// Avoid some checks and module map parsing when loading PCM files.420ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;421422std::unique_ptr<FrontendAction> Action;423424if (ModuleName)425Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);426else427Action = std::make_unique<ReadPCHAndPreprocessAction>();428429if (ScanInstance.getDiagnostics().hasErrorOccurred())430return false;431432// Each action is responsible for calling finish.433DiagConsumerFinisher.release();434const bool Result = ScanInstance.ExecuteAction(*Action);435436if (Result)437setLastCC1Arguments(std::move(OriginalInvocation));438439// Propagate the statistics to the parent FileManager.440DriverFileMgr->AddStats(ScanInstance.getFileManager());441442return Result;443}444445bool hasScanned() const { return Scanned; }446447/// Take the cc1 arguments corresponding to the most recent invocation used448/// with this action. Any modifications implied by the discovered dependencies449/// will have already been applied.450std::vector<std::string> takeLastCC1Arguments() {451std::vector<std::string> Result;452std::swap(Result, LastCC1Arguments); // Reset LastCC1Arguments to empty.453return Result;454}455456private:457void setLastCC1Arguments(CompilerInvocation &&CI) {458if (MDC)459MDC->applyDiscoveredDependencies(CI);460LastCC1Arguments = CI.getCC1CommandLine();461}462463private:464StringRef WorkingDirectory;465DependencyConsumer &Consumer;466DependencyActionController &Controller;467llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;468ScanningOutputFormat Format;469ScanningOptimizations OptimizeArgs;470bool EagerLoadModules;471bool DisableFree;472std::optional<StringRef> ModuleName;473std::optional<CompilerInstance> ScanInstanceStorage;474std::shared_ptr<ModuleDepCollector> MDC;475std::vector<std::string> LastCC1Arguments;476bool Scanned = false;477};478479} // end anonymous namespace480481DependencyScanningWorker::DependencyScanningWorker(482DependencyScanningService &Service,483llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)484: Format(Service.getFormat()), OptimizeArgs(Service.getOptimizeArgs()),485EagerLoadModules(Service.shouldEagerLoadModules()) {486PCHContainerOps = std::make_shared<PCHContainerOperations>();487// We need to read object files from PCH built outside the scanner.488PCHContainerOps->registerReader(489std::make_unique<ObjectFilePCHContainerReader>());490// The scanner itself writes only raw ast files.491PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());492493switch (Service.getMode()) {494case ScanningMode::DependencyDirectivesScan:495DepFS =496new DependencyScanningWorkerFilesystem(Service.getSharedCache(), FS);497BaseFS = DepFS;498break;499case ScanningMode::CanonicalPreprocessing:500DepFS = nullptr;501BaseFS = FS;502break;503}504}505506llvm::Error DependencyScanningWorker::computeDependencies(507StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,508DependencyConsumer &Consumer, DependencyActionController &Controller,509std::optional<StringRef> ModuleName) {510std::vector<const char *> CLI;511for (const std::string &Arg : CommandLine)512CLI.push_back(Arg.c_str());513auto DiagOpts = CreateAndPopulateDiagOpts(CLI);514sanitizeDiagOpts(*DiagOpts);515516// Capture the emitted diagnostics and report them to the client517// in the case of a failure.518std::string DiagnosticOutput;519llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);520TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.release());521522if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,523DiagPrinter, ModuleName))524return llvm::Error::success();525return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),526llvm::inconvertibleErrorCode());527}528529static bool forEachDriverJob(530ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, FileManager &FM,531llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {532SmallVector<const char *, 256> Argv;533Argv.reserve(ArgStrs.size());534for (const std::string &Arg : ArgStrs)535Argv.push_back(Arg.c_str());536537llvm::vfs::FileSystem *FS = &FM.getVirtualFileSystem();538539std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(540Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,541"clang LLVM compiler", FS);542Driver->setTitle("clang_based_tool");543544llvm::BumpPtrAllocator Alloc;545bool CLMode = driver::IsClangCL(546driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1)));547548if (llvm::Error E = driver::expandResponseFiles(Argv, CLMode, Alloc, FS)) {549Diags.Report(diag::err_drv_expand_response_file)550<< llvm::toString(std::move(E));551return false;552}553554const std::unique_ptr<driver::Compilation> Compilation(555Driver->BuildCompilation(llvm::ArrayRef(Argv)));556if (!Compilation)557return false;558559if (Compilation->containsError())560return false;561562for (const driver::Command &Job : Compilation->getJobs()) {563if (!Callback(Job))564return false;565}566return true;567}568569static bool createAndRunToolInvocation(570std::vector<std::string> CommandLine, DependencyScanningAction &Action,571FileManager &FM,572std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,573DiagnosticsEngine &Diags, DependencyConsumer &Consumer) {574575// Save executable path before providing CommandLine to ToolInvocation576std::string Executable = CommandLine[0];577ToolInvocation Invocation(std::move(CommandLine), &Action, &FM,578PCHContainerOps);579Invocation.setDiagnosticConsumer(Diags.getClient());580Invocation.setDiagnosticOptions(&Diags.getDiagnosticOptions());581if (!Invocation.run())582return false;583584std::vector<std::string> Args = Action.takeLastCC1Arguments();585Consumer.handleBuildCommand({std::move(Executable), std::move(Args)});586return true;587}588589bool DependencyScanningWorker::computeDependencies(590StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,591DependencyConsumer &Consumer, DependencyActionController &Controller,592DiagnosticConsumer &DC, std::optional<StringRef> ModuleName) {593// Reset what might have been modified in the previous worker invocation.594BaseFS->setCurrentWorkingDirectory(WorkingDirectory);595596std::optional<std::vector<std::string>> ModifiedCommandLine;597llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS;598599// If we're scanning based on a module name alone, we don't expect the client600// to provide us with an input file. However, the driver really wants to have601// one. Let's just make it up to make the driver happy.602if (ModuleName) {603auto OverlayFS =604llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);605auto InMemoryFS =606llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();607InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);608OverlayFS->pushOverlay(InMemoryFS);609ModifiedFS = OverlayFS;610611SmallString<128> FakeInputPath;612// TODO: We should retry the creation if the path already exists.613llvm::sys::fs::createUniquePath(*ModuleName + "-%%%%%%%%.input",614FakeInputPath,615/*MakeAbsolute=*/false);616InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));617618ModifiedCommandLine = CommandLine;619ModifiedCommandLine->emplace_back(FakeInputPath);620}621622const std::vector<std::string> &FinalCommandLine =623ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;624auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;625626auto FileMgr =627llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions{}, FinalFS);628629std::vector<const char *> FinalCCommandLine(FinalCommandLine.size(), nullptr);630llvm::transform(FinalCommandLine, FinalCCommandLine.begin(),631[](const std::string &Str) { return Str.c_str(); });632633auto DiagOpts = CreateAndPopulateDiagOpts(FinalCCommandLine);634sanitizeDiagOpts(*DiagOpts);635IntrusiveRefCntPtr<DiagnosticsEngine> Diags =636CompilerInstance::createDiagnostics(DiagOpts.release(), &DC,637/*ShouldOwnClient=*/false);638639// Although `Diagnostics` are used only for command-line parsing, the640// custom `DiagConsumer` might expect a `SourceManager` to be present.641SourceManager SrcMgr(*Diags, *FileMgr);642Diags->setSourceManager(&SrcMgr);643// DisableFree is modified by Tooling for running644// in-process; preserve the original value, which is645// always true for a driver invocation.646bool DisableFree = true;647DependencyScanningAction Action(WorkingDirectory, Consumer, Controller, DepFS,648Format, OptimizeArgs, EagerLoadModules,649DisableFree, ModuleName);650651bool Success = false;652if (FinalCommandLine[1] == "-cc1") {653Success = createAndRunToolInvocation(FinalCommandLine, Action, *FileMgr,654PCHContainerOps, *Diags, Consumer);655} else {656Success = forEachDriverJob(657FinalCommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) {658if (StringRef(Cmd.getCreator().getName()) != "clang") {659// Non-clang command. Just pass through to the dependency660// consumer.661Consumer.handleBuildCommand(662{Cmd.getExecutable(),663{Cmd.getArguments().begin(), Cmd.getArguments().end()}});664return true;665}666667// Insert -cc1 comand line options into Argv668std::vector<std::string> Argv;669Argv.push_back(Cmd.getExecutable());670Argv.insert(Argv.end(), Cmd.getArguments().begin(),671Cmd.getArguments().end());672673// Create an invocation that uses the underlying file674// system to ensure that any file system requests that675// are made by the driver do not go through the676// dependency scanning filesystem.677return createAndRunToolInvocation(std::move(Argv), Action, *FileMgr,678PCHContainerOps, *Diags, Consumer);679});680}681682if (Success && !Action.hasScanned())683Diags->Report(diag::err_fe_expected_compiler_job)684<< llvm::join(FinalCommandLine, " ");685return Success && Action.hasScanned();686}687688DependencyActionController::~DependencyActionController() {}689690691