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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" 10 #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" 11 #include "clang/Frontend/CompilerInstance.h" 12 #include "clang/Frontend/CompilerInvocation.h" 13 #include "clang/Frontend/FrontendActions.h" 14 #include "clang/Frontend/TextDiagnosticPrinter.h" 15 #include "clang/Frontend/Utils.h" 16 #include "clang/Lex/PreprocessorOptions.h" 17 #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" 18 #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" 19 #include "clang/Tooling/Tooling.h" 20 21 using namespace clang; 22 using namespace tooling; 23 using namespace dependencies; 24 25 namespace { 26 27 /// Forwards the gatherered dependencies to the consumer. 28 class DependencyConsumerForwarder : public DependencyFileGenerator { 29 public: 30 DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts, 31 DependencyConsumer &C) 32 : DependencyFileGenerator(*Opts), Opts(std::move(Opts)), C(C) {} 33 34 void finishedMainFile(DiagnosticsEngine &Diags) override { 35 llvm::SmallString<256> CanonPath; 36 for (const auto &File : getDependencies()) { 37 CanonPath = File; 38 llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true); 39 C.handleFileDependency(*Opts, CanonPath); 40 } 41 } 42 43 private: 44 std::unique_ptr<DependencyOutputOptions> Opts; 45 DependencyConsumer &C; 46 }; 47 48 /// A clang tool that runs the preprocessor in a mode that's optimized for 49 /// dependency scanning for the given compiler invocation. 50 class DependencyScanningAction : public tooling::ToolAction { 51 public: 52 DependencyScanningAction( 53 StringRef WorkingDirectory, DependencyConsumer &Consumer, 54 llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS, 55 ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings, 56 ScanningOutputFormat Format) 57 : WorkingDirectory(WorkingDirectory), Consumer(Consumer), 58 DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings), 59 Format(Format) {} 60 61 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation, 62 FileManager *FileMgr, 63 std::shared_ptr<PCHContainerOperations> PCHContainerOps, 64 DiagnosticConsumer *DiagConsumer) override { 65 // Create a compiler instance to handle the actual work. 66 CompilerInstance Compiler(std::move(PCHContainerOps)); 67 Compiler.setInvocation(std::move(Invocation)); 68 69 // Don't print 'X warnings and Y errors generated'. 70 Compiler.getDiagnosticOpts().ShowCarets = false; 71 // Create the compiler's actual diagnostics engine. 72 Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false); 73 if (!Compiler.hasDiagnostics()) 74 return false; 75 76 // Use the dependency scanning optimized file system if we can. 77 if (DepFS) { 78 const CompilerInvocation &CI = Compiler.getInvocation(); 79 // Add any filenames that were explicity passed in the build settings and 80 // that might be opened, as we want to ensure we don't run source 81 // minimization on them. 82 DepFS->IgnoredFiles.clear(); 83 for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) 84 DepFS->IgnoredFiles.insert(Entry.Path); 85 for (const auto &Entry : CI.getHeaderSearchOpts().VFSOverlayFiles) 86 DepFS->IgnoredFiles.insert(Entry); 87 88 // Support for virtual file system overlays on top of the caching 89 // filesystem. 90 FileMgr->setVirtualFileSystem(createVFSFromCompilerInvocation( 91 CI, Compiler.getDiagnostics(), DepFS)); 92 93 // Pass the skip mappings which should speed up excluded conditional block 94 // skipping in the preprocessor. 95 if (PPSkipMappings) 96 Compiler.getPreprocessorOpts() 97 .ExcludedConditionalDirectiveSkipMappings = PPSkipMappings; 98 } 99 100 FileMgr->getFileSystemOpts().WorkingDir = std::string(WorkingDirectory); 101 Compiler.setFileManager(FileMgr); 102 Compiler.createSourceManager(*FileMgr); 103 104 // Create the dependency collector that will collect the produced 105 // dependencies. 106 // 107 // This also moves the existing dependency output options from the 108 // invocation to the collector. The options in the invocation are reset, 109 // which ensures that the compiler won't create new dependency collectors, 110 // and thus won't write out the extra '.d' files to disk. 111 auto Opts = std::make_unique<DependencyOutputOptions>( 112 std::move(Compiler.getInvocation().getDependencyOutputOpts())); 113 // We need at least one -MT equivalent for the generator to work. 114 if (Opts->Targets.empty()) 115 Opts->Targets = {"clang-scan-deps dependency"}; 116 117 switch (Format) { 118 case ScanningOutputFormat::Make: 119 Compiler.addDependencyCollector( 120 std::make_shared<DependencyConsumerForwarder>(std::move(Opts), 121 Consumer)); 122 break; 123 case ScanningOutputFormat::Full: 124 Compiler.addDependencyCollector(std::make_shared<ModuleDepCollector>( 125 std::move(Opts), Compiler, Consumer)); 126 break; 127 } 128 129 // Consider different header search and diagnostic options to create 130 // different modules. This avoids the unsound aliasing of module PCMs. 131 // 132 // TODO: Implement diagnostic bucketing and header search pruning to reduce 133 // the impact of strict context hashing. 134 Compiler.getHeaderSearchOpts().ModulesStrictContextHash = true; 135 136 auto Action = std::make_unique<PreprocessOnlyAction>(); 137 const bool Result = Compiler.ExecuteAction(*Action); 138 if (!DepFS) 139 FileMgr->clearStatCache(); 140 return Result; 141 } 142 143 private: 144 StringRef WorkingDirectory; 145 DependencyConsumer &Consumer; 146 llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS; 147 ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings; 148 ScanningOutputFormat Format; 149 }; 150 151 } // end anonymous namespace 152 153 DependencyScanningWorker::DependencyScanningWorker( 154 DependencyScanningService &Service) 155 : Format(Service.getFormat()) { 156 DiagOpts = new DiagnosticOptions(); 157 158 PCHContainerOps = std::make_shared<PCHContainerOperations>(); 159 PCHContainerOps->registerReader( 160 std::make_unique<ObjectFilePCHContainerReader>()); 161 // We don't need to write object files, but the current PCH implementation 162 // requires the writer to be registered as well. 163 PCHContainerOps->registerWriter( 164 std::make_unique<ObjectFilePCHContainerWriter>()); 165 166 RealFS = llvm::vfs::createPhysicalFileSystem(); 167 if (Service.canSkipExcludedPPRanges()) 168 PPSkipMappings = 169 std::make_unique<ExcludedPreprocessorDirectiveSkipMapping>(); 170 if (Service.getMode() == ScanningMode::MinimizedSourcePreprocessing) 171 DepFS = new DependencyScanningWorkerFilesystem( 172 Service.getSharedCache(), RealFS, PPSkipMappings.get()); 173 if (Service.canReuseFileManager()) 174 Files = new FileManager(FileSystemOptions(), RealFS); 175 } 176 177 static llvm::Error runWithDiags( 178 DiagnosticOptions *DiagOpts, 179 llvm::function_ref<bool(DiagnosticConsumer &DC)> BodyShouldSucceed) { 180 // Capture the emitted diagnostics and report them to the client 181 // in the case of a failure. 182 std::string DiagnosticOutput; 183 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput); 184 TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts); 185 186 if (BodyShouldSucceed(DiagPrinter)) 187 return llvm::Error::success(); 188 return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(), 189 llvm::inconvertibleErrorCode()); 190 } 191 192 llvm::Error DependencyScanningWorker::computeDependencies( 193 const std::string &Input, StringRef WorkingDirectory, 194 const CompilationDatabase &CDB, DependencyConsumer &Consumer) { 195 RealFS->setCurrentWorkingDirectory(WorkingDirectory); 196 return runWithDiags(DiagOpts.get(), [&](DiagnosticConsumer &DC) { 197 /// Create the tool that uses the underlying file system to ensure that any 198 /// file system requests that are made by the driver do not go through the 199 /// dependency scanning filesystem. 200 tooling::ClangTool Tool(CDB, Input, PCHContainerOps, RealFS, Files); 201 Tool.clearArgumentsAdjusters(); 202 Tool.setRestoreWorkingDir(false); 203 Tool.setPrintErrorMessage(false); 204 Tool.setDiagnosticConsumer(&DC); 205 DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS, 206 PPSkipMappings.get(), Format); 207 return !Tool.run(&Action); 208 }); 209 } 210