Skip to content
This repository has been archived by the owner on Mar 21, 2024. It is now read-only.

Add find_package and add_subdirectory CMake support. #242

Merged
merged 1 commit into from
Feb 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ compare_git_to_perforce.bash
.clangd/
.cache
compile_commands.json
*~
27 changes: 27 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,33 @@
cmake_minimum_required(VERSION 3.8)
project(libcudacxx CXX)

# Determine whether libcudacxx is the top-level project or included into
# another project via add_subdirectory().
if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_LIST_DIR}")
set(libcudacxx_TOPLEVEL_PROJECT ON)
else()
set(libcudacxx_TOPLEVEL_PROJECT OFF)
endif()

include(cmake/libcudacxxInstallRules.cmake)
robertmaynard marked this conversation as resolved.
Show resolved Hide resolved

if (NOT libcudacxx_TOPLEVEL_PROJECT)
include(cmake/libcudacxxAddSubdir.cmake)
return()
endif()

# Note that this currently returns and skips the rest of the build
# system.
option(libcudacxx_ENABLE_CMAKE_TESTS "Enable ctest-based testing." OFF)
if (libcudacxx_ENABLE_CMAKE_TESTS)
# Might be able to lower this, but would need to do some testing:
cmake_minimum_required(VERSION 3.20.1)
include(CTest)
enable_testing() # Must be called in root CMakeLists.txt
add_subdirectory(cmake/test/)
return()
endif()

set(PACKAGE_NAME libcudacxx)
set(PACKAGE_VERSION 11.0)
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
Expand Down
8 changes: 8 additions & 0 deletions cmake/libcudacxxAddSubdir.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This effectively does a `find_package` actually going through the find_package
# machinery. Using `find_package` works for the first configure, but creates
# inconsistencies during subsequent configurations when using CPM..
#
# More details are in the discussion at
# https://github.com/NVIDIA/libcudacxx/pull/242#discussion_r794003857
include(${libcudacxx_SOURCE_DIR}/lib/cmake/libcudacxx/libcudacxx-config-version.cmake)
include(${libcudacxx_SOURCE_DIR}/lib/cmake/libcudacxx/libcudacxx-config.cmake)
35 changes: 35 additions & 0 deletions cmake/libcudacxxInstallRules.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
option(libcudacxx_ENABLE_INSTALL_RULES
"Enable installation of libcudacxx" ${libcudacxx_TOPLEVEL_PROJECT}
)

if (NOT libcudacxx_ENABLE_INSTALL_RULES)
return()
endif()

# Bring in CMAKE_INSTALL_LIBDIR
include(GNUInstallDirs)

# Libcudacxx headers
install(DIRECTORY "${libcudacxx_SOURCE_DIR}/include/cuda"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
install(DIRECTORY "${libcudacxx_SOURCE_DIR}/include/nv"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)

# Libcudacxx cmake package
install(DIRECTORY "${libcudacxx_SOURCE_DIR}/lib/cmake/libcudacxx"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake"
PATTERN libcudacxx-header-search EXCLUDE
)

# Need to configure a file to store CMAKE_INSTALL_INCLUDEDIR
# since it can be defined by the user. This is common to work around collisions
# with the CTK installed headers.
configure_file("${libcudacxx_SOURCE_DIR}/lib/cmake/libcudacxx/libcudacxx-header-search.cmake.in"
"${libcudacxx_BINARY_DIR}/lib/cmake/libcudacxx/libcudacxx-header-search.cmake"
@ONLY
)
install(FILES "${libcudacxx_BINARY_DIR}/lib/cmake/libcudacxx/libcudacxx-header-search.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/libcudacxx"
)
75 changes: 75 additions & 0 deletions cmake/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
if ("MSVC" STREQUAL "${CMAKE_CXX_COMPILER_ID}")
# There's a bug that prevents build-and-test from working on MSVC.
# See NVIDIA/nvbench#43.
return()
endif()

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(libcudacxx_add_compile_test full_test_name_var subdir test_id)
set(test_name libcudacxx.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()

################################################################################
# Test against source dir

libcudacxx_add_compile_test(test_name
test_export
source_tree
-D "libcudacxx_DIR=${libcudacxx_SOURCE_DIR}/lib/cmake/libcudacxx/"
-D TEST_TYPE=SOURCE_TREE
)

################################################################################
# Test against install tree

libcudacxx_add_compile_test(test_name
test_export
install_tree
-D "libcudacxx_DIR=${tmp_install_prefix}/lib/cmake/libcudacxx/"
-D TEST_TYPE=INSTALL_TREE
)
set_tests_properties(${test_name} PROPERTIES FIXTURES_REQUIRED install_tree)

################################################################################
# Install tree fixtures
add_test(NAME libcudacxx.test.cmake.install_tree.install
COMMAND "${CMAKE_COMMAND}"
--install "${libcudacxx_BINARY_DIR}"
--prefix "${tmp_install_prefix}"
)
set_tests_properties(libcudacxx.test.cmake.install_tree.install PROPERTIES
FIXTURES_SETUP install_tree
)

add_test(NAME libcudacxx.test.cmake.install_tree.cleanup
COMMAND "${CMAKE_COMMAND}" -E rm -rf "${tmp_install_prefix}"
)
set_tests_properties(libcudacxx.test.cmake.install_tree.cleanup PROPERTIES
FIXTURES_CLEANUP install_tree
)
21 changes: 21 additions & 0 deletions cmake/test/test_export/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.20.1)
project(libcudacxxTestExport CXX)

message(STATUS "libcudacxx_DIR=${libcudacxx_DIR}")
find_package(libcudacxx REQUIRED)

add_executable(version_check version_check.cxx)
target_link_libraries(version_check PRIVATE libcudacxx::libcudacxx)
enable_testing()
add_test(NAME version_check COMMAND "$<TARGET_FILE:version_check>")
set_property(TEST version_check PROPERTY
PASS_REGULAR_EXPRESSION
"${libcudacxx_VERSION_MAJOR}\.${libcudacxx_VERSION_MINOR}\.${libcudacxx_VERSION_PATCH}"
)

# Install tree exporting of version_check_objs
install(TARGETS version_check EXPORT test-targets)
install(EXPORT test-targets FILE test-targets.cmake DESTINATION "lib/cmake/libcudacxx-test/")

# Build tree exporting of version_check_objs
export(EXPORT test-targets FILE "${CMAKE_CURRENT_BINARY_DIR}/libcudacxx-test/test-targets.cmake")
15 changes: 15 additions & 0 deletions cmake/test/test_export/version_check.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <cuda/std/atomic>

#include <cstdio>

int main()
{
cuda::std::atomic<int> x{0};

printf("Built with libcudacxx version %d.%d.%d.\n",
_LIBCUDACXX_CUDA_API_VERSION_MAJOR,
_LIBCUDACXX_CUDA_API_VERSION_MINOR,
_LIBCUDACXX_CUDA_API_VERSION_PATCH);

return x;
}
36 changes: 36 additions & 0 deletions lib/cmake/libcudacxx/libcudacxx-config-version.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Parse version information from version header:
include("${CMAKE_CURRENT_LIST_DIR}/libcudacxx-header-search.cmake")

file(READ "${_libcudacxx_VERSION_INCLUDE_DIR}/cuda/std/detail/__config"
libcudacxx_VERSION_HEADER
)

string(REGEX MATCH
"#define[ \t]+_LIBCUDACXX_CUDA_API_VERSION[ \t]+([0-9]+)" unused_var
"${libcudacxx_VERSION_HEADER}"
)

set(libcudacxx_VERSION_FLAT ${CMAKE_MATCH_1})
math(EXPR libcudacxx_VERSION_MAJOR "${libcudacxx_VERSION_FLAT} / 1000000")
math(EXPR libcudacxx_VERSION_MINOR "(${libcudacxx_VERSION_FLAT} / 1000) % 1000")
math(EXPR libcudacxx_VERSION_PATCH "${libcudacxx_VERSION_FLAT} % 1000")
set(libcudacxx_VERSION_TWEAK 0)

set(libcudacxx_VERSION
"${libcudacxx_VERSION_MAJOR}.${libcudacxx_VERSION_MINOR}.${libcudacxx_VERSION_PATCH}.${libcudacxx_VERSION_TWEAK}"
)

set(PACKAGE_VERSION ${libcudacxx_VERSION})
set(PACKAGE_VERSION_COMPATIBLE FALSE)
set(PACKAGE_VERSION_EXACT FALSE)
set(PACKAGE_VERSION_UNSUITABLE FALSE)

if(PACKAGE_VERSION VERSION_GREATER_EQUAL PACKAGE_FIND_VERSION)
if(PACKAGE_FIND_VERSION_MAJOR VERSION_EQUAL libcudacxx_VERSION_MAJOR)
set(PACKAGE_VERSION_COMPATIBLE TRUE)
endif()

if(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()
58 changes: 58 additions & 0 deletions lib/cmake/libcudacxx/libcudacxx-config.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#
# find_package(libcudacxx) config file.
#
# Defines a libcudacxx::libcudacxx target that may be linked from user projects to include
# libcudacxx.

if (TARGET libcudacxx::libcudacxx)
return()
endif()

function(_libcudacxx_declare_interface_alias alias_name ugly_name)
# 1) Only IMPORTED and ALIAS targets can be placed in a namespace.
# 2) When an IMPORTED library is linked to another target, its include
# directories are treated as SYSTEM includes.
# 3) nvcc will automatically check the CUDA Toolkit include path *before* the
# system includes. This means that the Toolkit libcudacxx will *always* be used
# during compilation, and the include paths of an IMPORTED libcudacxx::libcudacxx
# target will never have any effect.
# 4) This behavior can be fixed by setting the property NO_SYSTEM_FROM_IMPORTED
# on EVERY target that links to libcudacxx::libcudacxx. This would be a burden and a
# footgun for our users. Forgetting this would silently pull in the wrong libcudacxx!
# 5) A workaround is to make a non-IMPORTED library outside of the namespace,
# configure it, and then ALIAS it into the namespace (or ALIAS and then
# configure, that seems to work too).
add_library(${ugly_name} INTERFACE)
robertmaynard marked this conversation as resolved.
Show resolved Hide resolved

add_library(${alias_name} INTERFACE IMPORTED GLOBAL)
target_link_libraries(${alias_name} INTERFACE ${ugly_name})
endfunction()

#
# Setup targets
#

_libcudacxx_declare_interface_alias(libcudacxx::libcudacxx _libcudacxx_libcudacxx)
# Pull in the include dir detected by libcudacxx-config-version.cmake
set(_libcudacxx_INCLUDE_DIR "${_libcudacxx_VERSION_INCLUDE_DIR}"
CACHE INTERNAL "Location of libcudacxx headers."
)
unset(_libcudacxx_VERSION_INCLUDE_DIR CACHE) # Clear tmp variable from cache
target_include_directories(_libcudacxx_libcudacxx INTERFACE "${_libcudacxx_INCLUDE_DIR}")

#
# Standardize version info
#

set(LIBCUDACXX_VERSION ${${CMAKE_FIND_PACKAGE_NAME}_VERSION} CACHE INTERNAL "")
set(LIBCUDACXX_VERSION_MAJOR ${${CMAKE_FIND_PACKAGE_NAME}_VERSION_MAJOR} CACHE INTERNAL "")
set(LIBCUDACXX_VERSION_MINOR ${${CMAKE_FIND_PACKAGE_NAME}_VERSION_MINOR} CACHE INTERNAL "")
set(LIBCUDACXX_VERSION_PATCH ${${CMAKE_FIND_PACKAGE_NAME}_VERSION_PATCH} CACHE INTERNAL "")
set(LIBCUDACXX_VERSION_TWEAK ${${CMAKE_FIND_PACKAGE_NAME}_VERSION_TWEAK} CACHE INTERNAL "")
set(LIBCUDACXX_VERSION_COUNT ${${CMAKE_FIND_PACKAGE_NAME}_VERSION_COUNT} CACHE INTERNAL "")

include(FindPackageHandleStandardArgs)
if (NOT libcudacxx_CONFIG)
set(libcudacxx_CONFIG "${CMAKE_CURRENT_LIST_FILE}")
endif()
find_package_handle_standard_args(libcudacxx CONFIG_MODE)
8 changes: 8 additions & 0 deletions lib/cmake/libcudacxx/libcudacxx-header-search.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Parse version information from version header:
unset(_libcudacxx_VERSION_INCLUDE_DIR CACHE) # Clear old result to force search
find_path(_libcudacxx_VERSION_INCLUDE_DIR cuda/std/detail/__config
NO_DEFAULT_PATH # Only search explicit paths below:
PATHS
"${CMAKE_CURRENT_LIST_DIR}/../../../include" # Source tree
)
set_property(CACHE _libcudacxx_VERSION_INCLUDE_DIR PROPERTY TYPE INTERNAL)
8 changes: 8 additions & 0 deletions lib/cmake/libcudacxx/libcudacxx-header-search.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Parse version information from version header:
unset(_libcudacxx_VERSION_INCLUDE_DIR CACHE) # Clear old result to force search
find_path(_libcudacxx_VERSION_INCLUDE_DIR cuda/std/detail/__config
NO_DEFAULT_PATH # Only search explicit paths below:
PATHS
"${CMAKE_CURRENT_LIST_DIR}/../../../@CMAKE_INSTALL_INCLUDEDIR@" # Install tree
)
set_property(CACHE _libcudacxx_VERSION_INCLUDE_DIR PROPERTY TYPE INTERNAL)