From 272d6441938d0ec82ddd4a53780e2cc28b042a1c Mon Sep 17 00:00:00 2001 From: Joseph Mirabel Date: Thu, 18 Jun 2020 12:20:15 +0200 Subject: [PATCH] Allow components in Config.cmake. (#396) * Allow components in Config.cmake. * Add macro PROJECT_INSTALL_COMPONENT * Update doc. * Add missing include * Add optional argument NAMESPACE to PROJECT_INSTALL_COMPONENT --- .docs/pages/developers.rst | 28 ++++++++++++++-- Config.cmake.in | 31 ++++++++++++++++-- componentConfig.cmake.in | 23 +++++++++++++ package-config.cmake | 66 +++++++++++++++++++++++++++++++------- 4 files changed, 132 insertions(+), 16 deletions(-) create mode 100644 componentConfig.cmake.in diff --git a/.docs/pages/developers.rst b/.docs/pages/developers.rst index 7539014fb..e7bbe5678 100644 --- a/.docs/pages/developers.rst +++ b/.docs/pages/developers.rst @@ -45,6 +45,18 @@ To document a macro or a variable in a ``.cmake`` file, use # Variable details details (Mind the empty line before). # Related to :cmake:command:`HOW_TO_DOCUMENT_A_COMMAND`. # + # A code block:: + # + # notice the double : and the indention + # + # or the more verbose, but supports other language + # + # .. code-block:: bash + # + # make html + # # Open build/html/index.html to see the doc + # firefox build/html/index.html + # # .. command:: HOW_TO_DOCUMENT_A_COMMAND(argname) # # Macro details (Mind the empty line before). @@ -58,8 +70,20 @@ gives .. variable:: HOW_TO_DOCUMENT_A_VARIABLE - Variable details (Mind the empty line before). - Related to :cmake:command:`HOW_TO_DOCUMENT_A_COMMAND`. + Variable details (Mind the empty line before). + Related to :cmake:command:`HOW_TO_DOCUMENT_A_COMMAND`. + + A cmake code block:: + + set(foo "notice the double : and the indention") + + or the more verbose, but supports other language + + .. code-block:: bash + + make html + # Open build/html/index.html to see the doc + firefox build/html/index.html .. command:: HOW_TO_DOCUMENT_A_COMMAND(argname) diff --git a/Config.cmake.in b/Config.cmake.in index 76c34cd55..ad10f7c87 100644 --- a/Config.cmake.in +++ b/Config.cmake.in @@ -1,6 +1,17 @@ @PACKAGE_INIT@ -if(@PROJECT_NAME@_FOUND) +set(skip_this_file TRUE) +if(NOT @PROJECT_NAME@_FOUND) + set(skip_this_file FALSE) +endif() +if(skip_this_file) + foreach(component ${@PROJECT_NAME@_FIND_COMPONENTS}) + if(NOT "@PROJECT_NAME@_${component}_FOUND") + set(skip_this_file FALSE) + endif() + endforeach() +endif() +if(skip_this_file) return() endif() @@ -82,9 +93,9 @@ set("@PROJECT_NAME_UPPER@_LIBRARIES" ${_PACKAGE_CONFIG_LIBRARIES}) include(CMakeFindDependencyMacro) if(${CMAKE_VERSION} VERSION_LESS "3.15.0") -@PACKAGE_DEPENDENCIES_FIND_PACKAGE@ + @PACKAGE_DEPENDENCIES_FIND_PACKAGE@ else() -@PACKAGE_DEPENDENCIES_FIND_DEPENDENCY@ + @PACKAGE_DEPENDENCIES_FIND_DEPENDENCY@ endif() IF(COMMAND ADD_REQUIRED_DEPENDENCY) @@ -98,6 +109,20 @@ IF(COMMAND ADD_REQUIRED_DEPENDENCY) ENDIF(COMMAND ADD_REQUIRED_DEPENDENCY) include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") + +foreach(component ${@PROJECT_NAME@_FIND_COMPONENTS}) + set(comp_file "${CMAKE_CURRENT_LIST_DIR}/${component}Config.cmake") + if(EXISTS ${comp_file}) + include(${comp_file}) + else() + set("@PROJECT_NAME@_${component}_FOUND" FALSE) + endif() + if("@PROJECT_NAME@_${component}_FOUND") + message(STATUS "@PROJECT_NAME@: ${component} found.") + else() + message(STATUS "@PROJECT_NAME@: ${component} not found.") + endif() +endforeach() check_required_components("@PROJECT_NAME@") @PACKAGE_EXTRA_MACROS@ diff --git a/componentConfig.cmake.in b/componentConfig.cmake.in new file mode 100644 index 000000000..5f9bba1f6 --- /dev/null +++ b/componentConfig.cmake.in @@ -0,0 +1,23 @@ +@PACKAGE_INIT@ + +if(NOT @PROJECT_NAME@_FOUND) + set(@PROJECT_NAME@_@COMPONENT@_FOUND FALSE) + return() +endif() + +# At the moment, components only support targets +#set("@PROJECT_NAME@_@COMPONENT@_INCLUDE_DIRS" "@CMAKE_INSTALL_FULL_INCLUDEDIR@") +#set("@PROJECT_NAME@_@COMPONENT@_LIBRARIES" ${_PACKAGE_CONFIG_LIBRARIES}) +set(@PROJECT_NAME@_@COMPONENT@_DEPENDENCIES "@_PACKAGE_CONFIG_COMPONENT_DEPENDENCIES_PROJECTS@") + +include(CMakeFindDependencyMacro) +if(${CMAKE_VERSION} VERSION_LESS "3.15.0") + @COMPONENT_FIND_PACKAGE@ +else() + @COMPONENT_FIND_DEPENDENCY@ +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/@COMPONENT@Targets.cmake") +set(@PROJECT_NAME@_@COMPONENT@_FOUND TRUE) + +@COMPONENT_EXTRA_MACRO@ diff --git a/package-config.cmake b/package-config.cmake index b89d7038a..8d7ae8349 100644 --- a/package-config.cmake +++ b/package-config.cmake @@ -53,7 +53,7 @@ set(PACKAGE_EXTRA_MACROS "" CACHE INTERNAL "") ENDMACRO(_SETUP_PROJECT_PACKAGE_INIT) #.rst: -# .. command:: ADD_PROJECT_DEPENDENCY(ARGS [PKG_CONFIG_REQUIRES pkg]) +# .. command:: ADD_PROJECT_DEPENDENCY(ARGS [PKG_CONFIG_REQUIRES pkg] [FOR_COMPONENT component]) # # This is a wrapper around find_package to add correct find_dependency calls in # the generated config script. All arguments are passed to find_package. @@ -66,25 +66,27 @@ ENDMACRO(_SETUP_PROJECT_PACKAGE_INIT) # section of the generated .pc file # MACRO(ADD_PROJECT_DEPENDENCY) - list(APPEND _PACKAGE_CONFIG_DEPENDENCIES_PROJECTS "${ARGV0}") - # add dependency to the generated .pc # ref https://github.com/jrl-umi3218/jrl-cmakemodules/pull/335 - cmake_parse_arguments(PARSED_ARGN "" "PKG_CONFIG_REQUIRES" "" ${ARGN}) + cmake_parse_arguments(PARSED_ARGN "" "PKG_CONFIG_REQUIRES;FOR_COMPONENT" "" ${ARGN}) IF(PARSED_ARGN_PKG_CONFIG_REQUIRES) _ADD_TO_LIST_IF_NOT_PRESENT(_PKG_CONFIG_REQUIRES "${PARSED_ARGN_PKG_CONFIG_REQUIRES}") _ADD_TO_LIST_IF_NOT_PRESENT(_PKG_CONFIG_DEP_NOT_FOR_CONFIG_CMAKE "${PARSED_ARGN_PKG_CONFIG_REQUIRES}") ENDIF() + if(PARSED_ARGN_FOR_COMPONENT) + set(component "_${PARSED_ARGN_FOR_COMPONENT}") + endif(PARSED_ARGN_FOR_COMPONENT) + _ADD_TO_LIST_IF_NOT_PRESENT(APPEND "_PACKAGE_CONFIG${component}_DEPENDENCIES_PROJECTS" "${ARGV0}") string(REPLACE ";" " " PACKAGE_ARGS "${PARSED_ARGN_UNPARSED_ARGUMENTS}") - list(APPEND _PACKAGE_CONFIG_DEPENDENCIES_FIND_PACKAGE " find_package(${PACKAGE_ARGS})") - list(APPEND _PACKAGE_CONFIG_DEPENDENCIES_FIND_DEPENDENCY " find_dependency(${PACKAGE_ARGS})") + _ADD_TO_LIST_IF_NOT_PRESENT("_PACKAGE_CONFIG${component}_DEPENDENCIES_FIND_PACKAGE" "find_package(${PACKAGE_ARGS})") + _ADD_TO_LIST_IF_NOT_PRESENT("_PACKAGE_CONFIG${component}_DEPENDENCIES_FIND_DEPENDENCY" "find_dependency(${PACKAGE_ARGS})") find_package(${PARSED_ARGN_UNPARSED_ARGUMENTS}) # Propagate variables changes to the cached values - set(_PACKAGE_CONFIG_DEPENDENCIES_PROJECTS "${_PACKAGE_CONFIG_DEPENDENCIES_PROJECTS}" CACHE INTERNAL "") - set(_PACKAGE_CONFIG_DEPENDENCIES_FIND_PACKAGE "${_PACKAGE_CONFIG_DEPENDENCIES_FIND_PACKAGE}" CACHE INTERNAL "") - set(_PACKAGE_CONFIG_DEPENDENCIES_FIND_DEPENDENCY "${_PACKAGE_CONFIG_DEPENDENCIES_FIND_DEPENDENCY}" CACHE INTERNAL "") + set(_PACKAGE_CONFIG${component}_DEPENDENCIES_PROJECTS "${_PACKAGE_CONFIG${component}_DEPENDENCIES_PROJECTS}" CACHE INTERNAL "") + set(_PACKAGE_CONFIG${component}_DEPENDENCIES_FIND_PACKAGE "${_PACKAGE_CONFIG${component}_DEPENDENCIES_FIND_PACKAGE}" CACHE INTERNAL "") + set(_PACKAGE_CONFIG${component}_DEPENDENCIES_FIND_DEPENDENCY "${_PACKAGE_CONFIG${component}_DEPENDENCIES_FIND_DEPENDENCY}" CACHE INTERNAL "") ENDMACRO() @@ -130,8 +132,8 @@ string(REGEX REPLACE "[^a-zA-Z0-9]" "_" PROJECT_NAME_UPPER "${PROJECT_NAME_UPPER # Include module with fuction 'write_basic_package_version_file' include(CMakePackageConfigHelpers) -string(REPLACE ";" "\n" PACKAGE_DEPENDENCIES_FIND_PACKAGE "${_PACKAGE_CONFIG_DEPENDENCIES_FIND_PACKAGE}") -string(REPLACE ";" "\n" PACKAGE_DEPENDENCIES_FIND_DEPENDENCY "${_PACKAGE_CONFIG_DEPENDENCIES_FIND_DEPENDENCY}") +string(REPLACE ";" "\n " PACKAGE_DEPENDENCIES_FIND_PACKAGE "${_PACKAGE_CONFIG_DEPENDENCIES_FIND_PACKAGE}") +string(REPLACE ";" "\n " PACKAGE_DEPENDENCIES_FIND_DEPENDENCY "${_PACKAGE_CONFIG_DEPENDENCIES_FIND_DEPENDENCY}") # Configure 'ConfigVersion.cmake' # Note: PROJECT_VERSION is used as a VERSION @@ -174,3 +176,45 @@ install( DESTINATION "${CONFIG_INSTALL_DIR}" ) ENDMACRO(SETUP_PROJECT_PACKAGE_FINALIZE) + +#.rst: +# .. command:: PROJECT_INSTALL_COMPONENT(COMPONENT [EXTRA_MACRO cmake_code] [NAMESPACE namespace]) +# +# Generates CMake componentConfig.cmake and Targets files so users can call:: +# +# find_package(MyPackage COMPONENT component) +# +# :param EXTRA_MACRO: optional argument. `cmake_code` will be appended to +# the generated *Config.cmake*. +# :param NAMESPACE: optional argument. Defaults to `${PROJECT_NAME}::`. +# +macro(PROJECT_INSTALL_COMPONENT COMPONENT) + cmake_parse_arguments(PARSED_ARGN "" "NAMESPACE;EXTRA_MACRO" "" ${ARGN}) + if(PARSED_ARGN_NAMESPACE) + set(namespace "${PARSED_ARGN_NAMESPACE}") + else() + set(namespace "${PROJECT_NAME}::") + endif() + + install(EXPORT ${COMPONENT}Targets + NAMESPACE "${namespace}" + DESTINATION "${CONFIG_INSTALL_DIR}") + + set(COMPONENT ${COMPONENT}) + set(_PACKAGE_CONFIG_COMPONENT_DEPENDENCIES_PROJECTS "${_PACKAGE_CONFIG_${COMPONENT}_DEPENDENCIES_PROJECTS}") + string(REPLACE ";" "\n " COMPONENT_FIND_PACKAGE + "${_PACKAGE_CONFIG_${COMPONENT}_DEPENDENCIES_FIND_PACKAGE}") + string(REPLACE ";" "\n " COMPONENT_FIND_DEPENDENCY + "${_PACKAGE_CONFIG_${COMPONENT}_DEPENDENCIES_FIND_DEPENDENCY}") + set(COMPONENT_CONFIG "${CMAKE_BINARY_DIR}/generated/${COMPONENT}Config.cmake") + set(COMPONENT_EXTRA_MACRO "${PARSED_ARGN_EXTRA_MACRO}") + include(CMakePackageConfigHelpers) + configure_package_config_file( + "${CMAKE_SOURCE_DIR}/cmake/componentConfig.cmake.in" + "${COMPONENT_CONFIG}" + INSTALL_DESTINATION "${CONFIG_INSTALL_DIR}" + NO_CHECK_REQUIRED_COMPONENTS_MACRO + NO_SET_AND_CHECK_MACRO) + install(FILES "${COMPONENT_CONFIG}" + DESTINATION "${CONFIG_INSTALL_DIR}") +endmacro()