diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..aec8d8b240b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +*build*/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000..1b1cc4ba9f5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "libcudacxx"] + path = libcudacxx + url = ../libcudacxx.git +[submodule "cub"] + path = cub + url = ../cub +[submodule "thrust"] + path = thrust + url = ../thrust diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000000..e40be2c4215 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,55 @@ +# 3.15 is the minimum for including the project with add_subdirectory. +# 3.21 is the minimum for the developer build. +cmake_minimum_required(VERSION 3.15) + +# Determine whether CCCL is the top-level project or included into +# another project via add_subdirectory() +if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_LIST_DIR}") + set(CCCL_TOPLEVEL_PROJECT ON) +else() + set(CCCL_TOPLEVEL_PROJECT OFF) +endif() + +# TODO find_package for add_subdir usecases, use components for subprojects. + +project(CCCL NONE) + +# Optionally include installation rules for non-top-level builds: +option(CCCL_ENABLE_INSTALL_RULES "Enable installation of CCCL." ${CCCL_TOPLEVEL_PROJECT}) +if (CCCL_ENABLE_INSTALL_RULES) + include(cmake/CCCLInstallRules.cmake) +endif() + +# Support adding Thrust to a parent project via add_subdirectory. +# See examples/cmake/add_subdir/CMakeLists.txt for details. +if (NOT CCCL_TOPLEVEL_PROJECT) + include(cmake/CCCLAddSubdir.cmake) + return() +endif() + +# We require a higher cmake version for dev builds +cmake_minimum_required(VERSION 3.21) + +option(CCCL_ENABLE_LIBCUDACXX "Enable the libcu++ developer build." ON) +option(CCCL_ENABLE_CUB "Enable the CUB developer build." ON) +option(CCCL_ENABLE_THRUST "Enable the Thrust developer build." ON) +option(CCCL_ENABLE_TESTING "Enable CUDA C++ Core Library tests." ON) + +include(CTest) +enable_testing() + +if (CCCL_ENABLE_LIBCUDACXX) + add_subdirectory(libcudacxx) +endif() + +if (CCCL_ENABLE_CUB) + add_subdirectory(cub) +endif() + +if (CCCL_ENABLE_THRUST) + add_subdirectory(thrust) +endif() + +if (CCCL_ENABLE_TESTING) + add_subdirectory(test) +endif() diff --git a/cmake/CCCLAddSubdir.cmake b/cmake/CCCLAddSubdir.cmake new file mode 100644 index 00000000000..b76238442d3 --- /dev/null +++ b/cmake/CCCLAddSubdir.cmake @@ -0,0 +1,6 @@ +find_package(CCCL REQUIRED CONFIG + NO_DEFAULT_PATH # Only check the explicit path in HINTS: + HINTS "${CCCL_SOURCE_DIR}" + COMPONENTS ${CCCL_REQUIRED_COMPONENTS} + OPTIONAL_COMPONENTS ${CCCL_OPTIONAL_COMPONENTS} +) diff --git a/cmake/CCCLInstallRules.cmake b/cmake/CCCLInstallRules.cmake new file mode 100644 index 00000000000..39c22d37a58 --- /dev/null +++ b/cmake/CCCLInstallRules.cmake @@ -0,0 +1,10 @@ +# Bring in CMAKE_INSTALL_LIBDIR +include(GNUInstallDirs) + +# CCCL has no installable binaries, no need to build before installing: +set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY TRUE) + +install(DIRECTORY "${CCCL_SOURCE_DIR}/share/cmake/cccl" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/" + PATTERN *.cmake.in EXCLUDE +) diff --git a/cub b/cub new file mode 160000 index 00000000000..d46f513cdd5 --- /dev/null +++ b/cub @@ -0,0 +1 @@ +Subproject commit d46f513cdd5a8580e7a674d3a334b1fccc466233 diff --git a/libcudacxx b/libcudacxx new file mode 160000 index 00000000000..ab5a255bae6 --- /dev/null +++ b/libcudacxx @@ -0,0 +1 @@ +Subproject commit ab5a255bae6b692716728862a7f97c740f1cfcd3 diff --git a/share/cmake/cccl/cccl-config-version.cmake b/share/cmake/cccl/cccl-config-version.cmake new file mode 100644 index 00000000000..008257bc475 --- /dev/null +++ b/share/cmake/cccl/cccl-config-version.cmake @@ -0,0 +1,13 @@ +# TODO we should parse this from a header, and update to CalVer if that's still POR +set(CCCL_VERSION_MAJOR 2) +set(CCCL_VERSION_MINOR 1) +set(CCCL_VERSION_PATCH 0) +set(CCCL_VERSION_TWEAK 0) + +set(CCCL_VERSION "${CCCL_VERSION_MAJOR}.${CCCL_VERSION_MINOR}.${CCCL_VERSION_PATCH}.${CCCL_VERSION_TWEAK}") + +# TODO decide and implement versioning logic. +set(PACKAGE_VERSION ${CCCL_VERSION}) +set(PACKAGE_VERSION_COMPATIBLE TRUE) +set(PACKAGE_VERSION_EXACT TRUE) +set(PACKAGE_VERSION_UNSUITABLE FALSE) diff --git a/share/cmake/cccl/cccl-config.cmake b/share/cmake/cccl/cccl-config.cmake new file mode 100644 index 00000000000..b2e7e211085 --- /dev/null +++ b/share/cmake/cccl/cccl-config.cmake @@ -0,0 +1,59 @@ +# +# find_package(CCCL) config file. +# +# Imports the Thrust, CUB, and libcudacxx components of the NVIDIA +# CUDA/C++ Core Libraries. + +unset(cccl_version) # TODO + +set(cccl_cmake_dir "${CMAKE_CURRENT_LIST_DIR}") + +if (${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY) + set(cccl_quiet_flag "QUIET") +else() + set(cccl_quiet_flag "") +endif() + +foreach(component ${${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS}) + unset(req) + if (${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED_${component}) + set(cccl_comp_required_flag "REQUIRED") + endif() + + if(component STREQUAL "libcudacxx") + find_package(libcudacxx ${cccl_version} CONFIG + ${cccl_quiet_flag} + ${cccl_comp_required_flag} + NO_DEFAULT_PATH # Only check the explicit HINTS below: + HINTS + "${cccl_cmake_dir}/../../../libcudacxx/lib/cmake/" # Source layout (GitHub) + "${cccl_cmake_dir}/.." # Install layout + ) + elseif(component STREQUAL "CUB") + find_package(CUB ${cccl_version} CONFIG + ${cccl_quiet_flag} + ${cccl_comp_required_flag} + NO_DEFAULT_PATH # Only check the explicit HINTS below: + HINTS + "${cccl_cmake_dir}/../../../cub/cub/cmake/" # Source layout (GitHub) + "${cccl_cmake_dir}/.." # Install layout + ) + elseif(component STREQUAL "Thrust") + find_package(Thrust ${cccl_version} CONFIG + ${cccl_quiet_flag} + ${cccl_comp_required_flag} + NO_DEFAULT_PATH # Only check the explicit HINTS below: + HINTS + "${cccl_cmake_dir}/../../../thrust/thrust/cmake/" # Source layout (GitHub) + "${cccl_cmake_dir}/.." # Install layout + ) + else() + message(FATAL_ERROR "Invalid CCCL component requested: '${component}'") + endif() +endforeach() + +include(FindPackageHandleStandardArgs) +if (NOT CCCL_CONFIG) + set(CCCL_CONFIG "${CMAKE_CURRENT_LIST_FILE}") +endif() +find_package_handle_standard_args(CCCL CONFIG_MODE) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 00000000000..24c8f171635 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(cmake) diff --git a/test/cmake/CMakeLists.txt b/test/cmake/CMakeLists.txt new file mode 100644 index 00000000000..c8f3aa32b92 --- /dev/null +++ b/test/cmake/CMakeLists.txt @@ -0,0 +1,81 @@ +set(cmake_opts + -D "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" + -D "CMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}" + -D "CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" +) + +# Temporary installation prefix for tests against installed project: +set(tmp_install_prefix "${CMAKE_CURRENT_BINARY_DIR}/test_install") + +# Add a build-and-test CTest. +# - full_test_name_var will be set to the full name of the test. +# - subdir is the relative path to the test project directory. +# - test_id is used to generate a unique name for this test, allowing the +# subdir to be reused. +# - Any additional args will be passed to the project configure step. +function(cccl_add_compile_test full_test_name_var subdir test_id) + set(test_name cccl.test.cmake.${subdir}.${test_id}) + set(src_dir "${CMAKE_CURRENT_SOURCE_DIR}/${subdir}") + set(build_dir "${CMAKE_CURRENT_BINARY_DIR}/${subdir}/${test_id}") + add_test(NAME ${test_name} + COMMAND "${CMAKE_CTEST_COMMAND}" + --build-and-test "${src_dir}" "${build_dir}" + --build-generator "${CMAKE_GENERATOR}" + --build-options + ${cmake_opts} + ${ARGN} + --test-command "${CMAKE_CTEST_COMMAND}" --output-on-failure + ) + set(${full_test_name_var} ${test_name} PARENT_SCOPE) +endfunction() + +foreach (root_type IN ITEMS SOURCE INSTALL) + if (root_type STREQUAL "INSTALL") + set(cccl_root "${tmp_install_prefix}") + else() + set(cccl_root "${CCCL_SOURCE_DIR}") + endif() + + foreach (components IN ITEMS DEFAULT Thrust CUB libcudacxx) + set(package_types CCCL) + if (NOT components STREQUAL "DEFAULT") + list(APPEND package_types NATIVE) + endif() + if (root_type STREQUAL "SOURCE") + list(APPEND package_types SUBDIR) + endif() + foreach (package_type IN LISTS package_types) + string(TOLOWER "${root_type}.${package_type}.${components}" suffix) + cccl_add_compile_test(test_name + test_export + "${suffix}" + -D "CCCL_ROOT=${cccl_root}" + -D "ROOT_TYPE=${root_type}" + -D "COMPONENTS=${components}" + -D "PACKAGE_TYPE=${package_type}" + ) + + if (root_type STREQUAL "INSTALL") + set_tests_properties(${test_name} PROPERTIES FIXTURES_REQUIRED install_tree) + endif() + endforeach() # package_type + endforeach() # components +endforeach() # root_type + +################################################################################ +# Install tree fixtures +add_test(NAME cccl.test.cmake.install_tree.install + COMMAND "${CMAKE_COMMAND}" + --install "${CCCL_BINARY_DIR}" + --prefix "${tmp_install_prefix}" +) +set_tests_properties(cccl.test.cmake.install_tree.install PROPERTIES + FIXTURES_SETUP install_tree +) + +add_test(NAME cccl.test.cmake.install_tree.cleanup + COMMAND "${CMAKE_COMMAND}" -E rm -rf "${tmp_install_prefix}" +) +set_tests_properties(cccl.test.cmake.install_tree.cleanup PROPERTIES + FIXTURES_CLEANUP install_tree +) diff --git a/test/cmake/test_export/CMakeLists.txt b/test/cmake/test_export/CMakeLists.txt new file mode 100644 index 00000000000..56e3ffb7f11 --- /dev/null +++ b/test/cmake/test_export/CMakeLists.txt @@ -0,0 +1,143 @@ +# Test the CMake packages for CCCL and all subprojects. +# +# Parameters: +# - CCCL_ROOT [Path] Root of the CCCL repo, or an installation root. +# - ROOT_TYPE [String] {SOURCE | INSTALL} Whether CCCL_ROOT is an +# installation prefix or the source root. +# - COMPONENTS [StringList] {Thrust CUB libcudacxx} Which CCCL subprojects +# should be found. +# - PACKAGE_TYPE [String] {CCCL | NATIVE | SUBDIR}: +# - CCCL -> `find_package(CCCL COMPONENTS )` +# - NATIVE -> `find_package()` +# - SUBDIR -> `set(CCCL_REQUIRED_COMPONENTS )` +# `add_subdirectory(${cccl_root})` + + +cmake_minimum_required(VERSION 3.21) +project(CCCLTestExport CXX) + +include(CTest) +enable_testing() + +set(CCCL_ROOT "" CACHE PATH + "Root of the CCCL repo, or an installation root.") +set(ROOT_TYPE "" CACHE STRING + "{SOURCE | INSTALL} Whether CCCL_ROOT is an install prefix or source root.") +set_property(CACHE ROOT_TYPE PROPERTY STRINGS SOURCE INSTALL) +set(COMPONENTS "" CACHE STRING + "DEFAULT for no components, or semi-colon delimited list of Thrust, CUB, and/or libcudacxx.") +set(PACKAGE_TYPE "" CACHE STRING + "CCCL: Find CCCL with subpackages as components; NATIVE: Find subpackages directly; SUBDIR: add_subdirectory(${CCCL_ROOT}") +set_property(CACHE PACKAGE_TYPE PROPERTY STRINGS CCCL NATIVE SUBDIR) + +message(STATUS "CCCL_ROOT=${CCCL_ROOT}") +message(STATUS "ROOT_TYPE=${ROOT_TYPE}") +message(STATUS "COMPONENTS=${COMPONENTS}") +message(STATUS "PACKAGE_TYPE=${PACKAGE_TYPE}") + +# TODO `ROOT_TYPE` probably won't be needed after the configs are all moved +# to the CCCL repo. +if (ROOT_TYPE STREQUAL "SOURCE") + cmake_path(APPEND CCCL_ROOT thrust OUTPUT_VARIABLE Thrust_ROOT) + cmake_path(APPEND CCCL_ROOT cub OUTPUT_VARIABLE CUB_ROOT) + cmake_path(APPEND CCCL_ROOT libcudacxx OUTPUT_VARIABLE libcudacxx_ROOT) +elseif (ROOT_TYPE STREQUAL "INSTALL") + set(Thrust_ROOT "${CCCL_ROOT}") + set(CUB_ROOT "${CCCL_ROOT}") + set(libcudacxx_ROOT "${CCCL_ROOT}") +else() + message(FATAL_ERROR "Invalid ROOT_TYPE: ${ROOT_TYPE}") +endif() + +message(STATUS "Thrust_ROOT=${Thrust_ROOT}") +message(STATUS "CUB_ROOT=${CUB_ROOT}") +message(STATUS "libcudacxx_ROOT=${libcudacxx_ROOT}") + +function(do_find_package pkg_name pkg_prefix) + list(APPEND arg_list + REQUIRED + ${ARGN} + NO_DEFAULT_PATH + HINTS "${pkg_prefix}" + ) + list(JOIN arg_list " " arg_str) + message(STATUS "Executing: find_package(${pkg_name} ${arg_str})") + find_package(${pkg_name} ${arg_list}) + if (NOT ${pkg_name}_FOUND) + message(FATAL_ERROR "Failed: find_package(${pkg_name} ${arg_str})") + endif() +endfunction() + +# Run find package with the requested configuration: +if (PACKAGE_TYPE STREQUAL "CCCL") + if (COMPONENTS STREQUAL "DEFAULT") + do_find_package(CCCL "${CCCL_ROOT}") + else() + do_find_package(CCCL "${CCCL_ROOT}" COMPONENTS ${COMPONENTS}) + endif() +elseif(PACKAGE_TYPE STREQUAL "NATIVE") + if (COMPONENTS STREQUAL "DEFAULT") + message(FATAL_ERROR "COMPONENTS=DEFAULT incompatible with PACKAGE_TYPE=NATIVE") + endif() + foreach (component IN LISTS COMPONENTS) + do_find_package(${component} "${${component}_ROOT}") + endforeach() +elseif(PACKAGE_TYPE STREQUAL "SUBDIR") + if (COMPONENTS STREQUAL "DEFAULT") + set(CCCL_REQUIRED_COMPONENTS) + else() + set(CCCL_REQUIRED_COMPONENTS ${COMPONENTS}) + endif() + add_subdirectory("${CCCL_ROOT}" "${CMAKE_CURRENT_BINARY_DIR}/subdir") +else() + message(FATAL_ERROR "Invalid PACKAGE_TYPE: ${PACKAGE_TYPE}") +endif() + +if (COMPONENTS STREQUAL "DEFAULT") + set(COMPONENTS libcudacxx CUB Thrust) +endif() + +foreach (component IN LISTS COMPONENTS) + set(test_target version_check.${component}) + set(component_target "${component}::${component}") + add_executable(${test_target} version_check.cxx) + target_link_libraries(${test_target} PRIVATE ${component_target}) + add_test(NAME ${test_target} COMMAND ${test_target}) + + if (component STREQUAL "libcudacxx") + find_package(libcudacxx) # Make sure version vars are in scope + # TODO Should this be a genex? Will these stay correct as versions change? + math(EXPR component_cmake_version + "(${libcudacxx_VERSION_MAJOR} * 1000000) + + ${libcudacxx_VERSION_MINOR} * 1000 + + ${libcudacxx_VERSION_PATCH}") + target_compile_definitions(${test_target} PRIVATE + "VERSION_HEADER=cuda/std/version" + "VERSION_MACRO=_LIBCUDACXX_CUDA_API_VERSION" + "EXPECTED_VERSION=${component_cmake_version}") + elseif (component STREQUAL "CUB") + find_package(CUB) # Make sure version vars are in scope + # TODO Should this be a genex? Will these stay correct as versions change? + math(EXPR component_cmake_version + "(${CUB_VERSION_MAJOR} * 100000) + + ${CUB_VERSION_MINOR} * 100 + + ${CUB_VERSION_PATCH}") + target_compile_definitions(${test_target} PRIVATE + "VERSION_HEADER=cub/version.cuh" + "VERSION_MACRO=CUB_VERSION" + "EXPECTED_VERSION=${component_cmake_version}") + elseif (component STREQUAL "Thrust") + find_package(Thrust) # Make sure version vars are in scope + # TODO Should this be a genex? Will these stay correct as versions change? + math(EXPR component_cmake_version + "(${THRUST_VERSION_MAJOR} * 100000) + + ${THRUST_VERSION_MINOR} * 100 + + ${THRUST_VERSION_PATCH}") + target_compile_definitions(${test_target} PRIVATE + "VERSION_HEADER=thrust/version.h" + "VERSION_MACRO=THRUST_VERSION" + "EXPECTED_VERSION=${component_cmake_version}") + else() + message(FATAL_ERROR "Valid COMPONENTS are (case-sensitive): Thrust;CUB;libcudacxx") + endif() +endforeach() diff --git a/test/cmake/test_export/version_check.cxx b/test/cmake/test_export/version_check.cxx new file mode 100644 index 00000000000..c09e8d9cd1f --- /dev/null +++ b/test/cmake/test_export/version_check.cxx @@ -0,0 +1,20 @@ +// Compile with: +// -DVERSION_HEADER=include/path/for/version.h +// -DEXPECTED_VERSION=XXYYZZ +// -DVERSION_MACRO=PROJECT_VERSION + +#define HEADER +#include HEADER + +#include + +#define DETECTED_VERSION VERSION_MACRO + +int main() +{ + printf("Expected version: %d\n" + "Detected version: %d\n", + EXPECTED_VERSION, + VERSION_MACRO); + return EXPECTED_VERSION == DETECTED_VERSION ? 0 : 1; +} diff --git a/thrust b/thrust new file mode 160000 index 00000000000..c1d4edd7fab --- /dev/null +++ b/thrust @@ -0,0 +1 @@ +Subproject commit c1d4edd7fabc48462525b3cfb40a1629d6af007a