From b3b1e4737808bc2f2ccaef88d133404877ebce58 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Mon, 27 Jul 2020 10:17:04 +0200 Subject: [PATCH] CMake: Introduce qt_internal_add_app This new function is meant to be used where load(qt_app) is used. It delegates functionality to qt_add_executable, while handling some additional behavior via a finalization function (mostly handling of macOS Info.plist files and icons, as well as Windows icons and resource files) It uses a new PlatformAppInternal interface target. Task-number: QTBUG-85757 Change-Id: I1a2d5851b137fcd4a6323e0e06fb154f91619800 Reviewed-by: Cristian Adam --- cmake/QtApp.cmake | 92 +++++++++++++++++++++++++++++++++ cmake/QtBaseGlobalTargets.cmake | 2 + cmake/QtBuild.cmake | 56 +++++++++++++++++--- cmake/QtInternalTargets.cmake | 8 ++- src/corelib/Qt6CoreMacros.cmake | 2 +- 5 files changed, 150 insertions(+), 10 deletions(-) create mode 100644 cmake/QtApp.cmake diff --git a/cmake/QtApp.cmake b/cmake/QtApp.cmake new file mode 100644 index 00000000000..c54bb3e9401 --- /dev/null +++ b/cmake/QtApp.cmake @@ -0,0 +1,92 @@ +# This function creates a CMake target for a Qt internal app. +# Such projects had a load(qt_app) command. +function(qt_internal_add_app target) + qt_parse_all_arguments(arg + "qt_internal_add_app" + "NO_INSTALL" + "${__default_target_info_args}" + "${__default_private_args}" + ${ARGN}) + + set(output_directory "${QT_BUILD_DIR}/${INSTALL_BINDIR}") + + set(no_install "") + if(arg_NO_INSTALL) + set(no_install NO_INSTALL) + endif() + + qt_add_executable("${target}" + DELAY_RC + DELAY_TARGET_INFO + OUTPUT_DIRECTORY "${output_directory}" + ${no_install} + SOURCES ${arg_SOURCES} + INCLUDE_DIRECTORIES + ${arg_INCLUDE_DIRECTORIES} + DEFINES + ${arg_DEFINES} + LIBRARIES ${arg_LIBRARIES} Qt::PlatformAppInternal + COMPILE_OPTIONS ${arg_COMPILE_OPTIONS} + LINK_OPTIONS ${arg_LINK_OPTIONS} + MOC_OPTIONS ${arg_MOC_OPTIONS} + ENABLE_AUTOGEN_TOOLS ${arg_ENABLE_AUTOGEN_TOOLS} + DISABLE_AUTOGEN_TOOLS ${arg_DISABLE_AUTOGEN_TOOLS} + TARGET_VERSION "${arg_TARGET_VERSION}" + TARGET_PRODUCT "${arg_TARGET_PRODUCT}" + TARGET_DESCRIPTION "${arg_TARGET_DESCRIPTION}" + TARGET_COMPANY "${arg_TARGET_COMPANY}" + TARGET_COPYRIGHT "${arg_TARGET_COPYRIGHT}" + ) + qt_internal_add_target_aliases("${target}") + + # To mimic the default behaviors of qt_app.prf, we by default enable GUI Windows applications, + # but don't enable macOS bundles. + # Bundles are enabled in a separate set_target_properties call if an Info.plist file + # is provided. + # Similary, the Windows GUI flag is disabled in a separate call + # if CONFIG += console was encountered during conversion. + set_target_properties("${target}" PROPERTIES WIN32_EXECUTABLE TRUE) + + qt_add_list_file_finalizer(qt_internal_finalize_app ${target}) +endfunction() + +function(qt_internal_get_title_case value out_var) + if(NOT value) + set(${out_var} "" PARENT_SCOPE) + return() + endif() + string(SUBSTRING "${value}" 0 1 first_char) + string(TOUPPER "${first_char}" first_char_upper) + string(SUBSTRING "${target}" 1 -1 rest_of_value) + set(title_value "${first_char_upper}${rest_of_value}") + set(${out_var} "${title_value}" PARENT_SCOPE) +endfunction() + +function(qt_internal_update_app_target_info_properties target) + # First update the delayed properties with any values that might have been set after the + # qt_internal_add_app() call. + qt_internal_update_delayed_target_info_properties(${target}) + + # Set defaults in case if no values were set. + get_target_property(target_version ${target} QT_DELAYED_TARGET_VERSION) + if(NOT target_version) + set_target_properties(${target} PROPERTIES QT_DELAYED_TARGET_VERSION "${PROJECT_VERSION}") + endif() + + get_target_property(target_description ${target} QT_DELAYED_TARGET_DESCRIPTION) + if(NOT target_description) + qt_internal_get_title_case("${target}" upper_name) + set_target_properties(${target} PROPERTIES QT_DELAYED_TARGET_DESCRIPTION "Qt ${upper_name}") + endif() + + # Finally set the final values. + qt_internal_set_target_info_properties_from_delayed_properties("${target}") +endfunction() + +function(qt_internal_finalize_app target) + qt_internal_update_app_target_info_properties("${target}") + + if(WIN32) + qt6_generate_win32_rc_file("${target}") + endif() +endfunction() diff --git a/cmake/QtBaseGlobalTargets.cmake b/cmake/QtBaseGlobalTargets.cmake index b630c3fe8de..fd3a1ea3bb2 100644 --- a/cmake/QtBaseGlobalTargets.cmake +++ b/cmake/QtBaseGlobalTargets.cmake @@ -331,6 +331,7 @@ set(__export_targets Platform PlatformCommonInternal PlatformModuleInternal PlatformPluginInternal + PlatformAppInternal PlatformToolInternal) set(__export_name "${INSTALL_CMAKE_NAMESPACE}Targets") qt_install(TARGETS ${__export_targets} EXPORT "${__export_name}") @@ -347,6 +348,7 @@ qt_internal_export_modern_cmake_config_targets_file(TARGETS ${__export_targets} qt_copy_or_install(FILES cmake/ModuleDescription.json.in cmake/Qt3rdPartyLibraryConfig.cmake.in + cmake/QtApp.cmake cmake/QtBuild.cmake cmake/QtBuildInformation.cmake cmake/QtCompilerFlags.cmake diff --git a/cmake/QtBuild.cmake b/cmake/QtBuild.cmake index 34757728bbc..9fd5b3c244d 100644 --- a/cmake/QtBuild.cmake +++ b/cmake/QtBuild.cmake @@ -2503,6 +2503,8 @@ function(qt_watch_current_list_dir variable access value current_list_file stack qt_finalize_module(${a1} ${a2} ${a3} ${a4} ${a5} ${a6} ${a7} ${a8} ${a9}) elseif(func STREQUAL "qt_finalize_plugin") qt_finalize_plugin(${a1} ${a2} ${a3} ${a4} ${a5} ${a6} ${a7} ${a8} ${a9}) + elseif(func STREQUAL "qt_internal_finalize_app") + qt_internal_finalize_app(${a1} ${a2} ${a3} ${a4} ${a5} ${a6} ${a7} ${a8} ${a9}) else() message(FATAL_ERROR "qt_watch_current_list_dir doesn't know about ${func}. Consider adding it.") endif() @@ -2569,6 +2571,32 @@ function(qt_set_target_info_properties target) QT_TARGET_PRODUCT_NAME "${arg_TARGET_PRODUCT}") endfunction() +# Uses the QT_DELAYED_TARGET_* property values to set the final QT_TARGET_* properties. +# Needed when doing executable finalization at the end of a subdirectory scope +# (aka scope finalization). +function(qt_internal_set_target_info_properties_from_delayed_properties target) + set(args "") + foreach(prop ${__default_target_info_args}) + get_target_property(prop_value "${target}" "QT_DELAYED_${prop}") + list(APPEND args "${prop}" "${prop_value}") + endforeach() + qt_set_target_info_properties(${target} ${args}) +endfunction() + +# Updates the QT_DELAYED_ properties with values from the QT_ variants, in case if they were +# set in-between a qt_add_* call and before scope finalization. +function(qt_internal_update_delayed_target_info_properties target) + foreach(prop ${__default_target_info_args}) + get_target_property(prop_value "${target}" "QT_${prop}") + get_target_property(delayed_prop_value ${target} "QT_DELAYED_${prop}") + set(final_value "${delayed_prop_value}") + if(prop_value) + set(final_value "${prop_value}") + endif() + set_target_properties(${target} PROPERTIES "QT_DELAYED_${prop}" "${final_value}") + endforeach() +endfunction() + # This is the main entry function for creating a Qt module, that typically # consists of a library, public header files, private header files and configurable # features. @@ -3962,7 +3990,7 @@ endfunction() # Collection of qt_add_executable arguments so they can be shared across qt_add_executable # and qt_add_test_helper. set(__qt_add_executable_optional_args - "GUI;BOOTSTRAP;NO_QT;NO_INSTALL;EXCEPTIONS" + "GUI;BOOTSTRAP;NO_QT;NO_INSTALL;EXCEPTIONS;DELAY_RC;DELAY_TARGET_INFO" ) set(__qt_add_executable_single_args "OUTPUT_DIRECTORY;INSTALL_DIRECTORY;VERSION" @@ -4023,15 +4051,27 @@ function(qt_add_executable name) endif() endif() - if("${arg_TARGET_DESCRIPTION}" STREQUAL "") - set(arg_TARGET_DESCRIPTION "Qt ${name}") + if(arg_DELAY_TARGET_INFO) + # Delay the setting of target info properties if requested. Needed for scope finalization + # of Qt apps. + set_target_properties("${name}" PROPERTIES + QT_DELAYED_TARGET_VERSION "${arg_VERSION}" + QT_DELAYED_TARGET_PRODUCT "${arg_TARGET_PRODUCT}" + QT_DELAYED_TARGET_DESCRIPTION "${arg_TARGET_DESCRIPTION}" + QT_DELAYED_TARGET_COMPANY "${arg_TARGET_COMPANY}" + QT_DELAYED_TARGET_COPYRIGHT "${arg_TARGET_COPYRIGHT}" + ) + else() + if("${arg_TARGET_DESCRIPTION}" STREQUAL "") + set(arg_TARGET_DESCRIPTION "Qt ${name}") + endif() + qt_set_target_info_properties(${name} ${ARGN} + TARGET_DESCRIPTION "${arg_TARGET_DESCRIPTION}" + TARGET_VERSION "${arg_VERSION}") endif() - qt_set_target_info_properties(${name} ${ARGN} - TARGET_DESCRIPTION "${arg_TARGET_DESCRIPTION}" - TARGET_VERSION "${arg_VERSION}") - if (WIN32) + if (WIN32 AND NOT arg_DELAY_RC) qt6_generate_win32_rc_file(${name}) endif() @@ -6027,6 +6067,8 @@ function(qt_internal_apply_win_prefix_and_suffix target) endif() endfunction() +include(QtApp) + # Compatibility macros that should be removed once all their usages are removed. function(extend_target) qt_extend_target(${ARGV}) diff --git a/cmake/QtInternalTargets.cmake b/cmake/QtInternalTargets.cmake index db3257d2f2d..c7da00c3f46 100644 --- a/cmake/QtInternalTargets.cmake +++ b/cmake/QtInternalTargets.cmake @@ -81,14 +81,18 @@ add_library(PlatformPluginInternal INTERFACE) add_library(Qt::PlatformPluginInternal ALIAS PlatformPluginInternal) target_link_libraries(PlatformPluginInternal INTERFACE PlatformCommonInternal) +add_library(PlatformAppInternal INTERFACE) +add_library(Qt::PlatformAppInternal ALIAS PlatformAppInternal) +target_link_libraries(PlatformAppInternal INTERFACE PlatformCommonInternal) + add_library(PlatformToolInternal INTERFACE) add_library(Qt::PlatformToolInternal ALIAS PlatformToolInternal) -target_link_libraries(PlatformToolInternal INTERFACE PlatformCommonInternal) +target_link_libraries(PlatformToolInternal INTERFACE PlatformAppInternal) if(WARNINGS_ARE_ERRORS) qt_internal_set_warnings_are_errors_flags(PlatformModuleInternal) qt_internal_set_warnings_are_errors_flags(PlatformPluginInternal) - qt_internal_set_warnings_are_errors_flags(PlatformToolInternal) + qt_internal_set_warnings_are_errors_flags(PlatformAppInternal) endif() if(WIN32) # Needed for M_PI define. Same as mkspecs/features/qt_module.prf. diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index 2d0aff8aff6..008452ea7c0 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -919,7 +919,7 @@ function(qt6_generate_win32_rc_file target) elseif (target_version MATCHES "[0-9]+") set(target_version "${target_version}.0.0.0") else() - message(FATAL_ERROR "Invalid version format") + message(FATAL_ERROR "Invalid version format: '${target_version}'") endif() set(product_version "${target_version}") else()