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 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.
getObjFilePath(StringRef SrcFile)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:
SingleCommandCompilationDatabase(tooling::CompileCommand Cmd)206 SingleCommandCompilationDatabase(tooling::CompileCommand Cmd)
207 : Command(std::move(Cmd)) {}
208
209 std::vector<tooling::CompileCommand>
getCompileCommands(StringRef FilePath) const210 getCompileCommands(StringRef FilePath) const override {
211 return {Command};
212 }
213
getAllCompileCommands() const214 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
handleMakeDependencyToolResult(const std::string & Input,llvm::Expected<std::string> & MaybeFile,SharedStream & OS,SharedStream & Errs)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
toJSONSorted(const llvm::StringSet<> & Set)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
toJSONSorted(std::vector<ModuleID> V)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:
mergeDeps(StringRef Input,FullDependenciesResult FDR,size_t InputIndex)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
printFullOutput(raw_ostream & OS)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:
lookupPCMPath(ModuleID MID)359 StringRef lookupPCMPath(ModuleID MID) {
360 return Modules[IndexedModuleID{MID, 0}].ImplicitModulePCMPath;
361 }
362
lookupModuleDeps(ModuleID MID)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
operator ==FullDeps::IndexedModuleID373 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 {
operator ()FullDeps::IndexedModuleIDHasher380 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
handleFullDependencyToolResult(const std::string & Input,llvm::Expected<FullDependenciesResult> & MaybeFullDeps,FullDeps & FD,size_t InputIndex,SharedStream & OS,SharedStream & Errs)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
main(int argc,const char ** argv)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