Subject: [PATCH 01/26] build-systems/emacs: Install packages in their own directory.

From: Maxim Cournoyer <[email-masked]>
Date: Fri, 18 Dec 2020 17:16:58 -0500

This avoids the risk of file name collisions between Emacs packages installed
to a profile.  One example of such a clash is the 'snippets' directory
installed by both the emacs-elpy and emacs-yasnippet-snippets packages.

* guix/build/emacs-build-system.scm (%install-prefix): Append '/guix' to the
existing %install-dir prefix.
(version->elpa-version): New procedure.
(build, patch-el-files, install, move-doc, make-autoloads): Adjust phases.
(create-pkg.el): New phase.
(outputs->elpa-install-dir): New procedure.
(%standard-phases): Register the new 'create-pkg.el phase.
* guix/build/emacs-utils.scm (%emacs-quick-arguments): New variable.
(emacs-batch-eval): Remove the "--quick" option, and use
%emacs-quick-arguments instead.
(emacs-batch-edit-file): Likewise.
* gnu/packages/aux-files/emacs/guix-emacs.el: Update various comments.
(guix-emacs-package-initialize-ran-p): New variable.
(guix-emacs-package-initialize): New procedure, to replace
guix-emacs-autoload-packages.  Use 'package-initialize' from the 'package'
library of Emacs to do the initialization (load path configuration and
autoloads loading) of the Emacs packages.  Use the same autoload invocation as
'package-initialize', to harmonize the output.
(guix-package-initialize): New alias.
(guix-emacs-autoload-packages): New deprecated alias.
* gnu/packages/emacs.scm (emacs)[phases]{install-site-start}: Adjust for the
auxiliary file name change and new procedure name.  Streamline the require
directive: the 'guix-emacs library is shipped with our Emacs package; it
cannot be missing.  Fix typo in a comment.  Do not use the "--quick" option
when byte compiling.
* doc/guix.texi: Update doc.
---
 doc/contributing.texi                      |   7 +-
 doc/guix.texi                              |  37 ++++---
 gnu/packages/aux-files/emacs/guix-emacs.el |  79 +++++++++++----
 gnu/packages/emacs.scm                     |  13 ++-
 guix/build/emacs-build-system.scm          | 109 +++++++++++++++------
 guix/build/emacs-utils.scm                 |  20 ++--
 6 files changed, 186 insertions(+), 79 deletions(-)

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 <[email-masked]>
 ;; Copyright © 2017 Kyle Meyer <[email-masked]>
-;; Copyright © 2019 Maxim Cournoyer <[email-masked]>
+;; Copyright © 2019, 2020 Maxim Cournoyer <[email-masked]>
 
 ;; 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 <[email-masked]>
 ;;; Copyright © 2016 David Thompson <[email-masked]>
 ;;; Copyright © 2016 Alex Kost <[email-masked]>
-;;; Copyright © 2018, 2019 Maxim Cournoyer <[email-masked]>
+;;; Copyright © 2018, 2019, 2020 Maxim Cournoyer <[email-masked]>
 ;;;
 ;;; 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)
   "Move info files from the ELPA package directory to the info directory."
-  (let* ((out (assoc-ref outputs "out"))
-         (site-lisp (string-append out %install-dir))
-         (info-dir (string-append out "/share/info/"))
-         (info-files (find-files site-lisp "\\.info$")))
+  (let* ((install-dir (outputs->elpa-install-dir outputs))
+         (info-dir (string-append (assoc-ref outputs "out") "/share/info/"))
+         (info-files (find-files install-dir "\\.info$")))
     (unless (null? info-files)
       (mkdir-p info-dir)
-      (with-directory-excursion site-lisp
+      (with-directory-excursion install-dir
         (when (file-exists? "dir") (delete-file "dir"))
         (for-each (lambda (f)
                     (copy-file f (string-append info-dir "/" (basename f)))
@@ -218,12 +255,12 @@ parallel. PARALLEL-TESTS? is ignored when using a non-make TEST-COMMAND."
 (define* (make-autoloads #:key outputs inputs #:allow-other-keys)
   "Generate the autoloads file."
   (let* ((emacs (string-append (assoc-ref inputs "emacs") "/bin/emacs"))
-         (out (assoc-ref outputs "out"))
-         (site-lisp (string-append out %install-dir))
-         (elpa-name-ver (store-directory->elpa-name-version out))
+         (install-dir (outputs->elpa-install-dir outputs))
+         (elpa-name-ver (store-directory->elpa-name-version
+                         (assoc-ref outputs "out")))
          (elpa-name (package-name->name+version elpa-name-ver)))
     (parameterize ((%emacs emacs))
-      (emacs-generate-autoloads elpa-name site-lisp))))
+      (emacs-generate-autoloads elpa-name install-dir))))
 
 (define* (enable-autoloads-compilation #:key outputs #:allow-other-keys)
   "Remove the NO-BYTE-COMPILATION local variable embedded in the generated
@@ -258,6 +295,13 @@ second hyphen.  This corresponds to 'name-version' as used in ELPA packages."
             strip-store-file-name)
    store-dir))
 
+(define (outputs->elpa-install-dir outputs)
+  "Given OUTPUTS, a package outputs, return the ELPA-like Guix installation
+directory of the Emacs package."
+  (let ((out (assoc-ref outputs "out")))
+    (string-append out %install-prefix "/"
+                   (store-directory->elpa-name-version out))))
+
 (define %standard-phases
   (modify-phases gnu:%standard-phases
     (replace 'unpack unpack)
@@ -267,7 +311,8 @@ second hyphen.  This corresponds to 'name-version' as used in ELPA packages."
     (delete 'build)
     (replace 'check check)
     (replace 'install install)
-    (add-after 'install 'make-autoloads make-autoloads)
+    (add-after 'install 'create-pkg.el create-pkg.el)
+    (add-after 'create-pkg.el 'make-autoloads make-autoloads)
     (add-after 'make-autoloads 'enable-autoloads-compilation
       enable-autoloads-compilation)
     (add-after 'enable-autoloads-compilation 'patch-el-files patch-el-files)
diff --git a/guix/build/emacs-utils.scm b/guix/build/emacs-utils.scm
index 5f7ba71244..485218d040 100644
--- a/guix/build/emacs-utils.scm
+++ b/guix/build/emacs-utils.scm
@@ -23,6 +23,7 @@
   #:use-module (guix build utils)
   #:use-module (ice-9 format)
   #:export (%emacs
+            %emacs-quick-arguments
             emacs-batch-eval
             emacs-batch-edit-file
             emacs-batch-disable-compilation
@@ -42,6 +43,11 @@
   ;; The `emacs' command.
   (make-parameter "emacs"))
 
+(define %emacs-quick-arguments
+  ;; Like --quick (-Q), except the site-start.el file is loaded since it is
+  ;; required to load packages.
+  '("--no-init-file" "--no-site-lisp" "--no-splash" "--no-x-resources"))
+
 (define (expr->string expr)
   "Converts EXPR, an expression, into a string."
   (if (string? expr)
@@ -51,15 +57,17 @@
 (define* (emacs-batch-eval expr #:key dynamic?)
   "Run Emacs in batch mode, and execute the Elisp code EXPR.  If DYNAMIC? is
 true, evaluate using dynamic scoping."
-  (invoke (%emacs) "--quick" "--batch"
-          (format #f "--eval=(eval '~a ~:[t~;nil~])"
-                  (expr->string expr) dynamic?)))
+  (apply invoke (%emacs) "--batch"
+         (format #f "--eval=(eval '~a ~:[t~;nil~])"
+                 (expr->string expr) dynamic?)
+         %emacs-quick-arguments))
 
 (define (emacs-batch-edit-file file expr)
   "Load FILE in Emacs using batch mode, and execute the elisp code EXPR."
-  (invoke (%emacs) "--quick" "--batch"
-          (string-append "--visit=" file)
-          (string-append "--eval=" (expr->string expr))))
+  (apply invoke (%emacs) "--batch"
+         (string-append "--visit=" file)
+         (string-append "--eval=" (expr->string expr))
+         %emacs-quick-arguments))
 
 (define (emacs-batch-disable-compilation file)
   (emacs-batch-edit-file file
-- 
2.29.2