Missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun
After upgrading to macOS Big Sur, I ran into an error sharing my tmux config:
➜ ~ grep -v ^# ~/.tmux.conf | strings | pbcopy
xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun
The fix was just a quick reinstall of the XCode command line tools.
➜ ~ sudo xcode-select --install
But why?
A little process of elimination reveals the command that triggered the error was strings.
The error seems to point to xcrun
. A quick man xcrun
says “xcrun provides a means to locate or invoke developer tools from the command-line”, which doesn’t seem relevant?
What about strings? There’s no strings at that path, right?
➜ ~ find /Library/Developer/CommandLineTools -name strings
/Library/Developer/CommandLineTools/usr/bin/strings
Hmm, ok… I thought it was in /usr/bin?
➜ ~ which strings
/usr/bin/strings
😕
Back to man xcrun
. “xcrun provides a means to locate or invoke developer tools from the command-line…” Ok, so xcrun is invoking strings, but from this path…?
➜ xcrun -find strings
/Library/Developer/CommandLineTools/usr/bin/strings
I’m confused. Time for some research. After a bit of reading, I found my answer in this article: https://macops.ca/developer-binaries-on-os-x-xcode-select-and-xcrun/. The author explains, “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.” He demonstrates this using otool
to see what a binary is linked to.
Using our strings
:
➜ ~ otool -L `which strings`
/usr/bin/strings:
/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 1292.60.1)
He goes on to say, “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.” Which we discovered earlier is pointing to /Library/Developer/CommandLineTools/usr/bin/strings.
To confuse things further, According to the macOS Big Sur 11 Beta Release Notes, “the system ships with a built-in dynamic linker cache of all system-provided libraries. As part of this change, copies of dynamic libraries are no longer present on the filesystem.” i.e., if you go searching for it in the filesystem you might not find anything.
If you trace strings with dtruss
➜ ~ sudo dtruss -n strings
— — — — — 8< — — — — — —
7782/0x11fd6: stat64(“/System/Library/dyld/dyld_shared_cache_x86_64h\0”, 0x7FFEE79E9058, 0x0) = 0 0
7782/0x11fd6: getpid(0x0, 0x0, 0x0) = 7782 0
7782/0x11fd6: stat64(“/Library/Developer/CommandLineTools/usr/bin/strings\0”, 0x7FFEE79E96C0, 0x0) = 0 0
7782/0x11fd6: stat64(“/System/Library/dyld/dyld_shared_cache_x86_64h\0”, 0x7FFEE79E9058, 0x0) = 0 0
7782/0x11fd6: getpid(0x0, 0x0, 0x0) = 7782 0
7782/0x11fd6: stat64(“/Library/Developer/CommandLineTools/usr/bin/strings\0”, 0x7FFEE79E96C0, 0x0) = 0 0
7782/0x11fd6: proc_info(0x2, 0x1E66, 0x1F) = 1040 0
7782/0x11fd6: stat64(“/usr/lib/libSystem.B.dylib\0”, 0x7FFEE79E8740, 0x0) = -1 2
…in the wall of text returned (after running the strings command), we can see that dyld_shared_cache
call.