index all rss twitter github linkedin email

Álvaro Ramírez

16 August 2020 Trying out gccemacs on macOS

UPDATE: I'm no longer using these steps. See Emacs plus –with-native-comp for an easier alternative.

Below are the instructions I use to build Andrea Corallo's gccemacs on macOS. It is based on Allen Dang's handy instructions plus some changes of my own.

Install gcc and libgccjit via homebrew

brew install gcc libgccjit

Save configure script

Create configure-gccemacs.sh

#!/bin/bash

set -o nounset
set -o errexit

# Configures Emacs for building native comp support
# http://akrl.sdf.org/gccemacs.html

readonly GCC_DIR="$(realpath $(brew --prefix libgccjit))"
[[ -d $GCC_DIR ]] ||  { echo "${GCC_DIR} not found"; exit 1; }

readonly SED_DIR="$(realpath $(brew --prefix gnu-sed))"
[[ -d $SED_DIR ]] ||  { echo "${SED_DIR} not found"; exit 1; }

readonly GCC_INCLUDE_DIR=${GCC_DIR}/include
[[ -d $GCC_INCLUDE_DIR ]] ||  { echo "${GCC_INCLUDE_DIR} not found"; exit 1; }

readonly GCC_LIB_DIR=${GCC_DIR}/lib/gcc/10
[[ -d $GCC_LIB_DIR ]] ||  { echo "${GCC_LIB_DIR} not found"; exit 1; }

export PATH="${SED_DIR}/libexec/gnubin:${PATH}"
export CFLAGS="-O2 -I${GCC_INCLUDE_DIR}"
export LDFLAGS="-L${GCC_LIB_DIR} -I${GCC_INCLUDE_DIR}"
export LD_LIBRARY_PATH="${GCC_LIB_DIR}"
export DYLD_FALLBACK_LIBRARY_PATH="${GCC_LIB_DIR}"

echo "Environment"
echo "-----------"
echo PATH: $PATH
echo CFLAGS: $CFLAGS
echo LDFLAGS: $LDFLAGS
echo DYLD_FALLBACK_LIBRARY_PATH: $DYLD_FALLBACK_LIBRARY_PATH
echo "-----------"

./autogen.sh

./configure \
     --prefix="$PWD/nextstep/Emacs.app/Contents/MacOS" \
     --enable-locallisppath="${PWD}/nextstep/Emacs.app/Contents/MacOS" \
     --with-mailutils \
     --with-ns \
     --with-imagemagick \
     --with-cairo \
     --with-modules \
     --with-xml2 \
     --with-gnutls \
     --with-json \
     --with-rsvg \
     --with-native-compilation \
     --disable-silent-rules \
     --disable-ns-self-contained \
     --without-dbus

Make it executable

chmod +x configure-gccemacs.sh

Clone Emacs source

git clone --branch master https://github.com/emacs-mirror/emacs gccemacs

Configure build

cd gccemacs
../configure-gccemacs.sh

Native lisp compiler found?

Verify native lisp compiler is found:

Does Emacs have native lisp compiler?                   yes

Build

Put those cores to use. Find out how many you got with:

sysctl hw.logicalcpu
hw.logicalcpu: 4

Ok so build with:

make -j4 NATIVE_FAST_BOOT=1
cp -r lisp nextstep/Emacs.app/Contents/Resources/
cp -r native-lisp nextstep/Emacs.app/Contents
make install

Note: Using NATIVE_FAST_BOOT=1 significantly improves build time (totalling between 20-30 mins, depending on your specs). Without it, the build can take hours.

The macOS app build (under nextstep/Emacs.app) is ready, but read on before launching.

Remove ~/emacs.d

You likely want to start with a clean install, byte-compiling all packages with the latest Emacs version. In any case, rename ~/emacs.d (for backup?) or remove ~/emacs.d.

init.el config

Ensure exec-path includes the script's "–prefix=" value, LIBRARY_PATH points to gcc's lib dir, and finally set comp-deferred-compilation. I wrapped the snippet in my exec-path-from-shell config, but setting early in init.el should be enough.

(use-package exec-path-from-shell
  :ensure t
  :config
  (exec-path-from-shell-initialize)
  (if (and (fboundp 'native-comp-available-p)
           (native-comp-available-p))
      (progn
        (message "Native comp is available")
        ;; Using Emacs.app/Contents/MacOS/bin since it was compiled with
        ;; ./configure --prefix="$PWD/nextstep/Emacs.app/Contents/MacOS"
        (add-to-list 'exec-path (concat invocation-directory "bin") t)
        (setenv "LIBRARY_PATH" (concat (getenv "LIBRARY_PATH")
                                       (when (getenv "LIBRARY_PATH")
                                         ":")
                                       ;; This is where Homebrew puts gcc libraries.
                                       (car (file-expand-wildcards
                                             (expand-file-name "~/homebrew/opt/gcc/lib/gcc/*")))))
        ;; Only set after LIBRARY_PATH can find gcc libraries.
        (setq comp-deferred-compilation t))
    (message "Native comp is *not* available")))

Launch Emacs.app

You're good to go. Open Emacs.app via finder or shell:

open nextstep/Emacs.app

Deferred compilation logs

After setting comp-deferred-compilation (in init.el config section), .elc files should be asyncronously compiled. Function definition should be updated to native compiled equivalent.

Look out for an Async-native-compile-log buffer. Should have content like:

Compiling .emacs.d/elpa/moody-20200514.1946/moody.el...
Compiling .emacs.d/elpa/minions-20200522.1052/minions.el...
Compiling .emacs.d/elpa/persistent-scratch-20190922.1046/persistent-scratch.el...
Compiling .emacs.d/elpa/which-key-20200721.1927/which-key.el...
...

Can also check for .eln files:

find ~/.emacs.d -iname *.eln | wc -l
149

UPDATE1: Added Symlink Emacs.app/Contents/eln-cache section for update 11.

UPDATE2: Noted using NATIVE_FAST_BOOT makes the build much faster.

UPDATE3: Removed symlinks and copied content instead. This simplifies things. Inspired by Ian Wahbe's build-emacs.sh.

UPDATE4: Removed homebrew recipe patching. Thanks to Dmitry Shishkin's instructions.

UPDATE5: Use new flag –with-native-compilation and master branch.