xref: /freebsd/contrib/llvm-project/clang/tools/clang-scan-deps/ClangScanDeps.cpp (revision 7ed1628066eaf55b86f35af86efe804508201cc8)
1*7ed16280SDimitry Andric //===- ClangScanDeps.cpp - Implementation of clang-scan-deps --------------===//
2*7ed16280SDimitry Andric //
3*7ed16280SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*7ed16280SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*7ed16280SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*7ed16280SDimitry Andric //
7*7ed16280SDimitry Andric //===----------------------------------------------------------------------===//
8*7ed16280SDimitry Andric 
9*7ed16280SDimitry Andric #include "clang/Driver/Compilation.h"
10*7ed16280SDimitry Andric #include "clang/Driver/Driver.h"
11*7ed16280SDimitry Andric #include "clang/Frontend/CompilerInstance.h"
12*7ed16280SDimitry Andric #include "clang/Frontend/TextDiagnosticPrinter.h"
13*7ed16280SDimitry Andric #include "clang/Tooling/CommonOptionsParser.h"
14*7ed16280SDimitry Andric #include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
15*7ed16280SDimitry Andric #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
16*7ed16280SDimitry Andric #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
17*7ed16280SDimitry Andric #include "clang/Tooling/JSONCompilationDatabase.h"
18*7ed16280SDimitry Andric #include "clang/Tooling/Tooling.h"
19*7ed16280SDimitry Andric #include "llvm/ADT/STLExtras.h"
20*7ed16280SDimitry Andric #include "llvm/ADT/Twine.h"
21*7ed16280SDimitry Andric #include "llvm/Support/CommandLine.h"
22*7ed16280SDimitry Andric #include "llvm/Support/FileUtilities.h"
23*7ed16280SDimitry Andric #include "llvm/Support/Format.h"
24*7ed16280SDimitry Andric #include "llvm/Support/JSON.h"
25*7ed16280SDimitry Andric #include "llvm/Support/LLVMDriver.h"
26*7ed16280SDimitry Andric #include "llvm/Support/Program.h"
27*7ed16280SDimitry Andric #include "llvm/Support/Signals.h"
28*7ed16280SDimitry Andric #include "llvm/Support/TargetSelect.h"
29*7ed16280SDimitry Andric #include "llvm/Support/ThreadPool.h"
30*7ed16280SDimitry Andric #include "llvm/Support/Threading.h"
31*7ed16280SDimitry Andric #include "llvm/Support/Timer.h"
32*7ed16280SDimitry Andric #include "llvm/TargetParser/Host.h"
33*7ed16280SDimitry Andric #include <mutex>
34*7ed16280SDimitry Andric #include <optional>
35*7ed16280SDimitry Andric #include <thread>
36*7ed16280SDimitry Andric 
37*7ed16280SDimitry Andric #include "Opts.inc"
38*7ed16280SDimitry Andric 
39*7ed16280SDimitry Andric using namespace clang;
40*7ed16280SDimitry Andric using namespace tooling::dependencies;
41*7ed16280SDimitry Andric 
42*7ed16280SDimitry Andric namespace {
43*7ed16280SDimitry Andric 
44*7ed16280SDimitry Andric using namespace llvm::opt;
45*7ed16280SDimitry Andric enum ID {
46*7ed16280SDimitry Andric   OPT_INVALID = 0, // This is not an option ID.
47*7ed16280SDimitry Andric #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
48*7ed16280SDimitry Andric #include "Opts.inc"
49*7ed16280SDimitry Andric #undef OPTION
50*7ed16280SDimitry Andric };
51*7ed16280SDimitry Andric 
52*7ed16280SDimitry Andric #define PREFIX(NAME, VALUE)                                                    \
53*7ed16280SDimitry Andric   constexpr llvm::StringLiteral NAME##_init[] = VALUE;                         \
54*7ed16280SDimitry Andric   constexpr llvm::ArrayRef<llvm::StringLiteral> NAME(                          \
55*7ed16280SDimitry Andric       NAME##_init, std::size(NAME##_init) - 1);
56*7ed16280SDimitry Andric #include "Opts.inc"
57*7ed16280SDimitry Andric #undef PREFIX
58*7ed16280SDimitry Andric 
59*7ed16280SDimitry Andric const llvm::opt::OptTable::Info InfoTable[] = {
60*7ed16280SDimitry Andric #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
61*7ed16280SDimitry Andric #include "Opts.inc"
62*7ed16280SDimitry Andric #undef OPTION
63*7ed16280SDimitry Andric };
64*7ed16280SDimitry Andric 
65*7ed16280SDimitry Andric class ScanDepsOptTable : public llvm::opt::GenericOptTable {
66*7ed16280SDimitry Andric public:
ScanDepsOptTable()67*7ed16280SDimitry Andric   ScanDepsOptTable() : GenericOptTable(InfoTable) {
68*7ed16280SDimitry Andric     setGroupedShortOptions(true);
69*7ed16280SDimitry Andric   }
70*7ed16280SDimitry Andric };
71*7ed16280SDimitry Andric 
72*7ed16280SDimitry Andric enum ResourceDirRecipeKind {
73*7ed16280SDimitry Andric   RDRK_ModifyCompilerPath,
74*7ed16280SDimitry Andric   RDRK_InvokeCompiler,
75*7ed16280SDimitry Andric };
76*7ed16280SDimitry Andric 
77*7ed16280SDimitry Andric static std::string OutputFileName = "-";
78*7ed16280SDimitry Andric static ScanningMode ScanMode = ScanningMode::DependencyDirectivesScan;
79*7ed16280SDimitry Andric static ScanningOutputFormat Format = ScanningOutputFormat::Make;
80*7ed16280SDimitry Andric static ScanningOptimizations OptimizeArgs;
81*7ed16280SDimitry Andric static std::string ModuleFilesDir;
82*7ed16280SDimitry Andric static bool EagerLoadModules;
83*7ed16280SDimitry Andric static unsigned NumThreads = 0;
84*7ed16280SDimitry Andric static std::string CompilationDB;
85*7ed16280SDimitry Andric static std::string ModuleName;
86*7ed16280SDimitry Andric static std::vector<std::string> ModuleDepTargets;
87*7ed16280SDimitry Andric static bool DeprecatedDriverCommand;
88*7ed16280SDimitry Andric static ResourceDirRecipeKind ResourceDirRecipe;
89*7ed16280SDimitry Andric static bool Verbose;
90*7ed16280SDimitry Andric static bool PrintTiming;
91*7ed16280SDimitry Andric static llvm::BumpPtrAllocator Alloc;
92*7ed16280SDimitry Andric static llvm::StringSaver Saver{Alloc};
93*7ed16280SDimitry Andric static std::vector<const char *> CommandLine;
94*7ed16280SDimitry Andric 
95*7ed16280SDimitry Andric #ifndef NDEBUG
96*7ed16280SDimitry Andric static constexpr bool DoRoundTripDefault = true;
97*7ed16280SDimitry Andric #else
98*7ed16280SDimitry Andric static constexpr bool DoRoundTripDefault = false;
99*7ed16280SDimitry Andric #endif
100*7ed16280SDimitry Andric 
101*7ed16280SDimitry Andric static bool RoundTripArgs = DoRoundTripDefault;
102*7ed16280SDimitry Andric 
ParseArgs(int argc,char ** argv)103*7ed16280SDimitry Andric static void ParseArgs(int argc, char **argv) {
104*7ed16280SDimitry Andric   ScanDepsOptTable Tbl;
105*7ed16280SDimitry Andric   llvm::StringRef ToolName = argv[0];
106*7ed16280SDimitry Andric   llvm::opt::InputArgList Args =
107*7ed16280SDimitry Andric       Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
108*7ed16280SDimitry Andric         llvm::errs() << Msg << '\n';
109*7ed16280SDimitry Andric         std::exit(1);
110*7ed16280SDimitry Andric       });
111*7ed16280SDimitry Andric 
112*7ed16280SDimitry Andric   if (Args.hasArg(OPT_help)) {
113*7ed16280SDimitry Andric     Tbl.printHelp(llvm::outs(), "clang-scan-deps [options]", "clang-scan-deps");
114*7ed16280SDimitry Andric     std::exit(0);
115*7ed16280SDimitry Andric   }
116*7ed16280SDimitry Andric   if (Args.hasArg(OPT_version)) {
117*7ed16280SDimitry Andric     llvm::outs() << ToolName << '\n';
118*7ed16280SDimitry Andric     llvm::cl::PrintVersionMessage();
119*7ed16280SDimitry Andric     std::exit(0);
120*7ed16280SDimitry Andric   }
121*7ed16280SDimitry Andric   if (const llvm::opt::Arg *A = Args.getLastArg(OPT_mode_EQ)) {
122*7ed16280SDimitry Andric     auto ModeType =
123*7ed16280SDimitry Andric         llvm::StringSwitch<std::optional<ScanningMode>>(A->getValue())
124*7ed16280SDimitry Andric             .Case("preprocess-dependency-directives",
125*7ed16280SDimitry Andric                   ScanningMode::DependencyDirectivesScan)
126*7ed16280SDimitry Andric             .Case("preprocess", ScanningMode::CanonicalPreprocessing)
127*7ed16280SDimitry Andric             .Default(std::nullopt);
128*7ed16280SDimitry Andric     if (!ModeType) {
129*7ed16280SDimitry Andric       llvm::errs() << ToolName
130*7ed16280SDimitry Andric                    << ": for the --mode option: Cannot find option named '"
131*7ed16280SDimitry Andric                    << A->getValue() << "'\n";
132*7ed16280SDimitry Andric       std::exit(1);
133*7ed16280SDimitry Andric     }
134*7ed16280SDimitry Andric     ScanMode = *ModeType;
135*7ed16280SDimitry Andric   }
136*7ed16280SDimitry Andric 
137*7ed16280SDimitry Andric   if (const llvm::opt::Arg *A = Args.getLastArg(OPT_format_EQ)) {
138*7ed16280SDimitry Andric     auto FormatType =
139*7ed16280SDimitry Andric         llvm::StringSwitch<std::optional<ScanningOutputFormat>>(A->getValue())
140*7ed16280SDimitry Andric             .Case("make", ScanningOutputFormat::Make)
141*7ed16280SDimitry Andric             .Case("p1689", ScanningOutputFormat::P1689)
142*7ed16280SDimitry Andric             .Case("experimental-full", ScanningOutputFormat::Full)
143*7ed16280SDimitry Andric             .Default(std::nullopt);
144*7ed16280SDimitry Andric     if (!FormatType) {
145*7ed16280SDimitry Andric       llvm::errs() << ToolName
146*7ed16280SDimitry Andric                    << ": for the --format option: Cannot find option named '"
147*7ed16280SDimitry Andric                    << A->getValue() << "'\n";
148*7ed16280SDimitry Andric       std::exit(1);
149*7ed16280SDimitry Andric     }
150*7ed16280SDimitry Andric     Format = *FormatType;
151*7ed16280SDimitry Andric   }
152*7ed16280SDimitry Andric 
153*7ed16280SDimitry Andric   std::vector<std::string> OptimizationFlags =
154*7ed16280SDimitry Andric       Args.getAllArgValues(OPT_optimize_args_EQ);
155*7ed16280SDimitry Andric   OptimizeArgs = ScanningOptimizations::None;
156*7ed16280SDimitry Andric   for (const auto &Arg : OptimizationFlags) {
157*7ed16280SDimitry Andric     auto Optimization =
158*7ed16280SDimitry Andric         llvm::StringSwitch<std::optional<ScanningOptimizations>>(Arg)
159*7ed16280SDimitry Andric             .Case("none", ScanningOptimizations::None)
160*7ed16280SDimitry Andric             .Case("header-search", ScanningOptimizations::HeaderSearch)
161*7ed16280SDimitry Andric             .Case("system-warnings", ScanningOptimizations::SystemWarnings)
162*7ed16280SDimitry Andric             .Case("vfs", ScanningOptimizations::VFS)
163*7ed16280SDimitry Andric             .Case("canonicalize-macros", ScanningOptimizations::Macros)
164*7ed16280SDimitry Andric             .Case("all", ScanningOptimizations::All)
165*7ed16280SDimitry Andric             .Default(std::nullopt);
166*7ed16280SDimitry Andric     if (!Optimization) {
167*7ed16280SDimitry Andric       llvm::errs()
168*7ed16280SDimitry Andric           << ToolName
169*7ed16280SDimitry Andric           << ": for the --optimize-args option: Cannot find option named '"
170*7ed16280SDimitry Andric           << Arg << "'\n";
171*7ed16280SDimitry Andric       std::exit(1);
172*7ed16280SDimitry Andric     }
173*7ed16280SDimitry Andric     OptimizeArgs |= *Optimization;
174*7ed16280SDimitry Andric   }
175*7ed16280SDimitry Andric   if (OptimizationFlags.empty())
176*7ed16280SDimitry Andric     OptimizeArgs = ScanningOptimizations::Default;
177*7ed16280SDimitry Andric 
178*7ed16280SDimitry Andric   if (const llvm::opt::Arg *A = Args.getLastArg(OPT_module_files_dir_EQ))
179*7ed16280SDimitry Andric     ModuleFilesDir = A->getValue();
180*7ed16280SDimitry Andric 
181*7ed16280SDimitry Andric   if (const llvm::opt::Arg *A = Args.getLastArg(OPT_o))
182*7ed16280SDimitry Andric     OutputFileName = A->getValue();
183*7ed16280SDimitry Andric 
184*7ed16280SDimitry Andric   EagerLoadModules = Args.hasArg(OPT_eager_load_pcm);
185*7ed16280SDimitry Andric 
186*7ed16280SDimitry Andric   if (const llvm::opt::Arg *A = Args.getLastArg(OPT_j)) {
187*7ed16280SDimitry Andric     StringRef S{A->getValue()};
188*7ed16280SDimitry Andric     if (!llvm::to_integer(S, NumThreads, 0)) {
189*7ed16280SDimitry Andric       llvm::errs() << ToolName << ": for the -j option: '" << S
190*7ed16280SDimitry Andric                    << "' value invalid for uint argument!\n";
191*7ed16280SDimitry Andric       std::exit(1);
192*7ed16280SDimitry Andric     }
193*7ed16280SDimitry Andric   }
194*7ed16280SDimitry Andric 
195*7ed16280SDimitry Andric   if (const llvm::opt::Arg *A = Args.getLastArg(OPT_compilation_database_EQ))
196*7ed16280SDimitry Andric     CompilationDB = A->getValue();
197*7ed16280SDimitry Andric 
198*7ed16280SDimitry Andric   if (const llvm::opt::Arg *A = Args.getLastArg(OPT_module_name_EQ))
199*7ed16280SDimitry Andric     ModuleName = A->getValue();
200*7ed16280SDimitry Andric 
201*7ed16280SDimitry Andric   for (const llvm::opt::Arg *A : Args.filtered(OPT_dependency_target_EQ))
202*7ed16280SDimitry Andric     ModuleDepTargets.emplace_back(A->getValue());
203*7ed16280SDimitry Andric 
204*7ed16280SDimitry Andric   DeprecatedDriverCommand = Args.hasArg(OPT_deprecated_driver_command);
205*7ed16280SDimitry Andric 
206*7ed16280SDimitry Andric   if (const llvm::opt::Arg *A = Args.getLastArg(OPT_resource_dir_recipe_EQ)) {
207*7ed16280SDimitry Andric     auto Kind =
208*7ed16280SDimitry Andric         llvm::StringSwitch<std::optional<ResourceDirRecipeKind>>(A->getValue())
209*7ed16280SDimitry Andric             .Case("modify-compiler-path", RDRK_ModifyCompilerPath)
210*7ed16280SDimitry Andric             .Case("invoke-compiler", RDRK_InvokeCompiler)
211*7ed16280SDimitry Andric             .Default(std::nullopt);
212*7ed16280SDimitry Andric     if (!Kind) {
213*7ed16280SDimitry Andric       llvm::errs() << ToolName
214*7ed16280SDimitry Andric                    << ": for the --resource-dir-recipe option: Cannot find "
215*7ed16280SDimitry Andric                       "option named '"
216*7ed16280SDimitry Andric                    << A->getValue() << "'\n";
217*7ed16280SDimitry Andric       std::exit(1);
218*7ed16280SDimitry Andric     }
219*7ed16280SDimitry Andric     ResourceDirRecipe = *Kind;
220*7ed16280SDimitry Andric   }
221*7ed16280SDimitry Andric 
222*7ed16280SDimitry Andric   PrintTiming = Args.hasArg(OPT_print_timing);
223*7ed16280SDimitry Andric 
224*7ed16280SDimitry Andric   Verbose = Args.hasArg(OPT_verbose);
225*7ed16280SDimitry Andric 
226*7ed16280SDimitry Andric   RoundTripArgs = Args.hasArg(OPT_round_trip_args);
227*7ed16280SDimitry Andric 
228*7ed16280SDimitry Andric   if (const llvm::opt::Arg *A = Args.getLastArgNoClaim(OPT_DASH_DASH))
229*7ed16280SDimitry Andric     CommandLine.assign(A->getValues().begin(), A->getValues().end());
230*7ed16280SDimitry Andric }
231*7ed16280SDimitry Andric 
232*7ed16280SDimitry Andric class SharedStream {
233*7ed16280SDimitry Andric public:
SharedStream(raw_ostream & OS)234*7ed16280SDimitry Andric   SharedStream(raw_ostream &OS) : OS(OS) {}
applyLocked(llvm::function_ref<void (raw_ostream & OS)> Fn)235*7ed16280SDimitry Andric   void applyLocked(llvm::function_ref<void(raw_ostream &OS)> Fn) {
236*7ed16280SDimitry Andric     std::unique_lock<std::mutex> LockGuard(Lock);
237*7ed16280SDimitry Andric     Fn(OS);
238*7ed16280SDimitry Andric     OS.flush();
239*7ed16280SDimitry Andric   }
240*7ed16280SDimitry Andric 
241*7ed16280SDimitry Andric private:
242*7ed16280SDimitry Andric   std::mutex Lock;
243*7ed16280SDimitry Andric   raw_ostream &OS;
244*7ed16280SDimitry Andric };
245*7ed16280SDimitry Andric 
246*7ed16280SDimitry Andric class ResourceDirectoryCache {
247*7ed16280SDimitry Andric public:
248*7ed16280SDimitry Andric   /// findResourceDir finds the resource directory relative to the clang
249*7ed16280SDimitry Andric   /// compiler being used in Args, by running it with "-print-resource-dir"
250*7ed16280SDimitry Andric   /// option and cache the results for reuse. \returns resource directory path
251*7ed16280SDimitry Andric   /// associated with the given invocation command or empty string if the
252*7ed16280SDimitry Andric   /// compiler path is NOT an absolute path.
findResourceDir(const tooling::CommandLineArguments & Args,bool ClangCLMode)253*7ed16280SDimitry Andric   StringRef findResourceDir(const tooling::CommandLineArguments &Args,
254*7ed16280SDimitry Andric                             bool ClangCLMode) {
255*7ed16280SDimitry Andric     if (Args.size() < 1)
256*7ed16280SDimitry Andric       return "";
257*7ed16280SDimitry Andric 
258*7ed16280SDimitry Andric     const std::string &ClangBinaryPath = Args[0];
259*7ed16280SDimitry Andric     if (!llvm::sys::path::is_absolute(ClangBinaryPath))
260*7ed16280SDimitry Andric       return "";
261*7ed16280SDimitry Andric 
262*7ed16280SDimitry Andric     const std::string &ClangBinaryName =
263*7ed16280SDimitry Andric         std::string(llvm::sys::path::filename(ClangBinaryPath));
264*7ed16280SDimitry Andric 
265*7ed16280SDimitry Andric     std::unique_lock<std::mutex> LockGuard(CacheLock);
266*7ed16280SDimitry Andric     const auto &CachedResourceDir = Cache.find(ClangBinaryPath);
267*7ed16280SDimitry Andric     if (CachedResourceDir != Cache.end())
268*7ed16280SDimitry Andric       return CachedResourceDir->second;
269*7ed16280SDimitry Andric 
270*7ed16280SDimitry Andric     std::vector<StringRef> PrintResourceDirArgs{ClangBinaryName};
271*7ed16280SDimitry Andric     if (ClangCLMode)
272*7ed16280SDimitry Andric       PrintResourceDirArgs.push_back("/clang:-print-resource-dir");
273*7ed16280SDimitry Andric     else
274*7ed16280SDimitry Andric       PrintResourceDirArgs.push_back("-print-resource-dir");
275*7ed16280SDimitry Andric 
276*7ed16280SDimitry Andric     llvm::SmallString<64> OutputFile, ErrorFile;
277*7ed16280SDimitry Andric     llvm::sys::fs::createTemporaryFile("print-resource-dir-output",
278*7ed16280SDimitry Andric                                        "" /*no-suffix*/, OutputFile);
279*7ed16280SDimitry Andric     llvm::sys::fs::createTemporaryFile("print-resource-dir-error",
280*7ed16280SDimitry Andric                                        "" /*no-suffix*/, ErrorFile);
281*7ed16280SDimitry Andric     llvm::FileRemover OutputRemover(OutputFile.c_str());
282*7ed16280SDimitry Andric     llvm::FileRemover ErrorRemover(ErrorFile.c_str());
283*7ed16280SDimitry Andric     std::optional<StringRef> Redirects[] = {
284*7ed16280SDimitry Andric         {""}, // Stdin
285*7ed16280SDimitry Andric         OutputFile.str(),
286*7ed16280SDimitry Andric         ErrorFile.str(),
287*7ed16280SDimitry Andric     };
288*7ed16280SDimitry Andric     if (llvm::sys::ExecuteAndWait(ClangBinaryPath, PrintResourceDirArgs, {},
289*7ed16280SDimitry Andric                                   Redirects)) {
290*7ed16280SDimitry Andric       auto ErrorBuf = llvm::MemoryBuffer::getFile(ErrorFile.c_str());
291*7ed16280SDimitry Andric       llvm::errs() << ErrorBuf.get()->getBuffer();
292*7ed16280SDimitry Andric       return "";
293*7ed16280SDimitry Andric     }
294*7ed16280SDimitry Andric 
295*7ed16280SDimitry Andric     auto OutputBuf = llvm::MemoryBuffer::getFile(OutputFile.c_str());
296*7ed16280SDimitry Andric     if (!OutputBuf)
297*7ed16280SDimitry Andric       return "";
298*7ed16280SDimitry Andric     StringRef Output = OutputBuf.get()->getBuffer().rtrim('\n');
299*7ed16280SDimitry Andric 
300*7ed16280SDimitry Andric     Cache[ClangBinaryPath] = Output.str();
301*7ed16280SDimitry Andric     return Cache[ClangBinaryPath];
302*7ed16280SDimitry Andric   }
303*7ed16280SDimitry Andric 
304*7ed16280SDimitry Andric private:
305*7ed16280SDimitry Andric   std::map<std::string, std::string> Cache;
306*7ed16280SDimitry Andric   std::mutex CacheLock;
307*7ed16280SDimitry Andric };
308*7ed16280SDimitry Andric 
309*7ed16280SDimitry Andric } // end anonymous namespace
310*7ed16280SDimitry Andric 
311*7ed16280SDimitry Andric /// Takes the result of a dependency scan and prints error / dependency files
312*7ed16280SDimitry Andric /// based on the result.
313*7ed16280SDimitry Andric ///
314*7ed16280SDimitry Andric /// \returns True on error.
315*7ed16280SDimitry Andric static bool
handleMakeDependencyToolResult(const std::string & Input,llvm::Expected<std::string> & MaybeFile,SharedStream & OS,SharedStream & Errs)316*7ed16280SDimitry Andric handleMakeDependencyToolResult(const std::string &Input,
317*7ed16280SDimitry Andric                                llvm::Expected<std::string> &MaybeFile,
318*7ed16280SDimitry Andric                                SharedStream &OS, SharedStream &Errs) {
319*7ed16280SDimitry Andric   if (!MaybeFile) {
320*7ed16280SDimitry Andric     llvm::handleAllErrors(
321*7ed16280SDimitry Andric         MaybeFile.takeError(), [&Input, &Errs](llvm::StringError &Err) {
322*7ed16280SDimitry Andric           Errs.applyLocked([&](raw_ostream &OS) {
323*7ed16280SDimitry Andric             OS << "Error while scanning dependencies for " << Input << ":\n";
324*7ed16280SDimitry Andric             OS << Err.getMessage();
325*7ed16280SDimitry Andric           });
326*7ed16280SDimitry Andric         });
327*7ed16280SDimitry Andric     return true;
328*7ed16280SDimitry Andric   }
329*7ed16280SDimitry Andric   OS.applyLocked([&](raw_ostream &OS) { OS << *MaybeFile; });
330*7ed16280SDimitry Andric   return false;
331*7ed16280SDimitry Andric }
332*7ed16280SDimitry Andric 
toJSONSorted(const llvm::StringSet<> & Set)333*7ed16280SDimitry Andric static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) {
334*7ed16280SDimitry Andric   std::vector<llvm::StringRef> Strings;
335*7ed16280SDimitry Andric   for (auto &&I : Set)
336*7ed16280SDimitry Andric     Strings.push_back(I.getKey());
337*7ed16280SDimitry Andric   llvm::sort(Strings);
338*7ed16280SDimitry Andric   return llvm::json::Array(Strings);
339*7ed16280SDimitry Andric }
340*7ed16280SDimitry Andric 
341*7ed16280SDimitry Andric // Technically, we don't need to sort the dependency list to get determinism.
342*7ed16280SDimitry Andric // Leaving these be will simply preserve the import order.
toJSONSorted(std::vector<ModuleID> V)343*7ed16280SDimitry Andric static llvm::json::Array toJSONSorted(std::vector<ModuleID> V) {
344*7ed16280SDimitry Andric   llvm::sort(V);
345*7ed16280SDimitry Andric 
346*7ed16280SDimitry Andric   llvm::json::Array Ret;
347*7ed16280SDimitry Andric   for (const ModuleID &MID : V)
348*7ed16280SDimitry Andric     Ret.push_back(llvm::json::Object(
349*7ed16280SDimitry Andric         {{"module-name", MID.ModuleName}, {"context-hash", MID.ContextHash}}));
350*7ed16280SDimitry Andric   return Ret;
351*7ed16280SDimitry Andric }
352*7ed16280SDimitry Andric 
353*7ed16280SDimitry Andric static llvm::json::Array
toJSONSorted(llvm::SmallVector<Module::LinkLibrary,2> & LinkLibs)354*7ed16280SDimitry Andric toJSONSorted(llvm::SmallVector<Module::LinkLibrary, 2> &LinkLibs) {
355*7ed16280SDimitry Andric   llvm::sort(LinkLibs, [](const Module::LinkLibrary &lhs,
356*7ed16280SDimitry Andric                           const Module::LinkLibrary &rhs) {
357*7ed16280SDimitry Andric     return lhs.Library < rhs.Library;
358*7ed16280SDimitry Andric   });
359*7ed16280SDimitry Andric 
360*7ed16280SDimitry Andric   llvm::json::Array Ret;
361*7ed16280SDimitry Andric   for (const Module::LinkLibrary &LL : LinkLibs)
362*7ed16280SDimitry Andric     Ret.push_back(llvm::json::Object(
363*7ed16280SDimitry Andric         {{"link-name", LL.Library}, {"isFramework", LL.IsFramework}}));
364*7ed16280SDimitry Andric   return Ret;
365*7ed16280SDimitry Andric }
366*7ed16280SDimitry Andric 
367*7ed16280SDimitry Andric // Thread safe.
368*7ed16280SDimitry Andric class FullDeps {
369*7ed16280SDimitry Andric public:
FullDeps(size_t NumInputs)370*7ed16280SDimitry Andric   FullDeps(size_t NumInputs) : Inputs(NumInputs) {}
371*7ed16280SDimitry Andric 
mergeDeps(StringRef Input,TranslationUnitDeps TUDeps,size_t InputIndex)372*7ed16280SDimitry Andric   void mergeDeps(StringRef Input, TranslationUnitDeps TUDeps,
373*7ed16280SDimitry Andric                  size_t InputIndex) {
374*7ed16280SDimitry Andric     mergeDeps(std::move(TUDeps.ModuleGraph), InputIndex);
375*7ed16280SDimitry Andric 
376*7ed16280SDimitry Andric     InputDeps ID;
377*7ed16280SDimitry Andric     ID.FileName = std::string(Input);
378*7ed16280SDimitry Andric     ID.ContextHash = std::move(TUDeps.ID.ContextHash);
379*7ed16280SDimitry Andric     ID.FileDeps = std::move(TUDeps.FileDeps);
380*7ed16280SDimitry Andric     ID.ModuleDeps = std::move(TUDeps.ClangModuleDeps);
381*7ed16280SDimitry Andric     ID.DriverCommandLine = std::move(TUDeps.DriverCommandLine);
382*7ed16280SDimitry Andric     ID.Commands = std::move(TUDeps.Commands);
383*7ed16280SDimitry Andric 
384*7ed16280SDimitry Andric     assert(InputIndex < Inputs.size() && "Input index out of bounds");
385*7ed16280SDimitry Andric     assert(Inputs[InputIndex].FileName.empty() && "Result already populated");
386*7ed16280SDimitry Andric     Inputs[InputIndex] = std::move(ID);
387*7ed16280SDimitry Andric   }
388*7ed16280SDimitry Andric 
mergeDeps(ModuleDepsGraph Graph,size_t InputIndex)389*7ed16280SDimitry Andric   void mergeDeps(ModuleDepsGraph Graph, size_t InputIndex) {
390*7ed16280SDimitry Andric     std::vector<ModuleDeps *> NewMDs;
391*7ed16280SDimitry Andric     {
392*7ed16280SDimitry Andric       std::unique_lock<std::mutex> ul(Lock);
393*7ed16280SDimitry Andric       for (const ModuleDeps &MD : Graph) {
394*7ed16280SDimitry Andric         auto I = Modules.find({MD.ID, 0});
395*7ed16280SDimitry Andric         if (I != Modules.end()) {
396*7ed16280SDimitry Andric           I->first.InputIndex = std::min(I->first.InputIndex, InputIndex);
397*7ed16280SDimitry Andric           continue;
398*7ed16280SDimitry Andric         }
399*7ed16280SDimitry Andric         auto Res = Modules.insert(I, {{MD.ID, InputIndex}, std::move(MD)});
400*7ed16280SDimitry Andric         NewMDs.push_back(&Res->second);
401*7ed16280SDimitry Andric       }
402*7ed16280SDimitry Andric       // First call to \c getBuildArguments is somewhat expensive. Let's call it
403*7ed16280SDimitry Andric       // on the current thread (instead of the main one), and outside the
404*7ed16280SDimitry Andric       // critical section.
405*7ed16280SDimitry Andric       for (ModuleDeps *MD : NewMDs)
406*7ed16280SDimitry Andric         (void)MD->getBuildArguments();
407*7ed16280SDimitry Andric     }
408*7ed16280SDimitry Andric   }
409*7ed16280SDimitry Andric 
roundTripCommand(ArrayRef<std::string> ArgStrs,DiagnosticsEngine & Diags)410*7ed16280SDimitry Andric   bool roundTripCommand(ArrayRef<std::string> ArgStrs,
411*7ed16280SDimitry Andric                         DiagnosticsEngine &Diags) {
412*7ed16280SDimitry Andric     if (ArgStrs.empty() || ArgStrs[0] != "-cc1")
413*7ed16280SDimitry Andric       return false;
414*7ed16280SDimitry Andric     SmallVector<const char *> Args;
415*7ed16280SDimitry Andric     for (const std::string &Arg : ArgStrs)
416*7ed16280SDimitry Andric       Args.push_back(Arg.c_str());
417*7ed16280SDimitry Andric     return !CompilerInvocation::checkCC1RoundTrip(Args, Diags);
418*7ed16280SDimitry Andric   }
419*7ed16280SDimitry Andric 
420*7ed16280SDimitry Andric   // Returns \c true if any command lines fail to round-trip. We expect
421*7ed16280SDimitry Andric   // commands already be canonical when output by the scanner.
roundTripCommands(raw_ostream & ErrOS)422*7ed16280SDimitry Andric   bool roundTripCommands(raw_ostream &ErrOS) {
423*7ed16280SDimitry Andric     IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions{};
424*7ed16280SDimitry Andric     TextDiagnosticPrinter DiagConsumer(ErrOS, &*DiagOpts);
425*7ed16280SDimitry Andric     IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
426*7ed16280SDimitry Andric         CompilerInstance::createDiagnostics(&*DiagOpts, &DiagConsumer,
427*7ed16280SDimitry Andric                                             /*ShouldOwnClient=*/false);
428*7ed16280SDimitry Andric 
429*7ed16280SDimitry Andric     for (auto &&M : Modules)
430*7ed16280SDimitry Andric       if (roundTripCommand(M.second.getBuildArguments(), *Diags))
431*7ed16280SDimitry Andric         return true;
432*7ed16280SDimitry Andric 
433*7ed16280SDimitry Andric     for (auto &&I : Inputs)
434*7ed16280SDimitry Andric       for (const auto &Cmd : I.Commands)
435*7ed16280SDimitry Andric         if (roundTripCommand(Cmd.Arguments, *Diags))
436*7ed16280SDimitry Andric           return true;
437*7ed16280SDimitry Andric 
438*7ed16280SDimitry Andric     return false;
439*7ed16280SDimitry Andric   }
440*7ed16280SDimitry Andric 
printFullOutput(raw_ostream & OS)441*7ed16280SDimitry Andric   void printFullOutput(raw_ostream &OS) {
442*7ed16280SDimitry Andric     // Skip sorting modules and constructing the JSON object if the output
443*7ed16280SDimitry Andric     // cannot be observed anyway. This makes timings less noisy.
444*7ed16280SDimitry Andric     if (&OS == &llvm::nulls())
445*7ed16280SDimitry Andric       return;
446*7ed16280SDimitry Andric 
447*7ed16280SDimitry Andric     // Sort the modules by name to get a deterministic order.
448*7ed16280SDimitry Andric     std::vector<IndexedModuleID> ModuleIDs;
449*7ed16280SDimitry Andric     for (auto &&M : Modules)
450*7ed16280SDimitry Andric       ModuleIDs.push_back(M.first);
451*7ed16280SDimitry Andric     llvm::sort(ModuleIDs);
452*7ed16280SDimitry Andric 
453*7ed16280SDimitry Andric     using namespace llvm::json;
454*7ed16280SDimitry Andric 
455*7ed16280SDimitry Andric     Array OutModules;
456*7ed16280SDimitry Andric     for (auto &&ModID : ModuleIDs) {
457*7ed16280SDimitry Andric       auto &MD = Modules[ModID];
458*7ed16280SDimitry Andric       Object O{{"name", MD.ID.ModuleName},
459*7ed16280SDimitry Andric                {"context-hash", MD.ID.ContextHash},
460*7ed16280SDimitry Andric                {"file-deps", toJSONSorted(MD.FileDeps)},
461*7ed16280SDimitry Andric                {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)},
462*7ed16280SDimitry Andric                {"clang-modulemap-file", MD.ClangModuleMapFile},
463*7ed16280SDimitry Andric                {"command-line", MD.getBuildArguments()},
464*7ed16280SDimitry Andric                {"link-libraries", toJSONSorted(MD.LinkLibraries)}};
465*7ed16280SDimitry Andric       OutModules.push_back(std::move(O));
466*7ed16280SDimitry Andric     }
467*7ed16280SDimitry Andric 
468*7ed16280SDimitry Andric     Array TUs;
469*7ed16280SDimitry Andric     for (auto &&I : Inputs) {
470*7ed16280SDimitry Andric       Array Commands;
471*7ed16280SDimitry Andric       if (I.DriverCommandLine.empty()) {
472*7ed16280SDimitry Andric         for (const auto &Cmd : I.Commands) {
473*7ed16280SDimitry Andric           Object O{
474*7ed16280SDimitry Andric               {"input-file", I.FileName},
475*7ed16280SDimitry Andric               {"clang-context-hash", I.ContextHash},
476*7ed16280SDimitry Andric               {"file-deps", I.FileDeps},
477*7ed16280SDimitry Andric               {"clang-module-deps", toJSONSorted(I.ModuleDeps)},
478*7ed16280SDimitry Andric               {"executable", Cmd.Executable},
479*7ed16280SDimitry Andric               {"command-line", Cmd.Arguments},
480*7ed16280SDimitry Andric           };
481*7ed16280SDimitry Andric           Commands.push_back(std::move(O));
482*7ed16280SDimitry Andric         }
483*7ed16280SDimitry Andric       } else {
484*7ed16280SDimitry Andric         Object O{
485*7ed16280SDimitry Andric             {"input-file", I.FileName},
486*7ed16280SDimitry Andric             {"clang-context-hash", I.ContextHash},
487*7ed16280SDimitry Andric             {"file-deps", I.FileDeps},
488*7ed16280SDimitry Andric             {"clang-module-deps", toJSONSorted(I.ModuleDeps)},
489*7ed16280SDimitry Andric             {"executable", "clang"},
490*7ed16280SDimitry Andric             {"command-line", I.DriverCommandLine},
491*7ed16280SDimitry Andric         };
492*7ed16280SDimitry Andric         Commands.push_back(std::move(O));
493*7ed16280SDimitry Andric       }
494*7ed16280SDimitry Andric       TUs.push_back(Object{
495*7ed16280SDimitry Andric           {"commands", std::move(Commands)},
496*7ed16280SDimitry Andric       });
497*7ed16280SDimitry Andric     }
498*7ed16280SDimitry Andric 
499*7ed16280SDimitry Andric     Object Output{
500*7ed16280SDimitry Andric         {"modules", std::move(OutModules)},
501*7ed16280SDimitry Andric         {"translation-units", std::move(TUs)},
502*7ed16280SDimitry Andric     };
503*7ed16280SDimitry Andric 
504*7ed16280SDimitry Andric     OS << llvm::formatv("{0:2}\n", Value(std::move(Output)));
505*7ed16280SDimitry Andric   }
506*7ed16280SDimitry Andric 
507*7ed16280SDimitry Andric private:
508*7ed16280SDimitry Andric   struct IndexedModuleID {
509*7ed16280SDimitry Andric     ModuleID ID;
510*7ed16280SDimitry Andric 
511*7ed16280SDimitry Andric     // FIXME: This is mutable so that it can still be updated after insertion
512*7ed16280SDimitry Andric     //  into an unordered associative container. This is "fine", since this
513*7ed16280SDimitry Andric     //  field doesn't contribute to the hash, but it's a brittle hack.
514*7ed16280SDimitry Andric     mutable size_t InputIndex;
515*7ed16280SDimitry Andric 
operator ==FullDeps::IndexedModuleID516*7ed16280SDimitry Andric     bool operator==(const IndexedModuleID &Other) const {
517*7ed16280SDimitry Andric       return ID == Other.ID;
518*7ed16280SDimitry Andric     }
519*7ed16280SDimitry Andric 
operator <FullDeps::IndexedModuleID520*7ed16280SDimitry Andric     bool operator<(const IndexedModuleID &Other) const {
521*7ed16280SDimitry Andric       /// We need the output of clang-scan-deps to be deterministic. However,
522*7ed16280SDimitry Andric       /// the dependency graph may contain two modules with the same name. How
523*7ed16280SDimitry Andric       /// do we decide which one to print first? If we made that decision based
524*7ed16280SDimitry Andric       /// on the context hash, the ordering would be deterministic, but
525*7ed16280SDimitry Andric       /// different across machines. This can happen for example when the inputs
526*7ed16280SDimitry Andric       /// or the SDKs (which both contribute to the "context" hash) live in
527*7ed16280SDimitry Andric       /// different absolute locations. We solve that by tracking the index of
528*7ed16280SDimitry Andric       /// the first input TU that (transitively) imports the dependency, which
529*7ed16280SDimitry Andric       /// is always the same for the same input, resulting in deterministic
530*7ed16280SDimitry Andric       /// sorting that's also reproducible across machines.
531*7ed16280SDimitry Andric       return std::tie(ID.ModuleName, InputIndex) <
532*7ed16280SDimitry Andric              std::tie(Other.ID.ModuleName, Other.InputIndex);
533*7ed16280SDimitry Andric     }
534*7ed16280SDimitry Andric 
535*7ed16280SDimitry Andric     struct Hasher {
operator ()FullDeps::IndexedModuleID::Hasher536*7ed16280SDimitry Andric       std::size_t operator()(const IndexedModuleID &IMID) const {
537*7ed16280SDimitry Andric         return llvm::hash_value(IMID.ID);
538*7ed16280SDimitry Andric       }
539*7ed16280SDimitry Andric     };
540*7ed16280SDimitry Andric   };
541*7ed16280SDimitry Andric 
542*7ed16280SDimitry Andric   struct InputDeps {
543*7ed16280SDimitry Andric     std::string FileName;
544*7ed16280SDimitry Andric     std::string ContextHash;
545*7ed16280SDimitry Andric     std::vector<std::string> FileDeps;
546*7ed16280SDimitry Andric     std::vector<ModuleID> ModuleDeps;
547*7ed16280SDimitry Andric     std::vector<std::string> DriverCommandLine;
548*7ed16280SDimitry Andric     std::vector<Command> Commands;
549*7ed16280SDimitry Andric   };
550*7ed16280SDimitry Andric 
551*7ed16280SDimitry Andric   std::mutex Lock;
552*7ed16280SDimitry Andric   std::unordered_map<IndexedModuleID, ModuleDeps, IndexedModuleID::Hasher>
553*7ed16280SDimitry Andric       Modules;
554*7ed16280SDimitry Andric   std::vector<InputDeps> Inputs;
555*7ed16280SDimitry Andric };
556*7ed16280SDimitry Andric 
handleTranslationUnitResult(StringRef Input,llvm::Expected<TranslationUnitDeps> & MaybeTUDeps,FullDeps & FD,size_t InputIndex,SharedStream & OS,SharedStream & Errs)557*7ed16280SDimitry Andric static bool handleTranslationUnitResult(
558*7ed16280SDimitry Andric     StringRef Input, llvm::Expected<TranslationUnitDeps> &MaybeTUDeps,
559*7ed16280SDimitry Andric     FullDeps &FD, size_t InputIndex, SharedStream &OS, SharedStream &Errs) {
560*7ed16280SDimitry Andric   if (!MaybeTUDeps) {
561*7ed16280SDimitry Andric     llvm::handleAllErrors(
562*7ed16280SDimitry Andric         MaybeTUDeps.takeError(), [&Input, &Errs](llvm::StringError &Err) {
563*7ed16280SDimitry Andric           Errs.applyLocked([&](raw_ostream &OS) {
564*7ed16280SDimitry Andric             OS << "Error while scanning dependencies for " << Input << ":\n";
565*7ed16280SDimitry Andric             OS << Err.getMessage();
566*7ed16280SDimitry Andric           });
567*7ed16280SDimitry Andric         });
568*7ed16280SDimitry Andric     return true;
569*7ed16280SDimitry Andric   }
570*7ed16280SDimitry Andric   FD.mergeDeps(Input, std::move(*MaybeTUDeps), InputIndex);
571*7ed16280SDimitry Andric   return false;
572*7ed16280SDimitry Andric }
573*7ed16280SDimitry Andric 
handleModuleResult(StringRef ModuleName,llvm::Expected<ModuleDepsGraph> & MaybeModuleGraph,FullDeps & FD,size_t InputIndex,SharedStream & OS,SharedStream & Errs)574*7ed16280SDimitry Andric static bool handleModuleResult(
575*7ed16280SDimitry Andric     StringRef ModuleName, llvm::Expected<ModuleDepsGraph> &MaybeModuleGraph,
576*7ed16280SDimitry Andric     FullDeps &FD, size_t InputIndex, SharedStream &OS, SharedStream &Errs) {
577*7ed16280SDimitry Andric   if (!MaybeModuleGraph) {
578*7ed16280SDimitry Andric     llvm::handleAllErrors(MaybeModuleGraph.takeError(),
579*7ed16280SDimitry Andric                           [&ModuleName, &Errs](llvm::StringError &Err) {
580*7ed16280SDimitry Andric                             Errs.applyLocked([&](raw_ostream &OS) {
581*7ed16280SDimitry Andric                               OS << "Error while scanning dependencies for "
582*7ed16280SDimitry Andric                                  << ModuleName << ":\n";
583*7ed16280SDimitry Andric                               OS << Err.getMessage();
584*7ed16280SDimitry Andric                             });
585*7ed16280SDimitry Andric                           });
586*7ed16280SDimitry Andric     return true;
587*7ed16280SDimitry Andric   }
588*7ed16280SDimitry Andric   FD.mergeDeps(std::move(*MaybeModuleGraph), InputIndex);
589*7ed16280SDimitry Andric   return false;
590*7ed16280SDimitry Andric }
591*7ed16280SDimitry Andric 
592*7ed16280SDimitry Andric class P1689Deps {
593*7ed16280SDimitry Andric public:
printDependencies(raw_ostream & OS)594*7ed16280SDimitry Andric   void printDependencies(raw_ostream &OS) {
595*7ed16280SDimitry Andric     addSourcePathsToRequires();
596*7ed16280SDimitry Andric     // Sort the modules by name to get a deterministic order.
597*7ed16280SDimitry Andric     llvm::sort(Rules, [](const P1689Rule &A, const P1689Rule &B) {
598*7ed16280SDimitry Andric       return A.PrimaryOutput < B.PrimaryOutput;
599*7ed16280SDimitry Andric     });
600*7ed16280SDimitry Andric 
601*7ed16280SDimitry Andric     using namespace llvm::json;
602*7ed16280SDimitry Andric     Array OutputRules;
603*7ed16280SDimitry Andric     for (const P1689Rule &R : Rules) {
604*7ed16280SDimitry Andric       Object O{{"primary-output", R.PrimaryOutput}};
605*7ed16280SDimitry Andric 
606*7ed16280SDimitry Andric       if (R.Provides) {
607*7ed16280SDimitry Andric         Array Provides;
608*7ed16280SDimitry Andric         Object Provided{{"logical-name", R.Provides->ModuleName},
609*7ed16280SDimitry Andric                         {"source-path", R.Provides->SourcePath},
610*7ed16280SDimitry Andric                         {"is-interface", R.Provides->IsStdCXXModuleInterface}};
611*7ed16280SDimitry Andric         Provides.push_back(std::move(Provided));
612*7ed16280SDimitry Andric         O.insert({"provides", std::move(Provides)});
613*7ed16280SDimitry Andric       }
614*7ed16280SDimitry Andric 
615*7ed16280SDimitry Andric       Array Requires;
616*7ed16280SDimitry Andric       for (const P1689ModuleInfo &Info : R.Requires) {
617*7ed16280SDimitry Andric         Object RequiredInfo{{"logical-name", Info.ModuleName}};
618*7ed16280SDimitry Andric         if (!Info.SourcePath.empty())
619*7ed16280SDimitry Andric           RequiredInfo.insert({"source-path", Info.SourcePath});
620*7ed16280SDimitry Andric         Requires.push_back(std::move(RequiredInfo));
621*7ed16280SDimitry Andric       }
622*7ed16280SDimitry Andric 
623*7ed16280SDimitry Andric       if (!Requires.empty())
624*7ed16280SDimitry Andric         O.insert({"requires", std::move(Requires)});
625*7ed16280SDimitry Andric 
626*7ed16280SDimitry Andric       OutputRules.push_back(std::move(O));
627*7ed16280SDimitry Andric     }
628*7ed16280SDimitry Andric 
629*7ed16280SDimitry Andric     Object Output{
630*7ed16280SDimitry Andric         {"version", 1}, {"revision", 0}, {"rules", std::move(OutputRules)}};
631*7ed16280SDimitry Andric 
632*7ed16280SDimitry Andric     OS << llvm::formatv("{0:2}\n", Value(std::move(Output)));
633*7ed16280SDimitry Andric   }
634*7ed16280SDimitry Andric 
addRules(P1689Rule & Rule)635*7ed16280SDimitry Andric   void addRules(P1689Rule &Rule) {
636*7ed16280SDimitry Andric     std::unique_lock<std::mutex> LockGuard(Lock);
637*7ed16280SDimitry Andric     Rules.push_back(Rule);
638*7ed16280SDimitry Andric   }
639*7ed16280SDimitry Andric 
640*7ed16280SDimitry Andric private:
addSourcePathsToRequires()641*7ed16280SDimitry Andric   void addSourcePathsToRequires() {
642*7ed16280SDimitry Andric     llvm::DenseMap<StringRef, StringRef> ModuleSourceMapper;
643*7ed16280SDimitry Andric     for (const P1689Rule &R : Rules)
644*7ed16280SDimitry Andric       if (R.Provides && !R.Provides->SourcePath.empty())
645*7ed16280SDimitry Andric         ModuleSourceMapper[R.Provides->ModuleName] = R.Provides->SourcePath;
646*7ed16280SDimitry Andric 
647*7ed16280SDimitry Andric     for (P1689Rule &R : Rules) {
648*7ed16280SDimitry Andric       for (P1689ModuleInfo &Info : R.Requires) {
649*7ed16280SDimitry Andric         auto Iter = ModuleSourceMapper.find(Info.ModuleName);
650*7ed16280SDimitry Andric         if (Iter != ModuleSourceMapper.end())
651*7ed16280SDimitry Andric           Info.SourcePath = Iter->second;
652*7ed16280SDimitry Andric       }
653*7ed16280SDimitry Andric     }
654*7ed16280SDimitry Andric   }
655*7ed16280SDimitry Andric 
656*7ed16280SDimitry Andric   std::mutex Lock;
657*7ed16280SDimitry Andric   std::vector<P1689Rule> Rules;
658*7ed16280SDimitry Andric };
659*7ed16280SDimitry Andric 
660*7ed16280SDimitry Andric static bool
handleP1689DependencyToolResult(const std::string & Input,llvm::Expected<P1689Rule> & MaybeRule,P1689Deps & PD,SharedStream & Errs)661*7ed16280SDimitry Andric handleP1689DependencyToolResult(const std::string &Input,
662*7ed16280SDimitry Andric                                 llvm::Expected<P1689Rule> &MaybeRule,
663*7ed16280SDimitry Andric                                 P1689Deps &PD, SharedStream &Errs) {
664*7ed16280SDimitry Andric   if (!MaybeRule) {
665*7ed16280SDimitry Andric     llvm::handleAllErrors(
666*7ed16280SDimitry Andric         MaybeRule.takeError(), [&Input, &Errs](llvm::StringError &Err) {
667*7ed16280SDimitry Andric           Errs.applyLocked([&](raw_ostream &OS) {
668*7ed16280SDimitry Andric             OS << "Error while scanning dependencies for " << Input << ":\n";
669*7ed16280SDimitry Andric             OS << Err.getMessage();
670*7ed16280SDimitry Andric           });
671*7ed16280SDimitry Andric         });
672*7ed16280SDimitry Andric     return true;
673*7ed16280SDimitry Andric   }
674*7ed16280SDimitry Andric   PD.addRules(*MaybeRule);
675*7ed16280SDimitry Andric   return false;
676*7ed16280SDimitry Andric }
677*7ed16280SDimitry Andric 
678*7ed16280SDimitry Andric /// Construct a path for the explicitly built PCM.
constructPCMPath(ModuleID MID,StringRef OutputDir)679*7ed16280SDimitry Andric static std::string constructPCMPath(ModuleID MID, StringRef OutputDir) {
680*7ed16280SDimitry Andric   SmallString<256> ExplicitPCMPath(OutputDir);
681*7ed16280SDimitry Andric   llvm::sys::path::append(ExplicitPCMPath, MID.ContextHash,
682*7ed16280SDimitry Andric                           MID.ModuleName + "-" + MID.ContextHash + ".pcm");
683*7ed16280SDimitry Andric   return std::string(ExplicitPCMPath);
684*7ed16280SDimitry Andric }
685*7ed16280SDimitry Andric 
lookupModuleOutput(const ModuleID & MID,ModuleOutputKind MOK,StringRef OutputDir)686*7ed16280SDimitry Andric static std::string lookupModuleOutput(const ModuleID &MID, ModuleOutputKind MOK,
687*7ed16280SDimitry Andric                                       StringRef OutputDir) {
688*7ed16280SDimitry Andric   std::string PCMPath = constructPCMPath(MID, OutputDir);
689*7ed16280SDimitry Andric   switch (MOK) {
690*7ed16280SDimitry Andric   case ModuleOutputKind::ModuleFile:
691*7ed16280SDimitry Andric     return PCMPath;
692*7ed16280SDimitry Andric   case ModuleOutputKind::DependencyFile:
693*7ed16280SDimitry Andric     return PCMPath + ".d";
694*7ed16280SDimitry Andric   case ModuleOutputKind::DependencyTargets:
695*7ed16280SDimitry Andric     // Null-separate the list of targets.
696*7ed16280SDimitry Andric     return join(ModuleDepTargets, StringRef("\0", 1));
697*7ed16280SDimitry Andric   case ModuleOutputKind::DiagnosticSerializationFile:
698*7ed16280SDimitry Andric     return PCMPath + ".diag";
699*7ed16280SDimitry Andric   }
700*7ed16280SDimitry Andric   llvm_unreachable("Fully covered switch above!");
701*7ed16280SDimitry Andric }
702*7ed16280SDimitry Andric 
getModuleCachePath(ArrayRef<std::string> Args)703*7ed16280SDimitry Andric static std::string getModuleCachePath(ArrayRef<std::string> Args) {
704*7ed16280SDimitry Andric   for (StringRef Arg : llvm::reverse(Args)) {
705*7ed16280SDimitry Andric     Arg.consume_front("/clang:");
706*7ed16280SDimitry Andric     if (Arg.consume_front("-fmodules-cache-path="))
707*7ed16280SDimitry Andric       return std::string(Arg);
708*7ed16280SDimitry Andric   }
709*7ed16280SDimitry Andric   SmallString<128> Path;
710*7ed16280SDimitry Andric   driver::Driver::getDefaultModuleCachePath(Path);
711*7ed16280SDimitry Andric   return std::string(Path);
712*7ed16280SDimitry Andric }
713*7ed16280SDimitry Andric 
714*7ed16280SDimitry Andric /// Attempts to construct the compilation database from '-compilation-database'
715*7ed16280SDimitry Andric /// or from the arguments following the positional '--'.
716*7ed16280SDimitry Andric static std::unique_ptr<tooling::CompilationDatabase>
getCompilationDatabase(int argc,char ** argv,std::string & ErrorMessage)717*7ed16280SDimitry Andric getCompilationDatabase(int argc, char **argv, std::string &ErrorMessage) {
718*7ed16280SDimitry Andric   ParseArgs(argc, argv);
719*7ed16280SDimitry Andric 
720*7ed16280SDimitry Andric   if (!(CommandLine.empty() ^ CompilationDB.empty())) {
721*7ed16280SDimitry Andric     llvm::errs() << "The compilation command line must be provided either via "
722*7ed16280SDimitry Andric                     "'-compilation-database' or after '--'.";
723*7ed16280SDimitry Andric     return nullptr;
724*7ed16280SDimitry Andric   }
725*7ed16280SDimitry Andric 
726*7ed16280SDimitry Andric   if (!CompilationDB.empty())
727*7ed16280SDimitry Andric     return tooling::JSONCompilationDatabase::loadFromFile(
728*7ed16280SDimitry Andric         CompilationDB, ErrorMessage,
729*7ed16280SDimitry Andric         tooling::JSONCommandLineSyntax::AutoDetect);
730*7ed16280SDimitry Andric 
731*7ed16280SDimitry Andric   llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
732*7ed16280SDimitry Andric       CompilerInstance::createDiagnostics(new DiagnosticOptions);
733*7ed16280SDimitry Andric   driver::Driver TheDriver(CommandLine[0], llvm::sys::getDefaultTargetTriple(),
734*7ed16280SDimitry Andric                            *Diags);
735*7ed16280SDimitry Andric   TheDriver.setCheckInputsExist(false);
736*7ed16280SDimitry Andric   std::unique_ptr<driver::Compilation> C(
737*7ed16280SDimitry Andric       TheDriver.BuildCompilation(CommandLine));
738*7ed16280SDimitry Andric   if (!C || C->getJobs().empty())
739*7ed16280SDimitry Andric     return nullptr;
740*7ed16280SDimitry Andric 
741*7ed16280SDimitry Andric   auto Cmd = C->getJobs().begin();
742*7ed16280SDimitry Andric   auto CI = std::make_unique<CompilerInvocation>();
743*7ed16280SDimitry Andric   CompilerInvocation::CreateFromArgs(*CI, Cmd->getArguments(), *Diags,
744*7ed16280SDimitry Andric                                      CommandLine[0]);
745*7ed16280SDimitry Andric   if (!CI)
746*7ed16280SDimitry Andric     return nullptr;
747*7ed16280SDimitry Andric 
748*7ed16280SDimitry Andric   FrontendOptions &FEOpts = CI->getFrontendOpts();
749*7ed16280SDimitry Andric   if (FEOpts.Inputs.size() != 1) {
750*7ed16280SDimitry Andric     llvm::errs()
751*7ed16280SDimitry Andric         << "Exactly one input file is required in the per-file mode ('--').\n";
752*7ed16280SDimitry Andric     return nullptr;
753*7ed16280SDimitry Andric   }
754*7ed16280SDimitry Andric 
755*7ed16280SDimitry Andric   // There might be multiple jobs for a compilation. Extract the specified
756*7ed16280SDimitry Andric   // output filename from the last job.
757*7ed16280SDimitry Andric   auto LastCmd = C->getJobs().end();
758*7ed16280SDimitry Andric   LastCmd--;
759*7ed16280SDimitry Andric   if (LastCmd->getOutputFilenames().size() != 1) {
760*7ed16280SDimitry Andric     llvm::errs()
761*7ed16280SDimitry Andric         << "Exactly one output file is required in the per-file mode ('--').\n";
762*7ed16280SDimitry Andric     return nullptr;
763*7ed16280SDimitry Andric   }
764*7ed16280SDimitry Andric   StringRef OutputFile = LastCmd->getOutputFilenames().front();
765*7ed16280SDimitry Andric 
766*7ed16280SDimitry Andric   class InplaceCompilationDatabase : public tooling::CompilationDatabase {
767*7ed16280SDimitry Andric   public:
768*7ed16280SDimitry Andric     InplaceCompilationDatabase(StringRef InputFile, StringRef OutputFile,
769*7ed16280SDimitry Andric                                ArrayRef<const char *> CommandLine)
770*7ed16280SDimitry Andric         : Command(".", InputFile, {}, OutputFile) {
771*7ed16280SDimitry Andric       for (auto *C : CommandLine)
772*7ed16280SDimitry Andric         Command.CommandLine.push_back(C);
773*7ed16280SDimitry Andric     }
774*7ed16280SDimitry Andric 
775*7ed16280SDimitry Andric     std::vector<tooling::CompileCommand>
776*7ed16280SDimitry Andric     getCompileCommands(StringRef FilePath) const override {
777*7ed16280SDimitry Andric       if (FilePath != Command.Filename)
778*7ed16280SDimitry Andric         return {};
779*7ed16280SDimitry Andric       return {Command};
780*7ed16280SDimitry Andric     }
781*7ed16280SDimitry Andric 
782*7ed16280SDimitry Andric     std::vector<std::string> getAllFiles() const override {
783*7ed16280SDimitry Andric       return {Command.Filename};
784*7ed16280SDimitry Andric     }
785*7ed16280SDimitry Andric 
786*7ed16280SDimitry Andric     std::vector<tooling::CompileCommand>
787*7ed16280SDimitry Andric     getAllCompileCommands() const override {
788*7ed16280SDimitry Andric       return {Command};
789*7ed16280SDimitry Andric     }
790*7ed16280SDimitry Andric 
791*7ed16280SDimitry Andric   private:
792*7ed16280SDimitry Andric     tooling::CompileCommand Command;
793*7ed16280SDimitry Andric   };
794*7ed16280SDimitry Andric 
795*7ed16280SDimitry Andric   return std::make_unique<InplaceCompilationDatabase>(
796*7ed16280SDimitry Andric       FEOpts.Inputs[0].getFile(), OutputFile, CommandLine);
797*7ed16280SDimitry Andric }
798*7ed16280SDimitry Andric 
clang_scan_deps_main(int argc,char ** argv,const llvm::ToolContext &)799*7ed16280SDimitry Andric int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) {
800*7ed16280SDimitry Andric   llvm::InitializeAllTargetInfos();
801*7ed16280SDimitry Andric   std::string ErrorMessage;
802*7ed16280SDimitry Andric   std::unique_ptr<tooling::CompilationDatabase> Compilations =
803*7ed16280SDimitry Andric       getCompilationDatabase(argc, argv, ErrorMessage);
804*7ed16280SDimitry Andric   if (!Compilations) {
805*7ed16280SDimitry Andric     llvm::errs() << ErrorMessage << "\n";
806*7ed16280SDimitry Andric     return 1;
807*7ed16280SDimitry Andric   }
808*7ed16280SDimitry Andric 
809*7ed16280SDimitry Andric   llvm::cl::PrintOptionValues();
810*7ed16280SDimitry Andric 
811*7ed16280SDimitry Andric   // Expand response files in advance, so that we can "see" all the arguments
812*7ed16280SDimitry Andric   // when adjusting below.
813*7ed16280SDimitry Andric   Compilations = expandResponseFiles(std::move(Compilations),
814*7ed16280SDimitry Andric                                      llvm::vfs::getRealFileSystem());
815*7ed16280SDimitry Andric 
816*7ed16280SDimitry Andric   Compilations = inferTargetAndDriverMode(std::move(Compilations));
817*7ed16280SDimitry Andric 
818*7ed16280SDimitry Andric   Compilations = inferToolLocation(std::move(Compilations));
819*7ed16280SDimitry Andric 
820*7ed16280SDimitry Andric   // The command options are rewritten to run Clang in preprocessor only mode.
821*7ed16280SDimitry Andric   auto AdjustingCompilations =
822*7ed16280SDimitry Andric       std::make_unique<tooling::ArgumentsAdjustingCompilations>(
823*7ed16280SDimitry Andric           std::move(Compilations));
824*7ed16280SDimitry Andric   ResourceDirectoryCache ResourceDirCache;
825*7ed16280SDimitry Andric 
826*7ed16280SDimitry Andric   AdjustingCompilations->appendArgumentsAdjuster(
827*7ed16280SDimitry Andric       [&ResourceDirCache](const tooling::CommandLineArguments &Args,
828*7ed16280SDimitry Andric                           StringRef FileName) {
829*7ed16280SDimitry Andric         std::string LastO;
830*7ed16280SDimitry Andric         bool HasResourceDir = false;
831*7ed16280SDimitry Andric         bool ClangCLMode = false;
832*7ed16280SDimitry Andric         auto FlagsEnd = llvm::find(Args, "--");
833*7ed16280SDimitry Andric         if (FlagsEnd != Args.begin()) {
834*7ed16280SDimitry Andric           ClangCLMode =
835*7ed16280SDimitry Andric               llvm::sys::path::stem(Args[0]).contains_insensitive("clang-cl") ||
836*7ed16280SDimitry Andric               llvm::is_contained(Args, "--driver-mode=cl");
837*7ed16280SDimitry Andric 
838*7ed16280SDimitry Andric           // Reverse scan, starting at the end or at the element before "--".
839*7ed16280SDimitry Andric           auto R = std::make_reverse_iterator(FlagsEnd);
840*7ed16280SDimitry Andric           auto E = Args.rend();
841*7ed16280SDimitry Andric           // Don't include Args[0] in the iteration; that's the executable, not
842*7ed16280SDimitry Andric           // an option.
843*7ed16280SDimitry Andric           if (E != R)
844*7ed16280SDimitry Andric             E--;
845*7ed16280SDimitry Andric           for (auto I = R; I != E; ++I) {
846*7ed16280SDimitry Andric             StringRef Arg = *I;
847*7ed16280SDimitry Andric             if (ClangCLMode) {
848*7ed16280SDimitry Andric               // Ignore arguments that are preceded by "-Xclang".
849*7ed16280SDimitry Andric               if ((I + 1) != E && I[1] == "-Xclang")
850*7ed16280SDimitry Andric                 continue;
851*7ed16280SDimitry Andric               if (LastO.empty()) {
852*7ed16280SDimitry Andric                 // With clang-cl, the output obj file can be specified with
853*7ed16280SDimitry Andric                 // "/opath", "/o path", "/Fopath", and the dash counterparts.
854*7ed16280SDimitry Andric                 // Also, clang-cl adds ".obj" extension if none is found.
855*7ed16280SDimitry Andric                 if ((Arg == "-o" || Arg == "/o") && I != R)
856*7ed16280SDimitry Andric                   LastO = I[-1]; // Next argument (reverse iterator)
857*7ed16280SDimitry Andric                 else if (Arg.starts_with("/Fo") || Arg.starts_with("-Fo"))
858*7ed16280SDimitry Andric                   LastO = Arg.drop_front(3).str();
859*7ed16280SDimitry Andric                 else if (Arg.starts_with("/o") || Arg.starts_with("-o"))
860*7ed16280SDimitry Andric                   LastO = Arg.drop_front(2).str();
861*7ed16280SDimitry Andric 
862*7ed16280SDimitry Andric                 if (!LastO.empty() && !llvm::sys::path::has_extension(LastO))
863*7ed16280SDimitry Andric                   LastO.append(".obj");
864*7ed16280SDimitry Andric               }
865*7ed16280SDimitry Andric             }
866*7ed16280SDimitry Andric             if (Arg == "-resource-dir")
867*7ed16280SDimitry Andric               HasResourceDir = true;
868*7ed16280SDimitry Andric           }
869*7ed16280SDimitry Andric         }
870*7ed16280SDimitry Andric         tooling::CommandLineArguments AdjustedArgs(Args.begin(), FlagsEnd);
871*7ed16280SDimitry Andric         // The clang-cl driver passes "-o -" to the frontend. Inject the real
872*7ed16280SDimitry Andric         // file here to ensure "-MT" can be deduced if need be.
873*7ed16280SDimitry Andric         if (ClangCLMode && !LastO.empty()) {
874*7ed16280SDimitry Andric           AdjustedArgs.push_back("/clang:-o");
875*7ed16280SDimitry Andric           AdjustedArgs.push_back("/clang:" + LastO);
876*7ed16280SDimitry Andric         }
877*7ed16280SDimitry Andric 
878*7ed16280SDimitry Andric         if (!HasResourceDir && ResourceDirRecipe == RDRK_InvokeCompiler) {
879*7ed16280SDimitry Andric           StringRef ResourceDir =
880*7ed16280SDimitry Andric               ResourceDirCache.findResourceDir(Args, ClangCLMode);
881*7ed16280SDimitry Andric           if (!ResourceDir.empty()) {
882*7ed16280SDimitry Andric             AdjustedArgs.push_back("-resource-dir");
883*7ed16280SDimitry Andric             AdjustedArgs.push_back(std::string(ResourceDir));
884*7ed16280SDimitry Andric           }
885*7ed16280SDimitry Andric         }
886*7ed16280SDimitry Andric         AdjustedArgs.insert(AdjustedArgs.end(), FlagsEnd, Args.end());
887*7ed16280SDimitry Andric         return AdjustedArgs;
888*7ed16280SDimitry Andric       });
889*7ed16280SDimitry Andric 
890*7ed16280SDimitry Andric   SharedStream Errs(llvm::errs());
891*7ed16280SDimitry Andric 
892*7ed16280SDimitry Andric   std::optional<llvm::raw_fd_ostream> FileOS;
893*7ed16280SDimitry Andric   llvm::raw_ostream &ThreadUnsafeDependencyOS = [&]() -> llvm::raw_ostream & {
894*7ed16280SDimitry Andric     if (OutputFileName == "-")
895*7ed16280SDimitry Andric       return llvm::outs();
896*7ed16280SDimitry Andric 
897*7ed16280SDimitry Andric     if (OutputFileName == "/dev/null")
898*7ed16280SDimitry Andric       return llvm::nulls();
899*7ed16280SDimitry Andric 
900*7ed16280SDimitry Andric     std::error_code EC;
901*7ed16280SDimitry Andric     FileOS.emplace(OutputFileName, EC);
902*7ed16280SDimitry Andric     if (EC) {
903*7ed16280SDimitry Andric       llvm::errs() << "Failed to open output file '" << OutputFileName
904*7ed16280SDimitry Andric                    << "': " << llvm::errorCodeToError(EC) << '\n';
905*7ed16280SDimitry Andric       std::exit(1);
906*7ed16280SDimitry Andric     }
907*7ed16280SDimitry Andric     return *FileOS;
908*7ed16280SDimitry Andric   }();
909*7ed16280SDimitry Andric   SharedStream DependencyOS(ThreadUnsafeDependencyOS);
910*7ed16280SDimitry Andric 
911*7ed16280SDimitry Andric   std::vector<tooling::CompileCommand> Inputs =
912*7ed16280SDimitry Andric       AdjustingCompilations->getAllCompileCommands();
913*7ed16280SDimitry Andric 
914*7ed16280SDimitry Andric   std::atomic<bool> HadErrors(false);
915*7ed16280SDimitry Andric   std::optional<FullDeps> FD;
916*7ed16280SDimitry Andric   P1689Deps PD;
917*7ed16280SDimitry Andric 
918*7ed16280SDimitry Andric   std::mutex Lock;
919*7ed16280SDimitry Andric   size_t Index = 0;
920*7ed16280SDimitry Andric   auto GetNextInputIndex = [&]() -> std::optional<size_t> {
921*7ed16280SDimitry Andric     std::unique_lock<std::mutex> LockGuard(Lock);
922*7ed16280SDimitry Andric     if (Index < Inputs.size())
923*7ed16280SDimitry Andric       return Index++;
924*7ed16280SDimitry Andric     return {};
925*7ed16280SDimitry Andric   };
926*7ed16280SDimitry Andric 
927*7ed16280SDimitry Andric   if (Format == ScanningOutputFormat::Full)
928*7ed16280SDimitry Andric     FD.emplace(ModuleName.empty() ? Inputs.size() : 0);
929*7ed16280SDimitry Andric 
930*7ed16280SDimitry Andric   auto ScanningTask = [&](DependencyScanningService &Service) {
931*7ed16280SDimitry Andric     DependencyScanningTool WorkerTool(Service);
932*7ed16280SDimitry Andric 
933*7ed16280SDimitry Andric     llvm::DenseSet<ModuleID> AlreadySeenModules;
934*7ed16280SDimitry Andric     while (auto MaybeInputIndex = GetNextInputIndex()) {
935*7ed16280SDimitry Andric       size_t LocalIndex = *MaybeInputIndex;
936*7ed16280SDimitry Andric       const tooling::CompileCommand *Input = &Inputs[LocalIndex];
937*7ed16280SDimitry Andric       std::string Filename = std::move(Input->Filename);
938*7ed16280SDimitry Andric       std::string CWD = std::move(Input->Directory);
939*7ed16280SDimitry Andric 
940*7ed16280SDimitry Andric       std::optional<StringRef> MaybeModuleName;
941*7ed16280SDimitry Andric       if (!ModuleName.empty())
942*7ed16280SDimitry Andric         MaybeModuleName = ModuleName;
943*7ed16280SDimitry Andric 
944*7ed16280SDimitry Andric       std::string OutputDir(ModuleFilesDir);
945*7ed16280SDimitry Andric       if (OutputDir.empty())
946*7ed16280SDimitry Andric         OutputDir = getModuleCachePath(Input->CommandLine);
947*7ed16280SDimitry Andric       auto LookupOutput = [&](const ModuleID &MID, ModuleOutputKind MOK) {
948*7ed16280SDimitry Andric         return ::lookupModuleOutput(MID, MOK, OutputDir);
949*7ed16280SDimitry Andric       };
950*7ed16280SDimitry Andric 
951*7ed16280SDimitry Andric       // Run the tool on it.
952*7ed16280SDimitry Andric       if (Format == ScanningOutputFormat::Make) {
953*7ed16280SDimitry Andric         auto MaybeFile = WorkerTool.getDependencyFile(Input->CommandLine, CWD);
954*7ed16280SDimitry Andric         if (handleMakeDependencyToolResult(Filename, MaybeFile, DependencyOS,
955*7ed16280SDimitry Andric                                            Errs))
956*7ed16280SDimitry Andric           HadErrors = true;
957*7ed16280SDimitry Andric       } else if (Format == ScanningOutputFormat::P1689) {
958*7ed16280SDimitry Andric         // It is useful to generate the make-format dependency output during
959*7ed16280SDimitry Andric         // the scanning for P1689. Otherwise the users need to scan again for
960*7ed16280SDimitry Andric         // it. We will generate the make-format dependency output if we find
961*7ed16280SDimitry Andric         // `-MF` in the command lines.
962*7ed16280SDimitry Andric         std::string MakeformatOutputPath;
963*7ed16280SDimitry Andric         std::string MakeformatOutput;
964*7ed16280SDimitry Andric 
965*7ed16280SDimitry Andric         auto MaybeRule = WorkerTool.getP1689ModuleDependencyFile(
966*7ed16280SDimitry Andric             *Input, CWD, MakeformatOutput, MakeformatOutputPath);
967*7ed16280SDimitry Andric 
968*7ed16280SDimitry Andric         if (handleP1689DependencyToolResult(Filename, MaybeRule, PD, Errs))
969*7ed16280SDimitry Andric           HadErrors = true;
970*7ed16280SDimitry Andric 
971*7ed16280SDimitry Andric         if (!MakeformatOutputPath.empty() && !MakeformatOutput.empty() &&
972*7ed16280SDimitry Andric             !HadErrors) {
973*7ed16280SDimitry Andric           static std::mutex Lock;
974*7ed16280SDimitry Andric           // With compilation database, we may open different files
975*7ed16280SDimitry Andric           // concurrently or we may write the same file concurrently. So we
976*7ed16280SDimitry Andric           // use a map here to allow multiple compile commands to write to the
977*7ed16280SDimitry Andric           // same file. Also we need a lock here to avoid data race.
978*7ed16280SDimitry Andric           static llvm::StringMap<llvm::raw_fd_ostream> OSs;
979*7ed16280SDimitry Andric           std::unique_lock<std::mutex> LockGuard(Lock);
980*7ed16280SDimitry Andric 
981*7ed16280SDimitry Andric           auto OSIter = OSs.find(MakeformatOutputPath);
982*7ed16280SDimitry Andric           if (OSIter == OSs.end()) {
983*7ed16280SDimitry Andric             std::error_code EC;
984*7ed16280SDimitry Andric             OSIter =
985*7ed16280SDimitry Andric                 OSs.try_emplace(MakeformatOutputPath, MakeformatOutputPath, EC)
986*7ed16280SDimitry Andric                     .first;
987*7ed16280SDimitry Andric             if (EC)
988*7ed16280SDimitry Andric               llvm::errs() << "Failed to open P1689 make format output file \""
989*7ed16280SDimitry Andric                            << MakeformatOutputPath << "\" for " << EC.message()
990*7ed16280SDimitry Andric                            << "\n";
991*7ed16280SDimitry Andric           }
992*7ed16280SDimitry Andric 
993*7ed16280SDimitry Andric           SharedStream MakeformatOS(OSIter->second);
994*7ed16280SDimitry Andric           llvm::Expected<std::string> MaybeOutput(MakeformatOutput);
995*7ed16280SDimitry Andric           if (handleMakeDependencyToolResult(Filename, MaybeOutput,
996*7ed16280SDimitry Andric                                              MakeformatOS, Errs))
997*7ed16280SDimitry Andric             HadErrors = true;
998*7ed16280SDimitry Andric         }
999*7ed16280SDimitry Andric       } else if (MaybeModuleName) {
1000*7ed16280SDimitry Andric         auto MaybeModuleDepsGraph = WorkerTool.getModuleDependencies(
1001*7ed16280SDimitry Andric             *MaybeModuleName, Input->CommandLine, CWD, AlreadySeenModules,
1002*7ed16280SDimitry Andric             LookupOutput);
1003*7ed16280SDimitry Andric         if (handleModuleResult(*MaybeModuleName, MaybeModuleDepsGraph, *FD,
1004*7ed16280SDimitry Andric                                LocalIndex, DependencyOS, Errs))
1005*7ed16280SDimitry Andric           HadErrors = true;
1006*7ed16280SDimitry Andric       } else {
1007*7ed16280SDimitry Andric         auto MaybeTUDeps = WorkerTool.getTranslationUnitDependencies(
1008*7ed16280SDimitry Andric             Input->CommandLine, CWD, AlreadySeenModules, LookupOutput);
1009*7ed16280SDimitry Andric         if (handleTranslationUnitResult(Filename, MaybeTUDeps, *FD, LocalIndex,
1010*7ed16280SDimitry Andric                                         DependencyOS, Errs))
1011*7ed16280SDimitry Andric           HadErrors = true;
1012*7ed16280SDimitry Andric       }
1013*7ed16280SDimitry Andric     }
1014*7ed16280SDimitry Andric   };
1015*7ed16280SDimitry Andric 
1016*7ed16280SDimitry Andric   DependencyScanningService Service(ScanMode, Format, OptimizeArgs,
1017*7ed16280SDimitry Andric                                     EagerLoadModules);
1018*7ed16280SDimitry Andric 
1019*7ed16280SDimitry Andric   llvm::Timer T;
1020*7ed16280SDimitry Andric   T.startTimer();
1021*7ed16280SDimitry Andric 
1022*7ed16280SDimitry Andric   if (Inputs.size() == 1) {
1023*7ed16280SDimitry Andric     ScanningTask(Service);
1024*7ed16280SDimitry Andric   } else {
1025*7ed16280SDimitry Andric     llvm::DefaultThreadPool Pool(llvm::hardware_concurrency(NumThreads));
1026*7ed16280SDimitry Andric 
1027*7ed16280SDimitry Andric     if (Verbose) {
1028*7ed16280SDimitry Andric       llvm::outs() << "Running clang-scan-deps on " << Inputs.size()
1029*7ed16280SDimitry Andric                    << " files using " << Pool.getMaxConcurrency()
1030*7ed16280SDimitry Andric                    << " workers\n";
1031*7ed16280SDimitry Andric     }
1032*7ed16280SDimitry Andric 
1033*7ed16280SDimitry Andric     for (unsigned I = 0; I < Pool.getMaxConcurrency(); ++I)
1034*7ed16280SDimitry Andric       Pool.async([ScanningTask, &Service]() { ScanningTask(Service); });
1035*7ed16280SDimitry Andric 
1036*7ed16280SDimitry Andric     Pool.wait();
1037*7ed16280SDimitry Andric   }
1038*7ed16280SDimitry Andric 
1039*7ed16280SDimitry Andric   T.stopTimer();
1040*7ed16280SDimitry Andric   if (PrintTiming)
1041*7ed16280SDimitry Andric     llvm::errs() << llvm::format(
1042*7ed16280SDimitry Andric         "clang-scan-deps timing: %0.2fs wall, %0.2fs process\n",
1043*7ed16280SDimitry Andric         T.getTotalTime().getWallTime(), T.getTotalTime().getProcessTime());
1044*7ed16280SDimitry Andric 
1045*7ed16280SDimitry Andric   if (RoundTripArgs)
1046*7ed16280SDimitry Andric     if (FD && FD->roundTripCommands(llvm::errs()))
1047*7ed16280SDimitry Andric       HadErrors = true;
1048*7ed16280SDimitry Andric 
1049*7ed16280SDimitry Andric   if (Format == ScanningOutputFormat::Full)
1050*7ed16280SDimitry Andric     FD->printFullOutput(ThreadUnsafeDependencyOS);
1051*7ed16280SDimitry Andric   else if (Format == ScanningOutputFormat::P1689)
1052*7ed16280SDimitry Andric     PD.printDependencies(ThreadUnsafeDependencyOS);
1053*7ed16280SDimitry Andric 
1054*7ed16280SDimitry Andric   return HadErrors;
1055*7ed16280SDimitry Andric }
1056