doomemacs/bin/doom
Henrik Lissner e632871a11
core-cli: backport more refactors from rewrite
Still a long way to go, but this introduces a few niceties for
debugging CLI failures:

+ The (extended) output of the last bin/doom command is now logged to
  ~/.emacs.d/.local/doom.log
+ If an error occurs, short backtraces are displayed whether or not you
  have debug mode on. The full backtrace is written to
  ~/.emacs.d/.local/doom.error.log.
+ bin/doom now aborts with a warning if:
  - The script itself or its parent directory is a symlink. It's fine if
    ~/.emacs.d is symlinked though.
  - Running bin/doom as root when your DOOMDIR isn't in /root/.
  - If you're sporting Emacs 26.1 (now handled in the elisp side rather
    than the /bin/sh shebang preamble).
+ If a 'doom sync' was aborted prematurely, you'll be warned that Doom
  was left in an inconsistent state and that you must run `doom sync`
  again.

May address #3746
2020-08-24 23:00:32 -04:00

129 lines
6.2 KiB
Bash
Executable File

#!/usr/bin/env sh
:; set -e # -*- mode: emacs-lisp; lexical-binding: t -*-
:; ( echo "$EMACS" | grep -q "term" ) && EMACS=emacs || EMACS=${EMACS:-emacs}
:; command -v "$EMACS" >/dev/null || { >&2 echo "Can't find emacs in your PATH"; exit 1; }
:; export EMACSDIR="${EMACSDIR:-`dirname "$0"`/..}"
:; export __DOOMPOST="${TMPDIR:-/tmp}/doom.sh"
:; __DOOMCODE=0
:; "$EMACS" --no-site-file --script "$0" -- "$@" || __DOOMCODE=$?
:; [ $__DOOMCODE -eq 128 ] && { "$__DOOMPOST" "$0" "$@"; __DOOMCODE=$?; }
:; exit $__DOOMCODE
;; The garbage collector isn't important during CLI ops. A higher threshold
;; makes it 15-30% faster, but set it too high and we risk spiralling memory
;; usage in longer sessions.
(setq gc-cons-threshold 134217728) ; 128mb
;; Prioritize non-byte-compiled source files in non-interactive sessions to
;; prevent loading stale byte-code.
(setq load-prefer-newer t)
;; Ensure Doom runs out of this file's parent directory, where Doom is
;; presumably installed. EMACSDIR is set in the shell script preamble earlier in
;; this file.
(setq user-emacs-directory
(file-name-as-directory ; ensure the trailing slash...
(expand-file-name (or (getenv "EMACSDIR") ""))))
;; Handle some potential issues early
(when (version< emacs-version "26.1")
(error (concat "Detected Emacs %s (at %s).\n\n"
"Doom only supports Emacs 26.1 and newer. 27.1 is highly recommended. A guide\n"
"to install a newer version of Emacs can be found at:\n\n "
(cond ((eq system-type 'darwin)
"https://github.com/hlissner/doom-emacs/blob/develop/docs/getting_started.org#on-macos")
((memq system-type '(cygwin windows-nt ms-dos))
"https://github.com/hlissner/doom-emacs/blob/develop/docs/getting_started.org#on-windows")
("https://github.com/hlissner/doom-emacs/blob/develop/docs/getting_started.org#on-linux"))
"Aborting...")
emacs-version
(car command-line-args)))
(unless (file-exists-p (expand-file-name "core/core.el" user-emacs-directory))
(error (concat "Couldn't find Doom Emacs in %S.\n\n"
"This is likely because this script (or its parent directory) is a symlink.\n"
"If you must use a symlink, you'll need to specify an EMACSDIR so Doom knows\n"
"where to find itself. e.g.\n\n "
(if (string-match "/fish$" (getenv "SHELL"))
"env EMACSDIR=~/.emacs.d doom"
"EMACSDIR=~/.emacs.d doom sync")
"\n\n"
"Aborting...")
(abbreviate-file-name (file-truename user-emacs-directory))
(abbreviate-file-name load-file-name)))
(when (and (equal (user-real-uid) 0)
(not (file-in-directory-p user-emacs-directory "/root")))
(error (concat "This script is running as root. This likely wasn't intentional and\n"
"will cause file permissions errors later if this Doom install is\n"
"ever used on a non-root account.\n\n"
"Aborting...")))
;; Load the heart of the beast and its CLI processing library
(load (expand-file-name "core/core.el" user-emacs-directory) nil t)
(require 'core-cli)
;; Use our own home-grown debugger to display and log errors + backtraces.
;; Control over its formatting is important, because Emacs produces
;; difficult-to-read debug information otherwise. By making its errors more
;; presentable (and storing them somewhere users can access them later) we go a
;; long way toward making it easier for users to write better bug reports.
(setq debugger #'doom-cli--debugger
debug-on-error t
debug-ignored-errors nil)
;; HACK Load `cl' and site files manually to prevent polluting logs and stdout
;; with deprecation and/or file load messages.
(quiet! (if EMACS27+ (require 'cl))
(load "site-start" t t))
(kill-emacs
(pcase
;; Process the arguments passed to this script. `doom-cli-execute' should
;; return a boolean, integer (error code) or throw an 'exit event, which we
;; handle specially.
(apply #'doom-cli-execute :doom (cdr (member "--" argv)))
;; Any non-zero integer is treated as an error code.
((and (pred integerp) code) code)
;; If, instead, we were given a list or string, copy these as shell script
;; commands to a temp script file which this script will execute after this
;; session finishes. Also accepts special keywords, like `:restart', to rerun
;; the current command.
((and (or (pred consp)
(pred stringp)
(pred keywordp))
command)
(let ((script (doom-path (getenv "__DOOMPOST")))
(coding-system-for-write 'utf-8-unix)
(coding-system-for-read 'utf-8-unix))
(with-temp-file script
(insert "#!/usr/bin/env sh\n"
"_postscript() {\n"
" rm -f " (shell-quote-argument script) "\n "
(cond ((eq command :restart)
"$@")
((stringp command)
command)
((string-join
(if (listp (car-safe command))
(cl-loop for line in (doom-enlist command)
collect (mapconcat #'shell-quote-argument (remq nil line) " "))
(list (mapconcat #'shell-quote-argument (remq nil command) " ")))
"\n ")))
"\n}\n"
(save-match-data
(cl-loop for env in process-environment
if (string-match "^\\([a-zA-Z0-9_]+\\)=\\(.+\\)$" env)
concat (format "%s=%s \\\n"
(match-string 1 env)
(shell-quote-argument (match-string 2 env)))))
(format "PATH=\"%s%s$PATH\" \\\n" (concat doom-emacs-dir "bin/") path-separator)
"_postscript $@\n"))
(set-file-modes script #o700))
;; Error code 128 is special: it means run the post-script after this
;; session ends.
128)
;; Anything else (e.g. booleans) is treated as a successful run. Yes, a `nil'
;; indicates a successful run too!
(_ 0)))