CMake: Introduce Qt6::Startup target
Add an abstraction over Qt::WinMain (aka qtmain.lib) and iOS's runtime linker entry point (_qt_main_wrapper). The Core target will now link against the Startup target on all platforms, instead of just WinMain on Windows. The creation and linkage interface definition of the Startup target is done at find_package(Qt6Core) time via the private call of _qt_internal_setup_startup_target(). This will add automatic linkage of WinMain to executables marked with the WIN32_EXECUTABLE property on Windows. As well as the addition of the '-Wl,-e,_qt_main_wrapper' linker flag when linking iOS executables. Qt users can opt out of this behavior by either setting the QT_NO_LINK_QTMAIN property or variable. This is in line with Qt 5 behavior. Task-number: QTBUG-87060 Change-Id: I7d5e9f1be0e402cf8e67e6f55bfd285f9e6b04f4 Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
This commit is contained in:
parent
7c12c8d113
commit
c6a5cdcee6
@ -460,6 +460,7 @@ qt_copy_or_install(FILES
|
||||
cmake/QtSeparateDebugInfo.cmake
|
||||
cmake/QtSetup.cmake
|
||||
cmake/QtSimdHelpers.cmake
|
||||
cmake/QtStartupHelpers.cmake
|
||||
cmake/QtStandaloneTestsConfig.cmake.in
|
||||
cmake/QtSyncQtHelpers.cmake
|
||||
cmake/QtTargetHelpers.cmake
|
||||
|
@ -478,6 +478,7 @@ include(QtRpathHelpers)
|
||||
include(QtSanitizerHelpers)
|
||||
include(QtScopeFinalizerHelpers)
|
||||
include(QtSimdHelpers)
|
||||
include(QtStartupHelpers)
|
||||
include(QtSyncQtHelpers)
|
||||
include(QtTargetHelpers)
|
||||
include(QtTestHelpers)
|
||||
|
@ -29,6 +29,16 @@ function(qt_internal_walk_libs target out_var dict_name operation)
|
||||
return()
|
||||
endif()
|
||||
list(APPEND collected ${target})
|
||||
|
||||
if(target STREQUAL "${QT_CMAKE_EXPORT_NAMESPACE}::Startup")
|
||||
# We can't (and don't need to) process Startup, because it contains $<TARGET_PROPERTY:prop>
|
||||
# genexes which get replaced with $<TARGET_PROPERTY:Startup,prop> genexes in the code below
|
||||
# and that causes 'INTERFACE_LIBRARY targets may only have whitelisted properties.' errors
|
||||
# with CMake versions equal to or lower than 3.18. These errors are super unintuitive to
|
||||
# debug because there's no mention that it's happening during a file(GENERATE) call.
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT TARGET ${dict_name})
|
||||
add_library(${dict_name} INTERFACE IMPORTED GLOBAL)
|
||||
endif()
|
||||
|
16
cmake/QtStartupHelpers.cmake
Normal file
16
cmake/QtStartupHelpers.cmake
Normal file
@ -0,0 +1,16 @@
|
||||
# Set up some internal requirements for the Startup target.
|
||||
#
|
||||
# The creation of the Startup target and its linkage setup happens in 2 places:
|
||||
# - in src/corelib/CMakeLists.txt when building qtbase.
|
||||
# - at find_package(Qt6Core) time.
|
||||
#
|
||||
# See _qt_internal_setup_startup_target() in Qt6CoreMacros.cmake for the implementation of that.
|
||||
function(qt_internal_setup_startup_target)
|
||||
set(dependent_target "Core")
|
||||
|
||||
# On windows, find_package(Qt6Core) should call find_package(Qt6WinMain) so that Startup can
|
||||
# link against WinMain.
|
||||
if(WIN32)
|
||||
qt_record_extra_qt_package_dependency("${dependent_target}" WinMain "${PROJECT_VERSION}")
|
||||
endif()
|
||||
endfunction()
|
@ -1293,15 +1293,12 @@ endif()
|
||||
|
||||
qt_internal_apply_gc_binaries_conditional(Core PUBLIC)
|
||||
|
||||
if(WIN32)
|
||||
set(isExe $<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>)
|
||||
set(isWin32 $<BOOL:$<TARGET_PROPERTY:WIN32_EXECUTABLE>>)
|
||||
set(isNotExcluded $<NOT:$<BOOL:$<TARGET_PROPERTY:Qt5_NO_LINK_QTMAIN>>>)
|
||||
set(isPolicyNEW $<TARGET_POLICY:CMP0020>)
|
||||
# Run some required internal code before creating the Startup target.
|
||||
qt_internal_setup_startup_target()
|
||||
|
||||
target_link_libraries(Core INTERFACE $<$<AND:${isExe},${isWin32},${isNotExcluded},${isPolicyNEW}>:Qt::WinMain>)
|
||||
qt_record_extra_qt_package_dependency(Core WinMain "${PROJECT_VERSION}")
|
||||
endif()
|
||||
# Create the Startup target and set flags on it, so that they are already
|
||||
# available at Core build time.
|
||||
_qt_internal_setup_startup_target()
|
||||
|
||||
# Record darwin minimum deployment target.
|
||||
if(APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET)
|
||||
|
@ -47,6 +47,8 @@ set(QT@PROJECT_VERSION_MAJOR@_IS_SHARED_LIBS_BUILD "@BUILD_SHARED_LIBS@")
|
||||
get_filename_component(_Qt6CoreConfigDir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
set(_Qt6CTestMacros "${_Qt6CoreConfigDir}/Qt6CTestMacros.cmake")
|
||||
|
||||
_qt_internal_setup_startup_target()
|
||||
|
||||
if(ANDROID_PLATFORM)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@QT_CMAKE_EXPORT_NAMESPACE@AndroidMacros.cmake")
|
||||
endif()
|
||||
|
@ -1317,3 +1317,103 @@ function(_qt_internal_apply_strict_cpp target)
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Sets up auto-linkage of platform-specific entry points.
|
||||
#
|
||||
# See qt_internal_setup_startup_target() in qtbase/cmake/QtStartupHelpers.cmake for the internal
|
||||
# implementation counterpart.
|
||||
#
|
||||
# A project that uses Qt can opt-out of this auto-linking behavior by either setting the
|
||||
# QT_NO_LINK_QTMAIN property to TRUE on a target, or by setting the
|
||||
# QT_NO_LINK_QTMAIN variable to TRUE before the find_package(Qt6) call.
|
||||
#
|
||||
# QT_NO_LINK_QTMAIN replaces the old Qt5_NO_LINK_QTMAIN name for both the property and variable
|
||||
#name.
|
||||
#
|
||||
# This function is called by Qt6CoreConfigExtras.cmake at find_package(Qt6Core) time.
|
||||
# The reason the linkage is done at find_package() time instead of Qt build time is to allow
|
||||
# opting out via a variable. This ensures compatibility with Qt5 behavior.
|
||||
# If it was done at build time, opt-out could only be achieved via the property.
|
||||
function(_qt_internal_setup_startup_target)
|
||||
set(target "${QT_CMAKE_EXPORT_NAMESPACE}::Startup")
|
||||
set(dependent_target "${QT_CMAKE_EXPORT_NAMESPACE}::Core")
|
||||
|
||||
# Get actual Core target name.
|
||||
get_target_property(dependent_aliased_target "${dependent_target}" ALIASED_TARGET)
|
||||
if(dependent_aliased_target)
|
||||
set(dependent_target "${dependent_aliased_target}")
|
||||
endif()
|
||||
|
||||
# Check if Core is being built as part of current CMake invocation.
|
||||
# If it is, that means the Core target scope is global and the same scope should be set for the
|
||||
# to-be-created Startup target, to avoid creating 100s of local IMPORTED Startup targets
|
||||
# when building with -DBUILD_TESTING=ON and -DBUILD_EXAMPLES=ON due to multiple
|
||||
# find_package(Qt6Core) calls.
|
||||
get_target_property(core_imported "${dependent_target}" IMPORTED)
|
||||
set(create_global "")
|
||||
if(NOT core_imported)
|
||||
set(create_global "GLOBAL")
|
||||
endif()
|
||||
|
||||
# Create Startup only if it's not available in the current scope.
|
||||
# Guards against multiple find_package(Qt6Core) calls.
|
||||
if(NOT TARGET "${target}")
|
||||
add_library("${target}" INTERFACE IMPORTED ${create_global})
|
||||
endif()
|
||||
|
||||
# Allow variable opt-out. Has to be after target creation, because Core always links against
|
||||
# Startup.
|
||||
if(QT_NO_LINK_QTMAIN)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# find_package(Qt6Core) can be called multiple times, but we only want to set the flags once.
|
||||
set(initialized_prop "_qt_startup_target_initialized")
|
||||
get_target_property(initialized "${target}" "${initialized_prop}")
|
||||
if(initialized)
|
||||
return()
|
||||
else()
|
||||
set_target_properties("${target}" PROPERTIES "${initialized_prop}" TRUE)
|
||||
endif()
|
||||
|
||||
# On Windows this enables automatic linkage to WinMain.
|
||||
# On iOS this enables automatic passing of a linker flag that will change the default
|
||||
# entry point of the linked executable.
|
||||
set(isExe "$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>")
|
||||
set(isNotExcluded "$<NOT:$<BOOL:$<TARGET_PROPERTY:QT_NO_LINK_QTMAIN>>>")
|
||||
if(WIN32)
|
||||
set(isWin32 "$<BOOL:$<TARGET_PROPERTY:WIN32_EXECUTABLE>>")
|
||||
set(isPolicyNEW "$<TARGET_POLICY:CMP0020>")
|
||||
set(finalGenex "$<$<AND:${isExe},${isWin32},${isNotExcluded},${isPolicyNEW}>:Qt::WinMain>")
|
||||
|
||||
# Use set_target_properties instead of target_link_libraries because the latter has some
|
||||
# weird additional behavior of checking which project the target belongs to, and might
|
||||
# error out when called multiple times from different scopes.
|
||||
set_target_properties("${target}" PROPERTIES INTERFACE_LINK_LIBRARIES "${finalGenex}")
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||
set(flag "-Wl,-e,_qt_main_wrapper")
|
||||
set(finalGenex "$<$<AND:${isExe},${isNotExcluded}>:${flag}>")
|
||||
|
||||
set_target_properties("${target}" PROPERTIES INTERFACE_LINK_OPTIONS "${finalGenex}")
|
||||
endif()
|
||||
|
||||
# Set up the dependency on Startup for the local Core target, if it hasn't been set yet.
|
||||
set(initialized_prop "_qt_core_startup_dependency_initialized")
|
||||
get_target_property(initialized "${dependent_target}" "${initialized_prop}")
|
||||
if(initialized)
|
||||
get_target_property(thelibs "${dependent_target}" INTERFACE_LINK_LIBRARIES)
|
||||
return()
|
||||
else()
|
||||
set_target_properties("${dependent_target}" PROPERTIES "${initialized_prop}" TRUE)
|
||||
|
||||
# Export the initialized property on Core, to ensure that Core links against Startup
|
||||
# only once in a non-qtbase project.
|
||||
if(NOT core_imported)
|
||||
set_property(TARGET "${dependent_target}" APPEND PROPERTY
|
||||
EXPORT_PROPERTIES "${initialized_prop}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_link_libraries("${dependent_target}" INTERFACE "${target}")
|
||||
get_target_property(thelibs "${dependent_target}" INTERFACE_LINK_LIBRARIES)
|
||||
endfunction()
|
||||
|
Loading…
x
Reference in New Issue
Block a user