diff --git a/Makefile b/Makefile index b9ac79b32..9623cc824 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,18 @@ clean: clean-cache: @$(EMACS) -f 'doom/clean-cache' + +# Syntactic sugar for bootstrapping modules. Allows: make bootstrap javascript +# See doom/bootstrap for more information. +ifeq (bootstrap,$(firstword $(MAKECMDGOALS))) + ARGV := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) + $(eval $(ARGV):;@:) +endif + +bootstrap: init.el + @$(EMACS) -f 'doom-initialize-autoloads' --eval "(doom/bootstrap '($(ARGV)))" + + # This is only useful if your emacs.d is somewhere other than ~/.emacs.d (for # development purposes for instance). run: @@ -35,4 +47,5 @@ run: init.el: @[ -e init.el ] || $(error No init.el file; create one or copy init.example.el) -.PHONY: all test + +.PHONY: all test bootstrap diff --git a/core/autoload/bootstrap.el b/core/autoload/bootstrap.el new file mode 100644 index 000000000..7422ccbe0 --- /dev/null +++ b/core/autoload/bootstrap.el @@ -0,0 +1,46 @@ +;;; ../core/autoload/bootstrap.el + +(defvar doom-bootstraps nil + "TODO") + +;;;###autoload +(defmacro def-bootstrap! (name &rest forms) + (declare (indent defun)) + `(push (cons ',name + (lambda () + (cl-flet ((sh (lambda (&rest args) (apply 'doom-sh args))) + (sh& (lambda (&rest args) (apply 'doom-async-sh args))) + (sudo (lambda (&rest args) (apply 'doom-sudo args))) + (fetch (lambda (&rest args) (apply 'doom-fetch args))) + (message (lambda (&rest args) + (apply 'message (format "[%s] %s" ,(symbol-name name) (car args)) + (cdr args))))) + (with-demoted-errors "BOOTSTRAP ERROR: %s" + ,@forms)))) + doom-bootstraps)) + +;;;###autoload +(defun doom/bootstrap (ids) + "Bootstraps a module, if it has a bootstrapper. Bootstraps are expected to be +recipes for setting up the external dependencies of a module by, for instance, +using the OS package manager to install them, or retrieving them from a repo +using `doom-fetch'." + (interactive + (list (list (completing-read "Bootstrap: " (mapcar 'car doom-bootstraps) nil t)))) + (doom-initialize-packages t) + ;; Error out if any of the bootstraps don't exist or aren't valid functions. + ;; If something goes wrong, it's likely we don't want to continue. + (let ((err-not-found (cl-remove-if (lambda (id) (assq id doom-bootstraps)) ids)) + (err-not-func (cl-remove-if (lambda (id) (functionp (cdr (assq id doom-bootstraps)))) ids))) + (when (or (and err-not-found + (message "ERROR: These bootstraps don't exist: %s" err-not-found)) + (and err-not-func + (message "ERROR: These bootstraps were invalid: %s" err-not-func))) + (error "There were errors. Aborting."))) + (dolist (id ids) + (let ((bootstrap (assq id doom-bootstraps))) + (message "[%s] BOOTSTRAP START" id) + (with-demoted-errors (format "[%s] ERROR: %%s" id) + (unless (funcall (cdr bootstrap)) + (message "[%s] DONE (already bootstrapped)" id)))))) + diff --git a/core/autoload/system.el b/core/autoload/system.el index f46ef3324..840c03899 100644 --- a/core/autoload/system.el +++ b/core/autoload/system.el @@ -18,19 +18,21 @@ (t (error "Unknown OS: %s" system-type))))) ;;;###autoload -(defun doom-sh (&rest args) +(defun doom-sh (command &rest args) "Runs a shell command and prints any output to the DOOM buffer." - (error "doom-sh not implemented yet")) + (if (equal (car (split-string command " ")) "sudo") + (apply 'doom-sudo command args) + (princ (shell-command-to-string (apply 'format command args))))) ;;;###autoload -(defun doom-async-sh (&rest args) - "Like `doom-sh', but runs command asynchronously." - (error "doom-async-sh not implemented yet")) - -;;;###autoload -(defun doom-sudo (&rest args) +(defun doom-sudo (command &rest args) "Like `doom-sh', but runs as root (prompts for password)." - (error "doom-sudo not implemented yet")) + (let ((tramp-verbose 2) + (buf (get-buffer-create "*sudo*"))) + (with-current-buffer buf + (unless (string-prefix-p "/sudo::/" default-directory) + (cd "/sudo::/")) + (apply 'doom-sh command args)))) ;;;###autoload (defun doom-fetch (fetcher location dest) @@ -59,3 +61,4 @@ etc." (when (featurep 'evil) (evil-change-state 'normal)) (set-buffer-modified-p nil)))))) + diff --git a/modules/lang/go/packages.el b/modules/lang/go/packages.el index 9a2cc0316..c841c1b5d 100644 --- a/modules/lang/go/packages.el +++ b/modules/lang/go/packages.el @@ -8,3 +8,29 @@ (when (featurep! :completion company) (package! company-go)) + +;; +(def-bootstrap! go + (let ((gobin (executable-find "go")) + (gopath (getenv "GOPATH")) + changed) + (unless gobin + (pcase (doom-system-os) + ('arch + (sudo "pacman --noconfirm -S go")) + ('debian) ;; TODO + ('macos + (sh "brew install go"))) + (unless (executable-find "go") + (error "Go isn't installed (%s)" gobin))) + (unless (file-directory-p gopath) + (error "GOPATH isn't set up (%s)" gopath)) + (mapc (lambda (url) + (unless (file-directory-p (expand-file-name (concat "src/" url) gopath)) + (sh "%s get -u '%s'" gobin url) + (setq changed t))) + '("github.com/nsf/gocode" + "github.com/motemen/gore" + "golang.org/x/tools/cmd/guru" + "golang.org/x/tools/cmd/gorename")) + (or changed (not gobin)))) diff --git a/modules/lang/javascript/packages.el b/modules/lang/javascript/packages.el index 39bf89213..d20fe8ef8 100644 --- a/modules/lang/javascript/packages.el +++ b/modules/lang/javascript/packages.el @@ -14,3 +14,23 @@ (when (featurep! :feature jump) (package! xref-js2)) +;; +(def-bootstrap! javascript + (unless (cl-every 'executable-find '("node" "npm" "tern")) + (pcase (doom-system-os) + ('arch + (let (progs) + (unless (executable-find "node") (push "nodejs" progs)) + (unless (executable-find "npm") (push "npm" progs)) + (when progs + (sudo "pacman --noconfirm -S %s" progs)))) + ('debian) ;; TODO + ('macos + (unless (executable-find "node") + (sh "brew install node")))) + (unless (executable-find "node") + (error "Failed to install NodeJS")) + (unless (executable-find "tern") + (funcall (if (file-writable-p (executable-find "npm")) 'sh 'sudo) + "npm -g install tern")) + t)) diff --git a/modules/lang/sh/boostrap.el b/modules/lang/sh/boostrap.el deleted file mode 100644 index 1693e02a2..000000000 --- a/modules/lang/sh/boostrap.el +++ /dev/null @@ -1,14 +0,0 @@ -;;; lang/sh/boostrap.el - -(bootstrap! - :title "{z,ba}sh" - :desc "Sets up the zshdb and bashdb debuggers, and shell-check" - - :if-debian - (sudo "apt-get update && apt-get install zshdb bashdb spellcheck") - - :if-arch - (sudo "pacman --noconfirm --needed -S zshdb bashdb shellcheck") - - :if-macos - (sh "brew install zshdb bashdb")) diff --git a/modules/lang/sh/packages.el b/modules/lang/sh/packages.el index 751744560..cf008077d 100644 --- a/modules/lang/sh/packages.el +++ b/modules/lang/sh/packages.el @@ -3,3 +3,16 @@ (when (featurep! :completion company) (package! company-shell)) + +;; +(def-bootstrap! sh + (when-let (progs (cl-remove-if 'executable-find '("zshdb" "bashdb" "shellcheck"))) + (let ((prog-str (string-join progs " "))) + (pcase (doom-system-os) + ('arch + (sudo "pacman --noconfirm -S %s" prog-str)) + ('debian + (sudo "apt-get install -y %s" prog-str)) + ('macos + (sh "brew install %s" prog-str)))) + t))