#[[ Warning: This module depends on `qmcorecmd` after installation. ]] # if(NOT QMSETUP_CORECMD_EXECUTABLE) message(FATAL_ERROR "QMSETUP_CORECMD_EXECUTABLE not defined. Add find_package(qmsetup) to CMake first.") endif() if(NOT DEFINED QMSETUP_DEFINITION_NUMERICAL) set(QMSETUP_DEFINITION_NUMERICAL off) endif() if(NOT DEFINED QMSETUP_DEFINITION_SCOPE) set(QMSETUP_DEFINITION_SCOPE) endif() if(NOT DEFINED QMSETUP_DEFINITION_PROPERTY) set(QMSETUP_DEFINITION_PROPERTY) endif() if(NOT DEFINED QMSETUP_SYNC_INCLUDE_STANDARD) set(QMSETUP_SYNC_INCLUDE_STANDARD on) endif() include_guard(DIRECTORY) #[[ Generate indirect reference files for header files to make the include statements more orderly. The generated file has the same timestamp as the source file. qm_sync_include( [STANDARD] [NO_STANDARD] [NO_ALL] [INCLUDE ...] [EXCLUDE ] [INSTALL_DIR ] [FORCE] [VERBOSE] ) STANDARD: Enable standard public-private pattern, can be forced to enable by enabling `QMSETUP_SYNC_INCLUDE_STANDARD` NO_STANDARD: Disable standard public-private pattern, enable it to override `QMSETUP_SYNC_INCLUDE_STANDARD` #]] function(qm_sync_include _src_dir _dest_dir) set(options FORCE VERBOSE NO_STANDARD NO_ALL) set(oneValueArgs INSTALL_DIR) set(multiValueArgs INCLUDE EXCLUDE) cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) # Check tool if(NOT QMSETUP_CORECMD_EXECUTABLE) message(FATAL_ERROR "qm_sync_include: corecmd tool not found.") endif() if(NOT IS_ABSOLUTE ${_src_dir}) get_filename_component(_src_dir ${_src_dir} ABSOLUTE) else() string(REPLACE "\\" "/" _src_dir ${_src_dir}) endif() if(NOT IS_ABSOLUTE ${_dest_dir}) get_filename_component(_dest_dir ${_dest_dir} ABSOLUTE) else() string(REPLACE "\\" "/" _dest_dir ${_dest_dir}) endif() if(IS_DIRECTORY ${_src_dir}) file(GLOB_RECURSE header_files ${_src_dir}/*.h ${_src_dir}/*.hpp) set(_args) if(FUNC_STANDARD OR(QMSETUP_SYNC_INCLUDE_STANDARD AND NOT FUNC_NO_STANDARD)) list(APPEND _args -s) endif() if(FUNC_NO_ALL) list(APPEND _args -n) endif() set(_even off) foreach(_item IN LISTS FUNC_INCLUDE) if(_even) set(_even off) list(APPEND _args ${_item}) else() set(_even on) list(APPEND _args -i ${_item}) endif() endforeach() foreach(_item IN LISTS FUNC_EXCLUDE) list(APPEND _args -e ${_item}) endforeach() if(FUNC_VERBOSE) list(APPEND _args -V) endif() if(FUNC_FORCE OR NOT EXISTS ${_dest_dir}) if(EXISTS ${_dest_dir}) file(REMOVE_RECURSE ${_dest_dir}) endif() execute_process( COMMAND ${QMSETUP_CORECMD_EXECUTABLE} incsync ${_args} ${_src_dir} ${_dest_dir} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND_ERROR_IS_FATAL ANY ) endif() if(FUNC_INSTALL_DIR) set(_install_dir ${FUNC_INSTALL_DIR}) set(_args_quoted) foreach(_item IN LISTS _args) set(_args_quoted "${_args_quoted}\"${_item}\" ") endforeach() # Get command output only and use file(INSTALL) to install files install(CODE " get_filename_component(_install_dir \"${_install_dir}\" ABSOLUTE BASE_DIR \${CMAKE_INSTALL_PREFIX}) execute_process( COMMAND \"${QMSETUP_CORECMD_EXECUTABLE}\" incsync -d ${_args_quoted} \"${_src_dir}\" \${_install_dir} WORKING_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}\" OUTPUT_VARIABLE _output_contents OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY ) string(REPLACE \"\\n\" \";\" _lines \"\${_output_contents}\") foreach(_line IN LISTS _lines) string(REGEX MATCH \"from \\\"([^\\\"]*)\\\" to \\\"([^\\\"]*)\\\"\" _ \${_line}) get_filename_component(_target_path \${CMAKE_MATCH_2} DIRECTORY) file(INSTALL \${CMAKE_MATCH_1} DESTINATION \${_target_path}) endforeach() ") endif() else() message(FATAL_ERROR "qm_sync_include: source directory doesn't exist.") endif() endfunction() #[[ Add a definition to a property scope. qm_add_definition( | [GLOBAL | TARGET | SOURCE | DIRECTORY ] [PROPERTY ] [CONDITION ] [STRING_LITERAL] [NO_KEYWORD] [NUMERICAL] [CLASSICAL] ) STRING_LITERAL: Force quotes on values NO_KEYWORD: Treat any keyword as string NUMERICAL: Use 1/-1 as defined/undefined, can be forced to enable by enabling `QMSETUP_DEFINITION_NUMERICAL` CLASSICAL: Use classical definition, enable it to override `QMSETUP_DEFINITION_NUMERICAL` ]] # function(qm_add_definition _first) set(options STRING_LITERAL NO_KEYWORD NUMERICAL CLASSICAL) set(oneValueArgs TARGET SOURCE DIRECTORY PROPERTY) set(multiValueArgs CONDITION) cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(_result) set(_is_pair off) set(_defined off) set(_list ${_first} ${FUNC_UNPARSED_ARGUMENTS}) list(LENGTH _list _len) set(_cond on) set(_numerical off) if(NOT FUNC_CLASSICAL AND(QMSETUP_DEFINITION_NUMERICAL OR FUNC_NUMERICAL)) set(_numerical on) endif() if(FUNC_CONDITION) if(NOT(${FUNC_CONDITION})) set(_cond off) endif() elseif(DEFINED FUNC_CONDITION) set(_cond off) endif() if(${_len} EQUAL 1) set(_result ${_list}) set(_defined on) if(NOT _cond) set(_defined off) endif() elseif(${_len} EQUAL 2) # Get key list(POP_FRONT _list _key) list(POP_FRONT _list _val) if(FUNC_STRING_LITERAL AND NOT ${_val} MATCHES "\".+\"") set(_val "\"${_val}\"") endif() # Boolean string(TOLOWER ${_val} _val_lower) if(NOT FUNC_NO_KEYWORD AND(${_val_lower} STREQUAL "off" OR ${_val_lower} STREQUAL "false")) set(_result ${_key}) set(_defined off) if(NOT _cond) set(_defined on) endif() elseif(NOT FUNC_NO_KEYWORD AND(${_val_lower} STREQUAL "on" OR ${_val_lower} STREQUAL "true")) set(_result ${_key}) set(_defined on) if(NOT _cond) set(_defined off) endif() else() set(_result "${_key}=${_val}") set(_is_pair on) set(_defined on) if(NOT _cond) set(_defined off) endif() endif() else() message(FATAL_ERROR "qm_add_definition: called with incorrect number of arguments") endif() if(_numerical AND NOT _is_pair) if(_defined) set(_result "${_result}=1") else() set(_result "${_result}=-1") endif() elseif(NOT _defined) return() endif() _qm_calc_property_scope_helper(_scope _prop) set_property(${_scope} APPEND PROPERTY ${_prop} "${_result}") endfunction() #[[ Remove a definition from a property scope. qm_remove_definition( [GLOBAL | TARGET | SOURCE | DIRECTORY ] [PROPERTY ] ) ]] # function(qm_remove_definition _key) set(options) set(oneValueArgs TARGET PROPERTY) set(multiValueArgs) cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(FUNC_TARGET) get_target_property(_definitions ${FUNC_TARGET} ${_prop}) else() get_property(_definitions GLOBAL PROPERTY ${_prop}) endif() # Filter list(FILTER _definitions EXCLUDE REGEX "^${_key}(=.*)?$") _qm_calc_property_scope_helper(_scope _prop) set_property(${_scope} PROPERTY ${_prop} "${_definitions}") endfunction() #[[ Generate a configuration header of a property scope. If the configuration has not changed, the generated file's timestemp will not be updated when you reconfigure it. qm_generate_config( [GLOBAL | TARGET | SOURCE | DIRECTORY ] [PROPERTY ] [PROJECT_NAME ] [WARNING_FILE ] [NO_WARNING] [NO_HASH] ) ]] # function(qm_generate_config _file) set(options NO_WARNING NO_HASH) set(oneValueArgs TARGET PROPERTY PROJECT_NAME WARNING_FILE) set(multiValueArgs) cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) _qm_calc_property_scope_helper(_scope _prop) get_property(_definitions ${_scope} PROPERTY ${_prop}) if(NOT _definitions) set(_definitions) # May be _-NOTFOUND endif() _qm_generate_config_helper() endfunction() #[[ Generate build info information header. qm_generate_build_info( [ROOT_DIRECTORY ] [PREFIX ] [YEAR] [TIME] [PROJECT_NAME ] [WARNING_FILE ] [NO_WARNING] [NO_HASH] [REQUIRED] ) file: Output file ROOT_DIRECTORY: Repository root directory (CMake will try to run `git` at this directory) PREFIX: Macros prefix, default to `PROJECT_NAME` REQUIRED: Abort if there's any error with git ]] # function(qm_generate_build_info _file) set(options NO_WARNING NO_HASH YEAR TIME REQUIRED) set(oneValueArgs ROOT_DIRECTORY PREFIX PROJECT_NAME WARNING_FILE) set(multiValueArgs) cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(FUNC_PREFIX) set(_prefix ${FUNC_PREFIX}) else() string(TOUPPER "${PROJECT_NAME}" _prefix) endif() set(_dir) qm_set_value(_dir FUNC_ROOT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) set(_git_branch "unknown") set(_git_hash "unknown") set(_git_commit_time "unknown") set(_git_commit_author "unknown") set(_git_commit_email "unknown") find_package(Git QUIET) if(Git_FOUND) # Branch execute_process( COMMAND ${GIT_EXECUTABLE} symbolic-ref --short -q HEAD OUTPUT_VARIABLE _temp ERROR_VARIABLE _err OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET WORKING_DIRECTORY ${_dir} RESULT_VARIABLE _code ) if(${_code} EQUAL 0) set(_git_branch ${_temp}) elseif(FUNC_REQUIRED) message(FATAL_ERROR "${_err}") endif() # Hash execute_process( COMMAND ${GIT_EXECUTABLE} log -1 "--pretty=format:%H;%aI;%aN;%aE" # Use `;` as separator OUTPUT_VARIABLE _temp ERROR_VARIABLE _err OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET WORKING_DIRECTORY ${_dir} RESULT_VARIABLE _code ) if(${_code} EQUAL 0) list(GET _temp 0 _git_hash) list(GET _temp 1 _git_commit_time) list(GET _temp 2 _git_commit_author) list(GET _temp 3 _git_commit_email) elseif(FUNC_REQUIRED) message(FATAL_ERROR "${_err}") endif() elseif(FUNC_REQUIRED) message(FATAL_ERROR "Git not found") endif() qm_set_value(_system_name CMAKE_SYSTEM_NAME unknown) qm_set_value(_system_version CMAKE_SYSTEM_VERSION unknown) qm_set_value(_system_processor CMAKE_SYSTEM_PROCESSOR unknown) qm_set_value(_host_system_name CMAKE_HOST_SYSTEM_NAME unknown) qm_set_value(_host_system_version CMAKE_HOST_SYSTEM_VERSION unknown) qm_set_value(_host_system_processor CMAKE_HOST_SYSTEM_PROCESSOR unknown) qm_set_value(_compiler_name CMAKE_CXX_COMPILER_ID unknown) qm_set_value(_compiler_version CMAKE_CXX_COMPILER_VERSION unknown) qm_set_value(_compiler_arch CMAKE_CXX_COMPILER_ARCHITECTURE_ID unknown) qm_set_value(_compiler_abi CMAKE_CXX_COMPILER_ABI unknown) set(_definitions) set(_has_time off) # year if(FUNC_YEAR) string(TIMESTAMP _build_year "%Y") list(APPEND _definitions ${_prefix}_BUILD_YEAR=\"${_build_year}\") set(_has_time on) endif() # time if(FUNC_TIME) string(TIMESTAMP _build_time "%Y/%m/%d %H:%M:%S") list(APPEND _definitions ${_prefix}_BUILD_TIME=\"${_build_time}\") set(_has_time on) endif() if(_has_time) list(APPEND _definitions "%") endif() # system list(APPEND _definitions ${_prefix}_SYSTEM_NAME=\"${_system_name}\") list(APPEND _definitions ${_prefix}_SYSTEM_VERSION=\"${_system_version}\") list(APPEND _definitions ${_prefix}_SYSTEM_PROCESSOR=\"${_system_processor}\") list(APPEND _definitions ${_prefix}_HOST_SYSTEM_NAME=\"${_host_system_name}\") list(APPEND _definitions ${_prefix}_HOST_SYSTEM_VERSION=\"${_host_system_version}\") list(APPEND _definitions ${_prefix}_HOST_SYSTEM_PROCESSOR=\"${_host_system_processor}\") list(APPEND _definitions "%") # compiler list(APPEND _definitions ${_prefix}_COMPILER_ID=\"${_compiler_name}\") list(APPEND _definitions ${_prefix}_COMPILER_VERSION=\"${_compiler_version}\") list(APPEND _definitions ${_prefix}_COMPILER_ARCH=\"${_compiler_arch}\") list(APPEND _definitions ${_prefix}_COMPILER_ABI=\"${_compiler_abi}\") list(APPEND _definitions "%") # build time (deprecated) # list(APPEND _definitions ${_prefix}_BUILD_DATE_TIME=\"${_build_time}\") # list(APPEND _definitions ${_prefix}_BUILD_YEAR=\"${_build_year}\") # list(APPEND _definitions "%") # git info list(APPEND _definitions ${_prefix}_GIT_BRANCH=\"${_git_branch}\") list(APPEND _definitions ${_prefix}_GIT_LAST_COMMIT_HASH=\"${_git_hash}\") list(APPEND _definitions ${_prefix}_GIT_LAST_COMMIT_TIME=\"${_git_commit_time}\") list(APPEND _definitions ${_prefix}_GIT_LAST_COMMIT_AUTHOR=\"${_git_commit_author}\") list(APPEND _definitions ${_prefix}_GIT_LAST_COMMIT_EMAIL=\"${_git_commit_email}\") _qm_generate_config_helper() endfunction() # ---------------------------------- # Private functions # ---------------------------------- function(_qm_calc_property_scope_helper _scope _prop) if(FUNC_TARGET) set(_scope TARGET ${FUNC_TARGET}) elseif(FUNC_SOURCE) set(_scope SOURCE ${FUNC__SOURCE}) elseif(FUNC_DIRECTORY) set(_scope DIRECTORY ${FUNC_DIRECTORY}) elseif(FUNC_GLOBAL) set(_scope GLOBAL) elseif(QMSETUP_DEFINITION_SCOPE) set(_scope ${QMSETUP_DEFINITION_SCOPE}) else() set(_scope GLOBAL) endif() qm_set_value(_prop FUNC_PROPERTY QMSETUP_DEFINITION_PROPERTY "CONFIG_DEFINITIONS") set(_scope ${_scope} PARENT_SCOPE) set(_prop ${_prop} PARENT_SCOPE) endfunction() function(_qm_generate_config_helper) set(_args) foreach(_item IN LISTS _definitions) list(APPEND _args "-D${_item}") endforeach() list(APPEND _args ${_file}) if(NOT FUNC_NO_WARNING) list(APPEND _args "-w" ${FUNC_WARNING_FILE}) endif() if(FUNC_PROJECT_NAME) list(APPEND _args "-p" ${FUNC_PROJECT_NAME}) endif() if(FUNC_NO_HASH) list(APPEND _args "-f") endif() execute_process(COMMAND ${QMSETUP_CORECMD_EXECUTABLE} configure ${_args} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND_ERROR_IS_FATAL ANY ) endfunction()