On 07-Oct-15 16:57, Gregor Jasny wrote:
Hello,

thank you for working on this. This is really hairy stuff.

On 24/09/15 11:10, Ruslan Baratov via cmake-developers wrote:
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.
some general remarks:

* iOS is not the only platform where the simulator / device situation is
problematic. Others are now watchOS and the upcoming tvOS (see
xcodebuild -showsdks). So it would make sense to replace the ios naming
in the method signatures with something more general.
It cames from PlatformIsAppleIos.

* Does the patches handle Bundles and Frameworks properly?
No. At least for now it's designed to be used only for libraries (cmInstall_{STATIC,SHARED,MODULE}_LIBRARY). I don't think it make sense to extend this feature for Bundles (simulator architectures are useless in AppStore and will be rejected). If Bundle use Framework with simulator architectures it will be rejected too. Though it make sense to create "development" version of Framework with simulator archs but we need to resign it after fuse procedure (libs are not signed).

I'm planning to try to make such patches for Frameworks next.

* This functionality should be limited to XCODE Generator
Fixed.

0001-

* Could you make the CMAKE_IOS_INSTALL_UNIVERSAL_LIBS a two staged
property like CMAKE_INSTALL_RPATH / INSTALL_RPATH. That way one would be
able to enable this behavior for only some targets
Implemented. But I'm not sure it make sense. Can you describe scenario when it can be helpful?

0002-CMake-module-for-universal-iOS-library-install

* Could you avoid to hard-code architecture names like i386 or armv7 in
the module?

Ok. Added function to get "real" architectures by parsing `lipo -info`. If it's not equal to the one specified by SDK then needless archs removed.


* In Xcode 7 (and maybe earlier) I see the following environment
variables when I install for Device:

export CORRESPONDING_SIMULATOR_PLATFORM_NAME=iphonesimulator
export CORRESPONDING_SIMULATOR_SDK_NAME=iphonesimulator9.0
export SUPPORTED_PLATFORMS="iphonesimulator iphoneos"
export SDK_NAME=iphoneos9.0
for Simulator:

export CORRESPONDING_DEVICE_PLATFORM_NAME=iphoneos
export CORRESPONDING_DEVICE_SDK_NAME=iphoneos9.0
export SDK_NAME=iphonesimulator9.0
Could you use those variables to avoid hardcoding iphoneos/simulator in
the module?
I see CORRESPONDING_*_SDK_NAME variables in Xcode 7.0 but not in 6.4.

+  install_universal_ios_remove_arch("${dev_libpath}" "i386")
+  install_universal_ios_remove_arch("${dev_libpath}" "x86_64")
Doing it the other way round (keeping only what's needed) looks saner to me.

+  install_universal_ios_remove_arch("${dev_libpath}" ...)
+
+  install_universal_ios_remove_arch("${sim_libpath}" ...)
+
+  set(cmd lipo -create ${src} ${dst} -output ${dst})
Would it be possible to just add selected architectures from src to dst
(via lipo -replace)? That way you would save some disk i/o and would
make the pruning step for src superfluous.
This is kind of "unusual" situation. In most cases this command will just exit with "no such architecture" error (and ignored). I've added a warning message if unexpected arch detected.

Rebased version of patches attached.
Github fork: https://github.com/ruslo/CMake/commits/pr.ios.universal.2

Thanks, Ruslo
>From 1576f4d44902041407ab405544e6925bc52f8285 Mon Sep 17 00:00:00 2001
From: Ruslan Baratov <ruslan_bara...@yahoo.com>
Date: Thu, 8 Oct 2015 03:09:34 +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
 * Generator is Xcode
 * Target is library
 * CMake variable CMAKE_IOS_INSTALL_UNIVERSAL_LIBS or target property
   IOS_INSTALL_UNIVERSAL_LIBS is ON

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

diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx
index 30cf175..34728fa 100644
--- a/Source/cmInstallTargetGenerator.cxx
+++ b/Source/cmInstallTargetGenerator.cxx
@@ -869,3 +869,45 @@ cmInstallTargetGenerator::AddRanlibRule(std::ostream& os,
   os << indent << "execute_process(COMMAND \""
      << ranlib << "\" \"" << toDestDirPath << "\")\n";
 }
+
+std::string
+cmInstallTargetGenerator
+::GetTargetNameForUniversalIosInstall(cmInstallType type) const
+{
+  cmMakefile const* mf = this->Target->Target->GetMakefile();
+  if(!mf->PlatformIsAppleIos())
+    {
+    return "";
+    }
+
+  switch(type)
+    {
+    case cmInstallType_STATIC_LIBRARY:
+    case cmInstallType_SHARED_LIBRARY:
+    case cmInstallType_MODULE_LIBRARY:
+      break;
+    default:
+      return "";
+    }
+
+  const char* xcode = mf->GetDefinition("XCODE");
+  if(cmSystemTools::IsOff(xcode))
+    {
+    // Xcode only
+    return "";
+    }
+
+  if(this->Target->Target->GetPropertyAsBool("IOS_INSTALL_UNIVERSAL_LIBS"))
+    {
+    return this->Target->GetName();
+    }
+
+  const char* var = "CMAKE_IOS_INSTALL_UNIVERSAL_LIBS";
+  const char* flag = mf->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 9b882c510742cbb8c72156897882a7f7c9bba755 Mon Sep 17 00:00:00 2001
From: Ruslan Baratov <ruslan_bara...@yahoo.com>
Date: Thu, 8 Oct 2015 03:11:59 +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 | 347 ++++++++++++++++++++++++++++
 1 file changed, 347 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..a1b79b0
--- /dev/null
+++ b/Modules/install_universal_ios_library.cmake
@@ -0,0 +1,347 @@
+#=============================================================================
+# 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()
+
+# 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(install_universal_ios_get_real_archs filename resultvar)
+  set(cmd lipo -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()
+
+  # 'lipo -info' succeeded, check file has only one architecture
+  string(
+      REGEX
+      REPLACE ".*Non-fat file: .* is architecture: " ""
+      single_arch
+      "${output}"
+  )
+  if(NOT "${single_arch}" STREQUAL "${output}")
+    # REGEX matches
+    string(REPLACE " " ";" single_arch "${single_arch}")
+    list(LENGTH single_arch len)
+    if(NOT len EQUAL 1)
+      message(FATAL_ERROR "Expected one architecture for output: ${output}")
+    endif()
+    set(${resultvar} "${single_arch}" PARENT_SCOPE)
+    return()
+  endif()
+
+  # 'lipo -info' succeeded, check file has multiple architectures
+  string(
+      REGEX
+      REPLACE "^Architectures in the fat file: .* are: " ""
+      architectures
+      "${output}"
+  )
+  if("${architectures}" STREQUAL "${output}")
+    # REGEX doesn't match
+    message(FATAL_ERROR "Unexpected output: ${output}")
+  endif()
+  string(REPLACE " " ";" architectures "${architectures}")
+  list(LENGTH architectures len)
+  if(len EQUAL 0 OR len EQUAL 1)
+    message(FATAL_ERROR "Expected >1 architecture for output: ${output}")
+  endif()
+  set(${resultvar} "${architectures}" 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()
+
+# Remove given architecture from file. This step needed only in rare cases
+# when target was built in "unusual" way. Emit warning message.
+function(install_universal_ios_remove_arch lib arch)
+  set(msg_p1 "Warning! Unexpected architecture `${arch}` detected")
+  set(msg_p2 "and will be removed from file `${lib}`")
+  install_universal_ios_message("${msg_p1} ${msg_p2}")
+  set(cmd lipo -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(install_universal_ios_keep_archs lib archs)
+  install_universal_ios_get_real_archs("${lib}" real_archs)
+  set(archs_to_remove ${real_archs})
+  list(REMOVE_ITEM archs_to_remove ${archs})
+  foreach(x ${archs_to_remove})
+    install_universal_ios_remove_arch("${lib}" "${x}")
+  endforeach()
+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("${dev_sdk}" dev_archs)
+  install_universal_ios_get_archs("${sim_sdk}" sim_archs)
+
+  if(build_sim)
+    set(build_archs "${sim_archs}")
+  else()
+    set(build_archs "${dev_archs}")
+  endif()
+
+  # 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_keep_archs("${dev_libpath}" "${dev_archs}")
+  install_universal_ios_keep_archs("${sim_libpath}" "${sim_archs}")
+
+  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 ca4d0b04d8f1a5a2bc21acf2c8c82243fe3914d6 Mon Sep 17 00:00:00 2001
From: Ruslan Baratov <ruslan_bara...@yahoo.com>
Date: Thu, 8 Oct 2015 03:13:57 +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 34728fa..1e2ea3d 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