Skip to content

Commit

Permalink
Extend rapids_export to support the concept of optional COMPONENTS
Browse files Browse the repository at this point in the history
rapids_export via the COMPONENTS and COMPONENTS_EXPORT_SET commands
can now generate find modules that have optional components.
  • Loading branch information
robertmaynard committed Jan 18, 2023
1 parent 05e915d commit 8a158d8
Show file tree
Hide file tree
Showing 11 changed files with 651 additions and 18 deletions.
80 changes: 80 additions & 0 deletions rapids-cmake/export/detail/component.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#=============================================================================
# Copyright (c) 2022-2023, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#=============================================================================
include_guard(GLOBAL)

#[=======================================================================[.rst:
rapids_export_component
-----------------------

.. versionadded:: v22.04.00

.. code-block:: cmake

Generate the necessary -Config.cmake modules needed for an optional component
of a rapids export package.

rapids_export_component( (BUILD|INSTALL) <project_name> <component_name> <export_set> <unique_name> <namespace>)

The :cmake:command:`rapids_export_component` function generates
the `<proj>-<unique_name>-targets.cmake` and `<proj>-<unique_name>-dependencies.cmake`
files necessary for :cmake:command:`rapids_export` to support optional
components.

.. note::
It is an anti-pattern to use this function directly. The vast majority of projects
would use :cmake:command:`rapids_export`


#]=======================================================================]
# cmake-lint: disable=W0105,R0913
function(rapids_export_component type project_name component_name export_set unique_name namespace)
list(APPEND CMAKE_MESSAGE_CONTEXT "rapids.export.rapids_export_component")

string(TOLOWER ${type} type)

set(deps_destination)
if(type STREQUAL "install")
include("${rapids-cmake-dir}/cmake/install_lib_dir.cmake")
rapids_cmake_install_lib_dir(install_location)
set(install_location "${install_location}/cmake/${project_name}")

set(deps_destination
"${PROJECT_BINARY_DIR}/rapids-cmake/${project_name}/export/${component_name}/")
file(MAKE_DIRECTORY "${deps_destination}")
install(DIRECTORY "${deps_destination}" DESTINATION "${install_location}"
COMPONENT ${component_name})
install(EXPORT ${export_set}
FILE ${project_name}-${unique_name}-targets.cmake
NAMESPACE ${namespace}
DESTINATION "${install_location}"
COMPONENT ${component_name})

else()
set(install_location "${PROJECT_BINARY_DIR}")
set(deps_destination "${install_location}/")

export(EXPORT ${export_set} NAMESPACE ${namespace}
FILE "${install_location}/${project_name}-${unique_name}-targets.cmake")

endif()

if(TARGET rapids_export_${type}_${export_set})
include("${rapids-cmake-dir}/export/write_dependencies.cmake")
rapids_export_write_dependencies(
${type} ${export_set} "${deps_destination}/${project_name}-${unique_name}-dependencies.cmake")
endif()

endfunction()
64 changes: 57 additions & 7 deletions rapids-cmake/export/export.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Generate a projects -Config.cmake module and all related information
rapids_export( (BUILD|INSTALL) <project_name>
EXPORT_SET <export_set>
[ GLOBAL_TARGETS <targets...> ]
[ COMPONENTS <components...> ]
[ COMPONENTS_EXPORT_SET <component 1 export set, component 2 export set...> ]
[ VERSION <X.Y.Z> ]
[ NAMESPACE <name_space> ]
[ DOCUMENTATION <doc_variable> ]
Expand All @@ -50,6 +52,30 @@ calls to :cmake:command:`find_dependency`, or :cmake:command:`CPMFindPackage`.
Explicitly list what targets should be made globally visible to
the consuming project.

``COMPONENTS``
.. versionadded:: v23.02.00
A list of the optional `COMPONENTS` that are offered by this exported
package. The names listed here will be what consumers calling
:cmake:command:`find_package` will use to enable these components.

For each entry in `COMPONENTS` it is required to have an entry in
`COMPONENTS_EXPORT_SET` at the same positional location.
-> COMPONENTS A B
-> COMPONENTS A-export B-export
This is needed so that :cmake:command:`rapids_export` can correctly
establish the dependency and import target information for each
component.


``COMPONENTS_EXPORT_SET``
.. versionadded:: v23.02.00

A list of the associated export set for each optional `COMPONENT`.

Each entry in `COMPONENTS_EXPORT_SET` is associated to the component
as the same position in the `COMPONENTS` list.


``VERSION``
Explicitly list the version of the package being exported. By
default :cmake:command:`rapids_export` uses the version specified by the
Expand Down Expand Up @@ -159,9 +185,13 @@ function(rapids_export type project_name)
list(APPEND CMAKE_MESSAGE_CONTEXT "rapids.export.export")
string(TOLOWER ${type} type)

set(project_name_orig ${project_name})
string(TOLOWER ${project_name} project_name)
string(TOUPPER ${project_name} project_name_uppercase)

set(options "")
set(one_value EXPORT_SET VERSION NAMESPACE DOCUMENTATION FINAL_CODE_BLOCK)
set(multi_value GLOBAL_TARGETS LANGUAGES)
set(multi_value GLOBAL_TARGETS COMPONENTS COMPONENTS_EXPORT_SET LANGUAGES)
cmake_parse_arguments(_RAPIDS "${options}" "${one_value}" "${multi_value}" ${ARGN})

set(rapids_version_set ON)
Expand All @@ -187,6 +217,25 @@ function(rapids_export type project_name)
set(_RAPIDS_PROJECT_NAMESPACE ${_RAPIDS_NAMESPACE})
endif()

if(_RAPIDS_COMPONENTS AND NOT _RAPIDS_COMPONENTS_EXPORT_SET)
message(FATAL_ERROR "rapids_export(${type} ${project_name} is missing COMPONENTS_EXPORT_SET as COMPONENTS was provided."
)
endif()
if(_RAPIDS_COMPONENTS_EXPORT_SET AND NOT _RAPIDS_COMPONENTS)
message(FATAL_ERROR "rapids_export(${type} ${project_name} is missing COMPONENTS as COMPONENTS_EXPORT_SET was provided."
)
endif()

include("${rapids-cmake-dir}/export/detail/component.cmake")
set(_RAPIDS_COMPONENTS_EXPORT_SET_NICE_NAMES)
foreach(comp comp_export_set IN ZIP_LISTS _RAPIDS_COMPONENTS _RAPIDS_COMPONENTS_EXPORT_SET)
string(REGEX REPLACE "(${project_name}[-_])|([-_]export[s]?)" "" nice_export_name
"${comp_export_set}")
rapids_export_component(${type} ${project_name} ${comp} ${comp_export_set} ${nice_export_name}
${_RAPIDS_PROJECT_NAMESPACE})
list(APPEND _RAPIDS_COMPONENTS_EXPORT_SET_NICE_NAMES ${nice_export_name})
endforeach()

set(_RAPIDS_PROJECT_DOCUMENTATION "Generated ${project_name}-config module")
if(DEFINED _RAPIDS_DOCUMENTATION)
if(NOT DEFINED ${_RAPIDS_DOCUMENTATION})
Expand All @@ -203,14 +252,12 @@ function(rapids_export type project_name)
endif()

# Write configuration and version files
string(TOLOWER ${project_name} project_name)
string(TOUPPER ${project_name} project_name_uppercase)
if(type STREQUAL "install")
include("${rapids-cmake-dir}/cmake/install_lib_dir.cmake")
rapids_cmake_install_lib_dir(install_location)
set(install_location "${install_location}/cmake/${project_name}")

set(scratch_dir "${PROJECT_BINARY_DIR}/rapids-cmake/${project_name}/export")
set(scratch_dir "${PROJECT_BINARY_DIR}/rapids-cmake/${project_name}/export/${project_name}")

configure_package_config_file("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/template/config.cmake.in"
"${scratch_dir}/${project_name}-config.cmake"
Expand All @@ -222,8 +269,11 @@ function(rapids_export type project_name)
COMPATIBILITY ${rapids_project_version_compat})
endif()

install(EXPORT ${_RAPIDS_EXPORT_SET} FILE ${project_name}-targets.cmake
NAMESPACE ${_RAPIDS_PROJECT_NAMESPACE} DESTINATION "${install_location}")
install(EXPORT ${_RAPIDS_EXPORT_SET}
FILE ${project_name}-targets.cmake
NAMESPACE ${_RAPIDS_PROJECT_NAMESPACE}
DESTINATION "${install_location}"
COMPONENT ${project_name})

if(TARGET rapids_export_install_${_RAPIDS_EXPORT_SET})
include("${rapids-cmake-dir}/export/write_dependencies.cmake")
Expand All @@ -240,7 +290,7 @@ function(rapids_export type project_name)
endif()

# Install everything we have generated
install(DIRECTORY "${scratch_dir}/" DESTINATION "${install_location}")
install(DIRECTORY "${scratch_dir}/" DESTINATION "${install_location}" COMPONENT ${project_name})

else()
set(install_location "${PROJECT_BINARY_DIR}")
Expand Down
34 changes: 24 additions & 10 deletions rapids-cmake/export/template/config.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,29 @@ foreach(lang IN LISTS rapids_global_languages)
endforeach()
unset(rapids_global_languages)

if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
endif()
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]" OPTIONAL)

# The same component can be listed multiple times in `rapids_comp_names` so that we
# can support multiple export sets being mapped to the same component
set(rapids_comp_names @_RAPIDS_COMPONENTS@)
set(rapids_comp_unique_ids @_RAPIDS_COMPONENTS_EXPORT_SET_NICE_NAMES@)
foreach(comp unique_name IN ZIP_LISTS rapids_comp_names rapids_comp_unique_ids)
# find dependencies before creating targets that use them
# this way if a dependency can't be found we fail
if(${comp} IN_LIST @project_name@_FIND_COMPONENTS)
include("${CMAKE_CURRENT_LIST_DIR}/@project_name@-${unique_name}-dependencies.cmake" OPTIONAL)
endif()
endforeach()

if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
endif()
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]" OPTIONAL)
foreach(comp unique_name IN ZIP_LISTS rapids_comp_names rapids_comp_unique_ids)
if(${comp} IN_LIST @project_name@_FIND_COMPONENTS)
include("${CMAKE_CURRENT_LIST_DIR}/@project_name@-${unique_name}-targets.cmake" OPTIONAL)
set(@project_name@_${comp}_FOUND TRUE)
endif()
endforeach()

if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
endif()
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]" OPTIONAL)

# Set our version variables
set(@project_name_uppercase@_VERSION_MAJOR @rapids_orig_major_version@)
Expand All @@ -62,7 +74,7 @@ set(@project_name_uppercase@_VERSION @rapids_orig_version@)
set(rapids_global_targets @_RAPIDS_GLOBAL_TARGETS@)
set(rapids_namespaced_global_targets @_RAPIDS_GLOBAL_TARGETS@)
if(rapids_namespaced_global_targets)
list(TRANSFORM rapids_namespaced_global_targets PREPEND @_RAPIDS_NAMESPACE@ )
list(TRANSFORM rapids_namespaced_global_targets PREPEND @_RAPIDS_PROJECT_NAMESPACE@ )
endif()

foreach(target IN LISTS rapids_namespaced_global_targets)
Expand Down Expand Up @@ -91,6 +103,8 @@ if("rapids_config_@type@" STREQUAL "rapids_config_build")
endforeach()
endif()

unset(rapids_comp_names)
unset(rapids_comp_unique_ids)
unset(rapids_global_targets)
unset(rapids_namespaced_global_targets)

Expand Down
10 changes: 9 additions & 1 deletion testing/export/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#=============================================================================
# Copyright (c) 2021, NVIDIA CORPORATION.
# Copyright (c) 2021-2023, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -20,12 +20,18 @@ add_cmake_config_test( export_cpm-build.cmake )
add_cmake_config_test( export_cpm-install.cmake )
add_cmake_config_test( export_cpm-options-escaped.cmake )

add_cmake_build_test( export-verify-build-components-dependencies )
add_cmake_build_test( export-verify-build-components-targets )
add_cmake_build_test( export-verify-build-multiple-export-sets-to-single-component )
add_cmake_build_test( export-verify-build-namespaces )
add_cmake_build_test( export-verify-code-block )
add_cmake_build_test( export-verify-doc-string )
add_cmake_build_test( export-verify-global-targets )
add_cmake_build_test( export-verify-install-components )

add_cmake_config_test( export-verify-bad-code-block-var.cmake SHOULD_FAIL "FINAL_CODE_BLOCK variable `var_doesn't_exist` doesn't exist")
add_cmake_config_test( export-verify-missing-components.cmake SHOULD_FAIL "is missing COMPONENTS as COMPONENTS_EXPORT_SET was provided")
add_cmake_config_test( export-verify-missing-components-export.cmake SHOULD_FAIL "is missing COMPONENTS_EXPOR22T_SET as COMPONENTS was provided")
add_cmake_config_test( export-verify-bad-doc-var.cmake SHOULD_FAIL "DOCUMENTATION variable `var_doesn't_exist` doesn't exist")
add_cmake_config_test( export-verify-calendar-version-matching.cmake )
add_cmake_config_test( export-verify-explicit-disabled-version.cmake )
Expand All @@ -38,6 +44,8 @@ add_cmake_config_test( export-verify-implicit-disabled-version.cmake )
add_cmake_config_test( export-verify-implicit-major-version-only-matching.cmake )
add_cmake_config_test( export-verify-version.cmake )

add_cmake_config_test( export_component-build )

add_cmake_config_test( export_package-build-possible-dir.cmake )
add_cmake_config_test( export_package-build-with-components.cmake )
add_cmake_config_test( export_package-build-with-components-and-version.cmake )
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#=============================================================================
# Copyright (c) 2023, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#=============================================================================
include(${rapids-cmake-dir}/export/export.cmake)

cmake_minimum_required(VERSION 3.20)
project(FakEProJecT LANGUAGES CXX VERSION 3.1.4)

add_library(fakeLib INTERFACE)
install(TARGETS fakeLib EXPORT fake_set)

add_library(fakeLib_c1 INTERFACE)
install(TARGETS fakeLib_c1 EXPORT fake_set_c1_export)

include(${rapids-cmake-dir}/export/package.cmake)
rapids_export_package( build CUDAToolkit fake_set_c1_export)

rapids_export(BUILD FakEProJecT
EXPORT_SET fake_set
COMPONENTS c1
COMPONENTS_EXPORT_SET fake_set_c1_export
LANGUAGES CXX
NAMESPACE test::
)

# Add a custom command that generates a second project
# that verifies our component targetd are exported correctly
file(WRITE "${CMAKE_BINARY_DIR}/verify-1/CMakeLists.txt" [=[
cmake_minimum_required(VERSION 3.20)
project(verify_build_targets LANGUAGES CXX)

set(components c1)
set(export_sets fake_set_c1)
foreach(comp exp_set IN ZIP_LISTS components export_sets)
message(STATUS "${CMAKE_CURRENT_LIST_DIR}/../fakeproject-${exp_set}-dependencies.cmake")
if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/../fakeproject-${exp_set}-dependencies.cmake")
message(FATAL_ERROR "rapids_export failed to generate correct component dependencies file name[fakeproject-${exp_set}-dependencies.cmake]")
endif()

include("${CMAKE_CURRENT_LIST_DIR}/../fakeproject-${exp_set}-dependencies.cmake")
if(NOT TARGET CUDA::toolkit)
message(FATAL_ERROR "rapids_export failed to generate correct dependencies imports[fakeproject-${exp_set}-dependencies.cmake]")
endif()
endforeach()
]=])

add_custom_target(verify_target_dependencies_files ALL
COMMAND ${CMAKE_COMMAND} -E rm -rf "${CMAKE_BINARY_DIR}/verify-1/build"
COMMAND ${CMAKE_COMMAND} -S="${CMAKE_BINARY_DIR}/verify-1" -B="${CMAKE_BINARY_DIR}/verify-1/build"
)

# Add a custom command that generates a second project
# that verifies our component targetd are exported correctly
file(WRITE "${CMAKE_BINARY_DIR}/verify-2/CMakeLists.txt" [=[
cmake_minimum_required(VERSION 3.20)
project(verify_build_targets LANGUAGES CXX)

set(CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../)
find_package(fakeproject COMPONENTS c1 REQUIRED)
if(NOT TARGET CUDA::toolkit)
message(FATAL_ERROR "rapids_export failed to generate correct dependencies imports")
endif()
]=])

add_custom_target(verify_target_dependencies_find ALL
COMMAND ${CMAKE_COMMAND} -E rm -rf "${CMAKE_BINARY_DIR}/verify-2/build"
COMMAND ${CMAKE_COMMAND} -S="${CMAKE_BINARY_DIR}/verify-2" -B="${CMAKE_BINARY_DIR}/verify-2/build"
)
Loading

0 comments on commit 8a158d8

Please sign in to comment.