summaryrefslogtreecommitdiffstats
path: root/mkspecs/features/uikit
diff options
context:
space:
mode:
Diffstat (limited to 'mkspecs/features/uikit')
-rw-r--r--mkspecs/features/uikit/bitcode.prf13
-rw-r--r--mkspecs/features/uikit/default_post.prf109
-rw-r--r--mkspecs/features/uikit/default_pre.prf31
-rwxr-xr-xmkspecs/features/uikit/device_destinations.sh65
-rw-r--r--mkspecs/features/uikit/exclusive_builds_post.prf8
-rw-r--r--mkspecs/features/uikit/qt.prf33
-rw-r--r--mkspecs/features/uikit/qt_config.prf18
-rw-r--r--mkspecs/features/uikit/qt_parts.prf5
-rw-r--r--mkspecs/features/uikit/resolve_config.prf68
-rw-r--r--mkspecs/features/uikit/sdk.prf32
-rw-r--r--mkspecs/features/uikit/testcase.prf12
-rw-r--r--mkspecs/features/uikit/testcase_targets.prf3
-rw-r--r--mkspecs/features/uikit/xcodebuild.mk101
-rw-r--r--mkspecs/features/uikit/xcodebuild.prf61
14 files changed, 559 insertions, 0 deletions
diff --git a/mkspecs/features/uikit/bitcode.prf b/mkspecs/features/uikit/bitcode.prf
new file mode 100644
index 0000000000..ecc6542b3c
--- /dev/null
+++ b/mkspecs/features/uikit/bitcode.prf
@@ -0,0 +1,13 @@
+lessThan(QMAKE_XCODE_VERSION, "7.0") {
+ warning("You need to update Xcode to version 7 or newer to support bitcode")
+} else {
+ release:device {
+ QMAKE_CFLAGS += -fembed-bitcode
+ QMAKE_CXXFLAGS += -fembed-bitcode
+ QMAKE_OBJECTIVE_CFLAGS += -fembed-bitcode
+ } else {
+ QMAKE_CFLAGS += -fembed-bitcode-marker
+ QMAKE_CXXFLAGS += -fembed-bitcode-marker
+ QMAKE_OBJECTIVE_CFLAGS += -fembed-bitcode-marker
+ }
+}
diff --git a/mkspecs/features/uikit/default_post.prf b/mkspecs/features/uikit/default_post.prf
new file mode 100644
index 0000000000..7c92a24556
--- /dev/null
+++ b/mkspecs/features/uikit/default_post.prf
@@ -0,0 +1,109 @@
+equals(TEMPLATE, app):qt {
+ # If the application uses Qt, it needs to be an application bundle
+ # to be able to deploy and run on iOS. The only exception to this
+ # is if you're working with a jailbroken device and can run the
+ # resulting binary from the console/over SSH, but that's not a
+ # use-case we care about, so no need to complicate the logic.
+ CONFIG *= app_bundle
+
+ # For Qt applications we want Xcode project files as the generated output,
+ # but since qmake doesn't handle the transition between makefiles and Xcode
+ # project files (which happens when using subdirs), we can't just override
+ # MAKEFILE_GENERATOR. Instead, we generate the Xcode project by spawning a
+ # child qmake process with -spec macx-xcode and let the top level qmake
+ # process generate a wrapper makefile that forwards everything to xcodebuild.
+ equals(MAKEFILE_GENERATOR, UNIX): \
+ CONFIG = xcodebuild $$CONFIG
+}
+
+load(default_post)
+
+macx-xcode {
+ device_family.name = TARGETED_DEVICE_FAMILY
+ ios: device_family.value = $$QMAKE_IOS_TARGETED_DEVICE_FAMILY
+ tvos: device_family.value = $$QMAKE_TVOS_TARGETED_DEVICE_FAMILY
+ QMAKE_MAC_XCODE_SETTINGS += device_family
+
+ ios {
+ # If QMAKE_BUNDLE_DATA contains an asset catalog that includes an
+ # AppIcon.appiconset, we configure Xcode to use it for app icons.
+ for(bundle_data, QMAKE_BUNDLE_DATA) {
+ for(bundle_file, $${bundle_data}.files) {
+ !contains(bundle_file, .*\.xcassets$): next()
+ !exists($$absolute_path($$bundle_file/AppIcon.appiconset, $$_PRO_FILE_PWD_)): next()
+
+ asset_catalog_appicon.name = "ASSETCATALOG_COMPILER_APPICON_NAME"
+ asset_catalog_appicon.value = "AppIcon"
+ QMAKE_MAC_XCODE_SETTINGS += asset_catalog_appicon
+ break()
+ }
+ !isEmpty(asset_catalog_appicon.name): break()
+ }
+
+ # Set up default 4-inch iPhone/iPod launch image so that our apps
+ # support the full screen resolution of those devices.
+ qmake_launch_image = Default-568h@2x.png
+ qmake_copy_image.input = $$QMAKESPEC/$$qmake_launch_image
+ qmake_copy_image.output = $$OUT_PWD/$${TARGET}.xcodeproj/$$qmake_launch_image
+ qmake_copy_image.CONFIG = verbatim
+ QMAKE_SUBSTITUTES += qmake_copy_image
+ qmake_launch_images.files = $$qmake_copy_image.output
+ QMAKE_BUNDLE_DATA += qmake_launch_images
+
+ lessThan(QMAKE_XCODE_VERSION, "6.0") {
+ warning("You need to update Xcode to version 6 or newer to fully support iPhone6/6+")
+ } else {
+ # Set up default LaunchScreen to support iPhone6/6+
+ qmake_launch_screen = LaunchScreen.xib
+ qmake_copy_launch_screen.input = $$QMAKESPEC/$$qmake_launch_screen
+ qmake_copy_launch_screen.output = $$OUT_PWD/$${TARGET}.xcodeproj/$$qmake_launch_screen
+ QMAKE_SUBSTITUTES += qmake_copy_launch_screen
+ qmake_launch_screens.files = $$qmake_copy_launch_screen.output
+ QMAKE_BUNDLE_DATA += qmake_launch_screens
+ }
+ }
+}
+
+macx-xcode {
+ arch_device.name = "ARCHS[sdk=$${device.sdk}*]"
+ arch_simulator.name = "ARCHS[sdk=$${simulator.sdk}*]"
+ ios {
+ arch_device.value = $$QMAKE_IOS_DEVICE_ARCHS
+ arch_simulator.value = $$QMAKE_IOS_SIMULATOR_ARCHS
+ QMAKE_XCODE_ARCHS = $$QMAKE_IOS_DEVICE_ARCHS $$QMAKE_IOS_SIMULATOR_ARCHS
+ }
+ tvos {
+ arch_device.value = $$QMAKE_TVOS_DEVICE_ARCHS
+ arch_simulator.value = $$QMAKE_TVOS_SIMULATOR_ARCHS
+ QMAKE_XCODE_ARCHS = $$QMAKE_TVOS_DEVICE_ARCHS $$QMAKE_TVOS_SIMULATOR_ARCHS
+ }
+
+ QMAKE_MAC_XCODE_SETTINGS += arch_device arch_simulator
+
+ only_active_arch.name = ONLY_ACTIVE_ARCH
+ only_active_arch.value = YES
+ only_active_arch.build = debug
+ QMAKE_MAC_XCODE_SETTINGS += only_active_arch
+} else {
+ # Be more specific about which architecture we're targeting
+ contains(QT_ARCH, arm.*) {
+ ios: VALID_ARCHS = $$QMAKE_IOS_DEVICE_ARCHS
+ tvos: VALID_ARCHS = $$QMAKE_TVOS_DEVICE_ARCHS
+ } else {
+ ios: VALID_ARCHS = $$QMAKE_IOS_SIMULATOR_ARCHS
+ tvos: VALID_ARCHS = $$QMAKE_TVOS_SIMULATOR_ARCHS
+ }
+
+ single_arch: VALID_ARCHS = $$first(VALID_ARCHS)
+
+ ACTIVE_ARCHS = $(filter $(EXPORT_VALID_ARCHS), $(ARCHS))
+ ARCH_ARGS = $(foreach arch, $(if $(EXPORT_ACTIVE_ARCHS), $(EXPORT_ACTIVE_ARCHS), $(EXPORT_VALID_ARCHS)), -arch $(arch))
+
+ QMAKE_EXTRA_VARIABLES += VALID_ARCHS ACTIVE_ARCHS ARCH_ARGS
+
+ arch_flags = $(EXPORT_ARCH_ARGS)
+
+ QMAKE_CFLAGS += $$arch_flags
+ QMAKE_CXXFLAGS += $$arch_flags
+ QMAKE_LFLAGS += $$arch_flags
+}
diff --git a/mkspecs/features/uikit/default_pre.prf b/mkspecs/features/uikit/default_pre.prf
new file mode 100644
index 0000000000..ff45c1de0d
--- /dev/null
+++ b/mkspecs/features/uikit/default_pre.prf
@@ -0,0 +1,31 @@
+
+load(default_pre)
+
+# In case Qt was built for a specific SDK
+!contains(QT_CONFIG, simulator_and_device):contains(QMAKE_MAC_SDK, ^$${simulator.sdk}.*): \
+ CONFIG += simulator $${simulator.sdk}
+
+# Check for supported Xcode versions
+lessThan(QMAKE_XCODE_VERSION, "4.3"): \
+ error("This mkspec requires Xcode 4.3 or later")
+
+build_pass:simulator {
+ # For a simulator_and_device build all the config tests
+ # are based on the iPhoneOS ARM SDK, but we know that the simulator
+ # is i386 and that we support SSE/SSE2.
+ QT_ARCH = i386
+ QT_CPU_FEATURES.i386 = sse sse2
+ DEFINES += QT_COMPILER_SUPPORTS_SSE2
+ CONFIG -= neon
+ CONFIG += sse sse2
+}
+build_pass:appletvsimulator {
+ # For a simulator_and_device build all the config tests
+ # are based on the AppleTVOS ARM SDK, but we know that the simulator
+ # is x64 and that we support SSE/SSE2.
+ QT_ARCH = x64
+ QT_CPU_FEATURES.x64 = sse sse2
+ DEFINES += QT_COMPILER_SUPPORTS_SSE2
+ CONFIG -= neon
+ CONFIG += sse sse2
+}
diff --git a/mkspecs/features/uikit/device_destinations.sh b/mkspecs/features/uikit/device_destinations.sh
new file mode 100755
index 0000000000..af7cb91e85
--- /dev/null
+++ b/mkspecs/features/uikit/device_destinations.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+
+#############################################################################
+##
+## Copyright (C) 2016 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is the build configuration utility of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+booted_simulator=$(xcrun simctl list devices | grep -E '$1' | grep -v unavailable | grep Booted | perl -lne 'print $2 if /\((.*?)\)/')
+echo "SIMULATOR_DEVICES = $booted_simulator"
+
+xcodebuild test -scheme $2 -destination 'id=0' -destination-timeout 1 2>&1| sed -n 's/{ \(platform:.*\) }/\1/p' | while read destination; do
+ id=$(echo $destination | sed -n -E 's/.*id:([^ ,]+).*/\1/p')
+ echo $destination | tr ',' '\n' | while read keyval; do
+ key=$(echo $keyval | cut -d ':' -f 1 | tr '[:lower:]' '[:upper:]')
+ val=$(echo $keyval | cut -d ':' -f 2)
+ echo "%_$id: DESTINATION_${key} = $val"
+
+ if [ $key = 'PLATFORM' ]; then
+ if [ "$val" = "iOS" ]; then
+ echo "HARDWARE_DEVICES += $id"
+ elif [ "$val" = "iOS Simulator" -a "$id" != "$booted_simulator" ]; then
+ echo "SIMULATOR_DEVICES += $id"
+ elif [ "$val" = "tvOS" ]; then
+ echo "HARDWARE_DEVICES += $id"
+ elif [ "$val" = "tvOS Simulator" -a "$id" != "$booted_simulator" ]; then
+ echo "SIMULATOR_DEVICES += $id"
+ fi
+ fi
+ done
+ echo
+done
diff --git a/mkspecs/features/uikit/exclusive_builds_post.prf b/mkspecs/features/uikit/exclusive_builds_post.prf
new file mode 100644
index 0000000000..1fb0a55846
--- /dev/null
+++ b/mkspecs/features/uikit/exclusive_builds_post.prf
@@ -0,0 +1,8 @@
+
+xcodebuild {
+ # Prevent qmake from generating empty output dirs for each exclusive build,
+ # as Xcode will do this by itself, and with a different name.
+ QMAKE_DIR_REPLACE =
+}
+
+load(exclusive_builds_post)
diff --git a/mkspecs/features/uikit/qt.prf b/mkspecs/features/uikit/qt.prf
new file mode 100644
index 0000000000..af047be466
--- /dev/null
+++ b/mkspecs/features/uikit/qt.prf
@@ -0,0 +1,33 @@
+
+qt_depends = $$resolve_depends(QT, "QT.")
+equals(TEMPLATE, app):contains(qt_depends, gui(-private)?) {
+ LIBS *= -L$$[QT_INSTALL_PLUGINS/get]/platforms
+
+ lib_name = qios
+ lib_path_and_base = $$[QT_INSTALL_PLUGINS/get]/platforms/lib$${lib_name}$$qtPlatformTargetSuffix()
+ LIBS += -l$${lib_name}$$qtPlatformTargetSuffix() $$fromfile($${lib_path_and_base}.prl, QMAKE_PRL_LIBS)
+
+ !bitcode {
+ # By marking qt_registerPlatformPlugin as undefined, we ensure that
+ # the plugin.o translation unit is considered for inclusion in
+ # the final binary, which in turn ensures that the plugin's
+ # static initializer is included and run.
+ QMAKE_LFLAGS += -u _qt_registerPlatformPlugin
+
+ # We do link and dependency resolution for the platform plugin
+ # manually, since we know we always need the plugin, so we don't
+ # need to generate an import for it.
+ QTPLUGIN.platforms = -
+ }
+
+ !no_main_wrapper {
+ # The LC_MAIN load command available in iOS 6.0 and above allows dyld to
+ # directly call the entrypoint instead of going through _start in crt.o.
+ # Passing -e to the linker changes the entrypoint from _main to our custom
+ # wrapper that calls UIApplicationMain and dispatches back to main() once
+ # the application has started up and is ready to initialize QApplication.
+ QMAKE_LFLAGS += -Wl,-e,_qt_main_wrapper
+ }
+}
+
+load(qt)
diff --git a/mkspecs/features/uikit/qt_config.prf b/mkspecs/features/uikit/qt_config.prf
new file mode 100644
index 0000000000..71e0982f7e
--- /dev/null
+++ b/mkspecs/features/uikit/qt_config.prf
@@ -0,0 +1,18 @@
+load(qt_config)
+
+isEmpty(QT_ARCH) {
+ # The configure tests are run without QT_ARCH being resolved yet, which
+ # means we fail to pass -arch to the compiler, resulting in broke tests.
+ # As the Xcode toolchain doesn't seem to have a way to auto-detect the
+ # arch based on the SDK, we have to hard-code the arch for configure.
+ contains(QMAKE_MAC_SDK, $${device.sdk}.*) {
+ QT_ARCH = arm
+ } else { # Simulator
+ ios: QT_ARCH = i386
+ tvos: QT_ARCH = x64
+ }
+
+ # Prevent the arch/config tests from building as multi-arch binaries,
+ # as we only want the lowest common denominator features.
+ CONFIG += single_arch
+}
diff --git a/mkspecs/features/uikit/qt_parts.prf b/mkspecs/features/uikit/qt_parts.prf
new file mode 100644
index 0000000000..81814a62b0
--- /dev/null
+++ b/mkspecs/features/uikit/qt_parts.prf
@@ -0,0 +1,5 @@
+
+# Disable tests for anything but qtbase for now
+!equals(TARGET, qtbase): QT_BUILD_PARTS -= tests
+
+load(qt_parts)
diff --git a/mkspecs/features/uikit/resolve_config.prf b/mkspecs/features/uikit/resolve_config.prf
new file mode 100644
index 0000000000..c3ab90f45e
--- /dev/null
+++ b/mkspecs/features/uikit/resolve_config.prf
@@ -0,0 +1,68 @@
+
+xcodebuild {
+ # Xcode project files always support both Debug and Release configurations
+ # and iOS device and simulator targets, so we make sure the wrapper-makefile
+ # also does.
+ CONFIG += debug_and_release simulator_and_device
+}
+
+load(resolve_config)
+
+# Legacy exclusive build configurations for backwards compatibility
+CONFIG($${device.CONFIG}, $${device.CONFIG}|$${simulator.CONFIG}): \
+ CONFIG += device
+else: CONFIG($${simulator.CONFIG}, $${device.CONFIG}|$${simulator.CONFIG}): \
+ CONFIG += simulator
+
+CONFIG(simulator, simulator|device): \
+ CONFIG -= device $${device.CONFIG}
+else: \
+ CONFIG -= simulator $${simulator.CONFIG}
+
+macx-xcode {
+ # There is no way to genereate Xcode projects that are limited to either
+ # simulator or device builds, so simulator_and_device is always
+ # effectivly active, even if the user disabled it explicitly.
+ # The Xcode generator doesn't support multiple BUILDS though (exclusive
+ # builds), so we have to manually set up the simulator suffix.
+ library_suffix_simulator.name = "$${QMAKE_XCODE_LIBRARY_SUFFIX_SETTING}[sdk=$${simulator.sdk}*]"
+ library_suffix_simulator.value = "_$${simulator.sdk}$($${QMAKE_XCODE_LIBRARY_SUFFIX_SETTING})"
+ QMAKE_MAC_XCODE_SETTINGS += library_suffix_simulator
+ CONFIG *= xcode_dynamic_library_suffix
+} else {
+ addExclusiveBuilds(simulator, device)
+}
+
+equals(TEMPLATE, subdirs) {
+ # Prevent recursion into host_builds
+ for(subdir, SUBDIRS) {
+ contains($${subdir}.CONFIG, host_build) {
+ $${subdir}.CONFIG += no_$${simulator.target}_target no_$${device.target}_target
+
+ # Other targets which we do want to recurse into may depend on this target,
+ # for example corelib depends on moc, rcc, bootstrap, etc, and other libs
+ # may depend on host-tools that are needed to build the lib, so we resolve
+ # the final target name and redirect it to the base target, so that the
+ # dependency chain is not broken for the other targets.
+
+ !isEmpty($${subdir}.target) {
+ target = $$eval($${subdir}.target)
+ } else {
+ !isEmpty($${subdir}.file): \
+ file = $$eval($${subdir}.file)
+ else: !isEmpty($${subdir}.subdir): \
+ file = $$eval($${subdir}.subdir)
+ else: \
+ file = $$subdir
+
+ target = sub-$$file
+ }
+
+ target ~= s,[^a-zA-Z0-9_],-,
+
+ $${target}-$${simulator.target}.depends = $$target
+ $${target}-$${device.target}.depends = $$target
+ QMAKE_EXTRA_TARGETS += $${target}-$${simulator.target} $${target}-$${device.target}
+ }
+ }
+}
diff --git a/mkspecs/features/uikit/sdk.prf b/mkspecs/features/uikit/sdk.prf
new file mode 100644
index 0000000000..94451ea196
--- /dev/null
+++ b/mkspecs/features/uikit/sdk.prf
@@ -0,0 +1,32 @@
+
+# In case the user sets the SDK manually
+contains(QMAKE_MAC_SDK, ^$${simulator.sdk}.*) {
+ contains(QT_CONFIG, simulator_and_device): \
+ error("Simulator is handled automatically for simulator_and_device")
+
+ CONFIG += simulator $${simulator.sdk}
+}
+
+build_pass:simulator: \
+ QMAKE_MAC_SDK ~= s,^$${device.sdk},$${simulator.sdk},
+
+load(sdk)
+
+lessThan(QMAKE_MAC_SDK_VERSION, "8.0"): \
+ error("Current $$QMAKE_MAC_SDK SDK version ($$QMAKE_MAC_SDK_VERSION) is too old. Please upgrade Xcode.")
+
+macx-xcode {
+ sdk_path_device.name = "QMAKE_MAC_SDK_PATH[sdk=$${device.sdk}*]"
+ sdk_path_device.value = $$xcodeSDKInfo(Path, $${device.sdk})
+ sdk_path_simulator.name = "QMAKE_MAC_SDK_PATH[sdk=$${simulator.sdk}*]"
+ sdk_path_simulator.value = $$xcodeSDKInfo(Path, $${simulator.sdk})
+ QMAKE_MAC_XCODE_SETTINGS += sdk_path_device sdk_path_simulator
+ QMAKE_MAC_SDK_PATH = "$(QMAKE_MAC_SDK_PATH)"
+
+ sdk_platform_path_device.name = "QMAKE_MAC_SDK_PLATFORM_PATH[sdk=$${device.sdk}*]"
+ sdk_platform_path_device.value = $$xcodeSDKInfo(PlatformPath, $${device.sdk})
+ sdk_platform_path_simulator.name = "QMAKE_MAC_SDK_PLATFORM_PATH[sdk=$${simulator.sdk}*]"
+ sdk_platform_path_simulator.value = $$xcodeSDKInfo(PlatformPath, $${simulator.sdk})
+ QMAKE_MAC_XCODE_SETTINGS += sdk_platform_path_device sdk_platform_path_simulator
+ QMAKE_MAC_SDK_PLATFORM_PATH = "$(QMAKE_MAC_SDK_PLATFORM_PATH)"
+}
diff --git a/mkspecs/features/uikit/testcase.prf b/mkspecs/features/uikit/testcase.prf
new file mode 100644
index 0000000000..e16c163ffa
--- /dev/null
+++ b/mkspecs/features/uikit/testcase.prf
@@ -0,0 +1,12 @@
+# Pretend we have a target, even though our template is aux
+xcodebuild: \
+ CONFIG += have_target
+
+load(testcase)
+
+# We provide our own check logic
+xcodebuild {
+ check.depends =
+ check.commands =
+ QMAKE_EXTRA_TARGETS *= check
+}
diff --git a/mkspecs/features/uikit/testcase_targets.prf b/mkspecs/features/uikit/testcase_targets.prf
new file mode 100644
index 0000000000..e0a2922c3f
--- /dev/null
+++ b/mkspecs/features/uikit/testcase_targets.prf
@@ -0,0 +1,3 @@
+# For the xcodebuild wrapper makefile we deal with test targets manually
+!xcodebuild: \
+ load(testcase_targets)
diff --git a/mkspecs/features/uikit/xcodebuild.mk b/mkspecs/features/uikit/xcodebuild.mk
new file mode 100644
index 0000000000..5cbad60804
--- /dev/null
+++ b/mkspecs/features/uikit/xcodebuild.mk
@@ -0,0 +1,101 @@
+
+# We don't want xcodebuild to run in parallel
+.NOTPARALLEL:
+
+# Functions
+targets = $(foreach target, $(EXPORT_SUBTARGETS), $(target)-$(strip $(1)))
+toupper = $(shell echo $1 | tr '[:lower:]' '[:upper:]')
+tolower = $(shell echo $1 | tr '[:upper:]' '[:lower:]')
+basesdk = $(shell echo $1 | sed 's/[0-9.]*$$//')
+
+# Explicit comma variable
+, := ,
+
+# Default targets
+first: build
+all: build_all
+
+.DEFAULT_GOAL = first
+
+# Top level targets
+build: build_first
+clean: clean_first
+install: install_first
+check: check_first
+distclean: clean_all
+
+$(EXPORT_SUBTARGETS): % : %-build
+
+# Generic targets
+%_first: $(firstword $(call targets, %)) ;
+%_all: $(call targets, %) ;
+
+# Actions
+%-build: ACTION = build
+%-build: xcodebuild-% ;
+
+%-clean: ACTION = clean
+%-clean: xcodebuild-% ;
+
+%-install: ACTION = install
+%-install: xcodebuild-% ;
+
+# Simulator doesn't support archiving
+%-simulator-install: ACTION = build
+simulator-install: ACTION = build
+
+# Limit check to a single configuration
+%-device-check: check-device ;
+%-simulator-check: check-simulator ;
+
+# SDK
+%-device: SDK = $(DEVICE_SDK)
+%-simulator: SDK = $(SIMULATOR_SDK)
+
+# Configuration
+release-%: CONFIGURATION = Release
+debug-%: CONFIGURATION = Debug
+
+# Test and build (device) destinations
+ifneq ($(filter check%,$(MAKECMDGOALS)),)
+ ifeq ($(DEVICES),)
+ $(info Enumerating test destinations (you may override this by setting DEVICES explicitly), please wait...)
+ SPECDIR := $(dir $(lastword $(MAKEFILE_LIST)))
+ DESTINATIONS_INCLUDE = /tmp/device_destinations.mk
+ $(shell $(SPECDIR)/../features/uikit/device_destinations.sh '$(EXPORT_DEVICE_FILTER)' $(TARGET) > $(DESTINATIONS_INCLUDE))
+ include $(DESTINATIONS_INCLUDE)
+ endif
+endif
+
+%-simulator: DEVICES = $(firstword $(SIMULATOR_DEVICES))
+%-device: DEVICES = $(HARDWARE_DEVICES)
+
+GENERIC_DEVICE_DESTINATION := $(EXPORT_GENERIC_DEVICE_DESTINATION)
+GENERIC_SIMULATOR_DESTINATION := "id=$(shell xcrun simctl list devices | grep -E '$(EXPORT_DEVICE_FILTER)' | grep -v unavailable | perl -lne 'print $$1 if /\((.*?)\)/' | tail -n 1)"
+
+%-simulator: DESTINATION = $(if $(DESTINATION_ID),"id=$(DESTINATION_ID)",$(GENERIC_SIMULATOR_DESTINATION))
+%-device: DESTINATION = $(if $(DESTINATION_ID),"id=$(DESTINATION_ID)",$(GENERIC_DEVICE_DESTINATION))
+
+# Xcodebuild
+
+DESTINATION_MESSAGE = "Running $(call tolower,$(CONFIGURATION)) $(ACTION) \
+ on '$(DESTINATION_NAME)' ($(DESTINATION_ID))$(if $(DESTINATION_OS),$(,) $(DESTINATION_PLATFORM) $(DESTINATION_OS),)"
+
+xcodebuild-%:
+ @$(if $(DESTINATION_NAME), echo $(DESTINATION_MESSAGE),)
+ xcodebuild $(ACTION) -scheme $(TARGET) $(if $(SDK), -sdk $(SDK),) $(if $(CONFIGURATION), -configuration $(CONFIGURATION),) $(if $(DESTINATION), -destination $(DESTINATION) -destination-timeout 1,)
+
+xcodebuild-check-device_%: DESTINATION_ID=$(lastword $(subst _, ,$@))
+
+# Special check target (requires SECONDEXPANSION due to devices)
+.SECONDEXPANSION:
+check-%: ACTION = test
+check-%: $$(foreach device, $$(DEVICES), xcodebuild-check-device_$$(device)) ;
+ @echo $(if $^, Ran $(call tolower,$(CONFIGURATION)) tests on $(words $^) $(SDK) destination\(s\): $(DEVICES), No compatible test devices found for \'$(SDK)\' SDK && false)
+
+# Determined by device
+check-%: SDK =
+
+# Default to debug for testing
+check-%: CONFIGURATION = Debug
+
diff --git a/mkspecs/features/uikit/xcodebuild.prf b/mkspecs/features/uikit/xcodebuild.prf
new file mode 100644
index 0000000000..6e50bbcf50
--- /dev/null
+++ b/mkspecs/features/uikit/xcodebuild.prf
@@ -0,0 +1,61 @@
+
+# For Qt applications we want Xcode project files as the generated output,
+# but since qmake doesn't handle the transition between makefiles and Xcode
+# project files (which happens when using subdirs), we can't just override
+# MAKEFILE_GENERATOR. Instead, we generate the Xcode project by spawing a
+# child qmake process with -spec macx-xcode and let the top level qmake
+# process generate a wrapper makefile that forwards everything to xcodebuild.
+
+TEMPLATE = aux
+
+SOURCES =
+OBJECTIVE_SOURCES =
+RESOURCES =
+INSTALLS =
+QMAKE_EXTRA_COMPILERS =
+
+!mkpath($$OUT_PWD): \
+ error("Failed to create $$OUT_PWD")
+
+args =
+for(arg, QMAKE_ARGS): \
+ args += $$system_quote($$arg)
+
+cmd = "$$QMAKE_QMAKE $$args $$system_quote($$_PRO_FILE_) -spec macx-xcode"
+debug(1, "Generating Xcode project in $$OUT_PWD using '$$cmd'")
+system("cd $$system_quote($$OUT_PWD) && $$cmd")
+
+# Subtargets
+
+for(build, BUILDS): \
+ SUBTARGETS += $$eval($${build}.target)
+QMAKE_EXTRA_VARIABLES += SUBTARGETS
+
+CONFIG += no_default_goal_deps
+
+DEVICE_SDK = $${device.sdk}
+SIMULATOR_SDK = $${simulator.sdk}
+ios {
+ DEVICE_FILTER = "iPhone|iPad"
+ GENERIC_DEVICE_DESTINATION = "generic/platform=iOS"
+}
+tvos {
+ DEVICE_FILTER = "Apple TV"
+ GENERIC_DEVICE_DESTINATION = "generic/platform=tvOS"
+}
+QMAKE_EXTRA_VARIABLES += DEVICE_SDK SIMULATOR_SDK DEVICE_FILTER GENERIC_DEVICE_DESTINATION
+
+QMAKE_EXTRA_INCLUDES += $$shell_quote($$PWD/xcodebuild.mk)
+
+# Distclean
+
+distfiles = $${TARGET}.xcodeproj
+for(build, BUILDS): \
+ distfiles += $$title($$eval($${build}.target))
+distclean_xcodebuild.commands = -$(DEL_FILE) -R $$distfiles
+
+distclean.depends += clean_all distclean_xcodebuild
+QMAKE_EXTRA_TARGETS += distclean distclean_xcodebuild
+
+# Empty exclusive builds, we've set them up manually
+BUILDS =