diff options
| author | Craig Jennings <c@cjennings.net> | 2025-09-05 13:30:07 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-09-05 13:30:07 -0500 |
| commit | 9a64fe7f29289d074d3dd51f75ac1c2912cca22c (patch) | |
| tree | 8378cb18d3cbf4b3833d5f12c8b7e04839569cca /scripts | |
| parent | 419111900eb6089516aa2c1a6d3aa6a9ac0c0d1f (diff) | |
| download | dotemacs-9a64fe7f29289d074d3dd51f75ac1c2912cca22c.tar.gz dotemacs-9a64fe7f29289d074d3dd51f75ac1c2912cca22c.zip | |
refactor(build): Streamline and modernize Emacs build script
The script has been redesigned with clearer organization, better
error handling, and more intelligent defaults. Key improvements
include auto-detection for Wayland, better parallel build support,
and more robust git operations.
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/build-emacs.sh | 180 |
1 files changed, 75 insertions, 105 deletions
diff --git a/scripts/build-emacs.sh b/scripts/build-emacs.sh index c5c92b96..5ac2d705 100755 --- a/scripts/build-emacs.sh +++ b/scripts/build-emacs.sh @@ -1,119 +1,103 @@ #!/usr/bin/env bash # Craig Jennings <c@cjennings.net> # Build & install Emacs from source -# safe, versioned, user-local, blunt, fast, reversible. +# safe, user-local, fast, versioned. -set -euo pipefail +set -Eeuo pipefail +IFS=$'\n\t' -### --------- CONFIG (edit these) --------- -# Where the git checkout lives: +# ---------- Config (override via env) ---------- SRC_DIR="${SRC_DIR:-$HOME/code/emacs-src}" - -# Which repo/branch/tag to build: EMACS_REPO="${EMACS_REPO:-https://git.savannah.gnu.org/git/emacs.git}" -CHECKOUT_REF="${CHECKOUT_REF:-emacs-29.2}" # e.g. "emacs-30.1", "master", "emacs-29" - -# Install prefix (keeps versions side-by-side): -PREFIX_BASE="${PREFIX_BASE:-$HOME/.local/opt}" +CHECKOUT_REF="${CHECKOUT_REF:-emacs-30.2}" +PREFIX_BASE="${PREFIX_BASE:-$HOME/.local/src/emacs}" PREFIX="${PREFIX:-$PREFIX_BASE/${CHECKOUT_REF}}" - -# Optional knobs: -ENABLE_NATIVE="${ENABLE_NATIVE:-0}" # 1 to enable native-comp if toolchain supports it -ENABLE_PGTK="${ENABLE_PGTK:-0}" # 1 to use pgtk (Wayland-only build) -ENABLE_XWIDGETS="${ENABLE_XWIDGETS:-0}" # 1 to enable xwidgets (known flaky on some 29/30 tags) -MAKE_JOBS="${MAKE_JOBS:-$(command -v nproc >/dev/null 2>&1 && nproc || sysctl -n hw.ncpu || echo 4)}" - -# CFLAGS (tune lightly; keep reproducible): -CFLAGS_OVERRIDES="${CFLAGS_OVERRIDES:--O2 -march=native}" - -LOG_DIR="${LOG_DIR:-$HOME/.cache/build-logs}" -mkdir -p "$LOG_DIR" -LOGFILE="$LOG_DIR/emacs-build-$(date +%Y%m%d-%H%M%S)-${CHECKOUT_REF}.log" -### --------------------------------------- - -say() { printf '%s\n' "$*" | tee -a "$LOGFILE" ; } +LOG_DIR="${LOG_DIR:-$HOME/.cache/emacs-build-logs}" +ENABLE_NATIVE="${ENABLE_NATIVE:-1}" +WITH_PGTK="${WITH_PGTK:-auto}" +JOBS="${JOBS:-auto}" +EXTRA_CONFIG="${EXTRA_CONFIG:-}" + +# ---------- Preflight ---------- +umask 022 +mkdir -p "$LOG_DIR" "$PREFIX_BASE" "$HOME/.local/bin" +: "${LOGFILE:=$LOG_DIR/emacs-build-$(date +%Y%m%d-%H%M%S)-${CHECKOUT_REF}.log}" + +say() { printf '>>> %s\n' "$*" | tee -a "$LOGFILE" ; } run() { say "+ $*"; eval "$@" >>"$LOGFILE" 2>&1; } +on_err(){ ec=$?; echo "ERROR [$ec] - see $LOGFILE" >&2; tail -n 80 "$LOGFILE" >&2 || true; exit "$ec"; } +trap on_err ERR -trap 'echo "ERROR: see $LOGFILE" >&2' ERR - -say ">>> Building Emacs" -say "SRC_DIR=$SRC_DIR" -say "CHECKOUT_REF=$CHECKOUT_REF" -say "PREFIX=$PREFIX" -say "LOGFILE=$LOGFILE" - -# Fetch or update source +# ---------- Clone/update ---------- if [[ -d "$SRC_DIR/.git" ]]; then - say "...updating existing checkout" - (cd "$SRC_DIR" && run "git fetch --tags --prune" && run "git status --porcelain") + run "git -C '$SRC_DIR' fetch --tags --prune" else - say "...cloning source" mkdir -p "$(dirname "$SRC_DIR")" - run "git clone --depth=1 --branch ${CHECKOUT_REF} ${EMACS_REPO} ${SRC_DIR}" || { - # If ref isn’t a branch, do full clone then checkout tag - run "git clone ${EMACS_REPO} ${SRC_DIR}" - } + run "git clone --origin origin '$EMACS_REPO' '$SRC_DIR'" fi -# Checkout desired ref/tag -( - cd "$SRC_DIR" - run "git fetch --tags --force" - run "git checkout ${CHECKOUT_REF}" -) +run "git -C '$SRC_DIR' reset --hard HEAD" +run "git -C '$SRC_DIR' clean -fdx" +run "git -C '$SRC_DIR' checkout -f '$CHECKOUT_REF'" +run "git -C '$SRC_DIR' submodule update --init --recursive || true" -# Autogen/bootstrap (needed for git checkouts) +# ---------- Autogen ---------- if [[ -x "$SRC_DIR/autogen.sh" ]]; then - say "...running autogen.sh" - (cd "$SRC_DIR" && run "./autogen.sh") + run "cd '$SRC_DIR' && ./autogen.sh" fi -# Configure flags +# ---------- Configure flags (ARRAY!) ---------- conf_flags=( "--prefix=${PREFIX}" "--with-json" "--with-modules" - "--with-cairo" - "--with-harfbuzz" - "--with-native-compilation=$( [[ $ENABLE_NATIVE -eq 1 ]] && echo yes || echo no )" - "--with-mailutils" - "--with-imagemagick" ) -# pgtk vs X -if [[ $ENABLE_PGTK -eq 1 ]]; then - conf_flags+=("--with-pgtk" "--without-x") +# Wayland/X choice +if [[ "$WITH_PGTK" == "auto" ]]; then + if [[ -n "${WAYLAND_DISPLAY:-}" ]]; then WITH_PGTK="yes"; else WITH_PGTK="no"; fi +fi +if [[ "$WITH_PGTK" == "yes" ]]; then + conf_flags+=("--with-pgtk") else - conf_flags+=("--with-x" "--with-x-toolkit=gtk3") + conf_flags+=("--with-x-toolkit=lucid") fi -# xwidgets if requested -if [[ $ENABLE_XWIDGETS -eq 1 ]]; then - conf_flags+=("--with-xwidgets") +# Native-compilation +if [[ "$ENABLE_NATIVE" == "1" ]]; then + conf_flags+=("--with-native-compilation=yes") +else + conf_flags+=("--with-native-compilation=no") fi -# Prefer system tree-sitter if present -if pkg-config --exists tree-sitter 2>/dev/null; then - conf_flags+=("--with-tree-sitter") +# Useful extras (disable later via EXTRA_CONFIG if missing deps) +conf_flags+=( + "--with-cairo" + "--with-harfbuzz" + "--with-tree-sitter" + "--with-imagemagick" + "--with-mailutils" +) + +# Optional extra flags from env +if [[ -n "$EXTRA_CONFIG" ]]; then + # shellcheck disable=SC2206 + conf_flags+=($EXTRA_CONFIG) fi -say "...configure flags:" -printf ' %s\n' "${conf_flags[@]}" | tee -a "$LOGFILE" +# ---------- Build & install ---------- +mkdir -p "$PREFIX" -# Configure -mkdir -p "$PREFIX" # ensure we can write there -( - cd "$SRC_DIR" - run "env CFLAGS='${CFLAGS_OVERRIDES}' ./configure ${conf_flags[*]}" -) +# Temporarily change IFS to space for configure argument expansion +old_ifs="$IFS" +IFS=' ' +run "cd '$SRC_DIR' && ./configure ${conf_flags[*]}" +IFS="$old_ifs" -# Build (and bootstrap if needed) -say "...compiling (jobs=$MAKE_JOBS)" -( - cd "$SRC_DIR" - # master sometimes needs bootstrap after significant changes; harmless otherwise: - run "make -j${MAKE_JOBS}" -) +if [[ "$JOBS" == "auto" ]]; then + if command -v nproc >/dev/null 2>&1; then JOBS=$(nproc); else JOBS=4; fi +fi +run "cd '$SRC_DIR' && make -j$JOBS" # Build documentation (info files) say "...building info files" @@ -122,33 +106,19 @@ say "...building info files" run "make info" ) -# Install to user-local prefix -say "...installing to ${PREFIX}" -( - cd "$SRC_DIR" - run "make install" - run "make install-info" -) +run "cd '$SRC_DIR' && make install" +run "cd '$SRC_DIR' && make install-info" -# ymlink all installed executables (emacs, emacsclient, etags, etc.) -mkdir -p "$HOME/.local/bin" - -# Atomic swap: build symlinks in a temp dir, then move them into place. +# ---------- Symlinks ---------- +run "ln -sfn '$PREFIX' '$PREFIX_BASE/emacs-current'" tmpdir="$(mktemp -d)" -for exe in "$PREFIX/bin/"*; do - [ -x "$exe" ] || continue - name="$(basename "$exe")" - ln -s "$exe" "$tmpdir/$name" -done - -# Move into ~/.local/bin (overwriting existing symlinks/files with same names) -for link in "$tmpdir"/*; do - name="$(basename "$link")" - mv -f "$link" "$HOME/.local/bin/$name" +for exe in "$PREFIX/bin/emacs" "$PREFIX/bin/emacsclient"; do + [[ -x "$exe" ]] || continue + ln -s "$exe" "$tmpdir/$(basename "$exe")" done +mv -f "$tmpdir"/* "$HOME/.local/bin/" || { echo "Failed to install shims to ~/.local/bin" >&2; exit 72; } rmdir "$tmpdir" -# Optional: quick sanity print command -v emacs >/dev/null && emacs --version | head -n1 || true command -v emacsclient >/dev/null && emacsclient --version | head -n1 || true - +echo "Done. See $LOGFILE" |
