summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Help/manual/cmake-properties.7.rst1
-rw-r--r--Help/manual/cmake-variables.7.rst1
-rw-r--r--Help/prop_tgt/IOS_INSTALL_COMBINED.rst11
-rw-r--r--Help/release/dev/ios-universal.rst7
-rw-r--r--Help/variable/CMAKE_IOS_INSTALL_COMBINED.rst8
-rw-r--r--Modules/CMakeIOSInstallCombined.cmake297
-rw-r--r--Source/cmInstallTargetGenerator.cxx44
-rw-r--r--Source/cmInstallTargetGenerator.h2
-rw-r--r--Source/cmTarget.cxx1
-rw-r--r--Tests/RunCMake/XcodeProject/RunCMakeTest.cmake36
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined-install-check.cmake30
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake27
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune-install-check.cmake26
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune-install-stdout.txt2
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake36
-rw-r--r--Tests/RunCMake/XcodeProject/main.cpp3
16 files changed, 532 insertions, 0 deletions
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 931363c..a41d484 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -191,6 +191,7 @@ Properties on Targets
/prop_tgt/INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
/prop_tgt/INTERPROCEDURAL_OPTIMIZATION_CONFIG
/prop_tgt/INTERPROCEDURAL_OPTIMIZATION
+ /prop_tgt/IOS_INSTALL_COMBINED
/prop_tgt/JOB_POOL_COMPILE
/prop_tgt/JOB_POOL_LINK
/prop_tgt/LABELS
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 2116900..3f49572 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -257,6 +257,7 @@ Variables that Control the Build
/variable/CMAKE_INSTALL_NAME_DIR
/variable/CMAKE_INSTALL_RPATH
/variable/CMAKE_INSTALL_RPATH_USE_LINK_PATH
+ /variable/CMAKE_IOS_INSTALL_COMBINED
/variable/CMAKE_LANG_COMPILER_LAUNCHER
/variable/CMAKE_LANG_INCLUDE_WHAT_YOU_USE
/variable/CMAKE_LANG_VISIBILITY_PRESET
diff --git a/Help/prop_tgt/IOS_INSTALL_COMBINED.rst b/Help/prop_tgt/IOS_INSTALL_COMBINED.rst
new file mode 100644
index 0000000..59f67a7
--- /dev/null
+++ b/Help/prop_tgt/IOS_INSTALL_COMBINED.rst
@@ -0,0 +1,11 @@
+IOS_INSTALL_COMBINED
+--------------------
+
+Build a combined (device and simulator) target when installing.
+
+When this property is set to set to false (which is the default) then it will
+either be built with the device SDK or the simulator SDK depending on the SDK
+set. But if this property is set to true then the target will at install time
+also be built for the corresponding SDK and combined into one library.
+
+This feature requires at least Xcode version 6.
diff --git a/Help/release/dev/ios-universal.rst b/Help/release/dev/ios-universal.rst
new file mode 100644
index 0000000..f96abed
--- /dev/null
+++ b/Help/release/dev/ios-universal.rst
@@ -0,0 +1,7 @@
+ios-universal
+-------------
+
+* When building for embedded Apple platforms like iOS CMake learned to build and
+ install combined targets which contain both a device and a simulator build.
+ This behavior can be enabled by setting the :prop_tgt:`IOS_INSTALL_COMBINED`
+ target property.
diff --git a/Help/variable/CMAKE_IOS_INSTALL_COMBINED.rst b/Help/variable/CMAKE_IOS_INSTALL_COMBINED.rst
new file mode 100644
index 0000000..c5cb9b6
--- /dev/null
+++ b/Help/variable/CMAKE_IOS_INSTALL_COMBINED.rst
@@ -0,0 +1,8 @@
+CMAKE_IOS_INSTALL_COMBINED
+--------------------------
+
+Default value for :prop_tgt:`IOS_INSTALL_COMBINED` of targets.
+
+This variable is used to initialize the :prop_tgt:`IOS_INSTALL_COMBINED`
+property on all the targets. See that target property for additional
+information.
diff --git a/Modules/CMakeIOSInstallCombined.cmake b/Modules/CMakeIOSInstallCombined.cmake
new file mode 100644
index 0000000..f052a3b
--- /dev/null
+++ b/Modules/CMakeIOSInstallCombined.cmake
@@ -0,0 +1,297 @@
+
+#=============================================================================
+# Copyright 2014-2015 Ruslan Baratov, Gregor Jasny
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+# License text for the above reference.)
+
+# Function to print messages of this module
+function(_ios_install_combined_message)
+ message("[iOS combined] " ${ARGN})
+endfunction()
+
+# Get build settings for the current target/config/SDK by running
+# `xcodebuild -sdk ... -showBuildSettings` and parsing it's output
+function(_ios_install_combined_get_build_setting sdk variable resultvar)
+ if("${sdk}" STREQUAL "")
+ message(FATAL_ERROR "`sdk` is empty")
+ endif()
+
+ if("${variable}" STREQUAL "")
+ message(FATAL_ERROR "`variable` is empty")
+ endif()
+
+ if("${resultvar}" STREQUAL "")
+ message(FATAL_ERROR "`resultvar` is empty")
+ endif()
+
+ set(
+ cmd
+ xcodebuild -showBuildSettings
+ -sdk "${sdk}"
+ -target "${CURRENT_TARGET}"
+ -config "${CURRENT_CONFIG}"
+ )
+
+ execute_process(
+ COMMAND ${cmd}
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+ RESULT_VARIABLE result
+ OUTPUT_VARIABLE output
+ )
+
+ if(NOT result EQUAL 0)
+ message(FATAL_ERROR "Command failed (${result}): ${cmd}")
+ endif()
+
+ if(NOT output MATCHES " ${variable} = ([^\n]*)")
+ message(FATAL_ERROR "${variable} not found.")
+ endif()
+
+ set("${resultvar}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
+endfunction()
+
+# Get architectures of given SDK (iphonesimulator/iphoneos)
+function(_ios_install_combined_get_valid_archs sdk resultvar)
+ cmake_policy(SET CMP0007 NEW)
+
+ if("${resultvar}" STREQUAL "")
+ message(FATAL_ERROR "`resultvar` is empty")
+ endif()
+
+ _ios_install_combined_get_build_setting("${sdk}" "VALID_ARCHS" valid_archs)
+
+ separate_arguments(valid_archs)
+ list(REMOVE_ITEM valid_archs "") # remove empty elements
+ list(REMOVE_DUPLICATES valid_archs)
+
+ set("${resultvar}" "${valid_archs}" PARENT_SCOPE)
+endfunction()
+
+# Final target can contain more architectures that specified by SDK. This
+# function will run 'lipo -info' and parse output. Result will be returned
+# as a CMake list.
+function(_ios_install_combined_get_real_archs filename resultvar)
+ set(cmd "${_lipo_path}" -info "${filename}")
+ execute_process(
+ COMMAND ${cmd}
+ RESULT_VARIABLE result
+ OUTPUT_VARIABLE output
+ ERROR_VARIABLE output
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_STRIP_TRAILING_WHITESPACE
+ )
+ if(NOT result EQUAL 0)
+ message(
+ FATAL_ERROR "Command failed (${result}): ${cmd}\n\nOutput:\n${output}"
+ )
+ endif()
+
+ if(NOT output MATCHES "(Architectures in the fat file: [^\n]+ are|Non-fat file: [^\n]+ is architecture): ([^\n]*)")
+ message(FATAL_ERROR "Could not detect architecture from: ${output}")
+ endif()
+
+ separate_arguments(CMAKE_MATCH_2)
+ set(${resultvar} ${CMAKE_MATCH_2} PARENT_SCOPE)
+endfunction()
+
+# Run build command for the given SDK
+function(_ios_install_combined_build sdk)
+ if("${sdk}" STREQUAL "")
+ message(FATAL_ERROR "`sdk` is empty")
+ endif()
+
+ _ios_install_combined_message("Build `${CURRENT_TARGET}` for `${sdk}`")
+
+ execute_process(
+ COMMAND
+ "${CMAKE_COMMAND}"
+ --build
+ .
+ --target "${CURRENT_TARGET}"
+ --config ${CURRENT_CONFIG}
+ --
+ -sdk "${sdk}"
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+ RESULT_VARIABLE result
+ )
+
+ if(NOT result EQUAL 0)
+ message(FATAL_ERROR "Build failed")
+ endif()
+endfunction()
+
+# Remove given architecture from file. This step needed only in rare cases
+# when target was built in "unusual" way. Emit warning message.
+function(_ios_install_combined_remove_arch lib arch)
+ _ios_install_combined_message(
+ "Warning! Unexpected architecture `${arch}` detected and will be removed "
+ "from file `${lib}`")
+ set(cmd "${_lipo_path}" -remove ${arch} -output ${lib} ${lib})
+ execute_process(
+ COMMAND ${cmd}
+ RESULT_VARIABLE result
+ OUTPUT_VARIABLE output
+ ERROR_VARIABLE output
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_STRIP_TRAILING_WHITESPACE
+ )
+ if(NOT result EQUAL 0)
+ message(
+ FATAL_ERROR "Command failed (${result}): ${cmd}\n\nOutput:\n${output}"
+ )
+ endif()
+endfunction()
+
+# Check that 'lib' contains only 'archs' architectures (remove others).
+function(_ios_install_combined_keep_archs lib archs)
+ _ios_install_combined_get_real_archs("${lib}" real_archs)
+ set(archs_to_remove ${real_archs})
+ list(REMOVE_ITEM archs_to_remove ${archs})
+ foreach(x ${archs_to_remove})
+ _ios_install_combined_remove_arch("${lib}" "${x}")
+ endforeach()
+endfunction()
+
+function(_ios_install_combined_detect_sdks this_sdk_var corr_sdk_var)
+ cmake_policy(SET CMP0057 NEW)
+
+ set(this_sdk "$ENV{PLATFORM_NAME}")
+ if("${this_sdk}" STREQUAL "")
+ message(FATAL_ERROR "Environment variable PLATFORM_NAME is empty")
+ endif()
+
+ set(all_platforms "$ENV{SUPPORTED_PLATFORMS}")
+ if("${all_platforms}" STREQUAL "")
+ message(FATAL_ERROR "Environment variable SUPPORTED_PLATFORMS is empty")
+ endif()
+
+ separate_arguments(all_platforms)
+ if(NOT this_sdk IN_LIST all_platforms)
+ message(FATAL_ERROR "`${this_sdk}` not found in `${all_platforms}`")
+ endif()
+
+ list(REMOVE_ITEM all_platforms "" "${this_sdk}")
+ list(LENGTH all_platforms all_platforms_length)
+ if(NOT all_platforms_length EQUAL 1)
+ message(FATAL_ERROR "Expected one element: ${all_platforms}")
+ endif()
+
+ set(${this_sdk_var} "${this_sdk}" PARENT_SCOPE)
+ set(${corr_sdk_var} "${all_platforms}" PARENT_SCOPE)
+endfunction()
+
+# Create combined binary for the given target.
+#
+# Preconditions:
+# * Target already installed at ${destination}
+# for the ${PLATFORM_NAME} platform
+#
+# This function will:
+# * Run build for the lacking platform, i.e. opposite to the ${PLATFORM_NAME}
+# * Fuse both libraries by running lipo
+function(ios_install_combined target destination)
+ if("${target}" STREQUAL "")
+ message(FATAL_ERROR "`target` is empty")
+ endif()
+
+ if("${destination}" STREQUAL "")
+ message(FATAL_ERROR "`destination` is empty")
+ endif()
+
+ if(NOT IS_ABSOLUTE "${destination}")
+ message(FATAL_ERROR "`destination` is not absolute: ${destination}")
+ endif()
+
+ if(IS_DIRECTORY "${destination}" OR IS_SYMLINK "${destination}")
+ message(FATAL_ERROR "`destination` is no regular file: ${destination}")
+ endif()
+
+ if("${CMAKE_BINARY_DIR}" STREQUAL "")
+ message(FATAL_ERROR "`CMAKE_BINARY_DIR` is empty")
+ endif()
+
+ if(NOT IS_DIRECTORY "${CMAKE_BINARY_DIR}")
+ message(FATAL_ERROR "Is not a directory: ${CMAKE_BINARY_DIR}")
+ endif()
+
+ if("${CMAKE_INSTALL_CONFIG_NAME}" STREQUAL "")
+ message(FATAL_ERROR "CMAKE_INSTALL_CONFIG_NAME is empty")
+ endif()
+
+ set(cmd xcrun -f lipo)
+ execute_process(
+ COMMAND ${cmd}
+ RESULT_VARIABLE result
+ OUTPUT_VARIABLE output
+ ERROR_VARIABLE output
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_STRIP_TRAILING_WHITESPACE
+ )
+ if(NOT result EQUAL 0)
+ message(
+ FATAL_ERROR "Command failed (${result}): ${cmd}\n\nOutput:\n${output}"
+ )
+ endif()
+ set(_lipo_path ${output})
+
+ set(CURRENT_CONFIG "${CMAKE_INSTALL_CONFIG_NAME}")
+ set(CURRENT_TARGET "${target}")
+
+ _ios_install_combined_message("Target: ${CURRENT_TARGET}")
+ _ios_install_combined_message("Config: ${CURRENT_CONFIG}")
+ _ios_install_combined_message("Destination: ${destination}")
+
+ # Get SDKs
+ _ios_install_combined_detect_sdks(this_sdk corr_sdk)
+
+ # Get architectures of the target
+ _ios_install_combined_get_valid_archs("${corr_sdk}" corr_valid_archs)
+ _ios_install_combined_get_valid_archs("${this_sdk}" this_valid_archs)
+
+ # Return if there are no valid architectures for the SDK.
+ # (note that library already installed)
+ if("${corr_valid_archs}" STREQUAL "")
+ _ios_install_combined_message(
+ "No architectures detected for `${corr_sdk}` (skip)"
+ )
+ return()
+ endif()
+
+ # Trigger build of corresponding target
+ _ios_install_combined_build("${corr_sdk}")
+
+ # Get location of the library in build directory
+ _ios_install_combined_get_build_setting(
+ "${corr_sdk}" "CONFIGURATION_BUILD_DIR" corr_build_dir)
+ _ios_install_combined_get_build_setting(
+ "${corr_sdk}" "EXECUTABLE_PATH" corr_executable_path)
+ set(corr "${corr_build_dir}/${corr_executable_path}")
+
+ _ios_install_combined_keep_archs("${corr}" "${corr_valid_archs}")
+ _ios_install_combined_keep_archs("${destination}" "${this_valid_archs}")
+
+ _ios_install_combined_message("Current: ${destination}")
+ _ios_install_combined_message("Corresponding: ${corr}")
+
+ set(cmd "${_lipo_path}" -create ${corr} ${destination} -output ${destination})
+
+ execute_process(
+ COMMAND ${cmd}
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ RESULT_VARIABLE result
+ )
+
+ if(NOT result EQUAL 0)
+ message(FATAL_ERROR "Command failed: ${cmd}")
+ endif()
+
+ _ios_install_combined_message("Install done: ${destination}")
+endfunction()
diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx
index c6908c3..1158a27 100644
--- a/Source/cmInstallTargetGenerator.cxx
+++ b/Source/cmInstallTargetGenerator.cxx
@@ -531,6 +531,7 @@ void cmInstallTargetGenerator::PostReplacementTweaks(std::ostream& os,
{
this->AddInstallNamePatchRule(os, indent, config, file);
this->AddChrpathPatchRule(os, indent, config, file);
+ this->AddUniversalInstallRule(os, indent, file);
this->AddRanlibRule(os, indent, file);
this->AddStripRule(os, indent, file);
}
@@ -867,3 +868,46 @@ cmInstallTargetGenerator::AddRanlibRule(std::ostream& os,
os << indent << "execute_process(COMMAND \""
<< ranlib << "\" \"" << toDestDirPath << "\")\n";
}
+
+//----------------------------------------------------------------------------
+void
+cmInstallTargetGenerator
+::AddUniversalInstallRule(std::ostream& os,
+ Indent const& indent,
+ const std::string& toDestDirPath)
+{
+ cmMakefile const* mf = this->Target->Target->GetMakefile();
+
+ if(!mf->PlatformIsAppleIos() || !mf->IsOn("XCODE"))
+ {
+ return;
+ }
+
+ const char* xcodeVersion = mf->GetDefinition("XCODE_VERSION");
+ if(!xcodeVersion || cmSystemTools::VersionCompareGreater("6", xcodeVersion))
+ {
+ return;
+ }
+
+ switch(this->Target->GetType())
+ {
+ case cmState::EXECUTABLE:
+ case cmState::STATIC_LIBRARY:
+ case cmState::SHARED_LIBRARY:
+ case cmState::MODULE_LIBRARY:
+ break;
+
+ default:
+ return;
+ }
+
+ if(!this->Target->Target->GetPropertyAsBool("IOS_INSTALL_COMBINED"))
+ {
+ return;
+ }
+
+ os << indent << "include(CMakeIOSInstallCombined)\n";
+ os << indent << "ios_install_combined("
+ << "\"" << this->Target->Target->GetName() << "\" "
+ << "\"" << toDestDirPath << "\")\n";
+}
diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h
index ec89c05..18b3130 100644
--- a/Source/cmInstallTargetGenerator.h
+++ b/Source/cmInstallTargetGenerator.h
@@ -101,6 +101,8 @@ protected:
const std::string& toDestDirPath);
void AddRanlibRule(std::ostream& os, Indent const& indent,
const std::string& toDestDirPath);
+ void AddUniversalInstallRule(std::ostream& os, Indent const& indent,
+ const std::string& toDestDirPath);
std::string TargetName;
cmGeneratorTarget* Target;
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 9ea1a34..1986e5f 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -132,6 +132,7 @@ void cmTarget::SetMakefile(cmMakefile* mf)
this->SetPropertyDefault("Fortran_MODULE_DIRECTORY", 0);
this->SetPropertyDefault("GNUtoMS", 0);
this->SetPropertyDefault("OSX_ARCHITECTURES", 0);
+ this->SetPropertyDefault("IOS_INSTALL_COMBINED", 0);
this->SetPropertyDefault("AUTOMOC", 0);
this->SetPropertyDefault("AUTOUIC", 0);
this->SetPropertyDefault("AUTORCC", 0);
diff --git a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
index f89d620..acc0075 100644
--- a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
+++ b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
@@ -94,3 +94,39 @@ if(NOT XCODE_VERSION VERSION_LESS 7)
run_cmake(XcodeTbdStub)
unset(RunCMake_TEST_OPTIONS)
endif()
+
+if(NOT XCODE_VERSION VERSION_LESS 6)
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/XcodeIOSInstallCombined-build)
+ set(RunCMake_TEST_NO_CLEAN 1)
+ set(RunCMake_TEST_OPTIONS
+ "-DCMAKE_INSTALL_PREFIX:PATH=${RunCMake_TEST_BINARY_DIR}/_install"
+ "-DCMAKE_IOS_INSTALL_COMBINED=YES")
+
+ file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+ file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+ run_cmake(XcodeIOSInstallCombined)
+ run_cmake_command(XcodeIOSInstallCombined-build ${CMAKE_COMMAND} --build .)
+ run_cmake_command(XcodeIOSInstallCombined-install ${CMAKE_COMMAND} --build . --target install)
+
+ unset(RunCMake_TEST_BINARY_DIR)
+ unset(RunCMake_TEST_NO_CLEAN)
+ unset(RunCMake_TEST_OPTIONS)
+
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/XcodeIOSInstallCombinedPrune-build)
+ set(RunCMake_TEST_NO_CLEAN 1)
+ set(RunCMake_TEST_OPTIONS
+ "-DCMAKE_INSTALL_PREFIX:PATH=${RunCMake_TEST_BINARY_DIR}/_install"
+ "-DCMAKE_IOS_INSTALL_COMBINED=YES")
+
+ file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+ file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+ run_cmake(XcodeIOSInstallCombinedPrune)
+ run_cmake_command(XcodeIOSInstallCombinedPrune-build ${CMAKE_COMMAND} --build .)
+ run_cmake_command(XcodeIOSInstallCombinedPrune-install ${CMAKE_COMMAND} --build . --target install)
+
+ unset(RunCMake_TEST_BINARY_DIR)
+ unset(RunCMake_TEST_NO_CLEAN)
+ unset(RunCMake_TEST_OPTIONS)
+endif()
diff --git a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined-install-check.cmake b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined-install-check.cmake
new file mode 100644
index 0000000..a1c0671
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined-install-check.cmake
@@ -0,0 +1,30 @@
+function(verify_architectures file)
+ execute_process(
+ COMMAND xcrun otool -vf ${RunCMake_TEST_BINARY_DIR}/_install/${file}
+ OUTPUT_VARIABLE otool_out
+ ERROR_VARIABLE otool_err
+ RESULT_VARIABLE otool_result)
+ if(NOT otool_result EQUAL "0")
+ message(SEND_ERROR "Could not retrieve fat headers: ${otool_err}")
+ return()
+ endif()
+
+ string(REGEX MATCHALL "architecture [^ \n\t]+" architectures ${otool_out})
+ string(REPLACE "architecture " "" actual "${architectures}")
+ list(SORT actual)
+
+ set(expected arm64 armv7 i386 x86_64)
+
+ if(NOT actual STREQUAL expected)
+ message(SEND_ERROR
+ "The actual library contains the architectures:\n ${actual} \n"
+ "which do not match expected ones:\n ${expected} \n"
+ "otool output:\n${otool_out}")
+ endif()
+endfunction()
+
+verify_architectures(bin/foo_app.app/foo_app)
+verify_architectures(lib/libfoo_static.a)
+verify_architectures(lib/libfoo_shared.dylib)
+verify_architectures(lib/foo_bundle.bundle/foo_bundle)
+verify_architectures(lib/foo_framework.framework/foo_framework)
diff --git a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake
new file mode 100644
index 0000000..fc830b1
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake
@@ -0,0 +1,27 @@
+cmake_minimum_required(VERSION 3.3)
+
+project(IOSInstallCombined CXX)
+
+set(CMAKE_OSX_SYSROOT iphoneos)
+set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf")
+set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
+
+set(CMAKE_OSX_ARCHITECTURES "armv7;arm64;i386;x86_64")
+
+add_executable(foo_app MACOSX_BUNDLE main.cpp)
+install(TARGETS foo_app BUNDLE DESTINATION bin)
+
+add_library(foo_static STATIC foo.cpp)
+install(TARGETS foo_static ARCHIVE DESTINATION lib)
+
+add_library(foo_shared SHARED foo.cpp)
+install(TARGETS foo_shared LIBRARY DESTINATION lib)
+
+add_library(foo_bundle MODULE foo.cpp)
+set_target_properties(foo_bundle PROPERTIES BUNDLE TRUE)
+install(TARGETS foo_bundle LIBRARY DESTINATION lib)
+
+add_library(foo_framework SHARED foo.cpp)
+set_target_properties(foo_framework PROPERTIES FRAMEWORK TRUE)
+install(TARGETS foo_framework FRAMEWORK DESTINATION lib)
diff --git a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune-install-check.cmake b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune-install-check.cmake
new file mode 100644
index 0000000..83da17d
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune-install-check.cmake
@@ -0,0 +1,26 @@
+function(verify_architectures file)
+ execute_process(
+ COMMAND xcrun otool -vf ${RunCMake_TEST_BINARY_DIR}/_install/${file}
+ OUTPUT_VARIABLE otool_out
+ ERROR_VARIABLE otool_err
+ RESULT_VARIABLE otool_result)
+ if(NOT otool_result EQUAL "0")
+ message(SEND_ERROR "Could not retrieve fat headers: ${otool_err}")
+ return()
+ endif()
+
+ string(REGEX MATCHALL "architecture [^ \n\t]+" architectures ${otool_out})
+ string(REPLACE "architecture " "" actual "${architectures}")
+ list(SORT actual)
+
+ set(expected armv7 x86_64)
+
+ if(NOT actual STREQUAL expected)
+ message(SEND_ERROR
+ "The actual library contains the architectures:\n ${actual} \n"
+ "which do not match expected ones:\n ${expected} \n"
+ "otool output:\n${otool_out}")
+ endif()
+endfunction()
+
+verify_architectures(lib/libfoo.dylib)
diff --git a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune-install-stdout.txt b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune-install-stdout.txt
new file mode 100644
index 0000000..28edadc
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune-install-stdout.txt
@@ -0,0 +1,2 @@
+.*Unexpected architecture `i386` detected.*
+.*Unexpected architecture `arm64` detected.* \ No newline at end of file
diff --git a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake
new file mode 100644
index 0000000..b47d3a5
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 3.3)
+
+project(XcodeIOSInstallCombinedPrune CXX)
+
+set(CMAKE_OSX_SYSROOT iphoneos)
+set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf")
+
+add_library(foo SHARED foo.cpp)
+install(TARGETS foo DESTINATION lib)
+
+add_library(baz SHARED foo.cpp)
+set_target_properties(
+ foo baz
+ PROPERTIES
+ XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] armv7
+ XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphoneos*] armv7
+ XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] x86_64
+ XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphonesimulator*] x86_64
+)
+
+add_library(boo SHARED foo.cpp)
+set_target_properties(
+ boo
+ PROPERTIES
+ XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] arm64
+ XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphoneos*] arm64
+ XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] i386
+ XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphonesimulator*] i386
+)
+
+add_custom_command(
+ TARGET foo
+ POST_BUILD
+ COMMAND lipo -create $<TARGET_FILE:baz> $<TARGET_FILE:boo> -output $<TARGET_FILE:foo>
+)
diff --git a/Tests/RunCMake/XcodeProject/main.cpp b/Tests/RunCMake/XcodeProject/main.cpp
new file mode 100644
index 0000000..1695921
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/main.cpp
@@ -0,0 +1,3 @@
+int main(int argc, const char * argv[]) {
+ return 0;
+}