From 5482432bf6cc7e334894734ebbdac0a97ee98b19 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Mon, 15 Aug 2022 17:54:00 -0700 Subject: [PATCH] [clang][deps] Compute command-lines for dependencies immediately Instead of delaying the generation of command-lines to after all dependencies are reported, compute them immediately. This is partly in preparation for splitting the TU driver command into its constituent cc1 and other jobs, but it also just simplifies working with the compiler invocation for modules if they are not "without paths". Also change the computation of the default output path in clang-scan-deps to scrape the implicit module cache from the command-line rather than get it from the dependency, since that is now unavailable at the time we make the callback. Differential Revision: https://reviews.llvm.org/D131934 --- .../DependencyScanningTool.h | 32 ++-- .../DependencyScanningWorker.h | 3 + .../DependencyScanning/ModuleDepCollector.h | 21 +-- .../DependencyScanningTool.cpp | 30 ++-- .../DependencyScanning/ModuleDepCollector.cpp | 139 +++++++++--------- clang/tools/clang-scan-deps/ClangScanDeps.cpp | 95 ++++++------ 6 files changed, 164 insertions(+), 156 deletions(-) diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h index aabc1e6b1667..9b4257dd7031 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h @@ -23,6 +23,10 @@ namespace clang { namespace tooling { namespace dependencies { +/// A callback to lookup module outputs for "-fmodule-file=", "-o" etc. +using LookupModuleOutputCallback = + llvm::function_ref; + /// The full dependencies and module graph for a specific input. struct FullDependencies { /// The identifier of the C++20 module this translation unit exports. @@ -45,17 +49,8 @@ struct FullDependencies { /// determined that the differences are benign for this compilation. std::vector ClangModuleDeps; - /// The original command line of the TU (excluding the compiler executable). - std::vector OriginalCommandLine; - - /// Get the full command line. - /// - /// \param LookupModuleOutput This function is called to fill in - /// "-fmodule-file=", "-o" and other output - /// arguments for dependencies. - std::vector getCommandLine( - llvm::function_ref - LookupModuleOutput) const; + /// The command line of the TU (excluding the compiler executable). + std::vector CommandLine; }; struct FullDependenciesResult { @@ -92,12 +87,16 @@ public: /// function for a single \c DependencyScanningTool in a /// single build. Use a different one for different tools, /// and clear it between builds. + /// \param LookupModuleOutput This function is called to fill in + /// "-fmodule-file=", "-o" and other output + /// arguments for dependencies. /// /// \returns a \c StringError with the diagnostic output if clang errors /// occurred, \c FullDependencies otherwise. llvm::Expected getFullDependencies(const std::vector &CommandLine, StringRef CWD, const llvm::StringSet<> &AlreadySeen, + LookupModuleOutputCallback LookupModuleOutput, llvm::Optional ModuleName = None); private: @@ -106,8 +105,9 @@ private: class FullDependencyConsumer : public DependencyConsumer { public: - FullDependencyConsumer(const llvm::StringSet<> &AlreadySeen) - : AlreadySeen(AlreadySeen) {} + FullDependencyConsumer(const llvm::StringSet<> &AlreadySeen, + LookupModuleOutputCallback LookupModuleOutput) + : AlreadySeen(AlreadySeen), LookupModuleOutput(LookupModuleOutput) {} void handleDependencyOutputOpts(const DependencyOutputOptions &) override {} @@ -127,6 +127,11 @@ public: ContextHash = std::move(Hash); } + std::string lookupModuleOutput(const ModuleID &ID, + ModuleOutputKind Kind) override { + return LookupModuleOutput(ID, Kind); + } + FullDependenciesResult getFullDependencies( const std::vector &OriginalCommandLine) const; @@ -138,6 +143,7 @@ private: std::string ContextHash; std::vector OutputPaths; const llvm::StringSet<> &AlreadySeen; + LookupModuleOutputCallback LookupModuleOutput; }; } // end namespace dependencies diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h index d6c0f2f1c6d6..0ee94a966411 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h @@ -42,6 +42,9 @@ public: virtual void handleModuleDependency(ModuleDeps MD) = 0; virtual void handleContextHash(std::string Hash) = 0; + + virtual std::string lookupModuleOutput(const ModuleID &ID, + ModuleOutputKind Kind) = 0; }; /// An individual dependency scanning worker that is able to run on its own diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h index 18c342ee85c1..76582efda551 100644 --- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h +++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h @@ -119,25 +119,11 @@ struct ModuleDeps { // the primary TU. bool ImportedByMainFile = false; - /// Whether the TU had a dependency file. The path in \c BuildInvocation is - /// cleared to avoid leaking the specific path from the TU into the module. - bool HadDependencyFile = false; - - /// Whether the TU had serialized diagnostics. The path in \c BuildInvocation - /// is cleared to avoid leaking the specific path from the TU into the module. - bool HadSerializedDiagnostics = false; - /// Compiler invocation that can be used to build this module (without paths). CompilerInvocation BuildInvocation; /// Gets the canonical command line suitable for passing to clang. - /// - /// \param LookupModuleOutput This function is called to fill in - /// "-fmodule-file=", "-o" and other output - /// arguments. - std::vector getCanonicalCommandLine( - llvm::function_ref - LookupModuleOutput) const; + std::vector getCanonicalCommandLine() const; }; class ModuleDepCollector; @@ -237,9 +223,12 @@ private: /// Constructs a CompilerInvocation that can be used to build the given /// module, excluding paths to discovered modular dependencies that are yet to /// be built. - CompilerInvocation makeInvocationForModuleBuildWithoutPaths( + CompilerInvocation makeInvocationForModuleBuildWithoutOutputs( const ModuleDeps &Deps, llvm::function_ref Optimize) const; + + /// Add paths that require looking up outputs to the given dependencies. + void addOutputPaths(ModuleDeps &Deps); }; } // end namespace dependencies diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp index cf6085857b4d..e96d325eb074 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp @@ -13,18 +13,12 @@ using namespace clang; using namespace tooling; using namespace dependencies; -std::vector FullDependencies::getCommandLine( - llvm::function_ref - LookupModuleOutput) const { +static std::vector +makeTUCommandLineWithoutPaths(ArrayRef OriginalCommandLine) { std::vector Args = OriginalCommandLine; Args.push_back("-fno-implicit-modules"); Args.push_back("-fno-implicit-module-maps"); - for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) - Args.push_back("-fmodule-file=" + PMD.PCMFile); - for (ModuleID MID : ClangModuleDeps) - Args.push_back("-fmodule-file=" + - LookupModuleOutput(MID, ModuleOutputKind::ModuleFile)); // These arguments are unused in explicit compiles. llvm::erase_if(Args, [](StringRef Arg) { @@ -72,6 +66,11 @@ llvm::Expected DependencyScanningTool::getDependencyFile( void handleContextHash(std::string Hash) override {} + std::string lookupModuleOutput(const ModuleID &ID, + ModuleOutputKind Kind) override { + llvm::report_fatal_error("unexpected call to lookupModuleOutput"); + } + void printDependencies(std::string &S) { assert(Opts && "Handled dependency output options."); @@ -113,8 +112,9 @@ llvm::Expected DependencyScanningTool::getFullDependencies( const std::vector &CommandLine, StringRef CWD, const llvm::StringSet<> &AlreadySeen, + LookupModuleOutputCallback LookupModuleOutput, llvm::Optional ModuleName) { - FullDependencyConsumer Consumer(AlreadySeen); + FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput); llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); if (Result) @@ -126,16 +126,24 @@ FullDependenciesResult FullDependencyConsumer::getFullDependencies( const std::vector &OriginalCommandLine) const { FullDependencies FD; - FD.OriginalCommandLine = ArrayRef(OriginalCommandLine).slice(1); + FD.CommandLine = makeTUCommandLineWithoutPaths( + ArrayRef(OriginalCommandLine).slice(1)); FD.ID.ContextHash = std::move(ContextHash); FD.FileDeps.assign(Dependencies.begin(), Dependencies.end()); + for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) + FD.CommandLine.push_back("-fmodule-file=" + PMD.PCMFile); + for (auto &&M : ClangModuleDeps) { auto &MD = M.second; - if (MD.ImportedByMainFile) + if (MD.ImportedByMainFile) { FD.ClangModuleDeps.push_back(MD.ID); + FD.CommandLine.push_back( + "-fmodule-file=" + + LookupModuleOutput(MD.ID, ModuleOutputKind::ModuleFile)); + } } FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index c6e7b08f3a2b..dd98316661e9 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -42,7 +42,47 @@ static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, Opts.UserEntries.push_back(Entries[Idx]); } -CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths( +static std::vector splitString(std::string S, char Separator) { + SmallVector Segments; + StringRef(S).split(Segments, Separator, /*MaxSplit=*/-1, /*KeepEmpty=*/false); + std::vector Result; + Result.reserve(Segments.size()); + for (StringRef Segment : Segments) + Result.push_back(Segment.str()); + return Result; +} + +void ModuleDepCollector::addOutputPaths(ModuleDeps &Deps) { + CompilerInvocation &CI = Deps.BuildInvocation; + for (ModuleID MID : Deps.ClangModuleDeps) + CI.getFrontendOpts().ModuleFiles.push_back( + Consumer.lookupModuleOutput(MID, ModuleOutputKind::ModuleFile)); + + CI.getFrontendOpts().OutputFile = + Consumer.lookupModuleOutput(Deps.ID, ModuleOutputKind::ModuleFile); + if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty()) + CI.getDiagnosticOpts().DiagnosticSerializationFile = + Consumer.lookupModuleOutput( + Deps.ID, ModuleOutputKind::DiagnosticSerializationFile); + if (!CI.getDependencyOutputOpts().OutputFile.empty()) { + CI.getDependencyOutputOpts().OutputFile = + Consumer.lookupModuleOutput(Deps.ID, ModuleOutputKind::DependencyFile); + CI.getDependencyOutputOpts().Targets = + splitString(Consumer.lookupModuleOutput( + Deps.ID, ModuleOutputKind::DependencyTargets), + '\0'); + if (!CI.getDependencyOutputOpts().OutputFile.empty() && + CI.getDependencyOutputOpts().Targets.empty()) { + // Fallback to -o as dependency target, as in the driver. + SmallString<128> Target; + quoteMakeTarget(CI.getFrontendOpts().OutputFile, Target); + CI.getDependencyOutputOpts().Targets.push_back(std::string(Target)); + } + } +} + +CompilerInvocation +ModuleDepCollector::makeInvocationForModuleBuildWithoutOutputs( const ModuleDeps &Deps, llvm::function_ref Optimize) const { // Make a deep copy of the original Clang invocation. @@ -58,8 +98,12 @@ CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths( CI.getFrontendOpts().OutputFile.clear(); CI.getCodeGenOpts().MainFileName.clear(); CI.getCodeGenOpts().DwarfDebugFlags.clear(); - CI.getDiagnosticOpts().DiagnosticSerializationFile.clear(); - CI.getDependencyOutputOpts().OutputFile.clear(); + // Map output paths that affect behaviour to "-" so their existence is in the + // context hash. The final path will be computed in addOutputPaths. + if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty()) + CI.getDiagnosticOpts().DiagnosticSerializationFile = "-"; + if (!CI.getDependencyOutputOpts().OutputFile.empty()) + CI.getDependencyOutputOpts().OutputFile = "-"; CI.getDependencyOutputOpts().Targets.clear(); CI.getFrontendOpts().ProgramAction = frontend::GenerateModule; @@ -78,6 +122,17 @@ CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths( CI.getHeaderSearchOpts().ModuleCachePruneInterval = 7 * 24 * 60 * 60; CI.getHeaderSearchOpts().ModuleCachePruneAfter = 31 * 24 * 60 * 60; + // Inputs + InputKind ModuleMapInputKind(CI.getFrontendOpts().DashX.getLanguage(), + InputKind::Format::ModuleMap); + CI.getFrontendOpts().Inputs.emplace_back(Deps.ClangModuleMapFile, + ModuleMapInputKind); + CI.getFrontendOpts().ModuleMapFiles = Deps.ModuleMapFileDeps; + + // Report the prebuilt modules this module uses. + for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps) + CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile); + // Remove any macro definitions that are explicitly ignored. if (!CI.getHeaderSearchOpts().ModulesIgnoreMacros.empty()) { llvm::erase_if( @@ -91,12 +146,6 @@ CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths( CI.getHeaderSearchOpts().ModulesIgnoreMacros.clear(); } - // Report the prebuilt modules this module uses. - for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps) - CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile); - - CI.getFrontendOpts().ModuleMapFiles = Deps.ModuleMapFileDeps; - Optimize(CI); // The original invocation probably didn't have strict context hash enabled. @@ -125,49 +174,8 @@ serializeCompilerInvocation(const CompilerInvocation &CI) { return std::vector{Args.begin(), Args.end()}; } -static std::vector splitString(std::string S, char Separator) { - SmallVector Segments; - StringRef(S).split(Segments, Separator, /*MaxSplit=*/-1, /*KeepEmpty=*/false); - std::vector Result; - Result.reserve(Segments.size()); - for (StringRef Segment : Segments) - Result.push_back(Segment.str()); - return Result; -} - -std::vector ModuleDeps::getCanonicalCommandLine( - llvm::function_ref - LookupModuleOutput) const { - CompilerInvocation CI(BuildInvocation); - FrontendOptions &FrontendOpts = CI.getFrontendOpts(); - - InputKind ModuleMapInputKind(FrontendOpts.DashX.getLanguage(), - InputKind::Format::ModuleMap); - FrontendOpts.Inputs.emplace_back(ClangModuleMapFile, ModuleMapInputKind); - FrontendOpts.OutputFile = - LookupModuleOutput(ID, ModuleOutputKind::ModuleFile); - if (HadSerializedDiagnostics) - CI.getDiagnosticOpts().DiagnosticSerializationFile = - LookupModuleOutput(ID, ModuleOutputKind::DiagnosticSerializationFile); - if (HadDependencyFile) { - DependencyOutputOptions &DepOpts = CI.getDependencyOutputOpts(); - DepOpts.OutputFile = - LookupModuleOutput(ID, ModuleOutputKind::DependencyFile); - DepOpts.Targets = splitString( - LookupModuleOutput(ID, ModuleOutputKind::DependencyTargets), '\0'); - if (!DepOpts.OutputFile.empty() && DepOpts.Targets.empty()) { - // Fallback to -o as dependency target, as in the driver. - SmallString<128> Target; - quoteMakeTarget(FrontendOpts.OutputFile, Target); - DepOpts.Targets.push_back(std::string(Target)); - } - } - - for (ModuleID MID : ClangModuleDeps) - FrontendOpts.ModuleFiles.push_back( - LookupModuleOutput(MID, ModuleOutputKind::ModuleFile)); - - return serializeCompilerInvocation(CI); +std::vector ModuleDeps::getCanonicalCommandLine() const { + return serializeCompilerInvocation(BuildInvocation); } static std::string getModuleContextHash(const ModuleDeps &MD) { @@ -190,23 +198,16 @@ static std::string getModuleContextHash(const ModuleDeps &MD) { return ""; }); - // Hash the input file paths and module dependencies. These paths may differ - // even if the invocation is identical if they depend on the contents of the - // files in the TU -- for example, case-insensitive paths to modulemap files. - // Usually such a case would indicate a missed optimization to canonicalize, - // but it may be difficult to canonicalize all cases when there is a VFS. - HashBuilder.add(MD.ClangModuleMapFile); - for (const auto &Dep : MD.PrebuiltModuleDeps) - HashBuilder.add(Dep.PCMFile); + // Hash the module dependencies. These paths may differ even if the invocation + // is identical if they depend on the contents of the files in the TU -- for + // example, case-insensitive paths to modulemap files. Usually such a case + // would indicate a missed optimization to canonicalize, but it may be + // difficult to canonicalize all cases when there is a VFS. for (const auto &ID : MD.ClangModuleDeps) { HashBuilder.add(ID.ModuleName); HashBuilder.add(ID.ContextHash); } - // Hash options that affect which callbacks are made for outputs. - HashBuilder.add(MD.HadDependencyFile); - HashBuilder.add(MD.HadSerializedDiagnostics); - llvm::BLAKE3Result<16> Hash = HashBuilder.final(); std::array Words; static_assert(sizeof(Hash) == sizeof(Words), "Hash must match Words"); @@ -387,22 +388,20 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { llvm::DenseSet SeenModules; addAllSubmodulePrebuiltDeps(M, MD, SeenModules); - MD.BuildInvocation = MDC.makeInvocationForModuleBuildWithoutPaths( + MD.BuildInvocation = MDC.makeInvocationForModuleBuildWithoutOutputs( MD, [&](CompilerInvocation &BuildInvocation) { if (MDC.OptimizeArgs) optimizeHeaderSearchOpts(BuildInvocation.getHeaderSearchOpts(), *MDC.ScanInstance.getASTReader(), *MF); }); - MD.HadSerializedDiagnostics = !MDC.OriginalInvocation.getDiagnosticOpts() - .DiagnosticSerializationFile.empty(); - MD.HadDependencyFile = - !MDC.OriginalInvocation.getDependencyOutputOpts().OutputFile.empty(); llvm::DenseSet AddedModules; addAllSubmoduleDeps(M, MD, AddedModules); - // Do this last since it requires the dependencies. + // Compute the context hash from the inputs. Requires dependencies. MD.ID.ContextHash = getModuleContextHash(MD); + // Finish the compiler invocation. Requires dependencies and the context hash. + MDC.addOutputPaths(MD); return MD.ID; } diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp index 8dfa02cd3e0f..9a5d091f9c86 100644 --- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Driver/Driver.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" @@ -268,10 +269,7 @@ public: Modules.insert(I, {{MD.ID, InputIndex}, std::move(MD)}); } - ID.CommandLine = - FD.getCommandLine([&](const ModuleID &MID, ModuleOutputKind MOK) { - return lookupModuleOutput(MID, MOK); - }); + ID.CommandLine = FD.CommandLine; Inputs.push_back(std::move(ID)); } @@ -301,10 +299,7 @@ public: {"file-deps", toJSONSorted(MD.FileDeps)}, {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)}, {"clang-modulemap-file", MD.ClangModuleMapFile}, - {"command-line", MD.getCanonicalCommandLine( - [&](const ModuleID &MID, ModuleOutputKind MOK) { - return lookupModuleOutput(MID, MOK); - })}, + {"command-line", MD.getCanonicalCommandLine()}, }; OutModules.push_back(std::move(O)); } @@ -330,42 +325,6 @@ public: } private: - std::string lookupModuleOutput(const ModuleID &MID, ModuleOutputKind MOK) { - // Cache the PCM path, since it will be queried repeatedly for each module. - // The other outputs are only queried once during getCanonicalCommandLine. - auto PCMPath = PCMPaths.insert({MID, ""}); - if (PCMPath.second) - PCMPath.first->second = constructPCMPath(MID); - switch (MOK) { - case ModuleOutputKind::ModuleFile: - return PCMPath.first->second; - case ModuleOutputKind::DependencyFile: - return PCMPath.first->second + ".d"; - case ModuleOutputKind::DependencyTargets: - // Null-separate the list of targets. - return join(ModuleDepTargets, StringRef("\0", 1)); - case ModuleOutputKind::DiagnosticSerializationFile: - return PCMPath.first->second + ".diag"; - } - llvm_unreachable("Fully covered switch above!"); - } - - /// Construct a path for the explicitly built PCM. - std::string constructPCMPath(ModuleID MID) const { - auto MDIt = Modules.find(IndexedModuleID{MID, 0}); - assert(MDIt != Modules.end()); - const ModuleDeps &MD = MDIt->second; - - StringRef Filename = llvm::sys::path::filename(MD.ImplicitModulePCMPath); - StringRef ModuleCachePath = llvm::sys::path::parent_path( - llvm::sys::path::parent_path(MD.ImplicitModulePCMPath)); - - SmallString<256> ExplicitPCMPath(!ModuleFilesDir.empty() ? ModuleFilesDir - : ModuleCachePath); - llvm::sys::path::append(ExplicitPCMPath, MD.ID.ContextHash, Filename); - return std::string(ExplicitPCMPath); - } - struct IndexedModuleID { ModuleID ID; mutable size_t InputIndex; @@ -395,7 +354,6 @@ private: std::mutex Lock; std::unordered_map Modules; - std::unordered_map PCMPaths; std::vector Inputs; }; @@ -417,6 +375,42 @@ static bool handleFullDependencyToolResult( return false; } +/// Construct a path for the explicitly built PCM. +static std::string constructPCMPath(ModuleID MID, StringRef OutputDir) { + SmallString<256> ExplicitPCMPath(OutputDir); + llvm::sys::path::append(ExplicitPCMPath, MID.ContextHash, + MID.ModuleName + "-" + MID.ContextHash + ".pcm"); + return std::string(ExplicitPCMPath); +} + +static std::string lookupModuleOutput(const ModuleID &MID, ModuleOutputKind MOK, + StringRef OutputDir) { + std::string PCMPath = constructPCMPath(MID, OutputDir); + switch (MOK) { + case ModuleOutputKind::ModuleFile: + return PCMPath; + case ModuleOutputKind::DependencyFile: + return PCMPath + ".d"; + case ModuleOutputKind::DependencyTargets: + // Null-separate the list of targets. + return join(ModuleDepTargets, StringRef("\0", 1)); + case ModuleOutputKind::DiagnosticSerializationFile: + return PCMPath + ".diag"; + } + llvm_unreachable("Fully covered switch above!"); +} + +static std::string getModuleCachePath(ArrayRef Args) { + for (StringRef Arg : llvm::reverse(Args)) { + Arg.consume_front("/clang:"); + if (Arg.consume_front("-fmodules-cache-path=")) + return std::string(Arg); + } + SmallString<128> Path; + driver::Driver::getDefaultModuleCachePath(Path); + return std::string(Path); +} + int main(int argc, const char **argv) { llvm::InitLLVM X(argc, argv); llvm::cl::HideUnrelatedOptions(DependencyScannerCategory); @@ -545,6 +539,14 @@ int main(int argc, const char **argv) { Optional MaybeModuleName; if (!ModuleName.empty()) MaybeModuleName = ModuleName; + + std::string OutputDir(ModuleFilesDir); + if (OutputDir.empty()) + OutputDir = getModuleCachePath(Input->CommandLine); + auto LookupOutput = [&](const ModuleID &MID, ModuleOutputKind MOK) { + return ::lookupModuleOutput(MID, MOK, OutputDir); + }; + // Run the tool on it. if (Format == ScanningOutputFormat::Make) { auto MaybeFile = WorkerTools[I]->getDependencyFile( @@ -554,7 +556,8 @@ int main(int argc, const char **argv) { HadErrors = true; } else { auto MaybeFullDeps = WorkerTools[I]->getFullDependencies( - Input->CommandLine, CWD, AlreadySeenModules, MaybeModuleName); + Input->CommandLine, CWD, AlreadySeenModules, LookupOutput, + MaybeModuleName); if (handleFullDependencyToolResult(Filename, MaybeFullDeps, FD, LocalIndex, DependencyOS, Errs)) HadErrors = true;