Skip to content
Snippets Groups Projects
Commit e9b0dcbb authored by Namniav W's avatar Namniav W Committed by Brad King
Browse files

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
parent 3e8f0211
No related branches found
No related tags found
No related merge requests found
Showing
with 264 additions and 6 deletions
......@@ -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);
}
}
......
......@@ -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)
......
((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))
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")
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)
import target7;
int main()
{
return target7();
}
export module mod1;
import partition_level;
export int mod1()
{
return partition_level::get();
}
export module mod2;
import mod1;
export int mod2()
{
return mod1();
}
export module mod3;
import mod2;
export int mod3()
{
return mod2();
}
export module mod4;
import mod3;
export int mod4()
{
return mod3();
}
export module mod5;
import mod4;
export int mod5()
{
return mod4();
}
export module mod6;
import mod5;
export int mod6()
{
return mod5();
}
export module mod7;
import mod6;
export int mod7()
{
return mod6();
}
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();
}
}
module partition_level:impl1;
namespace partition_level {
int impl1()
{
return 0;
}
}
module partition_level:impl2;
import :impl1;
namespace partition_level {
int impl2()
{
return impl1();
}
}
module partition_level:impl3;
import :impl2;
namespace partition_level {
int impl3()
{
return impl2();
}
}
module partition_level:impl4;
import :impl3;
namespace partition_level {
int impl4()
{
return impl3();
}
}
module partition_level:impl5;
import :impl4;
namespace partition_level {
int impl5()
{
return impl4();
}
}
module partition_level:impl6;
import :impl5;
namespace partition_level {
int impl6()
{
return impl5();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment