cdk/cmake/headers.cmake (371 lines of code) (raw):

# Copyright (c) 2015, 2024, Oracle and/or its affiliates. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2.0, as # published by the Free Software Foundation. # # This program is designed to work with certain software (including # but not limited to OpenSSL) that is licensed under separate terms, as # designated in a particular file or component or in included license # documentation. The authors of MySQL hereby grant you an additional # permission to link the program and your derivative works with the # separately licensed software that they have either included with # the program or referenced in the documentation. # # Without limiting anything contained in the foregoing, this file, # which is part of Connector/C++, is also subject to the # Universal FOSS Exception, version 1.0, a copy of which can be found at # https://oss.oracle.com/licenses/universal-foss-exception. # # 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, version 2.0, for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # Infrastructure for managing public headers of the project # ========================================================= # # This infrastructure assumes that all public headers are located in # a single folder of the project and its sub-folders. The top-level # folder should contain a CMakeLists.txt with the following declarations: # # SETUP_HEADERS(<top-level dir>) # # <public header declarations> # # ADD_HEADERS_TARGET() # # Command ADD_HEADERS_TARGET() adds to the project a target for doing # sanity checks on headers. In GUI systems the corresponding project # contains all declared public headers for easy access (implemented with # SOURCE_GROUP). # # Public header declarations are either: # # ADD_HEADERS(<list of headers>) - add given headers in the current folder # ADD_HEADERS_DIR(<dir name>) - add all headers declared in the named sub-folder # if(NOT DEFINED WITH_HEADER_CHECKS) option(WITH_HEADER_CHECKS "Add Public header checks to the project" OFF) endif() # # Determine location of files accompanying this headers.cmake module which are # in heders/ sub-folder of the location where headers.cmake is stored # GET_FILENAME_COMPONENT(headers_dir ${CMAKE_CURRENT_LIST_FILE} PATH) SET(headers_dir "${headers_dir}/headers") #MESSAGE("headers.cmake: ${headers_dir}") # # Check if given list of headers includes all headers that can be found in # the current directory. # function(check_headers) file(GLOB all_headers RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.h") foreach(header IN LISTS ARGV) #message("- checking header: ${header}\n") list(REMOVE_ITEM all_headers ${header}) endforeach() list(LENGTH all_headers remains) if(remains GREATER 0) message(WARNING "Extra headers found in ${CMAKE_CURRENT_SOURCE_DIR}: ${all_headers}") endif() endfunction() # # Set-up header declarations with given folder as a base location for all # public headers. # # A CMakeLists.txt file for a project doing sanity header checks # is created in the corresponding build location. This file is generated # from check.cmake.in template. When headers are declared later # appropriate commands are written to this CMakeLists.txt file. # # Variable hdrs_init is used to ensure single initialization of each sub-folder # declaring public headers (see HEADERS_DIR()). # MACRO(SETUP_HEADERS base_dir) if(WITH_HEADER_CHECKS) GET_FILENAME_COMPONENT(hdr_base_dir ${base_dir} ABSOLUTE) MESSAGE(STATUS "Public headers directory: ${hdr_base_dir}") SET(hdr_include_dir ${CMAKE_CURRENT_SOURCE_DIR}) file(RELATIVE_PATH headers_check_base_dir ${CMAKE_CURRENT_SOURCE_DIR} ${hdr_base_dir}) set(headers_check_base_dir "${CMAKE_CURRENT_BINARY_DIR}/${headers_check_base_dir}/check" CACHE INTERNAL "locations of headers check project") file(REMOVE_RECURSE ${headers_check_base_dir}) FILE(MAKE_DIRECTORY ${headers_check_base_dir}) CONFIGURE_FILE(${headers_dir}/check.cmake.in ${headers_check_base_dir}/CMakeLists.txt @ONLY) #message("headers: top-level CMakeLists.txt generated in: ${headers_check_base_dir}") endif() ENDMACRO(SETUP_HEADERS) # # Initialize current folder for public header declarations. # # A sub-folder foo/bar/baz of the base headers folder adds its headers to group # named "foo\\bar\\baz". The name of the grup and its variant of the form # "foo_bar_baz" are computed in hdr_group and hdr_prefix variables. If this is the # base headers folder then hdr_group is ".". If this folder is outside of the headers # base folder then hdr_group is "". # # The header group of this folder and all header sub-folders declared here are # collected in hdr_groups variable. All header files declared in this folder are # collected in hdr_list variable. # # Headers declared here are added to the project which does header sanity checks. # This is done by writing commands to CMakeLists.txt file in the corresponding build # location. The file is initialized here. # # Macro HEADERS_DIR() is protected with hdrs_init variable so that it can be called # several times but initializes given folder only once. # MACRO(HEADERS_DIR) IF(NOT hdrs_init) IF(NOT hdr_base_dir) MESSAGE(FATAL_ERROR "Header declarations without prior SETUP_HEADERS()") ENDIF() SET(hdrs_init 1) # # Compute header group name and prefix. # FILE(RELATIVE_PATH rel_path ${hdr_base_dir} ${CMAKE_CURRENT_SOURCE_DIR}) IF(rel_path STREQUAL "") SET(rel_path ".") ELSEIF(rel_path MATCHES "^\\.\\.") #MESSAGE("outside headers dir") SET(rel_path "") ENDIF() STRING(REPLACE "/" "\\" hdr_group "${rel_path}") STRING(REPLACE "/" "_" hdr_prefix "${rel_path}") # # Add header group of this folder to hdr_groups list, reset hdr_list. # LIST(APPEND hdr_groups ${hdr_group}) #MESSAGE("Current list of header groups: ${hdr_groups}") SET(hdr_list "") # # Initialize CMakeLists.txt file for the headers check project. # if(rel_path) set(current_check_dir "${headers_check_base_dir}/${rel_path}") set(check_cmakelists "${current_check_dir}/CMakeLists.txt") #message("current check dir: ${current_check_dir}") # Note: the top-level CMakeLists.txt was generated by # SETUP_HEADERS() if(NOT rel_path STREQUAL ".") file(MAKE_DIRECTORY ${current_check_dir}) file(WRITE ${check_cmakelists} "# Auto generated file\n") endif() else() set(check_cmakelists "") endif() ENDIF() ENDMACRO(HEADERS_DIR) # # Declare list of headers in the current folder as public headers of the project. # # Declared headers are appended to hdr_list variable and global variable headers_GGG # is set to the current value of hdr_list where GGG is the name of header group of # this folder. Also hdr_groups variable in parent scope is updated to make sure that # it contains the current list of all header groups defined so far in this folder. # # Note that ADD_HEADERS() can be called several times in a given folder adding new # headers to the list. # MACRO(ADD_HEADERS) if(WITH_HEADER_CHECKS) HEADERS_DIR() IF(hdr_group STREQUAL "") MESSAGE(ERROR "Headers added from outside of header base dir" " (${hdr_base_dir}) were ignored") ELSE() MESSAGE(STATUS "Adding public headers in: ${hdr_group}") FOREACH(hdr ${ARGV}) GET_FILENAME_COMPONENT(hdrn ${hdr} NAME) GET_FILENAME_COMPONENT(hdr ${hdr} ABSOLUTE) MESSAGE(STATUS " - ${hdrn}") LIST(APPEND hdr_list ${hdr}) ENDFOREACH(hdr) # # Set/update global headers_GGG variable to hold the current list # of headers in this group. # SET(headers_${hdr_group} ${hdr_list} CACHE INTERNAL "Public headers from ${hdr_group}" FORCE) # # Update parent's hdr_groups list to make sure that it contains # all header groups collected so far # SET(hdr_groups ${hdr_groups} PARENT_SCOPE) #MESSAGE("Current list of header groups: ${hdr_groups}") ENDIF() endif() ENDMACRO(ADD_HEADERS list) # # Add public header declarations from a named sub-folder. # # Variable hdr_groups in parent scope is updated to include new header # groups introduced in the sub-folder. # # The sub-folder is also added to the header sanity-checks project. # MACRO(ADD_HEADERS_DIR dir) if(WITH_HEADER_CHECKS) HEADERS_DIR() # # Save current value of hdr_groups in all_hdr_groups because it will be changed # by included sub-folder. # SET(all_hdr_groups ${hdr_groups}) # # Reset hdrs_init to 0 because we want folder initialization to happen in the # sub-folder. # SET(hdrs_init 0) # # Headers declared in the sub-folder will be added to new header groups. A list # of these new groups will be appended to hdr_groups. # ADD_SUBDIRECTORY(${dir}) SET(hdrs_init 1) # # Update parent's hdr_groups to hold the extended list of groups. # SET(hdr_groups ${hdr_groups} PARENT_SCOPE) #MESSAGE("Current list of header groups: ${hdr_groups}") # # Add sub-folder to header sanity checks project # if(check_cmakelists) FILE(APPEND ${check_cmakelists} "ADD_SUBDIRECTORY(${dir})\n") endif() else() # # Even if header checks are disabled, we still need to include header # subdirectories to execute other cmake commands that might be present there. # ADD_SUBDIRECTORY(${dir}) endif() ENDMACRO(ADD_HEADERS_DIR dir) # # Add all headers declared in the current folder to sanity checks project. # # Sanity check consists of compiling a simple file which includes given header alone. # This file is generated from check.source.in template with @HEADER@ placeholder for # header name (without extension). # # Note: this command should be executed after declaring all the headers in the folder. # MACRO(ADD_HEADER_CHECKS) if(WITH_HEADER_CHECKS) # # For each header HHH generate test source file check_HHH.cc and add it to ceck_sources # list # SET(check_sources "") SET(hdr_names "") FOREACH(hdr ${hdr_list}) GET_FILENAME_COMPONENT(hdrn ${hdr} NAME_WE) #MESSAGE("processing header: ${hdrn}.h") LIST(APPEND check_sources "check_${hdrn}.cc") LIST(APPEND hdr_names "${hdrn}.h") SET(HEADER "${CMAKE_CURRENT_SOURCE_DIR}/${hdrn}.h") SET(HEADERN "${hdrn}") CONFIGURE_FILE(${headers_dir}/check.source.in "${current_check_dir}/check_${hdrn}.cc" @ONLY) ENDFOREACH(hdr) # # Add static library check_GGG (where GGG is the header groups of this folder) built from # test sources for all the headers. Put this folder in include path as the check project # can be in different location. # FILE(APPEND ${check_cmakelists} "INCLUDE_DIRECTORIES(\"${CMAKE_CURRENT_BINARY_DIR}\")\n") FILE(APPEND ${check_cmakelists} "ADD_LIBRARY(check_${hdr_prefix} STATIC ${check_sources} ${hdr_list})\n") endif() ENDMACRO(ADD_HEADER_CHECKS) # # Add a target for public headers. # # Building this target will execute sanity checks for all declared public headers. # # Note: this macro should be called from the base headers folder and after declaring # all public headers of the project. # MACRO(ADD_HEADERS_TARGET) if(WITH_HEADER_CHECKS) #MESSAGE("groups: ${hdr_groups}") # # Collect all declared public headers in all_headers list. Headers are collected from # all header groups listed in all_hdr_groups variable. For group GGG the list of public # headers in that group is stored in headers_GGG variable. For each header group a # corresponding SOURCE_GROUP() is declared. # SET(all_headers "") FOREACH(group ${hdr_groups}) #MESSAGE("Headers in ${group}: ${headers_${group}}") LIST(APPEND all_headers ${headers_${group}}) IF(group STREQUAL ".") SET(group_name "Headers") ELSE() SET(group_name "Headers\\${group}") ENDIF() SOURCE_GROUP(${group_name} FILES ${headers_${group}}) ENDFOREACH(group) # # Add the Header target which builds the sanity check project. All public headers are # listed as sources of this target (which gives easy access to them in GUI systems). # ADD_CUSTOM_TARGET(Headers COMMAND ${CMAKE_COMMAND} --build . --clean-first WORKING_DIRECTORY ${headers_check_base_dir} COMMENT "Header checks" SOURCES ${all_headers} ) set_target_properties(Headers PROPERTIES FOLDER "Tests") # # Configure the sanity checks project. All CMakeLists.txt files defining the project # have been created while declaring public headers. # # Dirty trick to speed up cmake set up time. file( COPY "${CMAKE_BINARY_DIR}/CMakeFiles/${CMAKE_VERSION}" DESTINATION "${headers_check_base_dir}/CMakeFiles" ) MESSAGE(STATUS "Configuring header checks using cmake generator: ${CMAKE_GENERATOR}") EXECUTE_PROCESS( COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${headers_check_base_dir} ) endif() ENDMACRO(ADD_HEADERS_TARGET) MACRO(ADD_HEADERS_TEST) if(WITH_HEADER_CHECKS) add_test(NAME Headers COMMAND ${CMAKE_COMMAND} --build . --clean-first WORKING_DIRECTORY ${headers_check_base_dir}) message(STATUS "Added public headers test") endif() ENDMACRO() # # If public headers of the project use external headers and/or require some pre-processor # definitions to work correctly then the santiy check project must define these macros # and set required include paths. This can be done with HEADER_CHECKS_INCLUDE() and # HEADER_CHECKS_DEFINITIONS() macros. # MACRO(HEADER_CHECKS_INCLUDE) if(WITH_HEADER_CHECKS) FOREACH(dir ${ARGV}) #message("headers (${headers_check_base_dir}): adding inc dir:${dir}") FILE(APPEND ${headers_check_base_dir}/CMakeLists.txt "INCLUDE_DIRECTORIES(\"${dir}\")\n") ENDFOREACH(dir) endif() ENDMACRO(HEADER_CHECKS_INCLUDE) MACRO(HEADER_CHECKS_DEFINITIONS) if(WITH_HEADER_CHECKS) FOREACH(def ${ARGV}) FILE(APPEND ${headers_check_base_dir}/CMakeLists.txt "ADD_DEFINITIONS(${def})\n") ENDFOREACH(def) endif() ENDMACRO(HEADER_CHECKS_DEFINITIONS)