chore: baseline usable version

This commit is contained in:
dela
2026-06-10 11:17:51 +08:00
commit a2f82a86b6
932 changed files with 172551 additions and 0 deletions

View File

@@ -0,0 +1,238 @@
include_guard(DIRECTORY)
#[[
Disable all possible warnings from the compiler.
qm_compiler_no_warnings()
]] #
macro(qm_compiler_no_warnings)
foreach(__lang C CXX)
if(NOT "x${CMAKE_${__lang}_FLAGS}" STREQUAL "x")
if(MSVC)
string(REGEX REPLACE " [/-]W[01234] " " " CMAKE_${__lang}_FLAGS ${CMAKE_${__lang}_FLAGS})
else()
string(REGEX REPLACE " -W(all)?(extra)? " " " CMAKE_${__lang}_FLAGS ${CMAKE_${__lang}_FLAGS})
string(REGEX REPLACE " -[W]?pedantic " " " CMAKE_${__lang}_FLAGS ${CMAKE_${__lang}_FLAGS})
endif()
endif()
string(APPEND CMAKE_${__lang}_FLAGS " -w ")
endforeach()
if(MSVC)
add_compile_definitions(-D_CRT_NON_CONFORMING_SWPRINTFS)
add_compile_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE)
add_compile_definitions(-D_CRT_NONSTDC_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE)
add_compile_definitions(-D_SCL_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_DEPRECATE)
add_compile_definitions(-D_SILENCE_ALL_MS_EXT_DEPRECATION_WARNINGS)
else()
foreach(__lang C CXX)
string(APPEND CMAKE_${__lang}_FLAGS " -fpermissive ")
endforeach()
endif()
endmacro()
#[[
Enable all possible warnings from the compiler.
qm_compiler_max_warnings()
]] #
function(qm_compiler_max_warnings)
if(MSVC)
add_compile_options(-W4)
elseif("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xClang")
add_compile_options(-Weverything)
else()
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
endfunction()
#[[
Treat all warnings as errors.
qm_compiler_warnings_are_errors()
]] #
function(qm_compiler_warnings_are_errors)
if(MSVC)
add_compile_options(-WX)
else()
add_compile_options(-Werror)
endif()
endfunction()
#[[
Prevent the compiler from receiving unknown parameters.
qm_compiler_no_unknown_options()
]] #
function(qm_compiler_no_unknown_options)
if(MSVC)
if(MSVC_VERSION GREATER_EQUAL 1930) # Visual Studio 2022 version 17.0
add_compile_options(-options:strict)
endif()
add_link_options(-WX)
endif()
endfunction()
#[[
Remove all unused code from the final binary.
qm_compiler_eliminate_dead_code()
]] #
function(qm_compiler_eliminate_dead_code)
if(MSVC)
add_compile_options(-Gw -Gy -Zc:inline)
add_link_options(-OPT:REF -OPT:ICF -OPT:LBR)
else()
add_compile_options(-ffunction-sections -fdata-sections)
if(APPLE)
add_link_options(-Wl,-dead_strip)
else()
add_link_options(-Wl,--as-needed -Wl,--strip-all -Wl,--gc-sections)
endif()
if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xClang")
add_link_options(-Wl,--icf=all)
endif()
endif()
endfunction()
#[[
Only export symbols which are marked to be exported, just like MSVC.
qm_compiler_dont_export_by_default()
]] #
macro(qm_compiler_dont_export_by_default)
set(CMAKE_C_VISIBILITY_PRESET "hidden")
set(CMAKE_CXX_VISIBILITY_PRESET "hidden")
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
endmacro()
#[[
Enable all possible security issue mitigations from your compiler.
qm_compiler_enable_secure_code()
]] #
macro(qm_compiler_enable_secure_code)
if(MSVC)
add_compile_options(-GS -sdl -guard:cf)
add_link_options(-DYNAMICBASE -FIXED:NO -NXCOMPAT -GUARD:CF)
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
add_link_options(-SAFESEH)
endif()
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
add_link_options(-HIGHENTROPYVA)
endif()
if(MSVC_VERSION GREATER_EQUAL 1920) # Visual Studio 2019 version 16.0
add_link_options(-CETCOMPAT)
endif()
if(MSVC_VERSION GREATER_EQUAL 1925) # Visual Studio 2019 version 16.5
add_compile_options(-Qspectre-load)
elseif(MSVC_VERSION GREATER_EQUAL 1912) # Visual Studio 2017 version 15.5
add_compile_options(-Qspectre)
endif()
if(MSVC_VERSION GREATER_EQUAL 1927) # Visual Studio 2019 version 16.7
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
add_compile_options(-guard:ehcont)
add_link_options(-guard:ehcont)
endif()
endif()
if(MSVC_VERSION GREATER_EQUAL 1930) # Visual Studio 2022 version 17.0
add_compile_options(-Qspectre-jmp)
endif()
elseif(MINGW)
if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xClang")
add_compile_options(-mguard=cf)
add_link_options(-mguard=cf)
else()
endif()
else()
add_compile_options(-mshstk -ftrivial-auto-var-init=pattern
-fstack-protector-strong -fstack-clash-protection
-fcf-protection=full)
add_link_options(-Wl,-z,relro,-z,now)
if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xClang")
add_compile_options(-mretpoline -mspeculative-load-hardening)
if(NOT APPLE)
add_compile_options(-fsanitize=cfi -fsanitize-cfi-cross-dso)
endif()
endif()
endif()
endmacro()
#[[
Enable all possible Qt coding style policies.
qm_compiler_enable_strict_qt(
TARGETS <target1> <target2> ... <targetN>
[NO_DEPRECATED_API]
[ALLOW_KEYWORD]
[ALLOW_UNSAFE_FLAGS]
)
NO_DEPRECATED_API: Disable the use of any deprecated Qt APIs. Only has effect since Qt6.
ALLOW_KEYWORD: Allow the use of the traditional Qt keywords such as "signal" "slot" "emit".
ALLOW_UNSAFE_FLAGS: Allow the use of the unsafe QFlags (unsafe: can be implicitly cast to and from "int").
]] #
function(qm_compiler_enable_strict_qt)
cmake_parse_arguments(arg "NO_DEPRECATED_API;ALLOW_KEYWORD;ALLOW_UNSAFE_FLAGS" "" "TARGETS" ${ARGN})
if(NOT arg_TARGETS)
message(AUTHOR_WARNING "qm_compiler_enable_strict_qt: you need to specify at least one target!")
return()
endif()
if(arg_UNPARSED_ARGUMENTS)
message(AUTHOR_WARNING "qm_compiler_enable_strict_qt: Unrecognized arguments: ${arg_UNPARSED_ARGUMENTS}")
endif()
foreach(_target IN LISTS arg_TARGETS)
if(NOT TARGET ${_target})
message(AUTHOR_WARNING "qm_compiler_enable_strict_qt: ${_target} is not a valid CMake target!")
continue()
endif()
target_compile_definitions(${_target} PRIVATE
QT_NO_CAST_TO_ASCII
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_FROM_BYTEARRAY
QT_NO_URL_CAST_FROM_STRING
QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
QT_NO_JAVA_STYLE_ITERATORS
QT_NO_FOREACH QT_NO_QFOREACH
QT_NO_AS_CONST QT_NO_QASCONST
QT_NO_EXCHANGE QT_NO_QEXCHANGE
QT_NO_QPAIR
QT_NO_INTEGRAL_STRINGS
QT_NO_USING_NAMESPACE
QT_NO_CONTEXTLESS_CONNECT
QT_EXPLICIT_QFILE_CONSTRUCTION_FROM_PATH
QT_USE_NODISCARD_FILE_OPEN
QT_USE_QSTRINGBUILDER
QT_USE_FAST_OPERATOR_PLUS
QT_DEPRECATED_WARNINGS # Have no effect since 5.13
QT_DEPRECATED_WARNINGS_SINCE=0x0A0000 # Deprecated since 6.5
QT_WARN_DEPRECATED_UP_TO=0x0A0000 # Available since 6.5
)
if(arg_NO_DEPRECATED_API)
target_compile_definitions(${_target} PRIVATE
QT_DISABLE_DEPRECATED_BEFORE=0x0A0000 # Deprecated since 6.5
QT_DISABLE_DEPRECATED_UP_TO=0x0A0000 # Available since 6.5
)
endif()
# On Windows enabling this flag requires us re-compile Qt with this flag enabled,
# so only enable it on non-Windows platforms.
if(NOT WIN32)
target_compile_definitions(${_target} PRIVATE
QT_STRICT_ITERATORS
)
endif()
# We handle this flag specially because some Qt headers may still use the
# traditional keywords (especially some private headers).
if(NOT arg_ALLOW_KEYWORD)
target_compile_definitions(${_target} PRIVATE
QT_NO_KEYWORDS
)
endif()
# We handle this flag specially because some Qt headers may still use the
# unsafe flags (especially some QtQuick headers).
if(NOT arg_ALLOW_UNSAFE_FLAGS)
target_compile_definitions(${_target} PRIVATE
QT_TYPESAFE_FLAGS
)
endif()
endforeach()
endfunction()

View File

@@ -0,0 +1,421 @@
#[[
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_APPLOCAL_DEPS_PATHS)
set(QMSETUP_APPLOCAL_DEPS_PATHS)
endif()
if(NOT DEFINED QMSETUP_APPLOCAL_DEPS_PATHS_DEBUG)
set(QMSETUP_APPLOCAL_DEPS_PATHS_DEBUG ${QMSETUP_APPLOCAL_DEPS_PATHS})
endif()
if(NOT DEFINED QMSETUP_APPLOCAL_DEPS_PATHS_RELEASE)
set(QMSETUP_APPLOCAL_DEPS_PATHS_RELEASE ${QMSETUP_APPLOCAL_DEPS_PATHS})
endif()
if(NOT DEFINED QMSETUP_APPLOCAL_DEPS_PATHS_RELWITHDEBINFO)
set(QMSETUP_APPLOCAL_DEPS_PATHS_RELWITHDEBINFO ${QMSETUP_APPLOCAL_DEPS_PATHS_RELEASE})
endif()
if(NOT DEFINED QMSETUP_APPLOCAL_DEPS_PATHS_MINSIZEREL)
set(QMSETUP_APPLOCAL_DEPS_PATHS_MINSIZEREL ${QMSETUP_APPLOCAL_DEPS_PATHS_RELEASE})
endif()
include_guard(DIRECTORY)
#[[
Record searching paths for Windows Executables, must be called before calling `qm_win_applocal_deps`
or `qm_deploy_directory` if your project supports Windows.
WARNING: This function is deprecated.
qm_win_record_deps(<target>)
]] #
function(qm_win_record_deps _target)
if(NOT WIN32)
return()
endif()
set(_paths)
get_target_property(_link_libraries ${_target} LINK_LIBRARIES)
foreach(_item IN LISTS _link_libraries)
if(NOT TARGET ${_item})
continue()
endif()
get_target_property(_imported ${_item} IMPORTED)
if(_imported)
get_target_property(_path ${_item} LOCATION)
if(NOT _path OR NOT ${_path} MATCHES "\\.dll$")
continue()
endif()
set(_path "$<TARGET_PROPERTY:${_item},LOCATION_$<CONFIG>>")
else()
get_target_property(_type ${_item} TYPE)
if(NOT ${_type} MATCHES "SHARED_LIBRARY")
continue()
endif()
set(_path "$<TARGET_FILE:${_item}>")
endif()
list(APPEND _paths ${_path})
endforeach()
if(NOT _paths)
return()
endif()
set(_deps_file "${CMAKE_CURRENT_BINARY_DIR}/${_target}_deps_$<CONFIG>.txt")
file(GENERATE OUTPUT ${_deps_file} CONTENT "$<JOIN:${_paths},\n>")
set_target_properties(${_target} PROPERTIES QMSETUP_DEPENDENCIES_FILE ${_deps_file})
endfunction()
#[[
Automatically copy dependencies for Windows Executables after build.
qm_win_applocal_deps(<target>
[CUSTOM_TARGET <target>]
[FORCE] [VERBOSE]
[EXTRA_SEARCHING_PATHS <path...>]
[EXTRA_TARGETS <target...>]
[OUTPUT_DIR <dir>]
[EXCLUDE <pattern...>]
)
]] #
function(qm_win_applocal_deps _target)
if(NOT WIN32)
return()
endif()
set(options FORCE VERBOSE)
set(oneValueArgs TARGET CUSTOM_TARGET OUTPUT_DIR)
set(multiValueArgs EXTRA_SEARCHING_PATHS EXTRA_TARGETS EXCLUDE)
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# Get output directory and deploy target
set(_out_dir)
set(_deploy_target)
if(FUNC_CUSTOM_TARGET)
set(_deploy_target ${FUNC_CUSTOM_TARGET})
if(NOT TARGET ${_deploy_target})
add_custom_target(${_deploy_target})
endif()
else()
set(_deploy_target ${_target})
endif()
if(FUNC_OUTPUT_DIR)
set(_out_dir ${FUNC_OUTPUT_DIR})
else()
set(_out_dir "$<TARGET_FILE_DIR:${_target}>")
endif()
if(NOT _out_dir)
message(FATAL_ERROR "qm_win_applocal_deps: cannot determine output directory.")
endif()
# Get dep files
set(_dep_files)
_qm_win_get_all_dep_files(_dep_files ${_target})
foreach(_item IN LISTS FUNC_EXTRA_TARGETS)
_qm_win_get_all_dep_files(_dep_files ${_item})
endforeach()
# Prepare command
set(_args)
if(FUNC_FORCE)
list(APPEND _args -f)
endif()
if(FUNC_VERBOSE)
list(APPEND _args -V)
endif()
# Add extra searching paths
foreach(_item IN LISTS FUNC_EXTRA_SEARCHING_PATHS)
list(APPEND _args "-L${_item}")
endforeach()
# Add global extra searching paths
if(CMAKE_BUILD_TYPE)
string(TOUPPER ${CMAKE_BUILD_TYPE} _build_type_upper)
if(QMSETUP_APPLOCAL_DEPS_PATHS_${_build_type_upper})
foreach(_item IN LISTS QMSETUP_APPLOCAL_DEPS_PATHS_${_build_type_upper})
get_filename_component(_item ${_item} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
list(APPEND _args "-L${_item}")
endforeach()
elseif(QMSETUP_APPLOCAL_DEPS_PATHS)
foreach(_item IN LISTS QMSETUP_APPLOCAL_DEPS_PATHS)
get_filename_component(_item ${_item} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
list(APPEND _args "-L${_item}")
endforeach()
endif()
else()
foreach(_item IN LISTS QMSETUP_APPLOCAL_DEPS_PATHS)
get_filename_component(_item ${_item} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
list(APPEND _args "-L${_item}")
endforeach()
endif()
foreach(_item IN LISTS _dep_files)
list(APPEND _args "--linkdirs-file" "${_item}")
endforeach()
foreach(_item IN LISTS FUNC_EXCLUDE)
list(APPEND _args -e ${_item})
endforeach()
list(APPEND _args "$<TARGET_FILE:${_target}>")
add_custom_command(TARGET ${_deploy_target} POST_BUILD
COMMAND ${QMSETUP_CORECMD_EXECUTABLE} deploy ${_args}
WORKING_DIRECTORY ${_out_dir}
VERBATIM
)
endfunction()
#[[
Add deploy command when install project, not available in debug mode.
qm_deploy_directory(<install_dir>
[FORCE] [STANDARD] [VERBOSE]
[LIBRARY_DIR <dir>]
[EXTRA_LIBRARIES <path>...]
[EXTRA_PLUGIN_PATHS <path>...]
[EXTRA_SEARCHING_PATHS <path>...]
[PLUGINS <plugin>...]
[PLUGIN_DIR <dir>]
[QML <qml>...]
[QML_DIR <dir>]
[WIN_TARGETS <target>...]
[COMMENT <comment>]
)
PLUGINS: Qt plugins, in format of `<category>/<name>`
PLUGIN_DIR: Qt plugins destination
EXTRA_PLUGIN_PATHS: Extra Qt plugins searching paths
QML: Qt qml directories
QML_DIR: Qt qml destination
LIBRARY_DIR: Library destination
EXTRA_LIBRARIES Extra library names list to deploy
EXTRA_SEARCHING_PATHS: Extra library searching paths
]] #
function(qm_deploy_directory _install_dir)
set(options FORCE STANDARD VERBOSE)
set(oneValueArgs LIBRARY_DIR PLUGIN_DIR QML_DIR COMMENT)
set(multiValueArgs EXTRA_PLUGIN_PATHS PLUGINS QML WIN_TARGETS EXTRA_SEARCHING_PATHS EXTRA_LIBRARIES)
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# Get qmake
if((FUNC_PLUGINS OR FUNC_QML) AND NOT DEFINED QT_QMAKE_EXECUTABLE)
if(TARGET Qt${QT_VERSION_MAJOR}::qmake)
qm_get_executable_location(Qt${QT_VERSION_MAJOR}::qmake _qmake_path)
set(QT_QMAKE_EXECUTABLE "${_qmake_path}" CACHE FILEPATH "Path to qmake executable" FORCE)
elseif((FUNC_PLUGINS AND NOT FUNC_EXTRA_PLUGIN_PATHS) OR FUNC_QML)
message(FATAL_ERROR "qm_deploy_directory: qmake not defined. Add find_package(Qt5 COMPONENTS Core) to CMake to enable.")
endif()
endif()
# Set values
qm_set_value(_lib_dir FUNC_LIBRARY_DIR "${_install_dir}/${QMSETUP_SHARED_LIBRARY_CATEGORY}")
qm_set_value(_plugin_dir FUNC_PLUGIN_DIR "${_install_dir}/plugins")
qm_set_value(_qml_dir FUNC_QML_DIR "${_install_dir}/qml")
# Prepare commands
set(_args
-i "${_install_dir}"
-m "${QMSETUP_CORECMD_EXECUTABLE}"
--plugindir "${_plugin_dir}"
--libdir "${_lib_dir}"
--qmldir "${_qml_dir}"
)
set(_searching_paths)
if(QT_QMAKE_EXECUTABLE)
list(APPEND _args --qmake "${QT_QMAKE_EXECUTABLE}")
endif()
# Add Qt plugins
foreach(_item IN LISTS FUNC_PLUGINS)
list(APPEND _args --plugin "${_item}")
endforeach()
# Add QML modules
foreach(_item IN LISTS FUNC_QML)
list(APPEND _args --qml "${_item}")
endforeach()
# Add extra plugin paths
foreach(_item IN LISTS FUNC_EXTRA_PLUGIN_PATHS)
list(APPEND _args --extra "${_item}")
endforeach()
# Add extra searching paths
foreach(_item IN LISTS FUNC_EXTRA_SEARCHING_PATHS)
get_filename_component(_item ${_item} ABSOLUTE)
list(APPEND _searching_paths ${_item})
endforeach()
# Add global extra searching paths
if(CMAKE_BUILD_TYPE)
string(TOUPPER ${CMAKE_BUILD_TYPE} _build_type_upper)
if(QMSETUP_APPLOCAL_DEPS_PATHS_${_build_type_upper})
foreach(_item IN LISTS QMSETUP_APPLOCAL_DEPS_PATHS_${_build_type_upper})
get_filename_component(_item ${_item} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
list(APPEND _searching_paths ${_item})
endforeach()
elseif(QMSETUP_APPLOCAL_DEPS_PATHS)
foreach(_item IN LISTS QMSETUP_APPLOCAL_DEPS_PATHS)
get_filename_component(_item ${_item} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
list(APPEND _searching_paths ${_item})
endforeach()
endif()
else()
foreach(_item IN LISTS QMSETUP_APPLOCAL_DEPS_PATHS)
get_filename_component(_item ${_item} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
list(APPEND _searching_paths ${_item})
endforeach()
endif()
foreach(_item IN LISTS _searching_paths)
list(APPEND _args -L "${_item}")
endforeach()
if(WIN32)
set(_dep_files)
if(FUNC_WIN_TARGETS)
_qm_win_get_all_dep_files(_dep_files ${FUNC_WIN_TARGETS})
endif()
foreach(_item IN LISTS _dep_files)
list(APPEND _args "--linkdirs-file" "${_item}")
endforeach()
set(_script_quoted "cmd /c \"${QMSETUP_MODULES_DIR}/scripts/windeps.bat\"")
else()
set(_script_quoted "bash \"${QMSETUP_MODULES_DIR}/scripts/unixdeps.sh\"")
endif()
# Add extra libraries
foreach(_item IN LISTS _searching_paths)
foreach(_lib IN LISTS FUNC_EXTRA_LIBRARIES)
set(_path "${_item}/${_lib}")
if((EXISTS ${_path}) AND(NOT IS_DIRECTORY ${_path}))
list(APPEND _args --copy ${_path} ${_lib_dir})
endif()
endforeach()
endforeach()
# Add options
if(FUNC_FORCE)
list(APPEND _args "-f")
endif()
if(FUNC_STANDARD)
list(APPEND _args "-s")
endif()
if(FUNC_VERBOSE)
list(APPEND _args "-V")
endif()
set(_args_quoted)
foreach(_item IN LISTS _args)
set(_args_quoted "${_args_quoted}\"${_item}\" ")
endforeach()
set(_comment_code)
if(FUNC_COMMENT)
set(_comment_code "message(STATUS \"${FUNC_COMMENT}\")")
endif()
# Add install command
install(CODE "
${_comment_code}
execute_process(
COMMAND ${_script_quoted} ${_args_quoted}
WORKING_DIRECTORY \"${_install_dir}\"
COMMAND_ERROR_IS_FATAL ANY
)
")
endfunction()
# ----------------------------------
# Private functions
# ----------------------------------
function(_qm_win_get_all_dep_files _out)
# Get searching paths
macro(get_recursive_dynamic_dependencies _current_target _result)
get_target_property(_deps ${_current_target} LINK_LIBRARIES)
if(_deps)
foreach(_dep IN LISTS _deps)
if(NOT TARGET ${_dep})
continue()
endif()
get_target_property(_type ${_dep} TYPE)
if(_type STREQUAL "SHARED_LIBRARY")
list(APPEND ${_result} ${_dep})
endif()
get_recursive_dynamic_dependencies(${_dep} ${_result})
endforeach()
endif()
endmacro()
set(_visited_targets ${ARGN})
foreach(_target ${ARGN})
set(_all_deps)
get_recursive_dynamic_dependencies(${_target} _all_deps)
foreach(_cur_dep IN LISTS _all_deps)
if(${_cur_dep} IN_LIST _visited_targets)
continue()
endif()
list(APPEND _visited_targets ${_cur_dep})
endforeach()
endforeach()
set(_dep_files)
foreach(_target IN LISTS _visited_targets)
# Add file
get_target_property(_file ${_target} QMSETUP_DEPENDENCIES_FILE)
if(NOT _file)
continue()
endif()
list(APPEND _dep_files ${_file})
endforeach()
set(${_out} ${_dep_files} PARENT_SCOPE)
endfunction()

View File

@@ -0,0 +1,235 @@
include_guard(DIRECTORY)
#[[
Add Doxygen documentation generating target.
qm_setup_doxygen(<target>
[NAME <name>]
[VERSION <version>]
[DESCRIPTION <desc>]
[LOGO <file>]
[MDFILE <file>]
[OUTPUT_DIR <dir>]
[INSTALL_DIR <dir>]
[TAGFILES <file> ...]
[GENERATE_TAGFILE <file>]
[INPUT <file> ...]
[INCLUDE_DIRECTORIES <dir> ...]
[COMPILE_DEFINITIONS <NAME=VALUE> ...]
[TARGETS <target> ...]
[ENVIRONMENT_EXPORTS <key> ...]
[NO_EXPAND_MACROS <macro> ...]
[DEPENDS <dependency> ...]
)
]] #
function(qm_setup_doxygen _target)
set(options)
set(oneValueArgs NAME VERSION DESCRIPTION LOGO MDFILE OUTPUT_DIR INSTALL_DIR GENERATE_TAGFILE)
set(multiValueArgs INPUT TAGFILES INCLUDE_DIRECTORIES COMPILE_DEFINITIONS TARGETS ENVIRONMENT_EXPORTS
NO_EXPAND_MACROS DEPENDS
)
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT DOXYGEN_EXECUTABLE)
message(FATAL_ERROR "qm_setup_doxygen: DOXYGEN_EXECUTABLE not defined. Add find_package(Doxygen) to CMake to enable.")
endif()
set(DOXYGEN_FILE_DIR ${QMSETUP_MODULES_DIR}/doxygen)
if(FUNC_NAME)
set(_name ${FUNC_NAME})
else()
set(_name ${PROJECT_NAME})
endif()
if(FUNC_VERSION)
set(_version ${FUNC_VERSION})
else()
set(_version ${PROJECT_VERSION})
endif()
if(FUNC_DESCRIPTION)
set(_desc ${FUNC_DESCRIPTION})
elseif(PROJECT_DESCRIPTION)
set(_desc ${PROJECT_DESCRIPTION})
else()
set(_desc ${_name})
endif()
if(FUNC_LOGO)
set(_logo ${FUNC_LOGO})
else()
set(_logo)
endif()
if(FUNC_MDFILE)
set(_mdfile ${FUNC_MDFILE})
else()
set(_mdfile)
endif()
if(FUNC_GENERATE_TAGFILE)
set(_tagfile ${FUNC_GENERATE_TAGFILE})
else()
set(_tagfile)
endif()
set(_sep " \\\n ")
# Generate include file
set(_doxy_includes "${CMAKE_CURRENT_BINARY_DIR}/cmake/doxygen_${_target}_$<CONFIG>.inc")
set(_doxy_output_dir "${CMAKE_CURRENT_BINARY_DIR}/doxygen_${_target}")
set(_input "")
set(_tagfiles "")
set(_includes "")
set(_defines "")
set(_no_expand "")
if(FUNC_INPUT)
set(_input "INPUT = $<JOIN:${FUNC_INPUT},${_sep}>\n\n")
else()
set(_input "INPUT = \n\n")
endif()
if(FUNC_TAGFILES)
set(_tagfiles "TAGFILES = $<JOIN:${FUNC_TAGFILES},${_sep}>\n\n")
else()
set(_tagfiles "TAGFILES = \n\n")
endif()
if(FUNC_INCLUDE_DIRECTORIES)
set(_includes "INCLUDE_PATH = $<JOIN:${FUNC_INCLUDE_DIRECTORIES},${_sep}>\n\n")
else()
set(_includes "INCLUDE_PATH = \n\n")
endif()
if(FUNC_COMPILE_DEFINITIONS)
set(_defines "PREDEFINED = $<JOIN:${FUNC_COMPILE_DEFINITIONS},${_sep}>\n\n")
else()
set(_defines "PREDEFINED = \n\n")
endif()
if(FUNC_NO_EXPAND_MACROS)
set(_temp_list)
foreach(_item IN LISTS FUNC_NO_EXPAND_MACROS)
list(APPEND _temp_list "${_item}=")
endforeach()
set(_no_expand "PREDEFINED += $<JOIN:${_temp_list},${_sep}>\n\n")
unset(_temp_list)
endif()
# Extra
set(_extra_arguments)
if(FUNC_TARGETS)
foreach(item IN LISTS FUNC_TARGETS)
set(_extra_arguments
"${_extra_arguments}INCLUDE_PATH += $<JOIN:$<TARGET_PROPERTY:${item},INCLUDE_DIRECTORIES>,${_sep}>\n\n")
set(_extra_arguments
"${_extra_arguments}PREDEFINED += $<JOIN:$<TARGET_PROPERTY:${item},COMPILE_DEFINITIONS>,${_sep}>\n\n")
endforeach()
endif()
if(FUNC_OUTPUT_DIR)
set(_doxy_output_dir ${FUNC_OUTPUT_DIR})
endif()
if(_mdfile)
set(_extra_arguments "${_extra_arguments}INPUT += ${_mdfile}\n\n")
endif()
file(GENERATE
OUTPUT "${_doxy_includes}"
CONTENT "${_input}${_tagfiles}${_includes}${_defines}${_extra_arguments}${_no_expand}"
)
set(_env)
foreach(_export IN LISTS FUNC_ENVIRONMENT_EXPORTS)
if(NOT DEFINED "${_export}")
message(FATAL_ERROR "qm_setup_doxygen: ${_export} is not known when trying to export it.")
endif()
list(APPEND _env "${_export}=${${_export}}")
endforeach()
list(APPEND _env "DOXY_FILE_DIR=${DOXYGEN_FILE_DIR}")
list(APPEND _env "DOXY_INCLUDE_FILE=${_doxy_includes}")
list(APPEND _env "DOXY_PROJECT_NAME=${_name}")
list(APPEND _env "DOXY_PROJECT_VERSION=${_version}")
list(APPEND _env "DOXY_PROJECT_BRIEF=${_desc}")
list(APPEND _env "DOXY_PROJECT_LOGO=${_logo}")
list(APPEND _env "DOXY_MAINPAGE_MD_FILE=${_mdfile}")
set(_build_command "${CMAKE_COMMAND}" "-E" "env"
${_env}
"DOXY_OUTPUT_DIR=${_doxy_output_dir}"
"DOXY_GENERATE_TAGFILE=${_tagfile}"
"${DOXYGEN_EXECUTABLE}"
"${DOXYGEN_FILE_DIR}/Doxyfile"
)
if(FUNC_DEPENDS)
set(_dependencies DEPENDS ${FUNC_DEPENDS})
endif()
if(_tagfile)
get_filename_component(_tagfile_dir ${_tagfile} ABSOLUTE)
get_filename_component(_tagfile_dir ${_tagfile_dir} DIRECTORY)
set(_make_tagfile_dir_cmd COMMAND ${CMAKE_COMMAND} -E make_directory ${_tagfile_dir})
else()
set(_make_tagfile_dir_cmd)
endif()
add_custom_target(${_target}
COMMAND ${CMAKE_COMMAND} -E make_directory ${_doxy_output_dir}
${_make_tagfile_dir_cmd}
COMMAND ${_build_command}
COMMENT "Building HTML documentation"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
VERBATIM
${_dependencies}
)
if(FUNC_INSTALL_DIR)
set(_install_dir ${FUNC_INSTALL_DIR})
if(_tagfile)
get_filename_component(_name ${_tagfile} NAME)
set(_install_tagfile ${_name})
else()
set(_install_tagfile)
endif()
set(_install_command "${CMAKE_COMMAND}" "-E" "env"
${_env}
"DOXY_OUTPUT_DIR=\${_install_dir}"
"DOXY_GENERATE_TAGFILE=\${_install_dir}/${_install_tagfile}"
"${DOXYGEN_EXECUTABLE}"
"${DOXYGEN_FILE_DIR}/Doxyfile"
)
set(_install_command_quoted)
foreach(_item IN LISTS _install_command)
set(_install_command_quoted "${_install_command_quoted}\"${_item}\" ")
endforeach()
install(CODE "
message(STATUS \"Installing HTML documentation\")
get_filename_component(_install_dir \"${_install_dir}\" ABSOLUTE BASE_DIR \${CMAKE_INSTALL_PREFIX})
file(MAKE_DIRECTORY \${_install_dir})
execute_process(
COMMAND ${_install_command_quoted}
WORKING_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}\"
OUTPUT_QUIET
)
")
endif()
endfunction()

View File

@@ -0,0 +1,199 @@
include_guard(DIRECTORY)
if(NOT QMSETUP_MODULES_DIR)
get_filename_component(QMSETUP_MODULES_DIR ${CMAKE_CURRENT_LIST_DIR} DIRECTORY)
endif()
#[[
Initialize the build output directories of targets and resources.
qm_init_directories()
]] #
macro(qm_init_directories)
if(NOT DEFINED QMSETUP_BUILD_DIR)
set(QMSETUP_BUILD_DIR "${CMAKE_BINARY_DIR}/out-$<LOWER_CASE:${CMAKE_SYSTEM_PROCESSOR}>-$<CONFIG>")
endif()
if(NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${QMSETUP_BUILD_DIR}/bin)
endif()
if(NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${QMSETUP_BUILD_DIR}/lib)
endif()
if(NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${QMSETUP_BUILD_DIR}/lib)
endif()
if(NOT DEFINED CMAKE_BUILD_SHARE_DIR)
set(CMAKE_BUILD_SHARE_DIR ${QMSETUP_BUILD_DIR}/share)
endif()
endmacro()
#[[
Add a resources copying command for whole project.
qm_add_copy_command(<target>
[CUSTOM_TARGET <target>]
[EXTRA_ARGS <args...>]
[DEPENDS <targets...>]
[VERBOSE] [SKIP_BUILD] [SKIP_INSTALL]
SOURCES <file/dir...> [DESTINATION <dir>] [INSTALL_DIR <dir>]
)
CUSTOM_TARGET: Use a custom target to control the copy command
EXTRA_ARGS: Extra arguments to pass to file(INSTALL) statement
DEPENDS: Targets that the copy command depends
SOURCES: Source files or directories, directories ending with "/" will have their contents copied
DESTINATION: Copy the source file to the destination path. If the given value is a relative path,
the base directory depends on the type of the target
- `$<TARGET_FILE_DIR>`: real target
- `QMSETUP_BUILD_DIR`: custom target
INSTALL_DIR: Install the source files into a subdirectory in the given path. The subdirectory is the
relative path from the `QMSETUP_BUILD_DIR` to `DESTINATION`.
]] #
function(qm_add_copy_command _target)
set(options VERBOSE SKIP_BUILD SKIP_INSTALL)
set(oneValueArgs CUSTOM_TARGET DESTINATION INSTALL_DIR)
set(multiValueArgs SOURCES EXTRA_ARGS DEPENDS)
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT FUNC_SOURCES)
message(FATAL_ERROR "qm_add_copy_command: SOURCES not specified.")
endif()
if(NOT TARGET ${_target})
add_custom_target(${_target})
endif()
# Determine destination
set(_dest .)
if(FUNC_DESTINATION)
set(_dest ${FUNC_DESTINATION})
endif()
# Determine destination base directory
get_target_property(_type ${_target} TYPE)
if(_type STREQUAL "UTILITY")
if(QMSETUP_BUILD_DIR)
set(_dest_base ${QMSETUP_BUILD_DIR})
else()
set(_dest_base ${CMAKE_CURRENT_SOURCE_DIR})
endif()
else()
set(_dest_base "$<TARGET_FILE_DIR:${_target}>")
endif()
# Set deploy target
if(FUNC_CUSTOM_TARGET)
set(_deploy_target ${FUNC_CUSTOM_TARGET})
if(NOT TARGET ${_deploy_target})
add_custom_target(${_deploy_target})
endif()
else()
set(_deploy_target ${_target})
endif()
if(FUNC_DEPENDS)
add_dependencies(${_deploy_target} ${FUNC_DEPENDS})
endif()
# Prepare arguments
set(_extra_args)
set(_ignore_stdout)
if(FUNC_EXTRA_ARGS)
list(APPEND _extra_args -D "args=${FUNC_EXTRA_ARGS}")
endif()
if(NOT FUNC_VERBOSE)
set(_ignore_stdout ${QMSETUP_IGNORE_STDOUT})
endif()
if(NOT FUNC_SKIP_BUILD)
# Build phase
add_custom_command(TARGET ${_deploy_target} POST_BUILD
COMMAND ${CMAKE_COMMAND}
-D "src=${FUNC_SOURCES}"
-D "dest=${_dest}"
-D "dest_base=${_dest_base}"
${_extra_args}
-P "${QMSETUP_MODULES_DIR}/scripts/copy.cmake" ${_ignore_stdout}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
VERBATIM
)
endif()
if(FUNC_INSTALL_DIR AND NOT FUNC_SKIP_INSTALL)
if(NOT QMSETUP_BUILD_DIR)
message(FATAL_ERROR "qm_add_copy_command: `QMSETUP_BUILD_DIR` not defined, the install directory cannot be determined.")
endif()
# Install phase
install(CODE "
set(_src \"${FUNC_SOURCES}\")
set(_extra_args \"${FUNC_EXTRA_ARGS}\")
# Calculate the relative path from build phase destination to build directory
get_filename_component(_build_dir \"${_dest}\" ABSOLUTE BASE_DIR \"${_dest_base}\")
file(RELATIVE_PATH _rel_path \"${QMSETUP_BUILD_DIR}\" \${_build_dir})
# Calculate real install directory
get_filename_component(_dest \"${FUNC_INSTALL_DIR}/\${_rel_path}\" ABSOLUTE BASE_DIR \${CMAKE_INSTALL_PREFIX})
execute_process(
COMMAND \${CMAKE_COMMAND}
-D \"src=\${_src}\"
-D \"dest=\${_dest}\"
\${_extra_args}
-P \"${QMSETUP_MODULES_DIR}/scripts/copy.cmake\"
WORKING_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}\"
)
")
endif()
endfunction()
#[[
Add a custom command to run `configure_file`.
qm_future_configure_file(<_input> <output>
[FORCE]
[EXTRA_ARGS <args...>]
[DEPENDS <deps...>]
[VARIABLES <var...>]
)
]] #
function(qm_future_configure_file _input _output)
set(options FORCE)
set(oneValueArgs)
set(multiValueArgs VARIABLES EXTRA_ARGS DEPENDS)
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
set(_options)
foreach(_item IN LISTS FUNC_VARIABLES)
list(APPEND _options -D "${_item}=${${_item}}")
endforeach()
if(FUNC_FORCE)
list(APPLE _options -D "force=TRUE")
endif()
add_custom_command(OUTPUT ${_output}
COMMAND ${CMAKE_COMMAND}
-D "input=${_input}"
-D "output=${_output}"
-D "args=${FUNC_EXTRA_ARGS}"
${_options}
-P "${QMSETUP_MODULES_DIR}/scripts/configure_file.cmake"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${FUNC_DEPENDS}
VERBATIM
)
endfunction()

View File

@@ -0,0 +1,538 @@
#[[
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(<src> <dest>
[STANDARD] [NO_STANDARD] [NO_ALL]
[INCLUDE <expr> <sub> ...]
[EXCLUDE <expr...>]
[INSTALL_DIR <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})
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( <key | key=value> | <key> <value>
[GLOBAL | TARGET <target> | SOURCE <file> | DIRECTORY <dir>]
[PROPERTY <prop>]
[CONDITION <cond...>]
[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(<key>
[GLOBAL | TARGET <target> | SOURCE <file> | DIRECTORY <dir>]
[PROPERTY <prop>]
)
]] #
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(<file>
[GLOBAL | TARGET <target> | SOURCE <file> | DIRECTORY <dir>]
[PROPERTY <prop>]
[PROJECT_NAME <name>]
[WARNING_FILE <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(<file>
[REQUIRED]
[ROOT_DIRECTORY <dir>]
[PREFIX <prefix>]
[YEAR] [TIME]
[PROJECT_NAME <name>]
[WARNING_FILE <file>]
[NO_WARNING]
[NO_HASH]
)
file: Output file
REQUIRED: Abort if there's any error with git
ROOT_DIRECTORY: Repository root directory (CMake will try to run `git` at this directory)
PREFIX: Macros prefix, default to the upper case of `PROJECT_NAME`
]] #
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)
string(MAKE_C_IDENTIFIER ${_prefix} _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")
set(_git_revision_id "0")
# Check `git` command
if(NOT GIT_EXECUTABLE)
find_package(Git)
if(NOT Git_FOUND)
if(FUNC_REQUIRED)
message(FATAL_ERROR "Git not found")
endif()
endif()
endif()
if(GIT_EXECUTABLE)
# 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 "Error running git symbolic-ref: ${_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 "Error running git log: ${_err}")
endif()
# Revision ID
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-list --count 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_revision_id ${_temp})
elseif(FUNC_REQUIRED)
message(FATAL_ERROR "Error running git rev-list: ${_err}")
endif()
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 UTC)
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}\")
list(APPEND _definitions ${_prefix}_GIT_REVISION_ID=\"${_git_revision_id}\")
_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()
if(FUNC_PROJECT_NAME)
list(APPEND _args "-p" ${FUNC_PROJECT_NAME})
endif()
if(FUNC_NO_HASH)
list(APPEND _args "-f")
endif()
list(APPEND _args ${_file})
if(NOT FUNC_NO_WARNING)
list(APPEND _args "-w")
if(FUNC_WARNING_FILE)
list(APPEND _args ${FUNC_WARNING_FILE})
endif()
endif()
execute_process(COMMAND ${QMSETUP_CORECMD_EXECUTABLE} configure ${_args}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND_ERROR_IS_FATAL ANY
)
endfunction()

View File

@@ -0,0 +1,141 @@
include_guard(DIRECTORY)
#[[
Add rules for creating Google Protobuf source files.
qm_create_protobuf(<OUT>
INPUT <files...>
[OUTPUT_DIR <dir>]
[TARGET <target>]
[INCLUDE_DIRECTORIES <dirs...>]
[OPTIONS <options...>]
[DEPENDS <deps...>]
[CREATE_ONCE]
)
INPUT: source files
OUTPUT_DIR: output directory
TARGET: add a custom target to run the generating command
INCLUDE_DIRECTORIES: extra include directories
OPTIONS: extra options passed to protobuf compiler
DEPENDS: dependencies
CREATE_ONCE: create proto code files at configure phase if not exist
OUT: output source file paths
#]]
function(qm_create_protobuf _out)
set(options CREATE_ONCE)
set(oneValueArgs OUTPUT_DIR TARGET)
set(multiValueArgs INPUT INCLUDE_DIRECTORIES OPTIONS DEPENDS)
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# Find `protoc`
if(NOT PROTOC_EXECUTABLE)
if(NOT TARGET protobuf::protoc)
message(FATAL_ERROR "qm_create_protobuf: protobuf compiler not found. Add find_package(Protobuf) to CMake to enable.")
endif()
get_target_property(PROTOC_EXECUTABLE protobuf::protoc LOCATION)
if(NOT PROTOC_EXECUTABLE)
message(FATAL_ERROR "qm_create_protobuf: failed to get the location of `protoc`, you could set PROTOC_EXECUTABLE manually.")
endif()
# Cache value
set(PROTOC_EXECUTABLE ${PROTOC_EXECUTABLE} PARENT_SCOPE)
endif()
if(NOT FUNC_INPUT)
message(FATAL_ERROR "qm_create_protobuf: INPUT not specified.")
endif()
if(FUNC_OUTPUT_DIR)
get_filename_component(_out_dir ${FUNC_OUTPUT_DIR} ABSOLUTE)
file(MAKE_DIRECTORY ${_out_dir})
else()
set(_out_dir ${CMAKE_CURRENT_BINARY_DIR})
endif()
# Collect include paths and out files
set(_include_options)
set(_out_files)
set(_input_names)
foreach(_item IN LISTS FUNC_INCLUDE_DIRECTORIES)
get_filename_component(_abs_path ${_item} ABSOLUTE)
list(APPEND _include_options -I${_abs_path})
endforeach()
foreach(_item IN LISTS FUNC_INPUT)
get_filename_component(_item ${_item} ABSOLUTE)
get_filename_component(_abs_path ${_item} DIRECTORY)
list(APPEND _include_options -I${_abs_path})
endforeach()
list(REMOVE_DUPLICATES _include_options)
set(_create_once_warning)
set(_create_once_warning_printed off)
# Prepare for create once
if(FUNC_CREATE_ONCE)
# Check if options contain generator expressions
foreach(_opt IN LISTS _include_options LISTS FUNC_OPTIONS)
string(GENEX_STRIP "${_opt}" _no_genex)
if(NOT _no_genex STREQUAL _opt)
set(_create_once_warning "options contain generator expressions, skip generating source file now")
break()
endif()
endforeach()
endif()
foreach(_item IN LISTS FUNC_INPUT)
get_filename_component(_basename ${_item} NAME_WLE)
list(APPEND _out_files ${_out_dir}/${_basename}.pb.h ${_out_dir}/${_basename}.pb.cc)
get_filename_component(_name ${_item} NAME)
list(APPEND _input_names ${_name})
if(FUNC_CREATE_ONCE AND(NOT EXISTS ${_out_dir}/${_basename}.pb.h OR NOT EXISTS ${_out_dir}/${_basename}.pb.cc))
if(_create_once_warning)
if(NOT _create_once_warning_printed)
message(WARNING "qm_create_protobuf: ${_create_once_warning}")
set(_create_once_warning_printed on)
endif()
else()
get_filename_component(_abs_file ${_item} ABSOLUTE)
if(NOT EXISTS ${_abs_file})
message(WARNING "qm_create_protobuf: input file \"${_name}\" is not available, skip generating source file now")
else()
message(STATUS "Protoc: Generating ${_basename}.pb.h, ${_basename}.pb.cc")
execute_process(COMMAND
${PROTOC_EXECUTABLE} --cpp_out=${_out_dir} ${_include_options} ${FUNC_OPTIONS} ${_name}
)
endif()
endif()
endif()
add_custom_command(
OUTPUT ${_out_dir}/${_basename}.pb.h ${_out_dir}/${_basename}.pb.cc
COMMAND ${PROTOC_EXECUTABLE} --cpp_out=${_out_dir} ${_include_options} ${FUNC_OPTIONS} ${_name}
DEPENDS ${_item} ${FUNC_DEPENDS}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
VERBATIM
)
endforeach()
if(FUNC_TARGET)
if(NOT TARGET ${FUNC_TARGET})
add_custom_target(${FUNC_TARGET})
endif()
add_dependencies(${FUNC_TARGET} ${_out_files})
endif()
set(${_out} ${_out_files} PARENT_SCOPE)
endfunction()

View File

@@ -0,0 +1,96 @@
include_guard(DIRECTORY)
#[[
Installs a QML module and its runtime loadable plugin, meta information, QML files, and resources.
The module is identified by the given target name.
The Qt version should be greater than or equal to 6.3.
qm_install_qml_modules(target
[PREFIX prefix]
)
Arguments:
PREFIX: install directory prefix (default: "qml")
Notice:
For static library backing targets, you should specify "OUTPUT_TARGETS" when calling "qt_add_qml_module()"
to collect the internally generated targets (mainly object libraries), and then install them by calling:
install(TARGETS ${_output_targets}
EXPORT <target set>
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
OBJECTS DESTINATION lib
)
See also: https://doc.qt.io/qt-6/qt-add-qml-module.html
]] #
function(qm_install_qml_modules _target)
set(options)
set(oneValueArgs PREFIX)
set(multiValueArgs)
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
set(_prefix)
qm_set_value(_prefix FUNC_PREFIX "qml")
qt_query_qml_module(${_target}
URI _module_uri
VERSION _module_version
PLUGIN_TARGET _module_plugin_target
TARGET_PATH _module_target_path
QMLDIR _module_qmldir
TYPEINFO _module_typeinfo
QML_FILES _module_qml_files
QML_FILES_DEPLOY_PATHS _qml_files_deploy_paths
RESOURCES _module_resources
RESOURCES_DEPLOY_PATHS _resources_deploy_paths
)
# See also: https://doc.qt.io/qt-6/qt-query-qml-module.html#example
# Install the QML module runtime loadable plugin
set(_module_dir "${_prefix}/${_module_target_path}")
install(TARGETS "${_module_plugin_target}"
LIBRARY DESTINATION "${_module_dir}"
RUNTIME DESTINATION "${_module_dir}"
ARCHIVE DESTINATION "${_module_dir}"
)
# Install the QML module meta information.
install(FILES "${_module_qmldir}" DESTINATION "${_module_dir}")
install(FILES "${_module_typeinfo}" DESTINATION "${_module_dir}")
# Install QML files, possibly renamed.
list(LENGTH _module_qml_files _num_files)
if(_num_files GREATER 0)
math(EXPR _last_index "${_num_files} - 1")
foreach(_i RANGE 0 ${_last_index})
list(GET _module_qml_files ${_i} _src_file)
list(GET _qml_files_deploy_paths ${_i} _deploy_path)
get_filename_component(_dest_name "${_deploy_path}" NAME)
get_filename_component(_dest_dir "${_deploy_path}" DIRECTORY)
install(FILES "${_src_file}" DESTINATION "${_module_dir}/${_dest_dir}" RENAME "${_dest_name}")
endforeach()
endif()
# Install resources, possibly renamed.
list(LENGTH _module_resources _num_files)
if(_num_files GREATER 0)
math(EXPR _last_index "${_num_files} - 1")
foreach(_i RANGE 0 ${_last_index})
list(GET _module_resources ${_i} _src_file)
list(GET _resources_deploy_paths ${_i} _deploy_path)
get_filename_component(_dest_name "${_deploy_path}" NAME)
get_filename_component(_dest_dir "${_deploy_path}" DIRECTORY)
install(FILES "${_src_file}" DESTINATION "${_module_dir}/${_dest_dir}" RENAME "${_dest_name}")
endforeach()
endif()
endfunction()

View File

@@ -0,0 +1,374 @@
include_guard(DIRECTORY)
#[[
Add qt translation target.
qm_add_translation(<target>
[LOCALES locales]
[PREFIX prefix]
[SOURCES files... | DIRECTORIES dirs... | TARGETS targets... | TS_FILES files...]
[TS_DIR dir]
[QM_DIR dir]
[TS_OPTIONS options...]
[QM_OPTIONS options...]
[TS_DEPENDS targets...]
[QM_DEPENDS targets...]
[CREATE_ONCE]
)
Arguments:
LOCALES: language names, e.g. zh_CN en_US, must specify if SOURCES or TARGETS is specified
PREFIX: translation file prefix, default to target name
SOURCES: source files
DIRECTORIES: source directories
TARGETS: target names, the source files of which will be collected
TS_FILES: ts file names, add the specified ts file
TS_DIR: ts files destination, default to `CMAKE_CURRENT_SOURCE_DIR`
QM_DIR: qm files destination, default to `CMAKE_CURRENT_BINARY_DIR`
TS_DEPENDS: add lupdate task as a dependency to the given targets
QM_DEPENDS: add lrelease task as a dependency to the given targets
CREATE_ONCE: create translations at configure phase if not exist
]] #
function(qm_add_translation _target)
set(options CREATE_ONCE)
set(oneValueArgs PREFIX TS_DIR QM_DIR)
set(multiValueArgs LOCALES SOURCES DIRECTORIES TARGETS TS_FILES TS_OPTIONS QM_OPTIONS TS_DEPENDS QM_DEPENDS)
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# Get linguist tools
if(NOT TARGET Qt${QT_VERSION_MAJOR}::lupdate OR NOT TARGET Qt${QT_VERSION_MAJOR}::lrelease)
message(FATAL_ERROR "qm_add_translation: linguist tools not defined. Add find_package(Qt\${QT_VERSION_MAJOR} COMPONENTS LinguistTools) to CMake to enable.")
endif()
set(_src_files)
set(_include_dirs)
# Collect source files
if(FUNC_SOURCES)
list(APPEND _src_files ${FUNC_SOURCES})
endif()
# Collect source files
if(FUNC_TARGETS)
foreach(_item IN LISTS FUNC_TARGETS)
get_target_property(_type ${_item} TYPE)
if((_type STREQUAL "UTILITY") OR(_type STREQUAL "INTERFACE_LIBRARY"))
continue()
endif()
set(_tmp_files)
get_target_property(_tmp_files ${_item} SOURCES)
set(_qml_files)
get_target_property(_qml_files ${_item} QT_QML_MODULE_QML_FILES)
if (NOT _qml_files STREQUAL ${_item}-NOTFOUND)
list(APPEND _tmp_files ${_qml_files})
endif()
list(FILTER _tmp_files INCLUDE REGEX ".+\\.(h|hh|hpp|hxx|c|cc|cpp|cxx|m|mm|qml|js|mjs)$")
list(FILTER _tmp_files EXCLUDE REGEX "^(qasc|moc)_.+")
# Need to convert to absolute path
get_target_property(_target_dir ${_item} SOURCE_DIR)
foreach(_file IN LISTS _tmp_files)
get_filename_component(_abs_file ${_file} ABSOLUTE BASE_DIR ${_target_dir})
list(APPEND _src_files ${_abs_file})
endforeach()
unset(_tmp_files)
get_target_property(_tmp_dirs ${_item} INCLUDE_DIRECTORIES)
list(APPEND _include_dirs ${_tmp_dirs})
endforeach()
endif()
# Collect source directories
if(FUNC_DIRECTORIES)
foreach(_item IN LISTS FUNC_DIRECTORIES)
file(GLOB _tmp
${_item}/*.h ${_item}/*.hpp
${_item}/*.hh ${_item}/*.hxx
${_item}/*.cpp ${_item}/*.cxx
${_item}/*.c ${_item}/*.cc
${_item}/*.m ${_item}/*.mm
)
list(APPEND _src_files ${_tmp})
endforeach()
endif()
if(_src_files)
if(NOT FUNC_LOCALES)
message(FATAL_ERROR "qm_add_translation: source files collected but LOCALES not specified!")
endif()
elseif(NOT FUNC_TS_FILES)
message(FATAL_ERROR "qm_add_translation: no source files or ts files collected!")
endif()
add_custom_target(${_target})
set(_qm_depends)
if(FUNC_TS_OPTIONS)
set(_ts_options ${FUNC_TS_OPTIONS})
endif()
if(FUNC_QM_OPTIONS)
set(_qm_options ${FUNC_QM_OPTIONS})
endif()
if(_src_files)
if(FUNC_PREFIX)
set(_prefix ${FUNC_PREFIX})
else()
set(_prefix ${_target})
endif()
if(FUNC_TS_DIR)
set(_ts_dir ${FUNC_TS_DIR})
else()
set(_ts_dir ${CMAKE_CURRENT_SOURCE_DIR})
endif()
set(_ts_files)
foreach(_loc IN LISTS FUNC_LOCALES)
list(APPEND _ts_files ${_ts_dir}/${_prefix}_${_loc}.ts)
endforeach()
# Include options
set(_include_options)
foreach(_inc IN LISTS _include_dirs)
list(APPEND _include_options "-I${_inc}")
endforeach()
# May be an lupdate bug, so we skip passing include directories
# list(APPEND _ts_options ${_include_options})
if(_ts_options)
list(PREPEND _ts_options OPTIONS)
endif()
set(_create_once)
if(FUNC_CREATE_ONCE)
set(_create_once CREATE_ONCE)
endif()
_qm_add_lupdate_target(${_target}_lupdate
INPUT ${_src_files}
OUTPUT ${_ts_files}
${_ts_options}
${_create_once}
)
# Add update dependencies
# add_dependencies(${_target} ${_target}_lupdate)
foreach(_item IN LISTS FUNC_TS_DEPENDS)
add_dependencies(${_item} ${_target}_lupdate)
endforeach()
# list(APPEND _qm_depends DEPENDS ${_target}_lupdate)
else()
if(FUNC_PREFIX)
message(WARNING "qm_add_translation: no source files collected, PREFIX ignored")
endif()
if(FUNC_TS_DIR)
message(WARNING "qm_add_translation: no source files collected, TS_DIR ignored")
endif()
if(FUNC_TS_DEPENDS)
message(WARNING "qm_add_translation: no source files collected, TS_DEPENDS ignored")
endif()
endif()
if(FUNC_QM_DIR)
set(_qm_dir ${FUNC_QM_DIR})
else()
set(_qm_dir ${CMAKE_CURRENT_BINARY_DIR})
endif()
set(_qm_target)
if(_qm_options)
list(PREPEND _qm_options OPTIONS)
endif()
_qm_add_lrelease_target(${_target}_lrelease
INPUT ${_ts_files} ${FUNC_TS_FILES}
DESTINATION ${_qm_dir}
${_qm_options}
${_qm_depends}
)
add_dependencies(${_target} ${_target}_lrelease)
# Add release dependencies
if(FUNC_TARGETS)
foreach(_item IN LISTS FUNC_TARGETS)
add_dependencies(${_item} ${_target}_lrelease)
endforeach()
endif()
foreach(_item IN LISTS FUNC_QM_DEPENDS)
add_dependencies(${_item} ${_target}_lrelease)
endforeach()
endfunction()
# ----------------------------------
# Private functions
# ----------------------------------
# Input: cxx source files
# Output: target ts files
function(_qm_add_lupdate_target _target)
set(options CREATE_ONCE)
set(oneValueArgs)
set(multiValueArgs INPUT OUTPUT OPTIONS DEPENDS)
cmake_parse_arguments(_LUPDATE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
set(_lupdate_deps ${_LUPDATE_DEPENDS})
set(_my_sources ${_LUPDATE_INPUT})
set(_my_tsfiles ${_LUPDATE_OUTPUT})
add_custom_target(${_target} DEPENDS ${_lupdate_deps})
qm_get_executable_location(Qt${QT_VERSION_MAJOR}::lupdate _lupdate_exe)
set(_create_once_warning)
set(_create_once_warning_printed off)
# Prepare for create once
if(_LUPDATE_CREATE_ONCE)
# Check if all src files are available
foreach(_file IN LISTS _my_sources)
get_filename_component(_abs_file ${_file} ABSOLUTE)
if(NOT EXISTS ${_abs_file})
get_filename_component(_file ${_file} NAME)
set(_create_once_warning "source file \"${_file}\" is not available, skip generating ts file now")
break()
endif()
endforeach()
# Check if options contain generator expressions
if(NOT _create_once_warning)
foreach(_opt IN LISTS _LUPDATE_OPTIONS)
string(GENEX_STRIP "${_opt}" _no_genex)
if(NOT _no_genex STREQUAL _opt)
set(_create_once_warning "lupdate options contain generator expressions, skip generating ts file now")
break()
endif()
endforeach()
endif()
endif()
foreach(_ts_file IN LISTS _my_tsfiles)
# make a list file to call lupdate on, so we don't make our commands too
# long for some systems
get_filename_component(_ts_name ${_ts_file} NAME)
set(_ts_lst_file "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${_ts_name}_lst_file")
set(_lst_file_srcs)
foreach(_lst_file_src IN LISTS _my_sources)
set(_lst_file_srcs "${_lst_file_src}\n${_lst_file_srcs}")
endforeach()
get_directory_property(_inc_DIRS INCLUDE_DIRECTORIES)
foreach(_pro_include IN LISTS _inc_DIRS)
get_filename_component(_abs_include "${_pro_include}" ABSOLUTE)
set(_lst_file_srcs "-I${_pro_include}\n${_lst_file_srcs}")
endforeach()
file(WRITE ${_ts_lst_file} "${_lst_file_srcs}")
get_filename_component(_ts_abs ${_ts_file} ABSOLUTE)
if(_LUPDATE_CREATE_ONCE AND NOT EXISTS ${_ts_abs})
if(_create_once_warning)
if(NOT _create_once_warning_printed)
message(WARNING "qm_add_translation: ${_create_once_warning}")
set(_create_once_warning_printed on)
endif()
else()
message(STATUS "Lupdate: Generating ${_ts_name}")
get_filename_component(_abs_file ${_ts_file} ABSOLUTE)
get_filename_component(_dir ${_abs_file} DIRECTORY)
file(MAKE_DIRECTORY ${_dir})
execute_process(
COMMAND ${_lupdate_exe} ${_LUPDATE_OPTIONS} "@${_ts_lst_file}" -ts ${_ts_file}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_QUIET
COMMAND_ERROR_IS_FATAL ANY
)
endif()
endif()
add_custom_command(
TARGET ${_target} POST_BUILD
COMMAND ${_lupdate_exe}
ARGS ${_LUPDATE_OPTIONS} "@${_ts_lst_file}" -ts ${_ts_file}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
BYPRODUCTS ${_ts_lst_file}
VERBATIM
)
endforeach()
endfunction()
# Input: ts files
# Output: list to append qm files
function(_qm_add_lrelease_target _target)
set(options)
set(oneValueArgs DESTINATION OUTPUT)
set(multiValueArgs INPUT OPTIONS DEPENDS)
cmake_parse_arguments(_LRELEASE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
set(_lrelease_files ${_LRELEASE_INPUT})
set(_lrelease_deps ${_LRELEASE_DEPENDS})
qm_get_executable_location(Qt${QT_VERSION_MAJOR}::lrelease _lrelease_exe)
set(_qm_files)
foreach(_file IN LISTS _lrelease_files)
get_filename_component(_abs_FILE ${_file} ABSOLUTE)
get_filename_component(_qm_file ${_file} NAME)
# everything before the last dot has to be considered the file name (including other dots)
string(REGEX REPLACE "\\.[^.]*$" "" FILE_NAME ${_qm_file})
get_source_file_property(output_location ${_abs_FILE} OUTPUT_LOCATION)
if(output_location)
set(_out_dir ${output_location})
elseif(_LRELEASE_DESTINATION)
set(_out_dir ${_LRELEASE_DESTINATION})
else()
set(_out_dir ${CMAKE_CURRENT_BINARY_DIR})
endif()
set(_qm_file "${_out_dir}/${FILE_NAME}.qm")
add_custom_command(
OUTPUT ${_qm_file}
COMMAND ${CMAKE_COMMAND} -E make_directory ${_out_dir}
COMMAND ${_lrelease_exe} ARGS ${_LRELEASE_OPTIONS} ${_abs_FILE} -qm ${_qm_file}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${_lrelease_files}
VERBATIM
)
list(APPEND _qm_files ${_qm_file})
endforeach()
add_custom_target(${_target} ALL DEPENDS ${_lrelease_deps} ${_qm_files})
if(_LRELEASE_OUTPUT)
set(${_LRELEASE_OUTPUT} ${_qm_files} PARENT_SCOPE)
endif()
endfunction()

View File

@@ -0,0 +1,68 @@
#[[
Warning: This module is private, may be modified or removed in the future, please use with caution.
]] #
include_guard(DIRECTORY)
#[[
Create the names of output files preserving relative dirs. (Ported from MOC command)
qm_make_output_file(<infile> <prefix> <ext> <OUT>)
OUT: output source file paths
#]]
function(qm_make_output_file _infile _prefix _ext _out)
string(LENGTH ${CMAKE_CURRENT_BINARY_DIR} _binlength)
string(LENGTH ${_infile} _infileLength)
set(_checkinfile ${CMAKE_CURRENT_SOURCE_DIR})
if(_infileLength GREATER _binlength)
string(SUBSTRING "${_infile}" 0 ${_binlength} _checkinfile)
if(_checkinfile STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
file(RELATIVE_PATH _name ${CMAKE_CURRENT_BINARY_DIR} ${_infile})
else()
file(RELATIVE_PATH _name ${CMAKE_CURRENT_SOURCE_DIR} ${_infile})
endif()
else()
file(RELATIVE_PATH _name ${CMAKE_CURRENT_SOURCE_DIR} ${_infile})
endif()
if(CMAKE_HOST_WIN32 AND _name MATCHES "^([a-zA-Z]):(.*)$") # absolute path
set(_name "${CMAKE_MATCH_1}_${CMAKE_MATCH_2}")
endif()
set(_outfile "${CMAKE_CURRENT_BINARY_DIR}/${_name}")
string(REPLACE ".." "__" _outfile ${_outfile})
get_filename_component(_outpath ${_outfile} PATH)
get_filename_component(_outfile ${_outfile} NAME_WLE)
file(MAKE_DIRECTORY ${_outpath})
set(${_out} ${_outpath}/${_prefix}${_outfile}.${_ext} PARENT_SCOPE)
endfunction()
#[[
Create a custom command to run `xxd`.
qm_add_binary_resource(<input> <output>)
#]]
function(qm_add_binary_resource _input _output)
find_program(_xxd "xxd")
if(NOT _xxd)
get_filename_component(_name ${_output} NAME)
string(MAKE_C_IDENTIFIER ${_name} _name)
set(_cmd "${CMAKE_COMMAND}"
-D "input=${_input}"
-D "output=${_output}"
-D "name=${_name}"
-P "${QMSETUP_MODULES_DIR}/scripts/xxd.cmake")
else()
set(_cmd "${_xxd}" "-i" "${_input}" "${_output}")
endif()
add_custom_command(
OUTPUT ${_output}
COMMAND ${_cmd}
DEPENDS ${_input}
)
endfunction()

View File

@@ -0,0 +1,158 @@
#[[
Warning: This module is private, may be modified or removed in the future, please use with caution.
]] #
if(NOT DEFINED QMSETUP_PACKAGE_BUILD_TYPE)
set(QMSETUP_PACKAGE_BUILD_TYPE "Release")
endif()
include_guard(DIRECTORY)
#[[
Install external package at configuration phase.
qm_install_package(<name>
[SOURCE_DIR <dir>]
[BUILD_TREE_DIR <dir>]
[INSTALL_DIR <dir>]
[CMAKE_PACKAGE_SUBDIR <subdir>]
[BUILD_TYPE <type>]
[CONFIGURE_ARGS <arg...>]
[RESULT_PATH <VAR>]
)
]] #
function(qm_install_package _name)
set(options)
set(oneValueArgs SOURCE_DIR BUILD_TREE_DIR INSTALL_DIR CMAKE_PACKAGE_SUBDIR BUILD_TYPE RESULT_PATH)
set(multiValueArgs CONFIGURE_ARGS)
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# Directories
if(NOT FUNC_SOURCE_DIR)
message(FATAL_ERROR "pre_install_package: SOURCE_DIR is not spefified")
endif()
set(_src_dir ${FUNC_SOURCE_DIR})
if(FUNC_BUILD_TREE_DIR)
set(_build_tree_dir ${FUNC_BUILD_TREE_DIR})
else()
set(_build_tree_dir ${CMAKE_BINARY_DIR}/_build)
endif()
if(FUNC_INSTALL_DIR)
set(_install_dir ${FUNC_INSTALL_DIR})
else()
set(_install_dir ${CMAKE_BINARY_DIR}/_install)
endif()
if(FUNC_CMAKE_PACKAGE_SUBDIR)
set(_cmake_subdir ${FUNC_CMAKE_PACKAGE_SUBDIR})
else()
include(GNUInstallDirs)
set(_cmake_subdir "${CMAKE_INSTALL_LIBDIR}/cmake/${_name}")
endif()
# Build types
if(FUNC_BUILD_TYPE)
set(_build_type -DCMAKE_BUILD_TYPE=${FUNC_BUILD_TYPE})
elseif(CMAKE_BUILD_TYPE)
set(_build_type -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE})
elseif(NOT CMAKE_CONFIGURATION_TYPES)
set(_build_type -DCMAKE_BUILD_TYPE=${QMSETUP_PACKAGE_BUILD_TYPE})
else()
set(_build_type)
endif()
if(FUNC_BUILD_TYPE)
set(_build_types ${FUNC_BUILD_TYPE})
else()
set(_build_types ${QMSETUP_PACKAGE_BUILD_TYPE})
endif()
# Do it
set(_install_cmake_dir ${_install_dir}/${_cmake_subdir})
set(_build_dir ${_build_tree_dir}/${_name})
if(NOT IS_DIRECTORY ${_install_cmake_dir})
# Determine generator
set(_extra_args)
if(CMAKE_GENERATOR)
set(_extra_args -G "${CMAKE_GENERATOR}")
endif()
if(CMAKE_GENERATOR_PLATFORM)
set(_extra_args -A "${CMAKE_GENERATOR_PLATFORM}")
endif()
# Remove old build directory
if(IS_DIRECTORY ${_build_dir})
file(REMOVE_RECURSE ${_build_dir})
endif()
file(MAKE_DIRECTORY ${_build_tree_dir})
# Configure
message(STATUS "Configuring ${_name}...")
set(_log_file ${_build_tree_dir}/${_name}_configure.log)
execute_process(
COMMAND ${CMAKE_COMMAND} -S ${_src_dir} -B ${_build_dir}
${_extra_args} ${_build_type}
# Pass through CMAKE_INSTALL_LIBDIR to ensure the package uses the same
# lib directory convention (e.g., lib vs lib64) as the parent project.
# Without this, packages using GNUInstallDirs may default to a different
# directory structure than expected.
"-DCMAKE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR}"
"-DCMAKE_INSTALL_PREFIX=${_install_dir}" ${FUNC_CONFIGURE_ARGS}
OUTPUT_FILE ${_log_file}
ERROR_FILE ${_log_file}
RESULT_VARIABLE _code
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
if(NOT ${_code} EQUAL 0)
message(FATAL_ERROR "Configure failed, check \"${_log_file}\"")
endif()
# Build
foreach(_item IN LISTS _build_types)
message(STATUS "Building ${_name} (${_item})...")
set(_log_file ${_build_tree_dir}/${_name}_build-${_item}.log)
execute_process(
COMMAND ${CMAKE_COMMAND} --build ${_build_dir} --config ${_item} --parallel
OUTPUT_FILE ${_log_file}
ERROR_FILE ${_log_file}
RESULT_VARIABLE _code
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
if(NOT ${_code} EQUAL 0)
message(FATAL_ERROR "Build failed, check \"${_log_file}\"")
endif()
endforeach()
# Install
foreach(_item IN LISTS _build_types)
message(STATUS "Installing ${_name} (${_item})...")
set(_log_file ${_build_tree_dir}/${_name}_install-${_item}.log)
execute_process(
COMMAND ${CMAKE_COMMAND} --build ${_build_dir} --config ${_item} --target install
OUTPUT_FILE ${_log_file}
ERROR_FILE ${_log_file}
RESULT_VARIABLE _code
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
if(NOT ${_code} EQUAL 0)
message(FATAL_ERROR "Install failed, check \"${_log_file}\"")
endif()
endforeach()
endif()
if(FUNC_RESULT_PATH)
set(${FUNC_RESULT_PATH} ${_install_cmake_dir} PARENT_SCOPE)
endif()
endfunction()

View File

@@ -0,0 +1,37 @@
include_guard(DIRECTORY)
function(qm_get_windows_proxy _out)
if(NOT WIN32)
return()
endif()
execute_process(
COMMAND reg query "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable
OUTPUT_VARIABLE _proxy_enable_output
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(NOT _proxy_enable_output MATCHES "ProxyEnable[ \t\r\n]+REG_DWORD[ \t\r\n]+0x1")
return()
endif()
execute_process(
COMMAND reg query "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyServer
OUTPUT_VARIABLE _proxy_server_output
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(NOT _proxy_server_output MATCHES "ProxyServer[ \t\r\n]+REG_SZ[ \t\r\n]+(.*)")
return()
endif()
set(${_out} ${CMAKE_MATCH_1} PARENT_SCOPE)
endfunction()
macro(qm_set_proxy_env _proxy)
set(ENV{HTTP_PROXY} "http://${_proxy}")
set(ENV{HTTPS_PROXY} "http://${_proxy}")
set(ENV{ALL_PROXY} "http://${_proxy}")
endmacro()