Coverage Report

Created: 2022-07-16 07:03

/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
176
  SharedStream(raw_ostream &OS) : OS(OS) {}
36
137
  void applyLocked(llvm::function_ref<void(raw_ostream &OS)> Fn) {
37
137
    std::unique_lock<std::mutex> LockGuard(Lock);
38
137
    Fn(OS);
39
137
    OS.flush();
40
137
  }
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
1
                            bool ClangCLMode) {
56
1
    if (Args.size() < 1)
57
0
      return "";
58
59
1
    const std::string &ClangBinaryPath = Args[0];
60
1
    if (!llvm::sys::path::is_absolute(ClangBinaryPath))
61
0
      return "";
62
63
1
    const std::string &ClangBinaryName =
64
1
        std::string(llvm::sys::path::filename(ClangBinaryPath));
65
66
1
    std::unique_lock<std::mutex> LockGuard(CacheLock);
67
1
    const auto &CachedResourceDir = Cache.find(ClangBinaryPath);
68
1
    if (CachedResourceDir != Cache.end())
69
0
      return CachedResourceDir->second;
70
71
1
    std::vector<StringRef> PrintResourceDirArgs{ClangBinaryName};
72
1
    if (ClangCLMode)
73
0
      PrintResourceDirArgs.push_back("/clang:-print-resource-dir");
74
1
    else
75
1
      PrintResourceDirArgs.push_back("-print-resource-dir");
76
77
1
    llvm::SmallString<64> OutputFile, ErrorFile;
78
1
    llvm::sys::fs::createTemporaryFile("print-resource-dir-output",
79
1
                                       "" /*no-suffix*/, OutputFile);
80
1
    llvm::sys::fs::createTemporaryFile("print-resource-dir-error",
81
1
                                       "" /*no-suffix*/, ErrorFile);
82
1
    llvm::FileRemover OutputRemover(OutputFile.c_str());
83
1
    llvm::FileRemover ErrorRemover(ErrorFile.c_str());
84
1
    llvm::Optional<StringRef> Redirects[] = {
85
1
        {""}, // Stdin
86
1
        OutputFile.str(),
87
1
        ErrorFile.str(),
88
1
    };
89
1
    if (const int RC = llvm::sys::ExecuteAndWait(
90
1
            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
1
    auto OutputBuf = llvm::MemoryBuffer::getFile(OutputFile.c_str());
97
1
    if (!OutputBuf)
98
0
      return "";
99
1
    StringRef Output = OutputBuf.get()->getBuffer().rtrim('\n');
100
101
1
    Cache[ClangBinaryPath] = Output.str();
102
1
    return Cache[ClangBinaryPath];
103
1
  }
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
236
handleMakeDependencyToolResult(const std::string &Input,
237
                               llvm::Expected<std::string> &MaybeFile,
238
137
                               SharedStream &OS, SharedStream &Errs) {
239
137
  if (!MaybeFile) {
240
7
    llvm::handleAllErrors(
241
7
        MaybeFile.takeError(), [&Input, &Errs](llvm::StringError &Err) {
242
7
          Errs.applyLocked([&](raw_ostream &OS) {
243
7
            OS << "Error while scanning dependencies for " << Input << ":\n";
244
7
            OS << Err.getMessage();
245
7
          });
246
7
        });
247
7
    return true;
248
7
  }
249
130
  OS.applyLocked([&](raw_ostream &OS) { OS << *MaybeFile; });
250
130
  return false;
251
137
}
252
253
77
static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) {
254
77
  std::vector<llvm::StringRef> Strings;
255
77
  for (auto &&I : Set)
256
178
    Strings.push_back(I.getKey());
257
77
  llvm::sort(Strings);
258
77
  return llvm::json::Array(Strings);
259
77
}
260
261
131
static llvm::json::Array toJSONSorted(std::vector<ModuleID> V) {
262
131
  llvm::sort(V, [](const ModuleID &A, const ModuleID &B) {
263
39
    return std::tie(A.ModuleName, A.ContextHash) <
264
39
           std::tie(B.ModuleName, B.ContextHash);
265
39
  });
266
267
131
  llvm::json::Array Ret;
268
131
  for (const ModuleID &MID : V)
269
85
    Ret.push_back(llvm::json::Object(
270
85
        {{"module-name", MID.ModuleName}, {"context-hash", MID.ContextHash}}));
271
131
  return Ret;
272
131
}
273
274
// Thread safe.
275
class FullDeps {
276
public:
277
  void mergeDeps(StringRef Input, FullDependenciesResult FDR,
278
54
                 size_t InputIndex) {
279
54
    const FullDependencies &FD = FDR.FullDeps;
280
281
54
    InputDeps ID;
282
54
    ID.FileName = std::string(Input);
283
54
    ID.ContextHash = std::move(FD.ID.ContextHash);
284
54
    ID.FileDeps = std::move(FD.FileDeps);
285
54
    ID.ModuleDeps = std::move(FD.ClangModuleDeps);
286
287
54
    std::unique_lock<std::mutex> ul(Lock);
288
85
    for (const ModuleDeps &MD : FDR.DiscoveredModules) {
289
85
      auto I = Modules.find({MD.ID, 0});
290
85
      if (I != Modules.end()) {
291
8
        I->first.InputIndex = std::min(I->first.InputIndex, InputIndex);
292
8
        continue;
293
8
      }
294
77
      Modules.insert(I, {{MD.ID, InputIndex}, std::move(MD)});
295
77
    }
296
297
54
    ID.CommandLine =
298
54
        GenerateModulesPathArgs
299
54
            ? 
FD.getCommandLine([&](const ModuleID &MID, ModuleOutputKind MOK) 31
{
300
31
                return lookupModuleOutput(MID, MOK);
301
31
              })
302
54
            : 
FD.getCommandLineWithoutModulePaths()23
;
303
54
    Inputs.push_back(std::move(ID));
304
54
  }
305
306
40
  void printFullOutput(raw_ostream &OS) {
307
    // Sort the modules by name to get a deterministic order.
308
40
    std::vector<IndexedModuleID> ModuleIDs;
309
40
    for (auto &&M : Modules)
310
77
      ModuleIDs.push_back(M.first);
311
40
    llvm::sort(ModuleIDs,
312
98
               [](const IndexedModuleID &A, const IndexedModuleID &B) {
313
98
                 return std::tie(A.ID.ModuleName, A.InputIndex) <
314
98
                        std::tie(B.ID.ModuleName, B.InputIndex);
315
98
               });
316
317
40
    llvm::sort(Inputs, [](const InputDeps &A, const InputDeps &B) {
318
18
      return A.FileName < B.FileName;
319
18
    });
320
321
40
    using namespace llvm::json;
322
323
40
    Array OutModules;
324
77
    for (auto &&ModID : ModuleIDs) {
325
77
      auto &MD = Modules[ModID];
326
77
      Object O{
327
77
          {"name", MD.ID.ModuleName},
328
77
          {"context-hash", MD.ID.ContextHash},
329
77
          {"file-deps", toJSONSorted(MD.FileDeps)},
330
77
          {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)},
331
77
          {"clang-modulemap-file", MD.ClangModuleMapFile},
332
77
          {"command-line",
333
77
           GenerateModulesPathArgs
334
77
               ? MD.getCanonicalCommandLine(
335
94
                     [&](const ModuleID &MID, ModuleOutputKind MOK) {
336
94
                       return lookupModuleOutput(MID, MOK);
337
94
                     })
338
77
               : 
MD.getCanonicalCommandLineWithoutModulePaths()26
},
339
77
      };
340
77
      OutModules.push_back(std::move(O));
341
77
    }
342
343
40
    Array TUs;
344
54
    for (auto &&I : Inputs) {
345
54
      Object O{
346
54
          {"input-file", I.FileName},
347
54
          {"clang-context-hash", I.ContextHash},
348
54
          {"file-deps", I.FileDeps},
349
54
          {"clang-module-deps", toJSONSorted(I.ModuleDeps)},
350
54
          {"command-line", I.CommandLine},
351
54
      };
352
54
      TUs.push_back(std::move(O));
353
54
    }
354
355
40
    Object Output{
356
40
        {"modules", std::move(OutModules)},
357
40
        {"translation-units", std::move(TUs)},
358
40
    };
359
360
40
    OS << llvm::formatv("{0:2}\n", Value(std::move(Output)));
361
40
  }
362
363
private:
364
125
  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
125
    auto PCMPath = PCMPaths.insert({MID, ""});
368
125
    if (PCMPath.second)
369
51
      PCMPath.first->second = constructPCMPath(MID);
370
125
    switch (MOK) {
371
106
    case ModuleOutputKind::ModuleFile:
372
106
      return PCMPath.first->second;
373
8
    case ModuleOutputKind::DependencyFile:
374
8
      return PCMPath.first->second + ".d";
375
8
    case ModuleOutputKind::DependencyTargets:
376
      // Null-separate the list of targets.
377
8
      return join(ModuleDepTargets, StringRef("\0", 1));
378
3
    case ModuleOutputKind::DiagnosticSerializationFile:
379
3
      return PCMPath.first->second + ".diag";
380
125
    }
381
0
    llvm_unreachable("Fully covered switch above!");
382
0
  }
383
384
  /// Construct a path for the explicitly built PCM.
385
51
  std::string constructPCMPath(ModuleID MID) const {
386
51
    auto MDIt = Modules.find(IndexedModuleID{MID, 0});
387
51
    assert(MDIt != Modules.end());
388
0
    const ModuleDeps &MD = MDIt->second;
389
390
51
    StringRef Filename = llvm::sys::path::filename(MD.ImplicitModulePCMPath);
391
51
    StringRef ModuleCachePath = llvm::sys::path::parent_path(
392
51
        llvm::sys::path::parent_path(MD.ImplicitModulePCMPath));
393
394
51
    SmallString<256> ExplicitPCMPath(!ModuleFilesDir.empty() ? 
ModuleFilesDir32
395
51
                                                             : 
ModuleCachePath19
);
396
51
    llvm::sys::path::append(ExplicitPCMPath, MD.ID.ContextHash, Filename);
397
51
    return std::string(ExplicitPCMPath);
398
51
  }
399
400
  struct IndexedModuleID {
401
    ModuleID ID;
402
    mutable size_t InputIndex;
403
404
175
    bool operator==(const IndexedModuleID &Other) const {
405
175
      return ID.ModuleName == Other.ID.ModuleName &&
406
175
             
ID.ContextHash == Other.ID.ContextHash141
;
407
175
    }
408
  };
409
410
  struct IndexedModuleIDHasher {
411
290
    std::size_t operator()(const IndexedModuleID &IMID) const {
412
290
      using llvm::hash_combine;
413
414
290
      return hash_combine(IMID.ID.ModuleName, IMID.ID.ContextHash);
415
290
    }
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
433
static bool handleFullDependencyToolResult(
434
    const std::string &Input,
435
    llvm::Expected<FullDependenciesResult> &MaybeFullDeps, FullDeps &FD,
436
54
    size_t InputIndex, SharedStream &OS, SharedStream &Errs) {
437
54
  if (!MaybeFullDeps) {
438
0
    llvm::handleAllErrors(
439
0
        MaybeFullDeps.takeError(), [&Input, &Errs](llvm::StringError &Err) {
440
0
          Errs.applyLocked([&](raw_ostream &OS) {
441
0
            OS << "Error while scanning dependencies for " << Input << ":\n";
442
0
            OS << Err.getMessage();
443
0
          });
444
0
        });
445
0
    return true;
446
0
  }
447
54
  FD.mergeDeps(Input, std::move(*MaybeFullDeps), InputIndex);
448
54
  return false;
449
54
}
450
451
88
int main(int argc, const char **argv) {
452
88
  llvm::InitLLVM X(argc, argv);
453
88
  llvm::cl::HideUnrelatedOptions(DependencyScannerCategory);
454
88
  if (!llvm::cl::ParseCommandLineOptions(argc, argv))
455
0
    return 1;
456
457
88
  std::string ErrorMessage;
458
88
  std::unique_ptr<tooling::JSONCompilationDatabase> Compilations =
459
88
      tooling::JSONCompilationDatabase::loadFromFile(
460
88
          CompilationDB, ErrorMessage,
461
88
          tooling::JSONCommandLineSyntax::AutoDetect);
462
88
  if (!Compilations) {
463
0
    llvm::errs() << "error: " << ErrorMessage << "\n";
464
0
    return 1;
465
0
  }
466
467
88
  llvm::cl::PrintOptionValues();
468
469
  // The command options are rewritten to run Clang in preprocessor only mode.
470
88
  auto AdjustingCompilations =
471
88
      std::make_unique<tooling::ArgumentsAdjustingCompilations>(
472
88
          std::move(Compilations));
473
88
  ResourceDirectoryCache ResourceDirCache;
474
475
88
  AdjustingCompilations->appendArgumentsAdjuster(
476
88
      [&ResourceDirCache](const tooling::CommandLineArguments &Args,
477
191
                          StringRef FileName) {
478
191
        std::string LastO;
479
191
        bool HasResourceDir = false;
480
191
        bool ClangCLMode = false;
481
191
        auto FlagsEnd = llvm::find(Args, "--");
482
191
        if (FlagsEnd != Args.begin()) {
483
191
          ClangCLMode =
484
191
              llvm::sys::path::stem(Args[0]).contains_insensitive("clang-cl") ||
485
191
              
llvm::is_contained(Args, "--driver-mode=cl")134
;
486
487
          // Reverse scan, starting at the end or at the element before "--".
488
191
          auto R = std::make_reverse_iterator(FlagsEnd);
489
1.75k
          for (auto I = R, E = Args.rend(); I != E; 
++I1.56k
) {
490
1.56k
            StringRef Arg = *I;
491
1.56k
            if (ClangCLMode) {
492
              // Ignore arguments that are preceded by "-Xclang".
493
454
              if ((I + 1) != E && 
I[1] == "-Xclang"390
)
494
4
                continue;
495
450
              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
311
                if ((Arg == "-o" || 
Arg == "/o"297
) &&
I != R15
)
500
15
                  LastO = I[-1]; // Next argument (reverse iterator)
501
296
                else if (Arg.startswith("/Fo") || 
Arg.startswith("-Fo")287
)
502
9
                  LastO = Arg.drop_front(3).str();
503
287
                else if (Arg.startswith("/o") || 
Arg.startswith("-o")285
)
504
3
                  LastO = Arg.drop_front(2).str();
505
506
311
                if (!LastO.empty() && 
!llvm::sys::path::has_extension(LastO)27
)
507
0
                  LastO.append(".obj");
508
311
              }
509
450
            }
510
1.55k
            if (Arg == "-resource-dir")
511
0
              HasResourceDir = true;
512
1.55k
          }
513
191
        }
514
191
        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
191
        if (ClangCLMode && 
!LastO.empty()64
) {
518
27
          AdjustedArgs.push_back("/clang:-o");
519
27
          AdjustedArgs.push_back("/clang:" + LastO);
520
27
        }
521
522
191
        if (!HasResourceDir && ResourceDirRecipe == RDRK_InvokeCompiler) {
523
1
          StringRef ResourceDir =
524
1
              ResourceDirCache.findResourceDir(Args, ClangCLMode);
525
1
          if (!ResourceDir.empty()) {
526
1
            AdjustedArgs.push_back("-resource-dir");
527
1
            AdjustedArgs.push_back(std::string(ResourceDir));
528
1
          }
529
1
        }
530
191
        AdjustedArgs.insert(AdjustedArgs.end(), FlagsEnd, Args.end());
531
191
        return AdjustedArgs;
532
191
      });
533
534
88
  SharedStream Errs(llvm::errs());
535
  // Print out the dependency results to STDOUT by default.
536
88
  SharedStream DependencyOS(llvm::outs());
537
538
88
  DependencyScanningService Service(ScanMode, Format, ReuseFileManager,
539
88
                                    OptimizeArgs);
540
88
  llvm::ThreadPool Pool(llvm::hardware_concurrency(NumThreads));
541
88
  std::vector<std::unique_ptr<DependencyScanningTool>> WorkerTools;
542
662
  for (unsigned I = 0; I < Pool.getThreadCount(); 
++I574
)
543
574
    WorkerTools.push_back(std::make_unique<DependencyScanningTool>(Service));
544
545
88
  std::vector<tooling::CompileCommand> Inputs =
546
88
      AdjustingCompilations->getAllCompileCommands();
547
548
88
  std::atomic<bool> HadErrors(false);
549
88
  FullDeps FD;
550
88
  std::mutex Lock;
551
88
  size_t Index = 0;
552
553
88
  if (Verbose) {
554
0
    llvm::outs() << "Running clang-scan-deps on " << Inputs.size()
555
0
                 << " files using " << Pool.getThreadCount() << " workers\n";
556
0
  }
557
662
  for (unsigned I = 0; I < Pool.getThreadCount(); 
++I574
) {
558
574
    Pool.async([I, &Lock, &Index, &Inputs, &HadErrors, &FD, &WorkerTools,
559
574
                &DependencyOS, &Errs]() {
560
568
      llvm::StringSet<> AlreadySeenModules;
561
759
      while (
true753
) {
562
759
        const tooling::CompileCommand *Input;
563
759
        std::string Filename;
564
759
        std::string CWD;
565
759
        size_t LocalIndex;
566
        // Take the next input.
567
759
        {
568
759
          std::unique_lock<std::mutex> LockGuard(Lock);
569
759
          if (Index >= Inputs.size())
570
574
            return;
571
185
          LocalIndex = Index;
572
185
          Input = &Inputs[Index++];
573
185
          Filename = std::move(Input->Filename);
574
185
          CWD = std::move(Input->Directory);
575
185
        }
576
0
        Optional<StringRef> MaybeModuleName;
577
185
        if (!ModuleName.empty())
578
4
          MaybeModuleName = ModuleName;
579
        // Run the tool on it.
580
185
        if (Format == ScanningOutputFormat::Make) {
581
137
          auto MaybeFile = WorkerTools[I]->getDependencyFile(
582
137
              Input->CommandLine, CWD, MaybeModuleName);
583
137
          if (handleMakeDependencyToolResult(Filename, MaybeFile, DependencyOS,
584
137
                                             Errs))
585
7
            HadErrors = true;
586
137
        } else {
587
48
          auto MaybeFullDeps = WorkerTools[I]->getFullDependencies(
588
48
              Input->CommandLine, CWD, AlreadySeenModules, MaybeModuleName);
589
48
          if (handleFullDependencyToolResult(Filename, MaybeFullDeps, FD,
590
48
                                             LocalIndex, DependencyOS, Errs))
591
0
            HadErrors = true;
592
48
        }
593
185
      }
594
568
    });
595
574
  }
596
88
  Pool.wait();
597
598
88
  if (Format == ScanningOutputFormat::Full)
599
40
    FD.printFullOutput(llvm::outs());
600
601
88
  return HadErrors;
602
88
}