vcupp-native-comp.el

;;; vcupp-native-comp.el --- Native-compile an Emacs config -*- lexical-binding: t -*-

;; Author: Michael Olson
;; SPDX-License-Identifier: GPL-3.0-or-later
;; URL: https://github.com/mwolson/vcupp

;;; Commentary:

;; Batch native-compilation with optional compile-angel integration.
;; See the vcupp README for usage.

;;; Code:

(require 'vcupp-batch)

(declare-function compile-angel-on-load-mode "compile-angel" (&optional arg))

(defvar vcupp-native-comp-active-p nil
  "Non-nil while `vcupp-native-comp-all' is running.
User configs can check this with `bound-and-true-p' to skip their own
compile-angel or native-comp settings during batch compilation.")

(defvar vcupp-native-comp-use-compile-angel t
  "When non-nil, try enabling `compile-angel-on-load-mode' during batch runs.")

(defconst vcupp-native-comp--compile-angel-spec
  '(compile-angel :url "https://github.com/jamescherti/compile-angel.el"
                  :main-file "compile-angel.el")
  "VC spec used to install `compile-angel' on demand.")

(defun vcupp-native-comp--use-compile-angel-p (args)
  "Return non-nil if compile-angel should be used per ARGS."
  (vcupp-batch--plist-value args :use-compile-angel
                            vcupp-native-comp-use-compile-angel))

(defun vcupp-native-comp--ensure-compile-angel (args)
  "Install and load compile-angel if requested by ARGS."
  (when (vcupp-native-comp--use-compile-angel-p args)
    (or (require 'compile-angel nil t)
        (progn
          (message "Installing compile-angel via package-vc...")
          (package-vc-install vcupp-native-comp--compile-angel-spec)
          (require 'compile-angel nil t))
        (progn
          (message "Unable to install compile-angel; falling back to explicit native compilation only")
          nil))))

(defun vcupp-native-comp--enable-compile-angel (compile-angel-available)
  "Enable `compile-angel-on-load-mode' if COMPILE-ANGEL-AVAILABLE is non-nil.
Returns state needed by `vcupp-native-comp--disable-compile-angel'."
  (when compile-angel-available
    (let ((previous-load-prefer-newer load-prefer-newer))
      (setq load-prefer-newer t)
      (compile-angel-on-load-mode 1)
      (list :enabled t :load-prefer-newer previous-load-prefer-newer))))

(defun vcupp-native-comp--disable-compile-angel (state)
  "Disable `compile-angel-on-load-mode' and restore settings from STATE."
  (when (plist-get state :enabled)
    (compile-angel-on-load-mode -1)
    (setq load-prefer-newer (plist-get state :load-prefer-newer))))

(defun vcupp-native-comp-all (&optional args)
  "Native-compile the configured Emacs files.

ARGS is an optional plist.  Supported keys are `:root', `:load-files',
`:compile-files', `:setup-forms', `:preload-features',
`:delete-elc-globs', `:post-load-forms', and `:use-compile-angel'.

When `:use-compile-angel' is non-nil (the default),
`compile-angel-on-load-mode' is enabled before the config files are
loaded so that libraries loaded during init are byte-compiled and
native-compiled automatically.  `vcupp-native-comp-active-p' is set
to t before the config load; user configs can check this variable
with `bound-and-true-p', or call `vcupp-suppress-native-comp-jit'
which is a no-op when it is set."
  (vcupp-batch-with-effective-args args
    (unless (native-comp-available-p)
      (user-error "Native compilation is not available in this Emacs"))
    (vcupp-batch-run-setup)
    (setq package-install-upgrade-built-in t)
    (package-initialize)
    (let ((compile-angel-available
           (vcupp-native-comp--ensure-compile-angel args)))
      (advice-add 'package-vc-install :override #'ignore)
      (setq vcupp-native-comp-active-p t)
      (let ((compile-angel-state
             (vcupp-native-comp--enable-compile-angel compile-angel-available)))
        (unwind-protect
            (progn
              (vcupp-batch-load-config)
              (message "Native-compiling files...")
              (dolist (file (vcupp-batch-compile-files))
                (native-compile (vcupp-batch-expand-file file)))
              (message "Native-compiling files...done"))
          (vcupp-native-comp--disable-compile-angel compile-angel-state)
          (setq vcupp-native-comp-active-p nil))))))

(provide 'vcupp-native-comp)
;;; vcupp-native-comp.el ends here