Skip to content

Commit

Permalink
Consolidate header testing infra. (#2460)
Browse files Browse the repository at this point in the history
  • Loading branch information
alliepiper authored Oct 7, 2024
1 parent c86caca commit 8aaeb29
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 193 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ if (CCCL_TOPLEVEL_PROJECT)
include(cmake/CCCLBuildCompilerTargets.cmake)
include(cmake/CCCLClangdCompileInfo.cmake)
include(cmake/CCCLConfigureTarget.cmake)
include(cmake/CCCLGenerateHeaderTests.cmake)
include(cmake/CCCLGetDependencies.cmake)

cccl_build_compiler_targets()
Expand Down
111 changes: 111 additions & 0 deletions cmake/CCCLGenerateHeaderTests.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Usage:
# cccl_generate_header_tests(<target_name> <project_include_path>
# [cccl_configure_target options]
# [LANGUAGE <CXX|CUDA>]
# [HEADER_TEMPLATE <template>]
# [GLOBS <glob1> [glob2 ...]]
# [EXCLUDES <glob1> [glob2 ...]]
# [HEADERS <header1> [header2 ...]]
# )
#
# Options:
# target_name: The name of the meta-target that will build this set of header tests.
# project_include_path: The path to the project's include directory, relative to <CCCL_SOURCE_DIR>.
# cccl_configure_target options: Options to pass to cccl_configure_target. Must appear before any other named arguments.
# LANGUAGE: The language to use for the header tests. Defaults to CUDA.
# HEADER_TEMPLATE: A file that will be used as a template for each header test. The template will be configured for each header.
# GLOBS: All files that match these globbing patterns will be included in the header tests, unless they also match EXCLUDES.
# EXCLUDES: Files that match these globbing patterns will be excluded from the header tests.
# HEADERS: An explicit list of headers to include in the header tests.
#
# Notes:
# - The header globs are applied relative to <project_include_path>.
# - If no HEADER_TEMPLATE is provided, a default template will be used.
# - The HEADER_TEMPLATE will be configured for each header, with the following variables:
# - @header@: The path to the target header, relative to <project_include_path>.
function(cccl_generate_header_tests target_name project_include_path)
set(options)
set(oneValueArgs LANGUAGE HEADER_TEMPLATE)
set(multiValueArgs GLOBS EXCLUDES HEADERS DEFINES)
cmake_parse_arguments(CGHT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

# Setup defaults
if (NOT DEFINED CGHT_LANGUAGE)
set(CGHT_LANGUAGE CUDA)
endif()

if (NOT DEFINED CGHT_HEADER_TEMPLATE)
set(CGHT_HEADER_TEMPLATE "${CCCL_SOURCE_DIR}/cmake/header_test.cu.in")
endif()

# Derived vars:
if (${CGHT_LANGUAGE} STREQUAL "CXX")
set(extension "cpp")
elseif(${CGHT_LANGUAGE} STREQUAL "CUDA")
set(extension "cu")
else()
message(FATAL_ERROR "Unsupported language: ${CGHT_LANGUAGE}")
endif()

set(cccl_configure_target_options ${CGHT_UNPARSED_ARGUMENTS})
set(base_path "${CCCL_SOURCE_DIR}/${project_include_path}")

# Prepend the basepath to all globbing expressions:
if (DEFINED CGHT_GLOBS)
set(globs)
foreach (glob IN LISTS CGHT_GLOBS)
list(APPEND globs "${base_path}/${glob}")
endforeach()
set(CGHT_GLOBS ${globs})
endif()
if (DEFINED CGHT_EXCLUDES)
set(excludes)
foreach (exclude IN LISTS CGHT_EXCLUDES)
list(APPEND excludes "${base_path}/${exclude}")
endforeach()
set(CGHT_EXCLUDES ${excludes})
endif()

# Determine header list
set(headers)

# Add globs:
if (DEFINED CGHT_GLOBS)
file(GLOB_RECURSE headers
RELATIVE "${base_path}"
CONFIGURE_DEPENDS
${CGHT_GLOBS}
)
endif()

# Remove excludes:
if (DEFINED CGHT_EXCLUDES)
file(GLOB_RECURSE header_excludes
RELATIVE "${base_path}"
CONFIGURE_DEPENDS
${CGHT_EXCLUDES}
)
list(REMOVE_ITEM headers ${header_excludes})
endif()

# Add explicit headers:
if (DEFINED CGHT_HEADERS)
list(APPEND headers ${CGHT_HEADERS})
endif()

# Cleanup:
list(REMOVE_DUPLICATES headers)

# Configure header templates:
set(header_srcs)
foreach (header IN LISTS headers)
set(header_src "${CMAKE_CURRENT_BINARY_DIR}/headers/${target_name}/${header}.${extension}")
configure_file(${CGHT_HEADER_TEMPLATE} ${header_src} @ONLY)
list(APPEND header_srcs ${header_src})
endforeach()

# Object library that compiles each header:
add_library(${target_name} OBJECT ${header_srcs})
cccl_configure_target(${target_name} ${cccl_configure_target_options})

endfunction()
59 changes: 28 additions & 31 deletions thrust/cmake/header_test.in → cmake/header_test.cu.in
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
// This source file checks that:
// 1) Header <thrust/${header}> compiles without error.
// 1) Header <@header@> compiles without error.
// 2) Common macro collisions with platform/system headers are avoided.
// 3) half/bf16 aren't included when these are explicitly disabled.

// Turn off failures for certain configurations:
#define THRUST_CPP11_REQUIRED_NO_ERROR
#define THRUST_CPP14_REQUIRED_NO_ERROR
#define THRUST_MODERN_GCC_REQUIRED_NO_ERROR

#ifndef THRUST_IGNORE_MACRO_CHECKS

// Define THRUST_MACRO_CHECK(macro, header), which emits a diagnostic indicating
// Define CCCL_HEADER_MACRO_CHECK(macro, header), which emits a diagnostic indicating
// a potential macro collision and halts.
//
// Hacky way to build a string, but it works on all tested platforms.
#define THRUST_MACRO_CHECK(MACRO, HEADER) \
THRUST_MACRO_CHECK_IMPL(Identifier MACRO should not be used from Thrust \
#define CCCL_HEADER_MACRO_CHECK(MACRO, HEADER) \
CCCL_HEADER_MACRO_CHECK_IMPL(Identifier MACRO should not be used from Thrust \
headers due to conflicts with HEADER macros.)

// Use raw platform macros instead of the CCCL macros since we
Expand All @@ -24,58 +18,61 @@
#if defined(_MSC_VER) // MSVC

// Fake up an error for MSVC
#define THRUST_MACRO_CHECK_IMPL(msg) \
/* Print message that looks like an error: */ \
__pragma(message(__FILE__ ":" THRUST_MACRO_CHECK_IMPL0(__LINE__) \
": error: " #msg)) \
/* abort compilation due to static_assert or syntax error: */ \
#define CCCL_HEADER_MACRO_CHECK_IMPL(msg) \
/* Print message that looks like an error: */ \
__pragma(message(__FILE__ ":" CCCL_HEADER_MACRO_CHECK_IMPL0(__LINE__) \
": error: " #msg)) \
/* abort compilation due to static_assert or syntax error: */ \
static_assert(false, #msg);
#define THRUST_MACRO_CHECK_IMPL0(x) THRUST_MACRO_CHECK_IMPL1(x)
#define THRUST_MACRO_CHECK_IMPL1(x) #x
#define CCCL_HEADER_MACRO_CHECK_IMPL0(x) CCCL_HEADER_MACRO_CHECK_IMPL1(x)
#define CCCL_HEADER_MACRO_CHECK_IMPL1(x) #x

#elif defined(__clang__) || defined(__GNUC__)

// GCC/clang are easy:
#define THRUST_MACRO_CHECK_IMPL(msg) THRUST_MACRO_CHECK_IMPL0(GCC error #msg)
#define THRUST_MACRO_CHECK_IMPL0(expr) _Pragma(#expr)
#define CCCL_HEADER_MACRO_CHECK_IMPL(msg) CCCL_HEADER_MACRO_CHECK_IMPL0(GCC error #msg)
#define CCCL_HEADER_MACRO_CHECK_IMPL0(expr) _Pragma(#expr)

#endif // msvc vs. the world

#endif
// May be defined to skip macro check for certain configurations.
#ifndef CCCL_IGNORE_HEADER_MACRO_CHECKS

// complex.h conflicts
#define I THRUST_MACRO_CHECK('I', complex.h)
#define I CCCL_HEADER_MACRO_CHECK('I', complex.h)

// windows.h conflicts
#define small THRUST_MACRO_CHECK('small', windows.h)
#define small CCCL_HEADER_MACRO_CHECK('small', windows.h)
// We can't enable these checks without breaking some builds -- some standard
// library implementations unconditionally `#undef` these macros, which then
// causes random failures later.
// Leaving these commented out as a warning: Here be dragons.
//#define min(...) THRUST_MACRO_CHECK('min', windows.h)
//#define max(...) THRUST_MACRO_CHECK('max', windows.h)
//#define min(...) CCCL_HEADER_MACRO_CHECK('min', windows.h)
//#define max(...) CCCL_HEADER_MACRO_CHECK('max', windows.h)

#ifdef _WIN32
// On Windows, make sure any include of Windows.h (e.g. via NVTX) does not define the checked macros
# define WIN32_LEAN_AND_MEAN
#endif // _WIN32

// termios.h conflicts (NVIDIA/thrust#1547)
#define B0 THRUST_MACRO_CHECK("B0", termios.h)
#define B0 CCCL_HEADER_MACRO_CHECK("B0", termios.h)

#endif // THRUST_IGNORE_MACRO_CHECKS
#endif // CCCL_IGNORE_HEADER_MACRO_CHECKS

#include <thrust/${header}>
#include <@header@>

#if defined(CCCL_DISABLE_BF16_SUPPORT)
#if defined(__CUDA_BF16_TYPES_EXIST__)
#error Thrust should not include cuda_bf16.h when BF16 support is disabled
#error We should not include cuda_bf16.h when BF16 support is disabled
#endif // __CUDA_BF16_TYPES_EXIST__
#endif // CCCL_DISABLE_BF16_SUPPORT

#if defined(CCCL_DISABLE_FP16_SUPPORT)
#if defined(__CUDA_FP16_TYPES_EXIST__)
#error Thrust should not include cuda_fp16.h when half support is disabled
#error We should not include cuda_fp16.h when half support is disabled
#endif // __CUDA_FP16_TYPES_EXIST__
#if defined(__CUDA_BF16_TYPES_EXIST__)
#error Thrust should not include cuda_bf16.h when half support is disabled
#error We should not include cuda_bf16.h when half support is disabled
#endif // __CUDA_BF16_TYPES_EXIST__
#endif // CCCL_DISABLE_FP16_SUPPORT
24 changes: 8 additions & 16 deletions cub/cmake/CubHeaderTesting.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,17 @@
# Meta target for all configs' header builds:
add_custom_target(cub.all.headers)

file(GLOB_RECURSE headers
RELATIVE "${CUB_SOURCE_DIR}/cub"
CONFIGURE_DEPENDS
cub/*.cuh
)

set(headertest_srcs)
foreach (header IN LISTS headers)
set(headertest_src "headers/${header}.cu")
configure_file("${CUB_SOURCE_DIR}/cmake/header_test.in" "${headertest_src}")
list(APPEND headertest_srcs "${headertest_src}")
endforeach()

function(cub_add_header_test label definitions)
foreach(cub_target IN LISTS CUB_TARGETS)
cub_get_target_property(config_dialect ${cub_target} DIALECT)
cub_get_target_property(config_prefix ${cub_target} PREFIX)

set(headertest_target ${config_prefix}.headers.${label})
add_library(${headertest_target} OBJECT ${headertest_srcs})

cccl_generate_header_tests(${headertest_target} cub
DIALECT ${config_dialect}
GLOBS "cub/*.cuh"
)
target_link_libraries(${headertest_target} PUBLIC ${cub_target})
target_compile_definitions(${headertest_target} PRIVATE ${definitions})
cub_clone_target_properties(${headertest_target} ${cub_target})
Expand All @@ -47,11 +39,11 @@ set(header_definitions
"THRUST_WRAPPED_NAMESPACE=wrapped_thrust"
"CUB_WRAPPED_NAMESPACE=wrapped_cub"
"CCCL_DISABLE_BF16_SUPPORT")
cub_add_header_test(bf16 "${header_definitions}")
cub_add_header_test(no_bf16 "${header_definitions}")

# Check that half support can be disabled
set(header_definitions
"THRUST_WRAPPED_NAMESPACE=wrapped_thrust"
"CUB_WRAPPED_NAMESPACE=wrapped_cub"
"CCCL_DISABLE_FP16_SUPPORT")
cub_add_header_test(half "${header_definitions}")
cub_add_header_test(no_half "${header_definitions}")
72 changes: 0 additions & 72 deletions cub/cmake/header_test.in

This file was deleted.

38 changes: 15 additions & 23 deletions cudax/cmake/cudaxHeaderTesting.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,27 @@
# Meta target for all configs' header builds:
add_custom_target(cudax.all.headers)

file(GLOB_RECURSE headers
RELATIVE "${cudax_SOURCE_DIR}/include"
CONFIGURE_DEPENDS1
"${cudax_SOURCE_DIR}/include/*.cuh"
"${cudax_SOURCE_DIR}/include/*.h"
)

# The following internal headers are not required to compile independently:
list(REMOVE_ITEM headers
"cuda/experimental/__async/prologue.cuh"
"cuda/experimental/__async/epilogue.cuh"
)

set(headertest_srcs)
foreach (header IN LISTS headers)
set(headertest_src "headers/${header}.cu")
configure_file("${cudax_SOURCE_DIR}/cmake/header_test.in.cu" "${headertest_src}")
list(APPEND headertest_srcs "${headertest_src}")
endforeach()

function(cudax_add_header_test label definitions)
foreach(cn_target IN LISTS cudax_TARGETS)
cudax_get_target_property(config_dialect ${cn_target} DIALECT)
cudax_get_target_property(config_prefix ${cn_target} PREFIX)

set(headertest_target ${config_prefix}.headers.${label})
add_library(${headertest_target} OBJECT ${headertest_srcs})
cccl_generate_header_tests(${headertest_target} cudax/include
DIALECT ${config_dialect}
# The cudax header template removes the check for the `small` macro.
HEADER_TEMPLATE "${cudax_SOURCE_DIR}/cmake/header_test.in.cu"
GLOBS "cuda/experimental/*.cuh"
EXCLUDES
# The following internal headers are not required to compile independently:
"cuda/experimental/__async/prologue.cuh"
"cuda/experimental/__async/epilogue.cuh"
)
target_link_libraries(${headertest_target} PUBLIC ${cn_target})
target_compile_definitions(${headertest_target} PRIVATE ${definitions}
"-DLIBCUDACXX_ENABLE_EXPERIMENTAL_MEMORY_RESOURCE")
target_compile_definitions(${headertest_target} PRIVATE
${definitions}
"-DLIBCUDACXX_ENABLE_EXPERIMENTAL_MEMORY_RESOURCE"
)
cudax_clone_target_properties(${headertest_target} ${cn_target})

add_dependencies(cudax.all.headers ${headertest_target})
Expand Down
Loading

0 comments on commit 8aaeb29

Please sign in to comment.