Rework byte recompile of built-in libs

This commit is contained in:
JAremko 2021-01-25 16:56:36 +02:00 committed by Eugene Yaremenko
parent e712eae9f1
commit 5182a450e6
8 changed files with 44 additions and 1325 deletions

View File

@ -12,6 +12,4 @@ https://raw.githubusercontent.com/nashamri/spacemacs-theme/master/spacemacs-comm
https://raw.githubusercontent.com/nashamri/spacemacs-theme/master/spacemacs-dark-theme.el core/libs/spacemacs-theme/spacemacs-dark-theme.el
https://raw.githubusercontent.com/nashamri/spacemacs-theme/master/spacemacs-light-theme.el core/libs/spacemacs-theme/spacemacs-light-theme.el
https://raw.githubusercontent.com/nashamri/spacemacs-theme/master/spacemacs-theme-pkg.el core/libs/spacemacs-theme/spacemacs-theme-pkg.el
https://raw.githubusercontent.com/sigma/mocker.el/master/mocker.el core/libs/mocker.el
https://raw.githubusercontent.com/emacscollective/packed/master/packed.el core/libs/packed.el
https://raw.githubusercontent.com/emacscollective/auto-compile/master/auto-compile.el core/libs/auto-compile.el
https://raw.githubusercontent.com/sigma/mocker.el/master/mocker.el core/libs/mocker.el

View File

@ -9,12 +9,13 @@
;;
;;; License: GPLv3
(defconst spacemacs-compiled-files
(require 'cl-lib)
(require 'subr-x)
(defconst spacemacs--compiled-files
'(;; Built-in libs that we changed
"core/libs/forks/load-env-vars.el"
;; Rest of built-in libs.
"core/libs/packed.el"
"core/libs/auto-compile.el"
"core/libs/dash.el"
"core/libs/ht.el"
"core/libs/ido-vertical-mode.el"
@ -27,13 +28,12 @@
"List of Spacemacs files that should be compiled.
File paths are relative to the `spacemacs-start-directory'.")
(defun spacemacs//ensure-byte-compilation (files)
"Make sure that elisp FILES are byte-compiled."
(dolist (file files)
(let ((fbp (file-name-sans-extension (file-truename file))))
(unless (file-exists-p (concat fbp ".elc"))
(byte-compile-file (concat fbp ".el"))))))
(unless (file-exists-p (concat fbp ".elc"))
(byte-compile-file (concat fbp ".el"))))))
(defun spacemacs//remove-byte-compiled-files (files)
"Remove .elc files corresponding to the source FILES."
@ -44,4 +44,15 @@ File paths are relative to the `spacemacs-start-directory'.")
(format "%s.elc")
(delete-file))))
(defun spacemacs//remove-byte-compiled-if-stale (files)
"Remove all .elc files corresponding to the source FILES if any is stale."
(when (cl-dolist (file files)
(let* ((file-el (file-truename file))
(file-elc (thread-last file-el
(file-name-sans-extension)
(format "%s.elc"))))
(when (file-newer-than-file-p file-el file-elc)
(cl-return t))))
(spacemacs//remove-byte-compiled-files files)))
(provide 'core-compilation)

View File

@ -255,10 +255,10 @@ OWNER REPO."
"Hard reset the current branch to specified TAG."
(let ((proc-buffer "git-hard-reset")
(default-directory (file-truename spacemacs-start-directory)))
(prog1
(eq 0 (process-file "git" nil proc-buffer nil
"reset" "--hard" tag))
(kill-buffer proc-buffer))))
(prog1 (eq 0 (process-file "git" nil proc-buffer nil
"reset" "--hard" tag))
(kill-buffer proc-buffer)
(spacemacs//revision-update))))
(defun spacemacs//git-latest-tag (remote branch)
"Returns the latest tag on REMOTE/BRANCH."
@ -348,6 +348,20 @@ Example: (1 42 3) = 1 042 003"
(add-hook 'spacemacs-post-theme-change-hook
'spacemacs/set-new-version-lighter-mode-line-faces)
(defun spacemacs//revision-update ()
"Update saved revision value and trigger `spacemacs-revision--changed-hook'."
(let ((proc-buffer "spacemacs//revision-update:git-get-current-rev"))
(when (eq 0 (process-file "git" nil proc-buffer nil "rev-parse" "HEAD"))
(with-current-buffer proc-buffer
(goto-char 1)
(setq spacemacs-revision--last (current-word))
(kill-buffer proc-buffer))))
(with-temp-file spacemacs-revision--file
(insert (format "(setq spacemacs-revision--last %S)"
spacemacs-revision--last))
(make-directory (file-name-directory spacemacs-revision--file) t))
(run-hooks 'spacemacs-revision--changed-hook))
(defun spacemacs//revision-check ()
"Update saved value of the current revision asynchronously.
NOTE: If old and new revisions are different `spacemacs-revision--changed-hook'

View File

@ -253,12 +253,10 @@ Note: the hooked function is not executed when in dumped mode."
(unless (version< emacs-version "27")
(setq read-process-output-max dotspacemacs-read-process-output-max))))
;; Ensure that `spacemacs-compiled-files' are byte-compiled.
;; Ensure that `spacemacs--compiled-files' are byte-compiled.
(let ((default-directory spacemacs-start-directory))
(spacemacs//ensure-byte-compilation spacemacs-compiled-files))
;; Remove Spacemacs'es .elc files if Spacemacs revision has changed.
(add-hook 'spacemacs-revision--changed-hook 'spacemacs//byte-compile-cleanup)
;; Update saved revision.
(spacemacs//ensure-byte-compilation spacemacs--compiled-files))
;; Check if revision has changed.
(spacemacs//revision-check))
(provide 'core-spacemacs)

View File

@ -1,789 +0,0 @@
;;; auto-compile.el --- automatically compile Emacs Lisp libraries -*- lexical-binding: t -*-
;; Copyright (C) 2008-2020 Jonas Bernoulli
;; Author: Jonas Bernoulli <jonas@bernoul.li>
;; Homepage: https://github.com/emacscollective/auto-compile
;; Keywords: compile, convenience, lisp
;; Package-Requires: ((emacs "25.1") (packed "3.0.1"))
;; This file is not part of GNU Emacs.
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; For a full copy of the GNU General Public License
;; see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This package provides two minor modes which automatically recompile
;; Emacs Lisp source files. Together these modes guarantee that Emacs
;; never loads outdated byte code files.
;; `auto-compile-on-save-mode' re-compiles source files when they are
;; being saved and `auto-compile-on-load-mode' does so before they are
;; being loaded (by advising `load' and `require'). Both modes only
;; ever _re-compile_ a source file when the respective byte code file
;; already exists but is outdated. Otherwise they do _not_ compile
;; the source file.
;; Even when using `auto-compile-on-save-mode' it can happen that some
;; source file is newer than the respective byte code file, which is a
;; problem because by default Emacs loads the byte code file even when
;; the respective source file has been modified more recently.
;; Starting with Emacs version 24.4, setting `load-prefer-newer' to t
;; prevents outdated byte code files from being loaded. However this
;; does not cause re-compilation of the source file, to actually do
;; that `auto-compile-on-load-mode' is still required.
;; Setup
;; -----
;; To reduce the risk of loading outdated byte code files, you should
;; set `load-prefer-newer' and enable `auto-compile-on-load-mode' as
;; early as possible. Then also enable `auto-compile-on-save-mode'.
;; You should also consider not byte-compiling your personal init
;; file, or setting `load-prefer-newer' in a system-wide init file.
;; If you use `package.el' then use something like this:
;;
;; ;;; init.el --- user init file
;; (setq load-prefer-newer t)
;; (package-initialize)
;; (require 'auto-compile)
;; (auto-compile-on-load-mode)
;; (auto-compile-on-save-mode)
;; otherwise:
;;
;; ;;; init.el --- user init file
;; (setq load-prefer-newer t)
;; (add-to-list 'load-path "/path/to/packed")
;; (add-to-list 'load-path "/path/to/auto-compile")
;; (require 'auto-compile)
;; (auto-compile-on-load-mode)
;; (auto-compile-on-save-mode)
;; You might want to set the file-local value of `no-byte-compile' to
;; t, e.g. by adding "-*- no-byte-compile: t -*-" (without the quotes)
;; at the end of the very first line. That way all user files benefit
;; from the protection offered by `load-prefer-newer' and the modes
;; that are defined here, otherwise `~/.emacs.d/init.el' is the only
;; exception.
;; If you are using Emacs 27 or later, then these settings should be
;; placed in `early-init.el', which should never be compiled:
;; ;;; early-init.el --- early bird -*- no-byte-compile: t -*-
;; (setq load-prefer-newer t)
;; (add-to-list 'load-path "/path/to/packed")
;; (add-to-list 'load-path "/path/to/auto-compile")
;; (require 'auto-compile)
;; (auto-compile-on-load-mode)
;; (auto-compile-on-save-mode)
;; ;;; early-init.el ends here
;; Usage
;; -----
;; Take note of the compile warnings and fix them.
;; To permanently or temporarily toggle automatic compilation of some
;; source file use the command `toggle-auto-compile'. Since the modes
;; only ever _update_ byte code files, toggling automatic compilation
;; is done simply by either creating the byte code file or by removing
;; it. `toggle-auto-compile' can also toggle automatic compilation of
;; multiple files at once; see its doc-string for more information.
;; Customization
;; -------------
;; Constantly having the *Compile-Log* buffer pop up when a file is
;; being saved can quickly become annoying. Obviously the first thing
;; you should do to about that is to actually fix outstanding issues.
;; Once you have done that you might also want to keep that buffer
;; from being automatically displayed and instead only show the number
;; of compile warnings for the current file in the mode-line.
;; (setq auto-compile-display-buffer nil)
;; (setq auto-compile-mode-line-counter t)
;; To display the buffer use `M-x auto-compile-display-log' or click
;; on the counter in the mode-line.
;; Using `auto-compile-inhibit-compile-hook' it is possible to inhibit
;; automatic compilation under certain circumstances; e.g. when HEAD
;; is detached inside a Git repository (useful during rebase sessions).
;;; Code:
(require 'bytecomp)
(require 'cl-lib)
(require 'packed)
(declare-function autoload-rubric "autoload")
(declare-function autoload-find-destination "autoload")
(declare-function autoload-file-load-name "autoload")
(declare-function autoload-generate-file-autoloads "autoload")
(defvar autoload-modified-buffers)
(defvar warning-minimum-level)
(defvar auto-compile-use-mode-line)
(defgroup auto-compile nil
"Automatically compile Emacs Lisp source libraries."
:group 'convenience
:prefix 'auto-compile
:link '(function-link toggle-auto-compile)
:link '(function-link auto-compile-mode))
;;; Auto-Compile-On-Save Mode
;;;###autoload
(define-minor-mode auto-compile-mode
"Compile Emacs Lisp source files after the visiting buffers are saved.
After a buffer containing Emacs Lisp code is saved to its source
file update the respective byte code file. If the latter does
not exist do nothing. Therefore to disable automatic compilation
remove the byte code file. See command `toggle-auto-compile' for
a convenient way to do so.
This mode should be enabled globally, using it's globalized
variant `auto-compile-on-save-mode'. Also see the related
`auto-compile-on-load-mode'."
:lighter auto-compile-mode-lighter
:group 'auto-compile
(unless (derived-mode-p 'emacs-lisp-mode)
(user-error "This mode only makes sense with emacs-lisp-mode"))
(if auto-compile-mode
(add-hook 'after-save-hook 'auto-compile-byte-compile nil t)
(remove-hook 'after-save-hook 'auto-compile-byte-compile t))
(auto-compile-modify-mode-line auto-compile-use-mode-line))
;;;###autoload
(define-globalized-minor-mode auto-compile-on-save-mode
auto-compile-mode turn-on-auto-compile-mode)
(defun turn-on-auto-compile-mode ()
(when (eq major-mode 'emacs-lisp-mode)
(auto-compile-mode 1)))
(defvar auto-compile-mode-lighter ""
"Mode lighter for Auto-Compile Mode.")
;;; Options
(defcustom auto-compile-visit-failed t
"Whether to visit source files which failed to compile.
If this is non-nil visit but don't select a source file if it
isn't being visited in a buffer already. Also set the buffer
local value of variable `auto-compile-pretend-byte-compiled'
\(which see) to t and mark the buffer as modified if the value
of variable `auto-compile-mark-failed-modified' is non-nil."
:group 'auto-compile
:type 'boolean)
(defcustom auto-compile-mark-failed-modified nil
"Whether to mark buffers which failed to compile as modified.
This serves as a reminder to fix fatal errors. While useful this
can get annoying so this variable can be quickly toggled with the
command `auto-compile-toggle-mark-failed-modified'."
:group 'auto-compile
:type 'boolean)
(defcustom auto-compile-ding t
"Whether to beep (or flash the screen) when an error occurs.
Expected errors (such as compile error, unmatched parens, or
failure to remove a file) are caught and execution continues so
that failure to process one file does not prevent other files
from being processed.
To inform users of such errors Auto-Compile instead beeps or
flashes the screen; set this variable to nil to disable even
that."
:group 'auto-compile
:type 'boolean)
(defcustom auto-compile-check-parens t
"Whether to check for unbalanced parentheses before compiling.
This only has as an effect on files which are currently being
visited in a buffer. Other files are compiled without performing
this check first. If unbalanced parentheses are found no attempt
is made to compile the file as that would obviously fail also."
:group 'auto-compile
:type 'boolean)
(defcustom auto-compile-update-autoloads nil
"Whether to update autoloads after compiling.
If no autoload file as specified by `packed-loaddefs-filename' can be
found quietly skip this step."
:group 'auto-compile
:type 'boolean)
(defcustom auto-compile-inhibit-compile-hook nil
"Hook used to inhibit automatic compilation.
This hook is run before automatic compilation takes place, if
any of the hook functions returns non-nil, then do not compile."
:group 'auto-compile
:options '(auto-compile-inhibit-compile-detached-git-head)
:type 'hook)
(defcustom auto-compile-verbose nil
"Whether to print messages describing progress of byte-compiler.
This overrides `byte-compile-verbose' but unlike that does not
default to t, and thus avoids unnecessary echo-area messages."
:group 'auto-compile
:type 'boolean)
(defcustom auto-compile-display-buffer t
"Whether to automatically display the *Compile-Log* buffer.
When there are errors then the buffer is always displayed,
when there are no warnings or errors it is never displayed."
:group 'auto-compile
:type 'boolean)
(defcustom auto-compile-mode-line-counter nil
"Whether to display the number of warnings in the mode line.
This assumes that `auto-compile-use-mode-line' (which see) is
non-nil."
:group 'auto-compile
:type 'boolean)
(defun auto-compile-modify-mode-line (after)
(let ((format (delete 'mode-line-auto-compile
(default-value 'mode-line-format)))
cell)
(when (and after auto-compile-mode
(setq cell (member after format)))
(push 'mode-line-auto-compile (cdr cell)))
(set-default 'mode-line-format format)))
(defcustom auto-compile-use-mode-line
(car (memq 'mode-line-modified (default-value 'mode-line-format)))
"Whether to show information about the byte code file in the mode line.
This works by inserting `mode-line-auto-compile' into the default
value of `mode-line-format' after the construct (usually a symbol)
specified here. This happens every time local Auto-Compile mode
is turned on so the specified construct does not have to a member
of `mode-line-format' when this is set (this allows loading that
package after `auto-compile-on-load-mode' has been activated, so
that it can ensures the respective byte code file is up-to-date).
If you want to add `mode-line-auto-compile' as a member of a
variable that is itself a member of `mode-line-format' then you
have to set this option to nil and manually modify that variable
to include `mode-line-auto-compile'."
:group 'auto-compile
:set (lambda (symbol value)
(set-default symbol value)
(auto-compile-modify-mode-line value))
:type '(choice (const :tag "don't insert" nil)
(const :tag "after mode-line-modified" mode-line-modified)
(const :tag "after mode-line-remote" mode-line-remote)
(sexp :tag "after construct")))
(defcustom auto-compile-toggle-recompiles t
"Whether to recompile all source files when turning on compilation.
When turning on auto compilation for multiple files at once
recompile source files even if their byte code file already
exist and are up-to-date. It's advisable to keep this enabled
to ensure changes to macros are picked up."
:group 'auto-compile
:type 'boolean)
(defcustom auto-compile-delete-stray-dest t
"Whether to remove stray byte code files.
If this is non-nil byte code files without a corresponding source
file are removed as they are encountered. This happens in the
functions `auto-compile-on-save' and `toggle-auto-compile'. The
main purpose of this functionality is to prevent leftover byte
code files from shadowing a source or byte code file in a
directory that comes later in the `load-path'."
:group 'auto-compile
:type 'boolean)
(defcustom auto-compile-toggle-deletes-nonlib-dest nil
"Whether to delete non-library byte code files when toggling compilation."
:group 'auto-compile
:type 'boolean)
(defcustom auto-compile-source-recreate-deletes-dest nil
"Whether to delete leftover byte code file when creating source file.
When this is non-nil and saving a source buffer causes the file
to be created (as opposed to being overwritten) while its byte
code file already exists (because the source already existed and
was compiled in the past), then remove the latter (instead of
updating it by recompiling the source). This can e.g. happen
when switching git branches."
:group 'auto-compile
:type 'boolean)
;;; Toggle and Perform Compilation
;;;###autoload
(defun toggle-auto-compile (file action)
"Toggle automatic compilation of an Emacs Lisp source file or files.
Read a file or directory name from the minibuffer defaulting to
the visited Emacs Lisp source file or `default-directory' if no
such file is being visited in the current buffer.
If the user selects a file then automatic compilation of only
that file is toggled. Since both `auto-compile-on-save' and
`auto-compile-on-save' only ever _recompile_ byte code files,
toggling automatic compilation is done simply by creating or
removing the respective byte code file.
If the user selects a directory then automatic compilation for
multiple files is toggled as follows:
* With a positive prefix argument always compile source files;
with a negative prefix argument always remove byte code files.
* Otherwise the existence or absence of the byte code file of
the source file that was current when this command was invoked
determines whether byte code files should be created or removed.
* If no Emacs Lisp source file is being visited in the buffer
that was current when the command was invoked ask the user what
to do.
* When _removing_ byte code files then all byte code files are
removed. If `auto-compile-deletes-stray-dest' is non-nil this
even includes byte code files for which no source file exists.
* When _creating_ byte code files only do so for source files
that are actual libraries. Source files that provide the
correct feature are considered to be libraries; see
`packed-library-p'.
* Note that non-libraries can still be automatically compiled,
you just cannot _recursively_ turn on automatic compilation
using this command.
* When `auto-compile-toggle-recompiles' is non-nil recompile all
affected source files even when the respective source files are
up-to-date. Do so even for non-library source files.
* Only enter subdirectories for which `packed-ignore-directory-p'
returns nil; i.e. don't enter hidden directories or directories
containing a file named \".nosearch\"."
(interactive
(let* ((file (and (eq major-mode 'emacs-lisp-mode)
(buffer-file-name)))
(action
(cond
(current-prefix-arg
(if (> (prefix-numeric-value current-prefix-arg) 0)
'start
'quit))
(file
(if (file-exists-p (byte-compile-dest-file file))
'quit
'start))
(t
(pcase (read-char-choice
"Toggle automatic compilation (s=tart, q=uit, C-g)? "
'(?s ?q))
(?s 'start)
(?q 'quit))))))
(list (read-file-name (concat (capitalize (symbol-name action))
" auto-compiling: ")
(and file (file-name-directory file))
nil t
(and file (file-name-nondirectory file)))
action)))
(if (file-regular-p file)
(pcase action
(`start (auto-compile-byte-compile file t))
(`quit (auto-compile-delete-dest (byte-compile-dest-file file))))
(when (called-interactively-p 'any)
(let ((buffer (get-buffer byte-compile-log-buffer)))
(when (buffer-live-p buffer)
(kill-buffer buffer))))
(dolist (f (directory-files file t))
(cond
((file-directory-p f)
(unless (packed-ignore-directory-p f)
(toggle-auto-compile f action)))
((packed-library-p f)
(let ((dest (byte-compile-dest-file f)))
(if (eq action 'start)
(and (file-exists-p f)
(or auto-compile-toggle-recompiles
(file-newer-than-file-p f dest))
(or (not (string-match "^\\.?#" (file-name-nondirectory f)))
(file-exists-p dest))
(auto-compile-byte-compile f t))
(auto-compile-delete-dest dest))))
((and auto-compile-toggle-deletes-nonlib-dest
(eq action 'quit)
(string-match (packed-el-regexp) f))
(auto-compile-delete-dest (byte-compile-dest-file f)))
((and auto-compile-delete-stray-dest
(string-match "\\.elc$" f)
(not (file-exists-p (packed-el-file f))))
(auto-compile-delete-dest f))))))
(defalias 'auto-compile-toggle 'toggle-auto-compile)
(defun auto-compile-toggle-mark-failed-modified ()
"Toggle whether buffers which failed to compile are marked as modified."
(interactive)
(message (concat (if (setq auto-compile-mark-failed-modified
(not auto-compile-mark-failed-modified))
"Mark "
"Don't mark ")
"files that failed to compile as modified")))
(defvar-local auto-compile-pretend-byte-compiled nil
"Whether to try again to compile this file after a failed attempt.
Command `auto-compile-byte-compile' sets this buffer local
variable to t after failing to compile a source file being
visited in a buffer (or when variable `auto-compile-visit-failed'
is non-nil for all files being compiled) causing it to try again
when being called again. Command `toggle-auto-compile' will also
pretend the byte code file exists.")
(defvar auto-compile-file-buffer nil)
(defvar-local auto-compile-warnings 0)
(define-advice byte-compile-log-warning
(:before (_string &optional _fill _level) auto-compile)
"Increment local value of `auto-compile-warnings'."
(when auto-compile-file-buffer
(with-current-buffer auto-compile-file-buffer
(cl-incf auto-compile-warnings))))
(cl-defun auto-compile-byte-compile (&optional file start)
"Perform byte compilation for Auto-Compile mode."
(when (run-hook-with-args-until-success 'auto-compile-inhibit-compile-hook)
(cl-return-from auto-compile-byte-compile))
(let ((default-directory default-directory)
dest buf auto-compile-file-buffer success loaddefs)
(when (and file
(setq buf (get-file-buffer file))
(buffer-modified-p buf)
(y-or-n-p (format "Save buffer %s first? " (buffer-name buf))))
(with-current-buffer buf (save-buffer)))
(unless file
(setq file (buffer-file-name))
(setq buf (get-file-buffer file)))
(setq default-directory (file-name-directory file))
(setq auto-compile-file-buffer buf)
(when (buffer-live-p buf)
(with-current-buffer buf
(setq auto-compile-warnings 0)))
(catch 'auto-compile
(when (and auto-compile-check-parens buf)
(condition-case check-parens
(save-restriction
(widen)
(check-parens))
(error
(message (error-message-string check-parens))
(auto-compile-handle-compile-error file buf start)
(throw 'auto-compile nil))))
(setq dest (byte-compile-dest-file file))
(when (or start
(and (file-exists-p dest)
(or (file-exists-p file)
(not auto-compile-source-recreate-deletes-dest)
(prog1 nil
(auto-compile-delete-dest dest))))
(and (buffer-live-p buf)
(buffer-local-value auto-compile-pretend-byte-compiled
buf)))
(condition-case nil
(let ((byte-compile-verbose auto-compile-verbose)
(warning-minimum-level
(if auto-compile-display-buffer :warning :error)))
(setq success (packed-byte-compile-file file))
(when (buffer-live-p buf)
(with-current-buffer buf
(kill-local-variable auto-compile-pretend-byte-compiled))))
(file-error
(message "Byte-compiling %s failed" file)
(auto-compile-handle-compile-error file buf start)
(setq success nil)))
(when (and auto-compile-update-autoloads
(setq loaddefs (packed-loaddefs-file)))
(require 'autoload)
(condition-case nil
(packed-with-loaddefs loaddefs
(let ((autoload-modified-buffers nil))
(autoload-generate-file-autoloads
file nil generated-autoload-file)))
(error
(message "Generating loaddefs for %s failed" file)
(setq loaddefs nil))))
(pcase success
(`no-byte-compile)
(`t (message "Wrote %s.{%s,%s}%s"
(file-name-sans-extension
(file-name-sans-extension file))
(progn (string-match "\\(\\.[^./]+\\)+$" file)
(substring (match-string 0 file) 1))
(file-name-extension dest)
(if loaddefs " (+)" "")))
(_ (message "Wrote %s (byte-compiling failed)" file))))
success)))
(defun auto-compile-delete-dest (dest &optional failurep)
(unless failurep
(let ((buffer (get-file-buffer (packed-el-file dest))))
(when (buffer-live-p buffer)
(with-current-buffer buffer
(kill-local-variable 'auto-compile-pretend-byte-compiled)))))
(condition-case nil
(when (file-exists-p dest)
(message "Deleting %s..." dest)
(delete-file dest)
(message "Deleting %s...done" dest))
(file-error
(auto-compile-ding)
(message "Deleting %s...failed" dest))))
(defun auto-compile-handle-compile-error (file buf &optional start)
(auto-compile-ding)
(let (update)
(let ((dest (byte-compile-dest-file file)))
(when (file-exists-p dest)
(setq update t)
(auto-compile-delete-dest dest t)))
(when (or buf
(and auto-compile-visit-failed
(setq buf (find-file-noselect file))))
(with-current-buffer buf
(when (or update start)
(setq auto-compile-pretend-byte-compiled t))
(when auto-compile-mark-failed-modified
(set-buffer-modified-p t))))))
(defun auto-compile-handle-autoloads-error (dest)
(auto-compile-ding)
(packed-remove-autoloads dest nil))
(defun auto-compile-ding ()
(when auto-compile-ding
(ding)))
(define-advice save-buffers-kill-emacs
(:around (fn &optional arg) auto-compile)
"Bind `auto-compile-mark-failed-modified' to nil when killing Emacs.
If the regular value of this variable is non-nil the user might
still be asked whether she wants to save modified buffers, which
she actually did already safe. This advice ensures she at least
is only asked once about each such file."
(let ((auto-compile-mark-failed-modified nil))
(funcall fn arg)))
(define-advice save-buffers-kill-terminal
(:around (fn &optional arg) auto-compile)
"Bind `auto-compile-mark-failed-modified' to nil when killing Emacs.
If the regular value of this variable is non-nil the user might
still be asked whether she wants to save modified buffers, which
she actually did already safe. This advice ensures she at least
is only asked once about each such file."
(let ((auto-compile-mark-failed-modified nil))
(funcall fn arg)))
(defun auto-compile-inhibit-compile-detached-git-head ()
"Inhibit compiling in Git repositories when `HEAD' is detached.
This is especially useful during rebase sessions."
(with-temp-buffer
(call-process "git" nil t nil "symbolic-ref" "HEAD")
(equal (buffer-string) "fatal: ref HEAD is not a symbolic ref\n")))
;;; Mode-Line
(defvar-local mode-line-auto-compile
'(auto-compile-mode (:eval (mode-line-auto-compile-control))))
(put 'mode-line-auto-compile 'risky-local-variable t)
(defun mode-line-auto-compile-control ()
(let ((src (buffer-file-name))
dst)
(when (and src (setq dst (byte-compile-dest-file src)))
(list
(when (and auto-compile-mode-line-counter
(> auto-compile-warnings 0))
(propertize
(format "%s" auto-compile-warnings)
'help-echo (format "%s compile warnings\nmouse-1 display compile log"
auto-compile-warnings)
'face 'error
'mouse-face 'mode-line-highlight
'local-map (purecopy (make-mode-line-mouse-map
'mouse-1
#'auto-compile-display-log))))
(cond
((file-writable-p dst)
(propertize
"-"
'help-echo "Byte-compile destination is writable"
'mouse-face 'mode-line))
(t
(propertize
"%%"
'help-echo "Byte-compile destination is read-only"
'mouse-face 'mode-line)))
(cond
((and auto-compile-pretend-byte-compiled
(not (file-exists-p dst)))
(propertize
"!"
'help-echo "Failed to byte-compile updating\nmouse-1 retry"
'mouse-face 'mode-line-highlight
'local-map (purecopy (make-mode-line-mouse-map
'mouse-1
#'auto-compile-mode-line-byte-compile))))
((not (file-exists-p dst))
(propertize
"%%"
'help-echo "Byte-compiled file doesn't exist\nmouse-1 create"
'mouse-face 'mode-line-highlight
'local-map (purecopy (make-mode-line-mouse-map
'mouse-1
#'mode-line-toggle-auto-compile))))
((file-newer-than-file-p src dst)
(propertize
"*"
'help-echo "Byte-compiled file needs updating\nmouse-1 update"
'mouse-face 'mode-line-highlight
'local-map (purecopy (make-mode-line-mouse-map
'mouse-1
#'auto-compile-mode-line-byte-compile))))
(t
(propertize
"-"
'help-echo "Byte-compiled file is up-to-date\nmouse-1 remove"
'mouse-face 'mode-line-highlight
'local-map (purecopy (make-mode-line-mouse-map
'mouse-1
#'mode-line-toggle-auto-compile)))))))))
(defun auto-compile-display-log ()
"Display the *Compile-Log* buffer."
(interactive)
(let ((buffer (get-buffer byte-compile-log-buffer)))
(if (buffer-live-p buffer)
(pop-to-buffer buffer)
(user-error "Buffer %s doesn't exist" byte-compile-log-buffer))))
(defun mode-line-toggle-auto-compile (event)
"Toggle automatic compilation from the mode-line."
(interactive "e")
(save-selected-window
(select-window (posn-window (event-start event)))
(toggle-auto-compile
(buffer-file-name)
(if (file-exists-p (byte-compile-dest-file (buffer-file-name)))
'quit
'start))
(force-mode-line-update)))
(defun auto-compile-mode-line-byte-compile (event)
"Recompile visited file from the mode-line."
(interactive "e")
(save-selected-window
(select-window (posn-window (event-start event)))
(auto-compile-byte-compile (buffer-file-name) t)
(force-mode-line-update)))
;;; Auto-Compile-On-Load Mode
;;;###autoload
(define-minor-mode auto-compile-on-load-mode
"Before loading a library recompile it if it needs recompilation.
A library needs to be recompiled if the source file is newer than
it's byte-compile destination. Without this advice the outdated
byte code file would be loaded instead.
Also see the related `auto-compile-on-save-mode'."
:lighter auto-compile-on-load-mode-lighter
:group 'auto-compile
:global t)
(defvar auto-compile-on-load-mode-lighter ""
"Mode lighter for Auto-Compile-On-Load Mode.")
(define-advice load
(:before (file &optional _noerror _nomessage nosuffix _must-suffix)
auto-compile)
"Before loading the library recompile it if it needs recompilation.
If `auto-compile-on-load-mode' isn't enabled, then do nothing.
It needs recompilation if it is newer than the byte-code file.
Without this advice the outdated source file would get loaded."
(when auto-compile-on-load-mode
(auto-compile-on-load file nosuffix)))
(define-advice require
(:before (feature &optional filename _noerror) auto-compile)
"Before loading the library recompile it if it needs recompilation.
If `auto-compile-on-load-mode' isn't enabled, then do nothing.
It needs recompilation if it is newer than the byte-code file.
Without this advice the outdated source file would get loaded."
(when auto-compile-on-load-mode
(unless (featurep feature)
(auto-compile-on-load (or filename (symbol-name feature))))))
(defvar auto-compile--loading nil)
(defun auto-compile-on-load (file &optional nosuffix)
(unless (member file auto-compile--loading)
(let ((auto-compile--loading (cons file auto-compile--loading))
byte-compile-verbose el elc el*)
(condition-case nil
(when (setq el (packed-locate-library file nosuffix))
(setq elc (byte-compile-dest-file el))
(when (and (file-exists-p elc)
(file-writable-p elc)
(file-newer-than-file-p el elc))
(message "Recompiling %s..." el)
(packed-byte-compile-file el)
(message "Recompiling %s...done" el))
(when auto-compile-delete-stray-dest
(setq el* (locate-library file))
(unless (equal (file-name-directory el)
(file-name-directory el*))
(auto-compile-delete-dest el* t))))
(error
(message "Recompiling %s...failed" el)
(when elc
(auto-compile-delete-dest elc t)))))))
;;; _
(provide 'auto-compile)
;; Local Variables:
;; indent-tabs-mode: nil
;; End:
;;; auto-compile.el ends here

View File

@ -1,510 +0,0 @@
;;; packed.el --- package manager agnostic Emacs Lisp package utilities -*- lexical-binding: t -*-
;; Copyright (C) 2012-2020 Jonas Bernoulli
;; Author: Jonas Bernoulli <jonas@bernoul.li>
;; Homepage: https://github.com/emacscollective/packed
;; Keywords: compile, convenience, lisp, package, library
;; Package-Requires: ((emacs "24.3"))
;; This file is not part of GNU Emacs.
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; For a full copy of the GNU General Public License
;; see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Packed provides some package manager agnostic utilities to work
;; with Emacs Lisp packages. As far as Packed is concerned packages
;; are collections of Emacs Lisp libraries that are stored in a
;; dedicated directory such as a Git repository. And libraries are
;; Emacs Lisp files that provide the correct feature (matching the
;; filename).
;; Where a package manager might depend on metadata, Packed instead
;; uses some heuristics to get the same information — that is slower
;; and might also fail at times but makes it unnecessary to maintain
;; package recipes.
;;; Code:
(require 'bytecomp)
(require 'cl-lib)
(defvar autoload-modified-buffers)
(declare-function autoload-rubric "autoload")
(declare-function autoload-find-destination "autoload")
(declare-function autoload-file-load-name "autoload")
;;; Libraries
(defun packed-el-suffixes (&optional nosuffix must-suffix)
"Return a list of the valid suffixes of Emacs Lisp source libraries.
Unlike `get-load-suffixes' don't return the suffixes for
byte-compile destinations just those of source files.
If NOSUFFIX is non-nil the `.el' part is omitted. IF MUST-SUFFIX
is non-nil all returned suffixes contain `.el'. This uses the
variables `load-suffixes' (from which it removes \".elc\") and
`load-file-rep-suffixes'."
(packed--suffixes ".elc" nosuffix must-suffix))
(defun packed-elc-suffixes (&optional nosuffix must-suffix)
"Return a list of the valid suffixes of Emacs Lisp source libraries.
Unlike `get-load-suffixes' don't return the suffixes for
source files just those of byte-compile destinations.
If NOSUFFIX is non-nil the `.elc' part is omitted. IF MUST-SUFFIX
is non-nil all returned suffixes contain `.elc'. This uses the
variables `load-suffixes' (from which it removes \".el\") and
`load-file-rep-suffixes'."
(packed--suffixes ".el" nosuffix must-suffix))
(defun packed--suffixes (remove-suffix &optional nosuffix must-suffix)
(append (unless nosuffix
(let ((load-suffixes (remove remove-suffix load-suffixes)))
(get-load-suffixes)))
(unless must-suffix
load-file-rep-suffixes)))
(defun packed-el-regexp ()
"Return the valid suffixes of Emacs libraries as a regular expression.
The returned regular expression matches source files but not
byte-compile destinations and always expects the \".el\" suffix."
(concat (regexp-opt (packed-el-suffixes nil t)) "\\'"))
(defun packed-elc-regexp ()
"Return the valid suffixes of byte-compile destinations as a regexp.
The returned regular expression matches byte-compile destinations
but not source files and always expects the \".elc\" suffix."
(concat (regexp-opt (packed-elc-suffixes nil t)) "\\'"))
(defun packed-el-file (elc)
"Return the Emacs source file for byte-compile destination ELC."
(let ((standard (concat (file-name-sans-extension
(file-name-sans-extension elc)) ".el"))
(suffixes (remove ".el" (packed-el-suffixes)))
file)
(while (and (not file) suffixes)
(unless (file-exists-p (setq file (concat standard (pop suffixes))))
(setq file nil)))
(or file standard)))
(defalias 'packed-elc-file 'byte-compile-dest-file)
(defun packed-locate-library (library &optional nosuffix path interactive-call)
"Show the precise file name of Emacs library LIBRARY.
Unlike `locate-library' don't return the byte-compile destination
if it exists but always the Emacs source file.
LIBRARY should be a relative file name of the library, a string.
It can omit the suffix (a.k.a. file-name extension) if NOSUFFIX is
nil (which is the default, see below).
This command searches the directories in `load-path' like `\\[load-library]'
to find the file that `\\[load-library] RET LIBRARY RET' would load.
Optional second arg NOSUFFIX non-nil means don't add suffixes `load-suffixes'
to the specified name LIBRARY.
If the optional third arg PATH is specified, that list of directories
is used instead of `load-path'.
When called from a program, the file name is normally returned as a
string. When run interactively, the argument INTERACTIVE-CALL is t,
and the file name is displayed in the echo area."
(interactive (list (completing-read "Locate library: "
(apply-partially
'locate-file-completion-table
load-path (get-load-suffixes)))
nil nil t))
(let ((file (locate-file (substitute-in-file-name library)
(or path load-path)
(packed-el-suffixes nosuffix))))
(when interactive-call
(if file
(message "Library is file %s" (abbreviate-file-name file))
(message "No library %s in search path" library)))
file))
(defun packed-ignore-directory-p (directory)
"Return t if DIRECTORY is being ignored when searching for libraries.
DIRECTORY and all libraries it and its subdirectories contain
are being ignored if it contains a file named \".nosearch\" or
if it is a hidden directory."
(or (string-prefix-p "." (file-name-nondirectory
(directory-file-name directory)))
(file-exists-p (expand-file-name ".nosearch" directory))))
(defmacro packed-with-file (file &rest body)
"Execute BODY in a buffer containing the contents of FILE.
If FILE is nil or equal to `buffer-file-name' execute BODY in the
current buffer. Move to beginning of buffer before executing BODY.
FILE should be an Emacs lisp source file."
(declare (indent 1) (debug t))
(let ((filesym (make-symbol "--file--")))
`(let ((,filesym ,file))
(save-match-data
(save-excursion
(if (and ,filesym (not (equal ,filesym buffer-file-name)))
(with-temp-buffer
(insert-file-contents ,filesym)
(setq buffer-file-name ,filesym)
(set-buffer-modified-p nil)
(with-syntax-table emacs-lisp-mode-syntax-table
,@body))
(goto-char (point-min))
(with-syntax-table emacs-lisp-mode-syntax-table
,@body)))))))
(defun packed-library-p (file)
"Return non-nil if FILE is an Emacs source library.
Actually return the feature provided by FILE.
An Emacs lisp file is considered to be a library if it provides
the correct feature; that is a feature that matches its filename
\(and possibly parts of the path leading to it)."
(and (let ((filename (file-name-nondirectory file)))
(save-match-data
(and (string-match (packed-el-regexp) filename)
(not (or (file-symlink-p file)
(string-equal filename dir-locals-file)
(auto-save-file-name-p filename))))))
(packed-library-feature file)))
(defun packed-libraries (directory &optional full nonrecursive)
"Return a list of libraries that are part of PACKAGE located in DIRECTORY.
DIRECTORY is assumed to contain the libraries belonging to a
single package.
If optional FULL is non-nil return absolute paths otherwise paths
relative to DIRECTORY.
If optional NONRECURSIVE only return libraries directly located
in DIRECTORY."
(cl-mapcan (pcase-lambda (`(,library . ,feature))
(and feature
(list (if full
library
(file-relative-name library directory)))))
(packed-libraries-1 directory nonrecursive)))
(defun packed-libraries-1 (directory &optional nonrecursive)
"Return a list of Emacs lisp files DIRECTORY and its subdirectories.
The return value has the form ((LIBRARY . FEATURE)...). FEATURE
is nil if LIBRARY does not provide a feature or only features
that don't match the filename."
(let (libraries)
(dolist (f (directory-files directory t "^[^.]"))
(cond ((file-directory-p f)
(or nonrecursive
(packed-ignore-directory-p f)
(setq libraries (nconc (packed-libraries-1 f) libraries))))
((string-match (packed-el-regexp)
(file-name-nondirectory f))
(push (cons f (packed-library-p f)) libraries))))
(nreverse libraries)))
(defun packed-main-library (directory &optional package noerror nosingle)
"Return the main library from the package directory DIRECTORY.
Optional PACKAGE is the name of the package; if it is nil the
basename of DIRECTORY is used as the package name.
Return the library whose basename matches the package name. If
that fails append \"-mode\" to the package name, respectively
remove that substring, and try again.
The library must provide the correct feature; that is the feature
which matches the filename (and possibly parts of the path leading
to it).
Unless optional NOSINGLE is non-nil and if there is only a single
Emacs lisp file return that even if it doesn't match the package
name.
If the main library cannot be found raise an error or if optional
NOERROR is non-nil return nil."
(packed-main-library-1
(or package (file-name-nondirectory (directory-file-name directory)))
(packed-libraries-1 directory)
noerror nosingle))
(defun packed-main-library-1 (package libraries &optional noerror nosingle)
"Return the main library among LIBRARIES of the package PACKAGE.
PACKAGE is a package name, a string. LIBRARIES is a list of full
library filenames or an alist as returned by `packed-libraries-1'.
In the latter case also ensure that the main library provides the
correct feature.
Return the library whose basename matches the package name. If
that fails append \"-mode\" to the package name, respectively
remove that substring, and try again.
Unless optional NOSINGLE is non-nil and if there is only a single
Emacs lisp file return that even if it doesn't match the package
name.
If no library matches raise an error or if optional NOERROR is
non-nil return nil."
(let ((match
(cond ((and (not nosingle)
(not (cdr libraries)))
(car libraries))
((packed-main-library-2 package libraries))
((packed-main-library-2
(if (string-match "-mode$" package)
(substring package 0 -5)
(concat package "-mode"))
libraries)))))
(cond ((and (not match)
(not noerror))
(error "Cannot determine main library of %s" package))
((atom match)
match)
((cdr match)
(car match))
((not noerror)
(error "Main library %s provides no or wrong feature"
(car match))))))
(defun packed-main-library-2 (package libraries)
(let ((regexp (concat "^" (regexp-quote package) (packed-el-regexp) "$")))
(cl-find-if (lambda (lib)
(string-match regexp (file-name-nondirectory
(if (consp lib) (car lib) lib))))
libraries)))
;;; Load Path
(defun packed-add-to-load-path (directory)
"Add DIRECTORY and subdirectories to `load-path' if they contain libraries."
(dolist (d (packed-load-path directory))
(add-to-list 'load-path d)))
(defun packed-remove-from-load-path (directory)
"Remove DIRECTORY and its subdirectories from `load-path'.
Elements of `load-path' which no longer exist are not removed."
(setq directory (directory-file-name (expand-file-name directory)))
(setq load-path (delete directory load-path))
(dolist (f (directory-files directory t "^[^.]" t))
(when (file-directory-p f)
(packed-remove-from-load-path f))))
(defun packed-load-path (directory)
"Return a list of directories below DIRECTORY that contain libraries."
(let (lp in-lp)
(dolist (f (directory-files directory t "^[^.]"))
(cond ((file-regular-p f)
(and (not in-lp)
(packed-library-p f)
(push (directory-file-name directory) lp)
(setq in-lp t)))
((file-directory-p f)
(unless (packed-ignore-directory-p f)
(setq lp (nconc (packed-load-path f) lp))))))
lp))
;;; Byte Compile
(defmacro packed-without-mode-hooks (&rest body)
(declare (indent 0))
`(let (after-change-major-mode-hook
prog-mode-hook
emacs-lisp-mode-hook)
,@body))
(defun packed-byte-compile-file (filename)
"Like `byte-compile-file' but don't run any mode hooks."
(packed-without-mode-hooks (byte-compile-file filename)))
(defun packed-compile-package (directory &optional force)
(unless noninteractive
(save-some-buffers)
(force-mode-line-update))
(with-current-buffer (get-buffer-create byte-compile-log-buffer)
(setq default-directory (expand-file-name directory))
(unless (eq major-mode 'compilation-mode)
(compilation-mode))
(let ((default-directory default-directory)
(skip-count 0)
(fail-count 0)
(lib-count 0)
(dir-count 0)
file dir last-dir)
(displaying-byte-compile-warnings
(dolist (elt (packed-libraries-1 directory))
(setq file (car elt)
dir (file-name-nondirectory file))
(if (cdr elt)
(cl-incf (pcase (byte-recompile-file file force 0)
(`no-byte-compile skip-count)
(`t lib-count)
(_ fail-count)))
(setq skip-count (1+ skip-count)))
(unless (eq last-dir dir)
(setq last-dir dir dir-count (1+ dir-count)))))
(message "Done (Total of %d file%s compiled%s%s%s)"
lib-count (if (= lib-count 1) "" "s")
(if (> fail-count 0) (format ", %d failed" fail-count) "")
(if (> skip-count 0) (format ", %d skipped" skip-count) "")
(if (> dir-count 1)
(format " in %d director%s" dir-count
(if (= dir-count 1) "y" "ies"))
"")))))
;;; Autoloads
(defun packed-loaddefs-file (&optional file)
"Starting at FILE, look up directory hierarchy for an autoloads file.
An autoloads file is either named \"loaddefs.el\" or its name ends
with \"-autoloads.el\". FILE can be a file or a directory. If
it's a file, its directory will serve as the starting point for
searching the hierarchy of directories. Stop at the first parent
directory containing such a file, and return the file. Return
nil if not found."
(unless file
(setq file default-directory))
(setq file (abbreviate-file-name (expand-file-name file)))
(let (found)
(while (not (or found
(null file)
(string-match locate-dominating-stop-dir-regexp file)))
(unless (setq found
(car (directory-files
file t "\\(\\`loaddefs\\.el\\|-autoloads.el\\)\\'")))
(when (equal file
(setq file
(file-name-directory (directory-file-name file))))
(setq file nil))))
found))
(defun packed-load-loaddefs (&optional directory)
(let ((file (packed-loaddefs-file directory)))
(if file
(load file)
(message "Cannot locate loaddefs file for %s" directory))))
(defmacro packed-with-loaddefs (dest &rest body)
(declare (indent 1))
`(packed-without-mode-hooks
(require 'autoload)
(let ((generated-autoload-file ,dest) buf)
(prog1 (progn ,@body)
(while (setq buf (find-buffer-visiting generated-autoload-file))
(with-current-buffer buf
(save-buffer)
(kill-buffer)))))))
(defun packed-update-autoloads (dest path)
(packed-with-loaddefs dest
(cond ((fboundp 'make-directory-autoloads) ; >= 28
(make-directory-autoloads path generated-autoload-file))
((fboundp 'update-directory-autoloads) ; <= 27
(update-directory-autoloads path)))))
(defun packed-remove-autoloads (dest path)
(packed-with-loaddefs dest
;; `autoload-find-destination' clears out autoloads associated
;; with a file if they are not found in the current buffer
;; anymore (which is the case here because it is empty).
(with-temp-buffer
(let ((autoload-modified-buffers (list (current-buffer))))
(dolist (d path)
(when (file-directory-p d)
(dolist (f (directory-files d t (packed-el-regexp)))
(autoload-find-destination
f (autoload-file-load-name f)))))))))
;;; Features
(defconst packed-provided-regexp "\
\(\\(?:cc-\\|silentcomp-\\)?provide[\s\t\n]+'\
\\([^(),\s\t\n]+\\)\\(?:[\s\t\n]+'\
\(\\([^(),]+\\))\\)?)")
(defun packed-provided ()
(let (features)
(save-excursion
(goto-char (point-min))
(while (re-search-forward packed-provided-regexp nil t)
(unless (save-match-data
(or (nth 3 (syntax-ppss)) ; in string
(nth 4 (syntax-ppss)))) ; in comment
(dolist (feature (cons (match-string 1)
(let ((f (match-string 2)))
(and f (split-string f " " t)))))
(push (intern feature) features)))))
(or features
(and (goto-char (point-min))
(re-search-forward
"^(provide-theme[\s\t\n]+'\\([^)]+\\))" nil t)
(list (intern (concat (match-string 1)
"-theme"))))
(and (goto-char (point-min))
(re-search-forward
"^(provide-me\\(?:[\s\t\n]+\"\\(.+\\)\"\\)?)" nil t)
(list (intern (concat (match-string 1)
(file-name-sans-extension
(file-name-nondirectory
buffer-file-name)))))))))
(defun packed-library-feature (file)
"Return the first valid feature actually provided by FILE.
Here valid means that requiring that feature would actually load FILE.
Normally that is the case when the feature matches the filename, e.g.
when \"foo.el\" provides `foo'. But if \"foo.el\"s parent directory's
filename is \"bar\" then `bar/foo' would also be valid. Of course this
depends on the actual value of `load-path', here we just assume that it
allows for file to be found.
This can be used to determine if an Emacs lisp file should be considered
a library. Not every Emacs lisp file has to provide a feature / be a
library. If a file lacks an expected feature then loading it using
`require' still succeeds but causes an error."
(let* ((file (expand-file-name file))
(sans (file-name-sans-extension (file-name-sans-extension file)))
(last (file-name-nondirectory sans)))
(cl-find-if (lambda (feature)
(setq feature (symbol-name feature))
(or (equal feature last)
(string-suffix-p (concat "/" feature) sans)))
(packed-with-file file (packed-provided)))))
(defconst packed-required-regexp "\
\(\\(?:cc-\\)?require[\s\t\n]+'\
\\([^(),\s\t\n\"]+\\)\
\\(?:\\(?:[\s\t\n]+\\(?:nil\\|\"[^\"]*\"\\)\\)\
\\(?:[\s\t\n]+\\(?:nil\\|\\(t\\)\\)\\)?\\)?)")
(defun packed-required ()
(let (hard soft)
(save-excursion
(goto-char (point-min))
(while (re-search-forward packed-required-regexp nil t)
(let ((feature (intern (match-string 1))))
(cond ((save-match-data
(or (nth 3 (syntax-ppss)) ; in string
(nth 4 (syntax-ppss))))) ; in comment
((match-string 2)
(push feature soft))
(t
(push feature hard))))))
(list hard soft)))
;;; _
(provide 'packed)
;; Local Variables:
;; indent-tabs-mode: nil
;; End:
;;; packed.el ends here

View File

@ -22,11 +22,3 @@
;; the package manager before loading the init file, so this file is neither
;; needed nor loaded on those versions.
(setq package-enable-at-startup nil)
;; Auto compilation.
(setq load-prefer-newer t)
(load (concat user-emacs-directory "core/libs/packed") nil t)
(load (concat user-emacs-directory "core/libs/auto-compile") nil t)
(auto-compile-on-load-mode)
(auto-compile-on-save-mode)
;;; early-init.el ends here

View File

@ -25,6 +25,11 @@
(load (concat spacemacs-core-directory "core-dumper.el")
nil (not init-file-debug))
(load (concat spacemacs-core-directory "core-compilation.el")
nil (not init-file-debug))
(let ((default-directory spacemacs-start-directory))
(spacemacs//remove-byte-compiled-if-stale spacemacs--compiled-files))
(if (not (version<= spacemacs-emacs-min-version emacs-version))
(error (concat "Your version of Emacs (%s) is too old. "
"Spacemacs requires Emacs version %s or above.")