[cmake-developers] iOS: direction to official support and questions

Raffi Enficiaud raffi.enficiaud at mines-paris.org
Thu Sep 21 17:38:02 EDT 2017


Le 08.08.17 à 14:08, Raffi Enficiaud a écrit :
> Hi CMake ML,
>
> I am quite new to the topic of making toolchain files. However I need to
> build a not so trivial application for iOS and I want to do it with
> CMake, and if possible walk toward an official support of iOS in CMake.
>
> I have looked a bit to the Android toolchains, and I have to say I found
> those quite complicated as a first reading :)
>
> The target application I am building uses Qt and Boost.
>
> So far, I have managed to have an IOS toolchain file that is generating
> a looking good XCode project, that I can compile properly (up until
> signing).
>
>[snip]

Hi there,

Following the thread, I would like to suggest a toolchain file for iOS, 
attached to this post.

I used this to compile a not too small project using Qt, Boost, LSL and 
some other fancy dependencies, and the project runs at least on the 
simulator (waiting for a real device).

The way it works:

- we need different build trees for the simulator and for a real device 
(not same arch nor base SDK). Changing this is way too difficult and the 
workaround of having different build trees, although not very "elegant", 
just work

- for the simulator
cmake -G Xcode \
   -DCMAKE_TOOLCHAIN_FILE=ios_toolchain.cmake \
   -DIOS_PLATFORM=SIMULATOR \
   ../some-path

- for a real device
cmake -G Xcode \
   -DCMAKE_TOOLCHAIN_FILE=ios_toolchain.cmake \
   -DIOS_PLATFORM=OS \
   ../some-path

- for the device, we need a real certificate for signing that can be set 
directly from within XCode or by setting 
`XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=YES` and the companion variables 
to the appropriate values


Next steps:
* I can set up a machine for running the tests as I am doing for the 
Matlab package. Those tests would be for the moment compilation tests. I 
would be happy to discuss on how to do real "runtime" tests.
* I can proceed further with the documentation of course if people are 
happy with this toolchain file
* I can add other SDKs like watchOS and such, but before proceeding 
further I would prefer to have a feature complete implementation for iOS 
device+simulator (I believe this is the most demanded).
* I can check how to tweak CROSSCOMPILING_EMULATOR to run things on an 
emulator in case we are on the simulator. I believe add_test should be 
disabled when compiling for a real device
* I would like to know if there is any direction in indicating the 
bundles structures, as those are different for iOS and macOS. Right now 
I am using a switch like this

if(IOS)
   set(OSX_BUNDLE_RELATIVE_PATH ".")
else()
   set(OSX_BUNDLE_RELATIVE_PATH "../..")
endif()

add_custom_command(
TARGET myproj
POST_BUILD
COMMAND ${CMAKE_COMMAND} 
-DAPPFOLDER=$<TARGET_FILE_DIR:myproj>/${OSX_BUNDLE_RELATIVE_PATH} 
            "-DSOMEPARAMS=${BOOST_LIB_PATH}"
-P ${CMAKE_SOURCE_DIR}/cmake/osx_bundle.cmake
)

to get the path to the root folder of the bundle. I do not know if there 
is anything like this in CMake already (the BundleUtilities I believe 
has relative paths written in hard).

Thanks!
Raffi
-------------- next part --------------
# cmake -G Xcode \
#  -DCMAKE_TOOLCHAIN_FILE=./ios_toolchain.cmake \
#  -DCMAKE_PREFIX_PATH=~/Qt5.9.1/5.9.1/ios/ \
#  -DBOOST_ROOT=~/Code/SoftwareWorkshop/sw_thirdparties/osx/boost_1_63_0
#  -DBoost_COMPILER=-xgcc42 ../source

# * `IOS`: indicates that the build is being performed for iOS (generic device or simulator)
# * `IOS_PLATFORM`: indicates the platform to compile for. This can be the genuine operating system (`OS`) or the simulator (`SIMULATOR`)
# * `IOS_ARCH`: the architecture to compile. The default is dependant on the `IOS_PLATFORM`:
#    * `armv7;arm64` for the `IOS`
#    * `i386;x86_64` for the `SIMULATOR`
#
# Other variables
#
# * `CMAKE_IOS_SDK_ROOT`: indicates the name of an SDK to choose, defaults to the "most recent" SDK
#
# By default code signing is `OFF`, but should be activated per target in order to have the compilation
# possible for iOS device (Xcode defaults to some ad-hox signing otherwise). This can be done
# through the target properties `XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED`

# TODO
# - documentation
# - detects automatically sdks and target systems from XCode, and lists the sysroots
# - this is for IOS only, not watchOS or anything else
# - indicate the bundle structure for this target system (different than from macOS)
# - indicate how to run tests maybe through `CROSSCOMPILING_EMULATOR`
# - fix the tests
# - find other libraries that clang/clang++ ?


# some relevant doc/resources
# https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/cross_development/Using/using.html#//apple_ref/doc/uid/20002000-SW6

# for running simulator
# https://stackoverflow.com/questions/26031601/xcode-6-launch-simulator-from-command-line
# can be consumed with
# https://cmake.org/cmake/help/v3.8/prop_tgt/CROSSCOMPILING_EMULATOR.html

# Raffi: automated code signing
# public.kitware.com/pipermail/cmake/2016.../064602.html
# set_target_properties(app PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY  "PROPER IDENTIFIER")

# listing all signing identities
# security find-identity -v -p codesigning

# signing a binary
# codesign -s my-signing-identity -f ./md5

# signing code manually
# https://developer.apple.com/library/content/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html

# examining a .a
# lipo -info boost_1_63_0_ios/lib/libboost_filesystem.a


set(CMAKE_SYSTEM_NAME Darwin)
set(CMAKE_SYSTEM_VERSION 1)
set(UNIX True)
set(APPLE True)
set(IOS True)

#
if(NOT DEFINED IOS_PLATFORM)
  set(IOS_PLATFORM "OS")
endif()

# left as an example on how to list things from command line utilities
# to be integrated
if(FALSE)
  execute_process(
    COMMAND xcodebuild -sdk -version
    RESULT_VARIABLE
      XCODE_SYSROOT_LISTING_RES
    OUTPUT_VARIABLE
      XCODE_SYSROOT_LISTING
    ERROR_VARIABLE
      XCODE_SYSROOT_LISTING_ERROR
  )

  if(NOT "${XCODE_SYSROOT_LISTING_RES}" STREQUAL "0")
    message(FATAL_ERROR "Cannot list SDKs from xcodebuild. Please make sure XCode is properly installed")
  endif()

  string(REGEX MATCHALL "Path:[ ]*([a-zA-Z0-9/\\. ]+)"
         VAR_MATCH "${XCODE_SYSROOT_LISTING}"
        )
  list(REMOVE_DUPLICATES VAR_MATCH)
  message("${VAR_MATCH}")
endif()


# for each SDK, showing the version:
# xcrun --sdk iphoneos --show-sdk-version

#
# Detect the current XCode
#
if(NOT DEFINED XCODE_ROOT_PATH)
  execute_process(
    COMMAND xcode-select -print-path
    RESULT_VARIABLE
      XCODE_COMPILER_PRINT_PATH_RES
    OUTPUT_VARIABLE
      XCODE_COMPILER_PRINT_PATH
    ERROR_VARIABLE
      XCODE_COMPILER_PRINT_PATH_ERROR
  )
  # Raffi : check errors
  set(_suffix_app ".app")
  string(FIND "${XCODE_COMPILER_PRINT_PATH}" "${_suffix_app}/" VAR_LOCATION_APP)
  string(SUBSTRING "${XCODE_COMPILER_PRINT_PATH}" 0 "${VAR_LOCATION_APP}" XCODE_ROOT_PATH)
  set(XCODE_ROOT_PATH "${XCODE_ROOT_PATH}${_suffix_app}" CACHE STRING "XCode ROOT folder" FORCE)
endif()

#
# Check the platform selection and setup for developer root
if(IOS_PLATFORM STREQUAL "OS")
  set(IOS_PLATFORM_LOCATION "iPhoneOS.platform")
  set(CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphoneos") # This causes the installers to properly locate the output libraries
elseif(IOS_PLATFORM STREQUAL "SIMULATOR")
  set(IOS_PLATFORM_LOCATION "iPhoneSimulator.platform")
  set(CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator")
else()
  message(FATAL_ERROR "Unsupported IOS_PLATFORM value selected. Please choose OS or SIMULATOR")
endif()

# Indicate cross compilation: we suppose that we never perform compilation direclty
# on a device
set(CMAKE_CROSSCOMPILING TRUE)

#
# looking for the compilers
# Check how to do it consistently with XCODE_ROOT_PATH
if(NOT DEFINED XCODE_COMPILER_CLANG)
  execute_process(
    COMMAND xcodebuild -find-executable clang
    RESULT_VARIABLE
      XCODE_COMPILER_CLANG_RES
    OUTPUT_VARIABLE
      XCODE_COMPILER_CLANG
    ERROR_VARIABLE
      XCODE_COMPILER_CLANG_ERROR
  )
  if(NOT "${XCODE_COMPILER_CLANG_RES}" STREQUAL "0")
    message(FATAL_ERROR "IOSToolchain: 'clang' compiler cannot be found")
  endif()
  set(XCODE_COMPILER_CLANG "${XCODE_COMPILER_CLANG}" CACHE STRING "XCode clang compiler" FORCE)
endif()

if(NOT DEFINED XCODE_COMPILER_CLANGPP)
  execute_process(
    COMMAND xcodebuild -find-executable clang++
    RESULT_VARIABLE
      XCODE_COMPILER_CLANGPP_RES
    OUTPUT_VARIABLE
      XCODE_COMPILER_CLANGPP
    ERROR_VARIABLE
      XCODE_COMPILER_CLANGPP_ERROR
  )
  if(NOT "${XCODE_COMPILER_CLANGPP_RES}" STREQUAL "0")
    message(FATAL_ERROR "IOSToolchain: 'clang++' compiler cannot be found")
  endif()
  set(XCODE_COMPILER_CLANGPP "${XCODE_COMPILER_CLANGPP}" CACHE STRING "XCode clang++ compiler" FORCE)
endif()

set(CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for iOS" FORCE)
set(CMAKE_IOS_DEVELOPER_ROOT "${XCODE_ROOT_PATH}/Contents/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer")

# Find and use the most recent iOS sdk unless specified manually with CMAKE_IOS_SDK_ROOT
if (NOT DEFINED CMAKE_IOS_SDK_ROOT)
  file(GLOB _CMAKE_IOS_SDKS "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*")
  if(_CMAKE_IOS_SDKS)
    list(SORT _CMAKE_IOS_SDKS)
    list(REVERSE _CMAKE_IOS_SDKS)
    list(GET _CMAKE_IOS_SDKS 0 CMAKE_IOS_SDK_ROOT)
  else()
    message(FATAL_ERROR "No iOS SDK's found in default search path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.")
  endif()
  message(STATUS "Toolchain using default iOS SDK: ${CMAKE_IOS_SDK_ROOT}")
endif()

set (CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK")
# this variable is used all over the places in CMake, we override it with the
# iOS SDK root found
set (CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support")

#
# set the architecture for iOS
#
if(NOT DEFINED IOS_ARCH)
  if (IOS_PLATFORM STREQUAL "OS")
    set(CMAKE_SYSTEM_PROCESSOR arm)
    set(IOS_ARCH armv7 arm64)
  elseif(IOS_PLATFORM STREQUAL "SIMULATOR")
    set(CMAKE_SYSTEM_PROCESSOR x86_64)
    set(IOS_ARCH i386 x86_64)
  endif()
endif()
set(CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE string  "Build architecture for iOS")

# Set the find root to the iOS developer roots and to user defined paths
set(CMAKE_FIND_ROOT_PATH
  ${CMAKE_IOS_DEVELOPER_ROOT}
  ${CMAKE_IOS_SDK_ROOT}
  ${CMAKE_PREFIX_PATH}
  CACHE string  "iOS find search path root"
)

# set up the default search directories for frameworks
set (CMAKE_SYSTEM_FRAMEWORK_PATH
  ${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks
  ${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks
  ${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks
)

# visibility flags
set (CMAKE_CXX_FLAGS_INIT "-fvisibility=hidden -fvisibility-inlines-hidden")

# from https://public.kitware.com/Bug/view.php?id=15329
set(CMAKE_MACOSX_BUNDLE YES)
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")


if(CMAKE_OSX_SYSROOT)
  if(NOT IS_DIRECTORY "${CMAKE_OSX_SYSROOT}")
    message(FATAL_ERROR
      "iOS: The system root directory needed for the selected iOS version and architecture does not exist:\n"
      "  ${CMAKE_OSX_SYSROOT}\n"
      )
  endif()
else()
  message(FATAL_ERROR
    "iOS: No CMAKE_OSX_SYSROOT was selected."
    )
endif()

set(CMAKE_BUILD_TYPE_INIT Debug)


More information about the cmake-developers mailing list