Coverage Report

Created: 2021-08-24 07:12

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/tools/clang-scan-deps/ClangScanDeps.cpp
Line
Count
Source (jump to first uncovered line)
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
108
  SharedStream(raw_ostream &OS) : OS(OS) {}
36
131
  void applyLocked(llvm::function_ref<void(raw_ostream &OS)> Fn) {
37
131
    std::unique_lock<std::mutex> LockGuard(Lock);
38
131
    Fn(OS);
39
131
    OS.flush();
40
131
  }
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
155
                            bool ClangCLMode) {
56
155
    if (Args.size() < 1)
57
0
      return "";
58
59
155
    const std::string &ClangBinaryPath = Args[0];
60
155
    if (!llvm::sys::path::is_absolute(ClangBinaryPath))
61
155
      return "";
62
63
0
    const std::string &ClangBinaryName =
64
0
        std::string(llvm::sys::path::filename(ClangBinaryPath));
65
66
0
    std::unique_lock<std::mutex> LockGuard(CacheLock);
67
0
    const auto &CachedResourceDir = Cache.find(ClangBinaryPath);
68
0
    if (CachedResourceDir != Cache.end())
69
0
      return CachedResourceDir->second;
70
71
0
    std::vector<StringRef> PrintResourceDirArgs{ClangBinaryName};
72
0
    if (ClangCLMode)
73
0
      PrintResourceDirArgs.push_back("/clang:-print-resource-dir");
74
0
    else
75
0
      PrintResourceDirArgs.push_back("-print-resource-dir");
76
77
0
    llvm::SmallString<64> OutputFile, ErrorFile;
78
0
    llvm::sys::fs::createTemporaryFile("print-resource-dir-output",
79
0
                                       "" /*no-suffix*/, OutputFile);
80
0
    llvm::sys::fs::createTemporaryFile("print-resource-dir-error",
81
0
                                       "" /*no-suffix*/, ErrorFile);
82
0
    llvm::FileRemover OutputRemover(OutputFile.c_str());
83
0
    llvm::FileRemover ErrorRemover(ErrorFile.c_str());
84
0
    llvm::Optional<StringRef> Redirects[] = {
85
0
        {""}, // Stdin
86
0
        OutputFile.str(),
87
0
        ErrorFile.str(),
88
0
    };
89
0
    if (const int RC = llvm::sys::ExecuteAndWait(
90
0
            ClangBinaryPath, PrintResourceDirArgs, {}, Redirects)) {
91
0
      auto ErrorBuf = llvm::MemoryBuffer::getFile(ErrorFile.c_str());
92
0
      llvm::errs() << ErrorBuf.get()->getBuffer();
93
0
      return "";
94
0
    }
95
96
0
    auto OutputBuf = llvm::MemoryBuffer::getFile(OutputFile.c_str());
97
0
    if (!OutputBuf)
98
0
      return "";
99
0
    StringRef Output = OutputBuf.get()->getBuffer().rtrim('\n');
100
101
0
    Cache[ClangBinaryPath] = Output.str();
102
0
    return Cache[ClangBinaryPath];
103
0
  }
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
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
llvm::cl::opt<unsigned>
174
    NumThreads("j", llvm::cl::Optional,
175
               llvm::cl::desc("Number of worker threads to use (default: use "
176
                              "all concurrent threads)"),
177
               llvm::cl::init(0), llvm::cl::cat(DependencyScannerCategory));
178
179
llvm::cl::opt<std::string>
180
    CompilationDB("compilation-database",
181
                  llvm::cl::desc("Compilation database"), llvm::cl::Required,
182
                  llvm::cl::cat(DependencyScannerCategory));
183
184
llvm::cl::opt<bool> ReuseFileManager(
185
    "reuse-filemanager",
186
    llvm::cl::desc("Reuse the file manager and its cache between invocations."),
187
    llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory));
188
189
llvm::cl::opt<bool> SkipExcludedPPRanges(
190
    "skip-excluded-pp-ranges",
191
    llvm::cl::desc(
192
        "Use the preprocessor optimization that skips excluded conditionals by "
193
        "bumping the buffer pointer in the lexer instead of lexing the tokens  "
194
        "until reaching the end directive."),
195
    llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory));
196
197
llvm::cl::opt<bool> Verbose("v", llvm::cl::Optional,
198
                            llvm::cl::desc("Use verbose output."),
199
                            llvm::cl::init(false),
200
                            llvm::cl::cat(DependencyScannerCategory));
201
202
} // end anonymous namespace
203
204
/// Takes the result of a dependency scan and prints error / dependency files
205
/// based on the result.
206
///
207
/// \returns True on error.
208
static bool
209
handleMakeDependencyToolResult(const std::string &Input,
210
                               llvm::Expected<std::string> &MaybeFile,
211
130
                               SharedStream &OS, SharedStream &Errs) {
212
130
  if (!MaybeFile) {
213
6
    llvm::handleAllErrors(
214
6
        MaybeFile.takeError(), [&Input, &Errs](llvm::StringError &Err) {
215
6
          Errs.applyLocked([&](raw_ostream &OS) {
216
6
            OS << "Error while scanning dependencies for " << Input << ":\n";
217
6
            OS << Err.getMessage();
218
6
          });
219
6
        });
220
6
    return true;
221
6
  }
222
126
  
OS.applyLocked([&](raw_ostream &OS) 124
{ OS << *MaybeFile; });
223
124
  return false;
224
130
}
225
226
22
static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) {
227
22
  std::vector<llvm::StringRef> Strings;
228
22
  for (auto &&I : Set)
229
46
    Strings.push_back(I.getKey());
230
22
  llvm::sort(Strings);
231
22
  return llvm::json::Array(Strings);
232
22
}
233
234
45
static llvm::json::Array toJSONSorted(std::vector<ModuleID> V) {
235
45
  llvm::sort(V, [](const ModuleID &A, const ModuleID &B) {
236
2
    return std::tie(A.ModuleName, A.ContextHash) <
237
2
           std::tie(B.ModuleName, B.ContextHash);
238
2
  });
239
240
45
  llvm::json::Array Ret;
241
45
  for (const ModuleID &MID : V)
242
30
    Ret.push_back(llvm::json::Object(
243
30
        {{"module-name", MID.ModuleName}, {"context-hash", MID.ContextHash}}));
244
45
  return Ret;
245
45
}
246
247
// Thread safe.
248
class FullDeps {
249
public:
250
  void mergeDeps(StringRef Input, FullDependenciesResult FDR,
251
23
                 size_t InputIndex) {
252
23
    const FullDependencies &FD = FDR.FullDeps;
253
254
23
    InputDeps ID;
255
23
    ID.FileName = std::string(Input);
256
23
    ID.ContextHash = std::move(FD.ID.ContextHash);
257
23
    ID.FileDeps = std::move(FD.FileDeps);
258
23
    ID.ModuleDeps = std::move(FD.ClangModuleDeps);
259
260
23
    std::unique_lock<std::mutex> ul(Lock);
261
30
    for (const ModuleDeps &MD : FDR.DiscoveredModules) {
262
30
      auto I = Modules.find({MD.ID, 0});
263
30
      if (I != Modules.end()) {
264
8
        I->first.InputIndex = std::min(I->first.InputIndex, InputIndex);
265
8
        continue;
266
8
      }
267
22
      Modules.insert(I, {{MD.ID, InputIndex}, std::move(MD)});
268
22
    }
269
270
23
    ID.AdditionalCommandLine =
271
23
        GenerateModulesPathArgs
272
23
            ? FD.getAdditionalArgs(
273
19
                  [&](ModuleID MID) { return lookupPCMPath(MID); },
274
19
                  [&](ModuleID MID) -> const ModuleDeps & {
275
19
                    return lookupModuleDeps(MID);
276
19
                  })
277
23
            : 
FD.getAdditionalArgsWithoutModulePaths()9
;
278
279
23
    Inputs.push_back(std::move(ID));
280
23
  }
281
282
11
  void printFullOutput(raw_ostream &OS) {
283
    // Sort the modules by name to get a deterministic order.
284
11
    std::vector<IndexedModuleID> ModuleIDs;
285
11
    for (auto &&M : Modules)
286
22
      ModuleIDs.push_back(M.first);
287
11
    llvm::sort(ModuleIDs,
288
16
               [](const IndexedModuleID &A, const IndexedModuleID &B) {
289
16
                 return std::tie(A.ID.ModuleName, A.InputIndex) <
290
16
                        std::tie(B.ID.ModuleName, B.InputIndex);
291
16
               });
292
293
13
    llvm::sort(Inputs, [](const InputDeps &A, const InputDeps &B) {
294
13
      return A.FileName < B.FileName;
295
13
    });
296
297
11
    using namespace llvm::json;
298
299
11
    Array OutModules;
300
22
    for (auto &&ModID : ModuleIDs) {
301
22
      auto &MD = Modules[ModID];
302
22
      Object O{
303
22
          {"name", MD.ID.ModuleName},
304
22
          {"context-hash", MD.ID.ContextHash},
305
22
          {"file-deps", toJSONSorted(MD.FileDeps)},
306
22
          {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)},
307
22
          {"clang-modulemap-file", MD.ClangModuleMapFile},
308
22
          {"command-line",
309
22
           GenerateModulesPathArgs
310
22
               ? MD.getCanonicalCommandLine(
311
18
                     [&](ModuleID MID) { return lookupPCMPath(MID); },
312
15
                     [&](ModuleID MID) -> const ModuleDeps & {
313
3
                       return lookupModuleDeps(MID);
314
3
                     })
315
22
               : 
MD.getCanonicalCommandLineWithoutModulePaths()7
},
316
22
      };
317
22
      OutModules.push_back(std::move(O));
318
22
    }
319
320
11
    Array TUs;
321
23
    for (auto &&I : Inputs) {
322
23
      Object O{
323
23
          {"input-file", I.FileName},
324
23
          {"clang-context-hash", I.ContextHash},
325
23
          {"file-deps", I.FileDeps},
326
23
          {"clang-module-deps", toJSONSorted(I.ModuleDeps)},
327
23
          {"command-line", I.AdditionalCommandLine},
328
23
      };
329
23
      TUs.push_back(std::move(O));
330
23
    }
331
332
11
    Object Output{
333
11
        {"modules", std::move(OutModules)},
334
11
        {"translation-units", std::move(TUs)},
335
11
    };
336
337
11
    OS << llvm::formatv("{0:2}\n", Value(std::move(Output)));
338
11
  }
339
340
private:
341
37
  StringRef lookupPCMPath(ModuleID MID) {
342
37
    auto PCMPath = PCMPaths.insert({MID, ""});
343
37
    if (PCMPath.second)
344
15
      PCMPath.first->second = constructPCMPath(lookupModuleDeps(MID));
345
37
    return PCMPath.first->second;
346
37
  }
347
348
  /// Construct a path for the explicitly built PCM.
349
15
  std::string constructPCMPath(const ModuleDeps &MD) const {
350
15
    StringRef Filename = llvm::sys::path::filename(MD.ImplicitModulePCMPath);
351
352
15
    SmallString<256> ExplicitPCMPath(
353
15
        !ModuleFilesDir.empty()
354
15
            ? 
ModuleFilesDir8
355
15
            : 
MD.Invocation.getHeaderSearchOpts().ModuleCachePath7
);
356
15
    llvm::sys::path::append(ExplicitPCMPath, MD.ID.ContextHash, Filename);
357
15
    return std::string(ExplicitPCMPath);
358
15
  }
359
360
37
  const ModuleDeps &lookupModuleDeps(ModuleID MID) {
361
37
    auto I = Modules.find(IndexedModuleID{MID, 0});
362
37
    assert(I != Modules.end());
363
0
    return I->second;
364
37
  };
365
366
  struct IndexedModuleID {
367
    ModuleID ID;
368
    mutable size_t InputIndex;
369
370
87
    bool operator==(const IndexedModuleID &Other) const {
371
87
      return ID.ModuleName == Other.ID.ModuleName &&
372
87
             
ID.ContextHash == Other.ID.ContextHash74
;
373
87
    }
374
  };
375
376
  struct IndexedModuleIDHasher {
377
111
    std::size_t operator()(const IndexedModuleID &IMID) const {
378
111
      using llvm::hash_combine;
379
380
111
      return hash_combine(IMID.ID.ModuleName, IMID.ID.ContextHash);
381
111
    }
382
  };
383
384
  struct InputDeps {
385
    std::string FileName;
386
    std::string ContextHash;
387
    std::vector<std::string> FileDeps;
388
    std::vector<ModuleID> ModuleDeps;
389
    std::vector<std::string> AdditionalCommandLine;
390
  };
391
392
  std::mutex Lock;
393
  std::unordered_map<IndexedModuleID, ModuleDeps, IndexedModuleIDHasher>
394
      Modules;
395
  std::unordered_map<ModuleID, std::string, ModuleIDHasher> PCMPaths;
396
  std::vector<InputDeps> Inputs;
397
};
398
399
static bool handleFullDependencyToolResult(
400
    const std::string &Input,
401
    llvm::Expected<FullDependenciesResult> &MaybeFullDeps, FullDeps &FD,
402
23
    size_t InputIndex, SharedStream &OS, SharedStream &Errs) {
403
23
  if (!MaybeFullDeps) {
404
0
    llvm::handleAllErrors(
405
0
        MaybeFullDeps.takeError(), [&Input, &Errs](llvm::StringError &Err) {
406
0
          Errs.applyLocked([&](raw_ostream &OS) {
407
0
            OS << "Error while scanning dependencies for " << Input << ":\n";
408
0
            OS << Err.getMessage();
409
0
          });
410
0
        });
411
0
    return true;
412
0
  }
413
23
  FD.mergeDeps(Input, std::move(*MaybeFullDeps), InputIndex);
414
23
  return false;
415
23
}
416
417
54
int main(int argc, const char **argv) {
418
54
  llvm::InitLLVM X(argc, argv);
419
54
  llvm::cl::HideUnrelatedOptions(DependencyScannerCategory);
420
54
  if (!llvm::cl::ParseCommandLineOptions(argc, argv))
421
0
    return 1;
422
423
54
  std::string ErrorMessage;
424
54
  std::unique_ptr<tooling::JSONCompilationDatabase> Compilations =
425
54
      tooling::JSONCompilationDatabase::loadFromFile(
426
54
          CompilationDB, ErrorMessage,
427
54
          tooling::JSONCommandLineSyntax::AutoDetect);
428
54
  if (!Compilations) {
429
0
    llvm::errs() << "error: " << ErrorMessage << "\n";
430
0
    return 1;
431
0
  }
432
433
54
  llvm::cl::PrintOptionValues();
434
435
  // The command options are rewritten to run Clang in preprocessor only mode.
436
54
  auto AdjustingCompilations =
437
54
      std::make_unique<tooling::ArgumentsAdjustingCompilations>(
438
54
          std::move(Compilations));
439
54
  ResourceDirectoryCache ResourceDirCache;
440
441
54
  AdjustingCompilations->appendArgumentsAdjuster(
442
54
      [&ResourceDirCache](const tooling::CommandLineArguments &Args,
443
155
                          StringRef FileName) {
444
155
        std::string LastO = "";
445
155
        bool HasResourceDir = false;
446
155
        bool ClangCLMode = false;
447
155
        auto FlagsEnd = llvm::find(Args, "--");
448
155
        if (FlagsEnd != Args.begin()) {
449
155
          ClangCLMode =
450
155
              llvm::sys::path::stem(Args[0]).contains_insensitive("clang-cl") ||
451
155
              
llvm::is_contained(Args, "--driver-mode=cl")98
;
452
453
          // Reverse scan, starting at the end or at the element before "--".
454
155
          auto R = llvm::make_reverse_iterator(FlagsEnd);
455
1.32k
          for (auto I = R, E = Args.rend(); I != E; 
++I1.17k
) {
456
1.17k
            StringRef Arg = *I;
457
1.17k
            if (ClangCLMode) {
458
              // Ignore arguments that are preceded by "-Xclang".
459
445
              if ((I + 1) != E && 
I[1] == "-Xclang"380
)
460
4
                continue;
461
441
              if (LastO.empty()) {
462
                // With clang-cl, the output obj file can be specified with
463
                // "/opath", "/o path", "/Fopath", and the dash counterparts.
464
                // Also, clang-cl adds ".obj" extension if none is found.
465
299
                if ((Arg == "-o" || 
Arg == "/o"285
) &&
I != R15
)
466
15
                  LastO = I[-1]; // Next argument (reverse iterator)
467
284
                else if (Arg.startswith("/Fo") || 
Arg.startswith("-Fo")274
)
468
10
                  LastO = Arg.drop_front(3).str();
469
274
                else if (Arg.startswith("/o") || 
Arg.startswith("-o")272
)
470
3
                  LastO = Arg.drop_front(2).str();
471
472
299
                if (!LastO.empty() && 
!llvm::sys::path::has_extension(LastO)28
)
473
0
                  LastO.append(".obj");
474
299
              }
475
441
            }
476
1.16k
            if (Arg == "-resource-dir")
477
0
              HasResourceDir = true;
478
1.16k
          }
479
155
        }
480
155
        tooling::CommandLineArguments AdjustedArgs(Args.begin(), FlagsEnd);
481
        // The clang-cl driver passes "-o -" to the frontend. Inject the real
482
        // file here to ensure "-MT" can be deduced if need be.
483
155
        if (ClangCLMode && 
!LastO.empty()65
) {
484
28
          AdjustedArgs.push_back("/clang:-o");
485
28
          AdjustedArgs.push_back("/clang:" + LastO);
486
28
        }
487
488
155
        if (!HasResourceDir) {
489
155
          StringRef ResourceDir =
490
155
              ResourceDirCache.findResourceDir(Args, ClangCLMode);
491
155
          if (!ResourceDir.empty()) {
492
0
            AdjustedArgs.push_back("-resource-dir");
493
0
            AdjustedArgs.push_back(std::string(ResourceDir));
494
0
          }
495
155
        }
496
155
        AdjustedArgs.insert(AdjustedArgs.end(), FlagsEnd, Args.end());
497
155
        return AdjustedArgs;
498
155
      });
499
500
54
  SharedStream Errs(llvm::errs());
501
  // Print out the dependency results to STDOUT by default.
502
54
  SharedStream DependencyOS(llvm::outs());
503
504
54
  DependencyScanningService Service(ScanMode, Format, ReuseFileManager,
505
54
                                    SkipExcludedPPRanges);
506
54
  llvm::ThreadPool Pool(llvm::hardware_concurrency(NumThreads));
507
54
  std::vector<std::unique_ptr<DependencyScanningTool>> WorkerTools;
508
198
  for (unsigned I = 0; I < Pool.getThreadCount(); 
++I144
)
509
144
    WorkerTools.push_back(std::make_unique<DependencyScanningTool>(Service));
510
511
54
  std::vector<SingleCommandCompilationDatabase> Inputs;
512
54
  for (tooling::CompileCommand Cmd :
513
54
       AdjustingCompilations->getAllCompileCommands())
514
155
    Inputs.emplace_back(Cmd);
515
516
54
  std::atomic<bool> HadErrors(false);
517
54
  FullDeps FD;
518
54
  std::mutex Lock;
519
54
  size_t Index = 0;
520
521
54
  if (Verbose) {
522
0
    llvm::outs() << "Running clang-scan-deps on " << Inputs.size()
523
0
                 << " files using " << Pool.getThreadCount() << " workers\n";
524
0
  }
525
198
  for (unsigned I = 0; I < Pool.getThreadCount(); 
++I144
) {
526
144
    Pool.async([I, &Lock, &Index, &Inputs, &HadErrors, &FD, &WorkerTools,
527
144
                &DependencyOS, &Errs]() {
528
144
      llvm::StringSet<> AlreadySeenModules;
529
298
      while (true) {
530
298
        const SingleCommandCompilationDatabase *Input;
531
298
        std::string Filename;
532
298
        std::string CWD;
533
298
        size_t LocalIndex;
534
        // Take the next input.
535
298
        {
536
298
          std::unique_lock<std::mutex> LockGuard(Lock);
537
298
          if (Index >= Inputs.size())
538
144
            return;
539
154
          LocalIndex = Index;
540
154
          Input = &Inputs[Index++];
541
154
          tooling::CompileCommand Cmd = Input->getAllCompileCommands()[0];
542
154
          Filename = std::move(Cmd.Filename);
543
154
          CWD = std::move(Cmd.Directory);
544
154
        }
545
        // Run the tool on it.
546
154
        if (Format == ScanningOutputFormat::Make) {
547
132
          auto MaybeFile = WorkerTools[I]->getDependencyFile(*Input, CWD);
548
132
          if (handleMakeDependencyToolResult(Filename, MaybeFile, DependencyOS,
549
132
                                             Errs))
550
6
            HadErrors = true;
551
132
        } else {
552
22
          auto MaybeFullDeps = WorkerTools[I]->getFullDependencies(
553
22
              *Input, CWD, AlreadySeenModules);
554
22
          if (handleFullDependencyToolResult(Filename, MaybeFullDeps, FD,
555
22
                                             LocalIndex, DependencyOS, Errs))
556
0
            HadErrors = true;
557
22
        }
558
154
      }
559
144
    });
560
144
  }
561
54
  Pool.wait();
562
563
54
  if (Format == ScanningOutputFormat::Full)
564
11
    FD.printFullOutput(llvm::outs());
565
566
54
  return HadErrors;
567
54
}