#!/usr/bin/env sh :; set -e # -*- mode: emacs-lisp; lexical-binding: t -*- :; case "$EMACS" in *term*) EMACS=emacs ;; *) EMACS="${EMACS:-emacs}" ;; esac :; $EMACS --version >/dev/null 2>&1 || { >&2 echo "Can't find emacs in your PATH"; exit 1; } :; $EMACS --no-site-file --script "$0" -- "$@" || __DOOMCODE=$? :; [ "${__DOOMCODE:-0}" -eq 128 ] && { sh "`$EMACS -Q --batch --eval '(princ temporary-file-directory)'`/doom.sh" "$0" "$@" && true; __DOOMCODE=$?; } :; exit $__DOOMCODE ;; The garbage collector isn't as important during CLI ops. A higher threshold ;; makes it 15-30% faster, but set it too high and we risk runaway 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. Use the EMACSDIR envvar to change this. (setq user-emacs-directory (if (getenv-internal "EMACSDIR") (file-name-as-directory (expand-file-name (getenv-internal "EMACSDIR"))) (expand-file-name "../" (file-name-directory (file-truename load-file-name))))) ;; ;;; Sanity checks (when (version< emacs-version "27.1") (error (concat "Detected Emacs " emacs-version " (at " (car command-line-args) ").\n\n" "Doom only supports Emacs 27.1 and newer. A guide to install a newer version\n" "of Emacs can be found at:\n\n " (format "https://doomemacs.org/docs/getting_started.org#%s\n" (cond ((eq system-type 'darwin) "on-macos") ((memq system-type '(cygwin windows-nt ms-dos)) "on-windows") ("on-linux"))) "Aborting..."))) (unless (file-readable-p (expand-file-name "core/core.el" user-emacs-directory)) (error (concat "Couldn't find or read '" (abbreviate-file-name (expand-file-name "core/core.el" user-emacs-directory)) "'.\n\n" "Are you sure Doom Emacs is correctly installed?\n\n" (when (file-symlink-p load-file-name) (concat "This error can occur if you've symlinked the 'doom' script, which Doom does not\n" "support. Consider symlinking its parent directory instead or explicitly set the\n" "EMACSDIR environment variable, e.g.\n\n " (if (string-match-p "/fish$" (getenv "SHELL")) "env EMACSDIR=~/.emacs.d doom" "EMACSDIR=~/.emacs.d doom sync")) "\n\n") "Aborting..."))) (when (equal (user-real-uid) 0) ;; If ~/.emacs.d is owned by root, assume the user genuinely wants root to be ;; their primary user. (unless (= 0 (file-attribute-user-id (file-attributes user-emacs-directory))) (error (concat "Do not run this script as root. It will cause file permissions errors later.\n\n" "To carry on anyway, change the owner of your Emacs config to root:\n\n" " chown root:root -R " (abbreviate-file-name user-emacs-directory) "\n\n" "Aborting...")))) ;; ;;; Let 'er rip! ;; HACK Load `cl' and site files manually to prevent polluting logs and stdout ;; with deprecation and/or file load messages. (let ((inhibit-message t)) (require 'cl) (unless site-run-file (let ((site-run-file "site-start") (verbose (or (getenv "DEBUG") init-file-debug)) (tail load-path) (lispdir (expand-file-name "../lisp" data-directory)) dir) (while tail (setq dir (car tail)) (let ((default-directory dir)) (load (expand-file-name "subdirs.el") t (not verbose) t)) (or (string-prefix-p lispdir dir) (let ((default-directory dir)) (load (expand-file-name "leim-list.el") t (not verbose) t))) (setq tail (cdr tail))) (load site-run-file t (not verbose))))) ;; 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) (kill-emacs ;; Process the arguments passed to this script. `doom-cli-execute' should ;; return one of two things: a cons cell whose CAR is t, and CDR is the ;; command's return value OR one of: a keyword, command string, or command ;; list. (pcase (apply #'doom-cli-execute :doom (cdr (member "--" argv))) ;; If a CLI command returns an integer, treat it as an exit code. ((and (app car-safe `t) code) (if (integerp (cdr code)) (cdr code))) ;; CLI commands can do (throw 'exit SHELL-COMMAND) to run something after ;; this session ends. e.g. ;; ;; (throw 'exit "$@") or (throw 'exit :restart) ;; This reruns the current command with the same arguments. ;; (throw 'exit "$@ -h -c") ;; This reruns the current command with two new switches. ;; (throw 'exit "emacs -nw FILE") ;; Opens Emacs on FILE ;; (throw 'exit t) or (throw 'exit nil) ;; A safe way to simply abort back to the shell with exit code 0 ;; (throw 'exit 42) ;; Abort to shell with an explicit exit code (as a more abrupt ;; alternative to having the CLI command return 42). ;; ;; How this works: the command is written to a temporary shell script which ;; is executed after this session ends (see the shebang lines of this file). ;; It's done this way because Emacs' batch library lacks an implementation of ;; the exec system call. (command (cond ((integerp command) command) ((booleanp command) 0) ((let ((script (expand-file-name "doom.sh" temporary-file-directory)) (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) ((listp 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 (cl-set-difference process-environment (get 'process-environment 'initial-value) :test #'equal) 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 #o600) ;; Error code 128 is special: it means run the post-script after this ;; session ends. 128))))))