diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2021-11-19 20:06:13 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2021-11-19 20:06:13 +0000 |
| commit | c0981da47d5696fe36474fcf86b4ce03ae3ff818 (patch) | |
| tree | f42add1021b9f2ac6a69ac7cf6c4499962739a45 /clang/lib/Tooling/DependencyScanning | |
| parent | 344a3780b2e33f6ca763666c380202b18aab72a3 (diff) | |
Diffstat (limited to 'clang/lib/Tooling/DependencyScanning')
4 files changed, 196 insertions, 127 deletions
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp index 4f3e574719d2..4b6c87aba62f 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp @@ -15,9 +15,9 @@ using namespace dependencies; DependencyScanningService::DependencyScanningService( ScanningMode Mode, ScanningOutputFormat Format, bool ReuseFileManager, - bool SkipExcludedPPRanges) + bool SkipExcludedPPRanges, bool OptimizeArgs) : Mode(Mode), Format(Format), ReuseFileManager(ReuseFileManager), - SkipExcludedPPRanges(SkipExcludedPPRanges) { + SkipExcludedPPRanges(SkipExcludedPPRanges), OptimizeArgs(OptimizeArgs) { // Initialize targets for object file support. llvm::InitializeAllTargets(); llvm::InitializeAllTargetMCs(); diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp index 2fd12f7e12b1..739712baadd0 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp @@ -24,8 +24,6 @@ std::vector<std::string> FullDependencies::getAdditionalArgs( ClangModuleDeps, LookupPCMPath, LookupModuleDeps, PCMPaths, ModMapPaths); for (const std::string &PCMPath : PCMPaths) Ret.push_back("-fmodule-file=" + PCMPath); - for (const std::string &ModMapPath : ModMapPaths) - Ret.push_back("-fmodule-map-file=" + ModMapPath); return Ret; } @@ -37,10 +35,8 @@ FullDependencies::getAdditionalArgsWithoutModulePaths() const { "-fno-implicit-module-maps", }; - for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) { - Args.push_back("-fmodule-file=" + PMD.ModuleName + "=" + PMD.PCMFile); - Args.push_back("-fmodule-map-file=" + PMD.ModuleMapFile); - } + for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) + Args.push_back("-fmodule-file=" + PMD.PCMFile); return Args; } @@ -50,7 +46,8 @@ DependencyScanningTool::DependencyScanningTool( : Worker(Service) {} llvm::Expected<std::string> DependencyScanningTool::getDependencyFile( - const tooling::CompilationDatabase &Compilations, StringRef CWD) { + const std::vector<std::string> &CommandLine, StringRef CWD, + llvm::Optional<StringRef> ModuleName) { /// Prints out all of the gathered dependencies into a string. class MakeDependencyPrinterConsumer : public DependencyConsumer { public: @@ -102,17 +99,9 @@ llvm::Expected<std::string> DependencyScanningTool::getDependencyFile( std::vector<std::string> Dependencies; }; - // We expect a single command here because if a source file occurs multiple - // times in the original CDB, then `computeDependencies` would run the - // `DependencyScanningAction` once for every time the input occured in the - // CDB. Instead we split up the CDB into single command chunks to avoid this - // behavior. - assert(Compilations.getAllCompileCommands().size() == 1 && - "Expected a compilation database with a single command!"); - std::string Input = Compilations.getAllCompileCommands().front().Filename; - MakeDependencyPrinterConsumer Consumer; - auto Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer); + auto Result = + Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); if (Result) return std::move(Result); std::string Output; @@ -122,8 +111,9 @@ llvm::Expected<std::string> DependencyScanningTool::getDependencyFile( llvm::Expected<FullDependenciesResult> DependencyScanningTool::getFullDependencies( - const tooling::CompilationDatabase &Compilations, StringRef CWD, - const llvm::StringSet<> &AlreadySeen) { + const std::vector<std::string> &CommandLine, StringRef CWD, + const llvm::StringSet<> &AlreadySeen, + llvm::Optional<StringRef> ModuleName) { class FullDependencyPrinterConsumer : public DependencyConsumer { public: FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen) @@ -180,24 +170,15 @@ DependencyScanningTool::getFullDependencies( private: std::vector<std::string> Dependencies; std::vector<PrebuiltModuleDep> PrebuiltModuleDeps; - std::unordered_map<std::string, ModuleDeps> ClangModuleDeps; + std::map<std::string, ModuleDeps> ClangModuleDeps; std::string ContextHash; std::vector<std::string> OutputPaths; const llvm::StringSet<> &AlreadySeen; }; - // We expect a single command here because if a source file occurs multiple - // times in the original CDB, then `computeDependencies` would run the - // `DependencyScanningAction` once for every time the input occured in the - // CDB. Instead we split up the CDB into single command chunks to avoid this - // behavior. - assert(Compilations.getAllCompileCommands().size() == 1 && - "Expected a compilation database with a single command!"); - std::string Input = Compilations.getAllCompileCommands().front().Filename; - FullDependencyPrinterConsumer Consumer(AlreadySeen); llvm::Error Result = - Worker.computeDependencies(Input, CWD, Compilations, Consumer); + Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); if (Result) return std::move(Result); return Consumer.getFullDependencies(); diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp index d651ff23b387..7fdc49271791 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp @@ -133,6 +133,16 @@ deduceDepTarget(const std::string &OutputFile, return makeObjFileName(InputFiles.front().getFile()); } +/// Sanitize diagnostic options for dependency scan. +static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) { + // Don't print 'X warnings and Y errors generated'. + DiagOpts.ShowCarets = false; + // Don't write out diagnostic file. + DiagOpts.DiagnosticSerializationFile.clear(); + // Don't treat warnings as errors. + DiagOpts.Warnings.push_back("no-error"); +} + /// A clang tool that runs the preprocessor in a mode that's optimized for /// dependency scanning for the given compiler invocation. class DependencyScanningAction : public tooling::ToolAction { @@ -141,10 +151,11 @@ public: StringRef WorkingDirectory, DependencyConsumer &Consumer, llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS, ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings, - ScanningOutputFormat Format) + ScanningOutputFormat Format, bool OptimizeArgs, + llvm::Optional<StringRef> ModuleName = None) : WorkingDirectory(WorkingDirectory), Consumer(Consumer), - DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings), - Format(Format) {} + DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings), Format(Format), + OptimizeArgs(OptimizeArgs), ModuleName(ModuleName) {} bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation, FileManager *FileMgr, @@ -154,39 +165,34 @@ public: CompilerInvocation OriginalInvocation(*Invocation); // Create a compiler instance to handle the actual work. - CompilerInstance Compiler(std::move(PCHContainerOps)); - Compiler.setInvocation(std::move(Invocation)); + CompilerInstance ScanInstance(std::move(PCHContainerOps)); + ScanInstance.setInvocation(std::move(Invocation)); - // Don't print 'X warnings and Y errors generated'. - Compiler.getDiagnosticOpts().ShowCarets = false; - // Don't write out diagnostic file. - Compiler.getDiagnosticOpts().DiagnosticSerializationFile.clear(); - // Don't treat warnings as errors. - Compiler.getDiagnosticOpts().Warnings.push_back("no-error"); // Create the compiler's actual diagnostics engine. - Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false); - if (!Compiler.hasDiagnostics()) + sanitizeDiagOpts(ScanInstance.getDiagnosticOpts()); + ScanInstance.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false); + if (!ScanInstance.hasDiagnostics()) return false; - Compiler.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath = true; + ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath = + true; FileMgr->getFileSystemOpts().WorkingDir = std::string(WorkingDirectory); - Compiler.setFileManager(FileMgr); - Compiler.createSourceManager(*FileMgr); + ScanInstance.setFileManager(FileMgr); + ScanInstance.createSourceManager(*FileMgr); llvm::StringSet<> PrebuiltModulesInputFiles; // Store the list of prebuilt module files into header search options. This // will prevent the implicit build to create duplicate modules and will // force reuse of the existing prebuilt module files instead. - if (!Compiler.getPreprocessorOpts().ImplicitPCHInclude.empty()) + if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty()) visitPrebuiltModule( - Compiler.getPreprocessorOpts().ImplicitPCHInclude, Compiler, - Compiler.getHeaderSearchOpts().PrebuiltModuleFiles, + ScanInstance.getPreprocessorOpts().ImplicitPCHInclude, ScanInstance, + ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles, PrebuiltModulesInputFiles, /*VisitInputFiles=*/DepFS != nullptr); // Use the dependency scanning optimized file system if requested to do so. if (DepFS) { - const CompilerInvocation &CI = Compiler.getInvocation(); DepFS->clearIgnoredFiles(); // Ignore any files that contributed to prebuilt modules. The implicit // build validates the modules by comparing the reported sizes of their @@ -197,20 +203,20 @@ public: // Add any filenames that were explicity passed in the build settings and // that might be opened, as we want to ensure we don't run source // minimization on them. - for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) - DepFS->ignoreFile(Entry.Path); - for (const auto &Entry : CI.getHeaderSearchOpts().VFSOverlayFiles) - DepFS->ignoreFile(Entry); + for (const auto &E : ScanInstance.getHeaderSearchOpts().UserEntries) + DepFS->ignoreFile(E.Path); + for (const auto &F : ScanInstance.getHeaderSearchOpts().VFSOverlayFiles) + DepFS->ignoreFile(F); // Support for virtual file system overlays on top of the caching // filesystem. FileMgr->setVirtualFileSystem(createVFSFromCompilerInvocation( - CI, Compiler.getDiagnostics(), DepFS)); + ScanInstance.getInvocation(), ScanInstance.getDiagnostics(), DepFS)); // Pass the skip mappings which should speed up excluded conditional block // skipping in the preprocessor. if (PPSkipMappings) - Compiler.getPreprocessorOpts() + ScanInstance.getPreprocessorOpts() .ExcludedConditionalDirectiveSkipMappings = PPSkipMappings; } @@ -222,35 +228,43 @@ public: // which ensures that the compiler won't create new dependency collectors, // and thus won't write out the extra '.d' files to disk. auto Opts = std::make_unique<DependencyOutputOptions>(); - std::swap(*Opts, Compiler.getInvocation().getDependencyOutputOpts()); + std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts()); // We need at least one -MT equivalent for the generator of make dependency // files to work. if (Opts->Targets.empty()) - Opts->Targets = {deduceDepTarget(Compiler.getFrontendOpts().OutputFile, - Compiler.getFrontendOpts().Inputs)}; + Opts->Targets = { + deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile, + ScanInstance.getFrontendOpts().Inputs)}; Opts->IncludeSystemHeaders = true; switch (Format) { case ScanningOutputFormat::Make: - Compiler.addDependencyCollector( + ScanInstance.addDependencyCollector( std::make_shared<DependencyConsumerForwarder>(std::move(Opts), Consumer)); break; case ScanningOutputFormat::Full: - Compiler.addDependencyCollector(std::make_shared<ModuleDepCollector>( - std::move(Opts), Compiler, Consumer, std::move(OriginalInvocation))); + ScanInstance.addDependencyCollector(std::make_shared<ModuleDepCollector>( + std::move(Opts), ScanInstance, Consumer, + std::move(OriginalInvocation), OptimizeArgs)); break; } // Consider different header search and diagnostic options to create // different modules. This avoids the unsound aliasing of module PCMs. // - // TODO: Implement diagnostic bucketing and header search pruning to reduce - // the impact of strict context hashing. - Compiler.getHeaderSearchOpts().ModulesStrictContextHash = true; + // TODO: Implement diagnostic bucketing to reduce the impact of strict + // context hashing. + ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true; + + std::unique_ptr<FrontendAction> Action; + + if (ModuleName.hasValue()) + Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName); + else + Action = std::make_unique<ReadPCHAndPreprocessAction>(); - auto Action = std::make_unique<ReadPCHAndPreprocessAction>(); - const bool Result = Compiler.ExecuteAction(*Action); + const bool Result = ScanInstance.ExecuteAction(*Action); if (!DepFS) FileMgr->clearStatCache(); return Result; @@ -262,15 +276,15 @@ private: llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS; ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings; ScanningOutputFormat Format; + bool OptimizeArgs; + llvm::Optional<StringRef> ModuleName; }; } // end anonymous namespace DependencyScanningWorker::DependencyScanningWorker( DependencyScanningService &Service) - : Format(Service.getFormat()) { - DiagOpts = new DiagnosticOptions(); - + : Format(Service.getFormat()), OptimizeArgs(Service.canOptimizeArgs()) { PCHContainerOps = std::make_shared<PCHContainerOperations>(); PCHContainerOps->registerReader( std::make_unique<ObjectFilePCHContainerReader>()); @@ -279,7 +293,12 @@ DependencyScanningWorker::DependencyScanningWorker( PCHContainerOps->registerWriter( std::make_unique<ObjectFilePCHContainerWriter>()); - RealFS = llvm::vfs::createPhysicalFileSystem(); + auto OverlayFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>( + llvm::vfs::createPhysicalFileSystem()); + InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); + OverlayFS->pushOverlay(InMemoryFS); + RealFS = OverlayFS; + if (Service.canSkipExcludedPPRanges()) PPSkipMappings = std::make_unique<ExcludedPreprocessorDirectiveSkipMapping>(); @@ -290,36 +309,64 @@ DependencyScanningWorker::DependencyScanningWorker( Files = new FileManager(FileSystemOptions(), RealFS); } -static llvm::Error runWithDiags( - DiagnosticOptions *DiagOpts, - llvm::function_ref<bool(DiagnosticConsumer &DC)> BodyShouldSucceed) { +static llvm::Error +runWithDiags(DiagnosticOptions *DiagOpts, + llvm::function_ref<bool(DiagnosticConsumer &, DiagnosticOptions &)> + BodyShouldSucceed) { + sanitizeDiagOpts(*DiagOpts); + // Capture the emitted diagnostics and report them to the client // in the case of a failure. std::string DiagnosticOutput; llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput); TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts); - if (BodyShouldSucceed(DiagPrinter)) + if (BodyShouldSucceed(DiagPrinter, *DiagOpts)) return llvm::Error::success(); return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(), llvm::inconvertibleErrorCode()); } llvm::Error DependencyScanningWorker::computeDependencies( - const std::string &Input, StringRef WorkingDirectory, - const CompilationDatabase &CDB, DependencyConsumer &Consumer) { + StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, + DependencyConsumer &Consumer, llvm::Optional<StringRef> ModuleName) { + // Reset what might have been modified in the previous worker invocation. RealFS->setCurrentWorkingDirectory(WorkingDirectory); - return runWithDiags(DiagOpts.get(), [&](DiagnosticConsumer &DC) { - /// Create the tool that uses the underlying file system to ensure that any - /// file system requests that are made by the driver do not go through the - /// dependency scanning filesystem. - tooling::ClangTool Tool(CDB, Input, PCHContainerOps, RealFS, Files); - Tool.clearArgumentsAdjusters(); - Tool.setRestoreWorkingDir(false); - Tool.setPrintErrorMessage(false); - Tool.setDiagnosticConsumer(&DC); - DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS, - PPSkipMappings.get(), Format); - return !Tool.run(&Action); - }); + if (Files) + Files->setVirtualFileSystem(RealFS); + + llvm::IntrusiveRefCntPtr<FileManager> CurrentFiles = + Files ? Files : new FileManager(FileSystemOptions(), RealFS); + + Optional<std::vector<std::string>> ModifiedCommandLine; + if (ModuleName.hasValue()) { + ModifiedCommandLine = CommandLine; + InMemoryFS->addFile(*ModuleName, 0, llvm::MemoryBuffer::getMemBuffer("")); + ModifiedCommandLine->emplace_back(*ModuleName); + } + + const std::vector<std::string> &FinalCommandLine = + ModifiedCommandLine ? *ModifiedCommandLine : CommandLine; + + std::vector<const char *> FinalCCommandLine(CommandLine.size(), nullptr); + llvm::transform(CommandLine, FinalCCommandLine.begin(), + [](const std::string &Str) { return Str.c_str(); }); + + return runWithDiags(CreateAndPopulateDiagOpts(FinalCCommandLine).release(), + [&](DiagnosticConsumer &DC, DiagnosticOptions &DiagOpts) { + DependencyScanningAction Action( + WorkingDirectory, Consumer, DepFS, + PPSkipMappings.get(), Format, OptimizeArgs, + ModuleName); + // Create an invocation that uses the underlying file + // system to ensure that any file system requests that + // are made by the driver do not go through the + // dependency scanning filesystem. + ToolInvocation Invocation(FinalCommandLine, &Action, + CurrentFiles.get(), + PCHContainerOps); + Invocation.setDiagnosticConsumer(&DC); + Invocation.setDiagnosticOptions(&DiagOpts); + return Invocation.run(); + }); } diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index 88cee63c98aa..383a850301a1 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -1,9 +1,8 @@ //===- ModuleDepCollector.cpp - Callbacks to collect deps -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -18,11 +17,26 @@ using namespace clang; using namespace tooling; using namespace dependencies; +static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, + ASTReader &Reader, + const serialization::ModuleFile &MF) { + // Only preserve search paths that were used during the dependency scan. + std::vector<HeaderSearchOptions::Entry> Entries = Opts.UserEntries; + Opts.UserEntries.clear(); + for (unsigned I = 0; I < Entries.size(); ++I) + if (MF.SearchPathUsage[I]) + Opts.UserEntries.push_back(Entries[I]); +} + CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths( - const ModuleDeps &Deps) const { + const ModuleDeps &Deps, + llvm::function_ref<void(CompilerInvocation &)> Optimize) const { // Make a deep copy of the original Clang invocation. CompilerInvocation CI(OriginalInvocation); + CI.getLangOpts()->resetNonModularOptions(); + CI.getPreprocessorOpts().resetNonModularOptions(); + // Remove options incompatible with explicit module build. CI.getFrontendOpts().Inputs.clear(); CI.getFrontendOpts().OutputFile.clear(); @@ -34,12 +48,18 @@ CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths( CI.getLangOpts()->ImplicitModules = false; // Report the prebuilt modules this module uses. - for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps) { + for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps) CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile); - CI.getFrontendOpts().ModuleMapFiles.push_back(PrebuiltModule.ModuleMapFile); - } - CI.getPreprocessorOpts().ImplicitPCHInclude.clear(); + Optimize(CI); + + // The original invocation probably didn't have strict context hash enabled. + // We will use the context hash of this invocation to distinguish between + // multiple incompatible versions of the same module and will use it when + // reporting dependencies to the clients. Let's make sure we're using + // **strict** context hash in order to prevent accidental sharing of + // incompatible modules (e.g. with differences in search paths). + CI.getHeaderSearchOpts().ModulesStrictContextHash = true; return CI; } @@ -62,7 +82,7 @@ serializeCompilerInvocation(const CompilerInvocation &CI) { std::vector<std::string> ModuleDeps::getCanonicalCommandLine( std::function<StringRef(ModuleID)> LookupPCMPath, std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps) const { - CompilerInvocation CI(Invocation); + CompilerInvocation CI(BuildInvocation); FrontendOptions &FrontendOpts = CI.getFrontendOpts(); InputKind ModuleMapInputKind(FrontendOpts.DashX.getLanguage(), @@ -79,7 +99,7 @@ std::vector<std::string> ModuleDeps::getCanonicalCommandLine( std::vector<std::string> ModuleDeps::getCanonicalCommandLineWithoutModulePaths() const { - return serializeCompilerInvocation(Invocation); + return serializeCompilerInvocation(BuildInvocation); } void dependencies::detail::collectPCMAndModuleMapPaths( @@ -112,15 +132,15 @@ void ModuleDepCollectorPP::FileChanged(SourceLocation Loc, FileID PrevFID) { if (Reason != PPCallbacks::EnterFile) return; - + // This has to be delayed as the context hash can change at the start of // `CompilerInstance::ExecuteAction`. if (MDC.ContextHash.empty()) { - MDC.ContextHash = Instance.getInvocation().getModuleHash(); + MDC.ContextHash = MDC.ScanInstance.getInvocation().getModuleHash(); MDC.Consumer.handleContextHash(MDC.ContextHash); } - SourceManager &SM = Instance.getSourceManager(); + SourceManager &SM = MDC.ScanInstance.getSourceManager(); // Dependency generation really does want to go all the way to the // file entry for a source location to find out what is depended on. @@ -163,12 +183,14 @@ void ModuleDepCollectorPP::handleImport(const Module *Imported) { } void ModuleDepCollectorPP::EndOfMainFile() { - FileID MainFileID = Instance.getSourceManager().getMainFileID(); - MDC.MainFile = std::string( - Instance.getSourceManager().getFileEntryForID(MainFileID)->getName()); + FileID MainFileID = MDC.ScanInstance.getSourceManager().getMainFileID(); + MDC.MainFile = std::string(MDC.ScanInstance.getSourceManager() + .getFileEntryForID(MainFileID) + ->getName()); - if (!Instance.getPreprocessorOpts().ImplicitPCHInclude.empty()) - MDC.FileDeps.push_back(Instance.getPreprocessorOpts().ImplicitPCHInclude); + if (!MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty()) + MDC.FileDeps.push_back( + MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude); for (const Module *M : DirectModularDeps) { // A top-level module might not be actually imported as a module when @@ -207,15 +229,16 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { MD.ImplicitModulePCMPath = std::string(M->getASTFile()->getName()); MD.IsSystem = M->IsSystem; - const FileEntry *ModuleMap = Instance.getPreprocessor() + const FileEntry *ModuleMap = MDC.ScanInstance.getPreprocessor() .getHeaderSearchInfo() .getModuleMap() .getModuleMapFileForUniquing(M); MD.ClangModuleMapFile = std::string(ModuleMap ? ModuleMap->getName() : ""); serialization::ModuleFile *MF = - MDC.Instance.getASTReader()->getModuleManager().lookup(M->getASTFile()); - MDC.Instance.getASTReader()->visitInputFiles( + MDC.ScanInstance.getASTReader()->getModuleManager().lookup( + M->getASTFile()); + MDC.ScanInstance.getASTReader()->visitInputFiles( *MF, true, true, [&](const serialization::InputFile &IF, bool isSystem) { // __inferred_module.map is the result of the way in which an implicit // module build handles inferred modules. It adds an overlay VFS with @@ -232,10 +255,16 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { // Add direct prebuilt module dependencies now, so that we can use them when // creating a CompilerInvocation and computing context hash for this // ModuleDeps instance. - addDirectPrebuiltModuleDeps(M, MD); + llvm::DenseSet<const Module *> SeenModules; + addAllSubmodulePrebuiltDeps(M, MD, SeenModules); - MD.Invocation = MDC.makeInvocationForModuleBuildWithoutPaths(MD); - MD.ID.ContextHash = MD.Invocation.getModuleHash(); + MD.BuildInvocation = MDC.makeInvocationForModuleBuildWithoutPaths( + MD, [&](CompilerInvocation &BuildInvocation) { + if (MDC.OptimizeArgs) + optimizeHeaderSearchOpts(BuildInvocation.getHeaderSearchOpts(), + *MDC.ScanInstance.getASTReader(), *MF); + }); + MD.ID.ContextHash = MD.BuildInvocation.getModuleHash(); llvm::DenseSet<const Module *> AddedModules; addAllSubmoduleDeps(M, MD, AddedModules); @@ -243,12 +272,23 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { return MD.ID; } -void ModuleDepCollectorPP::addDirectPrebuiltModuleDeps(const Module *M, - ModuleDeps &MD) { +void ModuleDepCollectorPP::addAllSubmodulePrebuiltDeps( + const Module *M, ModuleDeps &MD, + llvm::DenseSet<const Module *> &SeenSubmodules) { + addModulePrebuiltDeps(M, MD, SeenSubmodules); + + for (const Module *SubM : M->submodules()) + addAllSubmodulePrebuiltDeps(SubM, MD, SeenSubmodules); +} + +void ModuleDepCollectorPP::addModulePrebuiltDeps( + const Module *M, ModuleDeps &MD, + llvm::DenseSet<const Module *> &SeenSubmodules) { for (const Module *Import : M->Imports) if (Import->getTopLevelModule() != M->getTopLevelModule()) - if (MDC.isPrebuiltModule(Import)) - MD.PrebuiltModuleDeps.emplace_back(Import); + if (MDC.isPrebuiltModule(Import->getTopLevelModule())) + if (SeenSubmodules.insert(Import->getTopLevelModule()).second) + MD.PrebuiltModuleDeps.emplace_back(Import->getTopLevelModule()); } void ModuleDepCollectorPP::addAllSubmoduleDeps( @@ -274,13 +314,14 @@ void ModuleDepCollectorPP::addModuleDep( } ModuleDepCollector::ModuleDepCollector( - std::unique_ptr<DependencyOutputOptions> Opts, CompilerInstance &I, - DependencyConsumer &C, CompilerInvocation &&OriginalCI) - : Instance(I), Consumer(C), Opts(std::move(Opts)), - OriginalInvocation(std::move(OriginalCI)) {} + std::unique_ptr<DependencyOutputOptions> Opts, + CompilerInstance &ScanInstance, DependencyConsumer &C, + CompilerInvocation &&OriginalCI, bool OptimizeArgs) + : ScanInstance(ScanInstance), Consumer(C), Opts(std::move(Opts)), + OriginalInvocation(std::move(OriginalCI)), OptimizeArgs(OptimizeArgs) {} void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) { - PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(Instance, *this)); + PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(*this)); } void ModuleDepCollector::attachToASTReader(ASTReader &R) {} @@ -288,7 +329,7 @@ void ModuleDepCollector::attachToASTReader(ASTReader &R) {} bool ModuleDepCollector::isPrebuiltModule(const Module *M) { std::string Name(M->getTopLevelModuleName()); const auto &PrebuiltModuleFiles = - Instance.getHeaderSearchOpts().PrebuiltModuleFiles; + ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles; auto PrebuiltModuleFileIt = PrebuiltModuleFiles.find(Name); if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end()) return false; |
