diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h index cb5d7e36d21c9..ee24e5d1543d3 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h @@ -15,6 +15,7 @@ #include "clang/Tooling/JSONCompilationDatabase.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/MapVector.h" +#include "llvm/ADT/STLExtras.h" #include #include #include @@ -56,6 +57,9 @@ struct TranslationUnitDeps { /// determined that the differences are benign for this compilation. std::vector ClangModuleDeps; + /// A list of the C++20 named modules this translation unit depends on. + std::vector NamedModuleDeps; + /// The sequence of commands required to build the translation unit. Commands /// should be executed in order. /// @@ -188,6 +192,14 @@ class FullDependencyConsumer : public DependencyConsumer { ContextHash = std::move(Hash); } + void handleProvidedAndRequiredStdCXXModules( + std::optional Provided, + std::vector Requires) override { + ModuleName = Provided ? Provided->ModuleName : ""; + llvm::transform(Requires, std::back_inserter(NamedModuleDeps), + [](const auto &Module) { return Module.ModuleName; }); + } + TranslationUnitDeps takeTranslationUnitDeps(); ModuleDepsGraph takeModuleGraphDeps(); @@ -195,6 +207,8 @@ class FullDependencyConsumer : public DependencyConsumer { std::vector Dependencies; std::vector PrebuiltModuleDeps; llvm::MapVector ClangModuleDeps; + std::string ModuleName; + std::vector NamedModuleDeps; std::vector DirectModuleDeps; std::vector Commands; std::string ContextHash; diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp index b015e79f400cf..515211d47b348 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp @@ -171,6 +171,8 @@ TranslationUnitDeps FullDependencyConsumer::takeTranslationUnitDeps() { TranslationUnitDeps TU; TU.ID.ContextHash = std::move(ContextHash); + TU.ID.ModuleName = std::move(ModuleName); + TU.NamedModuleDeps = std::move(NamedModuleDeps); TU.FileDeps = std::move(Dependencies); TU.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); TU.Commands = std::move(Commands); diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index b1495163ccc24..fa86d714ff69a 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -714,9 +714,8 @@ void ModuleDepCollectorPP::EndOfMainFile() { MDC.Consumer.handleDependencyOutputOpts(*MDC.Opts); - if (MDC.Service.getFormat() == ScanningOutputFormat::P1689) - MDC.Consumer.handleProvidedAndRequiredStdCXXModules( - MDC.ProvidedStdCXXModule, MDC.RequiredStdCXXModules); + MDC.Consumer.handleProvidedAndRequiredStdCXXModules( + MDC.ProvidedStdCXXModule, MDC.RequiredStdCXXModules); for (auto &&I : MDC.ModularDeps) MDC.Consumer.handleModuleDependency(*I.second); diff --git a/clang/test/ClangScanDeps/modules-full-named-modules.cppm b/clang/test/ClangScanDeps/modules-full-named-modules.cppm new file mode 100644 index 0000000000000..5967a8705c09d --- /dev/null +++ b/clang/test/ClangScanDeps/modules-full-named-modules.cppm @@ -0,0 +1,351 @@ +// This checks that clang-scan-deps properly outputs named module dependencies +// when using the the scanning output format 'experimental-full'. +// +// See PR #72304. +// UNSUPPORTED: target={{.*}}-aix{{.*}} +// +// RUN: rm -fr %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// Check the separated dependency format. +// RUN: sed "s|DIR|%/t|g" %t/compile_commands.json.in > %t/compile_commands.json +// RUN: clang-scan-deps -format=experimental-full \ +// RUN: -- %clang++ -std=c++20 -c -fprebuilt-module-path=%t %t/M.cppm -o %t/M.o \ +// RUN: | sed 's:\\\\\?:/:g' \ +// RUN: | FileCheck %t/M.cppm -DPREFIX=%/t +// RUN: clang-scan-deps -format=experimental-full \ +// RUN: -- %clang++ -std=c++20 -c -fprebuilt-module-path=%t %t/Impl.cpp -o %t/Impl.o \ +// RUN: | sed 's:\\\\\?:/:g' \ +// RUN: | FileCheck %t/Impl.cpp -DPREFIX=%/t +// RUN: clang-scan-deps -format=experimental-full \ +// RUN: -- %clang++ -std=c++20 -c -fprebuilt-module-path=%t %t/impl_part.cppm -o %t/impl_part.o \ +// RUN: | sed 's:\\\\\?:/:g' \ +// RUN: | FileCheck %t/impl_part.cppm -DPREFIX=%/t +// RUN: clang-scan-deps -format=experimental-full \ +// RUN: -- %clang++ -std=c++20 -c -fprebuilt-module-path=%t %t/interface_part.cppm -o %t/interface_part.o \ +// RUN: | sed 's:\\\\\?:/:g' \ +// RUN: | FileCheck %t/interface_part.cppm -DPREFIX=%/t +// RUN: clang-scan-deps -format=experimental-full \ +// RUN: -- %clang++ -std=c++20 -c -fprebuilt-module-path=%t %t/User.cpp -o %t/User.o \ +// RUN: | sed 's:\\\\\?:/:g' \ +// RUN: | FileCheck %t/User.cpp -DPREFIX=%/t +// +// Check the combined dependency format. +// RUN: clang-scan-deps -compilation-database %t/compile_commands.json -format=experimental-full \ +// RUN: | sed 's:\\\\\?:/:g' \ +// RUN: | FileCheck %t/Checks.cpp -DPREFIX=%/t +// RUN: clang-scan-deps --mode=preprocess-dependency-directives -compilation-database %t/compile_commands.json -format=experimental-full \ +// RUN: | sed 's:\\\\\?:/:g' \ +// RUN: | FileCheck %t/Checks.cpp -DPREFIX=%/t + +//--- compile_commands.json.in +[ +{ + "directory": "DIR", + "command": "clang++ -std=c++20 DIR/M.cppm -c -o DIR/M.o -MT DIR/M.o.ddi -MD -MF DIR/P1689.dep", + "file": "DIR/M.cppm", + "output": "DIR/M.o" +}, +{ + "directory": "DIR", + "command": "clang++ -std=c++20 DIR/Impl.cpp -c -o DIR/Impl.o -MT DIR/Impl.o.ddi -MD -MF DIR/P1689.dep", + "file": "DIR/Impl.cpp", + "output": "DIR/Impl.o" +}, +{ + "directory": "DIR", + "command": "clang++ -std=c++20 DIR/impl_part.cppm -c -o DIR/impl_part.o -MT DIR/impl_part.o.ddi -MD -MF DIR/P1689.dep", + "file": "DIR/impl_part.cppm", + "output": "DIR/impl_part.o" +}, +{ + "directory": "DIR", + "command": "clang++ -std=c++20 DIR/interface_part.cppm -c -o DIR/interface_part.o -MT DIR/interface_part.o.ddi -MD -MF DIR/P1689.dep", + "file": "DIR/interface_part.cppm", + "output": "DIR/interface_part.o" +}, +{ + "directory": "DIR", + "command": "clang++ -std=c++20 DIR/User.cpp -c -o DIR/User.o -MT DIR/User.o.ddi -MD -MF DIR/P1689.dep", + "file": "DIR/User.cpp", + "output": "DIR/User.o" +} +] + +//--- M.cppm +export module M; +export import :interface_part; +import :impl_part; +export void Hello(); + +// CHECK: { +// CHECK: "modules": [] +// CHECK: "translation-units": [ +// CHECK-NEXT: { +// CHECK: "commands": [ +// CHECK-NEXT: { +// CHECK: "named-module": "M" +// CHECK-NEXT: "named-module-deps": [ +// CHECK-NEXT: "M:interface_part", +// CHECK-NEXT: "M:impl_part" +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "{{.*}}/M-{{.*}}.pcm" +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/M.cppm" +// CHECK: }, +// CHECK-NEXT: { +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "[[PREFIX]]/M.o" +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/M.cppm" +// CHECK: } +// CHECK: ] +// CHECK: } +// CHECK: ] +// CHECK: } + +//--- Impl.cpp +module; +#include "header.mock" +module M; +void Hello() { + std::cout << "Hello "; +} + +// CHECK: { +// CHECK: "modules": [] +// CHECK: "translation-units": [ +// CHECK-NEXT: { +// CHECK: "commands": [ +// CHECK-NEXT: { +// CHECK-NOT: "named-module": +// CHECK: "named-module-deps": [ +// CHECK-NEXT: "M" +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "[[PREFIX]]/Impl.o" +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/Impl.cpp" +// CHECK: } +// CHECK: ] +// CHECK: } +// CHECK: ] +// CHECK: } + +//--- impl_part.cppm +module; +#include "header.mock" +module M:impl_part; +import :interface_part; + +std::string W = "World."; +void World() { + std::cout << W << std::endl; +} + +// CHECK: { +// CHECK: "modules": [], +// CHECK: "translation-units": [ +// CHECK-NEXT: { +// CHECK: "commands": [ +// CHECK-NEXT: { +// CHECK: "named-module": "M:impl_part" +// CHECK-NEXT: "named-module-deps": [ +// CHECK-NEXT: "M:interface_part" +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "{{.*}}/impl_part-{{.*}}.pcm", +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/impl_part.cppm" +// CHECK: }, +// CHECK-NEXT: { +// CHECK: "named-module": "M:impl_part" +// CHECK-NEXT: "named-module-deps": [ +// CHECK-NEXT: "M:interface_part" +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "[[PREFIX]]/impl_part.o", +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/impl_part.cppm" +// CHECK: } +// CHECK: ] +// CHECK: } +// CHECK: ] +// CHECK: } + +//--- interface_part.cppm +export module M:interface_part; +export void World(); + +// CHECK: { +// CHECK: "modules": [] +// CHECK: "translation-units": [ +// CHECK-NEXT: { +// CHECK: "commands": [ +// CHECK-NEXT: { +// CHECK: "named-module": "M:interface_part" +// CHECK-NOT: "named-module-deps": [] +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "{{.*}}/interface_part-{{.*}}.pcm", +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/interface_part.cppm" +// CHECK: }, +// CHECK-NEXT: { +// CHECK: "named-module": "M:interface_part" +// CHECK-NOT: "named-module-deps": [] +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "[[PREFIX]]/interface_part.o", +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/interface_part.cppm" +// CHECK: } +// CHECK: ] +// CHECK: } +// CHECK: ] +// CHECK: } + +//--- User.cpp +import M; +import third_party_module; +int main() { + Hello(); + World(); + return 0; +} + +// CHECK: { +// CHECK-NEXT: "modules": [] +// CHECK-NEXT: "translation-units": [ +// CHECK-NEXT: { +// CHECK-NEXT: "commands": [ +// CHECK-NEXT: { +// CHECK-NOT: "named-module": +// CHECK: "named-module-deps": [ +// CHECK-NEXT: "M" +// CHECK-NEXT: "third_party_module" +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "[[PREFIX]]/User.o", +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/User.cpp +// CHECK: } +// CHECK: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK: } + +//--- header.mock + +//--- Checks.cpp +// CHECK: { +// CHECK: "modules": [] +// CHECK: "translation-units": [ +// CHECK-NEXT: { +// CHECK: "commands": [ +// CHECK-NEXT: { +// CHECK: "named-module": "M" +// CHECK-NEXT: "named-module-deps": [ +// CHECK-NEXT: "M:interface_part", +// CHECK-NEXT: "M:impl_part" +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "{{.*}}/M-{{.*}}.pcm" +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/M.cppm" +// CHECK: }, +// CHECK-NEXT: { +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "[[PREFIX]]/M.o" +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/M.cppm" +// CHECK: }, +// CHECK: { +// CHECK-NOT: "named-module": +// CHECK: "named-module-deps": [ +// CHECK-NEXT: "M" +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "[[PREFIX]]/Impl.o" +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/Impl.cpp" +// CHECK: } +// CHECK: ] +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK: "commands": [ +// CHECK-NEXT: { +// CHECK: "named-module": "M:impl_part" +// CHECK-NEXT: "named-module-deps": [ +// CHECK-NEXT: "M:interface_part" +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "{{.*}}/impl_part-{{.*}}.pcm", +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/impl_part.cppm" +// CHECK: }, +// CHECK-NEXT: { +// CHECK: "named-module": "M:impl_part" +// CHECK-NEXT: "named-module-deps": [ +// CHECK-NEXT: "M:interface_part" +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "[[PREFIX]]/impl_part.o", +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/impl_part.cppm" +// CHECK: } +// CHECK: ] +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK: "commands": [ +// CHECK-NEXT: { +// CHECK: "named-module": "M:interface_part" +// CHECK-NOT: "named-module-deps": [] +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "{{.*}}/interface_part-{{.*}}.pcm", +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/interface_part.cppm" +// CHECK: }, +// CHECK-NEXT: { +// CHECK: "named-module": "M:interface_part" +// CHECK-NOT: "named-module-deps": [] +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "[[PREFIX]]/interface_part.o", +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/interface_part.cppm" +// CHECK: } +// CHECK: ] +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK: "commands": [ +// CHECK-NEXT: { +// CHECK-NOT: "named-module": +// CHECK: "named-module-deps": [ +// CHECK-NEXT: "M" +// CHECK-NEXT: "third_party_module" +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK: "-o", +// CHECK-NEXT: "[[PREFIX]]/User.o", +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/User.cpp +// CHECK: } +// CHECK: ] +// CHECK: } +// CHECK: ] +// CHECK: } diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp index 921ba7aadd67d..8b590bd57e1a3 100644 --- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -393,7 +393,9 @@ class FullDeps { ID.FileName = std::string(Input); ID.ContextHash = std::move(TUDeps.ID.ContextHash); ID.FileDeps = std::move(TUDeps.FileDeps); - ID.ModuleDeps = std::move(TUDeps.ClangModuleDeps); + ID.NamedModule = std::move(TUDeps.ID.ModuleName); + ID.NamedModuleDeps = std::move(TUDeps.NamedModuleDeps); + ID.ClangModuleDeps = std::move(TUDeps.ClangModuleDeps); ID.DriverCommandLine = std::move(TUDeps.DriverCommandLine); ID.Commands = std::move(TUDeps.Commands); @@ -508,8 +510,15 @@ class FullDeps { JOS.object([&] { JOS.attribute("clang-context-hash", StringRef(I.ContextHash)); + if (!I.NamedModule.empty()) + JOS.attribute("named-module", (I.NamedModule)); + if (!I.NamedModuleDeps.empty()) + JOS.attributeArray("named-module-deps", [&] { + for (const auto &Dep : I.NamedModuleDeps) + JOS.value(Dep); + }); JOS.attributeArray("clang-module-deps", - toJSONSorted(JOS, I.ModuleDeps)); + toJSONSorted(JOS, I.ClangModuleDeps)); JOS.attributeArray("command-line", toJSONStrings(JOS, Cmd.Arguments)); JOS.attribute("executable", StringRef(Cmd.Executable)); @@ -521,8 +530,15 @@ class FullDeps { } else { JOS.object([&] { JOS.attribute("clang-context-hash", StringRef(I.ContextHash)); + if (!I.NamedModule.empty()) + JOS.attribute("named-module", (I.NamedModule)); + if (!I.NamedModuleDeps.empty()) + JOS.attributeArray("named-module-deps", [&] { + for (const auto &Dep : I.NamedModuleDeps) + JOS.value(Dep); + }); JOS.attributeArray("clang-module-deps", - toJSONSorted(JOS, I.ModuleDeps)); + toJSONSorted(JOS, I.ClangModuleDeps)); JOS.attributeArray("command-line", toJSONStrings(JOS, I.DriverCommandLine)); JOS.attribute("executable", "clang"); @@ -577,7 +593,9 @@ class FullDeps { std::string FileName; std::string ContextHash; std::vector FileDeps; - std::vector ModuleDeps; + std::string NamedModule; + std::vector NamedModuleDeps; + std::vector ClangModuleDeps; std::vector DriverCommandLine; std::vector Commands; };