Files
build-emacs-for-macos/README.md

228 lines
8.7 KiB
Markdown

# build-emacs-for-macos
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.
Use this script at your own risk.
## Why?
- To use new features available from master or branches, which have not made it
into a official stable release yet.
- Homebrew builds of Emacs are not self-contained applications, making it very
difficult when doing HEAD builds and you need to rollback to a earlier
version.
- Both Homebrew HEAD builds, and nightly builds from emacsformacosx.com are
built from the `master` branch. This script allows you to choose any branch,
tag, or git ref you want.
## Status
As of writing (2021-04-25) it works for me on my machine. Your luck may vary.
I have successfully built:
- `emacs-27.1` release git tag
- `master` branch (Emacs 28.x)
- `feature/native-comp` branch (Emacs 28.x)
For reference, my machine is:
- 13-inch MacBook Pro (2020), 10th-gen 2.3 GHz Quad-Core Intel Core i7 (4c/8t)
- macOS Big Sur 11.2.3 (20D91)
- Xcode 12.4 (12D4e)
## Limitations
The build produced does have some limitations:
- It is not a universal application. The CPU architecture of the built
application will be that of the machine it was built on.
- The minimum required macOS version of the built application will be the same
as that of the machine it was built on.
- The application is not signed, so running it on machines other than the one
that built the application will yield warnings. If you want to make a signed
Emacs.app, google is you friend for finding signing instructions.
## Requirements
- [Xcode](https://apps.apple.com/gb/app/xcode/id497799835?mt=12)
- [Homebrew](https://brew.sh/)
- All Homebrew formula listed in the `Brewfile`, which can all easily be
installed by running:
```
brew bundle
```
- Ruby 2.3.0 or later is needed to execute the build script itself. macOS comes
with Ruby, check your version with `ruby --version`. If it's too old, you can
install a newer version with:
```
brew install ruby
```
## Usage
```
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:
-j, --parallel COUNT Compile using COUNT parallel processes (detected: 8)
--git-sha SHA Override detected git SHA of specified branch allowing builds of old commits
--[no-]xwidgets Enable/disable XWidgets (default: enabled if supported)
--[no-]native-comp Enable/disable native-comp (default: enabled if supported)
--[no-]native-full-aot Enable/disable NATIVE_FULL_AOT / Ahead of Time compilation (default: disabled)
--rsvg Enable SVG image support via librsvg, can yield a unstable build (default: disabled)
--no-titlebar Apply no-titlebar patch (default: disabled)
--no-frame-refocus Apply no-frame-refocus patch (default: disabled)
--[no-]native-fast-boot DEPRECATED: use --[no-]native-full-aot instead
--[no-]launcher DEPRECATED: Launcher script is no longer used.
```
Resulting applications are saved to the `builds` directory in a bzip2 compressed
tarball.
If you don't want the build process to eat all your CPU cores, pass in a `-j`
value of how many CPU cores you want it to use.
Re-building the same Git SHA again can yield weird results unless you first
trash the corresponding directory from the `sources` directory.
### Examples
To download a tarball of the `master` branch (Emacs 28.x with native-compilation
as of writing) and build Emacs.app from it:
```
./build-emacs-for-macos
```
To build the stable `emacs-27.1` release git tag run:
```
./build-emacs-for-macos emacs-27.1
```
All sources as downloaded as tarballs from the
[emacs-mirror](https://github.com/emacs-mirror/emacs) GitHub repository. Hence
to get a list of tags/branches available to install, simply check said
repository.
## Use Self-Contained Emacs.app as `emacs` CLI Tool
As the application bundle is self-contained, the main executable needs to be run
from within the application bundle. This means a simple symlink to
`Emacs.app/Contents/MacOS/Emacs` will not work. Instead the best approach is to
create a shell alias called `emacs` pointing to the right place.
Personally I use something similar to this:
```bash
if [ -f "/Applications/Emacs.app/Contents/MacOS/Emacs" ]; then
export EMACS="/Applications/Emacs.app/Contents/MacOS/Emacs"
alias emacs="$EMACS -nw"
fi
if [ -f "/Applications/Emacs.app/Contents/MacOS/bin/emacsclient" ]; then
alias emacsclient="/Applications/Emacs.app/Contents/MacOS/bin/emacsclient"
fi
```
Setting the `EMACS` variable to the binary path seems to be a good idea, as some
tools seems to use it to figure out the path to Emacs' executable, including
[doom-emacs](https://github.com/hlissner/doom-emacs)' `doom` CLI tool.
## Native-Comp
_Note: On 2021-04-25 the `feature/native-comp` branch was
[merged](http://git.savannah.gnu.org/cgit/emacs.git/commit/?id=289000eee729689b0cf362a21baa40ac7f9506f6)
into `master`._
The build script will automatically detect if the source tree being built
supports native-compilation, and enable it if available. You can override this
to force it on/off by passing `--native-comp` or `--no-native-comp`
respectfully.
By default `NATIVE_FULL_AOT` is disabled which ensures a fast build by native
compiling as few elisp source files as possible to build Emacs itself. Any
remaining elisp files will be dynamically compiled in the background the first
time they are used.
To enable native full Ahead-of-Time compilation, pass in the `--native-full-aot`
option, which will native-compile all of Emacs' elisp as built-time. On my
machine it takes around 10 minutes to build Emacs.app with `NATIVE_FULL_AOT`
disabled, and around 20-25 minutes with it enabled.
### Configuration
Add the following near the top of your `early-init.el` or `init.el`:
```elisp
(setq comp-speed 2)
```
By default natively compiled `*.eln` files will be cached in
`~/.emacs.d/eln-cache/`. If you want to customize that, simply set a new path as
the first element of the `comp-eln-load-path` variable. The path string must end
with a `/`.
Below is an example which stores all compiled `*.eln` files in `cache/eln-cache`
within your Emacs configuration directory:
```elisp
(when (boundp 'comp-eln-load-path)
(setcar comp-eln-load-path
(expand-file-name "cache/eln-cache/" user-emacs-directory)))
```
### Issues
Please see all issues with the
[`native-comp`](https://github.com/jimeh/build-emacs-for-macos/issues?q=is%3Aissue+is%3Aopen+label%3Anative-comp)
label. It's a good idea if you read through them so you're familiar with the
types of issues and or behavior you can expect.
### Known Good Commits/Builds
A list of known "good" commits which produce working builds is tracked in:
[#6 Known good commits of feature/native-comp branch](https://github.com/jimeh/build-emacs-for-macos/issues/6)
## Credits
- I've borrowed some ideas from [David Caldwell](https://github.com/caldwell)'s
excellent [build-emacs](https://github.com/caldwell/build-emacs) project,
which produces all builds for
[emacsformacosx.com](https://emacsformacosx.com).
- Patches applied are pulled from
[emacs-plus](https://github.com/d12frosted/homebrew-emacs-plus), which is an
excellent Homebrew formula with lots of options not available elsewhere.
- The following sources were extremely useful in figuring out how get get the
`feature/native-comp` branch building on macOS:
- https://gist.github.com/mikroskeem/0a5c909c1880408adf732ceba6d3f9ab#1-gcc-with-libgccjit-enabled
- https://github.com/shshkn/emacs.d/blob/master/docs/nativecomp.md
- https://gist.github.com/AllenDang/f019593e65572a8e0aefc96058a2d23e
## Internals
The script downloads the source code as a gzipped tar archive from the
[GitHub mirror](https://github.com/emacs-mirror/emacs) repository, as it makes
it very easy to get a tarball of any given git reference.
It then runs `./configure` with a various options, including copying various
dynamic libraries into the application itself. So the built application should
in theory run on a macOS install that does not have Homebrew, or does not have
the relevant Homebrew formulas installed.
Code quality of the script itself, is well, non-existent. The build script
started life a super-quick hack back in 2013, and now it's even more of a dirty
hack. I might clean it up and add unit tests if I end up relying on this script
for a prolonged period of time. For now I plan to use it at least until
native-comp lands in a stable Emacs release for macOS.
## License
[CC0 1.0 Universal](http://creativecommons.org/publicdomain/zero/1.0/)