leetal / ios-cmake

A CMake toolchain file for iOS, macOS, watchOS & tvOS C/C++/Obj-C++ development
BSD 3-Clause "New" or "Revised" License
1.83k stars 436 forks source link

Issues with COMBINED platforms in new CMake versions #200

Open galijot opened 2 months ago

galijot commented 2 months ago

I'm using this toolchain along with CMake v3.29.0 to generate iphoneos and iphonesimulator libraries. My goal is creating a couple of XCFrameworks, each consisting of two libraries; one for iphoneos (arm64) and one for iphonesimulator (arm64, x86_64). I've faced some troubles along the way, and would like to share what I found and how I got around them, since my workaround required a change in the iOS toolchain file, which I believe would be useful to others too. Note: I don't have much experience with CMake.

Since Apple now prefers using XCFrameworks, I'm not sure if there's still a use for building fat libraries consisting of device+simulator libraries, as XCFramework does not accept a library with multiple platforms. We could give it libraries with multiple architectures, but not multiple platforms (and iphoneos and iphonesimulator are considered platforms in this case) - which we currently get when running cmake install for a project generated for any COMBINED option.

From CMake version 3.28.0, CMAKE_IOS_INSTALL_COMBINED and IOS_INSTALL_COMBINED are deprecated. It seems to me that, when these variables are used to perform combined install, CMake's CMakeIOSInstallCombined is used to build combined iOS libraries. When this install script (for a lack of better word) is used when installing for iphonesimulator (arm64 and x86_64) and iphoneos (arm64), the script calls _ios_install_combined_remove_arch function which removes duplicated archs (if I'm not mistaken). The result is unusable, as it strips away the archs.

I do think it's useful to generate Xcode project and all the setup using COMBINED options, as it sets up the project for multiple architectures, but I'm not sure the install works in a desired way.

One of the issues I was trying to solve is specify the SDK that the build and install commands would use, in hope to force CMake's CMakeIOSInstallCombined it to build a single SDK. Specifically when using one of COMBINED options (I always used OS64COMBINED, but I assume it applies to others too), upon running:

cmake --build . --config Release
cmake --install . --config Release

I would see outputs like:

Command line invocation:
    .../xcodebuild -project some.xcodeproj ... -sdk iphonesimulator

First I'd get one of these for the install target, after which it calls more command line invocations for each library target.

I tried passing -sdk to build command, like so:

cmake --build . --config Release --  -sdk iphonesimulator

The result is that just the install target would get this sdk, and the following command line invocations for each library would still be using -sdk iphonesimulator. I wasn't able to figure how to solve this using COMBINED option; in order to use iphoneos sdk, I had to use OS64, which only builds arm64 and not x86_64.

It seemed to me I had to create two separate build system files (cmake .. -G Xcode) in two separate folders, and build iphoneos and iphonesimulator separately, and later combine them in an XCFramework. However, this would make the process much slower as generating build system files is a very slow process. If this is what's needed using newer CMake/Xcode versions, I think COMBINED options are obsolete.

However, I was able to bypass this; I'd generate build system files for OS64COMBINED, but I removed the following line inside of the iOS toolchain file:

set(CMAKE_IOS_INSTALL_COMBINED YES)

This allowed me to generate Xcode project that is set up for multiple archs, but when I run cmake --install, it's not performing any combined builds, so it uses a single sdk throughout the process. If I wanted to build for iphonesimulator, I'd just need to pass -sdk iphonesimulator to cmake build command, and just swap it with iphoneos to build for device. Also note that this variable is deprecated, as noted above.

This approach, like the approach of creating two separate build system files in two separate folders, still requires working with multiple separate libraries when creating an XCFramework, but it's at least derived from the same Xcode project. And it's possible due to the fact that COMBINED options configure Xcode project for both arm64 on iphoneos and arm64+x86_64 for simulator.

Would removing the aforementioned line be something worth considering? Is there another solution? In any case, I think something would have to be done regarding CMAKE_IOS_INSTALL_COMBINED since its deprecated; maybe deprecated all COMBINED options?

galijot commented 2 weeks ago

@leetal is there a chance you can take a look at this? Or someone else, maybe @kambala-decapitator?

kambala-decapitator commented 2 weeks ago

sorry, can't tell anything as I never use COMBINED

galijot commented 2 weeks ago

If you don't mind sharing @kambala-decapitator, how do you handle the process of creating XCFrameworks that consist of device + simulator libraries? Do you create two separate build system files (i.e. run cmake .. -G Xcode two times), one for device and one for simulator, and then combine those libraries into XCFramework?

kambala-decapitator commented 2 weeks ago

I don't have such task :) But yes, I'd create 2 separate build dirs for device and simulator. If you build XCFramework on CI, you could parallelize those steps (e.g. run on 2 different agents) and pass build artifacts to next step that would create XCFramework.

galijot commented 2 weeks ago

Thanks a lot!

I'll keep this open since there's still a chance COMBINED options might get removed/adjusted due to newer CMake versions not supporting it anymore.