Hi,

Patches help to install universal iOS (device + simulator) libraries by triggering some extra instructions (build + fuse) after "regular" library installation finished. This behavior controlled by CMake variable CMAKE_IOS_INSTALL_UNIVERSAL_LIBS.

= Example =

> cat CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(Foo)

add_library(foo foo.cpp)

install(TARGETS foo DESTINATION lib)

> cat toolchain.cmake
set(CMAKE_MACOSX_BUNDLE YES)
set(CMAKE_OSX_SYSROOT "iphoneos")
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.example")

> rm -rf _builds _install
> cmake -H. -B_builds -GXcode -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DCMAKE_INSTALL_PREFIX=_install ...

= Current functionality =

Device one arch (default):

> cmake ...
> cmake --build _builds --target install
> lipo -info _install/lib/libfoo.a
Non-fat file: _install/lib/libfoo.a is architecture: armv7

Simulator one arch:

> cmake ...
> cmake --build _builds --target install -- -sdk iphonesimulator
> lipo -info _install/lib/libfoo.a
Non-fat file: _install/lib/libfoo.a is architecture: x86_64

Device multi arch:

> cmake ... -DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO
> cmake --build _builds --target install
> lipo -info _install/lib/libfoo.a
Architectures in the fat file: _install/lib/libfoo.a are: armv7 arm64

Simulator multi arch:

> cmake ... -DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO
> cmake --build _builds --target install -- -sdk iphonesimulator
> lipo -info _install/lib/libfoo.a
Architectures in the fat file: _install/lib/libfoo.a are: i386 x86_64

= This upgrade =

Device + simulator one arch:

> cmake ... -DCMAKE_IOS_INSTALL_UNIVERSAL_LIBS=YES
> cmake --build _builds --target install
> lipo -info _install/lib/libfoo.a
Architectures in the fat file: _install/lib/libfoo.a are: armv7 x86_64

Device + simulator multi arch:

> cmake ... -DCMAKE_IOS_INSTALL_UNIVERSAL_LIBS=YES -DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO
> cmake --build _builds --target install
> lipo -info _install/lib/libfoo.a
Architectures in the fat file: _install/lib/libfoo.a are: i386 armv7 x86_64 arm64

Let me know if this looks acceptable, then I will add documentation part and tests.

Thanks, Ruslo
>From 81665dad37f6b102449cd5f36b2ea0a666cf9d68 Mon Sep 17 00:00:00 2001
From: Ruslan Baratov <ruslan_bara...@yahoo.com>
Date: Thu, 24 Sep 2015 11:45:44 +0300
Subject: [PATCH 1/3] Get target name for universal iOS library install

Add method GetTargetNameForUniversalIosInstall to cmInstallTargetGenerator.

This method will return target name if:
 * Platform is iOS
 * Target is library
 * CMake variable CMAKE_IOS_INSTALL_UNIVERSAL_LIBS is ON

Otherwise an empty string will be returned.
---
 Source/cmInstallTargetGenerator.cxx | 29 +++++++++++++++++++++++++++++
 Source/cmInstallTargetGenerator.h   |  5 +++++
 2 files changed, 34 insertions(+)

diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx
index 30cf175..db0ed6e 100644
--- a/Source/cmInstallTargetGenerator.cxx
+++ b/Source/cmInstallTargetGenerator.cxx
@@ -869,3 +869,32 @@ cmInstallTargetGenerator::AddRanlibRule(std::ostream& os,
   os << indent << "execute_process(COMMAND \""
      << ranlib << "\" \"" << toDestDirPath << "\")\n";
 }
+
+std::string
+cmInstallTargetGenerator
+::GetTargetNameForUniversalIosInstall(cmInstallType type) const
+{
+  if(!this->Target->Target->GetMakefile()->PlatformIsAppleIos())
+    {
+    return "";
+    }
+
+  switch(type)
+    {
+    case cmInstallType_STATIC_LIBRARY:
+    case cmInstallType_SHARED_LIBRARY:
+    case cmInstallType_MODULE_LIBRARY:
+      break;
+    default:
+      return "";
+    }
+
+  const char* var = "CMAKE_IOS_INSTALL_UNIVERSAL_LIBS";
+  const char* flag = this->Target->Target->GetMakefile()->GetDefinition(var);
+  if (cmSystemTools::IsOff(flag))
+    {
+    return "";
+    }
+
+  return this->Target->GetName();
+}
diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h
index a8f4a75..6335e31 100644
--- a/Source/cmInstallTargetGenerator.h
+++ b/Source/cmInstallTargetGenerator.h
@@ -109,6 +109,11 @@ protected:
   NamelinkModeType NamelinkMode;
   bool ImportLibrary;
   bool Optional;
+
+ private:
+  /** Get target name for installing universal iOS library. If target is not
+      an iOS library or universal build is disabled return empty string. */
+  std::string GetTargetNameForUniversalIosInstall(cmInstallType type) const;
 };
 
 #endif
-- 
1.9.3 (Apple Git-50)

>From 89e8b5aebb1fd8709f3a2bcff5ac77dc3e545a07 Mon Sep 17 00:00:00 2001
From: Ruslan Baratov <ruslan_bara...@yahoo.com>
Date: Thu, 24 Sep 2015 12:04:27 +0300
Subject: [PATCH 2/3] CMake module for universal iOS library install

Add CMake module with function `install_universal_ios_library`.
Target name and destination passed to the function. This function will be run
after library installed: will trigger additional build instructions for missing
platform and fuse libraries into universal in destination directory.

Module designed to be used in `cmake_install.cmake` script.
---
 Modules/install_universal_ios_library.cmake | 268 ++++++++++++++++++++++++++++
 1 file changed, 268 insertions(+)
 create mode 100644 Modules/install_universal_ios_library.cmake

diff --git a/Modules/install_universal_ios_library.cmake b/Modules/install_universal_ios_library.cmake
new file mode 100644
index 0000000..6c05ad1
--- /dev/null
+++ b/Modules/install_universal_ios_library.cmake
@@ -0,0 +1,268 @@
+#=============================================================================
+# Copyright 2014-2015 Ruslan Baratov
+#
+# 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(install_universal_ios_message str)
+  message("[iOS universal] ${str}")
+endfunction()
+
+# Get build settings for the current target/config/SDK by running
+# `xcodebuild -sdk ... -showBuildSettings` and parsing it's output
+function(install_universal_ios_get 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()
+
+  string(REPLACE "\n" ";" output "${output}")
+
+  set(var_pattern "    ${variable} = ")
+  string(LENGTH "${var_pattern}" var_pattern_len)
+
+  set(result "")
+  foreach(x ${output})
+    string(FIND "${x}" "${var_pattern}" index)
+    if(index EQUAL 0)
+      if(NOT "${result}" STREQUAL "")
+        message(FATAL_ERROR "${variable} already found: ${result}")
+      endif()
+      string(SUBSTRING "${x}" "${var_pattern_len}" -1 result)
+      if("${result}" STREQUAL "")
+        message(FATAL_ERROR "Empty value of variable: ${variable}")
+      endif()
+    endif()
+  endforeach()
+
+  set("${resultvar}" "${result}" PARENT_SCOPE)
+endfunction()
+
+# Get architectures of given SDK (iphonesimulator/iphoneos)
+function(install_universal_ios_get_archs sdk resultvar)
+  cmake_policy(SET CMP0007 NEW)
+
+  if("${resultvar}" STREQUAL "")
+    message(FATAL_ERROR "`resultvar` is empty")
+  endif()
+
+  install_universal_ios_get("${sdk}" "VALID_ARCHS" valid_archs)
+
+  string(REPLACE " " ";" valid_archs "${valid_archs}")
+
+  list(REMOVE_ITEM valid_archs "") # remove empty elements
+  list(REMOVE_DUPLICATES valid_archs)
+
+  set("${resultvar}" "${valid_archs}" PARENT_SCOPE)
+endfunction()
+
+# Run build command for the given SDK
+function(install_universal_ios_build sdk)
+  if("${sdk}" STREQUAL "")
+    message(FATAL_ERROR "`sdk` is empty")
+  endif()
+
+  install_universal_ios_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()
+
+# If linked with other fat libraries this library can contain extra
+# architectures. Remove them to avoid 'lipo -create' error.
+# Ignore result and output since architectures may not exist.
+function(install_universal_ios_remove_arch lib arch)
+  execute_process(
+      COMMAND lipo -remove ${arch} -output ${lib} ${lib}
+      WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+      OUTPUT_QUIET
+      ERROR_QUIET
+  )
+endfunction()
+
+# Create universal library for the given target.
+#
+# Preconditions:
+#  * Library already installed to ${destination} directory
+#    for the ${EFFECTIVE_PLATFORM_NAME} platform
+#
+# This function will:
+#  * Run build for the lacking platform,
+#    i.e. opposite to the ${EFFECTIVE_PLATFORM_NAME}
+#  * Fuse both libraries by running `lipo -create ${src} ${dst} -output ${dst}`
+#     src: library that was just built
+#     dst: installed library
+function(install_universal_ios_library 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(NOT IS_DIRECTORY "${destination}")
+    message(FATAL_ERROR "`destination` is not absolute: ${destination}")
+  endif()
+
+  if(NOT EXISTS "${destination}")
+    message(FATAL_ERROR "`destination` not exists: ${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(NOT EXISTS "${CMAKE_BINARY_DIR}")
+    message(FATAL_ERROR "Not exists: ${CMAKE_BINARY_DIR}")
+  endif()
+
+  if("${CMAKE_INSTALL_CONFIG_NAME}" STREQUAL "")
+    message(FATAL_ERROR "CMAKE_INSTALL_CONFIG_NAME is empty")
+  endif()
+
+  if("${EFFECTIVE_PLATFORM_NAME}" STREQUAL "")
+    message(FATAL_ERROR "EFFECTIVE_PLATFORM_NAME is empty")
+  elseif("${EFFECTIVE_PLATFORM_NAME}" STREQUAL "-iphoneos")
+    # iphoneos already installed - add simulator part
+    set(build_sdk "iphonesimulator")
+    set(build_sim YES)
+  elseif("${EFFECTIVE_PLATFORM_NAME}" STREQUAL "-iphonesimulator")
+    # iphonesimulator already installed - add device part
+    set(build_sdk "iphoneos")
+    set(build_sim NO)
+  else()
+    message(
+        FATAL_ERROR
+        "Unexpected EFFECTIVE_PLATFORM_NAME: ${EFFECTIVE_PLATFORM_NAME}"
+    )
+  endif()
+
+  set(CURRENT_CONFIG "${CMAKE_INSTALL_CONFIG_NAME}")
+  set(CURRENT_TARGET "${target}")
+
+  install_universal_ios_message("Target: ${CURRENT_TARGET}")
+  install_universal_ios_message("Config: ${CURRENT_CONFIG}")
+  install_universal_ios_message("Destination: ${destination}")
+
+  # SDK name for device/simulator
+  set(dev_sdk "iphoneos")
+  set(sim_sdk "iphonesimulator")
+
+  # Get architectures of the target
+  install_universal_ios_get_archs("${build_sdk}" build_archs)
+
+  # Return if there are no valid architectures for the SDK.
+  # (note that library already installed)
+  if("${build_archs}" STREQUAL "")
+    install_universal_ios_message(
+        "No architectures detected for build_sdk: ${build_sdk} (skip)"
+    )
+    return()
+  endif()
+
+  # Get location of the library in build directory
+  install_universal_ios_get("${build_sdk}" "CODESIGNING_FOLDER_PATH" src)
+
+  # Library output name
+  install_universal_ios_get("${dev_sdk}" "EXECUTABLE_NAME" dev_libname)
+  install_universal_ios_get("${sim_sdk}" "EXECUTABLE_NAME" sim_libname)
+
+  if("${dev_libname}" STREQUAL "${sim_libname}")
+    set(libname "${dev_libname}")
+  else()
+    message(FATAL_ERROR "Library names differs: ${dev_libname} ${sim_libname}")
+  endif()
+
+  set(dst "${destination}/${libname}")
+
+  install_universal_ios_build("${build_sdk}")
+
+  if(build_sim)
+    set(sim_libpath "${src}")
+    set(dev_libpath "${dst}")
+  else()
+    set(sim_libpath "${dst}")
+    set(dev_libpath "${src}")
+  endif()
+
+  install_universal_ios_remove_arch("${dev_libpath}" "i386")
+  install_universal_ios_remove_arch("${dev_libpath}" "x86_64")
+
+  install_universal_ios_remove_arch("${sim_libpath}" "armv7")
+  install_universal_ios_remove_arch("${sim_libpath}" "armv7s")
+  install_universal_ios_remove_arch("${sim_libpath}" "arm64")
+
+  install_universal_ios_message("Device: ${dev_libpath}")
+  install_universal_ios_message("Simulator: ${sim_libpath}")
+
+  set(cmd lipo -create ${src} ${dst} -output ${dst})
+
+  execute_process(
+      COMMAND ${cmd}
+      WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+      RESULT_VARIABLE result
+  )
+
+  if(NOT result EQUAL 0)
+    message(FATAL_ERROR "Command failed: ${cmd}")
+  endif()
+
+  install_universal_ios_message("Install done: ${dst}")
+endfunction()
-- 
1.9.3 (Apple Git-50)

>From 83c15d383700c9c14d4252cf878d7b4ff02352bf Mon Sep 17 00:00:00 2001
From: Ruslan Baratov <ruslan_bara...@yahoo.com>
Date: Thu, 24 Sep 2015 12:25:00 +0300
Subject: [PATCH 3/3] Universal iOS library install

Run `install_universal_ios_library` function in `cmake_install.cmake` script if
`GetTargetNameForUniversalIosInstall` returns non-empty target name.
---
 Source/cmInstallGenerator.cxx       | 10 +++++++++-
 Source/cmInstallGenerator.h         |  3 ++-
 Source/cmInstallTargetGenerator.cxx |  2 +-
 3 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/Source/cmInstallGenerator.cxx b/Source/cmInstallGenerator.cxx
index 2e1c5f0..5c2cd21 100644
--- a/Source/cmInstallGenerator.cxx
+++ b/Source/cmInstallGenerator.cxx
@@ -45,7 +45,8 @@ void cmInstallGenerator
                  const char* permissions_dir /* = 0 */,
                  const char* rename /* = 0 */,
                  const char* literal_args /* = 0 */,
-                 Indent const& indent
+                 Indent const& indent,
+                 std::string target_name_for_universal_ios_install
                  )
 {
   // Use the FILE command to install the file.
@@ -142,6 +143,13 @@ void cmInstallGenerator
     os << literal_args;
     }
   os << ")\n";
+  if(!target_name_for_universal_ios_install.empty())
+    {
+    os << indent << "include(install_universal_ios_library)\n";
+    os << indent << "install_universal_ios_library(";
+    os << "\"" << target_name_for_universal_ios_install << "\" ";
+    os << "\"" << absDest << "\")\n";
+    }
 }
 
 //----------------------------------------------------------------------------
diff --git a/Source/cmInstallGenerator.h b/Source/cmInstallGenerator.h
index b8e5b53..ee10b0c 100644
--- a/Source/cmInstallGenerator.h
+++ b/Source/cmInstallGenerator.h
@@ -49,7 +49,8 @@ public:
     const char* permissions_dir = 0,
     const char* rename = 0,
     const char* literal_args = 0,
-    Indent const& indent = Indent()
+    Indent const& indent = Indent(),
+    std::string target_name_for_universal_ios_install = ""
     );
 
   /** Get the install destination as it should appear in the
diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx
index db0ed6e..709ffbd 100644
--- a/Source/cmInstallTargetGenerator.cxx
+++ b/Source/cmInstallTargetGenerator.cxx
@@ -338,7 +338,7 @@ void cmInstallTargetGenerator::GenerateScriptForConfig(std::ostream& os,
                        type, filesFrom, optional,
                        this->FilePermissions.c_str(), no_dir_permissions,
                        no_rename, literal_args.c_str(),
-                       indent);
+                       indent, this->GetTargetNameForUniversalIosInstall(type));
 
   // Add post-installation tweaks.
   this->AddTweak(os, indent, config, filesTo,
-- 
1.9.3 (Apple Git-50)

-- 

Powered by www.kitware.com

Please keep messages on-topic and check the CMake FAQ at: 
http://www.cmake.org/Wiki/CMake_FAQ

Kitware offers various services to support the CMake community. For more 
information on each offering, please visit:

CMake Support: http://cmake.org/cmake/help/support.html
CMake Consulting: http://cmake.org/cmake/help/consulting.html
CMake Training Courses: http://cmake.org/cmake/help/training.html

Visit other Kitware open-source projects at 
http://www.kitware.com/opensource/opensource.html

Follow this link to subscribe/unsubscribe:
http://public.kitware.com/mailman/listinfo/cmake-developers

Reply via email to