diff options
| author | Craig Jennings <c@cjennings.net> | 2025-09-04 11:34:20 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-09-04 11:35:04 -0500 |
| commit | 19c3d4474001af15c6fdc4ebb62a003fb135de9c (patch) | |
| tree | 694c532b7f605a90a97c9408418503bf0e05c63a /dotfiles | |
| parent | e0e9cd113b4b1fc82dab18a371c54e533789f507 (diff) | |
working build-emacs.sh script
Diffstat (limited to 'dotfiles')
| -rwxr-xr-x | dotfiles/system/.local/bin/build-emacs.sh | 179 |
1 files changed, 74 insertions, 105 deletions
diff --git a/dotfiles/system/.local/bin/build-emacs.sh b/dotfiles/system/.local/bin/build-emacs.sh index f3f0f9b..a73e996 100755 --- a/dotfiles/system/.local/bin/build-emacs.sh +++ b/dotfiles/system/.local/bin/build-emacs.sh @@ -1,146 +1,115 @@ #!/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}" -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}" - +CHECKOUT_REF="${CHECKOUT_REF:-emacs-30.2}" +PREFIX_BASE="${PREFIX_BASE:-$HOME/.local/src}" +PREFIX="${PREFIX:-$PREFIX_BASE/emacs-${CHECKOUT_REF}}" 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" ; } -run() { say "+ $*"; eval "$@" >>"$LOGFILE" 2>&1; } +ENABLE_NATIVE="${ENABLE_NATIVE:-1}" +WITH_PGTK="${WITH_PGTK:-auto}" +JOBS="${JOBS:-auto}" +EXTRA_CONFIG="${EXTRA_CONFIG:-}" -trap 'echo "ERROR: see $LOGFILE" >&2' ERR +# ---------- 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 ">>> Building Emacs" -say "SRC_DIR=$SRC_DIR" -say "CHECKOUT_REF=$CHECKOUT_REF" -say "PREFIX=$PREFIX" -say "LOGFILE=$LOGFILE" +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 -# 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") -else - conf_flags+=("--with-x" "--with-x-toolkit=gtk3") +# Wayland/X choice +if [[ "$WITH_PGTK" == "auto" ]]; then + if [[ -n "${WAYLAND_DISPLAY:-}" ]]; then WITH_PGTK="yes"; else WITH_PGTK="no"; fi fi - -# xwidgets if requested -if [[ $ENABLE_XWIDGETS -eq 1 ]]; then - conf_flags+=("--with-xwidgets") +if [[ "$WITH_PGTK" == "yes" ]]; then + conf_flags+=("--with-pgtk") +else + conf_flags+=("--with-x-toolkit=lucid") fi -# Prefer system tree-sitter if present -if pkg-config --exists tree-sitter 2>/dev/null; then - conf_flags+=("--with-tree-sitter") +# Native-compilation +if [[ "$ENABLE_NATIVE" == "1" ]]; then + conf_flags+=("--with-native-compilation=yes") +else + conf_flags+=("--with-native-compilation=no") fi -say "...configure flags:" -printf ' %s\n' "${conf_flags[@]}" | tee -a "$LOGFILE" - -# Configure -mkdir -p "$PREFIX" # ensure we can write there -( - cd "$SRC_DIR" - run "env CFLAGS='${CFLAGS_OVERRIDES}' ./configure ${conf_flags[*]}" -) - -# 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}" +# Useful extras (disable later via EXTRA_CONFIG if missing deps) +conf_flags+=( + "--with-cairo" + "--with-harfbuzz" + "--with-tree-sitter" + "--with-imagemagick" + "--with-mailutils" ) -# Install to user-local prefix -say "...installing to ${PREFIX}" -( - cd "$SRC_DIR" - run "make install" -) +# Optional extra flags from env +if [[ -n "$EXTRA_CONFIG" ]]; then + # shellcheck disable=SC2206 + conf_flags+=($EXTRA_CONFIG) +fi -# ymlink all installed executables (emacs, emacsclient, etags, etc.) -mkdir -p "$HOME/.local/bin" +# ---------- Build & install ---------- +mkdir -p "$PREFIX" +# Temporarily change IFS to space for configure argument expansion +old_ifs="$IFS" +IFS=' ' +run "cd '$SRC_DIR' && ./configure ${conf_flags[*]}" +IFS="$old_ifs" +# If you prefer strict array expansion (safer with spaces in flags), use: ./configure "${conf_flags[@]}" + +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" +run "cd '$SRC_DIR' && make install" -# 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" |
