#+TITLE: My Emacs Bootstrap #+AUTHOR: Howard X. Abrams #+DATE: 2021-10-08 #+FILETAGS: :emacs: A literate programming file for bootstraping my Emacs Configuration. #+BEGIN_SRC emacs-lisp :exports none ;;; bootstrap.el --- file for bootstraping my Emacs Configuration ;; ;; Copyright (C) 2021 Howard X. Abrams ;; ;; Author: Howard X. Abrams ;; Maintainer: Howard X. Abrams ;; Created: October 8, 2021 ;; ;; This file is not part of GNU Emacs. ;; ;; *NB:* Do not edit this file. Instead, edit the original literate file at: ;; ~/other/hamacs/bootstrap.org ;; And tangle the file to recreate this one. ;; ;;; Code: #+END_SRC * Introduction This file contains all the variable definitions and library loading for the other files in my project. ** Straight Package Installer I'm going to be installing everything using the [[https://github.com/raxod502/straight.el#getting-started][straight.el]] for package installation and management. Here is the initialization/installation for it: #+BEGIN_SRC emacs-lisp (defvar bootstrap-version) (let ((bootstrap-file (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory)) (bootstrap-version 5)) (unless (file-exists-p bootstrap-file) (with-current-buffer (url-retrieve-synchronously "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" 'silent 'inhibit-cookies) (goto-char (point-max)) (eval-print-last-sexp))) (load bootstrap-file nil 'nomessage)) #+END_SRC Let's get the Straight project working with =use-package=: #+BEGIN_SRC emacs-lisp (straight-use-package 'use-package) #+END_SRC While that enables the =:straight t= extension to =use-package=, let's just have that be the default: #+BEGIN_SRC emacs-lisp (use-package straight :custom (straight-use-package-by-default t straight-default-vc 'git)) #+END_SRC See the details in [[https://dev.to/jkreeftmeijer/emacs-package-management-with-straight-el-and-use-package-3oc8][this essay]]. ** OS Path and Native Compilation Helper functions to allow code for specific operating systems: #+BEGIN_SRC emacs-lisp (defun ha-running-on-macos? () "Return non-nil if running on Mac OS systems." (equal system-type 'darwin)) (defun ha-running-on-linux? () "Return non-nil if running on Linux systems." (equal system-type 'gnu/linux)) #+END_SRC With the way I start Emacs, I may not have the PATH I /actually/ use (from the shell) available, so we'll force it (code taken [[https://www.emacswiki.org/emacs/ExecPath][from here]]): #+BEGIN_SRC emacs-lisp (defun set-exec-path-from-shell () "Set up Emacs' `exec-path' and PATH environment variable to match that used by the user's shell. This is particularly useful under Mac OS X and macOS, where GUI apps are not started from a shell." (interactive) (let* ((path-from-shell (shell-command-to-string "echo $PATH")) (trimmed-path (replace-regexp-in-string (rx (zero-or-more space) eol) "" path-from-shell)) (in-fish? (string-match (rx "fish" eol) (shell-command-to-string "echo $SHELL"))) (separator (if in-fish? " " ":")) (env-path (if in-fish? (replace-regexp-in-string " " ":" trimmed-path) trimmed-path))) (message "PATH=%s" path-from-shell) (setenv "PATH" env-path) (setq exec-path (split-string trimmed-path separator)))) #+END_SRC Clear up a Mac-specific issue that sometimes arises since I'm switching to [[http://akrl.sdf.org/gccemacs.html][native compilation project]], as the =Emacs.app= that I use doesn't have its =bin= directory, e.g. =Emacs.app/Contents/MacOS/bin=: #+BEGIN_SRC emacs-lisp (when (ha-running-on-macos?) (add-to-list 'exec-path "/usr/local/bin") (add-to-list 'exec-path (concat invocation-directory "bin") t)) #+END_SRC Getting tired off all the packages that I load spewing a bunch of warnings that I can't do anything about: #+BEGIN_SRC emacs-lisp (when (and (fboundp 'native-comp-available-p) (native-comp-available-p)) (setq native-comp-async-report-warnings-errors nil native-comp-deferred-compilation t)) #+END_SRC ** GNU Pretty Good Privacy On Linux, GPG is pretty straight-forward, but on the Mac, I often have troubles doing: #+BEGIN_SRC sh brew install gpg #+END_SRC And I need to install the [[https://gpgtools.org/][GPG Suite]]. While very nice, isn't a GUI I really need, but can take advantage when installed: #+BEGIN_SRC emacs-lisp (defun executable (path) "Return PATH if it is executable, see `file-executable-p'." (when (file-executable-p path) path)) (use-package epa-file :straight (:type built-in) :custom (epg-gpg-program (or (executable "/usr/local/MacGPG2/bin/gpg") (executable "/usr/local/bin/gpg") (executable "/usr/local/opt/gpg") (executable "/usr/bin/pgp"))) :config (epa-file-enable)) #+END_SRC ** Basic Libraries The following packages come with Emacs, but seems like they still need loading: #+BEGIN_SRC emacs-lisp (use-package cl-lib :straight (:type built-in) :init (defun first (elt) (car elt)) :commands (first)) (require 'subr-x) #+END_SRC Ugh. Why am I getting a missing =first= function error? I define a simple implementation, that the CL library will overwrite ... at some point. While most libraries will take care of their dependencies, I want to install /my dependent libraries/. Especially, [[https://github.com/magnars/.emacs.d/][Magnar Sveen]]'s Clojure-inspired [[https://github.com/magnars/dash.el][dash.el]] project: #+BEGIN_SRC emacs-lisp (use-package dash) #+END_SRC Sure this package is essentially syntactic sugar, and to help /share/ my configuration, I attempt to use =thread-last= instead of =->>=, but, I still like it. The [[https://github.com/magnars/s.el][s.el]] project is a simpler string manipulation library that I (and other projects) use: #+BEGIN_SRC emacs-lisp (use-package s) #+END_SRC Manipulate file paths with the [[https://github.com/rejeep/f.el][f.el]] project: #+BEGIN_SRC emacs-lisp (use-package f) #+END_SRC ** My Code Location Much of my more complicated code comes from my website essays and other projects. The destination, however, shows up here: #+BEGIN_SRC emacs-lisp (add-to-list 'load-path (f-expand "~/.emacs.d/elisp")) #+END_SRC Hopefully, this will tie me over while I transition. ** Emacs Server Control Sure the Emacs application will almost always have the =server-start= going, however, I need to control it just a bit (because I often have two instances running on some of my machines). What /defines/ the Emacs instance for work changes ... often: #+BEGIN_SRC emacs-lisp (defun ha-emacs-for-work? () "Return non-nil when the Emacs application's location matches as one for work. Currently, this is the `emacs-plus' app that I have built with the native-comp model, but I reserve the right to change this." (and (f-dir? "~/work") (->> Info-default-directory-list (first) (s-split "/") (--filter (s-starts-with? "emacs-plus" it)) (first)))) #+END_SRC #+BEGIN_SRC emacs-lisp (if (not (ha-emacs-for-work?)) (setq server-name "personal") (setq server-name "work") (when (ha-running-on-macos?) (set-exec-path-from-shell))) (server-start) #+END_SRC * Load the Rest The following loads the rest of my org-mode literate files. I add them as they are /ready/, but eventually, I'll trim this up into a nicer pattern. #+BEGIN_SRC emacs-lisp (defvar ha-hamacs-files (flatten-list `("ha-private.org" "ha-config.org" ,(when (display-graphic-p) "ha-display.org") "ha-org.org" ,(when (display-graphic-p) "ha-org-word-processor.org") "ha-org-clipboard.org" "ha-capturing-notes.org" "ha-agendas.org" "ha-passwords.org" "ha-remoting.org" "ha-programming.org" "ha-programming-python.org" ,(unless (ha-emacs-for-work?) '("ha-org-journaling.org" "ha-irc.org" "ha-org-publishing.org" "ha-email.org" "ha-aux-apps.org" "ha-feed-reader.org")) ,(when (ha-emacs-for-work?) '("ha-org-sprint.org" "ha-work.org")))) "List of org files that complete the hamacs project.") #+END_SRC We can test/debug/reload any individual file, via: #+BEGIN_SRC emacs-lisp (defun ha-hamacs-load (file) "Load or reload an org-mode FILE containing literate Emacs configuration code." (interactive (list (completing-read "Org file: " ha-hamacs-files))) (let ((full-file (f-join hamacs-source-dir file))) (when (f-exists? full-file) (org-babel-load-file full-file)))) #+END_SRC And we can now load everything: #+BEGIN_SRC emacs-lisp (defun ha-hamacs-reload-all () "Reload our entire ecosystem of configuration files." (interactive) (dolist (file ha-hamacs-files) (ha-hamacs-load file))) #+END_SRC And do it: #+BEGIN_SRC emacs-lisp (ha-hamacs-reload-all) #+END_SRC * Technical Artifacts :noexport: Let's provide a name so that the file can be required: #+BEGIN_SRC emacs-lisp :exports none (provide 'bootstrap) ;;; bootstrap.el ends here #+END_SRC Before you can build this on a new system, make sure that you put the cursor over any of these properties, and hit: ~C-c C-c~ #+DESCRIPTION: A literate programming file for bootstrapping my environment. #+PROPERTY: header-args:sh :tangle no #+PROPERTY: header-args:emacs-lisp :tangle yes #+PROPERTY: header-args :results none :eval no-export :comments no mkdirp yes #+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil #+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil #+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js