//===- DependencyScannerImpl.h - Implements dependency scanning *- C++ -*--===// // // 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 // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNER_H #define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNER_H #include "clang/Driver/Compilation.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Serialization/ObjectFilePCHContainerReader.h" #include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h" #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" namespace clang { class DiagnosticConsumer; namespace tooling { namespace dependencies { class DependencyScanningService; class DependencyScanningWorker; class DependencyConsumer; class DependencyActionController; class DependencyScanningWorkerFilesystem; class DependencyScanningAction { public: DependencyScanningAction( DependencyScanningService &Service, StringRef WorkingDirectory, DependencyConsumer &Consumer, DependencyActionController &Controller, IntrusiveRefCntPtr DepFS, std::optional ModuleName = std::nullopt) : Service(Service), WorkingDirectory(WorkingDirectory), Consumer(Consumer), Controller(Controller), DepFS(std::move(DepFS)) {} bool runInvocation(std::unique_ptr Invocation, IntrusiveRefCntPtr FS, std::shared_ptr PCHContainerOps, DiagnosticConsumer *DiagConsumer); bool hasScanned() const { return Scanned; } bool hasDiagConsumerFinished() const { return DiagConsumerFinished; } /// Take the cc1 arguments corresponding to the most recent invocation used /// with this action. Any modifications implied by the discovered dependencies /// will have already been applied. std::vector takeLastCC1Arguments() { std::vector Result; std::swap(Result, LastCC1Arguments); // Reset LastCC1Arguments to empty. return Result; } private: void setLastCC1Arguments(CompilerInvocation &&CI) { if (MDC) MDC->applyDiscoveredDependencies(CI); LastCC1Arguments = CI.getCC1CommandLine(); } DependencyScanningService &Service; StringRef WorkingDirectory; DependencyConsumer &Consumer; DependencyActionController &Controller; IntrusiveRefCntPtr DepFS; std::optional ScanInstanceStorage; std::shared_ptr MDC; std::vector LastCC1Arguments; bool Scanned = false; bool DiagConsumerFinished = false; }; // Helper functions and data types. std::unique_ptr createDiagOptions(ArrayRef CommandLine); struct DignosticsEngineWithDiagOpts { // We need to bound the lifetime of the DiagOpts used to create the // DiganosticsEngine with the DiagnosticsEngine itself. std::unique_ptr DiagOpts; IntrusiveRefCntPtr DiagEngine; DignosticsEngineWithDiagOpts(ArrayRef CommandLine, IntrusiveRefCntPtr FS, DiagnosticConsumer &DC); }; struct TextDiagnosticsPrinterWithOutput { // We need to bound the lifetime of the data that supports the DiagPrinter // with it together so they have the same lifetime. std::string DiagnosticOutput; llvm::raw_string_ostream DiagnosticsOS; std::unique_ptr DiagOpts; TextDiagnosticPrinter DiagPrinter; TextDiagnosticsPrinterWithOutput(ArrayRef CommandLine) : DiagnosticsOS(DiagnosticOutput), DiagOpts(createDiagOptions(CommandLine)), DiagPrinter(DiagnosticsOS, *DiagOpts) {} }; std::pair, std::unique_ptr> buildCompilation(ArrayRef ArgStrs, DiagnosticsEngine &Diags, IntrusiveRefCntPtr FS, llvm::BumpPtrAllocator &Alloc); std::unique_ptr createCompilerInvocation(ArrayRef CommandLine, DiagnosticsEngine &Diags); std::pair, std::vector> initVFSForTUBufferScanning(IntrusiveRefCntPtr BaseFS, ArrayRef CommandLine, StringRef WorkingDirectory, llvm::MemoryBufferRef TUBuffer); std::pair, std::vector> initVFSForByNameScanning(IntrusiveRefCntPtr BaseFS, ArrayRef CommandLine, StringRef WorkingDirectory, StringRef ModuleName); bool initializeScanCompilerInstance( CompilerInstance &ScanInstance, IntrusiveRefCntPtr FS, DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service, IntrusiveRefCntPtr DepFS); SmallVector getInitialStableDirs(const CompilerInstance &ScanInstance); std::optional computePrebuiltModulesASTMap(CompilerInstance &ScanInstance, SmallVector &StableDirs); std::unique_ptr takeAndUpdateDependencyOutputOptionsFrom(CompilerInstance &ScanInstance); /// Create the dependency collector that will collect the produced /// dependencies. May return the created ModuleDepCollector depending /// on the scanning format. std::shared_ptr initializeScanInstanceDependencyCollector( CompilerInstance &ScanInstance, std::unique_ptr DepOutputOpts, StringRef WorkingDirectory, DependencyConsumer &Consumer, DependencyScanningService &Service, CompilerInvocation &Inv, DependencyActionController &Controller, PrebuiltModulesAttrsMap PrebuiltModulesASTMap, llvm::SmallVector &StableDirs); class CompilerInstanceWithContext { // Context DependencyScanningWorker &Worker; llvm::StringRef CWD; std::vector CommandLine; // Context - file systems llvm::IntrusiveRefCntPtr OverlayFS; // Context - Diagnostics engine. std::unique_ptr DiagPrinterWithOS; // DiagConsumer may points to DiagPrinterWithOS->DiagPrinter, or a custom // DiagnosticConsumer passed in from initialize. DiagnosticConsumer *DiagConsumer = nullptr; std::unique_ptr DiagEngineWithCmdAndOpts; // Context - compiler invocation // Compilation's command's arguments may be owned by Alloc when expanded from // response files, so we need to keep Alloc alive in the context. llvm::BumpPtrAllocator Alloc; std::unique_ptr Driver; std::unique_ptr Compilation; std::unique_ptr OriginalInvocation; // Context - output options std::unique_ptr OutputOpts; // Context - stable directory handling llvm::SmallVector StableDirs; PrebuiltModulesAttrsMap PrebuiltModuleASTMap; // Compiler Instance std::unique_ptr CIPtr; // Source location offset. int32_t SrcLocOffset = 0; public: CompilerInstanceWithContext(DependencyScanningWorker &Worker, StringRef CWD, const std::vector &CMD) : Worker(Worker), CWD(CWD), CommandLine(CMD) {}; // The three methods below returns false when they fail, with the detail // accumulated in DiagConsumer. bool initialize(DiagnosticConsumer *DC); bool computeDependencies(StringRef ModuleName, DependencyConsumer &Consumer, DependencyActionController &Controller); bool finalize(); // The method below turns the return status from the above methods // into an llvm::Error using a default DiagnosticConsumer. llvm::Error handleReturnStatus(bool Success); }; } // namespace dependencies } // namespace tooling } // namespace clang #endif