1 //===- ClangScanDeps.cpp - Implementation of clang-scan-deps --------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang/Driver/Compilation.h"
10 #include "clang/Driver/Driver.h"
11 #include "clang/Frontend/CompilerInstance.h"
12 #include "clang/Frontend/TextDiagnosticPrinter.h"
13 #include "clang/Tooling/CommonOptionsParser.h"
14 #include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
15 #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
16 #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
17 #include "clang/Tooling/JSONCompilationDatabase.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/ADT/Twine.h"
20 #include "llvm/Support/CommandLine.h"
21 #include "llvm/Support/FileUtilities.h"
22 #include "llvm/Support/Format.h"
23 #include "llvm/Support/InitLLVM.h"
24 #include "llvm/Support/JSON.h"
25 #include "llvm/Support/LLVMDriver.h"
26 #include "llvm/Support/Program.h"
27 #include "llvm/Support/Signals.h"
28 #include "llvm/Support/ThreadPool.h"
29 #include "llvm/Support/Threading.h"
30 #include "llvm/Support/Timer.h"
31 #include "llvm/TargetParser/Host.h"
38 using namespace clang
;
39 using namespace tooling::dependencies
;
43 using namespace llvm::opt
;
45 OPT_INVALID
= 0, // This is not an option ID.
46 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
51 #define PREFIX(NAME, VALUE) \
52 constexpr llvm::StringLiteral NAME##_init[] = VALUE; \
53 constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \
54 NAME##_init, std::size(NAME##_init) - 1);
58 const llvm::opt::OptTable::Info InfoTable
[] = {
59 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
64 class ScanDepsOptTable
: public llvm::opt::GenericOptTable
{
66 ScanDepsOptTable() : GenericOptTable(InfoTable
) {
67 setGroupedShortOptions(true);
71 enum ResourceDirRecipeKind
{
72 RDRK_ModifyCompilerPath
,
76 static ScanningMode ScanMode
= ScanningMode::DependencyDirectivesScan
;
77 static ScanningOutputFormat Format
= ScanningOutputFormat::Make
;
78 static std::string ModuleFilesDir
;
79 static bool OptimizeArgs
;
80 static bool EagerLoadModules
;
81 static unsigned NumThreads
= 0;
82 static std::string CompilationDB
;
83 static std::string ModuleName
;
84 static std::vector
<std::string
> ModuleDepTargets
;
85 static bool DeprecatedDriverCommand
;
86 static ResourceDirRecipeKind ResourceDirRecipe
;
88 static bool PrintTiming
;
89 static std::vector
<const char *> CommandLine
;
92 static constexpr bool DoRoundTripDefault
= true;
94 static constexpr bool DoRoundTripDefault
= false;
97 static bool RoundTripArgs
= DoRoundTripDefault
;
99 static void ParseArgs(int argc
, char **argv
) {
100 ScanDepsOptTable Tbl
;
101 llvm::StringRef ToolName
= argv
[0];
102 llvm::BumpPtrAllocator A
;
103 llvm::StringSaver Saver
{A
};
104 llvm::opt::InputArgList Args
=
105 Tbl
.parseArgs(argc
, argv
, OPT_UNKNOWN
, Saver
, [&](StringRef Msg
) {
106 llvm::errs() << Msg
<< '\n';
110 if (Args
.hasArg(OPT_help
)) {
111 Tbl
.printHelp(llvm::outs(), "clang-scan-deps [options]", "clang-scan-deps");
114 if (Args
.hasArg(OPT_version
)) {
115 llvm::outs() << ToolName
<< '\n';
116 llvm::cl::PrintVersionMessage();
119 if (const llvm::opt::Arg
*A
= Args
.getLastArg(OPT_mode_EQ
)) {
121 llvm::StringSwitch
<std::optional
<ScanningMode
>>(A
->getValue())
122 .Case("preprocess-dependency-directives",
123 ScanningMode::DependencyDirectivesScan
)
124 .Case("preprocess", ScanningMode::CanonicalPreprocessing
)
125 .Default(std::nullopt
);
127 llvm::errs() << ToolName
128 << ": for the --mode option: Cannot find option named '"
129 << A
->getValue() << "'\n";
132 ScanMode
= *ModeType
;
135 if (const llvm::opt::Arg
*A
= Args
.getLastArg(OPT_format_EQ
)) {
137 llvm::StringSwitch
<std::optional
<ScanningOutputFormat
>>(A
->getValue())
138 .Case("make", ScanningOutputFormat::Make
)
139 .Case("p1689", ScanningOutputFormat::P1689
)
140 .Case("experimental-full", ScanningOutputFormat::Full
)
141 .Default(std::nullopt
);
143 llvm::errs() << ToolName
144 << ": for the --format option: Cannot find option named '"
145 << A
->getValue() << "'\n";
148 Format
= *FormatType
;
151 if (const llvm::opt::Arg
*A
= Args
.getLastArg(OPT_module_files_dir_EQ
))
152 ModuleFilesDir
= A
->getValue();
154 OptimizeArgs
= Args
.hasArg(OPT_optimize_args
);
155 EagerLoadModules
= Args
.hasArg(OPT_eager_load_pcm
);
157 if (const llvm::opt::Arg
*A
= Args
.getLastArg(OPT_j
)) {
158 StringRef S
{A
->getValue()};
159 if (!llvm::to_integer(S
, NumThreads
, 0)) {
160 llvm::errs() << ToolName
<< ": for the -j option: '" << S
161 << "' value invalid for uint argument!\n";
166 if (const llvm::opt::Arg
*A
= Args
.getLastArg(OPT_compilation_database_EQ
)) {
167 CompilationDB
= A
->getValue();
168 } else if (Format
!= ScanningOutputFormat::P1689
) {
169 llvm::errs() << ToolName
170 << ": for the --compiilation-database option: must be "
171 "specified at least once!";
175 if (const llvm::opt::Arg
*A
= Args
.getLastArg(OPT_module_name_EQ
))
176 ModuleName
= A
->getValue();
178 for (const llvm::opt::Arg
*A
: Args
.filtered(OPT_dependency_target_EQ
))
179 ModuleDepTargets
.emplace_back(A
->getValue());
181 DeprecatedDriverCommand
= Args
.hasArg(OPT_deprecated_driver_command
);
183 if (const llvm::opt::Arg
*A
= Args
.getLastArg(OPT_resource_dir_recipe_EQ
)) {
185 llvm::StringSwitch
<std::optional
<ResourceDirRecipeKind
>>(A
->getValue())
186 .Case("modify-compiler-path", RDRK_ModifyCompilerPath
)
187 .Case("invoke-compiler", RDRK_InvokeCompiler
)
188 .Default(std::nullopt
);
190 llvm::errs() << ToolName
191 << ": for the --resource-dir-recipe option: Cannot find "
193 << A
->getValue() << "'\n";
196 ResourceDirRecipe
= *Kind
;
199 PrintTiming
= Args
.hasArg(OPT_print_timing
);
201 Verbose
= Args
.hasArg(OPT_verbose
);
203 RoundTripArgs
= Args
.hasArg(OPT_round_trip_args
);
205 if (auto *A
= Args
.getLastArgNoClaim(OPT_DASH_DASH
))
206 CommandLine
.insert(CommandLine
.end(), A
->getValues().begin(),
207 A
->getValues().end());
212 SharedStream(raw_ostream
&OS
) : OS(OS
) {}
213 void applyLocked(llvm::function_ref
<void(raw_ostream
&OS
)> Fn
) {
214 std::unique_lock
<std::mutex
> LockGuard(Lock
);
224 class ResourceDirectoryCache
{
226 /// findResourceDir finds the resource directory relative to the clang
227 /// compiler being used in Args, by running it with "-print-resource-dir"
228 /// option and cache the results for reuse. \returns resource directory path
229 /// associated with the given invocation command or empty string if the
230 /// compiler path is NOT an absolute path.
231 StringRef
findResourceDir(const tooling::CommandLineArguments
&Args
,
236 const std::string
&ClangBinaryPath
= Args
[0];
237 if (!llvm::sys::path::is_absolute(ClangBinaryPath
))
240 const std::string
&ClangBinaryName
=
241 std::string(llvm::sys::path::filename(ClangBinaryPath
));
243 std::unique_lock
<std::mutex
> LockGuard(CacheLock
);
244 const auto &CachedResourceDir
= Cache
.find(ClangBinaryPath
);
245 if (CachedResourceDir
!= Cache
.end())
246 return CachedResourceDir
->second
;
248 std::vector
<StringRef
> PrintResourceDirArgs
{ClangBinaryName
};
250 PrintResourceDirArgs
.push_back("/clang:-print-resource-dir");
252 PrintResourceDirArgs
.push_back("-print-resource-dir");
254 llvm::SmallString
<64> OutputFile
, ErrorFile
;
255 llvm::sys::fs::createTemporaryFile("print-resource-dir-output",
256 "" /*no-suffix*/, OutputFile
);
257 llvm::sys::fs::createTemporaryFile("print-resource-dir-error",
258 "" /*no-suffix*/, ErrorFile
);
259 llvm::FileRemover
OutputRemover(OutputFile
.c_str());
260 llvm::FileRemover
ErrorRemover(ErrorFile
.c_str());
261 std::optional
<StringRef
> Redirects
[] = {
266 if (llvm::sys::ExecuteAndWait(ClangBinaryPath
, PrintResourceDirArgs
, {},
268 auto ErrorBuf
= llvm::MemoryBuffer::getFile(ErrorFile
.c_str());
269 llvm::errs() << ErrorBuf
.get()->getBuffer();
273 auto OutputBuf
= llvm::MemoryBuffer::getFile(OutputFile
.c_str());
276 StringRef Output
= OutputBuf
.get()->getBuffer().rtrim('\n');
278 Cache
[ClangBinaryPath
] = Output
.str();
279 return Cache
[ClangBinaryPath
];
283 std::map
<std::string
, std::string
> Cache
;
284 std::mutex CacheLock
;
287 } // end anonymous namespace
289 /// Takes the result of a dependency scan and prints error / dependency files
290 /// based on the result.
292 /// \returns True on error.
294 handleMakeDependencyToolResult(const std::string
&Input
,
295 llvm::Expected
<std::string
> &MaybeFile
,
296 SharedStream
&OS
, SharedStream
&Errs
) {
298 llvm::handleAllErrors(
299 MaybeFile
.takeError(), [&Input
, &Errs
](llvm::StringError
&Err
) {
300 Errs
.applyLocked([&](raw_ostream
&OS
) {
301 OS
<< "Error while scanning dependencies for " << Input
<< ":\n";
302 OS
<< Err
.getMessage();
307 OS
.applyLocked([&](raw_ostream
&OS
) { OS
<< *MaybeFile
; });
311 static llvm::json::Array
toJSONSorted(const llvm::StringSet
<> &Set
) {
312 std::vector
<llvm::StringRef
> Strings
;
314 Strings
.push_back(I
.getKey());
316 return llvm::json::Array(Strings
);
319 // Technically, we don't need to sort the dependency list to get determinism.
320 // Leaving these be will simply preserve the import order.
321 static llvm::json::Array
toJSONSorted(std::vector
<ModuleID
> V
) {
324 llvm::json::Array Ret
;
325 for (const ModuleID
&MID
: V
)
326 Ret
.push_back(llvm::json::Object(
327 {{"module-name", MID
.ModuleName
}, {"context-hash", MID
.ContextHash
}}));
334 FullDeps(size_t NumInputs
) : Inputs(NumInputs
) {}
336 void mergeDeps(StringRef Input
, TranslationUnitDeps TUDeps
,
338 mergeDeps(std::move(TUDeps
.ModuleGraph
), InputIndex
);
341 ID
.FileName
= std::string(Input
);
342 ID
.ContextHash
= std::move(TUDeps
.ID
.ContextHash
);
343 ID
.FileDeps
= std::move(TUDeps
.FileDeps
);
344 ID
.ModuleDeps
= std::move(TUDeps
.ClangModuleDeps
);
345 ID
.DriverCommandLine
= std::move(TUDeps
.DriverCommandLine
);
346 ID
.Commands
= std::move(TUDeps
.Commands
);
348 assert(InputIndex
< Inputs
.size() && "Input index out of bounds");
349 assert(Inputs
[InputIndex
].FileName
.empty() && "Result already populated");
350 Inputs
[InputIndex
] = std::move(ID
);
353 void mergeDeps(ModuleDepsGraph Graph
, size_t InputIndex
) {
354 std::vector
<ModuleDeps
*> NewMDs
;
356 std::unique_lock
<std::mutex
> ul(Lock
);
357 for (const ModuleDeps
&MD
: Graph
) {
358 auto I
= Modules
.find({MD
.ID
, 0});
359 if (I
!= Modules
.end()) {
360 I
->first
.InputIndex
= std::min(I
->first
.InputIndex
, InputIndex
);
363 auto Res
= Modules
.insert(I
, {{MD
.ID
, InputIndex
}, std::move(MD
)});
364 NewMDs
.push_back(&Res
->second
);
366 // First call to \c getBuildArguments is somewhat expensive. Let's call it
367 // on the current thread (instead of the main one), and outside the
369 for (ModuleDeps
*MD
: NewMDs
)
370 (void)MD
->getBuildArguments();
374 bool roundTripCommand(ArrayRef
<std::string
> ArgStrs
,
375 DiagnosticsEngine
&Diags
) {
376 if (ArgStrs
.empty() || ArgStrs
[0] != "-cc1")
378 SmallVector
<const char *> Args
;
379 for (const std::string
&Arg
: ArgStrs
)
380 Args
.push_back(Arg
.c_str());
381 return !CompilerInvocation::checkCC1RoundTrip(Args
, Diags
);
384 // Returns \c true if any command lines fail to round-trip. We expect
385 // commands already be canonical when output by the scanner.
386 bool roundTripCommands(raw_ostream
&ErrOS
) {
387 IntrusiveRefCntPtr
<DiagnosticOptions
> DiagOpts
= new DiagnosticOptions
{};
388 TextDiagnosticPrinter
DiagConsumer(ErrOS
, &*DiagOpts
);
389 IntrusiveRefCntPtr
<DiagnosticsEngine
> Diags
=
390 CompilerInstance::createDiagnostics(&*DiagOpts
, &DiagConsumer
,
391 /*ShouldOwnClient=*/false);
393 for (auto &&M
: Modules
)
394 if (roundTripCommand(M
.second
.getBuildArguments(), *Diags
))
397 for (auto &&I
: Inputs
)
398 for (const auto &Cmd
: I
.Commands
)
399 if (roundTripCommand(Cmd
.Arguments
, *Diags
))
405 void printFullOutput(raw_ostream
&OS
) {
406 // Sort the modules by name to get a deterministic order.
407 std::vector
<IndexedModuleID
> ModuleIDs
;
408 for (auto &&M
: Modules
)
409 ModuleIDs
.push_back(M
.first
);
410 llvm::sort(ModuleIDs
);
412 using namespace llvm::json
;
415 for (auto &&ModID
: ModuleIDs
) {
416 auto &MD
= Modules
[ModID
];
418 {"name", MD
.ID
.ModuleName
},
419 {"context-hash", MD
.ID
.ContextHash
},
420 {"file-deps", toJSONSorted(MD
.FileDeps
)},
421 {"clang-module-deps", toJSONSorted(MD
.ClangModuleDeps
)},
422 {"clang-modulemap-file", MD
.ClangModuleMapFile
},
423 {"command-line", MD
.getBuildArguments()},
425 OutModules
.push_back(std::move(O
));
429 for (auto &&I
: Inputs
) {
431 if (I
.DriverCommandLine
.empty()) {
432 for (const auto &Cmd
: I
.Commands
) {
434 {"input-file", I
.FileName
},
435 {"clang-context-hash", I
.ContextHash
},
436 {"file-deps", I
.FileDeps
},
437 {"clang-module-deps", toJSONSorted(I
.ModuleDeps
)},
438 {"executable", Cmd
.Executable
},
439 {"command-line", Cmd
.Arguments
},
441 Commands
.push_back(std::move(O
));
445 {"input-file", I
.FileName
},
446 {"clang-context-hash", I
.ContextHash
},
447 {"file-deps", I
.FileDeps
},
448 {"clang-module-deps", toJSONSorted(I
.ModuleDeps
)},
449 {"executable", "clang"},
450 {"command-line", I
.DriverCommandLine
},
452 Commands
.push_back(std::move(O
));
454 TUs
.push_back(Object
{
455 {"commands", std::move(Commands
)},
460 {"modules", std::move(OutModules
)},
461 {"translation-units", std::move(TUs
)},
464 OS
<< llvm::formatv("{0:2}\n", Value(std::move(Output
)));
468 struct IndexedModuleID
{
471 // FIXME: This is mutable so that it can still be updated after insertion
472 // into an unordered associative container. This is "fine", since this
473 // field doesn't contribute to the hash, but it's a brittle hack.
474 mutable size_t InputIndex
;
476 bool operator==(const IndexedModuleID
&Other
) const {
477 return ID
== Other
.ID
;
480 bool operator<(const IndexedModuleID
&Other
) const {
481 /// We need the output of clang-scan-deps to be deterministic. However,
482 /// the dependency graph may contain two modules with the same name. How
483 /// do we decide which one to print first? If we made that decision based
484 /// on the context hash, the ordering would be deterministic, but
485 /// different across machines. This can happen for example when the inputs
486 /// or the SDKs (which both contribute to the "context" hash) live in
487 /// different absolute locations. We solve that by tracking the index of
488 /// the first input TU that (transitively) imports the dependency, which
489 /// is always the same for the same input, resulting in deterministic
490 /// sorting that's also reproducible across machines.
491 return std::tie(ID
.ModuleName
, InputIndex
) <
492 std::tie(Other
.ID
.ModuleName
, Other
.InputIndex
);
496 std::size_t operator()(const IndexedModuleID
&IMID
) const {
497 return llvm::hash_value(IMID
.ID
);
503 std::string FileName
;
504 std::string ContextHash
;
505 std::vector
<std::string
> FileDeps
;
506 std::vector
<ModuleID
> ModuleDeps
;
507 std::vector
<std::string
> DriverCommandLine
;
508 std::vector
<Command
> Commands
;
512 std::unordered_map
<IndexedModuleID
, ModuleDeps
, IndexedModuleID::Hasher
>
514 std::vector
<InputDeps
> Inputs
;
517 static bool handleTranslationUnitResult(
518 StringRef Input
, llvm::Expected
<TranslationUnitDeps
> &MaybeTUDeps
,
519 FullDeps
&FD
, size_t InputIndex
, SharedStream
&OS
, SharedStream
&Errs
) {
521 llvm::handleAllErrors(
522 MaybeTUDeps
.takeError(), [&Input
, &Errs
](llvm::StringError
&Err
) {
523 Errs
.applyLocked([&](raw_ostream
&OS
) {
524 OS
<< "Error while scanning dependencies for " << Input
<< ":\n";
525 OS
<< Err
.getMessage();
530 FD
.mergeDeps(Input
, std::move(*MaybeTUDeps
), InputIndex
);
534 static bool handleModuleResult(
535 StringRef ModuleName
, llvm::Expected
<ModuleDepsGraph
> &MaybeModuleGraph
,
536 FullDeps
&FD
, size_t InputIndex
, SharedStream
&OS
, SharedStream
&Errs
) {
537 if (!MaybeModuleGraph
) {
538 llvm::handleAllErrors(MaybeModuleGraph
.takeError(),
539 [&ModuleName
, &Errs
](llvm::StringError
&Err
) {
540 Errs
.applyLocked([&](raw_ostream
&OS
) {
541 OS
<< "Error while scanning dependencies for "
542 << ModuleName
<< ":\n";
543 OS
<< Err
.getMessage();
548 FD
.mergeDeps(std::move(*MaybeModuleGraph
), InputIndex
);
554 void printDependencies(raw_ostream
&OS
) {
555 addSourcePathsToRequires();
556 // Sort the modules by name to get a deterministic order.
557 llvm::sort(Rules
, [](const P1689Rule
&A
, const P1689Rule
&B
) {
558 return A
.PrimaryOutput
< B
.PrimaryOutput
;
561 using namespace llvm::json
;
563 for (const P1689Rule
&R
: Rules
) {
564 Object O
{{"primary-output", R
.PrimaryOutput
}};
568 Object Provided
{{"logical-name", R
.Provides
->ModuleName
},
569 {"source-path", R
.Provides
->SourcePath
},
570 {"is-interface", R
.Provides
->IsStdCXXModuleInterface
}};
571 Provides
.push_back(std::move(Provided
));
572 O
.insert({"provides", std::move(Provides
)});
576 for (const P1689ModuleInfo
&Info
: R
.Requires
) {
577 Object RequiredInfo
{{"logical-name", Info
.ModuleName
}};
578 if (!Info
.SourcePath
.empty())
579 RequiredInfo
.insert({"source-path", Info
.SourcePath
});
580 Requires
.push_back(std::move(RequiredInfo
));
583 if (!Requires
.empty())
584 O
.insert({"requires", std::move(Requires
)});
586 OutputRules
.push_back(std::move(O
));
590 {"version", 1}, {"revision", 0}, {"rules", std::move(OutputRules
)}};
592 OS
<< llvm::formatv("{0:2}\n", Value(std::move(Output
)));
595 void addRules(P1689Rule
&Rule
) {
596 std::unique_lock
<std::mutex
> LockGuard(Lock
);
597 Rules
.push_back(Rule
);
601 void addSourcePathsToRequires() {
602 llvm::DenseMap
<StringRef
, StringRef
> ModuleSourceMapper
;
603 for (const P1689Rule
&R
: Rules
)
604 if (R
.Provides
&& !R
.Provides
->SourcePath
.empty())
605 ModuleSourceMapper
[R
.Provides
->ModuleName
] = R
.Provides
->SourcePath
;
607 for (P1689Rule
&R
: Rules
) {
608 for (P1689ModuleInfo
&Info
: R
.Requires
) {
609 auto Iter
= ModuleSourceMapper
.find(Info
.ModuleName
);
610 if (Iter
!= ModuleSourceMapper
.end())
611 Info
.SourcePath
= Iter
->second
;
617 std::vector
<P1689Rule
> Rules
;
621 handleP1689DependencyToolResult(const std::string
&Input
,
622 llvm::Expected
<P1689Rule
> &MaybeRule
,
623 P1689Deps
&PD
, SharedStream
&Errs
) {
625 llvm::handleAllErrors(
626 MaybeRule
.takeError(), [&Input
, &Errs
](llvm::StringError
&Err
) {
627 Errs
.applyLocked([&](raw_ostream
&OS
) {
628 OS
<< "Error while scanning dependencies for " << Input
<< ":\n";
629 OS
<< Err
.getMessage();
634 PD
.addRules(*MaybeRule
);
638 /// Construct a path for the explicitly built PCM.
639 static std::string
constructPCMPath(ModuleID MID
, StringRef OutputDir
) {
640 SmallString
<256> ExplicitPCMPath(OutputDir
);
641 llvm::sys::path::append(ExplicitPCMPath
, MID
.ContextHash
,
642 MID
.ModuleName
+ "-" + MID
.ContextHash
+ ".pcm");
643 return std::string(ExplicitPCMPath
);
646 static std::string
lookupModuleOutput(const ModuleID
&MID
, ModuleOutputKind MOK
,
647 StringRef OutputDir
) {
648 std::string PCMPath
= constructPCMPath(MID
, OutputDir
);
650 case ModuleOutputKind::ModuleFile
:
652 case ModuleOutputKind::DependencyFile
:
653 return PCMPath
+ ".d";
654 case ModuleOutputKind::DependencyTargets
:
655 // Null-separate the list of targets.
656 return join(ModuleDepTargets
, StringRef("\0", 1));
657 case ModuleOutputKind::DiagnosticSerializationFile
:
658 return PCMPath
+ ".diag";
660 llvm_unreachable("Fully covered switch above!");
663 static std::string
getModuleCachePath(ArrayRef
<std::string
> Args
) {
664 for (StringRef Arg
: llvm::reverse(Args
)) {
665 Arg
.consume_front("/clang:");
666 if (Arg
.consume_front("-fmodules-cache-path="))
667 return std::string(Arg
);
669 SmallString
<128> Path
;
670 driver::Driver::getDefaultModuleCachePath(Path
);
671 return std::string(Path
);
674 // getCompilationDataBase - If -compilation-database is set, load the
675 // compilation database from the specified file. Otherwise if the we're
676 // generating P1689 format, trying to generate the compilation database
677 // form specified command line after the positional parameter "--".
678 static std::unique_ptr
<tooling::CompilationDatabase
>
679 getCompilationDataBase(int argc
, char **argv
, std::string
&ErrorMessage
) {
680 llvm::InitLLVM
X(argc
, argv
);
681 ParseArgs(argc
, argv
);
683 if (!CompilationDB
.empty())
684 return tooling::JSONCompilationDatabase::loadFromFile(
685 CompilationDB
, ErrorMessage
,
686 tooling::JSONCommandLineSyntax::AutoDetect
);
688 if (Format
!= ScanningOutputFormat::P1689
) {
689 llvm::errs() << "the --compilation-database option: must be specified at "
694 // Trying to get the input file, the output file and the command line options
695 // from the positional parameter "--".
696 char **DoubleDash
= std::find(argv
, argv
+ argc
, StringRef("--"));
697 if (DoubleDash
== argv
+ argc
) {
698 llvm::errs() << "The command line arguments is required after '--' in "
699 "P1689 per file mode.";
703 llvm::IntrusiveRefCntPtr
<DiagnosticsEngine
> Diags
=
704 CompilerInstance::createDiagnostics(new DiagnosticOptions
);
705 driver::Driver
TheDriver(CommandLine
[0], llvm::sys::getDefaultTargetTriple(),
707 std::unique_ptr
<driver::Compilation
> C(
708 TheDriver
.BuildCompilation(CommandLine
));
712 auto Cmd
= C
->getJobs().begin();
713 auto CI
= std::make_unique
<CompilerInvocation
>();
714 CompilerInvocation::CreateFromArgs(*CI
, Cmd
->getArguments(), *Diags
,
719 FrontendOptions
&FEOpts
= CI
->getFrontendOpts();
720 if (FEOpts
.Inputs
.size() != 1) {
721 llvm::errs() << "Only one input file is allowed in P1689 per file mode.";
725 // There might be multiple jobs for a compilation. Extract the specified
726 // output filename from the last job.
727 auto LastCmd
= C
->getJobs().end();
729 if (LastCmd
->getOutputFilenames().size() != 1) {
730 llvm::errs() << "The command line should provide exactly one output file "
731 "in P1689 per file mode.\n";
733 StringRef OutputFile
= LastCmd
->getOutputFilenames().front();
735 class InplaceCompilationDatabase
: public tooling::CompilationDatabase
{
737 InplaceCompilationDatabase(StringRef InputFile
, StringRef OutputFile
,
738 ArrayRef
<const char *> CommandLine
)
739 : Command(".", InputFile
, {}, OutputFile
) {
740 for (auto *C
: CommandLine
)
741 Command
.CommandLine
.push_back(C
);
744 std::vector
<tooling::CompileCommand
>
745 getCompileCommands(StringRef FilePath
) const override
{
746 if (FilePath
!= Command
.Filename
)
751 std::vector
<std::string
> getAllFiles() const override
{
752 return {Command
.Filename
};
755 std::vector
<tooling::CompileCommand
>
756 getAllCompileCommands() const override
{
761 tooling::CompileCommand Command
;
764 return std::make_unique
<InplaceCompilationDatabase
>(
765 FEOpts
.Inputs
[0].getFile(), OutputFile
, CommandLine
);
768 int clang_scan_deps_main(int argc
, char **argv
, const llvm::ToolContext
&) {
769 std::string ErrorMessage
;
770 std::unique_ptr
<tooling::CompilationDatabase
> Compilations
=
771 getCompilationDataBase(argc
, argv
, ErrorMessage
);
773 llvm::errs() << ErrorMessage
<< "\n";
777 llvm::cl::PrintOptionValues();
779 // The command options are rewritten to run Clang in preprocessor only mode.
780 auto AdjustingCompilations
=
781 std::make_unique
<tooling::ArgumentsAdjustingCompilations
>(
782 std::move(Compilations
));
783 ResourceDirectoryCache ResourceDirCache
;
785 AdjustingCompilations
->appendArgumentsAdjuster(
786 [&ResourceDirCache
](const tooling::CommandLineArguments
&Args
,
787 StringRef FileName
) {
789 bool HasResourceDir
= false;
790 bool ClangCLMode
= false;
791 auto FlagsEnd
= llvm::find(Args
, "--");
792 if (FlagsEnd
!= Args
.begin()) {
794 llvm::sys::path::stem(Args
[0]).contains_insensitive("clang-cl") ||
795 llvm::is_contained(Args
, "--driver-mode=cl");
797 // Reverse scan, starting at the end or at the element before "--".
798 auto R
= std::make_reverse_iterator(FlagsEnd
);
799 for (auto I
= R
, E
= Args
.rend(); I
!= E
; ++I
) {
802 // Ignore arguments that are preceded by "-Xclang".
803 if ((I
+ 1) != E
&& I
[1] == "-Xclang")
806 // With clang-cl, the output obj file can be specified with
807 // "/opath", "/o path", "/Fopath", and the dash counterparts.
808 // Also, clang-cl adds ".obj" extension if none is found.
809 if ((Arg
== "-o" || Arg
== "/o") && I
!= R
)
810 LastO
= I
[-1]; // Next argument (reverse iterator)
811 else if (Arg
.startswith("/Fo") || Arg
.startswith("-Fo"))
812 LastO
= Arg
.drop_front(3).str();
813 else if (Arg
.startswith("/o") || Arg
.startswith("-o"))
814 LastO
= Arg
.drop_front(2).str();
816 if (!LastO
.empty() && !llvm::sys::path::has_extension(LastO
))
817 LastO
.append(".obj");
820 if (Arg
== "-resource-dir")
821 HasResourceDir
= true;
824 tooling::CommandLineArguments
AdjustedArgs(Args
.begin(), FlagsEnd
);
825 // The clang-cl driver passes "-o -" to the frontend. Inject the real
826 // file here to ensure "-MT" can be deduced if need be.
827 if (ClangCLMode
&& !LastO
.empty()) {
828 AdjustedArgs
.push_back("/clang:-o");
829 AdjustedArgs
.push_back("/clang:" + LastO
);
832 if (!HasResourceDir
&& ResourceDirRecipe
== RDRK_InvokeCompiler
) {
833 StringRef ResourceDir
=
834 ResourceDirCache
.findResourceDir(Args
, ClangCLMode
);
835 if (!ResourceDir
.empty()) {
836 AdjustedArgs
.push_back("-resource-dir");
837 AdjustedArgs
.push_back(std::string(ResourceDir
));
840 AdjustedArgs
.insert(AdjustedArgs
.end(), FlagsEnd
, Args
.end());
844 SharedStream
Errs(llvm::errs());
845 // Print out the dependency results to STDOUT by default.
846 SharedStream
DependencyOS(llvm::outs());
848 DependencyScanningService
Service(ScanMode
, Format
, OptimizeArgs
,
850 llvm::ThreadPool
Pool(llvm::hardware_concurrency(NumThreads
));
851 std::vector
<std::unique_ptr
<DependencyScanningTool
>> WorkerTools
;
852 for (unsigned I
= 0; I
< Pool
.getThreadCount(); ++I
)
853 WorkerTools
.push_back(std::make_unique
<DependencyScanningTool
>(Service
));
855 std::vector
<tooling::CompileCommand
> Inputs
=
856 AdjustingCompilations
->getAllCompileCommands();
858 std::atomic
<bool> HadErrors(false);
859 std::optional
<FullDeps
> FD
;
864 auto GetNextInputIndex
= [&]() -> std::optional
<size_t> {
865 std::unique_lock
<std::mutex
> LockGuard(Lock
);
866 if (Index
< Inputs
.size())
871 if (Format
== ScanningOutputFormat::Full
)
872 FD
.emplace(ModuleName
.empty() ? Inputs
.size() : 0);
875 llvm::outs() << "Running clang-scan-deps on " << Inputs
.size()
876 << " files using " << Pool
.getThreadCount() << " workers\n";
882 for (unsigned I
= 0; I
< Pool
.getThreadCount(); ++I
) {
883 Pool
.async([&, I
]() {
884 llvm::DenseSet
<ModuleID
> AlreadySeenModules
;
885 while (auto MaybeInputIndex
= GetNextInputIndex()) {
886 size_t LocalIndex
= *MaybeInputIndex
;
887 const tooling::CompileCommand
*Input
= &Inputs
[LocalIndex
];
888 std::string Filename
= std::move(Input
->Filename
);
889 std::string CWD
= std::move(Input
->Directory
);
891 std::optional
<StringRef
> MaybeModuleName
;
892 if (!ModuleName
.empty())
893 MaybeModuleName
= ModuleName
;
895 std::string
OutputDir(ModuleFilesDir
);
896 if (OutputDir
.empty())
897 OutputDir
= getModuleCachePath(Input
->CommandLine
);
898 auto LookupOutput
= [&](const ModuleID
&MID
, ModuleOutputKind MOK
) {
899 return ::lookupModuleOutput(MID
, MOK
, OutputDir
);
902 // Run the tool on it.
903 if (Format
== ScanningOutputFormat::Make
) {
905 WorkerTools
[I
]->getDependencyFile(Input
->CommandLine
, CWD
);
906 if (handleMakeDependencyToolResult(Filename
, MaybeFile
, DependencyOS
,
909 } else if (Format
== ScanningOutputFormat::P1689
) {
910 // It is useful to generate the make-format dependency output during
911 // the scanning for P1689. Otherwise the users need to scan again for
912 // it. We will generate the make-format dependency output if we find
913 // `-MF` in the command lines.
914 std::string MakeformatOutputPath
;
915 std::string MakeformatOutput
;
917 auto MaybeRule
= WorkerTools
[I
]->getP1689ModuleDependencyFile(
918 *Input
, CWD
, MakeformatOutput
, MakeformatOutputPath
);
920 if (handleP1689DependencyToolResult(Filename
, MaybeRule
, PD
, Errs
))
923 if (!MakeformatOutputPath
.empty() && !MakeformatOutput
.empty() &&
925 static std::mutex Lock
;
926 // With compilation database, we may open different files
927 // concurrently or we may write the same file concurrently. So we
928 // use a map here to allow multiple compile commands to write to the
929 // same file. Also we need a lock here to avoid data race.
930 static llvm::StringMap
<llvm::raw_fd_ostream
> OSs
;
931 std::unique_lock
<std::mutex
> LockGuard(Lock
);
933 auto OSIter
= OSs
.find(MakeformatOutputPath
);
934 if (OSIter
== OSs
.end()) {
936 OSIter
= OSs
.try_emplace(MakeformatOutputPath
,
937 MakeformatOutputPath
, EC
)
941 << "Failed to open P1689 make format output file \""
942 << MakeformatOutputPath
<< "\" for " << EC
.message()
946 SharedStream
MakeformatOS(OSIter
->second
);
947 llvm::Expected
<std::string
> MaybeOutput(MakeformatOutput
);
948 if (handleMakeDependencyToolResult(Filename
, MaybeOutput
,
952 } else if (MaybeModuleName
) {
953 auto MaybeModuleDepsGraph
= WorkerTools
[I
]->getModuleDependencies(
954 *MaybeModuleName
, Input
->CommandLine
, CWD
, AlreadySeenModules
,
956 if (handleModuleResult(*MaybeModuleName
, MaybeModuleDepsGraph
, *FD
,
957 LocalIndex
, DependencyOS
, Errs
))
960 auto MaybeTUDeps
= WorkerTools
[I
]->getTranslationUnitDependencies(
961 Input
->CommandLine
, CWD
, AlreadySeenModules
, LookupOutput
);
962 if (handleTranslationUnitResult(Filename
, MaybeTUDeps
, *FD
,
963 LocalIndex
, DependencyOS
, Errs
))
973 llvm::errs() << llvm::format(
974 "clang-scan-deps timing: %0.2fs wall, %0.2fs process\n",
975 T
.getTotalTime().getWallTime(), T
.getTotalTime().getProcessTime());
978 if (FD
&& FD
->roundTripCommands(llvm::errs()))
981 if (Format
== ScanningOutputFormat::Full
)
982 FD
->printFullOutput(llvm::outs());
983 else if (Format
== ScanningOutputFormat::P1689
)
984 PD
.printDependencies(llvm::outs());