Skip to content

Commit

Permalink
Enable multiconfig builds.
Browse files Browse the repository at this point in the history
  • Loading branch information
alliepiper committed May 22, 2020
1 parent 53aefc5 commit 2c245d4
Show file tree
Hide file tree
Showing 10 changed files with 690 additions and 411 deletions.
432 changes: 24 additions & 408 deletions CMakeLists.txt

Large diffs are not rendered by default.

119 changes: 119 additions & 0 deletions cmake/ThrustHeaderTesting.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# For every public header, build a translation unit containing `#include <header>`
# to let the compiler try to figure out warnings in that header if it is not otherwise
# included in tests, and also to verify if the headers are modular enough.
# .inl files are not globbed for, because they are not supposed to be used as public
# entrypoints.

foreach(thrust_target IN LISTS THRUST_TARGETS)
thrust_get_target_property(config_host ${thrust_target} HOST)
thrust_get_target_property(config_device ${thrust_target} DEVICE)
thrust_get_target_property(config_prefix ${thrust_target} PREFIX)

string(TOLOWER "${config_host}" host_lower)
string(TOLOWER "${config_device}" device_lower)

# GLOB ALL THE THINGS
set(headers_globs thrust/*.h)
set(headers_exclude_systems_globs thrust/system/*/*)
set(headers_systems_globs
thrust/system/${host_lower}/*
thrust/system/${device_lower}/*
)
set(headers_exclude_details_globs
thrust/detail/*
thrust/*/detail/*
thrust/*/*/detail/*
)

# Get all .h files...
file(GLOB_RECURSE headers
RELATIVE "${Thrust_SOURCE_DIR}/thrust"
${CMAKE_CONFIGURE_DEPENDS}
${headers_globs}
)

# ...then remove all system specific headers...
file(GLOB_RECURSE headers_exclude_systems
RELATIVE "${Thrust_SOURCE_DIR}/thrust"
${CMAKE_CONFIGURE_DEPENDS}
${headers_exclude_systems_globs}
)
list(REMOVE_ITEM headers ${headers_exclude_systems})

# ...then add all headers specific to the selected host and device systems back again...
file(GLOB_RECURSE headers_systems
RELATIVE ${Thrust_SOURCE_DIR}/thrust
${CMAKE_CONFIGURE_DEPENDS}
${headers_systems_globs}
)
list(APPEND headers ${headers_systems})

# ...and remove all the detail headers (also removing the detail headers from the selected systems).
file(GLOB_RECURSE headers_exclude_details
RELATIVE "${Thrust_SOURCE_DIR}/thrust"
${CMAKE_CONFIGURE_DEPENDS}
${headers_exclude_details_globs}
)
list(REMOVE_ITEM headers ${headers_exclude_details})

# List of headers that aren't implemented for all backends, but are implemented for CUDA.
set(partially_implemented_CUDA
async/copy.h
async/for_each.h
async/reduce.h
async/sort.h
async/transform.h
event.h
future.h
)

# List of headers that aren't implemented for all backends, but are implemented for CPP.
set(partially_implemented_CPP
)

# List of headers that aren't implemented for all backends, but are implemented for TBB.
set(partially_implemented_TBB
)

# List of headers that aren't implemented for all backends, but are implemented for OMP.
set(partially_implemented_OMP
)

# List of all partially implemented headers.
set(partially_implemented
${partially_implemented_CUDA}
${partially_implemented_CPP}
${partially_implemented_TBB}
${partially_implemented_OMP}
)
list(REMOVE_DUPLICATES partially_implemented)

set(headertest_srcs)

foreach (header IN LISTS headers)
if ("${header}" IN_LIST partially_implemented)
# This header is partially implemented on _some_ backends...
if (NOT "${header}" IN_LIST partially_implemented_${config_device})
# ...but not on the selected one.
continue()
endif()
endif()

set(headertest_src_ext .cpp)
if ("CUDA" STREQUAL "${config_device}")
set(headertest_src_ext .cu)
endif()

set(headertest_src "headers/${config_prefix}/${header}${headertest_src_ext}")
configure_file("${Thrust_SOURCE_DIR}/cmake/header_test.in" "${headertest_src}")

list(APPEND headertest_srcs "${headertest_src}")
endforeach()

set(headertest_target ${config_prefix}.headers)
add_library(${headertest_target} OBJECT ${headertest_srcs})
target_link_libraries(${headertest_target} PUBLIC ${thrust_target})
thrust_clone_target_properties(${headertest_target} ${thrust_target})

add_dependencies(${config_prefix}.meta ${headertest_target})
endforeach()
275 changes: 275 additions & 0 deletions cmake/ThrustTargetConfig.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
# This file handles thrust target creation and multiconfig.
#
# - Target names: (example) Thrust.cpp.cuda.cpp14 for multiconfig, or
# Thrust for single config
# - Target properties:
# - _THRUST_HOST: {CPP, OMP, TBB}
# - _THRUST_DEVICE: {CUDA, CPP, OMP, TBB}
# - _THRUST_DIALECT: {11, 14, 17}
# - _THRUST_PREFIX: (example) "thrust.cpp.cuda.cpp14" for multiconfig, or
# "thrust" for single config
# - THRUST_TARGETS list stores target names for each requested config
#

# Multiconfig setup:
option(THRUST_ENABLE_MULTICONFIG "Enable multiconfig options for coverage testing." OFF)

if (THRUST_ENABLE_MULTICONFIG)
# Dialects:
set(THRUST_MULTICONFIG_DIALECT_OPTIONS 11 14 17)
option(THRUST_MULTICONFIG_ENABLE_DIALECT_CPP11 "Generate C++11 build configurations." OFF)
option(THRUST_MULTICONFIG_ENABLE_DIALECT_CPP14 "Generate C++14 build configurations." ON)
option(THRUST_MULTICONFIG_ENABLE_DIALECT_CPP17 "Generate C++17 build configurations." OFF)

# Default to CPP14 if all are disabled
if (NOT THRUST_MULTICONFIG_ENABLE_DIALECT_CPP11 AND
NOT THRUST_MULTICONFIG_ENABLE_DIALECT_CPP14 AND
NOT THRUST_MULTICONFIG_ENABLE_DIALECT_CPP17)
set(THRUST_MULTICONFIG_ENABLE_DIALECT_CPP14 ON)
endif()

# Systems:
option(THRUST_MULTICONFIG_ENABLE_SYSTEM_CPP "Generate build configurations that use CPP." ON)
option(THRUST_MULTICONFIG_ENABLE_SYSTEM_CUDA "Generate build configurations that use CUDA." ON)
option(THRUST_MULTICONFIG_ENABLE_SYSTEM_OMP "Generate build configurations that use OpenMP." OFF)
option(THRUST_MULTICONFIG_ENABLE_SYSTEM_TBB "Generate build configurations that use TBB." OFF)

# Workload:
# - `FULL`: [12 configs] The complete cross product of all possible build configurations.
# - `LARGE`: [7 configs] All configurations that have likely usecases.
# - `MEDIUM`: [5 configs] Removes somewhat redundant cases.
# - `SMALL`: [3 configs] Minimal validation of each device system against the `CPP` host.
#
# Config | Workloads | Note
# --------------------------------------------------------------------
# CPP/CUDA | F L M S | Essential: validates CUDA against CPP
# CPP/TBB | F L M S | Essential: validates TBB against CPP
# CPP/OMP | F L M S | Essential: validates OMP against CPP
# TBB/CUDA | F L M | Important: validates TBB/CUDA interop
# OMP/CUDA | F L M | Important: validates OMP/CUDA interop
# TBB/TBB | F L | Somewhat redundant with CPP/TBB and TBB/CUDA
# OMP/OMP | F L | Somewhat redundant with CPP/OMP and OMP/CUDA
# CPP/CPP | F | Very unlikely
# TBB/CPP | F | Very unlikely
# OMP/CPP | F | Very unlikely
# TBB/OMP | F | Very unlikely
# OMP/TBB | F | Very unlikely
set(THRUST_MULTICONFIG_WORKLOAD SMALL CACHE STRING
"Limit host/device configs: SMALL (up to 3 h/d combos), MEDIUM(5), LARGE(7), FULL(12)"
)
set_property(CACHE THRUST_MULTICONFIG_WORKLOAD PROPERTY STRINGS
SMALL MEDIUM LARGE FULL
)
set(THRUST_MULTICONFIG_WORKLOAD_SMALL_CONFIGS
CPP_CUDA CPP_TBB CPP_OMP
)
set(THRUST_MULTICONFIG_WORKLOAD_MEDIUM_CONFIGS
${THRUST_MULTICONFIG_WORKLOAD_SMALL_CONFIGS}
TBB_CUDA OMP_CUDA
)
set(THRUST_MULTICONFIG_WORKLOAD_LARGE_CONFIGS
${THRUST_MULTICONFIG_WORKLOAD_MEDIUM_CONFIGS}
TBB_TBB OMP_OMP
)
set(THRUST_MULTICONFIG_WORKLOAD_FULL_CONFIGS
${THRUST_MULTICONFIG_WORKLOAD_LARGE_CONFIGS}
CPP_CPP TBB_CPP OMP_CPP TBB_OMP OMP_TBB
)
endif()

# These are used with the targets we create to store useful metadata for generic
# target handling
define_property(TARGET PROPERTY _THRUST_HOST
BRIEF_DOCS "A target's host system: CPP, TBB, or OMP."
FULL_DOCS "A target's host system: CPP, TBB, or OMP."
)
define_property(TARGET PROPERTY _THRUST_DEVICE
BRIEF_DOCS "A target's device system: CUDA, CPP, TBB, or OMP."
FULL_DOCS "A target's device system: CUDA, CPP, TBB, or OMP."
)
define_property(TARGET PROPERTY _THRUST_DIALECT
BRIEF_DOCS "A target's C++ dialect: 11, 14, or 17."
FULL_DOCS "A target's C++ dialect: 11, 14, or 17."
)
define_property(TARGET PROPERTY _THRUST_PREFIX
BRIEF_DOCS "A prefix describing the config, eg. 'thrust.cpp.cuda.cpp14'."
FULL_DOCS "A prefix describing the config, eg. 'thrust.cpp.cuda.cpp14'."
)

function(thrust_set_target_properties target_name host device dialect prefix)
set_property(TARGET ${target_name} PROPERTY _THRUST_HOST ${host})
set_property(TARGET ${target_name} PROPERTY _THRUST_DEVICE ${device})
set_property(TARGET ${target_name} PROPERTY _THRUST_DIALECT ${dialect})
set_property(TARGET ${target_name} PROPERTY _THRUST_PREFIX ${prefix})

get_target_property(type ${target_name} TYPE)
if (NOT ${type} STREQUAL "INTERFACE_LIBRARY")
set_property(TARGET ${target_name} PROPERTY CXX_STANDARD ${dialect})
set_property(TARGET ${target_name} PROPERTY CUDA_STANDARD ${dialect})
endif()
endfunction()

# Get a thrust property from a target and store it in var_name
# thrust_get_target_property(<var_name> <target_name> [HOST|DEVICE|DIALECT|PREFIX]
macro(thrust_get_target_property prop_var target_name prop)
get_property(${prop_var} TARGET ${target_name} PROPERTY _THRUST_${prop})
endmacro()

# Defines the following string variables in the caller's scope:
# - ${target_name}_HOST
# - ${target_name}_DEVICE
# - ${target_name}_DIALECT
# - ${target_name}_PREFIX
macro(thrust_get_target_properties target_name)
thrust_get_target_property(${target_name}_HOST ${target_name} HOST)
thrust_get_target_property(${target_name}_DEVICE ${target_name} DEVICE)
thrust_get_target_property(${target_name}_DIALECT ${target_name} DIALECT)
thrust_get_target_property(${target_name}_PREFIX ${target_name} PREFIX)
endmacro()

# Set one target's THRUST_* properties to match another target
function(thrust_clone_target_properties dst_target src_target)
thrust_get_target_properties(${src_target})
thrust_set_target_properties(${dst_target}
${${src_target}_HOST}
${${src_target}_DEVICE}
${${src_target}_DIALECT}
${${src_target}_PREFIX}
)
endfunction()

# Set _THRUST_CONFIG_VALID to TRUE or FALSE in the caller's scope
function(_thrust_is_config_valid host device dialect)
if (THRUST_MULTICONFIG_ENABLE_SYSTEM_${host} AND
THRUST_MULTICONFIG_ENABLE_SYSTEM_${device} AND
THRUST_MULTICONFIG_ENABLE_DIALECT_CPP${dialect} AND
"${host}_${device}" IN_LIST THRUST_MULTICONFIG_WORKLOAD_${THRUST_MULTICONFIG_WORKLOAD}_CONFIGS)
set(_THRUST_CONFIG_VALID TRUE PARENT_SCOPE)
else()
set(_THRUST_CONFIG_VALID FALSE PARENT_SCOPE)
endif()
endfunction()

function(_thrust_init_target_list)
set(THRUST_TARGETS "" CACHE INTERNAL "" FORCE)
endfunction()

function(_thrust_add_target_to_target_list target_name)
set(THRUST_TARGETS ${THRUST_TARGETS} ${target_name} CACHE INTERNAL "" FORCE)
endfunction()

function(_thrust_build_target_list_multiconfig)
# Hide the single config options if they exist:
if (DEFINED THRUST_HOST_SYSTEM)
set_property(CACHE THRUST_HOST_SYSTEM PROPERTY TYPE INTERNAL)
set_property(CACHE THRUST_DEVICE_SYSTEM PROPERTY TYPE INTERNAL)
endif()

set(req_systems)
if (THRUST_MULTICONFIG_ENABLE_SYSTEM_CUDA)
list(APPEND req_systems CUDA)
endif()
if (THRUST_MULTICONFIG_ENABLE_SYSTEM_CPP)
list(APPEND req_systems CPP)
endif()
if (THRUST_MULTICONFIG_ENABLE_SYSTEM_TBB)
list(APPEND req_systems TBB)
endif()
if (THRUST_MULTICONFIG_ENABLE_SYSTEM_OMP)
list(APPEND req_systems OMP)
endif()

find_package(Thrust REQUIRED CONFIG
NO_DEFAULT_PATH # Only check the explicit path in HINTS:
HINTS "${Thrust_SOURCE_DIR}"
COMPONENTS ${req_systems}
)

# Build THRUST_TARGETS
foreach(host IN LISTS THRUST_HOST_SYSTEM_OPTIONS)
foreach(device IN LISTS THRUST_DEVICE_SYSTEM_OPTIONS)
foreach(dialect IN LISTS THRUST_MULTICONFIG_DIALECT_OPTIONS)
_thrust_is_config_valid(${host} ${device} ${dialect})
if (_THRUST_CONFIG_VALID)
set(label "${host}.${device}.cpp${dialect}")
string(TOLOWER "${label}" label)

message(STATUS "Initializing configuration: ${label}")

set(target_name "Thrust.${label}")
thrust_create_target(${target_name}
HOST ${host}
DEVICE ${device}
${THRUST_TARGET_FLAGS}
)

set(prefix "thrust.${label}")
thrust_set_target_properties(${target_name} ${host} ${device} ${dialect} ${prefix})
_thrust_add_target_to_target_list(${target_name})
endif()
endforeach() # dialects
endforeach() # devices
endforeach() # hosts

list(LENGTH THRUST_TARGETS count)
message(STATUS "${count} unique host.device.dialect configurations generated")
endfunction()

function(_thrust_build_target_list_singleconfig)
# Restore system option visibility if these cache options already exist:
if (DEFINED THRUST_HOST_SYSTEM)
set_property(CACHE THRUST_HOST_SYSTEM PROPERTY TYPE STRING)
set_property(CACHE THRUST_DEVICE_SYSTEM PROPERTY TYPE STRING)
endif()

# Use our find_package config to assemble the Thrust library components we need:
find_package(Thrust REQUIRED CONFIG
NO_DEFAULT_PATH # Only check the explicit path in HINTS:
HINTS "${Thrust_SOURCE_DIR}"
)

thrust_create_target(Thrust FROM_OPTIONS ${THRUST_TARGET_FLAGS})
thrust_debug_target(Thrust "${THRUST_VERSION}")

set(host ${THRUST_HOST_SYSTEM})
set(device ${THRUST_DEVICE_SYSTEM})
set(dialect ${CMAKE_CXX_STANDARD})
set(prefix "thrust") # single config

thrust_set_target_properties(Thrust ${host} ${device} ${dialect} ${prefix})
_thrust_add_target_to_target_list(Thrust)
endfunction()

# Build a ${THRUST_TARGETS} list containing target names for all
# requested configurations
function(thrust_build_target_list)
# Clear the list of targets:
_thrust_init_target_list()

# Generic config flags:
set(THRUST_TARGET_FLAGS)
macro(add_flag_option flag docstring default)
set(opt "THRUST_${flag}")
option(${opt} "${docstring}" "${default}")
mark_as_advanced(${opt})
if (${${opt}})
list(APPEND THRUST_TARGET_FLAGS ${flag})
endif()
endmacro()
add_flag_option(IGNORE_DEPRECATED_CPP_DIALECT "Don't warn about any deprecated C++ standards and compilers." OFF)
add_flag_option(IGNORE_DEPRECATED_CPP_11 "Don't warn about deprecated C++11." OFF)
add_flag_option(IGNORE_DEPRECATED_COMPILER "Don't warn about deprecated COMPILERS." OFF)
add_flag_option(IGNORE_CUB_VERSION_CHECK "Don't warn about mismatched CUB versions." OFF)

if (THRUST_ENABLE_MULTICONFIG)
_thrust_build_target_list_multiconfig()
else()
_thrust_build_target_list_singleconfig()
endif()

# Create meta targets for each config:
foreach(thrust_target IN LISTS THRUST_TARGETS)
thrust_get_target_property(config_prefix ${thrust_target} PREFIX)
add_custom_target(${config_prefix}.meta)
endforeach()
endfunction()
Loading

0 comments on commit 2c245d4

Please sign in to comment.