cmakexcode12

CMake build fails with Xcode 12 and CMAKE_IOS_INSTALL_COMBINED=YES


EDIT This is a known CMake issue.


With Xcode 11 (specifically Xcode 11.2.1) I used to be able to cross-compile my project for iOS with this command:

cd /path/to/project
mkdir build
cd build
cmake .. -G Xcode -DCMAKE_SYSTEM_NAME=iOS \
                 "-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64" \
                  -DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO \
                  -DCMAKE_IOS_INSTALL_COMBINED=YES \
                  -DCMAKE_INSTALL_PREFIX=install
cmake --build . --config Release --target install

This created a build with two slices, one slice for the arm64 architecture (for running on a real device) and one slice for the x86_64 architecture (for running in a simulator on an Intel-based dev environment).

After upgrading to Xcode 12 (specifically Xcode 12.3) this no longer works. The build consistently fails with this error message (line break added by me):

error: unable to attach DB: error: accessing build database "/path/to/project/build/ios/build/XCBuildData/build.db":
  database is locked Possibly there are two concurrent builds running in the same filesystem location.

The issue seems to be a post-build rule that CMake creates for the install target. In that post-build rule a second build is initiated while the first one is still in progress.

if test "$CONFIGURATION" = "Release"; then :
  cd /path/to/project/build/ios
  /usr/local/Cellar/cmake/3.19.2/bin/cmake -DBUILD_TYPE=$CONFIGURATION -DEFFECTIVE_PLATFORM_NAME=$EFFECTIVE_PLATFORM_NAME -P cmake_install.cmake
fi

The build succeeds when I no longer set CMAKE_IOS_INSTALL_COMBINED to YES, but then the resulting build only contains one slice (arm64 in my case, presumably because this is the first architecture that is listed in CMAKE_OSX_ARCHITECTURES).

I'm considering making a separate build for each architecture, and then stitching the slices together manually. Before I go down that road, has anyone been able to find a more elegant solution?

Environment: macOS 11.1, Xcode 12.3, CMake 3.19.2


Solution

  • EDIT 3 years later The Xcode legacy build system has been removed in Xcode 14, and CMake also has made some internal changes. So with modern CMake and Xcode versions (3.28.1 and 15.1, respectively) the solution that uses CMAKE_IOS_INSTALL_COMBINED now looks like this:

    cmake .. -G Xcode \
      -DCMAKE_SYSTEM_NAME=iOS \
      "-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64" \
      -DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO \
      -DCMAKE_IOS_INSTALL_COMBINED=YES
    
    cmake --build . --config Release
    cmake --install . --prefix `pwd`/install
    

    The change is that the --build and --install steps now must be done separately, and of course the -T buildsystem=1 workaround no longer applies.

    Note: CMake has deprecated CMAKE_IOS_INSTALL_COMBINED, so the above solution is doomed. The reason for the deprecation is that on Silicon Macs simulator and device builds both use the arm64 architecture, so a combined build that generates a universal library is no longer viable (the same architecture can exist only once in an universal library). It looks as if the way forward is to 1) build a separate framework for the two platforms; and then 2) create an XCFramework with the platform-specific frameworks. Currently CMake has no built-in support for all of this.


    The CMake 3.19 release notes contain this hint:

    The Xcode generator now uses the Xcode “new build system” when generating for Xcode 12.0 or higher. See the CMAKE_XCODE_BUILD_SYSTEM variable. One may use -T buildsystem=1 to switch to the legacy build system.

    The workaround, for the moment, is therefore to add the -T option to the build system generation command line:

    cmake .. -G Xcode -T buildsystem=1
                      -DCMAKE_SYSTEM_NAME=iOS \
                     "-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64" \
                      -DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO \
                      -DCMAKE_IOS_INSTALL_COMBINED=YES \
                      -DCMAKE_INSTALL_PREFIX=install
    

    This should work as long as Xcode still supports the legacy build system. The Xcode 12 Release Notes have this to say on the matter:

    The legacy build system is deprecated, and will be removed in a future release. (62742902)

    So the -T buildsystem=1 option can only be considered a temporary workaround.