Skip to content

Commit

Permalink
Remove CMake < 3.18 support and modernise CMake
Browse files Browse the repository at this point in the history
CMake < 3.18 was already deprecated as 3.18 is required for c++17 CUDA

+ Updates the readme version requirements
+ Updates CMake Minimum required statements
+ Removes workarounds for old CMake versions
+ Use FindCUDAToolkit now it is always available
+ Switch to linking against the shared cuda runtime library, rather than implicit default of static on some platforms
+ Create INTERFACE library targets for header only libraries. This enforces the use of SYSTEM on includes
+ Create ALIAS library targets for 3rd party dependencies, for consistency
+ Stops the modification of CMAKE_<LANGUAGE>_FLAGS which is poor form
+ Switches from global defines/includes/links to per-target through the use of target specific functions
+ Ensure packages are available where they are used
+ Prevent in-source CMake builds
+ Use PUBLIC/INTERFACE/PRIVATE for link libraries etc. This may currently be excessivly public
+ Inherit visualisation dependency from target, required vis repo update.
+ Cmake organisation
+ Adjusts swig/python CMake to re-use components from elsewhere more
    + This file could do with work still
+ Misc other CMake changes
  • Loading branch information
ptheywood committed Aug 26, 2021
1 parent bba6d9d commit 646e834
Show file tree
Hide file tree
Showing 34 changed files with 733 additions and 637 deletions.
18 changes: 8 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# CMake 3.15+ for Thrust/Cub support
cmake_minimum_required(VERSION VERSION 3.15 FATAL_ERROR)
# Minimum CMake version 3.18 for CUDA --std=c++17
cmake_minimum_required(VERSION VERSION 3.18 FATAL_ERROR)

# Emit a deprecation warning if CMAKE < 3.18 which is required for c++17 in the future.
set(CMAKE_MINIMUM_SUPPORTED_VERSION 3.18)
if(${CMAKE_VERSION} VERSION_LESS ${CMAKE_MINIMUM_SUPPORTED_VERSION})
message(DEPRECATION "Support for CMake < ${CMAKE_MINIMUM_SUPPORTED_VERSION} is deprecated and will be removed in a future release.")
endif()
project(FLAMEGPU LANGUAGES NONE)

# Find the root directory
get_filename_component(FLAMEGPU_ROOT ${CMAKE_CURRENT_SOURCE_DIR} REALPATH)

project(FLAMEGPU LANGUAGES NONE)
# Ensure this is not an in-source build
include(${FLAMEGPU_ROOT}/cmake/OutOfSourceOnly.cmake)

# Don't create installation scripts (and hide CMAKE_INSTALL_PREFIX from cmake-gui)
set(CMAKE_SKIP_INSTALL_RULES TRUE)
Expand Down Expand Up @@ -69,8 +68,7 @@ endif()
# If CUDA is not available, or the minimum version is too low only build the docs.
if(DOCUMENTATION_ONLY_BUILD)
# Not able to build code, so just make docs
get_filename_component(FLAMEGPU_ROOT ${CMAKE_CURRENT_SOURCE_DIR} REALPATH)
include(./cmake/doxygen.cmake)
include(./cmake/dependencies/doxygen.cmake)
if(${BUILD_API_DOCUMENTATION})
create_doxygen_target("${FLAMEGPU_ROOT}" "${CMAKE_CURRENT_BINARY_DIR}" "")
endif()
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ This is used to build the FLAMEGPU2 library, examples, tests and documentation.
Building FLAME GPU has the following requirements. There are also optional dependencies which are required for some components, such as Documentation or Python bindings.

+ [CMake](https://cmake.org/download/) `>= 3.18`
+ CMake `>= 3.15` currently works, but support will be dropped in a future release.
+ [CUDA](https://developer.nvidia.com/cuda-downloads) `>= 11.0` and a [Compute Capability](https://developer.nvidia.com/cuda-gpus) `>= 3.5` NVIDIA GPU.
+ CUDA `>= 10.0` currently works, but support will be dropped in a future release.
+ C++17 capable C++ compiler (host), compatible with the installed CUDA version
Expand Down
19 changes: 19 additions & 0 deletions cmake/OutOfSourceOnly.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Define a cmake function which emits a fatal error if the source directory and binary directory are the same.
function(EnforceOutOfSourceBuilds)
# Resolve paths before comparioson to ensure comparions are accurate
get_filename_component(source_dir "${CMAKE_SOURCE_DIR}" REALPATH)
get_filename_component(binary_dir "${CMAKE_BINARY_DIR}" REALPATH)

if("${source_dir}" STREQUAL "${binary_dir}")
message(FATAL_ERROR
" In-source CMake builds are not allowed.\n"
" Use a build directory i.e. cmake -B build.\n"
" You may have to clear/delete the generated CMakeCache.txt and CMakeFiles/:\n"
" ${binary_dir}/CMakeCache.txt\n"
" ${binary_dir}/CMakeFiles/\n")
endif()
endfunction()

# Call the function imediately, so the file only needs to be included.
EnforceOutOfSourceBuilds()

396 changes: 130 additions & 266 deletions cmake/common.cmake

Large diffs are not rendered by default.

232 changes: 123 additions & 109 deletions cmake/cuda_arch.cmake
Original file line number Diff line number Diff line change
@@ -1,120 +1,134 @@
# Build a list of gencode arguments, based on CUDA verison.
# Accepts user override via CUDA_ARCH

# CMAKE > 3.18 introduces CUDA_ARCHITECTURES as a cmake-native way of generating gencodes (Policy CMP0104). Set the value to OFF to prevent errors for it being not provided.
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.18")
set(CMAKE_CUDA_ARCHITECTURES "OFF")
endif()


# Check if any have been provided by the users
string(LENGTH "${CUDA_ARCH}" CUDA_ARCH_LENGTH)

# Define the default compute capabilites incase not provided by the user
set(DEFAULT_CUDA_ARCH "35;50;60;70;80;")

# Get the valid options for the current compiler.
# Run nvcc --help to get the help string which contains all valid compute_ sm_ for that version.
execute_process(COMMAND ${CMAKE_CUDA_COMPILER} "--help" OUTPUT_VARIABLE NVCC_HELP_STR ERROR_VARIABLE NVCC_HELP_STR)
# Match all comptue_XX or sm_XXs
string(REGEX MATCHALL "'(sm|compute)_[0-9]+'" SUPPORTED_CUDA_ARCH "${NVCC_HELP_STR}" )
# Strip just the numeric component
string(REGEX REPLACE "'(sm|compute)_([0-9]+)'" "\\2" SUPPORTED_CUDA_ARCH "${SUPPORTED_CUDA_ARCH}" )
# Remove dupes and sort to build the correct list of supported CUDA_ARCH.
list(REMOVE_DUPLICATES SUPPORTED_CUDA_ARCH)
list(REMOVE_ITEM SUPPORTED_CUDA_ARCH "")
list(SORT SUPPORTED_CUDA_ARCH)

# Update defaults to only be those supported
# @todo might be better to instead do a dry run compilation with each gencode to validate?
foreach(ARCH IN LISTS DEFAULT_CUDA_ARCH)
if (NOT ARCH IN_LIST SUPPORTED_CUDA_ARCH)
list(REMOVE_ITEM DEFAULT_CUDA_ARCH "${ARCH}")
# Provides a per target function to set gencode compiler options.
# Function to suppress compiler warnings for a given target
# If the cmake variable CUDA_ARCH is set, to a non emtpy list or space separated string this will be used instead.
# @todo - find a way to warn about deprecated architectures once and only once (at cmake time?) Might need to just try compiling with old warnings and capture / post process the output.
# @todo - figure out how to do this once and only once as a function rather than a macro.
macro(SetCUDAGencodes)
# @todo - only get the available gencodes from nvcc once, rather than per target.

# Parse the expected arguments, prefixing variables.
cmake_parse_arguments(
SCG
""
"TARGET"
""
${ARGN}
)
# Ensure that a target has been passed, and that it is a valid target.
if(NOT SCG_TARGET)
message( FATAL_ERROR "SetCUDAGencodes: 'TARGET' argument required." )
elseif(NOT TARGET ${SCG_TARGET} )
message( FATAL_ERROR "SetCUDAGencodes: TARGET '${SCG_TARGET}' is not a valid target" )
endif()
list(REMOVE_DUPLICATES CUDA_ARCH)
list(REMOVE_ITEM CUDA_ARCH "")
list(SORT CUDA_ARCH)
endforeach()


if(NOT CUDA_ARCH_LENGTH EQUAL 0)
# Convert user provided string argument to a list.
string (REPLACE " " ";" CUDA_ARCH "${CUDA_ARCH}")
string (REPLACE "," ";" CUDA_ARCH "${CUDA_ARCH}")

# Remove duplicates, empty items and sort.
list(REMOVE_DUPLICATES CUDA_ARCH)
list(REMOVE_ITEM CUDA_ARCH "")
list(SORT CUDA_ARCH)

# Validate the list.
foreach(ARCH IN LISTS CUDA_ARCH)
if (NOT ARCH IN_LIST SUPPORTED_CUDA_ARCH)
message(WARNING
" CUDA_ARCH '${ARCH}' not supported by CUDA ${CMAKE_CUDA_COMPILER_VERSION} and is being ignored.\n"
" Choose from: ${SUPPORTED_CUDA_ARCH}")
list(REMOVE_ITEM CUDA_ARCH "${ARCH}")
endif()
endforeach()

# @todo - validate that the CUDA_ARCH provided are supported by the compiler
endif()

# If the list is empty post validation, set it to the (validated) defaults
list(LENGTH CUDA_ARCH CUDA_ARCH_LENGTH)
if(CUDA_ARCH_LENGTH EQUAL 0)
set(CUDA_ARCH ${DEFAULT_CUDA_ARCH})
endif()

# Propagate the validated values to the parent scope, to reduce warning duplication.
get_directory_property(hasParent PARENT_DIRECTORY)
if(hasParent)
set(CUDA_ARCH ${CUDA_ARCH} PARENT_SCOPE)
endif()
# If the list is somehow empty now, do not set any gencodes arguments, instead using the compiler defaults.
list(LENGTH CUDA_ARCH CUDA_ARCH_LENGTH)
if(NOT CUDA_ARCH_LENGTH EQUAL 0)
# Only do this if required.I.e. CUDA_ARCH is the same as the last time this file was included
if(NOT CUDA_ARCH_APPLIED EQUAL CUDA_ARCH)
message(STATUS "Generating Compute Capabilities: ${CUDA_ARCH}")
# CMAKE > 3.18 introduces CUDA_ARCHITECTURES as a cmake-native way of generating gencodes (Policy CMP0104). Set the value to OFF to prevent errors for it being not provided.
# We manually set gencode arguments, so we can (potentially) use LTO and are not restricted to CMake's availble options.
set_property(TARGET ${SCG_TARGET} PROPERTY CUDA_ARCHITECTURES OFF)

# Define the default compute capabilites incase not provided by the user
set(DEFAULT_CUDA_ARCH "35;50;60;70;80;")

# Determine if the user has provided a non default CUDA_ARCH value
string(LENGTH "${CUDA_ARCH}" CUDA_ARCH_LENGTH)

# Query NVCC in order to filter the provided list.
# @todo only do this once, and re-use the output for a given cmake configure?

# Get the valid options for the current compiler.
# Run nvcc --help to get the help string which contains all valid compute_ sm_ for that version.
if(NOT DEFINED SUPPORTED_CUDA_ARCH)
execute_process(COMMAND ${CMAKE_CUDA_COMPILER} "--help" OUTPUT_VARIABLE NVCC_HELP_STR ERROR_VARIABLE NVCC_HELP_STR)
# Match all comptue_XX or sm_XXs
string(REGEX MATCHALL "'(sm|compute)_[0-9]+'" SUPPORTED_CUDA_ARCH "${NVCC_HELP_STR}" )
# Strip just the numeric component
string(REGEX REPLACE "'(sm|compute)_([0-9]+)'" "\\2" SUPPORTED_CUDA_ARCH "${SUPPORTED_CUDA_ARCH}" )
# Remove dupes and sort to build the correct list of supported CUDA_ARCH.
list(REMOVE_DUPLICATES SUPPORTED_CUDA_ARCH)
list(REMOVE_ITEM SUPPORTED_CUDA_ARCH "")
list(SORT SUPPORTED_CUDA_ARCH)

# Store the supported arch's once and only once. This could be a cache var given the cuda compiler should not be able to change without clearing th cache?
get_directory_property(hasParent PARENT_DIRECTORY)
if(hasParent)
set(CUDA_ARCH_APPLIED "${CUDA_ARCH}" PARENT_SCOPE )
set(SUPPORTED_CUDA_ARCH ${SUPPORTED_CUDA_ARCH} PARENT_SCOPE)
endif()
endif()
set(GENCODES_FLAGS)
set(MIN_CUDA_ARCH)
# Convert to gencode arguments

foreach(ARCH IN LISTS CUDA_ARCH)
set(GENCODES_FLAGS "${GENCODES_FLAGS} -gencode arch=compute_${ARCH},code=sm_${ARCH}")


# Update defaults to only be those supported
# @todo might be better to instead do a dry run compilation with each gencode to validate?
foreach(ARCH IN LISTS DEFAULT_CUDA_ARCH)
if (NOT ARCH IN_LIST SUPPORTED_CUDA_ARCH)
list(REMOVE_ITEM DEFAULT_CUDA_ARCH "${ARCH}")
endif()
list(REMOVE_DUPLICATES CUDA_ARCH)
list(REMOVE_ITEM CUDA_ARCH "")
list(SORT CUDA_ARCH)
endforeach()

# Add the last arch again as compute_, compute_ to enable forward looking JIT
list(GET CUDA_ARCH -1 LAST_ARCH)
set(GENCODES_FLAGS "${GENCODES_FLAGS} -gencode arch=compute_${LAST_ARCH},code=compute_${LAST_ARCH}")

# Get the minimum device architecture to pass through to nvcc to enable graceful failure prior to cuda execution.
list(GET CUDA_ARCH 0 MIN_CUDA_ARCH)

# Set the gencode flags on NVCC
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${GENCODES_FLAGS}")
if(NOT CUDA_ARCH_LENGTH EQUAL 0)
# Convert user provided string argument to a list.
string (REPLACE " " ";" CUDA_ARCH "${CUDA_ARCH}")
string (REPLACE "," ";" CUDA_ARCH "${CUDA_ARCH}")

# Remove duplicates, empty items and sort.
list(REMOVE_DUPLICATES CUDA_ARCH)
list(REMOVE_ITEM CUDA_ARCH "")
list(SORT CUDA_ARCH)

# Validate the list.
foreach(ARCH IN LISTS CUDA_ARCH)
if (NOT ARCH IN_LIST SUPPORTED_CUDA_ARCH)
message(WARNING
" CUDA_ARCH '${ARCH}' not supported by CUDA ${CMAKE_CUDA_COMPILER_VERSION} and is being ignored.\n"
" Choose from: ${SUPPORTED_CUDA_ARCH}")
list(REMOVE_ITEM CUDA_ARCH "${ARCH}")
endif()
endforeach()
endif()

# Set the minimum arch flags for all compilers
set(CMAKE_CC_FLAGS "${CMAKE_C_FLAGS} -DMIN_CUDA_ARCH=${MIN_CUDA_ARCH}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMIN_CUDA_ARCH=${MIN_CUDA_ARCH}")
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -DMIN_CUDA_ARCH=${MIN_CUDA_ARCH}")
else()
message(STATUS "Generating default CUDA Compute Capabilities ${CUDA_ARCH}")
endif()

# Supress deprecated architecture warnings, as they are not fitered out by checking against nvcc help.
# Ideally a warning would be output once at config time (i.e. above) and not at every file compilation.
# But this is challenging due to multiline string detection.
# Could potentially compile a simple program, without this flag to detect if its valid/deprecated? Would likely increase build time.
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
# If the list is empty post validation, set it to the (validated) defaults
list(LENGTH CUDA_ARCH CUDA_ARCH_LENGTH)
if(CUDA_ARCH_LENGTH EQUAL 0)
set(CUDA_ARCH ${DEFAULT_CUDA_ARCH})
endif()

# If CUDA 11.2+, can build multiple architectures in parallel. Note this will be multiplicative against the number of threads launched for parallel cmake build, which may anger some systems.
if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "11.2" AND USE_NVCC_THREADS AND DEFINED NVCC_THREADS AND NVCC_THREADS GREATER_EQUAL 0)
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} --threads ${NVCC_THREADS}")
endif()
# Propagate the validated values to the parent scope, to reduce warning duplication.
get_directory_property(hasParent PARENT_DIRECTORY)
if(hasParent)
set(CUDA_ARCH ${CUDA_ARCH} PARENT_SCOPE)
endif()

# If the list is somehow empty now, do not set any gencodes arguments, instead using the compiler defaults.
list(LENGTH CUDA_ARCH CUDA_ARCH_LENGTH)
if(NOT CUDA_ARCH_LENGTH EQUAL 0)
# Only do this if required.I.e. CUDA_ARCH is the same as the last time this file was included
if(NOT CUDA_ARCH_APPLIED EQUAL CUDA_ARCH)
message(STATUS "Generating Compute Capabilities: ${CUDA_ARCH}")
if(hasParent)
set(CUDA_ARCH_APPLIED "${CUDA_ARCH}" PARENT_SCOPE )
endif()
endif()
set(MIN_CUDA_ARCH)
# Convert to gencode arguments

foreach(ARCH IN LISTS CUDA_ARCH)
target_compile_options(${SCG_TARGET} PRIVATE "$<$<COMPILE_LANGUAGE:CUDA>:SHELL:-gencode arch=compute_${ARCH}$<COMMA>code=sm_${ARCH}>")
target_link_options(${SCG_TARGET} PRIVATE "$<DEVICE_LINK:SHELL:-gencode arch=compute_${ARCH}$<COMMA>code=sm_${ARCH}>")
endforeach()

# Add the last arch again as compute_, compute_ to enable forward looking JIT
list(GET CUDA_ARCH -1 LAST_ARCH)
target_compile_options(${SCG_TARGET} PRIVATE "$<$<COMPILE_LANGUAGE:CUDA>:SHELL:-gencode arch=compute_${LAST_ARCH}$<COMMA>code=compute_${LAST_ARCH}>")
target_link_options(${SCG_TARGET} PRIVATE "$<DEVICE_LINK:SHELL:-gencode arch=compute_${LAST_ARCH}$<COMMA>code=compute_${LAST_ARCH}>")

# Get the minimum device architecture to pass through to nvcc to enable graceful failure prior to cuda execution.
list(GET CUDA_ARCH 0 MIN_CUDA_ARCH)

# Set the minimum arch flags for all compilers
target_compile_definitions(${SCG_TARGET} PRIVATE -DMIN_CUDA_ARCH=${MIN_CUDA_ARCH})
else()
message(STATUS "Generating default CUDA Compute Capabilities ${CUDA_ARCH}")
endif()
endmacro()
15 changes: 4 additions & 11 deletions cmake/cxxstd.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,10 @@ if(NOT FLAMEGPU_CXX_STD)
# FLAME GPU is c++14, however due to MSVC 16.10 regressions we build as 17 if possible, else 14.
# 14 Support is still required (CUDA 10.x, swig?).
# Start by assuming both should be availble.
# No need to check CMake version, as our minimum (3.18) supports CUDA c++17
set(CXX17_SUPPORTED ON)
# CMake 3.18 adds CUDA CXX 17, 20
# CMake 3.10 adds CUDA CXX 14
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.18)
# 17 OK
elseif(CMAKE_VERSION VERSION_GREATER_EQUAL 3.10)
# No need for deprecation warning here, already warning about CMAKE < 3.18 being deprecated elsewhere.
set(CXX17_SUPPORTED OFF)
else()
message(FATAL_ERROR "CMAKE ${CMAKE_VERSION} does not support -std=c++14")
endif()

# Check the CU
# CUDA 11.0 adds CXX 17
# CUDA 9.0 adds CXX 14
if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 11.0.0)
Expand All @@ -34,7 +27,7 @@ if(NOT FLAMEGPU_CXX_STD)
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.29)
# 17 required.
if(NOT CXX17_SUPPORTED)
message(FATAL_ERROR "MSVC >= 19.29 requires CMake >= 3.18 and CUDA >= 11.0")
message(FATAL_ERROR "MSVC >= 19.29 requires CUDA >= 11.0")
endif()
elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.11)
# 17 available?
Expand Down
7 changes: 4 additions & 3 deletions cmake/Jitify.cmake → cmake/dependencies/Jitify.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ FetchContent_GetProperties(jitify)
if(NOT jitify_POPULATED)
FetchContent_Populate(jitify)
# Jitify is not a cmake project, so cannot use add_subdirectory, use custom find_package.
set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};${jitify_SOURCE_DIR}/..")
find_package(Jitify REQUIRED)
# Include path is ${Jitify_INCLUDE_DIRS}
endif()

set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};${jitify_SOURCE_DIR}/..")
# Always find the package, even if jitify is already populated.
find_package(Jitify REQUIRED)
File renamed without changes.
Loading

0 comments on commit 646e834

Please sign in to comment.