Last active
May 20, 2025 09:02
-
-
Save abougouffa/4bcccc6d7dc34cf52e2877ca34445bc3 to your computer and use it in GitHub Desktop.
A helper script to build Emacs from source on Debian/Ubuntu
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# -*- sh-basic-offset: 2; tab-width: 2; -*- | |
# set -x | |
# Initially based on the emacs-git AUR package | |
## Build options | |
MINIMAL_BUILD= # Build Emacs with "--without-all", build with minimal external dependencies | |
USE_CLANG= # Use Clang instead of GCC. | |
LINK_TIME_OPTIMIZATION="YES" # Enable link-time optimization. | |
AOT_ELISP= # Compile all elisp files provided by upstream. | |
MAKE_TRAMPOLINES= # Compile jitted Elisp files with trampolines. | |
ALSA= # Linux sound support. | |
MAKE_DOCS_HTML= # Generate and install html documentation. | |
MAKE_DOCS_PDF= # Generate and install pdf documentation. You need a TeX installation. | |
EMACS_BRANCH="master" # Emacs' branch to be built. | |
UI="GTK3" # GTK3, PGTK, LUCID | |
XWIDGETS= # XWidgets support | |
PERFORMANCE_TWEAKS="YES" # Apply some performance related tweaks | |
MPS="YES" # Use the Memory Pool System (MPS) for garbage collection (use with the branch "feature/igc"). | |
emacs_system_make_depends=( | |
'git' | |
'gcc' | |
'autoconf' | |
) | |
# Maybe not complete list | |
debian_system_depends=( | |
'libgnutls28-dev' 'libxml2-dev' 'libharfbuzz-dev' 'texinfo' 'libxi-dev' | |
'libjpeg-dev' 'libpng-dev' 'giflib-tools' 'libgif-dev' 'libwebp-dev' 'libtiff-dev' 'libxpm-dev' | |
'libjansson-dev' # Native JSON support for Emacs 29, not needed for 30+ | |
'libgccjit-12-dev' # Native compile (28+) | |
'libsqlite3-dev' # SQLite | |
'libgpm-dev' # GPM | |
'libcairo2-dev' # Cairo | |
) | |
# Globally used paths ######################################################### | |
SRC_DIR="$PWD/emacs" | |
INSTALL_PREFIX="${HOME}/.local" | |
# Build and install some dependencies ######################################### | |
build_and_install_tree_sitter() { | |
# BUG: Emacs build doesn't seem to recognize the locally installed | |
# tree-sitter. For now, we need to run a classic "sudo make install" instead | |
if [[ ! -f "${INSTALL_PREFIX}/lib/libtree-sitter.so.0" ]]; then | |
if [[ ! -d tree-sitter ]]; then | |
git clone https://github.com/tree-sitter/tree-sitter.git | |
fi | |
cd tree-sitter || exit 1 | |
make clean | |
PREFIX="${INSTALL_PREFIX}" make | |
PREFIX="${INSTALL_PREFIX}" make install | |
fi | |
} | |
build_and_install_mps() { | |
if [[ ! -f "${INSTALL_PREFIX}/lib/libmps.a" ]] && [[ "${MPS}" == "YES" ]]; then | |
git clone https://github.com/Ravenbrook/mps.git | |
cd mps || exit 1 | |
./configure --prefix="${INSTALL_PREFIX}" | |
make install | |
fi | |
} | |
# Prepare the build environment ############################################### | |
if [[ "${USE_CLANG}" == "YES" ]]; then | |
export CC="/usr/bin/clang" | |
export CXX="/usr/bin/clang++" | |
export CPP="/usr/bin/clang -E" | |
export LD="/usr/bin/lld" | |
export AR="/usr/bin/llvm-ar" | |
export AS="/usr/bin/llvm-as" | |
export CCFLAGS+=' -fuse-ld=lld' | |
export CXXFLAGS+=' -fuse-ld=lld' | |
system_make_depends+=('clang' 'lld' 'llvm') | |
fi | |
if [[ "${UI}" == "LUCID" ]]; then | |
system_depends+=('dbus' 'hicolor-icon-theme' 'libxinerama-dev' 'libxfixes-dev' 'librsvg2-dev' 'xaw3dg-dev' 'libxrandr-dev' 'libxi-dev' 'libsm-dev' 'xcb' 'libxcb1-dev') | |
system_make_depends+=('x11proto-dev') | |
elif [[ "${UI}" == "GTK3" ]]; then | |
system_depends+=('libgtk-3-dev' 'libsm-dev' 'xcb' 'libxcb1-dev') | |
system_make_depends+=('x11proto-dev' 'libxi-dev') | |
elif [[ "${UI}" == "PGTK" ]]; then | |
system_depends+=('libgtk-3-dev' 'libsm-dev' 'xcb' 'libxcb1-dev') | |
system_make_depends+=('x11proto-dev' 'libxi-dev') | |
fi | |
if [[ "${XWIDGETS}" == "YES" ]]; then | |
system_make_depends+=('libwebkit2gtk-4.0-dev') | |
fi | |
if [[ "${ALSA}" == "YES" ]]; then | |
system_depends+=('alsa-lib') | |
fi | |
if [[ "${MAKE_DOCS_PDF}" == "YES" ]] && [[ ! -d '/usr/local/texlive' ]]; then | |
system_make_depends+=('texlive-core') | |
fi | |
emacs-install-dependencies() { | |
sudo apt install "${debian_system_depends[@]}" "${emacs_system_make_depends[@]}" | |
} | |
proxy-enable() { | |
if ping -c3 -W2 1.1.1.1 | grep -q "0 received"; then | |
echo "Cannot ping WAN, setting proxies" | |
export https_proxy=http://SOME_LOCAL_PROXY:8080/ | |
export http_proxy=http://SOME_LOCAL_PROXY:8080/ | |
export ftp_proxy=http://SOME_LOCAL_PROXY:8080/ | |
export no_proxy=localhost,127.0.0.1,.local,.SOME_LOCAL_TLD | |
fi | |
} | |
# There is no need to run autogen.sh after first checkout. | |
# Doing so, breaks incremental compilation. | |
emacs-prepare() { | |
# proxy-enable | |
if [[ ! -d "${SRC_DIR}" ]]; then | |
git clone https://github.com/emacs-mirror/emacs.git "${SRC_DIR}" -b "${EMACS_BRANCH}" | |
else | |
cd "${SRC_DIR}" || exit 1 | |
git stash --include-untracked | |
git fetch origin | |
git checkout "${EMACS_BRANCH}" | |
fi | |
[[ -d "${SRC_DIR}/build" ]] && rm -rf "${SRC_DIR}/build" | |
[[ -x configure ]] || (./autogen.sh git && ./autogen.sh autoconf) | |
mkdir -p "${SRC_DIR}/build" | |
} | |
emacs-check() { | |
cd "${SRC_DIR}/build" || exit 1 | |
make check | |
} | |
emacs-build() { | |
cd "${SRC_DIR}/build" || exit 1 | |
local config_options=( | |
--prefix="${INSTALL_PREFIX}" | |
--sysconfdir=/etc | |
--libexecdir="${INSTALL_PREFIX}/lib" | |
--localstatedir="${INSTALL_PREFIX}/var" | |
--mandir="${INSTALL_PREFIX}/share/man" | |
# ctags/etags may be provided by other packages, e.g, universal-ctags | |
--program-transform-name='s/\([ec]tags\)/\1.emacs/' | |
) | |
if [[ "$MINIMAL_BUILD" == "YES" ]]; then | |
config_options+=('--without-all') | |
else | |
config_options+=( | |
"--prefix=${INSTALL_PREFIX}" | |
"--sysconfdir=/etc" | |
"--libexecdir=${INSTALL_PREFIX}/lib" | |
"--localstatedir=${INSTALL_PREFIX}/var" | |
"--mandir=${INSTALL_PREFIX}/share/man" | |
"--with-tree-sitter=ifavailable" | |
"--without-compress-install" | |
# "--with-native-compilation" # No need for this on Emacs 30, it is the default if libgccjit is available | |
# "--with-xinput2" | |
# "--with-gnutls=ifavailable" | |
# "--with-modules" | |
# "--without-libotf" | |
"--without-m17n-flt" | |
# Beware https://debbugs.gnu.org/cgi/bugreport.cgi?bug=25228 | |
# dconf and gconf break font settings you set in ~/.emacs. | |
# If you insist you'll need to read that bug report in *full*. | |
# Good luck! | |
"--without-gconf" | |
) | |
if [[ "${PERFORMANCE_TWEAKS}" == "YES" ]]; then | |
# This disables the GC mark trace buffer for about 5% better garbage collection performance | |
config_options+=('--disable-gc-mark-trace') | |
# Compile with the best optimizations for the current machine | |
export CFLAGS+="-O2 -pipe -march=native -mtune=native -fomit-frame-pointer" | |
fi | |
if [[ "${USE_CLANG}" == "YES" ]]; then | |
config_options+=('--enable-autodepend') | |
fi | |
if [[ "${LINK_TIME_OPTIMIZATION}" == "YES" ]]; then | |
config_options+=('--enable-link-time-optimization') | |
fi | |
if [[ "${AOT_ELISP}" == "YES" ]]; then | |
config_options+=('--with-native-compilation=aot') | |
else | |
config_options+=('--with-native-compilation') | |
fi | |
if [[ "${UI}" == "LUCID" ]]; then | |
config_options+=('--with-x-toolkit=lucid' '--with-xft' '--with-xaw3d' '--without-cairo') | |
elif [[ "${UI}" == "GTK3" ]]; then | |
config_options+=('--with-x-toolkit=gtk3' '--without-xaw3d') | |
elif [[ "${UI}" == "PGTK" ]]; then | |
config_options+=('--with-pgtk' '--without-xaw3d' '--without-gsettings') | |
fi | |
if [[ "${XWIDGETS}" == "YES" ]]; then | |
config_options+=('--with-xwidgets') | |
fi | |
if [[ "${ALSA}" == "YES" ]]; then | |
config_options+=('--with-sound=alsa') | |
else | |
config_options+=('--with-sound=no') | |
fi | |
if [[ "${MPS}" == "YES" ]]; then | |
config_options+=('--with-mps') | |
fi | |
fi | |
# =================================================================== | |
# To build Emacs with locally installed MPS/Treesitter libraries, you need to create: | |
# ~/.local/share/config.site with the following content: | |
# CPPFLAGS=-I$HOME/.local/include | |
# LDFLAGS=-L$HOME/.local/lib | |
if [[ ! -f "${INSTALL_PREFIX}/share/config.site" ]]; then | |
mkdir -p "${INSTALL_PREFIX}/share/" | |
echo "CPPFLAGS=-I${INSTALL_PREFIX}/include" >>"${INSTALL_PREFIX}/share/config.site" | |
echo "LDFLAGS=-L${INSTALL_PREFIX}/lib" >>"${INSTALL_PREFIX}/share/config.site" | |
fi | |
../configure "${config_options[@]}" | |
make bootstrap | |
# ------------------------------------------------------------------- | |
# Or use "make" rather than "make bootstrap": | |
# > make | |
# Using "make" instead of "make bootstrap" enables incremental | |
# compiling. Less time recompiling. Yay! But you may | |
# need to use bootstrap sometimes to unbreak the build. | |
# Just add it to the command line. | |
# | |
# Please note that incremental compilation implies that you | |
# are reusing your src directory! | |
# | |
# You may need to run this if 'loaddefs.el' files become corrupt. | |
# cd "${SRC_DIR}/lisp" | |
# make autoloads | |
# cd ../build | |
# ------------------------------------------------------------------- | |
if [[ "${MAKE_TRAMPOLINES}" == "YES" ]]; then | |
make trampolines | |
fi | |
# Optional documentation formats. | |
if [[ "${MAKE_DOCS_HTML}" == "YES" ]]; then | |
make html | |
fi | |
if [[ "${MAKE_DOCS_PDF}" == "YES" ]]; then | |
make pdf | |
fi | |
} | |
emacs-install() { | |
cd "${SRC_DIR}/build" || exit 1 | |
make PREFIX="${INSTALL_PREFIX}/" install | |
# Install Emacs terminal command | |
if [[ ! -f "${INSTALL_PREFIX}/bin/nemacs" ]]; then | |
printf '#!/usr/bin/bash -e\n\n[[ -x $HOME/.local/bin/emacs ]] && $HOME/.local/bin/emacs -nw $*' >"${INSTALL_PREFIX}/bin/nemacs" | |
chmod a+x "${INSTALL_PREFIX}/bin/nemacs" | |
fi | |
# Install optional documentation formats | |
if [[ $MAKE_DOCS_HTML == "YES" ]]; then make PREFIX="${INSTALL_PREFIX}/" install-html; fi | |
if [[ $MAKE_DOCS_PDF == "YES" ]]; then make PREFIX="${INSTALL_PREFIX}/" install-pdf; fi | |
} | |
emacs-build-and-install() { | |
echo "Preparing the dependencies" | |
build_and_install_tree_sitter | |
build_and_install_mps | |
echo "Preparing the build" | |
emacs-prepare | |
echo "Building Emacs" | |
emacs-build | |
echo "Installing Emacs" | |
emacs-install | |
} | |
emacs-build-and-install |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment