diff --git a/utils.cmake b/utils.cmake index 9ae6911..d8f5cb1 100644 --- a/utils.cmake +++ b/utils.cmake @@ -106,7 +106,7 @@ function(compute_install_dir) endfunction() function(setup_project) - cmake_parse_arguments(PROJ_ARGS "QT_PROJECT;ENABLE_LTO;WARNINGS_ARE_ERRORS;MAX_WARNING;NO_WARNING;RTTI;EXCEPTIONS" "QML_IMPORT_DIR" "LANGUAGES" ${ARGN}) + cmake_parse_arguments(PROJ_ARGS "QT_PROJECT;ENABLE_LTO;WARNINGS_ARE_ERRORS;MAX_WARNING;NO_WARNING;RTTI;EXCEPTIONS" "QML_IMPORT_DIR;LICENSE_HEADER;MODIFY_LOST_WARNING;CONFIG_PREFIX;VERSION_PREFIX" "LANGUAGES" ${ARGN}) if(NOT PROJ_ARGS_LANGUAGES) message(AUTHOR_WARNING "setup_project: You need to specify at least one language for this function!") return() @@ -524,6 +524,22 @@ function(setup_project) endif() endif() endforeach() + set(__meta_target ${PROJECT_NAME}_METADATA) + if(NOT TARGET ${__meta_target}) + add_custom_target(${__meta_target}) + endif() + if(arg_LICENSE_HEADER) + set_target_properties(${__meta_target} PROPERTIES __PROJECT_LICENSE_HEADER "${arg_LICENSE_HEADER}") + endif() + if(arg_MODIFY_LOST_WARNING) + set_target_properties(${__meta_target} PROPERTIES __PROJECT_MODIFY_LOST_WARNING "${arg_MODIFY_LOST_WARNING}") + endif() + if(arg_CONFIG_PREFIX) + set_target_properties(${__meta_target} PROPERTIES __PROJECT_CONFIG_PREFIX "${arg_CONFIG_PREFIX}") + endif() + if(arg_VERSION_PREFIX) + set_target_properties(${__meta_target} PROPERTIES __PROJECT_VERSION_PREFIX "${arg_VERSION_PREFIX}") + endif() endfunction() function(get_commit_hash) @@ -1913,3 +1929,386 @@ function(setup_target_rpaths) endif() endforeach() endfunction() + +function(add_project_config) + set(__target ${PROJECT_NAME}_METADATA) + if(NOT TARGET ${__target}) + message(AUTHOR_WARNING "add_project_config: You need to call setup_project() first!") + return() + endif() + cmake_parse_arguments(arg "" "KEY;VALUE" "" ${ARGN}) + if(NOT arg_KEY) + message(AUTHOR_WARNING "add_project_config: You need to specify the KEY name!") + return() + endif() + if(NOT arg_VALUE) + message(AUTHOR_WARNING "add_project_config: You need to specify the VALUE value!") + return() + endif() + if(arg_UNPARSED_ARGUMENTS) + message(AUTHOR_WARNING "add_project_config: Unrecognized arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + set(__prefix "") + get_target_property(__prefix ${__target} __PROJECT_CONFIG_PREFIX) + if("x${__prefix}" STREQUAL "x") + set(__prefix "${PROJECT_NAME}_FEATURE") + message(AUTHOR_WARNING "add_project_config: config prefix is not set, using ${__prefix} as default.") + endif() + set_property(TARGET ${__target} APPEND PROPERTY __PROJECT_CONFIG_OPTIONS "${__prefix}_${arg_KEY}=${arg_VALUE}") +endfunction() + +function(enable_project_config) + cmake_parse_arguments(arg "" "KEY" "" ${ARGN}) + if(NOT arg_KEY) + message(AUTHOR_WARNING "enable_project_config: You need to specify the KEY name!") + return() + endif() + if(arg_UNPARSED_ARGUMENTS) + message(AUTHOR_WARNING "enable_project_config: Unrecognized arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + add_project_config(KEY "${arg_KEY}" TRUE) +endfunction() + +function(disable_project_config) + cmake_parse_arguments(arg "" "KEY" "" ${ARGN}) + if(NOT arg_KEY) + message(AUTHOR_WARNING "disable_project_config: You need to specify the KEY name!") + return() + endif() + if(arg_UNPARSED_ARGUMENTS) + message(AUTHOR_WARNING "disable_project_config: Unrecognized arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + add_project_config(KEY "${arg_KEY}" FALSE) +endfunction() + +function(generate_project_config) + set(__target ${PROJECT_NAME}_METADATA) + if(NOT TARGET ${__target}) + message(AUTHOR_WARNING "generate_project_config: You need to call setup_project() first!") + return() + endif() + cmake_parse_arguments(arg "OVERWRITE" "PATH" "" ${ARGN}) + if(NOT arg_PATH) + message(AUTHOR_WARNING "generate_project_config: You need to specify the file path!") + return() + endif() + if(arg_UNPARSED_ARGUMENTS) + message(AUTHOR_WARNING "generate_project_config: Unrecognized arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + if(EXISTS "${arg_PATH}" AND NOT arg_OVERWRITE) + return() + endif() + set(__raw_options "") + get_target_property(__raw_options ${__target} __PROJECT_CONFIG_OPTIONS) + if("x${__raw_options}" STREQUAL "x") + message(AUTHOR_WARNING "generate_project_config: You need to specify at least one configuration option!") + return() + endif() + set(__license_header "") + get_target_property(__license_header ${__target} __PROJECT_LICENSE_HEADER) + if("x${__license_header}" STREQUAL "x") + message(AUTHOR_WARNING "generate_project_config: The license header is not set.") + endif() + set(__modify_lost_warning "") + get_target_property(__modify_lost_warning ${__target} __PROJECT_MODIFY_LOST_WARNING) + if("x${__modify_lost_warning}" STREQUAL "x") + message(AUTHOR_WARNING "generate_project_config: The modification lost warning is not set.") + endif() + set(__inc_guard "_${PROJECT_NAME}_CONFIG_INCLUDE_GUARD_") + set(__options "") + foreach(__raw_option ${__raw_options}) + set(__length -1) + string(LENGTH "${__raw_option}" __length) + if(__length LESS_EQUAL 0) + continue() + endif() + set(__index -1) + string(FIND "${__raw_option}" "=" __index) + if(__index LESS_EQUAL 0) + continue() + endif() + set(__option "") + string(SUBSTRING "${__raw_option}" 0 ${__index} __option) + if("x${__option}" STREQUAL "x") + continue() + endif() + math(EXPR __value_index "${__index} + 1") + math(EXPR __value_length "${__length} - ${__value_index}") + set(__value "") + string(SUBSTRING "${__raw_option}" ${__value_index} ${__value_length} __value) + if("x${__value}" STREQUAL "TRUE" OR "x${__value}" STREQUAL "ENABLE" OR "x${__value}" STREQUAL "ON" OR "x${__value}" STREQUAL "YES" OR "x${__value}" STREQUAL "WANT" OR "x${__value}" STREQUAL "1") + set(__value 1) + else() + set(__value -1) + endif() + string(APPEND __options "#define ${__option} ${__value}\n") + endforeach() + set(__config_content "${__license_header} + +${__modify_lost_warning} + +#pragma once + +#ifndef ${__inc_guard} +#define ${__inc_guard} + +${__options} +#endif // ${__inc_guard} +") + file(WRITE "${arg_PATH}" "${__config_content}") +endfunction() + +function(query_git_information) + cmake_parse_arguments(arg "" "HASH;SUBJECT;AUTHOR;DATETIME;BRANCH" "" ${ARGN}) + if(arg_UNPARSED_ARGUMENTS) + message(AUTHOR_WARNING "query_git_information: Unrecognized arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + if(NOT Git_FOUND) + find_package(Git QUIET) + endif() + if(NOT Git_FOUND) + message(AUTHOR_WARNING "query_git_information: Can't find the Git package!") + return() + endif() + #message(STATUS "Git found: ${GIT_EXECUTABLE} (version ${GIT_VERSION_STRING})") + if(arg_HASH) + execute_process( + COMMAND "${GIT_EXECUTABLE}" log -1 --pretty=format:%H + OUTPUT_VARIABLE ${arg_HASH} + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + RESULT_VARIABLE __result_code + ) + endif() + if(arg_SUBJECT) + execute_process( + COMMAND "${GIT_EXECUTABLE}" log -1 --pretty=format:%s + OUTPUT_VARIABLE ${arg_SUBJECT} + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + RESULT_VARIABLE __result_code + ) + endif() + if(arg_AUTHOR) + execute_process( + COMMAND "${GIT_EXECUTABLE}" log -1 --pretty=format:"%aN (%aE)" + OUTPUT_VARIABLE ${arg_AUTHOR} + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + RESULT_VARIABLE __result_code + ) + endif() + if(arg_DATETIME) + execute_process( + COMMAND "${GIT_EXECUTABLE}" log -1 --pretty=format:%aI + OUTPUT_VARIABLE ${arg_DATETIME} + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + RESULT_VARIABLE __result_code + ) + endif() + if(arg_BRANCH) + execute_process( + COMMAND "${GIT_EXECUTABLE}" symbolic-ref --quiet --short HEAD + OUTPUT_VARIABLE ${arg_BRANCH} + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + RESULT_VARIABLE __result_code + ) + endif() +endfunction() + +function(query_compiler_information) + cmake_parse_arguments(arg "" "NAME;VENDOR;VERSION;HOMEPAGE" "" ${ARGN}) + if(arg_UNPARSED_ARGUMENTS) + message(AUTHOR_WARNING "query_compiler_information: Unrecognized arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + set(__lang "C") + set(__id "${CMAKE_${__lang}_COMPILER_ID}") + if("x${__id}" STREQUAL "x") + set(__lang "CXX") + set(__id "${CMAKE_${__lang}_COMPILER_ID}") + endif() + if("x${__id}" STREQUAL "x") + message(AUTHOR_WARNING "query_compiler_information: Can't find a valid C/C++ toolchain!") + return() + endif() + if(arg_VERSION) + set(${arg_VERSION} "${CMAKE_${__lang}_COMPILER_VERSION}" PARENT_SCOPE) + endif() + if(__id MATCHES "Clang") + if(arg_NAME) + set(${arg_NAME} "Clang" PARENT_SCOPE) + endif() + if(__id STREQUAL "AppleClang") + if(arg_VENDOR) + set(${arg_VENDOR} "Apple" PARENT_SCOPE) + endif() + if(arg_HOMEPAGE) + set(${arg_HOMEPAGE} "https://opensource.apple.com/source/clang/" PARENT_SCOPE) + endif() + elseif(__id STREQUAL "ARMClang") + if(arg_VENDOR) + set(${arg_VENDOR} "Arm" PARENT_SCOPE) + endif() + if(arg_HOMEPAGE) + set(${arg_HOMEPAGE} "https://developer.arm.com/documentation/100748/latest/" PARENT_SCOPE) + endif() + elseif(__id STREQUAL "IBMClang" OR __id STREQUAL "XLClang") + if(arg_VENDOR) + set(${arg_VENDOR} "IBM" PARENT_SCOPE) + endif() + if(arg_HOMEPAGE) + set(${arg_HOMEPAGE} "https://www.ibm.com/docs/en/openxl-c-and-cpp-aix" PARENT_SCOPE) + endif() + elseif(__id STREQUAL "Clang") + if(arg_VENDOR) + set(${arg_VENDOR} "LLVM" PARENT_SCOPE) + endif() + if(arg_HOMEPAGE) + set(${arg_HOMEPAGE} "https://clang.llvm.org/" PARENT_SCOPE) + endif() + else() + if(arg_VENDOR) + set(${arg_VENDOR} "UNKNOWN" PARENT_SCOPE) + endif() + if(arg_HOMEPAGE) + set(${arg_HOMEPAGE} "UNKNOWN" PARENT_SCOPE) + endif() + endif() + elseif(__id STREQUAL "GNU") + if(arg_VENDOR) + set(${arg_VENDOR} "GNU Compiler Collection" PARENT_SCOPE) + endif() + if(arg_HOMEPAGE) + set(${arg_HOMEPAGE} "https://gcc.gnu.org/" PARENT_SCOPE) + endif() + elseif(__id STREQUAL "MSVC") + if(arg_VENDOR) + set(${arg_VENDOR} "Microsoft" PARENT_SCOPE) + endif() + if(arg_HOMEPAGE) + set(${arg_HOMEPAGE} "https://visualstudio.microsoft.com/" PARENT_SCOPE) + endif() + elseif(__id MATCHES "Intel") + else() + if(arg_VENDOR) + set(${arg_VENDOR} "UNKNOWN" PARENT_SCOPE) + endif() + if(arg_HOMEPAGE) + set(${arg_HOMEPAGE} "UNKNOWN" PARENT_SCOPE) + endif() + endif() +endfunction() + +function(generate_project_version) + set(__target ${PROJECT_NAME}_METADATA) + if(NOT TARGET ${__target}) + message(AUTHOR_WARNING "generate_project_version: You need to call setup_project() first!") + return() + endif() + cmake_parse_arguments(arg "OVERWRITE;COMMIT_HASH;COMMIT_SUBJECT;COMMIT_AUTHOR;COMMIT_DATETIME;COMMIT_BRANCH;COMPILER_NAME;COMPILER_VENDOR;COMPILER_VERSION;COMPILER_HOMEPAGE" "PATH" "" ${ARGN}) + if(NOT arg_PATH) + message(AUTHOR_WARNING "generate_project_version: You need to specify the file path!") + return() + endif() + if(arg_UNPARSED_ARGUMENTS) + message(AUTHOR_WARNING "generate_project_version: Unrecognized arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + if(EXISTS "${arg_PATH}" AND NOT arg_OVERWRITE) + return() + endif() + set(__license_header "") + get_target_property(__license_header ${__target} __PROJECT_LICENSE_HEADER) + if("x${__license_header}" STREQUAL "x") + message(AUTHOR_WARNING "generate_project_version: The license header is not set.") + endif() + set(__modify_lost_warning "") + get_target_property(__modify_lost_warning ${__target} __PROJECT_MODIFY_LOST_WARNING) + if("x${__modify_lost_warning}" STREQUAL "x") + message(AUTHOR_WARNING "generate_project_version: The modification lost warning is not set.") + endif() + set(__prefix "") + get_target_property(__prefix ${__target} __PROJECT_VERSION_PREFIX) + if("x${__prefix}" STREQUAL "x") + set(__prefix "${PROJECT_NAME}_FEATURE") + message(AUTHOR_WARNING "generate_project_version: config prefix is not set, using ${__prefix} as default.") + endif() + set(__inc_guard "_${PROJECT_NAME}_VERSION_INCLUDE_GUARD_") + set(__versions "") + set(__common_num "[[maybe_unused]] inline constexpr const unsigned long") + set(__common_str "[[maybe_unused]] inline constexpr const char") + string(APPEND __versions "${__common_num} ${__prefix}_VERSION_MAJOR = ${PROJECT_VERSION_MAJOR};\n") + string(APPEND __versions "${__common_num} ${__prefix}_VERSION_MINOR = ${PROJECT_VERSION_MINOR};\n") + string(APPEND __versions "${__common_num} ${__prefix}_VERSION_PATCH = ${PROJECT_VERSION_PATCH};\n") + string(APPEND __versions "${__common_num} ${__prefix}_VERSION_TWEAK = ${PROJECT_VERSION_TWEAK};\n") + string(APPEND __versions "${__common_str} ${__prefix}_VERSION_STR[] = \"${PROJECT_VERSION}\";\n") + string(APPEND __versions "#define __${__prefix}_VERSION_MAJOR__ ${PROJECT_VERSION_MAJOR}\n") + string(APPEND __versions "#define __${__prefix}_VERSION_MINOR__ ${PROJECT_VERSION_MINOR}\n") + string(APPEND __versions "#define __${__prefix}_VERSION_PATCH__ ${PROJECT_VERSION_PATCH}\n") + string(APPEND __versions "#define __${__prefix}_VERSION_TWEAK__ ${PROJECT_VERSION_TWEAK}\n") + string(APPEND __versions "#define __${__prefix}_VERSION__ ${PROJECT_VERSION_MINOR}\n") + if(arg_COMMIT_HASH) + set(__hash "") + query_git_information(HASH __hash) + string(APPEND __versions "${__common_str} ${__prefix}_COMMIT_HASH_STR[] = \"${__hash}\";\n") + endif() + if(arg_COMMIT_SUBJECT) + set(__subject "") + query_git_information(SUBJECT __subject) + string(APPEND __versions "${__common_str} ${__prefix}_COMMIT_SUBJECT_STR[] = \"${__subject}\";\n") + endif() + if(arg_COMMIT_AUTHOR) + set(__author "") + query_git_information(AUTHOR __author) + string(APPEND __versions "${__common_str} ${__prefix}_COMMIT_AUTHOR_STR[] = \"${__author}\";\n") + endif() + if(arg_COMMIT_DATETIME) + set(__datetime "") + query_git_information(DATETIME __datetime) + string(APPEND __versions "${__common_str} ${__prefix}_COMMIT_DATETIME_STR[] = \"${__datetime}\";\n") + endif() + if(arg_COMMIT_BRANCH) + set(__branch "") + query_git_information(BRANCH __branch) + string(APPEND __versions "${__common_str} ${__prefix}_COMMIT_BRANCH_STR[] = \"${__branch}\";\n") + endif() + if(arg_COMPILER_NAME) + set(__name "") + query_compiler_information(NAME __name) + string(APPEND __versions "${__common_str} ${__prefix}_COMPILER_NAME_STR[] = \"${__name}\";\n") + endif() + if(arg_COMPILER_VERSION) + set(__version "") + query_compiler_information(VERSION __version) + string(APPEND __versions "${__common_str} ${__prefix}_COMPILER_VERSION_STR[] = \"${__version}\";\n") + endif() + if(arg_COMPILER_VENDOR) + set(__vendor "") + query_compiler_information(VENDOR __vendor) + string(APPEND __versions "${__common_str} ${__prefix}_COMPILER_VENDOR_STR[] = \"${__vendor}\";\n") + endif() + if(arg_COMPILER_HOMEPAGE) + set(__homepage "") + query_compiler_information(NAME __homepage) + string(APPEND __versions "${__common_str} ${__prefix}_COMPILER_HOMEPAGE_STR[] = \"${__homepage}\";\n") + endif() + set(__version_content "${__license_header} + +${__modify_lost_warning} + +#pragma once + +#ifndef ${__inc_guard} +#define ${__inc_guard} + +${__versions} +#endif // ${__inc_guard} +") + file(WRITE "${arg_PATH}" "${__version_content}") +endfunction()