Specifying CMAKE_OSX_SYSROOT breaks Xcode projects, but no other choice

If you don’t specify CMAKE_OSX_SYSROOT, then CMake seems to default to some invalid SDK, or uses the DEPLOYMENT_TARGET which is far too low for the SDK. SYSROOT is effectively, the maximum build of macOS that the app supports. Anything higher should be fine.

So then I specify the build number (see below), and that works on my machine. One another machine, the newer Xcode breaks. I don’t want devs to have to chase changing this, and CMake really really breaks when it can’t find the SDK and declares invalid C and CXX and OBJ_CXX compilers instead of just stating that it couldn’t find the SDK.

What is the cmake specification for use whatever is the available SDK in Xcode? Also setting ARCHS_STANDARD doesnt work. I’m on CMake 3.19.1 on macOS Intel and ARM.

Snippet from CMakeLists.txt

# Xcode 12.2 ships with macosx11.0, but 12.3 ships with macosx11.1
# Xcode 12.3 completely breaks when this is set and can't find c compilers.
set(CMAKE_OSX_SYSROOT macosx11.0) # this only works, finds macosx11.0.sdk in Xcode 12.2
# set(CMAKE_OSX_SYSROOT macos) # this doesn't work, despite there being a macos.sdk

set(CMAKE_OSX_DEPLOYMENT_TARGET 10.14) # this works, great!
set(CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD)")  <- this doesn't work either

This is not expected. Could you please share the error that you get when not specifying CMAKE_OSX_SYSROOT at all? You should most likely try that in a fresh build directory, since CMAKE_OSX_SYSROOT might be cached.

Don’t specify the version number of the sdk. In fact, for macOS, don’t specify the SDK at all and it will automatically select the default SDK for the macOS platform. You should still specify the minimum deployment version though (using CMAKE_OSX_DEPLOYMENT_TARGET) - this is the minimum macOS version you want to support your application running on and is (mostly) independent of the SDK you use (assuming your SDK is new enough to know about your selected minimum deployment version).

If you are working with Apple Silicon, I recommend CMake 3.19.2 and Xcode 12.2 as your minimum versions. Earlier versions are likely to have problems in some areas.

When I leave that out, this is what I see in Xcode 12.2 and CMake 3.19.3 (I upgraded). The build succeeds, but the XCode project is left with and invalid SDK set. I know cmake has it’s own build settings, but I like to see the Xcode project reflect correct data. When SYSROOT is set, then this field is set to a valid value. Without it, it’s not.

I guess a request is something like DEPLOYMENT_TARGET. Have a setting like SDK_TARGET=11.0 that says that I need 11.0 SDK or higher, fail if it’s not set. That’s way safer than what happens when SYSROOT is set, the sdk not found, and CMake fails to even gen a project. For CMake newbies, this doesn’t leave them with much to work with since they have to then understand CMake internals and debugging from the command line.

This is the symlink path on my machine to MacOSX.sdk, but I think Xcode just wants macosx11.0 for that field, not a full path as specified by CMake when SYSROOT isn’t specified.
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk

When I set the project “Base SDK” to macOS, then the project reflects this, but I’m unable to set SYSROOT to that. I can only set macosx11.0 so that CMake I guess can look it up. This should likely be the default that says “use the latest sdk”.

SDKROOT = macosx;

I also don’t understand why ARCHS is never set to $(ARCHS_STANDARD), despite the set call. My project only says this, and then again I have to manually set the value in Xcode.

ARCHS = “$(NATIVE_ARCH_ACTUAL)”;

1 Like

You seem to be confusing the SDK version with the deployment version, at least partially. They are two distinct things and should not be specified via a single variable.

A minimum SDK version basically corresponds to a minimum Xcode version. You get one SDK with an Xcode installation and you shouldn’t really ever have to specify the SDK version. If your project requires some minimum SDK version to build, it would be better to advertise that as some minimum Xcode version (my personal view). Way back in the past, people sometimes liked to copy forward an SDK from an older Xcode to a newer Xcode, but that has never been technically supported and is something that shouldn’t be done these days (there’s never been any guarantee that this would work and things could silently go wrong).

The deployment version is specified separately by the CMAKE_OSX_DEPLOYMENT_TARGET, which must be set before the first call to project() and it must be a cache variable. Your code sample sets it as an ordinary variable, so it will be discarded and replaced by the default (this may well be the source of your problems). The SDK is controlled by CMAKE_OSX_SYSROOT and should be left empty for macOS builds. I think there’s room for improvement in the way CMake handles the SDK setting, it should probably put macosx (or whatever the relevant value is) instead of the full path to the SDK in the relevant field of the Xcode project file, but I think that might require non-trivial changes to CMake due to the baked-in assumption about CMAKE_OSX_SYSROOT holding the full path to the SDK in some places. This is the cause of why you see that (harmless) warning in recent Xcode versions.

The default is $(NATIVE_ARCH_ACTUAL) so as to preserve backward compatible behavior where the build defaulted to a single architecture. This has been a fairly messy area in bringing the support for Big Sur to CMake and I’m not sure we’ve got it right, but we can’t really change that default now. I would recommend projects (maybe more toolchain files) explicitly set CMAKE_OSX_ARCHITECTURES to $(ARCHS_STANDARD) so that they can build universal binaries if they want. During development though, I would still expect $(NATIVE_ARCH_ACTUAL) to work and this would be more efficient (since you’d only be building for one architecture all the time).

If you are trying to set the architecture, perhaps you are setting in the wrong place. Your code sample doesn’t show where you are trying to set those variables. FWIW, I’d expect the following command line to be all you need for a macOS build that produces universal binaries with the default SDK (i.e. no need to specify anything other than CMAKE_OSX_DEPLOYMENT_TARGET in your project):

cmake -G Xcode -D "CMAKE_OSX_ARCHITECTURES:STRING=$(ARCHS_STANDARD)" path/to/source

Thanks for all the responses, I really appreciate them. I worked with and for Apple for a few years, so I know a little about working with Xcode. The issue I raised above is that there’s no good way currently to specify CMAKE_OS_SYSROOT that doesn’t break projects and in a very bad way for devs that get a CMake script with this set. On my recommendations on how to fix this, the response I’ve been seeing is “just don’t specify that”. But that’s not really a solution to the described problem.

My suggestion was that a variable similar to DEPLOYMENT_TARGET is needed for SDK, let’s call it SDK_TARGET. That would indicate in a script that the app that I’ve released needs at least an SDK of 11.0 or higher. A newer Xcode will have SDK 11.1+ and with SYSROOT=11.0 doing an == comparison, the CMake project generation breaks.

What I really am indicating is the minimum SDK requirement. DEPLOYMENT_TARGET is the minimum OS that the app will run on, but it will only build on SDK 11.0 or higher. There’s currently no search in CMAKE if SYSROOT is set for a different SDK, or a way to enforce == or >= to that SDK. But that’s what SDK_TARGET could do, or possibly SYSROOT could be fixed.

My sample app is at https://github.com/alecazam/kram. It’s a texture encoder and viewer. I think having sample CMake scripts that build a macOS or iOS app in the CMake repository would be a big help, but maybe I just haven’t found those. I typically have to piece all this together from various quality StackOverflow responses, but I think the CMakeLists.txt files I have are pretty solid now.

I acknowledge your suggestion. CMake doesn’t in general provide direct support for enforcing a minimum compiler version for any compiler or platform that I’m aware of, but it gives the information required for you to check and enforce that yourself. For example:

# Only supports Xcode generator
set(minXcodeVersion 11.0)
if(XCODE AND XCODE_VERSION VERSION_LESS minXcodeVersion)
    message(FATAL_ERROR "This project requires at least Xcode ${minXcodeVersion}")
endif()

# Should work for all generators but meaning of version number may be less clear.
# Replace CXX with whatever language is relevant for your project.
set(minAppleClangVersion 11.0.3.11030032)  # This would be Xcode 11.7!
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND
   CMAKE_CXX_COMPILER_VERSION VERSION_LESS minAppleClangVersion)
    message(FATAL_ERROR "This project requires at least Apple Clang ${minAppleClangVersion}")
endif()

I would write something like this:

project(...)

if(APPLE)
  if(NOT DEFINED CMAKE_OSX_SYSROOT)
    message(FATAL_ERROR "Cannot check SDK version if CMAKE_OSX_SYSROOT"
      " is not defined."
    )
  endif()

  execute_process(
    COMMAND xcrun --sdk "${CMAKE_OSX_SYSROOT}" --show-sdk-version
    OUTPUT_VARIABLE sdk_version
    OUTPUT_STRIP_TRAILING_WHITESPACE
  )

  if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
    if(sdk_version VERSION_LESS 14.2)
      message(FATAL_ERROR "This project requires at least iPhoneOS SDK"
        " 14.2, but got ${sdk_version} instead."
      )
    endif()
  else()
    if(sdk_version VERSION_LESS 11.0)
      message(FATAL_ERROR "This project requires at least macOS SDK"
        " 11.0, but got ${sdk_version} instead."
      )
    endif()
  endif()
endif()

Hi Vivian,

I added your snippet, so thanks. I was hoping for a test prior to “project” failing if CMAKE_OSX_SYSROOT was set, but this has to be set after the project command has executed (and broken cmake). One just can’t set CMAKE_OSX_SYSROOT safely when sharing projects or it breaks on Xcode updates.