diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h --- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h +++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h @@ -95,7 +95,7 @@ llvm::StringSet<> FileDeps; /// A collection of absolute paths to module map files that this module needs - /// to know about. + /// to know about. The ordering is significant. std::vector ModuleMapFileDeps; /// A collection of prebuilt modular dependencies this module directly depends @@ -236,6 +236,10 @@ const ModuleDeps &Deps, llvm::function_ref Optimize) const; + /// Collect module map files for given modules. + llvm::StringSet<> + collectModuleMapFiles(ArrayRef ClangModuleDeps) const; + /// Add module map files to the invocation, if needed. void addModuleMapFiles(CompilerInvocation &CI, ArrayRef ClangModuleDeps) const; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -108,7 +108,6 @@ #include #include #include -#include #include #include #include @@ -1528,9 +1527,9 @@ IFHAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); unsigned IFHAbbrevCode = Stream.EmitAbbrev(std::move(IFHAbbrev)); - // Get all ContentCache objects for files, sorted by whether the file is a - // system one or not. System files go at the back, users files at the front. - std::deque SortedFiles; + // Get all ContentCache objects for files. + std::vector UserFiles; + std::vector SystemFiles; for (unsigned I = 1, N = SourceMgr.local_sloc_entry_size(); I != N; ++I) { // Get this source location entry. const SrcMgr::SLocEntry *SLoc = &SourceMgr.getLocalSLocEntry(I); @@ -1581,11 +1580,15 @@ static_cast(CH.getHiBits(32).getZExtValue()); if (Entry.IsSystemFile) - SortedFiles.push_back(Entry); + SystemFiles.push_back(Entry); else - SortedFiles.push_front(Entry); + UserFiles.push_back(Entry); } + // User files go at the front, system files at the back. + auto SortedFiles = llvm::concat(std::move(UserFiles), + std::move(SystemFiles)); + unsigned UserFilesNum = 0; // Write out all of the input files. std::vector InputFileOffsets; diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -116,8 +116,33 @@ InputKind::Format::ModuleMap); CI.getFrontendOpts().Inputs.emplace_back(Deps.ClangModuleMapFile, ModuleMapInputKind); - CI.getFrontendOpts().ModuleMapFiles = Deps.ModuleMapFileDeps; - addModuleMapFiles(CI, Deps.ClangModuleDeps); + + auto CurrentModuleMapEntry = + ScanInstance.getFileManager().getFile(Deps.ClangModuleMapFile); + assert(CurrentModuleMapEntry && "module map file entry not found"); + + auto DepModuleMapFiles = collectModuleMapFiles(Deps.ClangModuleDeps); + for (StringRef ModuleMapFile : Deps.ModuleMapFileDeps) { + // Don't report module maps describing eagerly-loaded dependency. This + // information will be deserialized from the PCM. + // TODO: Verify this works fine when modulemap for module A is eagerly + // loaded from A.pcm, and module map passed on the command line contains + // definition of a submodule: "explicit module A.Private { ... }". + if (EagerLoadModules && DepModuleMapFiles.contains(ModuleMapFile)) + continue; + + // TODO: Track these as `FileEntryRef` to simplify the equality check below. + auto ModuleMapEntry = ScanInstance.getFileManager().getFile(ModuleMapFile); + assert(ModuleMapEntry && "module map file entry not found"); + + // Don't report module map file of the current module unless it also + // describes a dependency (for symmetry). + if (*ModuleMapEntry == *CurrentModuleMapEntry && + !DepModuleMapFiles.contains(ModuleMapFile)) + continue; + + CI.getFrontendOpts().ModuleMapFiles.emplace_back(ModuleMapFile); + } // Report the prebuilt modules this module uses. for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps) @@ -149,6 +174,17 @@ return CI; } +llvm::StringSet<> ModuleDepCollector::collectModuleMapFiles( + ArrayRef ClangModuleDeps) const { + llvm::StringSet<> ModuleMapFiles; + for (const ModuleID &MID : ClangModuleDeps) { + ModuleDeps *MD = ModuleDepsByID.lookup(MID); + assert(MD && "Inconsistent dependency info"); + ModuleMapFiles.insert(MD->ClangModuleMapFile); + } + return ModuleMapFiles; +} + void ModuleDepCollector::addModuleMapFiles( CompilerInvocation &CI, ArrayRef ClangModuleDeps) const { if (EagerLoadModules) @@ -203,7 +239,9 @@ if (KV.second->ImportedByMainFile) DirectDeps.push_back(KV.second->ID); + // TODO: Report module maps the same way it's done for modular dependencies. addModuleMapFiles(CI, DirectDeps); + addModuleFiles(CI, DirectDeps); for (const auto &KV : DirectPrebuiltModularDeps) @@ -402,27 +440,10 @@ addAllSubmoduleDeps(M, MD, SeenDeps); addAllAffectingModules(M, MD, SeenDeps); - llvm::DenseSet SeenModuleMaps; - for (const Module *SM : SeenDeps) - if (const FileEntry *SMM = MDC.ScanInstance.getPreprocessor() - .getHeaderSearchInfo() - .getModuleMap() - .getModuleMapFileForUniquing(SM)) - SeenModuleMaps.insert(SMM); - MDC.ScanInstance.getASTReader()->visitTopLevelModuleMaps( *MF, [&](const FileEntry *FE) { if (FE->getName().endswith("__inferred_module.map")) return; - // The top-level modulemap of this module will be the input file. We - // don't need to specify it via "-fmodule-map-file=". - if (FE == ModuleMap) - return; - // The top-level modulemap of dependencies will be serialized in the PCM - // file (eager loading mode) or passed on the command-line through a - // different mechanism (lazy loading mode). - if (SeenModuleMaps.contains(FE)) - return; MD.ModuleMapFileDeps.emplace_back(FE->getName()); }); diff --git a/clang/test/ClangScanDeps/modules-module-map-order.m b/clang/test/ClangScanDeps/modules-module-map-order.m new file mode 100644 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-module-map-order.m @@ -0,0 +1,55 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +//--- this/module.modulemap +module This { header "This.h" } +//--- this/This.h +#include "Foo.h" +#include "Foo_Private_Excluded.h" + +//--- modules/module.modulemap +module Foo { header "Foo.h" } +//--- modules/module.private.modulemap +explicit module Foo.Private { + header "Foo_Private.h" + exclude header "Foo_Private_Excluded.h" +} +//--- modules/Foo.h +//--- modules/Foo_Private.h +//--- modules/Foo_Private_Excluded.h + +//--- cdb.json.template +[{ + "file": "DIR/tu.m", + "directory": "DIR", + "command": "clang -fmodules -fmodules-cache-path=DIR/cache -I DIR/this -I DIR/modules -c DIR/tu.m -o DIR/tu.o" +}] + +//--- tu.m +@import This; + +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full > %t/result.json + +// RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t +// CHECK: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK: }, +// CHECK-NEXT: { +// CHECK: "command-line": [ +// CHECK: "-fmodule-map-file=[[PREFIX]]/modules/module.modulemap", +// CHECK-NEXT: "-fmodule-map-file=[[PREFIX]]/modules/module.private.modulemap", +// CHECK: ], +// CHECK: "name": "This" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK: } + +// RUN: %deps-to-rsp %t/result.json --module-name=Foo > %t/Foo.cc1.rsp +// RUN: %deps-to-rsp %t/result.json --module-name=This > %t/This.cc1.rsp +// RUN: %deps-to-rsp %t/result.json --tu-index=0 > %t/tu.rsp + +// RUN: %clang @%t/Foo.cc1.rsp +// RUN: %clang @%t/This.cc1.rsp +// RUN: %clang @%t/tu.rsp diff --git a/clang/test/Index/Core/index-with-module.m b/clang/test/Index/Core/index-with-module.m --- a/clang/test/Index/Core/index-with-module.m +++ b/clang/test/Index/Core/index-with-module.m @@ -23,5 +23,5 @@ // CHECK-DAG: 2:6 | function/C | ModA_func | c:@F@ModA_func | {{.*}} | Decl | rel: 0 // CHECK-DAG: 2:6 | function/C | SubModA_func | c:@F@SubModA_func | {{.*}} | Decl | rel: 0 // CHECK: ---- Module Inputs ---- -// CHECK: user | {{.*}}ModA.h // CHECK: user | {{.*}}module.modulemap +// CHECK: user | {{.*}}ModA.h diff --git a/clang/test/Modules/module-file-home-is-cwd.m b/clang/test/Modules/module-file-home-is-cwd.m --- a/clang/test/Modules/module-file-home-is-cwd.m +++ b/clang/test/Modules/module-file-home-is-cwd.m @@ -2,7 +2,7 @@ // RUN: %clang_cc1 -fmodules -fno-implicit-modules -fmodule-file-home-is-cwd -fmodule-name=libA -emit-module Inputs/normal-module-map/module.map -o %t/mod.pcm // RUN: llvm-bcanalyzer --dump --disable-histogram %t/mod.pcm | FileCheck %s -// CHECK: blob data = 'Inputs{{/|\\}}normal-module-map{{/|\\}}a1.h' -// CHECK: blob data = 'Inputs{{/|\\}}normal-module-map{{/|\\}}a2.h' // CHECK: blob data = 'Inputs{{/|\\}}normal-module-map{{/|\\}}module.map' +// CHECK: blob data = 'Inputs{{/|\\}}normal-module-map{{/|\\}}a2.h' +// CHECK: blob data = 'Inputs{{/|\\}}normal-module-map{{/|\\}}a1.h' // CHECK-NOT: MODULE_DIRECTORY diff --git a/clang/test/Modules/module_file_info.m b/clang/test/Modules/module_file_info.m --- a/clang/test/Modules/module_file_info.m +++ b/clang/test/Modules/module_file_info.m @@ -45,16 +45,16 @@ // CHECK: Predefined macros: // CHECK: -DBLARG // CHECK: -DWIBBLE=WOBBLE -// CHECK: Input file: {{.*}}DependsOnModulePrivate.h -// CHECK-NEXT: Input file: {{.*}}Other.h -// CHECK-NEXT: Input file: {{.*}}SubFramework.h -// CHECK-NEXT: Input file: {{.*}}not_coroutines.h -// CHECK-NEXT: Input file: {{.*}}not_cxx.h -// CHECK-NEXT: Input file: {{.*}}other.h -// CHECK-NEXT: Input file: {{.*}}module.map -// CHECK-NEXT: Input file: {{.*}}DependsOnModule.h +// CHECK: Input file: {{.*}}module.map // CHECK-NEXT: Input file: {{.*}}module_private.map +// CHECK-NEXT: Input file: {{.*}}DependsOnModule.h // CHECK-NEXT: Input file: {{.*}}module.map +// CHECK-NEXT: Input file: {{.*}}other.h +// CHECK-NEXT: Input file: {{.*}}not_cxx.h +// CHECK-NEXT: Input file: {{.*}}not_coroutines.h +// CHECK-NEXT: Input file: {{.*}}SubFramework.h +// CHECK-NEXT: Input file: {{.*}}Other.h +// CHECK-NEXT: Input file: {{.*}}DependsOnModulePrivate.h // CHECK: Diagnostic options: // CHECK: IgnoreWarnings: Yes