Skip to content

Detect the Xcode toolchain outside its default location - #35

Merged
coatless merged 4 commits into
mainfrom
xcode-cli-detection
Sep 16, 2026
Merged

coatless merged 4 commits into
mainfrom
xcode-cli-detection

Conversation

@coatless

Copy link
Copy Markdown
Contributor

The problem

is_xcode_cli_installed() and is_xcode_app_installed() compared the active developer directory against a single hardcoded path with identical(), so both returned FALSE whenever anything else was selected, even with a working toolchain installed:

is_xcode_cli_installed()
#> [1] FALSE
xcode_cli_path()
#> [1] "/Applications/Xcode_16.4.app/Contents/Developer"

A versioned bundle like that is the normal multi-Xcode layout, and it is what every GitHub-hosted macOS runner selects, so this was live on this package's own CI. macos_rtools_install() treated the two predicates as exhaustive and would start a softwareupdate install of Command Line Tools that were already present.

The fix

Split the conflated question in three, following Homebrew:

function answers
is_xcode_cli_installed() is the Command Line Tools package present
is_xcode_app_installed() is any Xcode installed, anywhere
is_xcode_toolchain_usable() can a compiler actually be reached

The install step gates on the last of these, which is what determines whether R can build a package, since R is configured with a bare clang that resolves through the active developer directory. It now reports which toolchain is active instead of assuming a full Xcode at /Applications/Xcode.app, and reports a failure rather than "no action needed" when the tools are on disk but nothing can compile.

Path comparisons go through normalize_developer_dir(), and DEVELOPER_DIR is honored where the package previously did not account for it.

The destructive path stays keyed on package presence. Widening is_xcode_cli_installed() to mean "a usable toolchain is selected" would let a machine with Xcode selected delete the Command Line Tools.

Listing the available toolchains

xcode_toolchains() reports every developer directory xcode-select could be pointed at, found by bundle identifier so a renamed or relocated install is still detected:

xcode_toolchains()
#>                                         path type             version active
#> 1 /Applications/Xcode.app/Contents/Developer  app                27.0   TRUE
#> 2        /Library/Developer/CommandLineTools  cli 27.0.0.0.1788430756  FALSE

xcode_cli_switch() takes a path from that table rather than always forcing the Command Line Tools. The target is validated, and the selection being replaced is named.

Also in here

  • macos_rtools_uninstall() no longer reports "Xcode CLI: Successfully removed" when nothing was removed.
  • The Command Line Tools version no longer comes from xcode-select --version, which reports the version of xcode-select itself and never changes with the toolchain.
  • A README logo fix: pkgdown's main img { height: auto } overrides the HTML height attribute, so the hex is sized by width instead.

Breaking changes are listed in NEWS.

pkgdown's Bootstrap CSS sets `main img { height: auto }`, which overrides
the HTML height attribute, so the hex logo rendered at its intrinsic
400x462 instead of the intended 170px tall. Setting width instead
survives the cascade and renders 150x173, matching the other packages.
is_xcode_cli_installed() and is_xcode_app_installed() compared the active
developer directory against a single hardcoded path with identical(), so
both returned FALSE whenever anything else was selected, even with a
working toolchain installed. A versioned bundle such as
/Applications/Xcode_16.4.app/Contents/Developer is the normal multi-Xcode
layout and is what every GitHub-hosted macOS runner selects, so this was
live on this package's own CI. macos_rtools_install() treated the two
predicates as exhaustive and would start a 10-15 minute softwareupdate
install of Command Line Tools that were already present.

Split the conflated question in three, following Homebrew: CLT package
presence, any-Xcode-installed, and whether a compiler is actually
reachable via xcrun. The install step now gates on the last of these.
Path comparisons go through normalize_developer_dir(), and DEVELOPER_DIR
is honored where it was previously ignored.

Keep the destructive path keyed on package presence. Widening
is_xcode_cli_installed() to mean "a usable toolchain is selected" would
let a machine with Xcode selected delete the Command Line Tools.

Add xcode_toolchains() to list every selectable developer directory, and
let xcode_cli_switch() take one rather than always forcing the CLT.
@coatless
coatless merged commit 5321e6d into main Sep 16, 2026
3 checks passed
@coatless
coatless deleted the xcode-cli-detection branch September 16, 2026 23:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant