emacs: use PATH instead of exec-path to expose wrapped binaries

(info "(elisp) Subprocess Creation") says:

> Generally, you should not modify ‘exec-path’ directly.  Instead,
> ensure that your ‘PATH’ environment variable is set appropriately
> before starting Emacs.  Trying to modify ‘exec-path’ independently
> of ‘PATH’ can lead to confusing results.

One example of "confusing results" can be found in envrc[1].

This patch follows that recommendation of Emacs lisp manual.

In addition, we added tests for PATH and exec-path.

[1]: https://github.com/purcell/envrc/issues/9
This commit is contained in:
Lin Jian
2026-09-27 18:55:45 +08:00
parent 78805715a6
commit 88accc0c92
3 changed files with 32 additions and 5 deletions

View File

@@ -41,6 +41,13 @@ which is generated by nix when building this elisp package."
(defun with-packages-unwrapped-site-start-is-loaded ()
(fboundp 'nix--profile-paths))
(defun with-packages-eval-in-sub-emacs (form)
"Evaluate FORM in a subprocess Emacs and return its result."
(pcase-exhaustive
(process-lines "emacs" "--batch"
"--eval" (format "(prin1 %S)" form))
(`(,result) (read result))))
;;;; Tests that can be run in a batch Emacs
(ert-deftest with-packages-requested-packages-are-available ()
@@ -71,8 +78,24 @@ which is generated by nix when building this elisp package."
(ert-deftest with-packages-unwrapped-site-start-is-loaded ()
(should (with-packages-unwrapped-site-start-is-loaded)))
(ert-deftest with-packages-bin-dirs-of-requested-packages-are-added-to-exec-path ()
(should (executable-find "cowsay")))
(ert-deftest with-packages-binaries-of-requested-packages-are-available ()
(ert-info ("find binary via PATH")
(should (equal (call-process-shell-command "cowsay") 0)))
(ert-info ("find binary via exec-path")
(should (executable-find "cowsay")))
(ert-info ("each item is unique")
(cl-flet ((item-should-be-unique (items)
(should (equal items
(cl-remove-duplicates items :test #'string=)))))
(ert-info ("PATH of this Emacs")
(item-should-be-unique (parse-colon-path (getenv "PATH"))))
(ert-info ("PATH of sub-Emacs")
(item-should-be-unique
(parse-colon-path (with-packages-eval-in-sub-emacs '(getenv "PATH")))))
(ert-info ("exec-path of this Emacs")
(item-should-be-unique exec-path))
(ert-info ("exec-path of sub-Emacs")
(item-should-be-unique (with-packages-eval-in-sub-emacs 'exec-path))))))
(ert-deftest with-packages-tree-sitter-dir-is-added-to-treesit-extra-load-path ()
(skip-unless (treesit-available-p))

View File

@@ -171,9 +171,6 @@ runCommand (lib.appendToName "with-packages" emacs).name
cat >"$siteStart" <<EOF
;;; -*- lexical-binding: t -*-
(load "$emacs/share/emacs/site-lisp/site-start" nil t)
;; "$out/share/emacs/site-lisp" is added to load-path in wrapper.sh
;; "$out/share/emacs/native-lisp" is added to native-comp-eln-load-path in wrapper.sh
(add-to-list 'exec-path "$out/bin")
${lib.optionalString withTreeSitter ''
(add-to-list 'treesit-extra-load-path "$out/lib/")
''}
@@ -213,6 +210,7 @@ runCommand (lib.appendToName "with-packages" emacs).name
--subst-var-by bash ${emacs.stdenv.shell} \
--subst-var-by wrapperSiteLisp "$deps/share/emacs/site-lisp" \
--subst-var-by wrapperSiteLispNative "$deps/share/emacs/native-lisp" \
--subst-var-by wrapperBinDir "$deps/bin" \
--subst-var-by wrapperInvocationDirectory "$out/bin/" \
--subst-var-by wrapperInvocationName "$progname" \
--subst-var prog
@@ -239,6 +237,7 @@ runCommand (lib.appendToName "with-packages" emacs).name
--subst-var-by bash ${emacs.stdenv.shell} \
--subst-var-by wrapperSiteLisp "$deps/share/emacs/site-lisp" \
--subst-var-by wrapperSiteLispNative "$deps/share/emacs/native-lisp" \
--subst-var-by wrapperBinDir "$deps/bin" \
--subst-var-by wrapperInvocationDirectory "$out/Applications/Emacs.app/Contents/MacOS/" \
--subst-var-by wrapperInvocationName "Emacs" \
--subst-var-by prog "$emacs/Applications/Emacs.app/Contents/MacOS/Emacs"

View File

@@ -53,4 +53,9 @@ export emacsWithPackages_siteLispNative=@wrapperSiteLispNative@
export emacsWithPackages_invocationDirectory=@wrapperInvocationDirectory@
export emacsWithPackages_invocationName=@wrapperInvocationName@
if [[ ${PATH-} != *"@wrapperBinDir@":* ]]
then
export PATH="@wrapperBinDir@${PATH:+:$PATH}"
fi
exec @prog@ "$@"