     (setq straight--recipe-lookup-cache (make-hash-table :test #'eq)
           doom--cli-updated-recipes t)))
 
+(defvar doom--expected-eln-files nil)
+
+(defun doom--elc-file-outdated-p (file)
+  (let ((elc-file (byte-compile-dest-file file)))
+    ;; NOTE Ignore missing elc files, they could be missing due to
+    ;; `no-byte-compile'. Rebuilding unnecessarily is expensive.
+    (when (and (file-exists-p elc-file)
+               (file-newer-than-file-p file elc-file))
+      (doom-log "%s is newer than %s" file elc-file)
+      t)))
+
+(defun doom--eln-file-outdated-p (file)
+  (when-let* (((require 'comp nil 'noerror))
+              (eln-file (comp-output-filename file))
+              (error-file (concat eln-file ".error")))
+    (push eln-file doom--expected-eln-files)
+    (cond ((file-exists-p eln-file)
+           (when (file-newer-than-file-p file eln-file)
+             (doom-log "%s is newer than %s" file eln-file)
+             t))
+          ((file-exists-p error-file)
+           (when (file-newer-than-file-p file error-file)
+             (doom-log "%s is newer than %s" file error-file)
+             t))
+          (t
+           (doom-log "%s doesn't exist" eln-file)
+           t))))
+
+(defun doom--native-compile-done-h (file)
+  (when-let* ((file)
+              (eln-file (comp-output-filename file))
+              (error-file (concat eln-file ".error")))
+    (if (file-exists-p eln-file)
+        (doom-log "Compiled %s" eln-file)
+      (make-directory (file-name-directory error-file) 'parents)
+      (write-region "" nil error-file)
+      (doom-log "Compiled %s" error-file))))
+
+(defun doom--native-compile-jobs ()
+  "How many async native compilation jobs are queued or in-progress."
+  (if (and (boundp 'comp-files-queue)
+           (fboundp 'comp-async-runnings))
+      (+ (length comp-files-queue)
+         (comp-async-runnings))
+    0))
+
+(defun doom--wait-for-compile-jobs ()
+  "Wait for all pending async native compilation jobs."
+  (cl-loop for pending = (doom--native-compile-jobs)
+           for tick = 0 then (% (1+ tick) 15)
+           with previous = 0
+           while (not (zerop pending))
+           if (and (zerop tick) (/= previous pending)) do
+           (print! "- Waiting for %d async jobs..." pending)
+           (setq previous pending)
+           else do
+           (let ((inhibit-message t))
+             (sleep-for 0.1)))
+  ;; HACK Write .error files for any missing files which still don't
+  ;; exist. We'll just assume there was some kind of error...
+  (cl-loop for eln-file in doom--expected-eln-files
+           for error-file = (concat eln-file ".error")
+           unless (or (file-exists-p eln-file)
+                      (file-exists-p error-file)) do
+           (make-directory (file-name-directory error-file) 'parents)
+           (write-region "" nil error-file)
+           (doom-log "Compiled %s" error-file))
+  (setq doom--expected-eln-files nil))
+
 
 (defun doom-cli-packages-install ()
   "Installs missing packages.

                            (straight--call "git" "config" "core.autocrlf" "true")))))
                  (error
                   (signal 'doom-package-error (list package e))))))
         (print! (success "Installed %d packages")
                 (length built))
       (print! (info "No packages need to be installed"))
       nil))))


                             ;; Doesn't make sense to compare el and elc files
                             ;; when the former isn't a symlink to their source.
                             (when straight-use-symlinks
                               (cl-loop for file in (doom-files-in build-dir :match "\\.el$" :full t)
                                        with want-byte = (straight--byte-compile-package-p recipe)
                                        with want-native = (straight--native-compile-package-p recipe)
                                        with outdated = nil
                                        if (and (file-symlink-p file)
                                                (or (and want-byte (doom--elc-file-outdated-p file))
                                                    (and want-native (doom--eln-file-outdated-p file))))
                                        do (setq outdated t)
                                        finally return outdated)))
                         (not (plist-get recipe :no-build))
                         (puthash package t straight--packages-to-rebuild))))
                (straight-use-package (intern package))))
         (progn
           (doom--wait-for-compile-jobs)
           (print! (success "Rebuilt %d package(s)") (length built)))
       (print! (success "No packages need rebuilding"))
       nil))))

