Multiple Xcode versions or Why xcrun
is your friend
The story
I recently spent a few hours helping a friend of mine investigate a weird issue in their Continuous Development infrastructure. Builds were failing with different fatal errors mostly related to SDK paths and .platform
directory locations. At first sight, it was clear that something is wrong with the current selected Xcode, but all our initial attempts to catch the problem failed.
In the end, we isolated the problem; one of the tools they use changes PATH
silently for the environment to simplify access to Xcode tools. Due to their internal logic, the CD pipeline changes a current selected Xcode a few times on the way within the same script. In some cases, the pipeline ended with xcodebuild
in the environment’s PATH
that does not reflect the expected version after the xcode-select — switch
command.
How? Pretty easy, actually. A simplified sequence looked like this:
# Innocent tool extends the `PATH`
# With the `bin` directory within the current selected Xcode
% export PATH=”/Xcode_12.5_beta_3.app/Contents/Developer/usr/bin:${PATH}”# We change Xcode in a proper way
% sudo xcode-select — switch /Xcode_12.1.app/Contents/Developer# Due to the overridden `PATH`, `xcodebuild` points to an incorrect version
% xcodebuild -version
Xcode 12.5
Build version 12E5244e# `xcrun` knows the truth
% xcrun xcodebuild -version
Xcode 12.1
Build version 12A7403
xcrun
— Invoke Xcode tools in a safer way
According to the documentation:
xcrun
provides a means to locate or invoke developer tools from the command-line, without requiring users to modify Makefiles or otherwise take inconvenient measures to support multiple Xcode toolchains.
The toolxcode-select(1)
is used to set a system default for the active developer directory and may be overridden by theDEVELOPER_DIR
environment variable.
In real life, it means that you can prefix all the calls to Xcode tools with xcrun
on CI/CD to avoid the situation I described in the beginning. Want to run xcodebuild
— call it via xcrun xcodebuild
command, need to run/invoke simulators, use the xcrun simctl
command.
It can save a few hours for your RE/DevOps team at the end of the day. And, depends on the company size, from 5 to 100 hours for your engineers.
What else?
xcrun
has a few additional commands.
find
— enables “find” mode, in which the resolved tool path is printed instead of the tool being executed.
$ xcrun — find xcodebuild
/Xcode_12.1.app/Contents/Developer/usr/bin/xcodebuild$ xcrun — find clang
/Xcode_12.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
A few helpers to print paths/versions of the selected SDKs.
# Print the path to the selected SDK
$ xcrun — show-sdk-path
/Xcode_12.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk# Print the version number of the selected SDK
$ xcrun — show-sdk-version
10.15.6# Print the build version number of the selected SDK
$ xcrun — show-sdk-build-version
19G68# Print the path to the platform for the selected SDK
$ xcrun — show-sdk-platform-path
/Xcode_12.1.app/Contents/Developer/Platforms/MacOSX.platform# Print the version number of the platform for the selected SDK
$ xcrun — show-sdk-platform-version
10.15.6
verbose
option shows the logic behind the xcrun
. For example, you can see that all the mappings are stored within a temporary xcrun_db
and populated on the stage when you select a different Xcode version.
% xcrun — verbose — find git
xcrun: note: PATH = ‘/Xcode_12.5_beta_3.app/Contents/Developer/usr/bin/:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin’
xcrun: note: SDKROOT = ‘/Xcode_12.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk’
xcrun: note: TOOLCHAINS = ‘’
xcrun: note: DEVELOPER_DIR = ‘/Xcode_12.1.app/Contents/Developer’
xcrun: note: XCODE_DEVELOPER_USR_PATH = ‘’# The database with all the links
xcrun: note: xcrun_db = ‘/var/folders/gn/8j0pyrwj5j3ggxprkczkpwtc0000gn/T/xcrun_db’xcrun: note: xcrun via git (xcrun)
xcrun: note: database key is: git|/Xcode_12.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk||/Xcode_12.1.app/Contents/Developer|
xcrun: note: lookup resolved in ‘/var/folders/gn/8j0pyrwj5j3ggxprkczkpwtc0000gn/T/xcrun_db’ : ‘/Xcode_12.1.app/Contents/Developer/usr/bin/git’
/Xcode_12.1.app/Contents/Developer/usr/bin/git
Side notes
- Check the previous note about xed
— Xcode invocation tool
- Create a minor task for your DevOps team to prefix all the calls to Xcode tools with xcrun