How to generate FAT STATIC lib with CMake Preset

Hi,

I would like to be able to compile some libraries in “fat” mode, i.e. to have a library that combines the arm64 and x86_64 version in one file.

I can do it very well for macOS by only setting CMAKE_OSX_ARCHITECTURES variable:

{
    "name": "release-macOS",
    "inherits": ["release"],
    "cacheVariables": {
        "CMAKE_OSX_ARCHITECTURES": "arm64;x86_64",
        "CMAKE_INSTALL_PREFIX": "${sourceParentDir}/_install/macOS/release"
    },
    "condition": {
        "type": "equals",
        "lhs": "${hostSystemName}",
        "rhs": "Darwin"
    }
},
{
    "name": "debug-macOS",
    "inherits": ["debug", "release-macOS"],
    "cacheVariables": {
        "CMAKE_INSTALL_PREFIX": "${sourceParentDir}/_install/macOS/debug"
    }
}

Result:

❯ lipo -info libCatch2d.a
Architectures in the fat file: libCatch2d.a are: x86_64 arm64

But for iOS doing this is apparently not enough because the result is always a library only for arm64:

❯ lipo -info libCatch2d.a
Non-fat file: libCatch2d.a is architecture: arm64

So I would like to know how to configure through the presets system and CMake variables to get what I want for iOS. Knowing that the difference between macOS and iOS for me is the use of Ninja for macOS and XCode for iOS via the “generator” variable in the presets.

OSX implies that it works for macOS. iOS is not macOS. I suspect that support isn’t too hard to add, but just hasn’t been done yet.

However, if we look at the CMake documentation in the cmake-toolchains section, we can see a paragraph where it is explained that we can generate a library with several architecture: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-ios-tvos-or-watchos

Now I just tried adding the variable CMAKE_IOS_INSTALL_COMBINED and I get something interesting.
After compiling and installing the arm64 version, I get this type of line:

-- [iOS combined] Target: Catch2
-- [iOS combined] Config: Debug
-- [iOS combined] Destination: /Users/naubry/xxx/NextVersion/publibs/_install/iOS/debug/lib/libCatch2d.a
-- [iOS combined] Architectures (iphoneos): arm64 arm64e armv7 armv7s
-- [iOS combined] Architectures (iphonesimulator): arm64 arm64e i386 x86_64
-- [iOS combined] Architectures (iphonesimulator) after pruning: i386 x86_64
-- [iOS combined] Build `Catch2` for `iphonesimulator`
Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -project Catch2.xcodeproj build -target Catch2 -parallelizeTargets -configuration Debug -hideShellScriptEnvironment -sdk iphonesimulator

It looks like he’s actually trying to make a library with multiple architectures. However, I get an error pretty quickly like:

Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -project Catch2.xcodeproj build -target Catch2 -parallelizeTargets -configuration Debug -hideShellScriptEnvironment -sdk iphonesimulator

User defaults from command line:
    HideShellScriptEnvironment = YES
    IDEPackageSupportUseBuiltinSCM = YES

Build settings from command line:
    SDKROOT = iphonesimulator16.0
    TOOLCHAINS = com.apple.dt.toolchain.XcodeDefault

Computing target dependency graph and provisioning inputs

Create build description
Build description signature: ac7b261392e652e6dcf24b6b17682a4a
Build description path: /Users/naubry/xxx/NextVersion/publibs/catch2/_build/build/build/XCBuildData/ac7b261392e652e6dcf24b6b17682a4a-desc.xcbuild

note: Building targets in dependency order
error: unable to attach DB: error: accessing build database "/Users/naubry/xxx/NextVersion/publibs/catch2/_build/build/build/XCBuildData/build.db": database is locked Possibly there are two concurrent builds running in the same filesystem location.
note: Run script build phase 'Generate CMakeFiles/ZERO_CHECK' will be run during every build because the option to run the script phase "Based on dependency analysis" is unchecked. (in target 'ZERO_CHECK' from project 'Catch2')
** BUILD FAILED **

CMake Error at /usr/local/Cellar/cmake/3.24.2/share/cmake/Modules/CMakeIOSInstallCombined.cmake:144 (message):
  Build failed
Call Stack (most recent call first):
  /usr/local/Cellar/cmake/3.24.2/share/cmake/Modules/CMakeIOSInstallCombined.cmake:299 (_ios_install_combined_build)
  src/cmake_install.cmake:66 (ios_install_combined)
  cmake_install.cmake:91 (include)



** BUILD FAILED **


The following build commands failed:
        PhaseScriptExecution CMake\ PostBuild\ Rules /Users/naubry/xxx/NextVersion/publibs/catch2/_build/build/Catch2.build/Debug-iphoneos/install.build/Script-93426DA6A4AC2C2AC9CE883C.sh (in target 'install' from project 'Catch2')
(1 failure)

I have two questions now, that is why it seems to do for all architectures:

-- [iOS combined] Architectures (iphoneos): arm64 arm64e armv7 armv7s
-- [iOS combined] Architectures (iphonesimulator): arm64 arm64e i386 x86_64
-- [iOS combined] Architectures (iphonesimulator) after pruning: i386 x86_64

while I ask only for arm64 and x86_64?

{
    "name": "release-iOS",
    "inherits": ["release"],
    "generator": "Xcode",
    "cacheVariables": {
        "CMAKE_SYSTEM_NAME": "iOS",
        "CMAKE_OSX_ARCHITECTURES": "arm64;x86_64",
        "CMAKE_IOS_INSTALL_COMBINED": "YES",
        "CMAKE_INSTALL_PREFIX": "${sourceParentDir}/_install/iOS/release"
    },
    "condition": {
        "type": "equals",
        "lhs": "${hostSystemName}",
        "rhs": "Darwin"
    }
},
{
    "name": "debug-iOS",
    "inherits": ["debug", "release-iOS"],
    "generator": "Xcode",
    "cacheVariables": {
        "CMAKE_INSTALL_PREFIX": "${sourceParentDir}/_install/iOS/debug"
    }
}

And the second question, why do I get a concurrency error when my python script first runs the configuration and then the compilation/installation?

What CMake and Xcode versions are you using? The following merge request and issues linked therein may be relevant to you:

It does look like my problem but the ticket is at least 1 year old and I am in version 3.24.2 for CMake and XCode 14.0.1 (14A400) so I think I have the fix in my version of CMake but it may not work now.

How are you invoking/executing the install?

To answer quickly – using a Python script that does this:

subprocess.run(["cmake",
                "--preset={0}-{1}".format(args.build, args.target)], cwd=srcDir)
subprocess.run(["cmake",
                "--build", "{0}".format(buildDir),
                "-j", "{0}".format(nbCore),
                "-t", "install"], cwd=srcDir)

But I will do a test by running the compilation by hand and come back to give you the result.
EDIT : I get exactly the same behavior as with the script.

The merge request I linked mentioned that CMake no longer supports using IOS_INSTALL_COMBINED with building the install target due to limitations in Xcode 12+. Use cmake --install instead to avoid the “unable to attach DB” problem you are seeing.

Very well by cutting out the build and installation phase, it works perfectly. Thanks a lot for the help.