xcode-select is a command-line utility on OS X that facilitates switching between different sets of command line developer tools provided by Apple. Its primary function is to be a “master switch” for the actual paths resolved when invoking the commands for tools like
From the manpage:
The tool xcode-select(1) is used to set a system default for the active developer directory, and may be overridden by the
The developer tool binaries actually ship with OS X as shim binaries, which use a system library to resolve a path to a “Developer” directory, where all the actual executables, libraries and support files are installed. Let’s see what
/usr/bin/git is actually linked to:
➜ ~ otool -L /usr/bin/git
/usr/lib/libxcselect.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)
What’s really going on when we call these shim binaries?
Behind the scenes, if I run
/usr/bin/git, this shim binary loads functions in
libxcselect.dylib that can locate the path to the real binary, depending on how the system has been configured. One part of this process is to check whether this path contains
usr/lib/libxcrun.dylib, and the
xcrun tool, in which case it will invoke
xcrun to run the binary.
xcrun binary seems to be present in developer directories included with Xcode but not the CLI tools, and it’s also able to query information about SDKs and their included tools.
libxcrun.dylib is a hard requirement for a developer dir to be validated, however. Try for yourself: temporarily rename
usr/lib/libxcrun.dylib within a developer dir like
/Library/Developer/CommandLineTools and then try to
xcode-select --switch to it.
xcode-select will error that it’s an invalid directory.
(If all of this isn’t yet enough indirection for you,
/usr/bin/xcrun itself is a shim, and so
libxcselect.dylib contains code to detect whether the executed
xcrun is a shim. Look for the
__xcrun_shim segment in the
__DATA section output by the command:
pagestuff /usr/bin/xcrun -a.)
Xcode apps include a Developer directory at
Contents/Developer within the app bundle, where among many other things, all the command line tools are installed.
Starting in OS X 10.9, Apple began making it very easy to get the CLI tools installed on-demand. Not every use case calls for a full Xcode installation, and the CLI tools are a tiny fraction of the install footprint of Xcode. The CLI tools from this package live in
The CLI tools are sufficient for providing some common developer tools, but they don’t include the SDKs or
xcodebuildneeded to build Xcode projects. In some cases, native extensions in packages for other languages like Ruby and Python also require Xcode.
So, it’s often the case that one might end up with both an Xcode and the CLI tools package installed, and at some point there may be confusion about when one is required over the other. In my experience Xcode always contains the full set of CLI tools. However, if a system at some point has either been switched to a different developer dir, an Xcode app was moved, or Xcode itself has been recently upgraded (silently, from the Mac App Store, and a new license has yet to be accepted, for example), one might reason that both Xcode and CLI tools are required for some tasks, when this shouldn’t be the case.
That being said, I’d like to know if there is a case where the CLI tools provided with Xcode apps are insufficient and the separate CLI tools package is required because it provides something not included with Xcode.
Check the currently selected directory with
xcode-select using the
-p/--print-path option. Here’s mine:
➜ ~ xcode-select -p
You can then use the
-s/--switch option to change this path.
Chances are you’ve worked on a machine where you’ve never had to run
xcode-select -s to explicitly select a directory, and other times you have. The
libxcselect library linked to by
xcode-select (the same linked to by the
git shim binary we showed earlier) has a series of configuration checks it will perform to try and auto-discover a developer directory, and assuming it finds one,
xcode-select -p will tell us what that is.
Below are what I’ve deduced its order of checks to be. Remember that this checking is the responsibility of
xcode-select simply provides a way of manipulating some configuration on the system that
libxcselect will make use of when one of these shim commands is run, or if
xcrun is invoked directly by the user with specific options.
DEVELOPER_DIR environment variable is set when a command is run, any system default will be overridden.
/var/db/xcode_select_link is present and it points to a valid developer dir within an Xcode or the CLI tools, this will be used. This symlink will be created when running
xcode-select -s <path> if
<path> is either an alternate Xcode path or that of the CLI tools, and there is already an
/Applications/Xcode.app/Contents/Developer is present, this is used.
/Library/Developer/CommandLineTools, these are selected.
A disassembly of
/usr/lib/libxcselect.dylib also suggests that there is an additional check performed, similar to the symlink check, if a file is present at
/usr/share/xcode-select/xcode_dir_path. This path is protected by System Integrity Protection, however, so it’s likely only used internally for development.
As mentioned earlier, when a directory switch is attempted,
libxcselect will perform a sanity check to at least ensure that the
usr/lib/libxcrun.dylib library exists within the directory.