| //===- ClangScanDeps.cpp - Implementation of clang-scan-deps --------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "clang/Tooling/CommonOptionsParser.h" |
| #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" |
| #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" |
| #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" |
| #include "clang/Tooling/JSONCompilationDatabase.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/Twine.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/FileUtilities.h" |
| #include "llvm/Support/InitLLVM.h" |
| #include "llvm/Support/JSON.h" |
| #include "llvm/Support/Program.h" |
| #include "llvm/Support/Signals.h" |
| #include "llvm/Support/ThreadPool.h" |
| #include "llvm/Support/Threading.h" |
| #include <mutex> |
| #include <thread> |
| |
| using namespace clang; |
| using namespace tooling::dependencies; |
| |
| namespace { |
| |
| class SharedStream { |
| public: |
| SharedStream(raw_ostream &OS) : OS(OS) {} |
| void applyLocked(llvm::function_ref<void(raw_ostream &OS)> Fn) { |
| std::unique_lock<std::mutex> LockGuard(Lock); |
| Fn(OS); |
| OS.flush(); |
| } |
| |
| private: |
| std::mutex Lock; |
| raw_ostream &OS; |
| }; |
| |
| class ResourceDirectoryCache { |
| public: |
| /// findResourceDir finds the resource directory relative to the clang |
| /// compiler being used in Args, by running it with "-print-resource-dir" |
| /// option and cache the results for reuse. \returns resource directory path |
| /// associated with the given invocation command or empty string if the |
| /// compiler path is NOT an absolute path. |
| StringRef findResourceDir(const tooling::CommandLineArguments &Args, |
| bool ClangCLMode) { |
| if (Args.size() < 1) |
| return ""; |
| |
| const std::string &ClangBinaryPath = Args[0]; |
| if (!llvm::sys::path::is_absolute(ClangBinaryPath)) |
| return ""; |
| |
| const std::string &ClangBinaryName = |
| std::string(llvm::sys::path::filename(ClangBinaryPath)); |
| |
| std::unique_lock<std::mutex> LockGuard(CacheLock); |
| const auto &CachedResourceDir = Cache.find(ClangBinaryPath); |
| if (CachedResourceDir != Cache.end()) |
| return CachedResourceDir->second; |
| |
| std::vector<StringRef> PrintResourceDirArgs{ClangBinaryName}; |
| if (ClangCLMode) |
| PrintResourceDirArgs.push_back("/clang:-print-resource-dir"); |
| else |
| PrintResourceDirArgs.push_back("-print-resource-dir"); |
| |
| llvm::SmallString<64> OutputFile, ErrorFile; |
| llvm::sys::fs::createTemporaryFile("print-resource-dir-output", |
| "" /*no-suffix*/, OutputFile); |
| llvm::sys::fs::createTemporaryFile("print-resource-dir-error", |
| "" /*no-suffix*/, ErrorFile); |
| llvm::FileRemover OutputRemover(OutputFile.c_str()); |
| llvm::FileRemover ErrorRemover(ErrorFile.c_str()); |
| llvm::Optional<StringRef> Redirects[] = { |
| {""}, // Stdin |
| OutputFile.str(), |
| ErrorFile.str(), |
| }; |
| if (const int RC = llvm::sys::ExecuteAndWait( |
| ClangBinaryPath, PrintResourceDirArgs, {}, Redirects)) { |
| auto ErrorBuf = llvm::MemoryBuffer::getFile(ErrorFile.c_str()); |
| llvm::errs() << ErrorBuf.get()->getBuffer(); |
| return ""; |
| } |
| |
| auto OutputBuf = llvm::MemoryBuffer::getFile(OutputFile.c_str()); |
| if (!OutputBuf) |
| return ""; |
| StringRef Output = OutputBuf.get()->getBuffer().rtrim('\n'); |
| |
| Cache[ClangBinaryPath] = Output.str(); |
| return Cache[ClangBinaryPath]; |
| } |
| |
| private: |
| std::map<std::string, std::string> Cache; |
| std::mutex CacheLock; |
| }; |
| |
| llvm::cl::opt<bool> Help("h", llvm::cl::desc("Alias for -help"), |
| llvm::cl::Hidden); |
| |
| llvm::cl::OptionCategory DependencyScannerCategory("Tool options"); |
| |
| static llvm::cl::opt<ScanningMode> ScanMode( |
| "mode", |
| llvm::cl::desc("The preprocessing mode used to compute the dependencies"), |
| llvm::cl::values( |
| clEnumValN(ScanningMode::MinimizedSourcePreprocessing, |
| "preprocess-minimized-sources", |
| "The set of dependencies is computed by preprocessing the " |
| "source files that were minimized to only include the " |
| "contents that might affect the dependencies"), |
| clEnumValN(ScanningMode::CanonicalPreprocessing, "preprocess", |
| "The set of dependencies is computed by preprocessing the " |
| "unmodified source files")), |
| llvm::cl::init(ScanningMode::MinimizedSourcePreprocessing), |
| llvm::cl::cat(DependencyScannerCategory)); |
| |
| static llvm::cl::opt<ScanningOutputFormat> Format( |
| "format", llvm::cl::desc("The output format for the dependencies"), |
| llvm::cl::values(clEnumValN(ScanningOutputFormat::Make, "make", |
| "Makefile compatible dep file"), |
| clEnumValN(ScanningOutputFormat::Full, "experimental-full", |
| "Full dependency graph suitable" |
| " for explicitly building modules. This format " |
| "is experimental and will change.")), |
| llvm::cl::init(ScanningOutputFormat::Make), |
| llvm::cl::cat(DependencyScannerCategory)); |
| |
| // This mode is mostly useful for development of explicitly built modules. |
| // Command lines will contain arguments specifying modulemap file paths and |
| // absolute paths to PCM files in the module cache directory. |
| // |
| // Build tools that want to put the PCM files in a different location should use |
| // the C++ APIs instead, of which there are two flavors: |
| // |
| // 1. APIs that generate arguments with paths to modulemap and PCM files via |
| // callbacks provided by the client: |
| // * ModuleDeps::getCanonicalCommandLine(LookupPCMPath, LookupModuleDeps) |
| // * FullDependencies::getAdditionalArgs(LookupPCMPath, LookupModuleDeps) |
| // |
| // 2. APIs that don't generate arguments with paths to modulemap or PCM files |
| // and instead expect the client to append them manually after the fact: |
| // * ModuleDeps::getCanonicalCommandLineWithoutModulePaths() |
| // * FullDependencies::getAdditionalArgsWithoutModulePaths() |
| // |
| static llvm::cl::opt<bool> GenerateModulesPathArgs( |
| "generate-modules-path-args", |
| llvm::cl::desc( |
| "With '-format experimental-full', include arguments specifying " |
| "modules-related paths in the generated command lines: " |
| "'-fmodule-file=', '-o', '-fmodule-map-file='."), |
| llvm::cl::init(false), llvm::cl::cat(DependencyScannerCategory)); |
| |
| static llvm::cl::opt<std::string> ModuleFilesDir( |
| "module-files-dir", |
| llvm::cl::desc("With '-generate-modules-path-args', paths to module files " |
| "in the generated command lines will begin with the " |
| "specified directory instead the module cache directory."), |
| llvm::cl::cat(DependencyScannerCategory)); |
| |
| static llvm::cl::opt<bool> OptimizeArgs( |
| "optimize-args", |
| llvm::cl::desc("Whether to optimize command-line arguments of modules."), |
| llvm::cl::init(false), llvm::cl::cat(DependencyScannerCategory)); |
| |
| llvm::cl::opt<unsigned> |
| NumThreads("j", llvm::cl::Optional, |
| llvm::cl::desc("Number of worker threads to use (default: use " |
| "all concurrent threads)"), |
| llvm::cl::init(0), llvm::cl::cat(DependencyScannerCategory)); |
| |
| llvm::cl::opt<std::string> |
| CompilationDB("compilation-database", |
| llvm::cl::desc("Compilation database"), llvm::cl::Required, |
| llvm::cl::cat(DependencyScannerCategory)); |
| |
| llvm::cl::opt<bool> ReuseFileManager( |
| "reuse-filemanager", |
| llvm::cl::desc("Reuse the file manager and its cache between invocations."), |
| llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory)); |
| |
| llvm::cl::opt<bool> SkipExcludedPPRanges( |
| "skip-excluded-pp-ranges", |
| llvm::cl::desc( |
| "Use the preprocessor optimization that skips excluded conditionals by " |
| "bumping the buffer pointer in the lexer instead of lexing the tokens " |
| "until reaching the end directive."), |
| llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory)); |
| |
| llvm::cl::opt<std::string> ModuleName( |
| "module-name", llvm::cl::Optional, |
| llvm::cl::desc("the module of which the dependencies are to be computed"), |
| llvm::cl::cat(DependencyScannerCategory)); |
| |
| enum ResourceDirRecipeKind { |
| RDRK_ModifyCompilerPath, |
| RDRK_InvokeCompiler, |
| }; |
| |
| static llvm::cl::opt<ResourceDirRecipeKind> ResourceDirRecipe( |
| "resource-dir-recipe", |
| llvm::cl::desc("How to produce missing '-resource-dir' argument"), |
| llvm::cl::values( |
| clEnumValN(RDRK_ModifyCompilerPath, "modify-compiler-path", |
| "Construct the resource directory from the compiler path in " |
| "the compilation database. This assumes it's part of the " |
| "same toolchain as this clang-scan-deps. (default)"), |
| clEnumValN(RDRK_InvokeCompiler, "invoke-compiler", |
| "Invoke the compiler with '-print-resource-dir' and use the " |
| "reported path as the resource directory. (deprecated)")), |
| llvm::cl::init(RDRK_ModifyCompilerPath), |
| llvm::cl::cat(DependencyScannerCategory)); |
| |
| llvm::cl::opt<bool> Verbose("v", llvm::cl::Optional, |
| llvm::cl::desc("Use verbose output."), |
| llvm::cl::init(false), |
| llvm::cl::cat(DependencyScannerCategory)); |
| |
| } // end anonymous namespace |
| |
| /// Takes the result of a dependency scan and prints error / dependency files |
| /// based on the result. |
| /// |
| /// \returns True on error. |
| static bool |
| handleMakeDependencyToolResult(const std::string &Input, |
| llvm::Expected<std::string> &MaybeFile, |
| SharedStream &OS, SharedStream &Errs) { |
| if (!MaybeFile) { |
| llvm::handleAllErrors( |
| MaybeFile.takeError(), [&Input, &Errs](llvm::StringError &Err) { |
| Errs.applyLocked([&](raw_ostream &OS) { |
| OS << "Error while scanning dependencies for " << Input << ":\n"; |
| OS << Err.getMessage(); |
| }); |
| }); |
| return true; |
| } |
| OS.applyLocked([&](raw_ostream &OS) { OS << *MaybeFile; }); |
| return false; |
| } |
| |
| static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) { |
| std::vector<llvm::StringRef> Strings; |
| for (auto &&I : Set) |
| Strings.push_back(I.getKey()); |
| llvm::sort(Strings); |
| return llvm::json::Array(Strings); |
| } |
| |
| static llvm::json::Array toJSONSorted(std::vector<ModuleID> V) { |
| llvm::sort(V, [](const ModuleID &A, const ModuleID &B) { |
| return std::tie(A.ModuleName, A.ContextHash) < |
| std::tie(B.ModuleName, B.ContextHash); |
| }); |
| |
| llvm::json::Array Ret; |
| for (const ModuleID &MID : V) |
| Ret.push_back(llvm::json::Object( |
| {{"module-name", MID.ModuleName}, {"context-hash", MID.ContextHash}})); |
| return Ret; |
| } |
| |
| // Thread safe. |
| class FullDeps { |
| public: |
| void mergeDeps(StringRef Input, FullDependenciesResult FDR, |
| size_t InputIndex) { |
| const FullDependencies &FD = FDR.FullDeps; |
| |
| InputDeps ID; |
| ID.FileName = std::string(Input); |
| ID.ContextHash = std::move(FD.ID.ContextHash); |
| ID.FileDeps = std::move(FD.FileDeps); |
| ID.ModuleDeps = std::move(FD.ClangModuleDeps); |
| |
| std::unique_lock<std::mutex> ul(Lock); |
| for (const ModuleDeps &MD : FDR.DiscoveredModules) { |
| auto I = Modules.find({MD.ID, 0}); |
| if (I != Modules.end()) { |
| I->first.InputIndex = std::min(I->first.InputIndex, InputIndex); |
| continue; |
| } |
| Modules.insert(I, {{MD.ID, InputIndex}, std::move(MD)}); |
| } |
| |
| ID.AdditionalCommandLine = |
| GenerateModulesPathArgs |
| ? FD.getAdditionalArgs( |
| [&](ModuleID MID) { return lookupPCMPath(MID); }, |
| [&](ModuleID MID) -> const ModuleDeps & { |
| return lookupModuleDeps(MID); |
| }) |
| : FD.getAdditionalArgsWithoutModulePaths(); |
| |
| Inputs.push_back(std::move(ID)); |
| } |
| |
| void printFullOutput(raw_ostream &OS) { |
| // Sort the modules by name to get a deterministic order. |
| std::vector<IndexedModuleID> ModuleIDs; |
| for (auto &&M : Modules) |
| ModuleIDs.push_back(M.first); |
| llvm::sort(ModuleIDs, |
| [](const IndexedModuleID &A, const IndexedModuleID &B) { |
| return std::tie(A.ID.ModuleName, A.InputIndex) < |
| std::tie(B.ID.ModuleName, B.InputIndex); |
| }); |
| |
| llvm::sort(Inputs, [](const InputDeps &A, const InputDeps &B) { |
| return A.FileName < B.FileName; |
| }); |
| |
| using namespace llvm::json; |
| |
| Array OutModules; |
| for (auto &&ModID : ModuleIDs) { |
| auto &MD = Modules[ModID]; |
| Object O{ |
| {"name", MD.ID.ModuleName}, |
| {"context-hash", MD.ID.ContextHash}, |
| {"file-deps", toJSONSorted(MD.FileDeps)}, |
| {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)}, |
| {"clang-modulemap-file", MD.ClangModuleMapFile}, |
| {"command-line", |
| GenerateModulesPathArgs |
| ? MD.getCanonicalCommandLine( |
| [&](ModuleID MID) { return lookupPCMPath(MID); }, |
| [&](ModuleID MID) -> const ModuleDeps & { |
| return lookupModuleDeps(MID); |
| }) |
| : MD.getCanonicalCommandLineWithoutModulePaths()}, |
| }; |
| OutModules.push_back(std::move(O)); |
| } |
| |
| Array TUs; |
| for (auto &&I : Inputs) { |
| Object O{ |
| {"input-file", I.FileName}, |
| {"clang-context-hash", I.ContextHash}, |
| {"file-deps", I.FileDeps}, |
| {"clang-module-deps", toJSONSorted(I.ModuleDeps)}, |
| {"command-line", I.AdditionalCommandLine}, |
| }; |
| TUs.push_back(std::move(O)); |
| } |
| |
| Object Output{ |
| {"modules", std::move(OutModules)}, |
| {"translation-units", std::move(TUs)}, |
| }; |
| |
| OS << llvm::formatv("{0:2}\n", Value(std::move(Output))); |
| } |
| |
| private: |
| StringRef lookupPCMPath(ModuleID MID) { |
| auto PCMPath = PCMPaths.insert({MID, ""}); |
| if (PCMPath.second) |
| PCMPath.first->second = constructPCMPath(lookupModuleDeps(MID)); |
| return PCMPath.first->second; |
| } |
| |
| /// Construct a path for the explicitly built PCM. |
| std::string constructPCMPath(const ModuleDeps &MD) const { |
| StringRef Filename = llvm::sys::path::filename(MD.ImplicitModulePCMPath); |
| |
| SmallString<256> ExplicitPCMPath( |
| !ModuleFilesDir.empty() |
| ? ModuleFilesDir |
| : MD.BuildInvocation.getHeaderSearchOpts().ModuleCachePath); |
| llvm::sys::path::append(ExplicitPCMPath, MD.ID.ContextHash, Filename); |
| return std::string(ExplicitPCMPath); |
| } |
| |
| const ModuleDeps &lookupModuleDeps(ModuleID MID) { |
| auto I = Modules.find(IndexedModuleID{MID, 0}); |
| assert(I != Modules.end()); |
| return I->second; |
| }; |
| |
| struct IndexedModuleID { |
| ModuleID ID; |
| mutable size_t InputIndex; |
| |
| bool operator==(const IndexedModuleID &Other) const { |
| return ID.ModuleName == Other.ID.ModuleName && |
| ID.ContextHash == Other.ID.ContextHash; |
| } |
| }; |
| |
| struct IndexedModuleIDHasher { |
| std::size_t operator()(const IndexedModuleID &IMID) const { |
| using llvm::hash_combine; |
| |
| return hash_combine(IMID.ID.ModuleName, IMID.ID.ContextHash); |
| } |
| }; |
| |
| struct InputDeps { |
| std::string FileName; |
| std::string ContextHash; |
| std::vector<std::string> FileDeps; |
| std::vector<ModuleID> ModuleDeps; |
| std::vector<std::string> AdditionalCommandLine; |
| }; |
| |
| std::mutex Lock; |
| std::unordered_map<IndexedModuleID, ModuleDeps, IndexedModuleIDHasher> |
| Modules; |
| std::unordered_map<ModuleID, std::string, ModuleIDHasher> PCMPaths; |
| std::vector<InputDeps> Inputs; |
| }; |
| |
| static bool handleFullDependencyToolResult( |
| const std::string &Input, |
| llvm::Expected<FullDependenciesResult> &MaybeFullDeps, FullDeps &FD, |
| size_t InputIndex, SharedStream &OS, SharedStream &Errs) { |
| if (!MaybeFullDeps) { |
| llvm::handleAllErrors( |
| MaybeFullDeps.takeError(), [&Input, &Errs](llvm::StringError &Err) { |
| Errs.applyLocked([&](raw_ostream &OS) { |
| OS << "Error while scanning dependencies for " << Input << ":\n"; |
| OS << Err.getMessage(); |
| }); |
| }); |
| return true; |
| } |
| FD.mergeDeps(Input, std::move(*MaybeFullDeps), InputIndex); |
| return false; |
| } |
| |
| int main(int argc, const char **argv) { |
| llvm::InitLLVM X(argc, argv); |
| llvm::cl::HideUnrelatedOptions(DependencyScannerCategory); |
| if (!llvm::cl::ParseCommandLineOptions(argc, argv)) |
| return 1; |
| |
| std::string ErrorMessage; |
| std::unique_ptr<tooling::JSONCompilationDatabase> Compilations = |
| tooling::JSONCompilationDatabase::loadFromFile( |
| CompilationDB, ErrorMessage, |
| tooling::JSONCommandLineSyntax::AutoDetect); |
| if (!Compilations) { |
| llvm::errs() << "error: " << ErrorMessage << "\n"; |
| return 1; |
| } |
| |
| llvm::cl::PrintOptionValues(); |
| |
| // The command options are rewritten to run Clang in preprocessor only mode. |
| auto AdjustingCompilations = |
| std::make_unique<tooling::ArgumentsAdjustingCompilations>( |
| std::move(Compilations)); |
| ResourceDirectoryCache ResourceDirCache; |
| |
| AdjustingCompilations->appendArgumentsAdjuster( |
| [&ResourceDirCache](const tooling::CommandLineArguments &Args, |
| StringRef FileName) { |
| std::string LastO = ""; |
| bool HasResourceDir = false; |
| bool ClangCLMode = false; |
| auto FlagsEnd = llvm::find(Args, "--"); |
| if (FlagsEnd != Args.begin()) { |
| ClangCLMode = |
| llvm::sys::path::stem(Args[0]).contains_insensitive("clang-cl") || |
| llvm::is_contained(Args, "--driver-mode=cl"); |
| |
| // Reverse scan, starting at the end or at the element before "--". |
| auto R = llvm::make_reverse_iterator(FlagsEnd); |
| for (auto I = R, E = Args.rend(); I != E; ++I) { |
| StringRef Arg = *I; |
| if (ClangCLMode) { |
| // Ignore arguments that are preceded by "-Xclang". |
| if ((I + 1) != E && I[1] == "-Xclang") |
| continue; |
| if (LastO.empty()) { |
| // With clang-cl, the output obj file can be specified with |
| // "/opath", "/o path", "/Fopath", and the dash counterparts. |
| // Also, clang-cl adds ".obj" extension if none is found. |
| if ((Arg == "-o" || Arg == "/o") && I != R) |
| LastO = I[-1]; // Next argument (reverse iterator) |
| else if (Arg.startswith("/Fo") || Arg.startswith("-Fo")) |
| LastO = Arg.drop_front(3).str(); |
| else if (Arg.startswith("/o") || Arg.startswith("-o")) |
| LastO = Arg.drop_front(2).str(); |
| |
| if (!LastO.empty() && !llvm::sys::path::has_extension(LastO)) |
| LastO.append(".obj"); |
| } |
| } |
| if (Arg == "-resource-dir") |
| HasResourceDir = true; |
| } |
| } |
| tooling::CommandLineArguments AdjustedArgs(Args.begin(), FlagsEnd); |
| // The clang-cl driver passes "-o -" to the frontend. Inject the real |
| // file here to ensure "-MT" can be deduced if need be. |
| if (ClangCLMode && !LastO.empty()) { |
| AdjustedArgs.push_back("/clang:-o"); |
| AdjustedArgs.push_back("/clang:" + LastO); |
| } |
| |
| if (!HasResourceDir && ResourceDirRecipe == RDRK_InvokeCompiler) { |
| StringRef ResourceDir = |
| ResourceDirCache.findResourceDir(Args, ClangCLMode); |
| if (!ResourceDir.empty()) { |
| AdjustedArgs.push_back("-resource-dir"); |
| AdjustedArgs.push_back(std::string(ResourceDir)); |
| } |
| } |
| AdjustedArgs.insert(AdjustedArgs.end(), FlagsEnd, Args.end()); |
| return AdjustedArgs; |
| }); |
| |
| SharedStream Errs(llvm::errs()); |
| // Print out the dependency results to STDOUT by default. |
| SharedStream DependencyOS(llvm::outs()); |
| |
| DependencyScanningService Service(ScanMode, Format, ReuseFileManager, |
| SkipExcludedPPRanges, OptimizeArgs); |
| llvm::ThreadPool Pool(llvm::hardware_concurrency(NumThreads)); |
| std::vector<std::unique_ptr<DependencyScanningTool>> WorkerTools; |
| for (unsigned I = 0; I < Pool.getThreadCount(); ++I) |
| WorkerTools.push_back(std::make_unique<DependencyScanningTool>(Service)); |
| |
| std::vector<tooling::CompileCommand> Inputs = |
| AdjustingCompilations->getAllCompileCommands(); |
| |
| std::atomic<bool> HadErrors(false); |
| FullDeps FD; |
| std::mutex Lock; |
| size_t Index = 0; |
| |
| if (Verbose) { |
| llvm::outs() << "Running clang-scan-deps on " << Inputs.size() |
| << " files using " << Pool.getThreadCount() << " workers\n"; |
| } |
| for (unsigned I = 0; I < Pool.getThreadCount(); ++I) { |
| Pool.async([I, &Lock, &Index, &Inputs, &HadErrors, &FD, &WorkerTools, |
| &DependencyOS, &Errs]() { |
| llvm::StringSet<> AlreadySeenModules; |
| while (true) { |
| const tooling::CompileCommand *Input; |
| std::string Filename; |
| std::string CWD; |
| size_t LocalIndex; |
| // Take the next input. |
| { |
| std::unique_lock<std::mutex> LockGuard(Lock); |
| if (Index >= Inputs.size()) |
| return; |
| LocalIndex = Index; |
| Input = &Inputs[Index++]; |
| Filename = std::move(Input->Filename); |
| CWD = std::move(Input->Directory); |
| } |
| Optional<StringRef> MaybeModuleName; |
| if (!ModuleName.empty()) |
| MaybeModuleName = ModuleName; |
| // Run the tool on it. |
| if (Format == ScanningOutputFormat::Make) { |
| auto MaybeFile = WorkerTools[I]->getDependencyFile( |
| Input->CommandLine, CWD, MaybeModuleName); |
| if (handleMakeDependencyToolResult(Filename, MaybeFile, DependencyOS, |
| Errs)) |
| HadErrors = true; |
| } else { |
| auto MaybeFullDeps = WorkerTools[I]->getFullDependencies( |
| Input->CommandLine, CWD, AlreadySeenModules, MaybeModuleName); |
| if (handleFullDependencyToolResult(Filename, MaybeFullDeps, FD, |
| LocalIndex, DependencyOS, Errs)) |
| HadErrors = true; |
| } |
| } |
| }); |
| } |
| Pool.wait(); |
| |
| if (Format == ScanningOutputFormat::Full) |
| FD.printFullOutput(llvm::outs()); |
| |
| return HadErrors; |
| } |