Toggle diff (383 lines)
diff --git a/doc/contributing.texi b/doc/contributing.texi
index 4195cb4105..1081ddc6d6 100644
--- a/doc/contributing.texi
+++ b/doc/contributing.texi
@@ -652,7 +652,12 @@ enabled by setting the @code{#:tests?} argument to @code{#true}. By
default, the command to run the test is @command{make check}, but any
command can be specified via the @code{#:test-command} argument. The
@code{#:test-command} argument expects a list containing a command and
-its arguments, to be invoked during the @code{check} phase.
+its arguments, to be invoked during the @code{check} phase. The Elisp
+libraries discovery mechanism used in Guix relies on the site file being
+run. For this reason, scripts or Makefile recipes must not invoke Emacs
+with the @option{--no-site-file}, @option{--quick} or @option{-Q}
+options as this would cause the Elisp dependencies added as inputs to
+appear unavailable.
The Elisp dependencies of Emacs packages are typically provided as
@code{propagated-inputs} when required at run time. As for other
diff --git a/doc/guix.texi b/doc/guix.texi
index 392baf5910..74713b1331 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -1887,19 +1887,27 @@ information.
@subsection Emacs Packages
@cindex @code{emacs}
-When you install Emacs packages with Guix, the Elisp files are placed
-under the @file{share/emacs/site-lisp/} directory of the profile in
-which they are installed. The Elisp libraries are made available to
-Emacs through the @env{EMACSLOADPATH} environment variable, which is
-set when installing Emacs itself.
-
-Additionally, autoload definitions are automatically evaluated at the
-initialization of Emacs, by the Guix-specific
-@code{guix-emacs-autoload-packages} procedure. If, for some reason, you
-want to avoid auto-loading the Emacs packages installed with Guix, you
-can do so by running Emacs with the @option{--no-site-file} option
-(@pxref{Init File,,, emacs, The GNU Emacs Manual}).
-
+When you install Emacs packages with Guix, the Elisp files are placed in
+their own directory under the @file{share/emacs/site-lisp/guix}
+installation prefix of the profile in which they are installed. The
+Elisp libraries are made available to Emacs through the
+@env{EMACSLOADPATH} environment variable, which is set when installing
+Emacs itself.
+
+@cindex package activation, emacs
+@cindex guix-package-initialize
+Additionally, the packages are ``activated'', that is, their autoload
+definitions are evaluated and their specific installation directory
+added to the load path at the initialization of Emacs, by the
+Guix-specific @code{guix-package-initialize} procedure. This procedure
+can also be called interactively in Emacs to refresh the list of
+available packages, for example, after installing new Emacs packages to
+the user profile.
+
+If, for some reason, you want to avoid auto-loading the Emacs packages
+installed with Guix, you can do so by running Emacs with the
+@option{--no-site-file} option (@pxref{Init File,,, emacs, The GNU Emacs
+Manual}).
@node Upgrading Guix
@section Upgrading Guix
@@ -7940,7 +7948,8 @@ It first creates the @code{@code{package}-autoloads.el} file, then it
byte compiles all Emacs Lisp files. Differently from the Emacs
packaging system, the Info documentation files are moved to the standard
documentation directory and the @file{dir} file is deleted. The Elisp
-package files are installed directly under @file{share/emacs/site-lisp}.
+package files are installed in their own directory under the
+@file{share/site-lisp/guix} installation prefix.
@end defvr
@defvr {Scheme Variable} font-build-system
diff --git a/gnu/packages/aux-files/emacs/guix-emacs.el b/gnu/packages/aux-files/emacs/guix-emacs.el
index ca9146c535..a3c0eaa6fc 100644
--- a/gnu/packages/aux-files/emacs/guix-emacs.el
+++ b/gnu/packages/aux-files/emacs/guix-emacs.el
@@ -1,8 +1,8 @@
-;;; guix-emacs.el --- Emacs packages installed with Guix
+;;; guix-emacs.el --- Autoload Emacs packages installed with Guix
;; Copyright © 2014, 2015, 2016, 2017 Alex Kost <alezost@gmail.com>
;; Copyright © 2017 Kyle Meyer <kyle@kyleam.com>
-;; Copyright © 2019 Maxim Cournoyer <maxim.cournoyer@gmail.com>
+;; Copyright © 2019, 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;; This file is part of GNU Guix.
@@ -21,43 +21,84 @@
;;; Commentary:
-;; This file provides auxiliary code to autoload Emacs packages
+;; This file provides auxiliary code to initialize the Emacs packages
;; installed with Guix.
;;; Code:
+(require 'package)
(require 'seq)
(defvar guix-emacs-autoloads-regexp
(rx (* any) "-autoloads.el" (zero-or-one "c") string-end)
- "Regexp to match Emacs 'autoloads' file.")
+ "Regexp to match 'autoloads' file.")
(defun guix-emacs-find-autoloads (directory)
- "Return a list of Emacs 'autoloads' files in DIRECTORY.
+ "Return a list of 'autoloads' files in DIRECTORY.
The files in the list do not have extensions (.el, .elc)."
;; `directory-files' doesn't honor group in regexp.
(delete-dups (mapcar #'file-name-sans-extension
(directory-files directory 'full-name
guix-emacs-autoloads-regexp))))
+(defvar guix-emacs-package-initialize-ran-p nil
+ "Non-nil if `guix-emacs-package-initialize' has run.")
+
;;;###autoload
-(defun guix-emacs-autoload-packages ()
- "Autoload Emacs packages found in EMACSLOADPATH.
+(defun guix-emacs-package-initialize ()
+ "Initialize the Emacs packages.
-'Autoload' means to load the 'autoloads' files matching
-`guix-emacs-autoloads-regexp'."
+Load the autoloads files of the packages, and add their directory
+to the Emacs load path if they aren't already."
(interactive)
- (let* ((emacs-non-core-load-path-directories
- ;; Filter out core Elisp directories, which are already autoloaded
- ;; by Emacs.
- (seq-filter (lambda (dir)
- (string-match-p "/share/emacs/site-lisp" dir))
- load-path))
- (autoloads (mapcan #'guix-emacs-find-autoloads
- emacs-non-core-load-path-directories)))
+ (let* (;; Filter out core Elisp directories, which have already been
+ ;; autoloaded by Emacs.
+ (load-path* (seq-filter (lambda (f)
+ (string-match-p "/share/emacs/site-lisp" f))
+ load-path))
+ (autoloads (mapcan #'guix-emacs-find-autoloads load-path*))
+ (package-directory-list* (mapcar (lambda (f)
+ (expand-file-name "guix" f))
+ load-path*))
+ (user-emacs-directory-warning nil) ;squelch extraneous warnings
+ (package-user-dir "")) ;do not activate package.el user packages
+
+ ;; Autoload packages installed without using 'emacs-build-system'; these
+ ;; are found directly under the site-lisp directory (flat hierarchy).
(mapc (lambda (f)
- (load f 'noerror))
- autoloads)))
+ (with-demoted-errors "Error loading autoloads: %s"
+ (load f nil t)))
+ autoloads)
+
+ ;; Set the package-directory-list variable to a meaningful default value
+ ;; on Guix, which for 'package.el' is akin to system packages. It's
+ ;; important that this variable contains the correct value to see the
+ ;; Guix packages listed (as 'external') in M-x package-list.
+ (setq-default package-directory-list package-directory-list*)
+
+ ;; Initialize the Guix-installed Emacs packages.
+ (package-initialize)
+
+ (unless guix-emacs-package-initialize-ran-p
+ ;; If this is running the first time, hide the fact that
+ ;; `'package-initialization' has run in order to leave the default
+ ;; behavior of package.el unchanged for its users (that is, to run
+ ;; automatically after the early-init.el file but before the user init
+ ;; file -- this can be useful if they configured their `'package-user-dir'
+ ;; variable).
+ (setq package--initialized nil)
+ (setq package--activated nil))
+
+ (setq guix-emacs-package-initialize-ran-p t)))
+
+;;;###autoload
+(defalias 'guix-package-initialize #'guix-emacs-package-initialize)
+
+;;;###autoload
+(define-obsolete-function-alias
+ 'guix-emacs-autoload-packages 'guix-package-initialize "20201218")
+;;; The symbol name is suffixed by -emacs to avoid a name clash with
+;;; Emacs-Guix's guix.el.
(provide 'guix-emacs)
;;; guix-emacs.el ends here
diff --git a/gnu/packages/emacs.scm b/gnu/packages/emacs.scm
index ca14584ada..98f886278a 100644
--- a/gnu/packages/emacs.scm
+++ b/gnu/packages/emacs.scm
@@ -145,8 +145,8 @@
"pwd"))
#t))
(add-after 'install 'install-site-start
- ;; Use 'guix-emacs' in "site-start.el", which is used autoload the
- ;; Elisp packages found in EMACSLOADPATH.
+ ;; Use 'guix-emacs' in "site-start.el", which is used to activate
+ ;; the Elisp packages found in EMACSLOADPATH.
(lambda* (#:key inputs outputs #:allow-other-keys)
(let* ((out (assoc-ref outputs "out"))
(lisp-dir (string-append out "/share/emacs/site-lisp"))
@@ -158,18 +158,17 @@
(setq byte-compile-debug t)
(byte-recompile-directory
(file-name-as-directory ,dir) 0 1))))
- (invoke emacs "--quick" "--batch"
- (format #f "--eval=~s" expr))))
+ (invoke emacs "--batch" (format #f "--eval=~s" expr))))
(copy-file (assoc-ref inputs "guix-emacs.el")
(string-append lisp-dir "/guix-emacs.el"))
(with-output-to-file (string-append lisp-dir "/site-start.el")
(lambda ()
(display
- (string-append "(when (require 'guix-emacs nil t)\n"
- " (guix-emacs-autoload-packages))\n"))))
+ (string-append "(require 'guix-emacs)\n"
+ "(guix-package-initialize)\n"))))
;; Remove the extraneous subdirs.el file, as it causes Emacs to
- ;; add recursively all the the sub-directories of a profile's
+ ;; add recursively all the sub-directories of a profile's
;; share/emacs/site-lisp union when added to EMACSLOADPATH,
;; which leads to conflicts.
(delete-file (string-append lisp-dir "/subdirs.el"))
diff --git a/guix/build/emacs-build-system.scm b/guix/build/emacs-build-system.scm
index 26ea59bc25..2846926298 100644
--- a/guix/build/emacs-build-system.scm
+++ b/guix/build/emacs-build-system.scm
@@ -2,7 +2,7 @@
;;; Copyright © 2015 Federico Beffa <beffa@fbengineering.ch>
;;; Copyright © 2016 David Thompson <davet@gnu.org>
;;; Copyright © 2016 Alex Kost <alezost@gmail.com>
-;;; Copyright © 2018, 2019 Maxim Cournoyer <maxim.cournoyer@gmail.com>
+;;; Copyright © 2018, 2019, 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -32,7 +32,8 @@
#:export (%standard-phases
%default-include
%default-exclude
- emacs-build))
+ emacs-build
+ outputs->elpa-install-dir))
;; Commentary:
;;
@@ -40,10 +41,13 @@
;;
;; Code:
-;;; All the packages are installed directly under site-lisp, which means that
-;;; having that directory in the EMACSLOADPATH is enough to have them found by
-;;; Emacs.
-(define %install-dir "/share/emacs/site-lisp")
+;;; All the packages are installed under their own directory under
+;;; site-lisp/guix, to prevent file name collisions in profiles. This means
+;;; that some machinery is needed to activate the packages, since they aren't
+;;; directly on the load path. This is taken care of by the
+;;; 'guix-package-initialize' procedure called from the site-start.el file
+;;; shipped with Emacs from Guix.
+(define %install-prefix "/share/emacs/site-lisp/guix")
;; These are the default inclusion/exclusion regexps for the install phase.
(define %default-include '("^[^/]*\\.el$" "^[^/]*\\.info$" "^doc/.*\\.info$"))
@@ -62,6 +66,26 @@ name that has been stripped of the hash and version number."
(strip-store-file-name file) suffix))))
(string-append name suffix))))
+(define (version->elpa-version version)
+ "Sanitize VERSION into an ELPA-compatible version.
+
+package.el uses the 'version-to-list' procedure from the 'subr' Emacs library,
+which supports a very limited set of version snapshot annotations, defined in
+the 'version-regexp-alist' variable of the same library.
+
+Strip the least significant components of a version string, so that a Guix
+version such as \"0.4.1-1.a41d5cc\" becomes the valid ELPA version \"0.4.1\".
+In case a valid ELPA version cannot be derived from VERSION, \"0.0.0\" is
+returned."
+ (let ((m (string-match "[0-9]+(\\.[0-9]+)*" version)))
+ (if m
+ (match:substring m)
+ (begin
+ (format (current-error-port)
+ "warning: failed producing an ELPA-compatible \
+version string from ~s; using \"0.0.0\"~%" version)
+ "0.0.0"))))
+
(define* (unpack #:key source #:allow-other-keys)
"Unpack SOURCE into the build directory. SOURCE may be a compressed
archive, a directory, or an Emacs Lisp file."
@@ -93,13 +117,12 @@ archive, a directory, or an Emacs Lisp file."
environment variable\n" source-directory)))
(define* (build #:key outputs inputs #:allow-other-keys)
- "Compile .el files."
- (let* ((emacs (string-append (assoc-ref inputs "emacs") "/bin/emacs"))
- (out (assoc-ref outputs "out"))
- (site-lisp (string-append out %install-dir)))
+ "Byte compile .el files."
+ (let ((emacs (string-append (assoc-ref inputs "emacs") "/bin/emacs"))
+ (install-dir (outputs->elpa-install-dir outputs)))
(setenv "SHELL" "sh")
(parameterize ((%emacs emacs))
- (emacs-byte-compile-directory site-lisp))))
+ (emacs-byte-compile-directory install-dir))))
(define* (patch-el-files #:key outputs #:allow-other-keys)
"Substitute the absolute \"/bin/\" directory with the right location in the
@@ -115,13 +138,12 @@ store in '.el' files."
(else (loop (read-line in 'concat))))))
#:binary #t))
- (let* ((out (assoc-ref outputs "out"))
- (site-lisp (string-append out %install-dir))
- ;; (ice-9 regex) uses libc's regexp routines, which cannot deal with
- ;; strings containing NULs. Filter out such files. TODO: Remove
- ;; this workaround when <https://bugs.gnu.org/30116> is fixed.
- (el-files (remove file-contains-nul-char?
- (find-files (getcwd) "\\.el$"))))
+ (let ((install-dir (outputs->elpa-install-dir outputs))
+ ;; (ice-9 regex) uses libc's regexp routines, which cannot deal with
+ ;; strings containing NULs. Filter out such files. TODO: Remove
+ ;; this workaround when <https://bugs.gnu.org/30116> is fixed.
+ (el-files (remove file-contains-nul-char?
+ (find-files (getcwd) "\\.el$"))))
(define (substitute-program-names)
(substitute* el-files
(("\"/bin/([^.]\\S*)\"" _ cmd-name)
@@ -130,7 +152,7 @@ store in '.el' files."
(error "patch-el-files: unable to locate " cmd-name))
(string-append "\"" cmd "\"")))))
- (with-directory-excursion site-lisp
+ (with-directory-excursion install-dir
;; Some old '.el' files (e.g., tex-buf.el in AUCTeX) are still
;; ISO-8859-1-encoded.
(unless (false-if-exception (substitute-program-names))
@@ -180,15 +202,14 @@ parallel. PARALLEL-TESTS? is ignored when using a non-make TEST-COMMAND."
(and (any (cut match-stripped-file "included" <>) include)
(not (any (cut match-stripped-file "excluded" <>) exclude)))))
- (let* ((out (assoc-ref outputs "out"))
- (site-lisp (string-append out %install-dir))
+ (let ((install-dir (outputs->elpa-install-dir outputs))
(files-to-install (find-files source install-file?)))
(cond
((not (null? files-to-install))
(for-each
(lambda (file)
(let* ((stripped-file (string-drop file (string-length source)))
- (target-file (string-append site-lisp stripped-file)))
+ (target-file (string-append install-dir stripped-file)))
(format #t "`~a' -> `~a'~%" file target-file)
(install-file file (dirname target-file))))
files-to-install)
@@ -199,15 +220,31 @@ parallel. PARALLEL-TESTS? is ignored when using a non-make TEST-COMMAND."
(install-file? file stat #:verbose? #t)))
#f))))
+(define* (create-pkg.el #:key outputs #:allow-other-keys)
+ "Generate the name-pkg.el file required by package.el."
+ (let*-values (((install-dir) (outputs->elpa-install-dir outputs))
+ ((elpa-name-ver) (store-directory->elpa-name-version
+ (assoc-ref outputs "out")))
+ ((name version) (package-name->name+version elpa-name-ver))
+ ((elpa-version) (version->elpa-version version)))
+ (call-with-output-file (string-append install-dir "/" name "-pkg.el")
+ (lambda (port)
+ ;; Note: the file cannot be byte compiled, as the loader expects a
+ ;; single form present, which leaves no option to include (require
+ ;; 'package).
+ (display ";; Generated by Guix -*- no-byte-compile: t -*-\n" port)
+ (write `(define-package ,name ,elpa-version) port)
+ (display "\n" port)
+ #t))))
+
(define* (move-doc #:key outputs #:allow-other-keys