What you get
Homebrew ships every Apple Silicon Mac the same bottle: a binary compiled for a generic ARMv8 baseline so it runs on different Apple Silicon generations. Your Mac has exactly one CPU, and the compiler knows its instruction set, its pipeline widths, and its scheduling model. Passing -mcpu=apple-m1 (or whichever generation you have) lets clang and rustc use that knowledge.
This guide rebuilds four tools this way: Vim, Git, ripgrep, and Universal Ctags. Unlike the earlier experiment, which compiled outside Homebrew and swapped the binary into the keg, this approach makes Homebrew do the build from a formula you control. That has two practical consequences:
- the Homebrew receipt is truthful:
brew infoshows a source build from your tap, not a bottle; brew upgraderebuilds from your formula instead of silently replacing the tuned binary with a bottle.
Expect gains in CPU-bound work: Vim regex and Vimscript loops measured 15-23% faster on an M4; Ctags parsers and ripgrep's Unicode regex improved in the earlier measurements. Memory-bound work and Git's everyday commands move little. The measurements page has the numbers; this page is only about how to do it.
Why Homebrew fights you
The obvious command does not work:
CFLAGS="-O3 -mcpu=apple-m1" brew reinstall --build-from-source vim # generic build
Homebrew builds inside superenv, an environment that replaces clang with a shim at /opt/homebrew/Library/Homebrew/shims/mac/super/cc. Reading that shim explains the failure:
- it deletes
CFLAGS,LDFLAGSand friends from the environment before the build starts; - it strips
-O*,-mcpu=*and-mtune=*from every compiler invocation, even ones the formula itself passes; - it then appends its own
-Osplus an architecture flag fromHOMEBREW_OPTFLAGS, which on Apple Silicon is an empty string. The Homebrew source carries the comment# TODO: -mcpu=apple-m1 when we've patched all our GCCs to support it; - the old escape hatch
brew install --env=stdnow aborts with Calling brew install --env is disabled! Use env :std in specific formula files instead.
The last line is the way in. A formula can declare env :std, and Homebrew then builds it with stdenv: the real /usr/bin/clang, no shim, and a CFLAGS variable the formula can append to. So the recipe is: copy the official formula, add env :std and the flags, put it in a tap of your own, and let brew install --build-from-source do the rest.
brew edit vim modifies homebrew-core, and the next brew update either reverts it or leaves the tap dirty. A dedicated tap keeps your changes separate, versionable, and visible in brew info.
Which CPU name to use
Ask the compiler what native resolves to on this machine. The M1 Pro used for the installation check resolves to apple-m1:
sysctl -n machdep.cpu.brand_string
# Apple M1 Pro
clang -mcpu=native -### -x c /dev/null 2>&1 | grep -oE '"-target-cpu" "[^"]+"'
# "-target-cpu" "apple-m1"
clang --print-supported-cpus
Committed formulas use native so each Mac targets its own CPU. The build helper records a concrete CPU in local formulas and Vim provenance. A concrete CPU makes the target explicit; it does not make the binary portable to other chip generations.
NATIVE_CPU only with a CPU supported by the compiler and compatible with the machine that will run the output.Do not use -march=armv9.2-a as a substitute. Generic ARMv9 profiles enable SVE, which Apple cores do not implement, and auto-vectorized code can then crash with an illegal instruction. -mcpu=apple-mN enables exactly the features the chip has.
Prerequisites
- An Apple Silicon Mac (
uname -mprintsarm64) with Homebrew in/opt/homebrew. - Xcode Command Line Tools recent enough to know your CPU (see above).
xcode-select --installifclangis missing. - For ripgrep: Homebrew's
rustformula is installed automatically as a build dependency. The build uses thatcargo, not rustup. - Python 3 available as
python3, for the formula-patching step in the script. - The Homebrew tap for the build implementations and saved formulas; dot-files provides compatibility launchers.
Build times depend on your CPU, compiler, dependencies, and caches. The helper replaces each existing keg before building its native replacement; if compilation fails, rerun after fixing the error or restore the core bottle using the commands below.
The one-command path
The tap owns the formulas and build scripts. Its external Homebrew command handles all four tools:
brew tap marianposaceanu/tap
brew update
brew native # vim git ripgrep universal-ctags
brew native vim ripgrep # a subset
brew native --formulas-only # regenerate portable formulas without building
NATIVE_CPU=apple-m1 brew native vim # explicit compatible CPU
Existing dot-files commands such as ./bootstrap/native/brew_native.sh are forwarding launchers. The tap command lives in cmd/brew-native, with its implementation in native/brew_native.sh.
It resolves the CPU name, installs the marianposaceanu/tap tap if needed, regenerates each formula from the current homebrew-core version, saves it to the tap’s Formula/ directory, syncs it into the tap, uninstalls the existing keg, installs from source, pins, and prints a verification summary. Rerunning it after a Homebrew update is how you pick up a new upstream version.
Use NATIVE_TAP_ROOT=~/work/playground/homebrew-tap with the dot-files launchers to select a development checkout. The launchers forward to the tap’s native/ scripts.
The manual path, step by step
Vim is used as the worked example. The same steps apply to the other three; their formula edits are in the next section.
1. Find your CPU name
CPU=$(clang -mcpu=native -### -x c /dev/null 2>&1 | grep -oE '"-target-cpu" "[^"]+"' | cut -d'"' -f4)
echo "$CPU" # apple-m1
2. Install the tap once
brew tap marianposaceanu/tap
brew --repo marianposaceanu/tap # /opt/homebrew/Library/Taps/marianposaceanu/homebrew-tap
The tap is a Git repository; native formulas are versioned in its Formula/ directory.
3. Copy the official formula into the tap
cp "$(brew formula homebrew/core/vim)" "$(brew --repo marianposaceanu/tap)/Formula/vim.rb"
brew formula prints the path of the formula file Homebrew is currently using, whether it came from the JSON API cache or a full homebrew-core checkout.
4. Edit the formula
Keep the upstream source, checksum, dependencies, features, and test block. Apply these edits:
- Delete the
bottle do ... endblock. Without it Homebrew never considers pouring a bottle, so plainbrew installandbrew upgradebuild from source automatically. - Restrict the formula to Apple Silicon macOS: add
depends_on :macosanddepends_on arch: :arm64. - Add
env :stdon its own line abovedef install. This is the switch from superenv to stdenv. - Add the flags as the first line inside
def install:ENV.append_to_cflags "-O3 -mcpu=apple-m1". stdenv pre-populatesCFLAGSwith-Os -w -pipe; appending puts-O3last, and the last-Oflag wins.
For Vim, two more lines make the flags visible on the intro screen and in :version. Replace "--with-compiledby=Homebrew", in the configure arguments with:
"--with-compiledby=native-apple-m1",
"--with-modified-by=[ apple-m1 :: -O3 -mcpu=apple-m1 ]",
The result, abbreviated:
class Vim < Formula
desc "Vi 'workalike' with many additional features"
homepage "https://www.vim.org/"
url "https://github.com/vim/vim/archive/refs/tags/v9.2.1150.tar.gz"
sha256 "..."
license "Vim"
# (bottle block removed)
depends_on "gettext"
depends_on "libsodium"
# ...
depends_on :macos
depends_on arch: :arm64
env :std
def install
ENV.append_to_cflags "-O3 -mcpu=apple-m1"
# ... unchanged core install body ...
system "./configure", "--prefix=#{HOMEBREW_PREFIX}",
# ...
"--with-compiledby=native-apple-m1",
"--with-modified-by=[ apple-m1 :: -O3 -mcpu=apple-m1 ]",
# ...
end
end
5. Prepare portable snapshots for review
cd ~/work/playground/homebrew-tap
./native/brew_native.sh --formulas-only vim
git diff -- Formula/vim.rb
6. Uninstall the core keg, then install from the tap
brew unpin vim 2>/dev/null
brew uninstall --ignore-dependencies vim
brew install --build-from-source marianposaceanu/tap/vim
brew reinstall marianposaceanu/tap/vim here
When the installed keg came from homebrew-core, brew reinstall resolves the formula from the keg's receipt and rebuilds the core formula under superenv, even though its output says "Reinstalling marianposaceanu/tap/vim". You get a fresh generic binary and a receipt that still says homebrew/core. Uninstalling first forces install to resolve the tap formula. Once the keg is from the tap, reinstall and upgrade behave.
--ignore-dependencies allows removal even when other packages depend on Vim. It does not prevent automatic removal of orphaned dependencies; the following installation restores required dependencies.
7. Pin it
brew pin vim
Pinning is belt-and-braces. Since the tap formula has no bottle, an upgrade would rebuild from source with your flags anyway, but the pin makes the intent explicit and stops brew upgrade from touching it until you decide to.
Per-tool details
Vim
Autotools; configure reads CFLAGS from the environment. Besides the cflags line, set --with-compiledby and --with-modified-by as shown above. All optional interpreters (Python 3, Lua, Ruby, Perl) and +sodium, +clipboard, +terminal survive unchanged because the rest of the install body is the core formula's.
Git
Git's Makefile takes CFLAGS=#{ENV.cflags} as an argument, and the core formula already passes it, so ENV.append_to_cflags is the only edit. The build also compiles git-credential-osxkeychain, git-subtree, and diff-highlight with the same flags. The native Git formula explicitly sets PERL_PATH=/usr/bin/perl and probes that interpreter’s version, preserving macOS Perl’s SSL support under stdenv. Git does not embed its compile flags. Check compiler commands in ~/Library/Logs/Homebrew/git/ alongside the source-build receipt.
ripgrep
Rust ignores CFLAGS. Two edits inside def install:
env :std
def install
ENV["RUSTFLAGS"] = "-C target-cpu=apple-m1"
ENV.append_to_cflags "-O3 -mcpu=apple-m1" # for any C build steps
system "cargo", "install", "--verbose", "--profile", "release-lto", *std_cargo_args(features: "pcre2")
# ...
end
release-lto is a profile ripgrep's own Cargo.toml defines for its release binaries: fat LTO, one codegen unit, panic = "abort", stripped symbols. Homebrew's default uses the plain release profile. The PCRE2 feature stays on, so rg -P keeps working.
Universal Ctags
Autotools with std_configure_args; the cflags line is the only edit. The build links against Homebrew's jansson, libyaml, and pcre2 bottles, which stay generic; only the ctags parsers themselves are tuned.
Dependencies that are pulled in during the build (rust, asciidoctor, jansson, and so on) are installed as normal bottles. Only the four formulas in the tap are compiled.
Verifying the build
Vim tells you directly:
vim --version | head -5
# VIM - Vi IMproved 9.2 (build timestamp varies)
# macOS version - arm64
# Included patches: 1-1150
# Modified by [ apple-m1 :: -O3 -mcpu=apple-m1 ]
# Compiled by native-apple-m1
vim --version | grep -o 'Compilation:.*' | grep -oE -- '-O[0-9s]|-mcpu=[a-z0-9-]+'
# -Os
# -O3
# -mcpu=apple-m1
For all four, the install receipt records formula ownership and whether a bottle was poured. Build logs provide the compiler flag evidence; the receipt alone does not prove optimization flags. A tuned build shows your tap, no bottle, and a formula path inside the tap:
for f in vim git ripgrep universal-ctags; do
printf '%-16s' "$f"
python3 -c '
import json, sys
r = json.load(open(f"/opt/homebrew/opt/{sys.argv[1]}/INSTALL_RECEIPT.json"))
print(r["source"]["tap"], "bottle=%s" % r["poured_from_bottle"], r["source"]["path"])' "$f"
done
# vim marianposaceanu/tap bottle=False /opt/homebrew/Library/Taps/marianposaceanu/homebrew-tap/Formula/vim.rb
# git marianposaceanu/tap bottle=False /opt/homebrew/Library/Taps/marianposaceanu/homebrew-tap/Formula/git.rb
# ripgrep marianposaceanu/tap bottle=False /opt/homebrew/Library/Taps/marianposaceanu/homebrew-tap/Formula/ripgrep.rb
# universal-ctags marianposaceanu/tap bottle=False /opt/homebrew/Library/Taps/marianposaceanu/homebrew-tap/Formula/universal-ctags.rb
If a line says homebrew/core bottle=True after you built it, you hit the reinstall pitfall above: uninstall and install again.
A quick smoke test after each build is worth the ten seconds:
vim -Es +'echo "ok"' +q && echo vim ok
git -C ~/dot-files status --short >/dev/null && echo git ok
rg -P '(?<=brew )pin' ~/dot-files/README.md >/dev/null && echo rg pcre2 ok
ctags --list-languages | wc -l
The benchmarks/ directory has the harnesses from the measurements page if you want numbers rather than a smoke test.
Verified installation on M1 Pro
On 3 October 2026, after brew update, the tap installed Vim 9.2.1150, Git 2.56.0, ripgrep 15.2.0, and Universal Ctags 6.2.1 from source on an M1 Pro. All four formula tests passed. Build logs confirmed -O3 -mcpu=apple-m1 for the C builds and target-cpu=apple-m1, fat LTO, and one codegen unit for ripgrep.
brew test marianposaceanu/tap/vim marianposaceanu/tap/git \
marianposaceanu/tap/ripgrep marianposaceanu/tap/universal-ctags
Functional checks also exercised Vim’s Python, Ruby, and Lua interpreters, ripgrep PCRE2 and Unicode matching, Ctags C/Ruby JSON output, and Git commit/show/blame/fsck. The installation record explains the checks. These results verify installation and behavior; they are not new performance measurements. Use each keg’s absolute executable path if another application shadows it in PATH.
Upgrading later
Because the tap formula is a snapshot of homebrew-core at the time you generated it, brew outdated will not report a newer upstream Vim until the tap formula changes. The maintenance procedure is: refresh Homebrew, regenerate portable formulas in a development checkout, review and publish them, then rebuild from the updated tap.
brew update
cd ~/work/playground/homebrew-tap
./native/brew_native.sh --formulas-only vim # refresh portable snapshots for review
git -C ~/work/playground/homebrew-tap diff Formula/vim.rb # see what upstream changed
git -C ~/work/playground/homebrew-tap commit -am "chore(native): vim 9.2.1200"
By hand, that is steps 3 to 7 again. Compare the freshly copied core formula with your saved one before rebuilding; if upstream changed the configure arguments or the install body, make sure your three edits still land in the right place. The script asserts that def install exists and that the Vim --with-compiledby replacement matched, and refuses to continue otherwise.
The same flow works when you move to a new Mac: run the script once and the formulas are regenerated with the new chip's name. Committed snapshots use host-native flags; local build mode records the detected CPU in regenerated formulas.
Restoring stock bottles
Going back is one line per tool. Naming the core formula explicitly avoids ambiguity with the tap:
for f in vim git ripgrep universal-ctags; do
brew unpin "$f"
brew uninstall --ignore-dependencies "$f"
brew install "homebrew/core/$f"
done
# Keep marianposaceanu/tap installed if you use mextdisplay or rz.
Pitfalls
brew reinstallfrom a core keg builds the core formula. Covered above; it is the one that bit hardest because the build visibly runs and succeeds.- The
vim --versionCompilation line does not prove anything under superenv. The shim injects its flags invisibly, so a superenv build shows-g -O2from Vim's own Makefile and nothing about-mcpu. Only under stdenv does the line show what clang actually received. brew uninstallcan autoremove orphaned dependencies even with--ignore-dependencies. The next install restores the required dependencies.HOMEBREW_NO_INSTALL_FROM_API=1is not needed for tap formulas; they are always loaded from the tap directory. It only matters if you wantbrew formula homebrew/core/vimto come from a full core checkout instead of the API cache. Both produce the same file.- Inspect flags and behavior. Vim exposes its flags. For Git, ripgrep, and Ctags, inspect compiler commands in
~/Library/Logs/Homebrew/and run each formula’sbrew test. A receipt proves source ownership, not which compiler flags took effect. - The tuned binary is not portable. A binary built with
-mcpu=apple-m1may use instructions an M1 lacks. Do not copy the Cellar to another Mac; regenerate there. - Rosetta and Intel Macs are out of scope. The script exits unless
uname -misarm64.