1 //===- ClangScanDeps.cpp - Implementation of clang-scan-deps --------------===// 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/Frontend/CompilerInstance.h" 10 #include "clang/Tooling/CommonOptionsParser.h" 11 #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" 12 #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" 13 #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" 14 #include "clang/Tooling/JSONCompilationDatabase.h" 15 #include "llvm/ADT/STLExtras.h" 16 #include "llvm/ADT/Twine.h" 17 #include "llvm/Support/CommandLine.h" 18 #include "llvm/Support/FileUtilities.h" 19 #include "llvm/Support/InitLLVM.h" 20 #include "llvm/Support/JSON.h" 21 #include "llvm/Support/Program.h" 22 #include "llvm/Support/Signals.h" 23 #include "llvm/Support/ThreadPool.h" 24 #include "llvm/Support/Threading.h" 25 #include <mutex> 26 #include <thread> 27 28 using namespace clang; 29 using namespace tooling::dependencies; 30 31 namespace { 32 33 class SharedStream { 34 public: 35 SharedStream(raw_ostream &OS) : OS(OS) {} 36 void applyLocked(llvm::function_ref<void(raw_ostream &OS)> Fn) { 37 std::unique_lock<std::mutex> LockGuard(Lock); 38 Fn(OS); 39 OS.flush(); 40 } 41 42 private: 43 std::mutex Lock; 44 raw_ostream &OS; 45 }; 46 47 class ResourceDirectoryCache { 48 public: 49 /// findResourceDir finds the resource directory relative to the clang 50 /// compiler being used in Args, by running it with "-print-resource-dir" 51 /// option and cache the results for reuse. \returns resource directory path 52 /// associated with the given invocation command or empty string if the 53 /// compiler path is NOT an absolute path. 54 StringRef findResourceDir(const tooling::CommandLineArguments &Args, 55 bool ClangCLMode) { 56 if (Args.size() < 1) 57 return ""; 58 59 const std::string &ClangBinaryPath = Args[0]; 60 if (!llvm::sys::path::is_absolute(ClangBinaryPath)) 61 return ""; 62 63 const std::string &ClangBinaryName = 64 std::string(llvm::sys::path::filename(ClangBinaryPath)); 65 66 std::unique_lock<std::mutex> LockGuard(CacheLock); 67 const auto &CachedResourceDir = Cache.find(ClangBinaryPath); 68 if (CachedResourceDir != Cache.end()) 69 return CachedResourceDir->second; 70 71 std::vector<StringRef> PrintResourceDirArgs{ClangBinaryName}; 72 if (ClangCLMode) 73 PrintResourceDirArgs.push_back("/clang:-print-resource-dir"); 74 else 75 PrintResourceDirArgs.push_back("-print-resource-dir"); 76 77 llvm::SmallString<64> OutputFile, ErrorFile; 78 llvm::sys::fs::createTemporaryFile("print-resource-dir-output", 79 "" /*no-suffix*/, OutputFile); 80 llvm::sys::fs::createTemporaryFile("print-resource-dir-error", 81 "" /*no-suffix*/, ErrorFile); 82 llvm::FileRemover OutputRemover(OutputFile.c_str()); 83 llvm::FileRemover ErrorRemover(ErrorFile.c_str()); 84 llvm::Optional<StringRef> Redirects[] = { 85 {""}, // Stdin 86 StringRef(OutputFile), 87 StringRef(ErrorFile), 88 }; 89 if (const int RC = llvm::sys::ExecuteAndWait( 90 ClangBinaryPath, PrintResourceDirArgs, {}, Redirects)) { 91 auto ErrorBuf = llvm::MemoryBuffer::getFile(ErrorFile.c_str()); 92 llvm::errs() << ErrorBuf.get()->getBuffer(); 93 return ""; 94 } 95 96 auto OutputBuf = llvm::MemoryBuffer::getFile(OutputFile.c_str()); 97 if (!OutputBuf) 98 return ""; 99 StringRef Output = OutputBuf.get()->getBuffer().rtrim('\n'); 100 101 Cache[ClangBinaryPath] = Output.str(); 102 return Cache[ClangBinaryPath]; 103 } 104 105 private: 106 std::map<std::string, std::string> Cache; 107 std::mutex CacheLock; 108 }; 109 110 llvm::cl::opt<bool> Help("h", llvm::cl::desc("Alias for -help"), 111 llvm::cl::Hidden); 112 113 llvm::cl::OptionCategory DependencyScannerCategory("Tool options"); 114 115 static llvm::cl::opt<ScanningMode> ScanMode( 116 "mode", 117 llvm::cl::desc("The preprocessing mode used to compute the dependencies"), 118 llvm::cl::values( 119 clEnumValN(ScanningMode::MinimizedSourcePreprocessing, 120 "preprocess-minimized-sources", 121 "The set of dependencies is computed by preprocessing the " 122 "source files that were minimized to only include the " 123 "contents that might affect the dependencies"), 124 clEnumValN(ScanningMode::CanonicalPreprocessing, "preprocess", 125 "The set of dependencies is computed by preprocessing the " 126 "unmodified source files")), 127 llvm::cl::init(ScanningMode::MinimizedSourcePreprocessing), 128 llvm::cl::cat(DependencyScannerCategory)); 129 130 static llvm::cl::opt<ScanningOutputFormat> Format( 131 "format", llvm::cl::desc("The output format for the dependencies"), 132 llvm::cl::values(clEnumValN(ScanningOutputFormat::Make, "make", 133 "Makefile compatible dep file"), 134 clEnumValN(ScanningOutputFormat::Full, "experimental-full", 135 "Full dependency graph suitable" 136 " for explicitly building modules. This format " 137 "is experimental and will change.")), 138 llvm::cl::init(ScanningOutputFormat::Make), 139 llvm::cl::cat(DependencyScannerCategory)); 140 141 // This mode is mostly useful for development of explicitly built modules. 142 // Command lines will contain arguments specifying modulemap file paths and 143 // absolute paths to PCM files in the module cache directory. 144 // 145 // Build tools that want to put the PCM files in a different location should use 146 // the C++ APIs instead, of which there are two flavors: 147 // 148 // 1. APIs that generate arguments with paths to modulemap and PCM files via 149 // callbacks provided by the client: 150 // * ModuleDeps::getCanonicalCommandLine(LookupPCMPath, LookupModuleDeps) 151 // * FullDependencies::getAdditionalArgs(LookupPCMPath, LookupModuleDeps) 152 // 153 // 2. APIs that don't generate arguments with paths to modulemap or PCM files 154 // and instead expect the client to append them manually after the fact: 155 // * ModuleDeps::getCanonicalCommandLineWithoutModulePaths() 156 // * FullDependencies::getAdditionalArgsWithoutModulePaths() 157 // 158 static llvm::cl::opt<bool> GenerateModulesPathArgs( 159 "generate-modules-path-args", 160 llvm::cl::desc( 161 "With '-format experimental-full', include arguments specifying " 162 "modules-related paths in the generated command lines: " 163 "'-fmodule-file=', '-o', '-fmodule-map-file='."), 164 llvm::cl::init(false), llvm::cl::cat(DependencyScannerCategory)); 165 166 llvm::cl::opt<unsigned> 167 NumThreads("j", llvm::cl::Optional, 168 llvm::cl::desc("Number of worker threads to use (default: use " 169 "all concurrent threads)"), 170 llvm::cl::init(0), llvm::cl::cat(DependencyScannerCategory)); 171 172 llvm::cl::opt<std::string> 173 CompilationDB("compilation-database", 174 llvm::cl::desc("Compilation database"), llvm::cl::Required, 175 llvm::cl::cat(DependencyScannerCategory)); 176 177 llvm::cl::opt<bool> ReuseFileManager( 178 "reuse-filemanager", 179 llvm::cl::desc("Reuse the file manager and its cache between invocations."), 180 llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory)); 181 182 llvm::cl::opt<bool> SkipExcludedPPRanges( 183 "skip-excluded-pp-ranges", 184 llvm::cl::desc( 185 "Use the preprocessor optimization that skips excluded conditionals by " 186 "bumping the buffer pointer in the lexer instead of lexing the tokens " 187 "until reaching the end directive."), 188 llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory)); 189 190 llvm::cl::opt<bool> Verbose("v", llvm::cl::Optional, 191 llvm::cl::desc("Use verbose output."), 192 llvm::cl::init(false), 193 llvm::cl::cat(DependencyScannerCategory)); 194 195 } // end anonymous namespace 196 197 /// \returns object-file path derived from source-file path. 198 static std::string getObjFilePath(StringRef SrcFile) { 199 SmallString<128> ObjFileName(SrcFile); 200 llvm::sys::path::replace_extension(ObjFileName, "o"); 201 return std::string(ObjFileName.str()); 202 } 203 204 class SingleCommandCompilationDatabase : public tooling::CompilationDatabase { 205 public: 206 SingleCommandCompilationDatabase(tooling::CompileCommand Cmd) 207 : Command(std::move(Cmd)) {} 208 209 std::vector<tooling::CompileCommand> 210 getCompileCommands(StringRef FilePath) const override { 211 return {Command}; 212 } 213 214 std::vector<tooling::CompileCommand> getAllCompileCommands() const override { 215 return {Command}; 216 } 217 218 private: 219 tooling::CompileCommand Command; 220 }; 221 222 /// Takes the result of a dependency scan and prints error / dependency files 223 /// based on the result. 224 /// 225 /// \returns True on error. 226 static bool 227 handleMakeDependencyToolResult(const std::string &Input, 228 llvm::Expected<std::string> &MaybeFile, 229 SharedStream &OS, SharedStream &Errs) { 230 if (!MaybeFile) { 231 llvm::handleAllErrors( 232 MaybeFile.takeError(), [&Input, &Errs](llvm::StringError &Err) { 233 Errs.applyLocked([&](raw_ostream &OS) { 234 OS << "Error while scanning dependencies for " << Input << ":\n"; 235 OS << Err.getMessage(); 236 }); 237 }); 238 return true; 239 } 240 OS.applyLocked([&](raw_ostream &OS) { OS << *MaybeFile; }); 241 return false; 242 } 243 244 static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) { 245 std::vector<llvm::StringRef> Strings; 246 for (auto &&I : Set) 247 Strings.push_back(I.getKey()); 248 llvm::sort(Strings); 249 return llvm::json::Array(Strings); 250 } 251 252 static llvm::json::Array toJSONSorted(std::vector<ModuleID> V) { 253 llvm::sort(V, [](const ModuleID &A, const ModuleID &B) { 254 return std::tie(A.ModuleName, A.ContextHash) < 255 std::tie(B.ModuleName, B.ContextHash); 256 }); 257 258 llvm::json::Array Ret; 259 for (const ModuleID &MID : V) 260 Ret.push_back(llvm::json::Object( 261 {{"module-name", MID.ModuleName}, {"context-hash", MID.ContextHash}})); 262 return Ret; 263 } 264 265 // Thread safe. 266 class FullDeps { 267 public: 268 void mergeDeps(StringRef Input, FullDependenciesResult FDR, 269 size_t InputIndex) { 270 const FullDependencies &FD = FDR.FullDeps; 271 272 InputDeps ID; 273 ID.FileName = std::string(Input); 274 ID.ContextHash = std::move(FD.ID.ContextHash); 275 ID.FileDeps = std::move(FD.FileDeps); 276 ID.ModuleDeps = std::move(FD.ClangModuleDeps); 277 278 std::unique_lock<std::mutex> ul(Lock); 279 for (const ModuleDeps &MD : FDR.DiscoveredModules) { 280 auto I = Modules.find({MD.ID, 0}); 281 if (I != Modules.end()) { 282 I->first.InputIndex = std::min(I->first.InputIndex, InputIndex); 283 continue; 284 } 285 Modules.insert(I, {{MD.ID, InputIndex}, std::move(MD)}); 286 } 287 288 ID.AdditionalCommandLine = 289 GenerateModulesPathArgs 290 ? FD.getAdditionalArgs( 291 [&](ModuleID MID) { return lookupPCMPath(MID); }, 292 [&](ModuleID MID) -> const ModuleDeps & { 293 return lookupModuleDeps(MID); 294 }) 295 : FD.getAdditionalArgsWithoutModulePaths(); 296 297 Inputs.push_back(std::move(ID)); 298 } 299 300 void printFullOutput(raw_ostream &OS) { 301 // Sort the modules by name to get a deterministic order. 302 std::vector<IndexedModuleID> ModuleIDs; 303 for (auto &&M : Modules) 304 ModuleIDs.push_back(M.first); 305 llvm::sort(ModuleIDs, 306 [](const IndexedModuleID &A, const IndexedModuleID &B) { 307 return std::tie(A.ID.ModuleName, A.InputIndex) < 308 std::tie(B.ID.ModuleName, B.InputIndex); 309 }); 310 311 llvm::sort(Inputs, [](const InputDeps &A, const InputDeps &B) { 312 return A.FileName < B.FileName; 313 }); 314 315 using namespace llvm::json; 316 317 Array OutModules; 318 for (auto &&ModID : ModuleIDs) { 319 auto &MD = Modules[ModID]; 320 Object O{ 321 {"name", MD.ID.ModuleName}, 322 {"context-hash", MD.ID.ContextHash}, 323 {"file-deps", toJSONSorted(MD.FileDeps)}, 324 {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)}, 325 {"clang-modulemap-file", MD.ClangModuleMapFile}, 326 {"command-line", 327 GenerateModulesPathArgs 328 ? MD.getCanonicalCommandLine( 329 [&](ModuleID MID) { return lookupPCMPath(MID); }, 330 [&](ModuleID MID) -> const ModuleDeps & { 331 return lookupModuleDeps(MID); 332 }) 333 : MD.getCanonicalCommandLineWithoutModulePaths()}, 334 }; 335 OutModules.push_back(std::move(O)); 336 } 337 338 Array TUs; 339 for (auto &&I : Inputs) { 340 Object O{ 341 {"input-file", I.FileName}, 342 {"clang-context-hash", I.ContextHash}, 343 {"file-deps", I.FileDeps}, 344 {"clang-module-deps", toJSONSorted(I.ModuleDeps)}, 345 {"command-line", I.AdditionalCommandLine}, 346 }; 347 TUs.push_back(std::move(O)); 348 } 349 350 Object Output{ 351 {"modules", std::move(OutModules)}, 352 {"translation-units", std::move(TUs)}, 353 }; 354 355 OS << llvm::formatv("{0:2}\n", Value(std::move(Output))); 356 } 357 358 private: 359 StringRef lookupPCMPath(ModuleID MID) { 360 return Modules[IndexedModuleID{MID, 0}].ImplicitModulePCMPath; 361 } 362 363 const ModuleDeps &lookupModuleDeps(ModuleID MID) { 364 auto I = Modules.find(IndexedModuleID{MID, 0}); 365 assert(I != Modules.end()); 366 return I->second; 367 }; 368 369 struct IndexedModuleID { 370 ModuleID ID; 371 mutable size_t InputIndex; 372 373 bool operator==(const IndexedModuleID &Other) const { 374 return ID.ModuleName == Other.ID.ModuleName && 375 ID.ContextHash == Other.ID.ContextHash; 376 } 377 }; 378 379 struct IndexedModuleIDHasher { 380 std::size_t operator()(const IndexedModuleID &IMID) const { 381 using llvm::hash_combine; 382 383 return hash_combine(IMID.ID.ModuleName, IMID.ID.ContextHash); 384 } 385 }; 386 387 struct InputDeps { 388 std::string FileName; 389 std::string ContextHash; 390 std::vector<std::string> FileDeps; 391 std::vector<ModuleID> ModuleDeps; 392 std::vector<std::string> AdditionalCommandLine; 393 }; 394 395 std::mutex Lock; 396 std::unordered_map<IndexedModuleID, ModuleDeps, IndexedModuleIDHasher> 397 Modules; 398 std::vector<InputDeps> Inputs; 399 }; 400 401 static bool handleFullDependencyToolResult( 402 const std::string &Input, 403 llvm::Expected<FullDependenciesResult> &MaybeFullDeps, FullDeps &FD, 404 size_t InputIndex, SharedStream &OS, SharedStream &Errs) { 405 if (!MaybeFullDeps) { 406 llvm::handleAllErrors( 407 MaybeFullDeps.takeError(), [&Input, &Errs](llvm::StringError &Err) { 408 Errs.applyLocked([&](raw_ostream &OS) { 409 OS << "Error while scanning dependencies for " << Input << ":\n"; 410 OS << Err.getMessage(); 411 }); 412 }); 413 return true; 414 } 415 FD.mergeDeps(Input, std::move(*MaybeFullDeps), InputIndex); 416 return false; 417 } 418 419 int main(int argc, const char **argv) { 420 llvm::InitLLVM X(argc, argv); 421 llvm::cl::HideUnrelatedOptions(DependencyScannerCategory); 422 if (!llvm::cl::ParseCommandLineOptions(argc, argv)) 423 return 1; 424 425 std::string ErrorMessage; 426 std::unique_ptr<tooling::JSONCompilationDatabase> Compilations = 427 tooling::JSONCompilationDatabase::loadFromFile( 428 CompilationDB, ErrorMessage, 429 tooling::JSONCommandLineSyntax::AutoDetect); 430 if (!Compilations) { 431 llvm::errs() << "error: " << ErrorMessage << "\n"; 432 return 1; 433 } 434 435 llvm::cl::PrintOptionValues(); 436 437 // The command options are rewritten to run Clang in preprocessor only mode. 438 auto AdjustingCompilations = 439 std::make_unique<tooling::ArgumentsAdjustingCompilations>( 440 std::move(Compilations)); 441 ResourceDirectoryCache ResourceDirCache; 442 AdjustingCompilations->appendArgumentsAdjuster( 443 [&ResourceDirCache](const tooling::CommandLineArguments &Args, 444 StringRef FileName) { 445 std::string LastO = ""; 446 bool HasMT = false; 447 bool HasMQ = false; 448 bool HasMD = false; 449 bool HasResourceDir = false; 450 bool ClangCLMode = false; 451 auto FlagsEnd = llvm::find(Args, "--"); 452 if (FlagsEnd != Args.begin()) { 453 ClangCLMode = 454 llvm::sys::path::stem(Args[0]).contains_lower("clang-cl") || 455 llvm::is_contained(Args, "--driver-mode=cl"); 456 457 // Reverse scan, starting at the end or at the element before "--". 458 auto R = llvm::make_reverse_iterator(FlagsEnd); 459 for (auto I = R, E = Args.rend(); I != E; ++I) { 460 StringRef Arg = *I; 461 if (ClangCLMode) { 462 // Ignore arguments that are preceded by "-Xclang". 463 if ((I + 1) != E && I[1] == "-Xclang") 464 continue; 465 if (LastO.empty()) { 466 // With clang-cl, the output obj file can be specified with 467 // "/opath", "/o path", "/Fopath", and the dash counterparts. 468 // Also, clang-cl adds ".obj" extension if none is found. 469 if ((Arg == "-o" || Arg == "/o") && I != R) 470 LastO = I[-1]; // Next argument (reverse iterator) 471 else if (Arg.startswith("/Fo") || Arg.startswith("-Fo")) 472 LastO = Arg.drop_front(3).str(); 473 else if (Arg.startswith("/o") || Arg.startswith("-o")) 474 LastO = Arg.drop_front(2).str(); 475 476 if (!LastO.empty() && !llvm::sys::path::has_extension(LastO)) 477 LastO.append(".obj"); 478 } 479 if (Arg == "/clang:-MT") 480 HasMT = true; 481 if (Arg == "/clang:-MQ") 482 HasMQ = true; 483 if (Arg == "/clang:-MD") 484 HasMD = true; 485 } else { 486 if (LastO.empty()) { 487 if (Arg == "-o" && I != R) 488 LastO = I[-1]; // Next argument (reverse iterator) 489 else if (Arg.startswith("-o")) 490 LastO = Arg.drop_front(2).str(); 491 } 492 if (Arg == "-MT") 493 HasMT = true; 494 if (Arg == "-MQ") 495 HasMQ = true; 496 if (Arg == "-MD") 497 HasMD = true; 498 } 499 if (Arg == "-resource-dir") 500 HasResourceDir = true; 501 } 502 } 503 // If there's no -MT/-MQ Driver would add -MT with the value of the last 504 // -o option. 505 tooling::CommandLineArguments AdjustedArgs(Args.begin(), FlagsEnd); 506 AdjustedArgs.push_back("-o"); 507 #ifdef _WIN32 508 AdjustedArgs.push_back("nul"); 509 #else 510 AdjustedArgs.push_back("/dev/null"); 511 #endif 512 if (!HasMT && !HasMQ) { 513 // We're interested in source dependencies of an object file. 514 std::string FileNameArg; 515 if (!HasMD) { 516 // FIXME: We are missing the directory unless the -o value is an 517 // absolute path. 518 FileNameArg = !LastO.empty() ? LastO : getObjFilePath(FileName); 519 } else { 520 FileNameArg = std::string(FileName); 521 } 522 if (ClangCLMode) { 523 AdjustedArgs.push_back("/clang:-M"); 524 AdjustedArgs.push_back("/clang:-MT"); 525 AdjustedArgs.push_back(Twine("/clang:", FileNameArg).str()); 526 } else { 527 AdjustedArgs.push_back("-M"); 528 AdjustedArgs.push_back("-MT"); 529 AdjustedArgs.push_back(std::move(FileNameArg)); 530 } 531 } 532 AdjustedArgs.push_back("-Xclang"); 533 AdjustedArgs.push_back("-Eonly"); 534 AdjustedArgs.push_back("-Xclang"); 535 AdjustedArgs.push_back("-sys-header-deps"); 536 AdjustedArgs.push_back("-Wno-error"); 537 538 if (!HasResourceDir) { 539 StringRef ResourceDir = 540 ResourceDirCache.findResourceDir(Args, ClangCLMode); 541 if (!ResourceDir.empty()) { 542 AdjustedArgs.push_back("-resource-dir"); 543 AdjustedArgs.push_back(std::string(ResourceDir)); 544 } 545 } 546 AdjustedArgs.insert(AdjustedArgs.end(), FlagsEnd, Args.end()); 547 return AdjustedArgs; 548 }); 549 AdjustingCompilations->appendArgumentsAdjuster( 550 tooling::getClangStripSerializeDiagnosticAdjuster()); 551 552 SharedStream Errs(llvm::errs()); 553 // Print out the dependency results to STDOUT by default. 554 SharedStream DependencyOS(llvm::outs()); 555 556 DependencyScanningService Service(ScanMode, Format, ReuseFileManager, 557 SkipExcludedPPRanges); 558 llvm::ThreadPool Pool(llvm::hardware_concurrency(NumThreads)); 559 std::vector<std::unique_ptr<DependencyScanningTool>> WorkerTools; 560 for (unsigned I = 0; I < Pool.getThreadCount(); ++I) 561 WorkerTools.push_back(std::make_unique<DependencyScanningTool>(Service)); 562 563 std::vector<SingleCommandCompilationDatabase> Inputs; 564 for (tooling::CompileCommand Cmd : 565 AdjustingCompilations->getAllCompileCommands()) 566 Inputs.emplace_back(Cmd); 567 568 std::atomic<bool> HadErrors(false); 569 FullDeps FD; 570 std::mutex Lock; 571 size_t Index = 0; 572 573 if (Verbose) { 574 llvm::outs() << "Running clang-scan-deps on " << Inputs.size() 575 << " files using " << Pool.getThreadCount() << " workers\n"; 576 } 577 for (unsigned I = 0; I < Pool.getThreadCount(); ++I) { 578 Pool.async([I, &Lock, &Index, &Inputs, &HadErrors, &FD, &WorkerTools, 579 &DependencyOS, &Errs]() { 580 llvm::StringSet<> AlreadySeenModules; 581 while (true) { 582 const SingleCommandCompilationDatabase *Input; 583 std::string Filename; 584 std::string CWD; 585 size_t LocalIndex; 586 // Take the next input. 587 { 588 std::unique_lock<std::mutex> LockGuard(Lock); 589 if (Index >= Inputs.size()) 590 return; 591 LocalIndex = Index; 592 Input = &Inputs[Index++]; 593 tooling::CompileCommand Cmd = Input->getAllCompileCommands()[0]; 594 Filename = std::move(Cmd.Filename); 595 CWD = std::move(Cmd.Directory); 596 } 597 // Run the tool on it. 598 if (Format == ScanningOutputFormat::Make) { 599 auto MaybeFile = WorkerTools[I]->getDependencyFile(*Input, CWD); 600 if (handleMakeDependencyToolResult(Filename, MaybeFile, DependencyOS, 601 Errs)) 602 HadErrors = true; 603 } else { 604 auto MaybeFullDeps = WorkerTools[I]->getFullDependencies( 605 *Input, CWD, AlreadySeenModules); 606 if (handleFullDependencyToolResult(Filename, MaybeFullDeps, FD, 607 LocalIndex, DependencyOS, Errs)) 608 HadErrors = true; 609 } 610 } 611 }); 612 } 613 Pool.wait(); 614 615 if (Format == ScanningOutputFormat::Full) 616 FD.printFullOutput(llvm::outs()); 617 618 return HadErrors; 619 } 620