Skip to content

Opt-in support for installing casks without the Xcode Command Line Tools #23029

Description

@milanvarady

Verification

Provide a detailed description of the proposed feature

Add an opt-in environment variable (name up to the maintainers, for example HOMEBREW_NO_DEVELOPER_TOOLS) that lets cask commands run on a Mac without the Xcode Command Line Tools, without failing and without opening the "install command line developer tools" system dialog.

Casks are prebuilt applications, so a compiler is not needed to download and place them. On Apple Silicon, several code paths still reach for the CLT during cask install and uninstall:

  • extend/os/mac/diagnostic.rb adds check_for_installed_developer_tools to fatal_preinstall_checks when Hardware::CPU.arm?, so brew install --cask aborts with "No developer tools installed" (via cask/installer.rb perform_preinstall_checks_once).
  • cask/quarantine.rb runs /usr/bin/xcrun -find swift to find the Swift helper for the quarantine attribute. Without the CLT that stub opens the installer dialog, and there is no non-Swift fallback, so the cask ends up unquarantined regardless.
  • cask/utils/trash.rb runs cask/utils/trash.swift (a #!/usr/bin/swift script) for trash: directives on uninstall and zap.
  • DevelopmentTools.locate (mac) falls back to /usr/bin/xcrun -find <tool>, which can open the same dialog.

When the variable is set, each path would take the no-tools route: skip the fatal dev-tools check, skip the xcrun -find fallback in locate, treat Swift as unavailable so quarantine is skipped, trash by moving to the Trash directly, and let the git shim skip its xcrun -find git fallback. Default behavior does not change.

There is a precedent in brew.sh, which already avoids calling xcrun when the tools are absent, with a comment that doing so "opens the install developer tools dialog." This makes the cask paths consistent with that.

What is the motivation for the feature?

I maintain Applite, a GUI app that installs Homebrew casks for non-technical users. It ships its own Homebrew and drives it for installs and uninstalls. Asking these users to download the multi gigabyte Command Line Tools before they can install a single prebuilt app is a large barrier, and today they either get a hard failure on Apple Silicon or an unexpected system dialog asking them to install developer tools.

More generally, any tool, script, or CI job that installs casks on a machine without the CLT hits the same friction, even though the compiler is not needed for the download and move case.

How will the feature be relevant to at least 90% of Homebrew users?

Honest answer: as an opt-in variable it will only be set by users and tools that specifically want cask installs without the CLT, so it is not something 90% of users would turn on. What is relevant to everyone is the behavior it corrects. Any macOS user who has not installed the CLT yet (a fresh machine, someone who declined the prompt, an automated setup) can currently hit a surprise developer-tools dialog or a hard failure when a cask is installed on their behalf, even though nothing is being compiled. The change removes that surprise for that case and stays invisible and zero cost to everyone who has the tools, since it is off by default. If the 90% bar is strict here, I understand this may fit better as a documented opt-in for the cask-without-CLT case rather than a default change, and I am happy to take direction.

What alternatives to the feature have been considered?

  • Requiring the CLT up front. This is the current situation and the barrier we are trying to remove for prebuilt apps.
  • Patching Homebrew's source in a shipped copy. This works but is fragile, since it has to be reapplied on every update and can break silently when internals move.
  • Reimplementing cask install natively. Not viable for full parity, because casks can run arbitrary Ruby in preflight and postflight blocks that is not in the JSON API and cannot be reproduced without a Ruby interpreter.

The opt-in variable is the smallest change that covers all the cask paths in one place without affecting default behavior.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions