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:
SharedStream(raw_ostream & OS)35 SharedStream(raw_ostream &OS) : OS(OS) {}
applyLocked(llvm::function_ref<void (raw_ostream & OS)> Fn)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.
findResourceDir(const tooling::CommandLineArguments & Args,bool ClangCLMode)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 OutputFile.str(),
87 ErrorFile.str(),
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::DependencyDirectivesScan,
120 "preprocess-dependency-directives",
121 "The set of dependencies is computed by preprocessing with "
122 "special lexing after scanning the source files to get the "
123 "directives that might affect the dependencies"),
124 clEnumValN(ScanningMode::CanonicalPreprocessing, "preprocess",
125 "The set of dependencies is computed by preprocessing the "
126 "source files")),
127 llvm::cl::init(ScanningMode::DependencyDirectivesScan),
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 PCM files via a callback provided
149 // by the client:
150 // * ModuleDeps::getCanonicalCommandLine(LookupPCMPath)
151 // * FullDependencies::getCommandLine(LookupPCMPath)
152 //
153 // 2. APIs that don't generate arguments with paths PCM files and instead expect
154 // the client to append them manually after the fact:
155 // * ModuleDeps::getCanonicalCommandLineWithoutModulePaths()
156 // * FullDependencies::getCommandLineWithoutModulePaths()
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 static llvm::cl::opt<std::string> ModuleFilesDir(
167 "module-files-dir",
168 llvm::cl::desc("With '-generate-modules-path-args', paths to module files "
169 "in the generated command lines will begin with the "
170 "specified directory instead the module cache directory."),
171 llvm::cl::cat(DependencyScannerCategory));
172
173 static llvm::cl::opt<bool> OptimizeArgs(
174 "optimize-args",
175 llvm::cl::desc("Whether to optimize command-line arguments of modules."),
176 llvm::cl::init(false), llvm::cl::cat(DependencyScannerCategory));
177
178 llvm::cl::opt<unsigned>
179 NumThreads("j", llvm::cl::Optional,
180 llvm::cl::desc("Number of worker threads to use (default: use "
181 "all concurrent threads)"),
182 llvm::cl::init(0), llvm::cl::cat(DependencyScannerCategory));
183
184 llvm::cl::opt<std::string>
185 CompilationDB("compilation-database",
186 llvm::cl::desc("Compilation database"), llvm::cl::Required,
187 llvm::cl::cat(DependencyScannerCategory));
188
189 llvm::cl::opt<bool> ReuseFileManager(
190 "reuse-filemanager",
191 llvm::cl::desc("Reuse the file manager and its cache between invocations."),
192 llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory));
193
194 llvm::cl::opt<std::string> ModuleName(
195 "module-name", llvm::cl::Optional,
196 llvm::cl::desc("the module of which the dependencies are to be computed"),
197 llvm::cl::cat(DependencyScannerCategory));
198
199 llvm::cl::list<std::string> ModuleDepTargets(
200 "dependency-target",
201 llvm::cl::desc("With '-generate-modules-path-args', the names of "
202 "dependency targets for the dependency file"),
203 llvm::cl::cat(DependencyScannerCategory));
204
205 enum ResourceDirRecipeKind {
206 RDRK_ModifyCompilerPath,
207 RDRK_InvokeCompiler,
208 };
209
210 static llvm::cl::opt<ResourceDirRecipeKind> ResourceDirRecipe(
211 "resource-dir-recipe",
212 llvm::cl::desc("How to produce missing '-resource-dir' argument"),
213 llvm::cl::values(
214 clEnumValN(RDRK_ModifyCompilerPath, "modify-compiler-path",
215 "Construct the resource directory from the compiler path in "
216 "the compilation database. This assumes it's part of the "
217 "same toolchain as this clang-scan-deps. (default)"),
218 clEnumValN(RDRK_InvokeCompiler, "invoke-compiler",
219 "Invoke the compiler with '-print-resource-dir' and use the "
220 "reported path as the resource directory. (deprecated)")),
221 llvm::cl::init(RDRK_ModifyCompilerPath),
222 llvm::cl::cat(DependencyScannerCategory));
223
224 llvm::cl::opt<bool> Verbose("v", llvm::cl::Optional,
225 llvm::cl::desc("Use verbose output."),
226 llvm::cl::init(false),
227 llvm::cl::cat(DependencyScannerCategory));
228
229 } // end anonymous namespace
230
231 /// Takes the result of a dependency scan and prints error / dependency files
232 /// based on the result.
233 ///
234 /// \returns True on error.
235 static bool
handleMakeDependencyToolResult(const std::string & Input,llvm::Expected<std::string> & MaybeFile,SharedStream & OS,SharedStream & Errs)236 handleMakeDependencyToolResult(const std::string &Input,
237 llvm::Expected<std::string> &MaybeFile,
238 SharedStream &OS, SharedStream &Errs) {
239 if (!MaybeFile) {
240 llvm::handleAllErrors(
241 MaybeFile.takeError(), [&Input, &Errs](llvm::StringError &Err) {
242 Errs.applyLocked([&](raw_ostream &OS) {
243 OS << "Error while scanning dependencies for " << Input << ":\n";
244 OS << Err.getMessage();
245 });
246 });
247 return true;
248 }
249 OS.applyLocked([&](raw_ostream &OS) { OS << *MaybeFile; });
250 return false;
251 }
252
toJSONSorted(const llvm::StringSet<> & Set)253 static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) {
254 std::vector<llvm::StringRef> Strings;
255 for (auto &&I : Set)
256 Strings.push_back(I.getKey());
257 llvm::sort(Strings);
258 return llvm::json::Array(Strings);
259 }
260
toJSONSorted(std::vector<ModuleID> V)261 static llvm::json::Array toJSONSorted(std::vector<ModuleID> V) {
262 llvm::sort(V, [](const ModuleID &A, const ModuleID &B) {
263 return std::tie(A.ModuleName, A.ContextHash) <
264 std::tie(B.ModuleName, B.ContextHash);
265 });
266
267 llvm::json::Array Ret;
268 for (const ModuleID &MID : V)
269 Ret.push_back(llvm::json::Object(
270 {{"module-name", MID.ModuleName}, {"context-hash", MID.ContextHash}}));
271 return Ret;
272 }
273
274 // Thread safe.
275 class FullDeps {
276 public:
mergeDeps(StringRef Input,FullDependenciesResult FDR,size_t InputIndex)277 void mergeDeps(StringRef Input, FullDependenciesResult FDR,
278 size_t InputIndex) {
279 const FullDependencies &FD = FDR.FullDeps;
280
281 InputDeps ID;
282 ID.FileName = std::string(Input);
283 ID.ContextHash = std::move(FD.ID.ContextHash);
284 ID.FileDeps = std::move(FD.FileDeps);
285 ID.ModuleDeps = std::move(FD.ClangModuleDeps);
286
287 std::unique_lock<std::mutex> ul(Lock);
288 for (const ModuleDeps &MD : FDR.DiscoveredModules) {
289 auto I = Modules.find({MD.ID, 0});
290 if (I != Modules.end()) {
291 I->first.InputIndex = std::min(I->first.InputIndex, InputIndex);
292 continue;
293 }
294 Modules.insert(I, {{MD.ID, InputIndex}, std::move(MD)});
295 }
296
297 ID.CommandLine =
298 GenerateModulesPathArgs
299 ? FD.getCommandLine([&](const ModuleID &MID, ModuleOutputKind MOK) {
300 return lookupModuleOutput(MID, MOK);
301 })
302 : FD.getCommandLineWithoutModulePaths();
303 Inputs.push_back(std::move(ID));
304 }
305
printFullOutput(raw_ostream & OS)306 void printFullOutput(raw_ostream &OS) {
307 // Sort the modules by name to get a deterministic order.
308 std::vector<IndexedModuleID> ModuleIDs;
309 for (auto &&M : Modules)
310 ModuleIDs.push_back(M.first);
311 llvm::sort(ModuleIDs,
312 [](const IndexedModuleID &A, const IndexedModuleID &B) {
313 return std::tie(A.ID.ModuleName, A.InputIndex) <
314 std::tie(B.ID.ModuleName, B.InputIndex);
315 });
316
317 llvm::sort(Inputs, [](const InputDeps &A, const InputDeps &B) {
318 return A.FileName < B.FileName;
319 });
320
321 using namespace llvm::json;
322
323 Array OutModules;
324 for (auto &&ModID : ModuleIDs) {
325 auto &MD = Modules[ModID];
326 Object O{
327 {"name", MD.ID.ModuleName},
328 {"context-hash", MD.ID.ContextHash},
329 {"file-deps", toJSONSorted(MD.FileDeps)},
330 {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)},
331 {"clang-modulemap-file", MD.ClangModuleMapFile},
332 {"command-line",
333 GenerateModulesPathArgs
334 ? MD.getCanonicalCommandLine(
335 [&](const ModuleID &MID, ModuleOutputKind MOK) {
336 return lookupModuleOutput(MID, MOK);
337 })
338 : MD.getCanonicalCommandLineWithoutModulePaths()},
339 };
340 OutModules.push_back(std::move(O));
341 }
342
343 Array TUs;
344 for (auto &&I : Inputs) {
345 Object O{
346 {"input-file", I.FileName},
347 {"clang-context-hash", I.ContextHash},
348 {"file-deps", I.FileDeps},
349 {"clang-module-deps", toJSONSorted(I.ModuleDeps)},
350 {"command-line", I.CommandLine},
351 };
352 TUs.push_back(std::move(O));
353 }
354
355 Object Output{
356 {"modules", std::move(OutModules)},
357 {"translation-units", std::move(TUs)},
358 };
359
360 OS << llvm::formatv("{0:2}\n", Value(std::move(Output)));
361 }
362
363 private:
lookupModuleOutput(const ModuleID & MID,ModuleOutputKind MOK)364 std::string lookupModuleOutput(const ModuleID &MID, ModuleOutputKind MOK) {
365 // Cache the PCM path, since it will be queried repeatedly for each module.
366 // The other outputs are only queried once during getCanonicalCommandLine.
367 auto PCMPath = PCMPaths.insert({MID, ""});
368 if (PCMPath.second)
369 PCMPath.first->second = constructPCMPath(MID);
370 switch (MOK) {
371 case ModuleOutputKind::ModuleFile:
372 return PCMPath.first->second;
373 case ModuleOutputKind::DependencyFile:
374 return PCMPath.first->second + ".d";
375 case ModuleOutputKind::DependencyTargets:
376 // Null-separate the list of targets.
377 return join(ModuleDepTargets, StringRef("\0", 1));
378 case ModuleOutputKind::DiagnosticSerializationFile:
379 return PCMPath.first->second + ".diag";
380 }
381 llvm_unreachable("Fully covered switch above!");
382 }
383
384 /// Construct a path for the explicitly built PCM.
constructPCMPath(ModuleID MID) const385 std::string constructPCMPath(ModuleID MID) const {
386 auto MDIt = Modules.find(IndexedModuleID{MID, 0});
387 assert(MDIt != Modules.end());
388 const ModuleDeps &MD = MDIt->second;
389
390 StringRef Filename = llvm::sys::path::filename(MD.ImplicitModulePCMPath);
391 StringRef ModuleCachePath = llvm::sys::path::parent_path(
392 llvm::sys::path::parent_path(MD.ImplicitModulePCMPath));
393
394 SmallString<256> ExplicitPCMPath(!ModuleFilesDir.empty() ? ModuleFilesDir
395 : ModuleCachePath);
396 llvm::sys::path::append(ExplicitPCMPath, MD.ID.ContextHash, Filename);
397 return std::string(ExplicitPCMPath);
398 }
399
400 struct IndexedModuleID {
401 ModuleID ID;
402 mutable size_t InputIndex;
403
operator ==FullDeps::IndexedModuleID404 bool operator==(const IndexedModuleID &Other) const {
405 return ID.ModuleName == Other.ID.ModuleName &&
406 ID.ContextHash == Other.ID.ContextHash;
407 }
408 };
409
410 struct IndexedModuleIDHasher {
operator ()FullDeps::IndexedModuleIDHasher411 std::size_t operator()(const IndexedModuleID &IMID) const {
412 using llvm::hash_combine;
413
414 return hash_combine(IMID.ID.ModuleName, IMID.ID.ContextHash);
415 }
416 };
417
418 struct InputDeps {
419 std::string FileName;
420 std::string ContextHash;
421 std::vector<std::string> FileDeps;
422 std::vector<ModuleID> ModuleDeps;
423 std::vector<std::string> CommandLine;
424 };
425
426 std::mutex Lock;
427 std::unordered_map<IndexedModuleID, ModuleDeps, IndexedModuleIDHasher>
428 Modules;
429 std::unordered_map<ModuleID, std::string, ModuleIDHasher> PCMPaths;
430 std::vector<InputDeps> Inputs;
431 };
432
handleFullDependencyToolResult(const std::string & Input,llvm::Expected<FullDependenciesResult> & MaybeFullDeps,FullDeps & FD,size_t InputIndex,SharedStream & OS,SharedStream & Errs)433 static bool handleFullDependencyToolResult(
434 const std::string &Input,
435 llvm::Expected<FullDependenciesResult> &MaybeFullDeps, FullDeps &FD,
436 size_t InputIndex, SharedStream &OS, SharedStream &Errs) {
437 if (!MaybeFullDeps) {
438 llvm::handleAllErrors(
439 MaybeFullDeps.takeError(), [&Input, &Errs](llvm::StringError &Err) {
440 Errs.applyLocked([&](raw_ostream &OS) {
441 OS << "Error while scanning dependencies for " << Input << ":\n";
442 OS << Err.getMessage();
443 });
444 });
445 return true;
446 }
447 FD.mergeDeps(Input, std::move(*MaybeFullDeps), InputIndex);
448 return false;
449 }
450
main(int argc,const char ** argv)451 int main(int argc, const char **argv) {
452 llvm::InitLLVM X(argc, argv);
453 llvm::cl::HideUnrelatedOptions(DependencyScannerCategory);
454 if (!llvm::cl::ParseCommandLineOptions(argc, argv))
455 return 1;
456
457 std::string ErrorMessage;
458 std::unique_ptr<tooling::JSONCompilationDatabase> Compilations =
459 tooling::JSONCompilationDatabase::loadFromFile(
460 CompilationDB, ErrorMessage,
461 tooling::JSONCommandLineSyntax::AutoDetect);
462 if (!Compilations) {
463 llvm::errs() << "error: " << ErrorMessage << "\n";
464 return 1;
465 }
466
467 llvm::cl::PrintOptionValues();
468
469 // The command options are rewritten to run Clang in preprocessor only mode.
470 auto AdjustingCompilations =
471 std::make_unique<tooling::ArgumentsAdjustingCompilations>(
472 std::move(Compilations));
473 ResourceDirectoryCache ResourceDirCache;
474
475 AdjustingCompilations->appendArgumentsAdjuster(
476 [&ResourceDirCache](const tooling::CommandLineArguments &Args,
477 StringRef FileName) {
478 std::string LastO;
479 bool HasResourceDir = false;
480 bool ClangCLMode = false;
481 auto FlagsEnd = llvm::find(Args, "--");
482 if (FlagsEnd != Args.begin()) {
483 ClangCLMode =
484 llvm::sys::path::stem(Args[0]).contains_insensitive("clang-cl") ||
485 llvm::is_contained(Args, "--driver-mode=cl");
486
487 // Reverse scan, starting at the end or at the element before "--".
488 auto R = std::make_reverse_iterator(FlagsEnd);
489 for (auto I = R, E = Args.rend(); I != E; ++I) {
490 StringRef Arg = *I;
491 if (ClangCLMode) {
492 // Ignore arguments that are preceded by "-Xclang".
493 if ((I + 1) != E && I[1] == "-Xclang")
494 continue;
495 if (LastO.empty()) {
496 // With clang-cl, the output obj file can be specified with
497 // "/opath", "/o path", "/Fopath", and the dash counterparts.
498 // Also, clang-cl adds ".obj" extension if none is found.
499 if ((Arg == "-o" || Arg == "/o") && I != R)
500 LastO = I[-1]; // Next argument (reverse iterator)
501 else if (Arg.startswith("/Fo") || Arg.startswith("-Fo"))
502 LastO = Arg.drop_front(3).str();
503 else if (Arg.startswith("/o") || Arg.startswith("-o"))
504 LastO = Arg.drop_front(2).str();
505
506 if (!LastO.empty() && !llvm::sys::path::has_extension(LastO))
507 LastO.append(".obj");
508 }
509 }
510 if (Arg == "-resource-dir")
511 HasResourceDir = true;
512 }
513 }
514 tooling::CommandLineArguments AdjustedArgs(Args.begin(), FlagsEnd);
515 // The clang-cl driver passes "-o -" to the frontend. Inject the real
516 // file here to ensure "-MT" can be deduced if need be.
517 if (ClangCLMode && !LastO.empty()) {
518 AdjustedArgs.push_back("/clang:-o");
519 AdjustedArgs.push_back("/clang:" + LastO);
520 }
521
522 if (!HasResourceDir && ResourceDirRecipe == RDRK_InvokeCompiler) {
523 StringRef ResourceDir =
524 ResourceDirCache.findResourceDir(Args, ClangCLMode);
525 if (!ResourceDir.empty()) {
526 AdjustedArgs.push_back("-resource-dir");
527 AdjustedArgs.push_back(std::string(ResourceDir));
528 }
529 }
530 AdjustedArgs.insert(AdjustedArgs.end(), FlagsEnd, Args.end());
531 return AdjustedArgs;
532 });
533
534 SharedStream Errs(llvm::errs());
535 // Print out the dependency results to STDOUT by default.
536 SharedStream DependencyOS(llvm::outs());
537
538 DependencyScanningService Service(ScanMode, Format, ReuseFileManager,
539 OptimizeArgs);
540 llvm::ThreadPool Pool(llvm::hardware_concurrency(NumThreads));
541 std::vector<std::unique_ptr<DependencyScanningTool>> WorkerTools;
542 for (unsigned I = 0; I < Pool.getThreadCount(); ++I)
543 WorkerTools.push_back(std::make_unique<DependencyScanningTool>(Service));
544
545 std::vector<tooling::CompileCommand> Inputs =
546 AdjustingCompilations->getAllCompileCommands();
547
548 std::atomic<bool> HadErrors(false);
549 FullDeps FD;
550 std::mutex Lock;
551 size_t Index = 0;
552
553 if (Verbose) {
554 llvm::outs() << "Running clang-scan-deps on " << Inputs.size()
555 << " files using " << Pool.getThreadCount() << " workers\n";
556 }
557 for (unsigned I = 0; I < Pool.getThreadCount(); ++I) {
558 Pool.async([I, &Lock, &Index, &Inputs, &HadErrors, &FD, &WorkerTools,
559 &DependencyOS, &Errs]() {
560 llvm::StringSet<> AlreadySeenModules;
561 while (true) {
562 const tooling::CompileCommand *Input;
563 std::string Filename;
564 std::string CWD;
565 size_t LocalIndex;
566 // Take the next input.
567 {
568 std::unique_lock<std::mutex> LockGuard(Lock);
569 if (Index >= Inputs.size())
570 return;
571 LocalIndex = Index;
572 Input = &Inputs[Index++];
573 Filename = std::move(Input->Filename);
574 CWD = std::move(Input->Directory);
575 }
576 Optional<StringRef> MaybeModuleName;
577 if (!ModuleName.empty())
578 MaybeModuleName = ModuleName;
579 // Run the tool on it.
580 if (Format == ScanningOutputFormat::Make) {
581 auto MaybeFile = WorkerTools[I]->getDependencyFile(
582 Input->CommandLine, CWD, MaybeModuleName);
583 if (handleMakeDependencyToolResult(Filename, MaybeFile, DependencyOS,
584 Errs))
585 HadErrors = true;
586 } else {
587 auto MaybeFullDeps = WorkerTools[I]->getFullDependencies(
588 Input->CommandLine, CWD, AlreadySeenModules, MaybeModuleName);
589 if (handleFullDependencyToolResult(Filename, MaybeFullDeps, FD,
590 LocalIndex, DependencyOS, Errs))
591 HadErrors = true;
592 }
593 }
594 });
595 }
596 Pool.wait();
597
598 if (Format == ScanningOutputFormat::Full)
599 FD.printFullOutput(llvm::outs());
600
601 return HadErrors;
602 }
603