build-emacs-for-macos builds a self-contained Emacs.app from any ref
Somewhat hacky script to automate building of Emac.app on macOS.
At a glance
- What is it?
- build-emacs-for-macos is a personal build script for Emacs on macOS that produces a self-contained Emacs.app from any branch, tag or git ref, with native compilation, Tree-sitter version selection and a set of optional patches. Its author labels it somewhat hacky, and it does not sign the application for you.
- Who is it for?
- Adopt build-emacs-for-macos if you want to run an unreleased Emacs on macOS as a self-contained application you can roll back by switching refs, and you are prepared to bootstrap a Nix or Homebrew toolchain and rebuild after each Emacs change.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A personal script, and it says so
The README opens without marketing: my personal hacked together script for building a completely self-contained Emacs.app application on macOS, from any git branch, tag, or ref, with support for native-compilation. And there is a line straight after: use this script at your own risk.
That framing is useful, because it tells you what kind of artefact you are looking at. This is a working script that produces a thing the author uses daily, not a packaged product with a support contract. The Status section is equally plain: as of writing, on 2024-11-30, it works for me on my machine and for the nightly builds in the separate jimeh/emacs-builds repository, followed by your luck may vary.
The three builds the author reports having made are the emacs-29.4 release tag, the emacs-30.0.92 pretest tag, and the master branch, which at that point was Emacs 31.x. The reference machine is a 14-inch MacBook Pro from 2023 with an Apple M3 Max, 16 cores, running macOS Sonoma 15.1.1 and Xcode 16.1.
The project is MIT licensed in spirit, though the repository record lists the licence as one that could not be classified and there is no LICENSE file among the top level entries. The default branch is master, the primary language is Go, and the release history shows v0.6.63 in September 2025, v0.6.64 in September 2026 and v0.6.65 on 2026-10-18, the same day as the last push.
The audience is Emacs users on macOS who want the unreleased features, and who are comfortable with a build toolchain rather than a double click.
Why not Homebrew: self-contained apps and rollback
The Why section gives three reasons, and the middle one is the technical core of the project.
The first is straightforward: to use new features available from master or branches which have not yet made it into an official stable release.
The second is about what a Homebrew Emacs actually is. Homebrew builds of Emacs are not self-contained applications, which the README says makes it very difficult when doing HEAD builds, because you need to roll back to an earlier version. A Homebrew install is a set of files in a prefix managed by a package manager, and a build of master overwrites them. There is no separate artefact you can keep.
That is the point of this script. It produces an Emacs.app that is entirely its own thing, sitting in a directory, which means you can build master, keep it, build something else, and switch between them.
The third reason is about the prebuilt alternatives. Both Homebrew HEAD builds and nightly builds from emacsformacosx.com are built from the master branch, so this script lets you choose any branch, tag, or git ref instead. The script takes its refs from the emacs-mirror/emacs GitHub repository, and the usage output spells that out along with the --git-sha option, which overrides the detected SHA so you can build an old commit.
The binary builds are published separately, as nightly and stable builds produced with this script, in the jimeh/emacs-builds repository, with GitHub Actions on GitHub-hosted runners using macos-13 for Intel builds and macos-14 for Apple Silicon builds, the build environment managed with Nix and targeting the macOS 11 SDK.
Bootstrapping the toolchain with Nix or Homebrew
Xcode is required either way. Beyond that there are two supported routes for dependencies, and the README calls Nix the preferred and most reliable way, by way of a Nix flake included in the project root.
With Nix, everything is installed by entering the development shell:
nix develop --command make bootstrapWith Homebrew, you need Ruby 3.3.x or later to execute the build script, with earlier versions possibly working but untested, and the simplest way to get a recent Ruby is:
brew install rubyfollowed by the same bootstrap step, which installs the build-time dependencies:
make bootstrapThe Makefile shows what that actually does. The bootstrap target depends on bootstrap-brew and bootstrap-ruby: the Ruby one runs bundle install with BUNDLE_WITHOUT=development, and the Homebrew one runs brew bundle, but only if IN_NIX_SHELL is not set, which is how the same target serves both routes. There is a separate bootstrap-pip target that installs requirements-ci.txt.
The build script itself is a shell script in the repository root, and the usage output in the README shows it is the same interface in both environments:
nix develop --command ./build-emacs-for-macos --helpThe Nix environment defaults to targeting the macOS 11 SDK, which makes the builds compatible with macOS 11.3 or later, and later SDKs are available by picking a flake output, for example nix develop .#macos12 to target the macOS 12 SDK, with versions 11 to 15 available.
The flag list shows what this script actually controls
The usage output of the build script is the best description of the project, and it is long. The first part covers the basics:
Usage: ./build-emacs-for-macos [options] <branch/tag/sha>
Branch, tag, and SHA are from the emacs-mirror/emacs/emacs Github repo,
available here: https://github.com/emacs-mirror/emacs
Options:
--info Print environment info and detected library paths, then exit
--preview Print preview details about build and exit.
-j, --parallel COUNT Compile using COUNT parallel processes (detected: 16)
--git-sha SHA Override detected git SHA of specified branch allowing builds of old commits
--[no-]use-nix Use Nix instead of Homebrew to find dependencies (default: enabled if IN_NIX_SHELL is set)
--[no-]tree-sitter Enable/disable tree-sitter if supported (default: enabled)
--[no-]native-comp Enable/disable native-comp (default: enabled if supported)
--optimize Shorthand for --native-march --native-mtune --fomit-frame-pointer (default: disabled)The rest of the list is the feature and patch configuration:
--[no-]native-march Enable/disable -march=native CFLAG (default: disabled)
--[no-]native-mtune Enable/disable -mtune=native CFLAG (default: disabled)
--[no-]fomit-frame-pointer Enable/disable -fomit-frame-pointer CFLAG (default: disabled)
--[no-]native-full-aot Enable/disable NATIVE_FULL_AOT / Ahead of Time compilation (default: disabled)
--[no-]relink-eln-files Enable/disable re-linking shared libraries in bundled *.eln files (default: enabled)
--[no-]rsvg Enable/disable SVG image support via librsvg (default: enabled)
--[no-]dbus Enable/disable dbus support (default: enabled)
--alpha-background Apply experimental alpha-background patch when building Emacs 29.x - 32.x (default: disabled)
--no-frame-refocus Apply no-frame-refocus patch when building Emacs 27.x - 29.x (default: disabled)
--no-titlebar Apply no-titlebar patch when building Emacs 27.x - 28.x (default: disabled)A few of those are worth pausing on. Native compilation is enabled by default when supported, and --optimize is a shorthand for the three native code generation flags, which is the setting you would use to get a build tuned for your exact CPU. The patch flags are version scoped, so the script applies the alpha-background patch to Emacs 29 through 32 and the no-frame-refocus patch to 27 through 29, and the alpha-background one is described as experimental.
Tree-sitter versions, privacy descriptions and the signing gap
Three details in the README show the amount of care in this script.
The first is Tree-sitter. The build selects Tree-sitter 0.25.x for Emacs 29 and 30, and 0.27.x for Emacs 31 and later, and both are built with the pinned Nixpkgs 25.05 Darwin toolchain so their bundled libraries stay compatible with macOS 11.3 and later. The Homebrew route installs both [email protected] and the current tree-sitter formula and selects between them with the same version policy. The reason for the pin is compatibility with the deployment target, not preference.
The second is the privacy usage descriptions. Every Emacs.app produced includes macOS privacy usage descriptions for protected resources commonly accessed by Emacs Lisp and child processes, covering both legacy and current macOS keys across the supported deployment range, and they are embedded before the application is signed. The README is careful about what this does: the descriptions allow macOS to explain a permission request and display its normal consent prompt, they do not grant access or bypass the user's privacy settings.
The third is signing, and it is a limitation rather than a feature. The application is not signed automatically, but the CLI tool used to sign the nightly builds is available, and the README points at go run ./cmd/emacs-builder package --help for details, with more detailed instructions to come.
That last point is the one to plan for. A self-contained application that is not signed will trip Gatekeeper on another Mac, and the fix is documented only as a command to read the help for.
A Go CLI and a Ruby build script in one repository
The repository contains both languages, and the split is informative.
The build script that drives the Emacs compilation is the shell script in the repository root, and it needs Ruby 3.3.x, which is why the Homebrew route installs Ruby and why the bootstrap target runs bundle install with the development group excluded. There is a Gemfile and a Gemfile.lock, a Brewfile for the Homebrew dependencies, a .rubocop.yml for the Ruby side, and a Makefile whose tools section installs gofumpt, golangci-lint and gomod into a local tools directory.
The Go side is the CLI. The go.mod names the module github.com/jimeh/build-emacs-for-macos and requires urfave/cli/v2 for the command line, the bearer/gon package for downloading, go-github for the GitHub API, oauth2 for authentication, hclog and zap for logging, gotextdiff and undent for producing readable diffs in the output, yaml.v3 for configuration, and howett.net/plist for handling macOS property lists, which is what the application bundle and its metadata are made of. The Go version is 1.23.
The repository layout has cmd/, pkg/, helper/, lib/ and patches/ directories, a spec/ directory for tests, and a patches/ directory that holds the patch files the flags apply. There is also a CLAUDE.md file, a .golangci.yml and a requirements-ci.txt.
The two-language design makes sense in context. The parts that need to talk to macOS tooling, to the Emacs build system and to Homebrew are comfortable in Ruby, while the parts that download refs, drive the CI release pipeline and render diffs to the terminal are comfortable in Go, and the Go CLI is the one that also knows how to sign a package.
Editorial conclusion
Adopt build-emacs-for-macos if you want to run an unreleased Emacs on macOS as a self-contained application you can roll back by switching refs, and you are prepared to bootstrap a Nix or Homebrew toolchain and rebuild after each Emacs change. Do not adopt it if you want a signed, notarised application, because the README is explicit that signing is a separate step, and do not adopt it to share binaries, since the CPU architecture and the minimum macOS version of your build come from the machine you built it on. Before you build, read the Limitations section and pick your SDK target with the flake, since the default macOS 11 target is what makes the nightly builds compatible with macOS 11.3.
Frequently asked questions
How do I bootstrap the dependencies to build Emacs on macOS?
With Nix, run `nix develop --command make bootstrap`, which the README calls the preferred and most reliable route. With Homebrew you need Ruby 3.3.x or later, install it with `brew install ruby`, and then run `make bootstrap` to install the build-time dependencies.
Can I build Emacs from a branch other than master?
Yes, that is the point of the script. It takes a branch, tag or git ref from the emacs-mirror/emacs repository, and the --git-sha option overrides the detected SHA so you can build an old commit. The prebuilt Homebrew HEAD and emacsformacosx nightlies are all built from master, which is the gap this fills.
Does build-emacs-for-macos sign the application it produces?
No. The README says the application is not signed automatically, but the CLI tool used to sign the nightly builds is available, and you can run `go run ./cmd/emacs-builder package --help` for details, with more detailed instructions promised.
What are the limitations of a build-emacs-for-macos application?
It is not a universal application, so the CPU architecture of the built application is that of the machine it was built on, and the minimum required macOS version is the same as that machine's. The build is also not signed automatically.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jimeh-build-emacs-for-macos)Was this useful?