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/Basic/DiagnosticDriver.h" 11 #include "clang/Basic/DiagnosticFrontend.h" 12 #include "clang/Basic/DiagnosticSerialization.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/Serialization/ObjectFilePCHContainerReader.h" 24 #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" 25 #include "clang/Tooling/DependencyScanning/InProcessModuleCache.h" 26 #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" 27 #include "clang/Tooling/Tooling.h" 28 #include "llvm/ADT/IntrusiveRefCntPtr.h" 29 #include "llvm/Support/Allocator.h" 30 #include "llvm/Support/Error.h" 31 #include "llvm/Support/MemoryBuffer.h" 32 #include "llvm/TargetParser/Host.h" 33 #include <optional> 34 35 using namespace clang; 36 using namespace tooling; 37 using namespace dependencies; 38 39 namespace { 40 41 /// Forwards the gatherered dependencies to the consumer. 42 class DependencyConsumerForwarder : public DependencyFileGenerator { 43 public: 44 DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts, 45 StringRef WorkingDirectory, DependencyConsumer &C) 46 : DependencyFileGenerator(*Opts), WorkingDirectory(WorkingDirectory), 47 Opts(std::move(Opts)), C(C) {} 48 49 void finishedMainFile(DiagnosticsEngine &Diags) override { 50 C.handleDependencyOutputOpts(*Opts); 51 llvm::SmallString<256> CanonPath; 52 for (const auto &File : getDependencies()) { 53 CanonPath = File; 54 llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true); 55 llvm::sys::fs::make_absolute(WorkingDirectory, CanonPath); 56 C.handleFileDependency(CanonPath); 57 } 58 } 59 60 private: 61 StringRef WorkingDirectory; 62 std::unique_ptr<DependencyOutputOptions> Opts; 63 DependencyConsumer &C; 64 }; 65 66 static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts, 67 const HeaderSearchOptions &ExistingHSOpts, 68 DiagnosticsEngine *Diags, 69 const LangOptions &LangOpts) { 70 if (LangOpts.Modules) { 71 if (HSOpts.VFSOverlayFiles != ExistingHSOpts.VFSOverlayFiles) { 72 if (Diags) { 73 Diags->Report(diag::warn_pch_vfsoverlay_mismatch); 74 auto VFSNote = [&](int Type, ArrayRef<std::string> VFSOverlays) { 75 if (VFSOverlays.empty()) { 76 Diags->Report(diag::note_pch_vfsoverlay_empty) << Type; 77 } else { 78 std::string Files = llvm::join(VFSOverlays, "\n"); 79 Diags->Report(diag::note_pch_vfsoverlay_files) << Type << Files; 80 } 81 }; 82 VFSNote(0, HSOpts.VFSOverlayFiles); 83 VFSNote(1, ExistingHSOpts.VFSOverlayFiles); 84 } 85 } 86 } 87 return false; 88 } 89 90 using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles); 91 92 /// A listener that collects the imported modules and the input 93 /// files. While visiting, collect vfsoverlays and file inputs that determine 94 /// whether prebuilt modules fully resolve in stable directories. 95 class PrebuiltModuleListener : public ASTReaderListener { 96 public: 97 PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles, 98 llvm::SmallVector<std::string> &NewModuleFiles, 99 PrebuiltModulesAttrsMap &PrebuiltModulesASTMap, 100 const HeaderSearchOptions &HSOpts, 101 const LangOptions &LangOpts, DiagnosticsEngine &Diags, 102 const ArrayRef<StringRef> StableDirs) 103 : PrebuiltModuleFiles(PrebuiltModuleFiles), 104 NewModuleFiles(NewModuleFiles), 105 PrebuiltModulesASTMap(PrebuiltModulesASTMap), ExistingHSOpts(HSOpts), 106 ExistingLangOpts(LangOpts), Diags(Diags), StableDirs(StableDirs) {} 107 108 bool needsImportVisitation() const override { return true; } 109 bool needsInputFileVisitation() override { return true; } 110 bool needsSystemInputFileVisitation() override { return true; } 111 112 /// Accumulate the modules are transitively depended on by the initial 113 /// prebuilt module. 114 void visitImport(StringRef ModuleName, StringRef Filename) override { 115 if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second) 116 NewModuleFiles.push_back(Filename.str()); 117 118 auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(Filename); 119 PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second; 120 if (PrebuiltMapEntry.second) 121 PrebuiltModule.setInStableDir(!StableDirs.empty()); 122 123 if (auto It = PrebuiltModulesASTMap.find(CurrentFile); 124 It != PrebuiltModulesASTMap.end() && CurrentFile != Filename) 125 PrebuiltModule.addDependent(It->getKey()); 126 } 127 128 /// For each input file discovered, check whether it's external path is in a 129 /// stable directory. Traversal is stopped if the current module is not 130 /// considered stable. 131 bool visitInputFile(StringRef FilenameAsRequested, StringRef Filename, 132 bool isSystem, bool isOverridden, 133 bool isExplicitModule) override { 134 if (StableDirs.empty()) 135 return false; 136 auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile); 137 if ((PrebuiltEntryIt == PrebuiltModulesASTMap.end()) || 138 (!PrebuiltEntryIt->second.isInStableDir())) 139 return false; 140 141 PrebuiltEntryIt->second.setInStableDir( 142 isPathInStableDir(StableDirs, Filename)); 143 return PrebuiltEntryIt->second.isInStableDir(); 144 } 145 146 /// Update which module that is being actively traversed. 147 void visitModuleFile(StringRef Filename, 148 serialization::ModuleKind Kind) override { 149 // If the CurrentFile is not 150 // considered stable, update any of it's transitive dependents. 151 auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile); 152 if ((PrebuiltEntryIt != PrebuiltModulesASTMap.end()) && 153 !PrebuiltEntryIt->second.isInStableDir()) 154 PrebuiltEntryIt->second.updateDependentsNotInStableDirs( 155 PrebuiltModulesASTMap); 156 CurrentFile = Filename; 157 } 158 159 /// Check the header search options for a given module when considering 160 /// if the module comes from stable directories. 161 bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts, 162 StringRef ModuleFilename, 163 StringRef SpecificModuleCachePath, 164 bool Complain) override { 165 166 auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile); 167 PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second; 168 if (PrebuiltMapEntry.second) 169 PrebuiltModule.setInStableDir(!StableDirs.empty()); 170 171 if (PrebuiltModule.isInStableDir()) 172 PrebuiltModule.setInStableDir(areOptionsInStableDir(StableDirs, HSOpts)); 173 174 return false; 175 } 176 177 /// Accumulate vfsoverlays used to build these prebuilt modules. 178 bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts, 179 bool Complain) override { 180 181 auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile); 182 PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second; 183 if (PrebuiltMapEntry.second) 184 PrebuiltModule.setInStableDir(!StableDirs.empty()); 185 186 PrebuiltModule.setVFS( 187 llvm::StringSet<>(llvm::from_range, HSOpts.VFSOverlayFiles)); 188 189 return checkHeaderSearchPaths( 190 HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts); 191 } 192 193 private: 194 PrebuiltModuleFilesT &PrebuiltModuleFiles; 195 llvm::SmallVector<std::string> &NewModuleFiles; 196 PrebuiltModulesAttrsMap &PrebuiltModulesASTMap; 197 const HeaderSearchOptions &ExistingHSOpts; 198 const LangOptions &ExistingLangOpts; 199 DiagnosticsEngine &Diags; 200 std::string CurrentFile; 201 const ArrayRef<StringRef> StableDirs; 202 }; 203 204 /// Visit the given prebuilt module and collect all of the modules it 205 /// transitively imports and contributing input files. 206 static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename, 207 CompilerInstance &CI, 208 PrebuiltModuleFilesT &ModuleFiles, 209 PrebuiltModulesAttrsMap &PrebuiltModulesASTMap, 210 DiagnosticsEngine &Diags, 211 const ArrayRef<StringRef> StableDirs) { 212 // List of module files to be processed. 213 llvm::SmallVector<std::string> Worklist; 214 215 PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModulesASTMap, 216 CI.getHeaderSearchOpts(), CI.getLangOpts(), 217 Diags, StableDirs); 218 219 Listener.visitModuleFile(PrebuiltModuleFilename, 220 serialization::MK_ExplicitModule); 221 if (ASTReader::readASTFileControlBlock( 222 PrebuiltModuleFilename, CI.getFileManager(), CI.getModuleCache(), 223 CI.getPCHContainerReader(), 224 /*FindModuleFileExtensions=*/false, Listener, 225 /*ValidateDiagnosticOptions=*/false, ASTReader::ARR_OutOfDate)) 226 return true; 227 228 while (!Worklist.empty()) { 229 Listener.visitModuleFile(Worklist.back(), serialization::MK_ExplicitModule); 230 if (ASTReader::readASTFileControlBlock( 231 Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(), 232 CI.getPCHContainerReader(), 233 /*FindModuleFileExtensions=*/false, Listener, 234 /*ValidateDiagnosticOptions=*/false)) 235 return true; 236 } 237 return false; 238 } 239 240 /// Transform arbitrary file name into an object-like file name. 241 static std::string makeObjFileName(StringRef FileName) { 242 SmallString<128> ObjFileName(FileName); 243 llvm::sys::path::replace_extension(ObjFileName, "o"); 244 return std::string(ObjFileName); 245 } 246 247 /// Deduce the dependency target based on the output file and input files. 248 static std::string 249 deduceDepTarget(const std::string &OutputFile, 250 const SmallVectorImpl<FrontendInputFile> &InputFiles) { 251 if (OutputFile != "-") 252 return OutputFile; 253 254 if (InputFiles.empty() || !InputFiles.front().isFile()) 255 return "clang-scan-deps\\ dependency"; 256 257 return makeObjFileName(InputFiles.front().getFile()); 258 } 259 260 /// Sanitize diagnostic options for dependency scan. 261 static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) { 262 // Don't print 'X warnings and Y errors generated'. 263 DiagOpts.ShowCarets = false; 264 // Don't write out diagnostic file. 265 DiagOpts.DiagnosticSerializationFile.clear(); 266 // Don't emit warnings except for scanning specific warnings. 267 // TODO: It would be useful to add a more principled way to ignore all 268 // warnings that come from source code. The issue is that we need to 269 // ignore warnings that could be surpressed by 270 // `#pragma clang diagnostic`, while still allowing some scanning 271 // warnings for things we're not ready to turn into errors yet. 272 // See `test/ClangScanDeps/diagnostic-pragmas.c` for an example. 273 llvm::erase_if(DiagOpts.Warnings, [](StringRef Warning) { 274 return llvm::StringSwitch<bool>(Warning) 275 .Cases("pch-vfs-diff", "error=pch-vfs-diff", false) 276 .StartsWith("no-error=", false) 277 .Default(true); 278 }); 279 } 280 281 // Clang implements -D and -U by splatting text into a predefines buffer. This 282 // allows constructs such as `-DFඞ=3 "-D F\u{0D9E} 4 3 2”` to be accepted and 283 // define the same macro, or adding C++ style comments before the macro name. 284 // 285 // This function checks that the first non-space characters in the macro 286 // obviously form an identifier that can be uniqued on without lexing. Failing 287 // to do this could lead to changing the final definition of a macro. 288 // 289 // We could set up a preprocessor and actually lex the name, but that's very 290 // heavyweight for a situation that will almost never happen in practice. 291 static std::optional<StringRef> getSimpleMacroName(StringRef Macro) { 292 StringRef Name = Macro.split("=").first.ltrim(" \t"); 293 std::size_t I = 0; 294 295 auto FinishName = [&]() -> std::optional<StringRef> { 296 StringRef SimpleName = Name.slice(0, I); 297 if (SimpleName.empty()) 298 return std::nullopt; 299 return SimpleName; 300 }; 301 302 for (; I != Name.size(); ++I) { 303 switch (Name[I]) { 304 case '(': // Start of macro parameter list 305 case ' ': // End of macro name 306 case '\t': 307 return FinishName(); 308 case '_': 309 continue; 310 default: 311 if (llvm::isAlnum(Name[I])) 312 continue; 313 return std::nullopt; 314 } 315 } 316 return FinishName(); 317 } 318 319 static void canonicalizeDefines(PreprocessorOptions &PPOpts) { 320 using MacroOpt = std::pair<StringRef, std::size_t>; 321 std::vector<MacroOpt> SimpleNames; 322 SimpleNames.reserve(PPOpts.Macros.size()); 323 std::size_t Index = 0; 324 for (const auto &M : PPOpts.Macros) { 325 auto SName = getSimpleMacroName(M.first); 326 // Skip optimizing if we can't guarantee we can preserve relative order. 327 if (!SName) 328 return; 329 SimpleNames.emplace_back(*SName, Index); 330 ++Index; 331 } 332 333 llvm::stable_sort(SimpleNames, llvm::less_first()); 334 // Keep the last instance of each macro name by going in reverse 335 auto NewEnd = std::unique( 336 SimpleNames.rbegin(), SimpleNames.rend(), 337 [](const MacroOpt &A, const MacroOpt &B) { return A.first == B.first; }); 338 SimpleNames.erase(SimpleNames.begin(), NewEnd.base()); 339 340 // Apply permutation. 341 decltype(PPOpts.Macros) NewMacros; 342 NewMacros.reserve(SimpleNames.size()); 343 for (std::size_t I = 0, E = SimpleNames.size(); I != E; ++I) { 344 std::size_t OriginalIndex = SimpleNames[I].second; 345 // We still emit undefines here as they may be undefining a predefined macro 346 NewMacros.push_back(std::move(PPOpts.Macros[OriginalIndex])); 347 } 348 std::swap(PPOpts.Macros, NewMacros); 349 } 350 351 class ScanningDependencyDirectivesGetter : public DependencyDirectivesGetter { 352 DependencyScanningWorkerFilesystem *DepFS; 353 354 public: 355 ScanningDependencyDirectivesGetter(FileManager &FileMgr) : DepFS(nullptr) { 356 FileMgr.getVirtualFileSystem().visit([&](llvm::vfs::FileSystem &FS) { 357 auto *DFS = llvm::dyn_cast<DependencyScanningWorkerFilesystem>(&FS); 358 if (DFS) { 359 assert(!DepFS && "Found multiple scanning VFSs"); 360 DepFS = DFS; 361 } 362 }); 363 assert(DepFS && "Did not find scanning VFS"); 364 } 365 366 std::unique_ptr<DependencyDirectivesGetter> 367 cloneFor(FileManager &FileMgr) override { 368 return std::make_unique<ScanningDependencyDirectivesGetter>(FileMgr); 369 } 370 371 std::optional<ArrayRef<dependency_directives_scan::Directive>> 372 operator()(FileEntryRef File) override { 373 return DepFS->getDirectiveTokens(File.getName()); 374 } 375 }; 376 377 /// A clang tool that runs the preprocessor in a mode that's optimized for 378 /// dependency scanning for the given compiler invocation. 379 class DependencyScanningAction : public tooling::ToolAction { 380 public: 381 DependencyScanningAction( 382 DependencyScanningService &Service, StringRef WorkingDirectory, 383 DependencyConsumer &Consumer, DependencyActionController &Controller, 384 llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS, 385 bool DisableFree, std::optional<StringRef> ModuleName = std::nullopt) 386 : Service(Service), WorkingDirectory(WorkingDirectory), 387 Consumer(Consumer), Controller(Controller), DepFS(std::move(DepFS)), 388 DisableFree(DisableFree), ModuleName(ModuleName) {} 389 390 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation, 391 FileManager *DriverFileMgr, 392 std::shared_ptr<PCHContainerOperations> PCHContainerOps, 393 DiagnosticConsumer *DiagConsumer) override { 394 // Make a deep copy of the original Clang invocation. 395 CompilerInvocation OriginalInvocation(*Invocation); 396 // Restore the value of DisableFree, which may be modified by Tooling. 397 OriginalInvocation.getFrontendOpts().DisableFree = DisableFree; 398 if (any(Service.getOptimizeArgs() & ScanningOptimizations::Macros)) 399 canonicalizeDefines(OriginalInvocation.getPreprocessorOpts()); 400 401 if (Scanned) { 402 // Scanning runs once for the first -cc1 invocation in a chain of driver 403 // jobs. For any dependent jobs, reuse the scanning result and just 404 // update the LastCC1Arguments to correspond to the new invocation. 405 // FIXME: to support multi-arch builds, each arch requires a separate scan 406 setLastCC1Arguments(std::move(OriginalInvocation)); 407 return true; 408 } 409 410 Scanned = true; 411 412 // Create a compiler instance to handle the actual work. 413 auto ModCache = makeInProcessModuleCache(Service.getModuleCacheEntries()); 414 ScanInstanceStorage.emplace(std::move(Invocation), 415 std::move(PCHContainerOps), ModCache.get()); 416 CompilerInstance &ScanInstance = *ScanInstanceStorage; 417 ScanInstance.setBuildingModule(false); 418 419 // Create the compiler's actual diagnostics engine. 420 sanitizeDiagOpts(ScanInstance.getDiagnosticOpts()); 421 assert(!DiagConsumerFinished && "attempt to reuse finished consumer"); 422 ScanInstance.createDiagnostics(DriverFileMgr->getVirtualFileSystem(), 423 DiagConsumer, /*ShouldOwnClient=*/false); 424 if (!ScanInstance.hasDiagnostics()) 425 return false; 426 427 ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath = 428 true; 429 430 if (ScanInstance.getHeaderSearchOpts().ModulesValidateOncePerBuildSession) 431 ScanInstance.getHeaderSearchOpts().BuildSessionTimestamp = 432 Service.getBuildSessionTimestamp(); 433 434 ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false; 435 ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false; 436 // This will prevent us compiling individual modules asynchronously since 437 // FileManager is not thread-safe, but it does improve performance for now. 438 ScanInstance.getFrontendOpts().ModulesShareFileManager = true; 439 ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw"; 440 ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage = 441 any(Service.getOptimizeArgs() & ScanningOptimizations::VFS); 442 443 // Support for virtual file system overlays. 444 auto FS = createVFSFromCompilerInvocation( 445 ScanInstance.getInvocation(), ScanInstance.getDiagnostics(), 446 DriverFileMgr->getVirtualFileSystemPtr()); 447 448 // Create a new FileManager to match the invocation's FileSystemOptions. 449 auto *FileMgr = ScanInstance.createFileManager(FS); 450 451 // Use the dependency scanning optimized file system if requested to do so. 452 if (DepFS) { 453 StringRef ModulesCachePath = 454 ScanInstance.getHeaderSearchOpts().ModuleCachePath; 455 456 DepFS->resetBypassedPathPrefix(); 457 if (!ModulesCachePath.empty()) 458 DepFS->setBypassedPathPrefix(ModulesCachePath); 459 460 ScanInstance.setDependencyDirectivesGetter( 461 std::make_unique<ScanningDependencyDirectivesGetter>(*FileMgr)); 462 } 463 464 ScanInstance.createSourceManager(*FileMgr); 465 466 // Create a collection of stable directories derived from the ScanInstance 467 // for determining whether module dependencies would fully resolve from 468 // those directories. 469 llvm::SmallVector<StringRef> StableDirs; 470 const StringRef Sysroot = ScanInstance.getHeaderSearchOpts().Sysroot; 471 if (!Sysroot.empty() && 472 (llvm::sys::path::root_directory(Sysroot) != Sysroot)) 473 StableDirs = {Sysroot, ScanInstance.getHeaderSearchOpts().ResourceDir}; 474 475 // Store a mapping of prebuilt module files and their properties like header 476 // search options. This will prevent the implicit build to create duplicate 477 // modules and will force reuse of the existing prebuilt module files 478 // instead. 479 PrebuiltModulesAttrsMap PrebuiltModulesASTMap; 480 481 if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty()) 482 if (visitPrebuiltModule( 483 ScanInstance.getPreprocessorOpts().ImplicitPCHInclude, 484 ScanInstance, 485 ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles, 486 PrebuiltModulesASTMap, ScanInstance.getDiagnostics(), StableDirs)) 487 return false; 488 489 // Create the dependency collector that will collect the produced 490 // dependencies. 491 // 492 // This also moves the existing dependency output options from the 493 // invocation to the collector. The options in the invocation are reset, 494 // which ensures that the compiler won't create new dependency collectors, 495 // and thus won't write out the extra '.d' files to disk. 496 auto Opts = std::make_unique<DependencyOutputOptions>(); 497 std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts()); 498 // We need at least one -MT equivalent for the generator of make dependency 499 // files to work. 500 if (Opts->Targets.empty()) 501 Opts->Targets = { 502 deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile, 503 ScanInstance.getFrontendOpts().Inputs)}; 504 Opts->IncludeSystemHeaders = true; 505 506 switch (Service.getFormat()) { 507 case ScanningOutputFormat::Make: 508 ScanInstance.addDependencyCollector( 509 std::make_shared<DependencyConsumerForwarder>( 510 std::move(Opts), WorkingDirectory, Consumer)); 511 break; 512 case ScanningOutputFormat::P1689: 513 case ScanningOutputFormat::Full: 514 MDC = std::make_shared<ModuleDepCollector>( 515 Service, std::move(Opts), ScanInstance, Consumer, Controller, 516 OriginalInvocation, std::move(PrebuiltModulesASTMap), StableDirs); 517 ScanInstance.addDependencyCollector(MDC); 518 break; 519 } 520 521 // Consider different header search and diagnostic options to create 522 // different modules. This avoids the unsound aliasing of module PCMs. 523 // 524 // TODO: Implement diagnostic bucketing to reduce the impact of strict 525 // context hashing. 526 ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true; 527 ScanInstance.getHeaderSearchOpts().ModulesSerializeOnlyPreprocessor = true; 528 ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true; 529 ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true; 530 ScanInstance.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings = 531 true; 532 ScanInstance.getHeaderSearchOpts().ModulesForceValidateUserHeaders = false; 533 534 // Avoid some checks and module map parsing when loading PCM files. 535 ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false; 536 537 std::unique_ptr<FrontendAction> Action; 538 539 if (Service.getFormat() == ScanningOutputFormat::P1689) 540 Action = std::make_unique<PreprocessOnlyAction>(); 541 else if (ModuleName) 542 Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName); 543 else 544 Action = std::make_unique<ReadPCHAndPreprocessAction>(); 545 546 if (ScanInstance.getDiagnostics().hasErrorOccurred()) 547 return false; 548 549 const bool Result = ScanInstance.ExecuteAction(*Action); 550 551 // ExecuteAction is responsible for calling finish. 552 DiagConsumerFinished = true; 553 554 if (Result) 555 setLastCC1Arguments(std::move(OriginalInvocation)); 556 557 // Propagate the statistics to the parent FileManager. 558 DriverFileMgr->AddStats(ScanInstance.getFileManager()); 559 560 return Result; 561 } 562 563 bool hasScanned() const { return Scanned; } 564 bool hasDiagConsumerFinished() const { return DiagConsumerFinished; } 565 566 /// Take the cc1 arguments corresponding to the most recent invocation used 567 /// with this action. Any modifications implied by the discovered dependencies 568 /// will have already been applied. 569 std::vector<std::string> takeLastCC1Arguments() { 570 std::vector<std::string> Result; 571 std::swap(Result, LastCC1Arguments); // Reset LastCC1Arguments to empty. 572 return Result; 573 } 574 575 private: 576 void setLastCC1Arguments(CompilerInvocation &&CI) { 577 if (MDC) 578 MDC->applyDiscoveredDependencies(CI); 579 LastCC1Arguments = CI.getCC1CommandLine(); 580 } 581 582 DependencyScanningService &Service; 583 StringRef WorkingDirectory; 584 DependencyConsumer &Consumer; 585 DependencyActionController &Controller; 586 llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS; 587 bool DisableFree; 588 std::optional<StringRef> ModuleName; 589 std::optional<CompilerInstance> ScanInstanceStorage; 590 std::shared_ptr<ModuleDepCollector> MDC; 591 std::vector<std::string> LastCC1Arguments; 592 bool Scanned = false; 593 bool DiagConsumerFinished = false; 594 }; 595 596 } // end anonymous namespace 597 598 DependencyScanningWorker::DependencyScanningWorker( 599 DependencyScanningService &Service, 600 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) 601 : Service(Service) { 602 PCHContainerOps = std::make_shared<PCHContainerOperations>(); 603 // We need to read object files from PCH built outside the scanner. 604 PCHContainerOps->registerReader( 605 std::make_unique<ObjectFilePCHContainerReader>()); 606 // The scanner itself writes only raw ast files. 607 PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>()); 608 609 if (Service.shouldTraceVFS()) 610 FS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(std::move(FS)); 611 612 switch (Service.getMode()) { 613 case ScanningMode::DependencyDirectivesScan: 614 DepFS = 615 new DependencyScanningWorkerFilesystem(Service.getSharedCache(), FS); 616 BaseFS = DepFS; 617 break; 618 case ScanningMode::CanonicalPreprocessing: 619 DepFS = nullptr; 620 BaseFS = FS; 621 break; 622 } 623 } 624 625 static std::unique_ptr<DiagnosticOptions> 626 createDiagOptions(const std::vector<std::string> &CommandLine) { 627 std::vector<const char *> CLI; 628 for (const std::string &Arg : CommandLine) 629 CLI.push_back(Arg.c_str()); 630 auto DiagOpts = CreateAndPopulateDiagOpts(CLI); 631 sanitizeDiagOpts(*DiagOpts); 632 return DiagOpts; 633 } 634 635 llvm::Error DependencyScanningWorker::computeDependencies( 636 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, 637 DependencyConsumer &Consumer, DependencyActionController &Controller, 638 std::optional<llvm::MemoryBufferRef> TUBuffer) { 639 // Capture the emitted diagnostics and report them to the client 640 // in the case of a failure. 641 std::string DiagnosticOutput; 642 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput); 643 auto DiagOpts = createDiagOptions(CommandLine); 644 TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, *DiagOpts); 645 646 if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller, 647 DiagPrinter, TUBuffer)) 648 return llvm::Error::success(); 649 return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(), 650 llvm::inconvertibleErrorCode()); 651 } 652 653 llvm::Error DependencyScanningWorker::computeDependencies( 654 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, 655 DependencyConsumer &Consumer, DependencyActionController &Controller, 656 StringRef ModuleName) { 657 // Capture the emitted diagnostics and report them to the client 658 // in the case of a failure. 659 std::string DiagnosticOutput; 660 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput); 661 auto DiagOpts = createDiagOptions(CommandLine); 662 TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, *DiagOpts); 663 664 if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller, 665 DiagPrinter, ModuleName)) 666 return llvm::Error::success(); 667 return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(), 668 llvm::inconvertibleErrorCode()); 669 } 670 671 static bool forEachDriverJob( 672 ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, FileManager &FM, 673 llvm::function_ref<bool(const driver::Command &Cmd)> Callback) { 674 SmallVector<const char *, 256> Argv; 675 Argv.reserve(ArgStrs.size()); 676 for (const std::string &Arg : ArgStrs) 677 Argv.push_back(Arg.c_str()); 678 679 llvm::vfs::FileSystem *FS = &FM.getVirtualFileSystem(); 680 681 std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>( 682 Argv[0], llvm::sys::getDefaultTargetTriple(), Diags, 683 "clang LLVM compiler", FS); 684 Driver->setTitle("clang_based_tool"); 685 686 llvm::BumpPtrAllocator Alloc; 687 bool CLMode = driver::IsClangCL( 688 driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1))); 689 690 if (llvm::Error E = driver::expandResponseFiles(Argv, CLMode, Alloc, FS)) { 691 Diags.Report(diag::err_drv_expand_response_file) 692 << llvm::toString(std::move(E)); 693 return false; 694 } 695 696 const std::unique_ptr<driver::Compilation> Compilation( 697 Driver->BuildCompilation(llvm::ArrayRef(Argv))); 698 if (!Compilation) 699 return false; 700 701 if (Compilation->containsError()) 702 return false; 703 704 for (const driver::Command &Job : Compilation->getJobs()) { 705 if (!Callback(Job)) 706 return false; 707 } 708 return true; 709 } 710 711 static bool createAndRunToolInvocation( 712 std::vector<std::string> CommandLine, DependencyScanningAction &Action, 713 FileManager &FM, 714 std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps, 715 DiagnosticsEngine &Diags, DependencyConsumer &Consumer) { 716 717 // Save executable path before providing CommandLine to ToolInvocation 718 std::string Executable = CommandLine[0]; 719 ToolInvocation Invocation(std::move(CommandLine), &Action, &FM, 720 PCHContainerOps); 721 Invocation.setDiagnosticConsumer(Diags.getClient()); 722 Invocation.setDiagnosticOptions(&Diags.getDiagnosticOptions()); 723 if (!Invocation.run()) 724 return false; 725 726 std::vector<std::string> Args = Action.takeLastCC1Arguments(); 727 Consumer.handleBuildCommand({std::move(Executable), std::move(Args)}); 728 return true; 729 } 730 731 bool DependencyScanningWorker::scanDependencies( 732 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, 733 DependencyConsumer &Consumer, DependencyActionController &Controller, 734 DiagnosticConsumer &DC, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, 735 std::optional<StringRef> ModuleName) { 736 auto FileMgr = 737 llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions{}, FS); 738 739 std::vector<const char *> CCommandLine(CommandLine.size(), nullptr); 740 llvm::transform(CommandLine, CCommandLine.begin(), 741 [](const std::string &Str) { return Str.c_str(); }); 742 auto DiagOpts = CreateAndPopulateDiagOpts(CCommandLine); 743 sanitizeDiagOpts(*DiagOpts); 744 IntrusiveRefCntPtr<DiagnosticsEngine> Diags = 745 CompilerInstance::createDiagnostics(FileMgr->getVirtualFileSystem(), 746 *DiagOpts, &DC, 747 /*ShouldOwnClient=*/false); 748 749 // Although `Diagnostics` are used only for command-line parsing, the 750 // custom `DiagConsumer` might expect a `SourceManager` to be present. 751 SourceManager SrcMgr(*Diags, *FileMgr); 752 Diags->setSourceManager(&SrcMgr); 753 // DisableFree is modified by Tooling for running 754 // in-process; preserve the original value, which is 755 // always true for a driver invocation. 756 bool DisableFree = true; 757 DependencyScanningAction Action(Service, WorkingDirectory, Consumer, 758 Controller, DepFS, DisableFree, ModuleName); 759 760 bool Success = false; 761 if (CommandLine[1] == "-cc1") { 762 Success = createAndRunToolInvocation(CommandLine, Action, *FileMgr, 763 PCHContainerOps, *Diags, Consumer); 764 } else { 765 Success = forEachDriverJob( 766 CommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) { 767 if (StringRef(Cmd.getCreator().getName()) != "clang") { 768 // Non-clang command. Just pass through to the dependency 769 // consumer. 770 Consumer.handleBuildCommand( 771 {Cmd.getExecutable(), 772 {Cmd.getArguments().begin(), Cmd.getArguments().end()}}); 773 return true; 774 } 775 776 // Insert -cc1 comand line options into Argv 777 std::vector<std::string> Argv; 778 Argv.push_back(Cmd.getExecutable()); 779 llvm::append_range(Argv, Cmd.getArguments()); 780 781 // Create an invocation that uses the underlying file 782 // system to ensure that any file system requests that 783 // are made by the driver do not go through the 784 // dependency scanning filesystem. 785 return createAndRunToolInvocation(std::move(Argv), Action, *FileMgr, 786 PCHContainerOps, *Diags, Consumer); 787 }); 788 } 789 790 if (Success && !Action.hasScanned()) 791 Diags->Report(diag::err_fe_expected_compiler_job) 792 << llvm::join(CommandLine, " "); 793 794 // Ensure finish() is called even if we never reached ExecuteAction(). 795 if (!Action.hasDiagConsumerFinished()) 796 DC.finish(); 797 798 return Success && Action.hasScanned(); 799 } 800 801 bool DependencyScanningWorker::computeDependencies( 802 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, 803 DependencyConsumer &Consumer, DependencyActionController &Controller, 804 DiagnosticConsumer &DC, std::optional<llvm::MemoryBufferRef> TUBuffer) { 805 // Reset what might have been modified in the previous worker invocation. 806 BaseFS->setCurrentWorkingDirectory(WorkingDirectory); 807 808 std::optional<std::vector<std::string>> ModifiedCommandLine; 809 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS; 810 811 // If we're scanning based on a module name alone, we don't expect the client 812 // to provide us with an input file. However, the driver really wants to have 813 // one. Let's just make it up to make the driver happy. 814 if (TUBuffer) { 815 auto OverlayFS = 816 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS); 817 auto InMemoryFS = 818 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); 819 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory); 820 auto InputPath = TUBuffer->getBufferIdentifier(); 821 InMemoryFS->addFile( 822 InputPath, 0, 823 llvm::MemoryBuffer::getMemBufferCopy(TUBuffer->getBuffer())); 824 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = 825 InMemoryFS; 826 827 OverlayFS->pushOverlay(InMemoryOverlay); 828 ModifiedFS = OverlayFS; 829 ModifiedCommandLine = CommandLine; 830 ModifiedCommandLine->emplace_back(InputPath); 831 } 832 833 const std::vector<std::string> &FinalCommandLine = 834 ModifiedCommandLine ? *ModifiedCommandLine : CommandLine; 835 auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS; 836 837 return scanDependencies(WorkingDirectory, FinalCommandLine, Consumer, 838 Controller, DC, FinalFS, /*ModuleName=*/std::nullopt); 839 } 840 841 bool DependencyScanningWorker::computeDependencies( 842 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, 843 DependencyConsumer &Consumer, DependencyActionController &Controller, 844 DiagnosticConsumer &DC, StringRef ModuleName) { 845 // Reset what might have been modified in the previous worker invocation. 846 BaseFS->setCurrentWorkingDirectory(WorkingDirectory); 847 848 // If we're scanning based on a module name alone, we don't expect the client 849 // to provide us with an input file. However, the driver really wants to have 850 // one. Let's just make it up to make the driver happy. 851 auto OverlayFS = 852 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS); 853 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); 854 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory); 855 SmallString<128> FakeInputPath; 856 // TODO: We should retry the creation if the path already exists. 857 llvm::sys::fs::createUniquePath(ModuleName + "-%%%%%%%%.input", FakeInputPath, 858 /*MakeAbsolute=*/false); 859 InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer("")); 860 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS; 861 862 OverlayFS->pushOverlay(InMemoryOverlay); 863 auto ModifiedCommandLine = CommandLine; 864 ModifiedCommandLine.emplace_back(FakeInputPath); 865 866 return scanDependencies(WorkingDirectory, ModifiedCommandLine, Consumer, 867 Controller, DC, OverlayFS, ModuleName); 868 } 869 870 DependencyActionController::~DependencyActionController() {} 871