summaryrefslogtreecommitdiffstats
path: root/cmake/QtPublicAppleHelpers.cmake
diff options
context:
space:
mode:
Diffstat (limited to 'cmake/QtPublicAppleHelpers.cmake')
-rw-r--r--cmake/QtPublicAppleHelpers.cmake954
1 files changed, 954 insertions, 0 deletions
diff --git a/cmake/QtPublicAppleHelpers.cmake b/cmake/QtPublicAppleHelpers.cmake
new file mode 100644
index 0000000000..5f7a7719b5
--- /dev/null
+++ b/cmake/QtPublicAppleHelpers.cmake
@@ -0,0 +1,954 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+function(_qt_internal_handle_ios_launch_screen target)
+ # Check if user provided a launch screen path via a variable.
+ set(launch_screen "")
+
+ # Check if the project provided a launch screen path via a variable.
+ # This variable is currently in Technical Preview.
+ if(QT_IOS_LAUNCH_SCREEN)
+ set(launch_screen "${QT_IOS_LAUNCH_SCREEN}")
+ endif()
+
+ # Check if the project provided a launch screen path via a target property, it takes precedence
+ # over the variable.
+ # This property is currently in Technical Preview.
+ get_target_property(launch_screen_from_prop "${target}" QT_IOS_LAUNCH_SCREEN)
+ if(launch_screen_from_prop)
+ set(launch_screen "${launch_screen_from_prop}")
+ endif()
+
+ # If the project hasn't provided a launch screen file path, use a copy of the template
+ # that qmake uses.
+ # It needs to be a copy because configure_file can't handle all the escaped double quotes
+ # present in the qmake template file.
+ set(is_default_launch_screen FALSE)
+ if(NOT launch_screen AND NOT QT_NO_SET_DEFAULT_IOS_LAUNCH_SCREEN)
+ set(is_default_launch_screen TRUE)
+ set(launch_screen
+ "${__qt_internal_cmake_apple_support_files_path}/LaunchScreen.storyboard")
+ endif()
+
+ # Check that the launch screen exists.
+ if(launch_screen)
+ if(NOT IS_ABSOLUTE "${launch_screen}")
+ message(FATAL_ERROR
+ "Provided launch screen value should be an absolute path: '${launch_screen}'")
+ endif()
+
+ if(NOT EXISTS "${launch_screen}")
+ message(FATAL_ERROR
+ "Provided launch screen file does not exist: '${launch_screen}'")
+ endif()
+ endif()
+
+ if(launch_screen AND NOT QT_NO_ADD_IOS_LAUNCH_SCREEN_TO_BUNDLE)
+ get_filename_component(launch_screen_name "${launch_screen}" NAME)
+
+ # Make a copy of the default launch screen template for this target and replace the
+ # label inside the template with the target name.
+ if(is_default_launch_screen)
+ # Configure our default template and place it in the build dir.
+ set(launch_screen_in_path "${launch_screen}")
+
+ string(MAKE_C_IDENTIFIER "${target}" target_identifier)
+ set(launch_screen_out_dir
+ "${CMAKE_CURRENT_BINARY_DIR}/.qt/launch_screen_storyboards/${target_identifier}")
+
+ set(launch_screen_out_path
+ "${launch_screen_out_dir}/${launch_screen_name}")
+
+ file(MAKE_DIRECTORY "${launch_screen_out_dir}")
+
+ configure_file("${launch_screen_in_path}" "${launch_screen_out_path}" COPYONLY)
+
+ set(final_launch_screen_path "${launch_screen_out_path}")
+ else()
+ set(final_launch_screen_path "${launch_screen}")
+ endif()
+
+ # Add the launch screen storyboard file as a source file, otherwise CMake doesn't consider
+ # it as a resource file and MACOSX_PACKAGE_LOCATION processing will be skipped.
+ target_sources("${target}" PRIVATE "${final_launch_screen_path}")
+
+ # Ensure Xcode compiles the storyboard file and installs the compiled storyboard .nib files
+ # into the app bundle.
+ # We use target_sources and the MACOSX_PACKAGE_LOCATION source file property for that
+ # instead of the RESOURCE target property, becaues the latter could potentially end up
+ # needlessly installing the source storyboard file.
+ #
+ # We can't rely on policy CMP0118 since user project controls it.
+ set(scope_args)
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18")
+ set(scope_args TARGET_DIRECTORY ${target})
+ endif()
+ set_source_files_properties("${final_launch_screen_path}" ${scope_args}
+ PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
+
+ # Save the launch screen name, so its value is added as an UILaunchStoryboardName entry
+ # in the Qt generated Info.plist file.
+ # Xcode expects an Info.plist storyboard entry without an extension.
+ get_filename_component(launch_screen_base_name "${launch_screen}" NAME_WE)
+ set_target_properties("${target}" PROPERTIES
+ _qt_ios_launch_screen_name "${launch_screen_name}"
+ _qt_ios_launch_screen_base_name "${launch_screen_base_name}"
+ _qt_ios_launch_screen_path "${final_launch_screen_path}")
+ endif()
+endfunction()
+
+function(_qt_internal_find_ios_development_team_id out_var)
+ get_property(team_id GLOBAL PROPERTY _qt_internal_ios_development_team_id)
+ get_property(team_id_computed GLOBAL PROPERTY _qt_internal_ios_development_team_id_computed)
+ if(team_id_computed)
+ # Just in case if the value is non-empty but still booly FALSE.
+ if(NOT team_id)
+ set(team_id "")
+ endif()
+ set("${out_var}" "${team_id}" PARENT_SCOPE)
+ return()
+ endif()
+
+ set_property(GLOBAL PROPERTY _qt_internal_ios_development_team_id_computed "TRUE")
+
+ set(home_dir "$ENV{HOME}")
+ set(xcode_preferences_path "${home_dir}/Library/Preferences/com.apple.dt.Xcode.plist")
+
+ # Extract the first account name (email) from the user's Xcode preferences
+ message(DEBUG "Trying to extract an Xcode development team id from '${xcode_preferences_path}'")
+ execute_process(COMMAND "/usr/libexec/PlistBuddy"
+ -x -c "print IDEProvisioningTeams" "${xcode_preferences_path}"
+ OUTPUT_VARIABLE teams_xml
+ ERROR_VARIABLE plist_error)
+
+ # Parsing state.
+ set(is_free "")
+ set(current_team_id "")
+ set(parsing_is_free FALSE)
+ set(parsing_team_id FALSE)
+ set(first_team_id "")
+
+ # Parse the xml output and return the first encountered non-free team id. If no non-free team id
+ # is found, return the first encountered free team id.
+ # If no team is found, return an empty string.
+ #
+ # Example input:
+ #<plist version="1.0">
+ #<dict>
+ # <key>marty@planet.local</key>
+ # <array>
+ # <dict>
+ # <key>isFreeProvisioningTeam</key>
+ # <false/>
+ # <key>teamID</key>
+ # <string>AAA</string>
+ # ...
+ # </dict>
+ # <dict>
+ # <key>isFreeProvisioningTeam</key>
+ # <true/>
+ # <key>teamID</key>
+ # <string>BBB</string>
+ # ...
+ # </dict>
+ # </array>
+ #</dict>
+ #</plist>
+ if(teams_xml AND NOT plist_error)
+ string(REPLACE "\n" ";" teams_xml_lines "${teams_xml}")
+
+ foreach(xml_line ${teams_xml_lines})
+ string(STRIP "${xml_line}" xml_line)
+ if(xml_line STREQUAL "<dict>")
+ # Clean any previously found values when a new team dict is matched.
+ set(is_free "")
+ set(current_team_id "")
+
+ elseif(xml_line STREQUAL "<key>isFreeProvisioningTeam</key>")
+ set(parsing_is_free TRUE)
+
+ elseif(parsing_is_free)
+ set(parsing_is_free FALSE)
+
+ if(xml_line MATCHES "true")
+ set(is_free TRUE)
+ else()
+ set(is_free FALSE)
+ endif()
+
+ elseif(xml_line STREQUAL "<key>teamID</key>")
+ set(parsing_team_id TRUE)
+
+ elseif(parsing_team_id)
+ set(parsing_team_id FALSE)
+ if(xml_line MATCHES "<string>([^<]+)</string>")
+ set(current_team_id "${CMAKE_MATCH_1}")
+ else()
+ continue()
+ endif()
+
+ string(STRIP "${current_team_id}" current_team_id)
+
+ # If this is the first team id we found so far, remember that, regardless if's free
+ # or not.
+ if(NOT first_team_id AND current_team_id)
+ set(first_team_id "${current_team_id}")
+ endif()
+
+ # Break early if we found a non-free team id and use it, because we prefer
+ # a non-free team for signing, just like qmake.
+ if(NOT is_free AND current_team_id)
+ set(first_team_id "${current_team_id}")
+ break()
+ endif()
+ endif()
+ endforeach()
+ endif()
+
+ if(NOT first_team_id)
+ message(DEBUG "Failed to extract an Xcode development team id.")
+ set("${out_var}" "" PARENT_SCOPE)
+ else()
+ message(DEBUG "Successfully extracted the first encountered Xcode development team id.")
+ set_property(GLOBAL PROPERTY _qt_internal_ios_development_team_id "${first_team_id}")
+ set("${out_var}" "${first_team_id}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(_qt_internal_get_apple_bundle_identifier_prefix out_var)
+ get_property(prefix GLOBAL PROPERTY _qt_internal_ios_bundle_identifier_prefix)
+ get_property(prefix_computed GLOBAL PROPERTY
+ _qt_internal_ios_bundle_identifier_prefix_computed)
+ if(prefix_computed)
+ # Just in case if the value is non-empty but still booly FALSE.
+ if(NOT prefix)
+ set(prefix "")
+ endif()
+ set("${out_var}" "${prefix}" PARENT_SCOPE)
+ return()
+ endif()
+
+ set_property(GLOBAL PROPERTY _qt_internal_ios_bundle_identifier_prefix_computed "TRUE")
+
+ set(home_dir "$ENV{HOME}")
+ set(xcode_preferences_path "${home_dir}/Library/Preferences/com.apple.dt.Xcode.plist")
+
+ message(DEBUG "Trying to extract the default bundle identifier prefix from Xcode preferences.")
+ execute_process(COMMAND "/usr/libexec/PlistBuddy"
+ -c "print IDETemplateOptions:bundleIdentifierPrefix"
+ "${xcode_preferences_path}"
+ OUTPUT_VARIABLE prefix
+ ERROR_VARIABLE prefix_error)
+ if(prefix AND NOT prefix_error)
+ message(DEBUG "Successfully extracted the default bundle identifier prefix.")
+ string(STRIP "${prefix}" prefix)
+ else()
+ message(DEBUG "Failed to extract the default bundle identifier prefix.")
+ endif()
+
+ if(prefix AND NOT prefix_error)
+ set_property(GLOBAL PROPERTY _qt_internal_ios_bundle_identifier_prefix "${prefix}")
+ set("${out_var}" "${prefix}" PARENT_SCOPE)
+ else()
+ set("${out_var}" "" PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(_qt_internal_escape_rfc_1034_identifier value out_var)
+ # According to https://datatracker.ietf.org/doc/html/rfc1034#section-3.5
+ # we can only use letters, digits, dot (.) and hyphens (-).
+ # Underscores are not allowed.
+ string(REGEX REPLACE "[^A-Za-z0-9.]" "-" value "${value}")
+
+ set("${out_var}" "${value}" PARENT_SCOPE)
+endfunction()
+
+function(_qt_internal_get_default_apple_bundle_identifier target out_var)
+ _qt_internal_get_apple_bundle_identifier_prefix(prefix)
+ if(NOT prefix)
+ set(prefix "com.yourcompany")
+
+ # For a better out-of-the-box experience, try to create a unique prefix by appending
+ # the sha1 of the team id, if one is found.
+ _qt_internal_find_ios_development_team_id(team_id)
+ if(team_id)
+ string(SHA1 hash "${team_id}")
+ string(SUBSTRING "${hash}" 0 8 infix)
+ string(APPEND prefix ".${infix}")
+ endif()
+
+ if(CMAKE_GENERATOR STREQUAL "Xcode")
+ message(WARNING
+ "No organization bundle identifier prefix could be retrieved from Xcode preferences. \
+ This can lead to code signing issues due to a non-unique bundle \
+ identifier. Please set up an organization prefix by creating a new project within \
+ Xcode, or consider providing a custom bundle identifier by specifying the \
+ MACOSX_BUNDLE_GUI_IDENTIFIER or XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER property."
+ )
+ endif()
+ endif()
+
+ # Escape the prefix according to rfc 1034, it's important for code-signing. If an invalid
+ # identifier is used, calling xcodebuild on the command line says that no provisioning profile
+ # could be found, with no additional error message. If one opens the generated project with
+ # Xcode and clicks on 'Try again' to get a new profile, it shows a semi-useful error message
+ # that the identifier is invalid.
+ _qt_internal_escape_rfc_1034_identifier("${prefix}" prefix)
+
+ if(CMAKE_GENERATOR STREQUAL "Xcode")
+ set(identifier "${prefix}.$(PRODUCT_NAME:rfc1034identifier)")
+ else()
+ set(identifier "${prefix}.${target}")
+ endif()
+
+ set("${out_var}" "${identifier}" PARENT_SCOPE)
+endfunction()
+
+function(_qt_internal_set_placeholder_apple_bundle_version target)
+ # If user hasn't provided neither a bundle version nor a bundle short version string for the
+ # app, set a placeholder value for both which will add them to the generated Info.plist file.
+ # This is required so that the app launches in the simulator (but apparently not for running
+ # on-device).
+ get_target_property(bundle_version "${target}" MACOSX_BUNDLE_BUNDLE_VERSION)
+ get_target_property(bundle_short_version "${target}" MACOSX_BUNDLE_SHORT_VERSION_STRING)
+
+ if(NOT MACOSX_BUNDLE_BUNDLE_VERSION AND
+ NOT MACOSX_BUNDLE_SHORT_VERSION_STRING AND
+ NOT bundle_version AND
+ NOT bundle_short_version AND
+ NOT QT_NO_SET_XCODE_BUNDLE_VERSION
+ )
+ get_target_property(version "${target}" VERSION)
+ if(NOT version)
+ set(version "${PROJECT_VERSION}")
+ if(NOT version)
+ set(version "1.0.0")
+ endif()
+ endif()
+
+ # Use x.y for short version and x.y.z for full version
+ # Any versions longer than this will fail App Store
+ # submission.
+ string(REPLACE "." ";" version_list ${version})
+ list(LENGTH version_list version_list_length)
+ list(GET version_list 0 version_major)
+ set(bundle_short_version "${version_major}")
+ if(version_list_length GREATER 1)
+ list(GET version_list 1 version_minor)
+ string(APPEND bundle_short_version ".${version_minor}")
+ endif()
+ set(bundle_version "${bundle_short_version}")
+ if(version_list_length GREATER 2)
+ list(GET version_list 2 version_patch)
+ string(APPEND bundle_version ".${version_patch}")
+ endif()
+
+
+ if(NOT CMAKE_XCODE_ATTRIBUTE_MARKETING_VERSION
+ AND NOT QT_NO_SET_XCODE_ATTRIBUTE_MARKETING_VERSION
+ AND NOT CMAKE_XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION
+ AND NOT QT_NO_SET_XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION
+ AND CMAKE_GENERATOR STREQUAL "Xcode")
+ get_target_property(marketing_version "${target}"
+ XCODE_ATTRIBUTE_MARKETING_VERSION)
+ get_target_property(current_project_version "${target}"
+ XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION)
+ if(NOT marketing_version AND NOT current_project_version)
+ set_target_properties("${target}"
+ PROPERTIES
+ XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${bundle_version}"
+ XCODE_ATTRIBUTE_MARKETING_VERSION "${bundle_short_version}"
+ )
+ set(bundle_version "$(CURRENT_PROJECT_VERSION)")
+ set(bundle_short_version "$(MARKETING_VERSION)")
+ endif()
+ endif()
+
+ set_target_properties("${target}"
+ PROPERTIES
+ MACOSX_BUNDLE_BUNDLE_VERSION "${bundle_version}"
+ MACOSX_BUNDLE_SHORT_VERSION_STRING "${bundle_short_version}"
+ )
+ endif()
+endfunction()
+
+function(_qt_internal_set_xcode_development_team_id target)
+ # If user hasn't provided a development team id, try to find the first one specified
+ # in the Xcode preferences.
+ if(NOT CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM AND NOT QT_NO_SET_XCODE_DEVELOPMENT_TEAM_ID)
+ get_target_property(existing_team_id "${target}" XCODE_ATTRIBUTE_DEVELOPMENT_TEAM)
+ if(NOT existing_team_id)
+ _qt_internal_find_ios_development_team_id(team_id)
+ set_target_properties("${target}"
+ PROPERTIES XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "${team_id}")
+ endif()
+ endif()
+endfunction()
+
+function(_qt_internal_set_apple_bundle_identifier target)
+ # Skip all logic if requested.
+ if(QT_NO_SET_XCODE_BUNDLE_IDENTIFIER)
+ return()
+ endif()
+
+ # There are two fields to consider: the CFBundleIdentifier key (ie., cmake_bundle_identifier)
+ # to be written to Info.plist and the PRODUCT_BUNDLE_IDENTIFIER (ie., xcode_bundle_identifier)
+ # property to set in the Xcode project. The `cmake_bundle_identifier` set by
+ # MACOSX_BUNDLE_GUI_IDENTIFIER applies to both Xcode, and other generators, while
+ # `xcode_bundle_identifier` set by XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER is
+ # Xcode specific.
+ #
+ # If Ninja is the generator, we set the value of `MACOSX_BUNDLE_GUI_IDENTIFIER`
+ # and don't touch the `XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER`.
+ # If Xcode is the generator, we set the value of `XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER`,
+ # and additionally, to silence a Xcode's warning, we set the `MACOSX_BUNDLE_GUI_IDENTIFIER` to
+ # `${PRODUCT_BUNDLE_IDENTIFIER}` so that Xcode could sort it out.
+
+ get_target_property(existing_cmake_bundle_identifier "${target}"
+ MACOSX_BUNDLE_GUI_IDENTIFIER)
+ get_target_property(existing_xcode_bundle_identifier "${target}"
+ XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER)
+
+ set(is_cmake_bundle_identifier_given FALSE)
+ if(existing_cmake_bundle_identifier)
+ set(is_cmake_bundle_identifier_given TRUE)
+ elseif(MACOSX_BUNDLE_GUI_IDENTIFIER)
+ set(is_cmake_bundle_identifier_given TRUE)
+ set(existing_cmake_bundle_identifier ${MACOSX_BUNDLE_GUI_IDENTIFIER})
+ endif()
+
+ set(is_xcode_bundle_identifier_given FALSE)
+ if(existing_xcode_bundle_identifier)
+ set(is_xcode_bundle_identifier_given TRUE)
+ elseif(CMAKE_XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER)
+ set(is_xcode_bundle_identifier_given TRUE)
+ set(existing_xcode_bundle_identifier ${CMAKE_XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER})
+ endif()
+
+ if(is_cmake_bundle_identifier_given
+ AND is_xcode_bundle_identifier_given
+ AND NOT existing_cmake_bundle_identifier STREQUAL existing_xcode_bundle_identifier)
+ message(WARNING
+ "MACOSX_BUNDLE_GUI_IDENTIFIER and XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "
+ "are set to different values. You only need to set one of them. ")
+ endif()
+
+ if(NOT is_xcode_bundle_identifier_given
+ AND NOT is_cmake_bundle_identifier_given)
+ _qt_internal_get_default_apple_bundle_identifier("${target}" bundle_id)
+ elseif(is_cmake_bundle_identifier_given)
+ set(bundle_id ${existing_cmake_bundle_identifier})
+ elseif(is_xcode_bundle_identifier_given)
+ set(bundle_id ${existing_xcode_bundle_identifier})
+ endif()
+
+ if(CMAKE_GENERATOR STREQUAL "Xcode")
+ set_target_properties("${target}"
+ PROPERTIES
+ XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${bundle_id}"
+ MACOSX_BUNDLE_GUI_IDENTIFIER "$(PRODUCT_BUNDLE_IDENTIFIER)")
+ else()
+ set_target_properties("${target}"
+ PROPERTIES
+ MACOSX_BUNDLE_GUI_IDENTIFIER "${bundle_id}")
+ endif()
+endfunction()
+
+function(_qt_internal_set_xcode_targeted_device_family target)
+ if(NOT CMAKE_XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY
+ AND NOT QT_NO_SET_XCODE_TARGETED_DEVICE_FAMILY)
+ get_target_property(existing_device_family
+ "${target}" XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY)
+ if(NOT existing_device_family)
+ set(device_family_iphone_and_ipad "1,2")
+ set_target_properties("${target}"
+ PROPERTIES
+ XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY
+ "${device_family_iphone_and_ipad}")
+ endif()
+ endif()
+endfunction()
+
+function(_qt_internal_set_xcode_code_sign_style target)
+ if(NOT CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE
+ AND NOT QT_NO_SET_XCODE_CODE_SIGN_STYLE)
+ get_target_property(existing_code_style
+ "${target}" XCODE_ATTRIBUTE_CODE_SIGN_STYLE)
+ if(NOT existing_code_style)
+ set(existing_code_style "Automatic")
+ set_target_properties("${target}"
+ PROPERTIES
+ XCODE_ATTRIBUTE_CODE_SIGN_STYLE
+ "${existing_code_style}")
+ endif()
+ endif()
+endfunction()
+
+# Workaround for https://gitlab.kitware.com/cmake/cmake/-/issues/15183
+function(_qt_internal_set_xcode_install_path target)
+ if(NOT CMAKE_XCODE_ATTRIBUTE_INSTALL_PATH
+ AND NOT QT_NO_SET_XCODE_INSTALL_PATH)
+ get_target_property(existing_install_path
+ "${target}" XCODE_ATTRIBUTE_INSTALL_PATH)
+ if(NOT existing_install_path)
+ set_target_properties("${target}"
+ PROPERTIES
+ XCODE_ATTRIBUTE_INSTALL_PATH
+ "$(inherited)")
+ endif()
+ endif()
+endfunction()
+
+function(_qt_internal_set_xcode_bundle_display_name target)
+ # We want the value of CFBundleDisplayName to be ${PRODUCT_NAME}, but we can't put that
+ # into the Info.plist.in template file directly, because the implicit configure_file(Info.plist)
+ # done by CMake is not using the @ONLY option, so CMake would treat the assignment as
+ # variable expansion. Escaping using backslashes does not help.
+ # Work around it by assigning the dollar char to a separate cache var, and expand it, so that
+ # the final value in the file will be ${PRODUCT_NAME}, to be evaluated at build time by Xcode.
+ set(QT_INTERNAL_DOLLAR_VAR "$" CACHE STRING "")
+endfunction()
+
+# Adds ${PRODUCT_NAME} to the Info.plist file, which is then evaluated by Xcode itself.
+function(_qt_internal_set_xcode_bundle_name target)
+ if(QT_NO_SET_XCODE_BUNDLE_NAME)
+ return()
+ endif()
+
+ get_target_property(existing_bundle_name "${target}" MACOSX_BUNDLE_BUNDLE_NAME)
+ if(NOT MACOSX_BUNDLE_BUNDLE_NAME AND NOT existing_bundle_name)
+ if(CMAKE_GENERATOR STREQUAL Xcode)
+ set_target_properties("${target}"
+ PROPERTIES
+ MACOSX_BUNDLE_BUNDLE_NAME "$(PRODUCT_NAME)")
+ else()
+ set_target_properties("${target}"
+ PROPERTIES
+ MACOSX_BUNDLE_BUNDLE_NAME "${target}")
+ endif()
+ endif()
+endfunction()
+
+function(_qt_internal_set_xcode_bitcode_enablement target)
+ if(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE
+ OR QT_NO_SET_XCODE_ENABLE_BITCODE)
+ return()
+ endif()
+
+ get_target_property(existing_bitcode_enablement
+ "${target}" XCODE_ATTRIBUTE_ENABLE_BITCODE)
+ if(NOT existing_bitcode_enablement MATCHES "-NOTFOUND")
+ return()
+ endif()
+
+ # Disable bitcode to match Xcode 14's new default
+ set_target_properties("${target}"
+ PROPERTIES
+ XCODE_ATTRIBUTE_ENABLE_BITCODE
+ "NO")
+endfunction()
+
+function(_qt_internal_copy_info_plist target)
+ # If the project already specifies a custom file, we don't override it.
+ get_target_property(info_plist_in "${target}" MACOSX_BUNDLE_INFO_PLIST)
+ if(NOT info_plist_in)
+ set(info_plist_in "${__qt_internal_cmake_apple_support_files_path}/Info.plist.app.in")
+ endif()
+
+ string(MAKE_C_IDENTIFIER "${target}" target_identifier)
+ set(info_plist_out_dir
+ "${CMAKE_CURRENT_BINARY_DIR}/.qt/info_plist/${target_identifier}")
+ set(info_plist_out "${info_plist_out_dir}/Info.plist")
+
+ # Check if we need to specify a custom launch screen storyboard entry.
+ get_target_property(launch_screen_base_name "${target}" _qt_ios_launch_screen_base_name)
+ if(launch_screen_base_name)
+ set(qt_ios_launch_screen_plist_entry "${launch_screen_base_name}")
+ endif()
+
+ # Call configure_file to substitute Qt-specific @FOO@ values, not ${FOO} values.
+ #
+ # The output file will be another template file to be fed to CMake via the
+ # MACOSX_BUNDLE_INFO_PLIST property. CMake will then call configure_file on it to provide
+ # content for regular entries like CFBundleName, etc.
+ #
+ # We require this extra configure_file call so we can create unique Info.plist files for each
+ # target in a project, while also providing a way to add Qt specific entries that CMake
+ # does not support out of the box (e.g. a launch screen name).
+ configure_file(
+ "${info_plist_in}"
+ "${info_plist_out}"
+ @ONLY
+ )
+
+ set_target_properties("${target}" PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${info_plist_out}")
+endfunction()
+
+function(_qt_internal_plist_buddy plist_file)
+ cmake_parse_arguments(PARSE_ARGV 1 arg
+ "" "OUTPUT_VARIABLE;ERROR_VARIABLE" "COMMANDS")
+ foreach(command ${arg_COMMANDS})
+ execute_process(COMMAND "/usr/libexec/PlistBuddy"
+ -c "${command}" "${plist_file}"
+ OUTPUT_VARIABLE plist_buddy_output
+ ERROR_VARIABLE plist_buddy_error)
+ string(STRIP "${plist_buddy_output}" plist_buddy_output)
+ if(arg_OUTPUT_VARIABLE)
+ list(APPEND ${arg_OUTPUT_VARIABLE} ${plist_buddy_output})
+ set(${arg_OUTPUT_VARIABLE} ${${arg_OUTPUT_VARIABLE}} PARENT_SCOPE)
+ endif()
+ if(arg_ERROR_VARIABLE)
+ list(APPEND ${arg_ERROR_VARIABLE} ${plist_buddy_error})
+ set(${arg_ERROR_VARIABLE} ${${arg_ERROR_VARIABLE}} PARENT_SCOPE)
+ endif()
+ if(plist_buddy_error)
+ return()
+ endif()
+ endforeach()
+endfunction()
+
+function(_qt_internal_set_apple_localizations target)
+ if(QT_NO_SET_PLIST_LOCALIZATIONS)
+ return()
+ endif()
+
+ set(supported_languages "${QT_I18N_TRANSLATED_LANGUAGES}")
+ if("${QT_I18N_TRANSLATED_LANGUAGES}" STREQUAL "")
+ get_target_property(supported_languages "${target}" _qt_apple_supported_languages)
+ if("${supported_languages}" STREQUAL "supported_languages-NOTFOUND")
+ return()
+ endif()
+ endif()
+ get_target_property(plist_file "${target}" MACOSX_BUNDLE_INFO_PLIST)
+ if (NOT plist_file)
+ return()
+ endif()
+
+ _qt_internal_plist_buddy("${plist_file}"
+ COMMANDS "print CFBundleLocalizations"
+ OUTPUT_VARIABLE existing_localizations
+ )
+ if(existing_localizations)
+ return()
+ endif()
+
+ list(TRANSFORM supported_languages PREPEND
+ "Add CFBundleLocalizations: string ")
+
+ _qt_internal_plist_buddy("${plist_file}"
+ COMMANDS
+ "Add CFBundleLocalizations array"
+ ${supported_languages}
+ "Delete CFBundleAllowMixedLocalizations"
+ )
+endfunction()
+
+function(_qt_internal_set_ios_simulator_arch target)
+ if(CMAKE_XCODE_ATTRIBUTE_ARCHS
+ OR QT_NO_SET_XCODE_ARCHS)
+ return()
+ endif()
+
+ get_target_property(existing_archs
+ "${target}" XCODE_ATTRIBUTE_ARCHS)
+ if(NOT existing_archs MATCHES "-NOTFOUND")
+ return()
+ endif()
+
+ if(NOT x86_64 IN_LIST QT_OSX_ARCHITECTURES)
+ return()
+ endif()
+
+ if(CMAKE_OSX_ARCHITECTURES AND NOT x86_64 IN_LIST CMAKE_OSX_ARCHITECTURES)
+ return()
+ endif()
+
+ set_target_properties("${target}"
+ PROPERTIES
+ "XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*]"
+ "x86_64")
+endfunction()
+
+# Export Apple platform sdk and xcode version requirements to Qt6ConfigExtras.cmake.
+function(_qt_internal_export_apple_sdk_and_xcode_version_requirements out_var)
+ if(NOT APPLE)
+ return()
+ endif()
+
+ if(IOS)
+ set(vars_to_assign
+ QT_SUPPORTED_MIN_IOS_SDK_VERSION
+ QT_SUPPORTED_MAX_IOS_SDK_VERSION
+ QT_SUPPORTED_MIN_IOS_XCODE_VERSION
+ )
+ elseif(VISIONOS)
+ set(vars_to_assign
+ QT_SUPPORTED_MIN_VISIONOS_SDK_VERSION
+ QT_SUPPORTED_MAX_VISIONOS_SDK_VERSION
+ QT_SUPPORTED_MIN_VISIONOS_XCODE_VERSION
+ )
+ else()
+ set(vars_to_assign
+ QT_SUPPORTED_MIN_MACOS_SDK_VERSION
+ QT_SUPPORTED_MAX_MACOS_SDK_VERSION
+ QT_SUPPORTED_MIN_MACOS_XCODE_VERSION
+ )
+ endif()
+
+ set(assignments "")
+ foreach(var IN LISTS vars_to_assign)
+ set(value "${${var}}")
+ list(APPEND assignments
+ "
+if(NOT ${var})
+ set(${var} \"${value}\")
+endif()")
+ endforeach()
+
+ list(JOIN assignments "\n" assignments)
+ set(${out_var} "${assignments}" PARENT_SCOPE)
+endfunction()
+
+function(_qt_internal_get_apple_sdk_version out_var)
+ if(APPLE)
+ if(CMAKE_SYSTEM_NAME STREQUAL iOS)
+ set(sdk_name "iphoneos")
+ elseif(CMAKE_SYSTEM_NAME STREQUAL visionOS)
+ set(sdk_name "xros")
+ else()
+ # Default to macOS
+ set(sdk_name "macosx")
+ endif()
+ set(xcrun_version_arg "--show-sdk-version")
+ execute_process(COMMAND /usr/bin/xcrun --sdk ${sdk_name} ${xcrun_version_arg}
+ OUTPUT_VARIABLE sdk_version
+ ERROR_VARIABLE xcrun_error)
+ if(NOT sdk_version)
+ message(FATAL_ERROR
+ "Can't determine darwin ${sdk_name} SDK version. Error: ${xcrun_error}")
+ endif()
+ string(STRIP "${sdk_version}" sdk_version)
+ set(${out_var} "${sdk_version}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(_qt_internal_get_xcode_version_raw out_var)
+ if(APPLE)
+ execute_process(COMMAND /usr/bin/xcrun xcodebuild -version
+ OUTPUT_VARIABLE xcode_version
+ ERROR_VARIABLE xcrun_error)
+ string(REPLACE "\n" " " xcode_version "${xcode_version}")
+ string(STRIP "${xcode_version}" xcode_version)
+ set(${out_var} "${xcode_version}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(_qt_internal_get_xcode_version out_var)
+ if(APPLE)
+ _qt_internal_get_xcode_version_raw(xcode_version_raw)
+
+ # The raw output is something like after the newlines are replaced with spaces:
+ # Xcode 14.3 Build version 14E222b
+ # We want only the '14.3' part. We could be more specific with the regex to match only
+ # digits separated by dots, but you never know how Apple might change the format.
+ string(REGEX REPLACE "Xcode (([^ ])+)" "\\2" xcode_version "${xcode_version_raw}")
+ if(xcode_version_raw MATCHES "Xcode ([^ ]+)")
+ set(xcode_version "${CMAKE_MATCH_1}")
+ else()
+ message(DEBUG "Failed to extract Xcode version from '${xcode_version_raw}'")
+ set(xcode_version "${xcode_version_raw}")
+ endif()
+
+ set(${out_var} "${xcode_version}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(_qt_internal_get_cached_apple_sdk_version out_var)
+ if(QT_INTERNAL_APPLE_SDK_VERSION)
+ set(sdk_version "${QT_INTERNAL_APPLE_SDK_VERSION}")
+ else()
+ _qt_internal_get_apple_sdk_version(sdk_version)
+ set(QT_INTERNAL_APPLE_SDK_VERSION "${sdk_version}" CACHE STRING "Apple SDK version")
+ endif()
+
+ set(${out_var} "${sdk_version}" PARENT_SCOPE)
+endfunction()
+
+function(_qt_internal_get_cached_xcode_version out_var)
+ if(QT_INTERNAL_XCODE_VERSION)
+ set(xcode_version "${QT_INTERNAL_XCODE_VERSION}")
+ else()
+ _qt_internal_get_xcode_version(xcode_version)
+ set(QT_INTERNAL_XCODE_VERSION "${xcode_version}" CACHE STRING "Xcode version")
+ endif()
+
+ set(${out_var} "${xcode_version}" PARENT_SCOPE)
+endfunction()
+
+# Warn when the platform SDK or Xcode version are not supported.
+#
+# The warnings are currently only shown when building Qt, not when building user projects
+# with CMake.
+# The warnings ARE shown for qmake user projects.
+#
+# The qmake equivalent for user projects is in mkspecs/features/mac/default_post.prf.
+function(_qt_internal_check_apple_sdk_and_xcode_versions)
+ if(NOT APPLE)
+ return()
+ endif()
+
+ if(QT_NO_APPLE_SDK_AND_XCODE_CHECK)
+ return()
+ endif()
+
+ # Only run the check once in a top-level build.
+ get_property(check_done GLOBAL PROPERTY _qt_internal_apple_sdk_and_xcode_check_done)
+ if(check_done)
+ return()
+ endif()
+ set_property(GLOBAL PROPERTY _qt_internal_apple_sdk_and_xcode_check_done "TRUE")
+
+ if(IOS)
+ set(min_sdk_version "${QT_SUPPORTED_MIN_IOS_SDK_VERSION}")
+ set(max_sdk_version "${QT_SUPPORTED_MAX_IOS_SDK_VERSION}")
+ set(min_xcode_version "${QT_SUPPORTED_MIN_IOS_XCODE_VERSION}")
+ elseif(VISIONOS)
+ set(min_sdk_version "${QT_SUPPORTED_MIN_VISIONOS_SDK_VERSION}")
+ set(max_sdk_version "${QT_SUPPORTED_MAX_VISIONOS_SDK_VERSION}")
+ set(min_xcode_version "${QT_SUPPORTED_MIN_VISIONOS_XCODE_VERSION}")
+ else()
+ set(min_sdk_version "${QT_SUPPORTED_MIN_MACOS_SDK_VERSION}")
+ set(max_sdk_version "${QT_SUPPORTED_MAX_MACOS_SDK_VERSION}")
+ set(min_xcode_version "${QT_SUPPORTED_MIN_MACOS_XCODE_VERSION}")
+ endif()
+
+ _qt_internal_get_cached_apple_sdk_version(sdk_version)
+ _qt_internal_get_cached_xcode_version(xcode_version)
+
+ if(NOT max_sdk_version MATCHES "^[0-9]+$")
+ message(FATAL_ERROR
+ "Invalid max SDK version: ${max_sdk_version} "
+ "It should be a major version number, without minor or patch version components.")
+ endif()
+
+ # The default differs in different branches.
+ set(failed_check_should_error FALSE)
+
+ if(failed_check_should_error)
+ # Allow downgrading the error into a warning.
+ if(QT_FORCE_WARN_APPLE_SDK_AND_XCODE_CHECK)
+ set(message_type WARNING)
+ set(extra_message " Due to QT_FORCE_WARN_APPLE_SDK_AND_XCODE_CHECK being ON "
+ "the build will continue, but it will likely fail. Use at your own risk.")
+ else()
+ set(message_type FATAL_ERROR)
+ set(extra_message " You can turn this error into a warning by configuring with "
+ "-DQT_FORCE_WARN_APPLE_SDK_AND_XCODE_CHECK=ON, but the build will likely fail. "
+ "Use at your own risk.")
+ endif()
+ else()
+ # Allow upgrading the warning into an error.
+ if(QT_FORCE_FATAL_APPLE_SDK_AND_XCODE_CHECK)
+ set(message_type FATAL_ERROR)
+ set(extra_message " Erroring out due to QT_FORCE_FATAL_APPLE_SDK_AND_XCODE_CHECK "
+ "being ON.")
+ else()
+ set(message_type WARNING)
+ set(extra_message " You can turn this warning into an error by configuring with "
+ "-DQT_FORCE_FATAL_APPLE_SDK_AND_XCODE_CHECK=ON. ")
+ endif()
+ endif()
+
+ if(sdk_version VERSION_LESS min_sdk_version AND NOT QT_NO_APPLE_SDK_MIN_VERSION_CHECK)
+ message(${message_type}
+ "Qt requires at least version ${min_sdk_version} of the platform SDK, "
+ "you're building against version ${sdk_version}. Please upgrade."
+ ${extra_message}
+ )
+ endif()
+
+ if(xcode_version VERSION_LESS min_xcode_version AND NOT QT_NO_XCODE_MIN_VERSION_CHECK)
+ message(${message_type}
+ "Qt requires at least version ${min_xcode_version} of Xcode, "
+ "you're building against version ${xcode_version}. Please upgrade."
+ ${extra_message}
+ )
+ endif()
+
+ if(QT_NO_APPLE_SDK_MAX_VERSION_CHECK)
+ return()
+ endif()
+
+ # Make sure we warn only when the current version is greater than the max supported version.
+ math(EXPR next_after_max_sdk_version "${max_sdk_version} + 1")
+ if(sdk_version VERSION_GREATER_EQUAL next_after_max_sdk_version)
+ message(WARNING
+ "Qt has only been tested with version ${max_sdk_version} "
+ "of the platform SDK, you're using ${sdk_version}. "
+ "This is an unsupported configuration. You may experience build issues, "
+ "and by using "
+ "the ${sdk_version} SDK you are opting in to new features "
+ "that Qt has not been prepared for. "
+ "Please downgrade the SDK you use to build your app to version "
+ "${max_sdk_version}, or configure "
+ "with -DQT_NO_APPLE_SDK_MAX_VERSION_CHECK=ON to silence this warning."
+ )
+ endif()
+endfunction()
+
+function(_qt_internal_finalize_apple_app target)
+ # Shared between macOS and UIKit apps
+
+ _qt_internal_copy_info_plist("${target}")
+ _qt_internal_set_apple_localizations("${target}")
+
+ # Only set the various properties if targeting the Xcode generator, otherwise the various
+ # Xcode tokens are embedded as-is instead of being dynamically evaluated.
+ # This affects things like the version number or application name as reported by Qt API.
+ if(CMAKE_GENERATOR STREQUAL "Xcode")
+ _qt_internal_set_xcode_development_team_id("${target}")
+ _qt_internal_set_xcode_code_sign_style("${target}")
+ _qt_internal_set_xcode_bundle_display_name("${target}")
+ _qt_internal_set_xcode_install_path("${target}")
+ endif()
+
+ _qt_internal_set_xcode_bundle_name("${target}")
+ _qt_internal_set_apple_bundle_identifier("${target}")
+ _qt_internal_set_placeholder_apple_bundle_version("${target}")
+endfunction()
+
+function(_qt_internal_finalize_uikit_app target)
+ if(CMAKE_SYSTEM_NAME STREQUAL iOS)
+ _qt_internal_finalize_ios_app("${target}")
+ else()
+ _qt_internal_finalize_apple_app("${target}")
+ endif()
+endfunction()
+
+function(_qt_internal_finalize_ios_app target)
+ # Must be called before we generate the Info.plist
+ _qt_internal_handle_ios_launch_screen("${target}")
+
+ _qt_internal_finalize_apple_app("${target}")
+ _qt_internal_set_xcode_targeted_device_family("${target}")
+ _qt_internal_set_xcode_bitcode_enablement("${target}")
+ _qt_internal_set_ios_simulator_arch("${target}")
+endfunction()
+
+function(_qt_internal_finalize_macos_app target)
+ get_target_property(is_bundle ${target} MACOSX_BUNDLE)
+ if(NOT is_bundle)
+ return()
+ endif()
+
+ _qt_internal_finalize_apple_app("${target}")
+
+ # Make sure the install rpath has at least the minimum needed if the app
+ # has any non-static frameworks. We can't rigorously know if the app will
+ # have any, even with a static Qt, so always add this. If there are no
+ # frameworks, it won't do any harm.
+ get_property(install_rpath TARGET ${target} PROPERTY INSTALL_RPATH)
+ list(APPEND install_rpath "@executable_path/../Frameworks")
+ list(REMOVE_DUPLICATES install_rpath)
+ set_property(TARGET ${target} PROPERTY INSTALL_RPATH "${install_rpath}")
+endfunction()