thrust / dependencies /cub /test /CMakeLists.txt
camenduru's picture
thanks to nvidia ❤
8ae5fc5
if ("NVCXX" STREQUAL "${CMAKE_CUDA_COMPILER_ID}")
# NVBugs 200770766
set(CUB_SEPARATE_CATCH2 ON)
else()
option(CUB_SEPARATE_CATCH2
"Build each catch2 test as a separate executable."
OFF
)
endif()
include("${CUB_SOURCE_DIR}/cmake/CPM.cmake")
CPMAddPackage("gh:catchorg/Catch2@2.13.9")
option(METAL_BUILD_DOC OFF)
option(METAL_BUILD_EXAMPLES OFF)
option(METAL_BUILD_TESTS OFF)
CPMAddPackage("gh:brunocodutra/metal@2.1.4")
find_package(CUDAToolkit REQUIRED)
# Some tests always build with RDC, so make sure that the sm_XX flags are
# compatible. See note in CubCudaConfig.cmake.
# TODO once we're using CUDA_ARCHITECTURES, we can setup non-rdc fallback
# tests to build for non-rdc arches. But for now, all files in a given directory
# must build with the same `CMAKE_CUDA_FLAGS` due to CMake constraints around
# how CUDA_FLAGS works.
set(CMAKE_CUDA_FLAGS "${CUB_CUDA_FLAGS_BASE} ${CUB_CUDA_FLAGS_RDC}")
# The function below reads the filepath `src`, extracts the %PARAM% comments,
# and fills `labels_var` with a list of `label1_value1.label2_value2...`
# strings, and puts the corresponding `DEFINITION=value1:DEFINITION=value2`
# entries into `defs_var`.
#
# See the README.md file in this directory for background info.
function(cub_get_test_params src labels_var defs_var)
file(READ "${src}" file_data)
set(param_regex "//[ ]+%PARAM%[ ]+([^ ]+)[ ]+([^ ]+)[ ]+([^\n]*)")
string(REGEX MATCHALL
"${param_regex}"
matches
"${file_data}"
)
set(variant_labels)
set(variant_defs)
foreach(match IN LISTS matches)
string(REGEX MATCH
"${param_regex}"
unused
"${match}"
)
set(def ${CMAKE_MATCH_1})
set(label ${CMAKE_MATCH_2})
set(values "${CMAKE_MATCH_3}")
string(REPLACE ":" ";" values "${values}")
# Build lists of test name suffixes (labels) and preprocessor definitions
# (defs) containing the cartesian product of all param values:
if (NOT variant_labels)
foreach(value IN LISTS values)
list(APPEND variant_labels ${label}_${value})
endforeach()
else()
set(tmp_labels)
foreach(old_label IN LISTS variant_labels)
foreach(value IN LISTS values)
list(APPEND tmp_labels ${old_label}.${label}_${value})
endforeach()
endforeach()
set(variant_labels "${tmp_labels}")
endif()
if (NOT variant_defs)
foreach(value IN LISTS values)
list(APPEND variant_defs ${def}=${value})
endforeach()
else()
set(tmp_defs)
foreach(old_def IN LISTS variant_defs)
foreach(value IN LISTS values)
list(APPEND tmp_defs ${old_def}:${def}=${value})
endforeach()
endforeach()
set(variant_defs "${tmp_defs}")
endif()
endforeach()
set(${labels_var} "${variant_labels}" PARENT_SCOPE)
set(${defs_var} "${variant_defs}" PARENT_SCOPE)
endfunction()
# Create meta targets that build all tests for a single configuration:
foreach(cub_target IN LISTS CUB_TARGETS)
cub_get_target_property(config_prefix ${cub_target} PREFIX)
set(config_meta_target ${config_prefix}.tests)
add_custom_target(${config_meta_target})
add_dependencies(${config_prefix}.all ${config_meta_target})
endforeach()
file(GLOB test_srcs
RELATIVE "${CUB_SOURCE_DIR}/test"
CONFIGURE_DEPENDS
test_*.cu
catch2_test_*.cu
)
## cub_is_catch2_test
#
# If the test_src contains the substring "catch2_test_", `result_var` will
# be set to TRUE.
function(cub_is_catch2_test result_var test_src)
string(FIND "${test_src}" "catch2_test_" idx)
if (idx EQUAL -1)
set(${result_var} FALSE PARENT_SCOPE)
else()
set(${result_var} TRUE PARENT_SCOPE)
endif()
endfunction()
## cub_add_test
#
# Add a test executable and register it with ctest.
#
# target_name_var: Variable name to overwrite with the name of the test
# target. Useful for post-processing target information.
# test_name: The name of the test minus "<config_prefix>.test." For example,
# testing/vector.cu will be "vector", and testing/cuda/copy.cu will be
# "cuda.copy".
# test_src: The source file that implements the test.
# cub_target: The reference cub target with configuration information.
#
function(cub_add_test target_name_var test_name test_src cub_target)
cub_get_target_property(config_prefix ${cub_target} PREFIX)
cub_is_catch2_test(is_catch2_test "${test_src}")
# The actual name of the test's target:
set(test_target ${config_prefix}.test.${test_name})
set(${target_name_var} ${test_target} PARENT_SCOPE)
set(config_meta_target ${config_prefix}.tests)
if (is_catch2_test)
# Per config helper library:
set(config_c2h_target ${config_prefix}.test.catch2_helper)
if (NOT TARGET ${config_c2h_target})
add_library(${config_c2h_target} STATIC c2h/generators.cu)
set_property(TARGET ${config_c2h_target}
PROPERTY POSITION_INDEPENDENT_CODE ON
)
if ("NVCXX" STREQUAL "${CMAKE_CUDA_COMPILER_ID}")
target_link_options(${config_c2h_target} PRIVATE "-cuda")
target_compile_options(${config_c2h_target} PRIVATE "-fPIC")
endif()
target_include_directories(${config_c2h_target}
PUBLIC "${CUB_SOURCE_DIR}/test"
)
cub_clone_target_properties(${config_c2h_target} ${cub_target})
target_link_libraries(${config_c2h_target} PRIVATE CUDA::curand ${cub_target})
if (CUB_IN_THRUST)
thrust_fix_clang_nvcc_build_for(${config_c2h_target})
endif()
if (CUB_ENABLE_TESTS_WITH_RDC)
cub_enable_rdc_for_cuda_target(${config_c2h_target})
endif()
endif() # config_c2h_target
if (CUB_SEPARATE_CATCH2)
add_executable(${test_target} "${test_src}")
target_compile_definitions(${test_target} PRIVATE "CUB_CONFIG_MAIN")
add_dependencies(${config_meta_target} ${test_target})
add_test(NAME ${test_target} COMMAND "$<TARGET_FILE:${test_target}>")
else() # Not CUB_SEPARATE_CATCH2
# Per config catch2 runner
set(config_c2run_target ${config_prefix}.catch2_test)
if (NOT TARGET ${config_c2run_target})
add_executable(${config_c2run_target} catch2_runner.cu)
target_link_libraries(${config_c2run_target} PRIVATE
${cub_target}
${config_c2h_target}
Metal
Catch2::Catch2
)
cub_clone_target_properties(${config_c2run_target} ${cub_target})
add_dependencies(${config_meta_target} ${config_c2run_target})
target_include_directories(${config_c2run_target} PRIVATE
"${CUB_SOURCE_DIR}/test"
)
if ("NVCXX" STREQUAL "${CMAKE_CUDA_COMPILER_ID}")
target_link_options(${config_c2run_target} PRIVATE "-cuda")
endif()
if (CUB_IN_THRUST)
thrust_fix_clang_nvcc_build_for(${config_c2run_target})
endif()
add_test(NAME ${config_c2run_target}
COMMAND "$<TARGET_FILE:${config_c2run_target}>"
)
endif() # per config catch2 runner
add_library(${test_target} OBJECT "${test_src}")
target_link_libraries(${config_c2run_target} PRIVATE ${test_target})
endif() # CUB_SEPARATE_CATCH2
if (CUB_IN_THRUST)
thrust_fix_clang_nvcc_build_for(${test_target})
endif()
target_link_libraries(${test_target} PRIVATE
${cub_target}
${config_c2h_target}
Metal
Catch2::Catch2
)
cub_clone_target_properties(${test_target} ${cub_target})
target_include_directories(${test_target}
PUBLIC "${CUB_SOURCE_DIR}/test"
)
else() # Not catch2:
# Related target names:
set(test_meta_target cub.all.test.${test_name})
add_executable(${test_target} "${test_src}")
target_link_libraries(${test_target} ${cub_target})
cub_clone_target_properties(${test_target} ${cub_target})
target_include_directories(${test_target} PRIVATE "${CUB_SOURCE_DIR}/test")
target_compile_definitions(${test_target} PRIVATE CUB_DEBUG_HOST_ASSERTIONS)
if (CUB_IN_THRUST)
thrust_fix_clang_nvcc_build_for(${test_target})
endif()
# Add to the active configuration's meta target
add_dependencies(${config_meta_target} ${test_target})
# Meta target that builds tests with this name for all configurations:
if (NOT TARGET ${test_meta_target})
add_custom_target(${test_meta_target})
endif()
add_dependencies(${test_meta_target} ${test_target})
add_test(NAME ${test_target} COMMAND "$<TARGET_FILE:${test_target}>")
endif() # Not catch2 test
endfunction()
# Sets out_var to 1 if the label contains cdp variants, regardless of whether
# or not CDP is enabled in this particular variant.
function(_cub_has_cdp_variant out_var label)
string(FIND "${label}" "cdp_" idx)
if (idx EQUAL -1)
set(${out_var} 0 PARENT_SCOPE)
else()
set(${out_var} 1 PARENT_SCOPE)
endif()
endfunction()
# Sets out_var to 1 if the label contains "cdp_1", e.g. cdp is explicitly
# requested for this variant.
function(_cub_is_cdp_enabled_variant out_var label)
string(FIND "${label}" "cdp_1" idx)
if (idx EQUAL -1)
set(${out_var} 0 PARENT_SCOPE)
else()
set(${out_var} 1 PARENT_SCOPE)
endif()
endfunction()
foreach (test_src IN LISTS test_srcs)
get_filename_component(test_name "${test_src}" NAME_WE)
string(REGEX REPLACE "^catch2_test_" "" test_name "${test_name}")
string(REGEX REPLACE "^test_" "" test_name "${test_name}")
cub_get_test_params("${test_src}" variant_labels variant_defs)
list(LENGTH variant_labels num_variants)
# Subtract 1 to support the inclusive endpoint of foreach(...RANGE...):
math(EXPR range_end "${num_variants} - 1")
# Verbose output:
if (num_variants GREATER 0)
message(VERBOSE "Detected ${num_variants} variants of test '${test_src}':")
foreach(var_idx RANGE ${range_end})
math(EXPR i "${var_idx} + 1")
list(GET variant_labels ${var_idx} label)
list(GET variant_defs ${var_idx} defs)
message(VERBOSE " ${i}: ${test_name} ${label} ${defs}")
endforeach()
endif()
foreach(cub_target IN LISTS CUB_TARGETS)
cub_get_target_property(config_prefix ${cub_target} PREFIX)
if (num_variants EQUAL 0)
# Only one version of this test.
cub_add_test(test_target ${test_name} "${test_src}" ${cub_target})
if (CUB_ENABLE_TESTS_WITH_RDC)
cub_enable_rdc_for_cuda_target(${test_target})
endif()
else() # has variants:
# Meta target to build all parametrizations of the current test for the
# current CUB_TARGET config
set(variant_meta_target ${config_prefix}.test.${test_name}.all)
if (NOT TARGET ${variant_meta_target})
add_custom_target(${variant_meta_target})
endif()
# Meta target to build all parametrizations of the current test for all
# CUB_TARGET configs
set(cub_variant_meta_target cub.all.test.${test_name}.all)
if (NOT TARGET ${cub_variant_meta_target})
add_custom_target(${cub_variant_meta_target})
endif()
# Generate multiple tests, one per variant.
# See `cub_get_test_params` for details.
foreach(var_idx RANGE ${range_end})
list(GET variant_labels ${var_idx} label)
list(GET variant_defs ${var_idx} defs)
string(REPLACE ":" ";" defs "${defs}")
# A unique index per variant:
list(APPEND defs VAR_IDX=${var_idx})
# Check if the test has explicit CDP variants:
_cub_has_cdp_variant(explicit_cdp "${label}")
_cub_is_cdp_enabled_variant(enable_cdp "${label}")
if (enable_cdp)
if (NOT CUB_ENABLE_TESTS_WITH_RDC)
continue()
endif()
endif()
cub_add_test(test_target
${test_name}.${label}
"${test_src}"
${cub_target}
)
add_dependencies(${variant_meta_target} ${test_target})
add_dependencies(${cub_variant_meta_target} ${test_target})
target_compile_definitions(${test_target} PRIVATE ${defs})
# Enable RDC if the test either:
# 1. Explicitly requests it (cdp_1 label)
# 2. Does not have an explicit CDP variant (no cdp_0 or cdp_1) but
# RDC testing is globally enabled.
#
# Tests that explicitly request no cdp (cdp_0 label) should never enable
# RDC.
if (enable_cdp OR ((NOT explicit_cdp) AND CUB_ENABLE_TESTS_WITH_RDC))
cub_enable_rdc_for_cuda_target(${test_target})
endif()
endforeach() # Variant
endif() # Has variants
endforeach() # CUB targets
endforeach() # Source file
add_subdirectory(cmake)