1
0
Fork 1
mirror of https://github.com/NixOS/nixpkgs.git synced 2024-12-25 03:17:13 +00:00
nixpkgs/pkgs/applications/editors/emacs/site-start.el
Alexey Lebedeff 5f9cbf9898
emacs: allow wrapped emacs to execute itself again
fixes #145302 #237855

emacsWithPackages wrapper script/`site-start.el` sanitize
EMACSLOADPATH, to make nested emacs invocations independent of the
package set specified in emacsWithPackages.

But there are valid use cases when one needs to call nested emacs with
the same package set. This includes built-in emacs functionality such
as async native compilations, and also external packages like
`emacs-async` and `esup`. In all of these cases
`invocation-directory`/`invocation-name` variables are being used to
launch nested emacs.

With this patch these variables will be populated to point to the
emacsWithPackages wrapper executable, so that executing
`(file-name-concat invocation-directory invocation-name)` will give a
fully functional emacs again.

`EMACSLOADPATH` sanitization was introduced by #106486, this behaviour
stays unchanged. The reasoning was to be able to run different emacs
executables without polluting their EMACSLOADPATH (as described here
23d4bfb666).
The only change is that invoking itself is again feasible (and that's
what emacs actually expects).

Co-authored-by: Lin Jian <me@linj.tech>
2024-12-06 13:12:47 +08:00

101 lines
4.7 KiB
EmacsLisp

;; -*- lexical-binding: t; -*-
(defun nix--profile-paths ()
"Return a list of all paths in NIX_PROFILES.
The list is ordered from more-specific (the user profile) to the
least specific (the system profile)"
(reverse (split-string (or (getenv "NIX_PROFILES") ""))))
;;; Extend `load-path' to search for elisp files in subdirectories of all folders in `NIX_PROFILES'.
;;; Non-Nix distros have similar logic in /usr/share/emacs/site-lisp/subdirs.el.
;;; See https://www.gnu.org/software/emacs/manual/html_node/elisp/Library-Search.html
(dolist (profile (reverse (nix--profile-paths)))
;; `directory-file-name' is important to add sub dirs to the right place of `load-path'
;; see the source code of `normal-top-level-add-to-load-path'
(let ((default-directory (directory-file-name
(expand-file-name "share/emacs/site-lisp/" profile))))
(when (file-exists-p default-directory)
(setq load-path (cons default-directory load-path))
(normal-top-level-add-subdirs-to-load-path))))
;;; Remove wrapper site-lisp from EMACSLOADPATH so it's not propagated
;;; to any other Emacsen that might be started as subprocesses.
(let ((wrapper-site-lisp (getenv "emacsWithPackages_siteLisp"))
(env-load-path (getenv "EMACSLOADPATH")))
(when wrapper-site-lisp
(setenv "emacsWithPackages_siteLisp" nil))
(when (and wrapper-site-lisp env-load-path)
(let* ((env-list (split-string env-load-path ":"))
(new-env-list (delete wrapper-site-lisp env-list)))
(setenv "EMACSLOADPATH" (when new-env-list
(mapconcat 'identity new-env-list ":"))))))
(let ((wrapper-site-lisp (getenv "emacsWithPackages_siteLispNative"))
(env-load-path (getenv "EMACSNATIVELOADPATH")))
(when wrapper-site-lisp
(setenv "emacsWithPackages_siteLispNative" nil))
(when (and wrapper-site-lisp env-load-path)
(let* ((env-list (split-string env-load-path ":"))
(new-env-list (delete wrapper-site-lisp env-list)))
(setenv "EMACSNATIVELOADPATH" (when new-env-list
(mapconcat 'identity new-env-list ":"))))))
(let ((wrapper-invocation-directory (getenv "emacsWithPackages_invocationDirectory")))
(when wrapper-invocation-directory
(setq invocation-directory (file-name-as-directory wrapper-invocation-directory))
(setenv "emacsWithPackages_invocationDirectory" nil)))
(let ((wrapper-invocation-name (getenv "emacsWithPackages_invocationName")))
(when wrapper-invocation-name
(setq invocation-name wrapper-invocation-name)
(setenv "emacsWithPackages_invocationName" nil)))
;;; Set up native-comp load path.
(when (featurep 'native-compile)
;; Append native-comp subdirectories from `NIX_PROFILES'.
;; Emacs writes asynchronous native-compilation files to the first writable directory[1].
;; At this time, (car native-comp-eln-load-path) is a writable one in `user-emacs-directory'[2].
;; So we keep that one unchanged.
;; [1]: info "(elisp) Native-Compilation Variables"
;; [2]: https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/startup.el?id=3685387e609753293c4518be75e77c659c3b2d8d#n601
(setq native-comp-eln-load-path
(append (list (car native-comp-eln-load-path))
(mapcar (lambda (profile-dir)
(concat profile-dir "/share/emacs/native-lisp/"))
(nix--profile-paths))
(cdr native-comp-eln-load-path))))
;;; Make `woman' find the man pages
(defvar woman-manpath)
(eval-after-load 'woman
'(setq woman-manpath
(append (mapcar (lambda (x) (concat x "/share/man/"))
(nix--profile-paths))
woman-manpath)))
;;; Make tramp work for remote NixOS machines
(defvar tramp-remote-path)
(eval-after-load 'tramp
;; TODO: We should also add the other `NIX_PROFILES' to this path.
;; However, these are user-specific, so we would need to discover
;; them dynamically after connecting via `tramp'
'(progn
(add-to-list 'tramp-remote-path "/run/current-system/sw/bin")
(add-to-list 'tramp-remote-path "/run/wrappers/bin")))
;;; C source directory
;;;
;;; Computes the location of the C source directory from the path of
;;; the current file:
;;; from: /nix/store/<hash>-emacs-<version>/share/emacs/site-lisp/site-start.el
;;; to: /nix/store/<hash>-emacs-<version>/share/emacs/<version>/src/
(defvar find-function-C-source-directory)
(let ((emacs
(file-name-directory ; .../emacs/
(directory-file-name ; .../emacs/site-lisp
(file-name-directory load-file-name)))) ; .../emacs/site-lisp/
(version
(file-name-as-directory
emacs-version))
(src (file-name-as-directory "src")))
(setq find-function-C-source-directory (concat emacs version src)))