#.rst: # C++ Headers # ----------- # # .. command:: _SETUP_PROJECT_HEADER # # This setup CMake to handle headers properly. # # 1. The `include` directory in the build and source trees is added to the # include search path (see INCLUDE_DIRECTORIES). As always, the build # directory has the priority over the source directory in case of conflict. # # However you *should not* have conflicting names for files which are both in # the build and source trees. Conflicting names are filenames which differ only # by a prefix: # # include/a.h vs _build/include/a.h src/a.h vs src/foo/a.h # # ...this files makes a project very fragile as the -I ordering will have a lot # of importance and may break easily when using tools which may reorder the # pre-processor flags such as pkg-config. # # 1. The headers are installed in the prefix in a way which preserves the # directory structure. # # The directory name for header follows the rule: each non alpha-numeric # character is replaced by a slash (`/`). In practice, it means that hpp-util # will put its header in: ${CMAKE_INSTALL_PREFIX}/include/hpp/util # # This rule has been decided to homogenize headers location, however some # packages do not follow this rule (dg-middleware for instance). # # In that case, CUSTOM_HEADER_DIR can be set to override this policy. # # Reminder: breaking the JRL/LAAS policies shoud be done after discussing the # issue. You should at least open a ticket or send an e-mail to notify this # behavior. # # Copyright (C) 2008-2024 LAAS-CNRS, JRL AIST-CNRS, INRIA. # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation, either version 3 of the License, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program. If not, see . # .rst: .. ifmode:: user # # .. variable:: ${PROJECT_NAME}_HEADERS # # List of C++ header filenames. They will be installed automatically using # :command:`HEADER_INSTALL` # macro(_SETUP_PROJECT_HEADER) # Install project headers. if(DEFINED PROJECT_CUSTOM_HEADER_DIR) set(HEADER_DIR "${PROJECT_CUSTOM_HEADER_DIR}") elseif(DEFINED CUSTOM_HEADER_DIR) set(HEADER_DIR "${CUSTOM_HEADER_DIR}") else() string(REGEX REPLACE "[^a-zA-Z0-9]" "/" HEADER_DIR "${PROJECT_NAME}") endif() if(NOT DEFINED PROJECT_CUSTOM_HEADER_EXTENSION) set(PROJECT_CUSTOM_HEADER_EXTENSION "hh") endif(NOT DEFINED PROJECT_CUSTOM_HEADER_EXTENSION) if(NOT DEFINED PROJECT_GENERATED_HEADERS_SKIP_CONFIG) set(PROJECT_GENERATED_HEADERS_SKIP_CONFIG OFF) endif() if(NOT DEFINED PROJECT_GENERATED_HEADERS_SKIP_DEPRECATED) set(PROJECT_GENERATED_HEADERS_SKIP_DEPRECATED OFF) endif() if(NOT DEFINED PROJECT_GENERATED_HEADERS_SKIP_WARNING) set(PROJECT_GENERATED_HEADERS_SKIP_WARNING OFF) endif() string(TOLOWER "${HEADER_DIR}" "HEADER_DIR") string(REGEX REPLACE "[^a-zA-Z0-9]" "_" PACKAGE_CPPNAME "${PROJECT_NAME}") string(TOLOWER "${PACKAGE_CPPNAME}" "PACKAGE_CPPNAME_LOWER") string(TOUPPER "${PACKAGE_CPPNAME}" "PACKAGE_CPPNAME") if(NOT PROJECT_GENERATED_HEADERS_SKIP_CONFIG) # Generate config.hh header. GENERATE_CONFIGURATION_HEADER( ${HEADER_DIR} config.${PROJECT_CUSTOM_HEADER_EXTENSION} ${PACKAGE_CPPNAME} ${PACKAGE_CPPNAME_LOWER}_EXPORTS ) endif() if(NOT PROJECT_GENERATED_HEADERS_SKIP_DEPRECATED) # Generate deprecated.hh header. configure_file( ${PROJECT_JRL_CMAKE_MODULE_DIR}/deprecated.hh.cmake ${CMAKE_CURRENT_BINARY_DIR}/include/${HEADER_DIR}/deprecated.${PROJECT_CUSTOM_HEADER_EXTENSION} @ONLY ) if(INSTALL_GENERATED_HEADERS) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/include/${HEADER_DIR}/deprecated.${PROJECT_CUSTOM_HEADER_EXTENSION} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${HEADER_DIR} PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE ) endif(INSTALL_GENERATED_HEADERS) endif() if(NOT PROJECT_GENERATED_HEADERS_SKIP_WARNING) # Generate warning.hh header. configure_file( ${PROJECT_JRL_CMAKE_MODULE_DIR}/warning.hh.cmake ${CMAKE_CURRENT_BINARY_DIR}/include/${HEADER_DIR}/warning.${PROJECT_CUSTOM_HEADER_EXTENSION} @ONLY ) if(INSTALL_GENERATED_HEADERS) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/include/${HEADER_DIR}/warning.${PROJECT_CUSTOM_HEADER_EXTENSION} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${HEADER_DIR} PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE ) endif(INSTALL_GENERATED_HEADERS) endif() # Generate config.h header. This header, unlike the previous one is *not* # installed and is generated in the top-level directory of the build tree. # Therefore it must not be included by any distributed header. configure_file( ${PROJECT_JRL_CMAKE_MODULE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h ) # Default include directories: - top-level build directory (for generated # non-distributed headers). - include directory in the build tree (for # generated, distributed headers). - include directory in the source tree # (non-generated, distributed headers). include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/include ${PROJECT_SOURCE_DIR}/include ) endmacro(_SETUP_PROJECT_HEADER) # GENERATE_CONFIGURATION_HEADER # ----------------------------- # # This macro generates a configuration header. Macro parameters may be used to # customize it. # # * HEADER_DIR : where to generate the header # * FILENAME : how the file should be named # * LIBRARY_NAME : CPP symbol prefix, should match the compiled library name # * EXPORT_SYMBOL : controls the switch between symbol import/export function( GENERATE_CONFIGURATION_HEADER HEADER_DIR FILENAME LIBRARY_NAME EXPORT_SYMBOL ) GENERATE_CONFIGURATION_HEADER_V2( INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/include HEADER_DIR ${HEADER_DIR} FILENAME ${FILENAME} LIBRARY_NAME ${LIBRARY_NAME} EXPORT_SYMBOL ${EXPORT_SYMBOL} ) endfunction(GENERATE_CONFIGURATION_HEADER) # ~~~ # .rst: .. command:: GENERATE_CONFIGURATION_HEADER_V2 ( # INCLUDE_DIR # HEADER_DIR # FILENAME # LIBRARY_NAME # EXPORT_SYBMOL ) # ~~~ # # This function generates a configuration header at # ``//``. # # If INSTALL_GENERATED_HEADERS is ON, the configuration header will be installed # in # ``${CMAKE_INSTALL_INCLUDEDIR}/``. # # :param INCLUDE_DIR: Include root directory (absolute). # # :param HEADER_DIR: Include sub directory. # # :param FILENAME: Configuration header name. # # :param LIBRARY_NAME: CPP symbol prefix, should match the compiled library # name. # # :param EXPORT_SYMBOL: Controls the switch between symbol import/export. function(GENERATE_CONFIGURATION_HEADER_V2) set(options) set( oneValueArgs INCLUDE_DIR HEADER_DIR FILENAME LIBRARY_NAME EXPORT_SYMBOL ) set(multiValueArgs) cmake_parse_arguments( ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) if(${PROJECT_VERSION_MAJOR} MATCHES UNKNOWN) set(PROJECT_VERSION_MAJOR_CONFIG ${ARGS_LIBRARY_NAME}_VERSION_UNKNOWN_TAG) else() set(PROJECT_VERSION_MAJOR_CONFIG ${PROJECT_VERSION_MAJOR}) endif() if(${PROJECT_VERSION_MINOR} MATCHES UNKNOWN) set(PROJECT_VERSION_MINOR_CONFIG ${ARGS_LIBRARY_NAME}_VERSION_UNKNOWN_TAG) else() set(PROJECT_VERSION_MINOR_CONFIG ${PROJECT_VERSION_MINOR}) endif() if(${PROJECT_VERSION_PATCH} MATCHES UNKNOWN) set(PROJECT_VERSION_PATCH_CONFIG ${ARGS_LIBRARY_NAME}_VERSION_UNKNOWN_TAG) else() set(PROJECT_VERSION_PATCH_CONFIG ${PROJECT_VERSION_PATCH}) endif() # Set variables for configure_file command set(EXPORT_SYMBOL ${ARGS_EXPORT_SYMBOL}) set(LIBRARY_NAME ${ARGS_LIBRARY_NAME}) # Generate the header. configure_file( ${PROJECT_JRL_CMAKE_MODULE_DIR}/config.hh.cmake ${ARGS_INCLUDE_DIR}/${ARGS_HEADER_DIR}/${ARGS_FILENAME} @ONLY ) # Install it if requested. if(INSTALL_GENERATED_HEADERS) install( FILES ${ARGS_INCLUDE_DIR}/${ARGS_HEADER_DIR}/${ARGS_FILENAME} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${ARGS_HEADER_DIR} PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE ) endif() endfunction(GENERATE_CONFIGURATION_HEADER_V2) # _SETUP_PROJECT_HEADER_FINALIZE # ------------------------------ # # Post-processing of the header management step. Install public headers if # required. # macro(_SETUP_PROJECT_HEADER_FINALIZE) # If the header list is set, install it. if(DEFINED ${PROJECT_NAME}_HEADERS AND NOT BUILD_STANDALONE_PYTHON_INTERFACE) HEADER_INSTALL(${${PROJECT_NAME}_HEADERS}) endif() endmacro(_SETUP_PROJECT_HEADER_FINALIZE) # .rst: .. ifmode:: internal # # ~~~ # .. command:: HEADER_INSTALL (COMPONENT ...) # ~~~ # # Install a list of headers. # # :param component: Component to forward to install command. # # :param files: Files to install. macro(HEADER_INSTALL) set(options) set(oneValueArgs COMPONENT) set(multiValueArgs) cmake_parse_arguments( ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) if(ARGS_COMPONENT) set(_COMPONENT_NAME ${ARGS_COMPONENT}) else() set(_COMPONENT_NAME ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) endif() set(FILES ${ARGS_UNPARSED_ARGUMENTS}) foreach(FILE ${FILES}) get_filename_component(DIR "${FILE}" PATH) string(REPLACE "${PROJECT_BINARY_DIR}" "" DIR "${DIR}") string(REPLACE "${PROJECT_SOURCE_DIR}" "" DIR "${DIR}") string(REPLACE "include" "" DIR "${DIR}") # workaround CMP0177 cmake_path(SET INSTALL_PATH NORMALIZE "${CMAKE_INSTALL_INCLUDEDIR}/${DIR}") install( FILES ${FILE} DESTINATION ${INSTALL_PATH} PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE COMPONENT ${_COMPONENT_NAME} ) endforeach() endmacro()