Răsfoiți Sursa

cmCxxModuleMapper: Fix transitive requirements computation

Previously CMake may generate incomplete transitive requirements in
CMakeFiles/<target>.dir/CXXModules.json and therefore in module mapper
for compiler, when source files were listed in CMakeList.txt in a
certain order.

This commit fixes the problem by correctly tracking unfinished
transitive requirements computation of module units.

There have been a simple circular test case whose circular dependency
was reported by build system. Now with this correct implementation it's
reported by CMake generating module mappers.

Add two test cases for transitive requirements computation, one with
adding source files in hardcoded order, and the other in randomized
order.

Fixes: #25465
namniav 1 an în urmă
părinte
comite
e9b0dcbbfa
36 a modificat fișierele cu 392 adăugiri și 6 ștergeri
  1. 8 5
      Source/cmCxxModuleMapper.cxx
  2. 2 0
      Tests/RunCMake/CXXModules/RunCMakeTest.cmake
  3. 1 1
      Tests/RunCMake/CXXModules/examples/circular-build-stdout.txt
  4. 9 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order-randomized/CMakeLists.txt
  5. 132 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/CMakeLists.txt
  6. 6 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/main.cxx
  7. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/module_level/mod1.cxx
  8. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/module_level/mod2.cxx
  9. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/module_level/mod3.cxx
  10. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/module_level/mod4.cxx
  11. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/module_level/mod5.cxx
  12. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/module_level/mod6.cxx
  13. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/module_level/mod7.cxx
  14. 10 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl-non-partition.cxx
  15. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl1.cxx
  16. 8 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl2.cxx
  17. 8 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl3.cxx
  18. 8 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl4.cxx
  19. 8 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl5.cxx
  20. 8 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl6.cxx
  21. 8 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl7.cxx
  22. 8 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/intf1.cxx
  23. 8 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/intf2.cxx
  24. 8 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/intf3.cxx
  25. 8 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/intf4.cxx
  26. 8 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/intf5.cxx
  27. 8 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/intf6.cxx
  28. 8 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/intf7.cxx
  29. 15 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/partition_level.cxx
  30. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/target_level/target1.cxx
  31. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/target_level/target2.cxx
  32. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/target_level/target3.cxx
  33. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/target_level/target4.cxx
  34. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/target_level/target5.cxx
  35. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/target_level/target6.cxx
  36. 7 0
      Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/target_level/target7.cxx

+ 8 - 5
Source/cmCxxModuleMapper.cxx

@@ -346,13 +346,16 @@ std::set<std::string> CxxModuleUsageSeed(
         // Add the direct usage.
         this_usages.insert(r.LogicalName);
 
-        // Add the transitive usage.
-        if (transitive_usages != usages.Usage.end()) {
+        if (transitive_usages == usages.Usage.end() ||
+            internal_usages.find(r.LogicalName) != internal_usages.end()) {
+          // Mark that we need to update transitive usages later.
+          if (bmi_loc.IsKnown()) {
+            internal_usages[p.LogicalName].insert(r.LogicalName);
+          }
+        } else {
+          // Add the transitive usage.
           this_usages.insert(transitive_usages->second.begin(),
                              transitive_usages->second.end());
-        } else if (bmi_loc.IsKnown()) {
-          // Mark that we need to update transitive usages later.
-          internal_usages[p.LogicalName].insert(r.LogicalName);
         }
       }
 

+ 2 - 0
Tests/RunCMake/CXXModules/RunCMakeTest.cmake

@@ -174,6 +174,8 @@ if ("named" IN_LIST CMake_TEST_MODULE_COMPILATION)
   run_cxx_module_test(object-library)
   run_cxx_module_test(generated)
   run_cxx_module_test(deep-chain)
+  run_cxx_module_test(non-trivial-collation-order)
+  run_cxx_module_test(non-trivial-collation-order-randomized)
   run_cxx_module_test(duplicate)
   set(RunCMake_CXXModules_NO_TEST 1)
   run_cxx_module_test(circular)

+ 1 - 1
Tests/RunCMake/CXXModules/examples/circular-build-stdout.txt

@@ -1 +1 @@
-((Ninja generators)?(build stopped: dependency cycle:)|(Visual Studio generators)?(error : Cannot build the following source files because there is a cyclic dependency between them))
+((Ninja generators)?(CMake Error: Circular dependency detected in the C\+\+ module import graph. See modules named: "a", "b")|(Visual Studio generators)?(error : Cannot build the following source files because there is a cyclic dependency between them))

+ 9 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order-randomized/CMakeLists.txt

@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.24...3.28)
+project(cxx_modules_non_trivial_collation_order_randomized CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+set(non_trivial_collation_order_randomized TRUE)
+
+add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../non-trivial-collation-order/"
+                 "${CMAKE_CURRENT_BINARY_DIR}/non-trivial-collation-order")

+ 132 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/CMakeLists.txt

@@ -0,0 +1,132 @@
+cmake_minimum_required(VERSION 3.24...3.28)
+project(cxx_modules_non_trivial_collation_order CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+  include(CheckCompilerFlag)
+  check_compiler_flag(CXX "-Wread-modules-implicitly" have_implicit_module_warning)
+  if (have_implicit_module_warning)
+    add_compile_options(-Werror=read-modules-implicitly)
+  endif ()
+endif ()
+
+# Returns an random integer from range [0,length)
+function(get_random_index result length)
+  # math(EXPR) parses and evaluates as 64-bit signed integer.
+  string(RANDOM LENGTH 18 ALPHABET "0123456789" value)
+  # Modulo is not uniformly distributed, but we doesn't need that here.
+  math(EXPR ${result} "${value} % ${length}")
+  return(PROPAGATE ${result})
+endfunction()
+
+# Shuffle list_var randomly
+function(shuffle_list list_var)
+  set(result)
+  set(length 0)
+  foreach(item IN LISTS "${list_var}")
+    math(EXPR length "${length} + 1")
+    get_random_index(index ${length})
+    list(INSERT result ${index} "${item}")
+  endforeach()
+  set("${list_var}" "${result}" PARENT_SCOPE)
+endfunction()
+
+# The import chain is:
+# impl-non-partition  --(implicitly)-->  partition_level(primary interface unit)
+# --> :intf7 --> :intf6 --> :intf5 --> :intf4 --> :intf3 --> :intf2 --> :intf1
+# --> :impl7 --> :impl6 --> :impl5 --> :impl4 --> :impl3 --> :impl2 --> :impl1
+set(partition_level_srcs
+  partition_level/partition_level.cxx
+  partition_level/intf1.cxx
+  partition_level/intf3.cxx
+  partition_level/intf4.cxx
+  partition_level/intf2.cxx   # intentional order
+  partition_level/intf5.cxx
+  partition_level/intf6.cxx
+  partition_level/intf7.cxx
+  partition_level/impl1.cxx
+  partition_level/impl3.cxx
+  partition_level/impl4.cxx
+  partition_level/impl2.cxx   # intentional order
+  partition_level/impl5.cxx
+  partition_level/impl6.cxx
+  partition_level/impl7.cxx
+)
+
+# The import chain is:
+# mod7 --> mod6 --> mod5 --> mod4 --> mod3 --> mod2 --> mod1 --> partition_level
+set(module_level_srcs
+  ${partition_level_srcs}
+  module_level/mod1.cxx
+  module_level/mod3.cxx
+  module_level/mod4.cxx
+  module_level/mod2.cxx   # intentional order
+  module_level/mod5.cxx
+  module_level/mod6.cxx
+  module_level/mod7.cxx
+)
+
+# The import chain is:
+# target7 --> target6 --> target5 --> target4 -->
+# target3 --> target2 --> target1 --> target_module_level
+set(targets
+  target_module_level
+  target1
+  target3
+  target4
+  target2 # intentional order
+  target5
+  target6
+  target7
+)
+
+if(non_trivial_collation_order_randomized)
+  shuffle_list(module_level_srcs)
+  shuffle_list(targets)
+endif()
+
+message(STATUS "module_level_srcs: ${module_level_srcs}")
+message(STATUS "targets: ${targets}")
+
+foreach(tgt IN LISTS targets)
+  if (tgt STREQUAL "target_module_level")
+
+    add_library(target_module_level STATIC)
+    target_sources(target_module_level
+      PUBLIC
+        FILE_SET CXX_MODULES
+          BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}"
+          FILES ${module_level_srcs}
+      PRIVATE
+        partition_level/impl-non-partition.cxx
+    )
+    target_compile_features(target_module_level PUBLIC cxx_std_20)
+
+  else()
+
+    add_library(${tgt} STATIC)
+    target_sources(${tgt}
+      PUBLIC
+        FILE_SET CXX_MODULES
+          BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}"
+          FILES target_level/${tgt}.cxx
+    )
+    target_compile_features(${tgt} PUBLIC cxx_std_20)
+    if (tgt MATCHES "^target([2-9])$")
+      math(EXPR dep_number "${CMAKE_MATCH_1} - 1")
+      target_link_libraries(${tgt} PRIVATE "target${dep_number}")
+    else()
+      target_link_libraries(${tgt} PRIVATE target_module_level)
+    endif()
+
+  endif()
+endforeach()
+
+add_executable(exe)
+target_link_libraries(exe PRIVATE target7)
+target_sources(exe
+  PRIVATE
+    main.cxx)
+
+add_test(NAME exe COMMAND exe)

+ 6 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/main.cxx

@@ -0,0 +1,6 @@
+import target7;
+
+int main()
+{
+  return target7();
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/module_level/mod1.cxx

@@ -0,0 +1,7 @@
+export module mod1;
+import partition_level;
+
+export int mod1()
+{
+  return partition_level::get();
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/module_level/mod2.cxx

@@ -0,0 +1,7 @@
+export module mod2;
+import mod1;
+
+export int mod2()
+{
+  return mod1();
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/module_level/mod3.cxx

@@ -0,0 +1,7 @@
+export module mod3;
+import mod2;
+
+export int mod3()
+{
+  return mod2();
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/module_level/mod4.cxx

@@ -0,0 +1,7 @@
+export module mod4;
+import mod3;
+
+export int mod4()
+{
+  return mod3();
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/module_level/mod5.cxx

@@ -0,0 +1,7 @@
+export module mod5;
+import mod4;
+
+export int mod5()
+{
+  return mod4();
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/module_level/mod6.cxx

@@ -0,0 +1,7 @@
+export module mod6;
+import mod5;
+
+export int mod6()
+{
+  return mod5();
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/module_level/mod7.cxx

@@ -0,0 +1,7 @@
+export module mod7;
+import mod6;
+
+export int mod7()
+{
+  return mod6();
+}

+ 10 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl-non-partition.cxx

@@ -0,0 +1,10 @@
+module partition_level;
+
+namespace partition_level {
+int non_partition()
+{
+  return primary() + intf1() + intf2() + intf3() + intf4() + intf5() +
+    intf6() + intf7() + impl1() + impl2() + impl3() + impl4() + impl5() +
+    impl6() + impl7();
+}
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl1.cxx

@@ -0,0 +1,7 @@
+module partition_level:impl1;
+namespace partition_level {
+int impl1()
+{
+  return 0;
+}
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl2.cxx

@@ -0,0 +1,8 @@
+module partition_level:impl2;
+import :impl1;
+namespace partition_level {
+int impl2()
+{
+  return impl1();
+}
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl3.cxx

@@ -0,0 +1,8 @@
+module partition_level:impl3;
+import :impl2;
+namespace partition_level {
+int impl3()
+{
+  return impl2();
+}
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl4.cxx

@@ -0,0 +1,8 @@
+module partition_level:impl4;
+import :impl3;
+namespace partition_level {
+int impl4()
+{
+  return impl3();
+}
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl5.cxx

@@ -0,0 +1,8 @@
+module partition_level:impl5;
+import :impl4;
+namespace partition_level {
+int impl5()
+{
+  return impl4();
+}
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl6.cxx

@@ -0,0 +1,8 @@
+module partition_level:impl6;
+import :impl5;
+namespace partition_level {
+int impl6()
+{
+  return impl5();
+}
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/impl7.cxx

@@ -0,0 +1,8 @@
+module partition_level:impl7;
+import :impl6;
+namespace partition_level {
+int impl7()
+{
+  return impl6();
+}
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/intf1.cxx

@@ -0,0 +1,8 @@
+export module partition_level:intf1;
+import :impl7;
+namespace partition_level {
+int intf1()
+{
+  return impl7();
+}
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/intf2.cxx

@@ -0,0 +1,8 @@
+export module partition_level:intf2;
+export import :intf1;
+namespace partition_level {
+int intf2()
+{
+  return intf1();
+}
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/intf3.cxx

@@ -0,0 +1,8 @@
+export module partition_level:intf3;
+export import :intf2;
+namespace partition_level {
+int intf3()
+{
+  return intf2();
+}
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/intf4.cxx

@@ -0,0 +1,8 @@
+export module partition_level:intf4;
+export import :intf3;
+namespace partition_level {
+int intf4()
+{
+  return intf3();
+}
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/intf5.cxx

@@ -0,0 +1,8 @@
+export module partition_level:intf5;
+export import :intf4;
+namespace partition_level {
+int intf5()
+{
+  return intf4();
+}
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/intf6.cxx

@@ -0,0 +1,8 @@
+export module partition_level:intf6;
+export import :intf5;
+namespace partition_level {
+int intf6()
+{
+  return intf5();
+}
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/intf7.cxx

@@ -0,0 +1,8 @@
+export module partition_level:intf7;
+export import :intf6;
+namespace partition_level {
+int intf7()
+{
+  return intf6();
+}
+}

+ 15 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/partition_level/partition_level.cxx

@@ -0,0 +1,15 @@
+export module partition_level;
+export import :intf7;
+namespace partition_level {
+int primary()
+{
+  return intf7();
+}
+
+int non_partition();
+
+export int get()
+{
+  return non_partition();
+}
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/target_level/target1.cxx

@@ -0,0 +1,7 @@
+export module target1;
+import mod7;
+
+export int target1()
+{
+  return mod7();
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/target_level/target2.cxx

@@ -0,0 +1,7 @@
+export module target2;
+import target1;
+
+export int target2()
+{
+  return target1();
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/target_level/target3.cxx

@@ -0,0 +1,7 @@
+export module target3;
+import target2;
+
+export int target3()
+{
+  return target2();
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/target_level/target4.cxx

@@ -0,0 +1,7 @@
+export module target4;
+import target3;
+
+export int target4()
+{
+  return target3();
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/target_level/target5.cxx

@@ -0,0 +1,7 @@
+export module target5;
+import target4;
+
+export int target5()
+{
+  return target4();
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/target_level/target6.cxx

@@ -0,0 +1,7 @@
+export module target6;
+import target5;
+
+export int target6()
+{
+  return target5();
+}

+ 7 - 0
Tests/RunCMake/CXXModules/examples/non-trivial-collation-order/target_level/target7.cxx

@@ -0,0 +1,7 @@
+export module target7;
+import target6;
+
+export int target7()
+{
+  return target6();
+}