Skip to content

ghostel-environment: user-configurable extra environment variables for spawned shell processes (cf. vterm's vterm-environment) #176

Description

@emil-e

vterm exposes a vterm-environment custom variable — a list of "VAR=VALUE" strings prepended to process-environment when spawning the shell process. It also honours dir-locals.el, so per-project overrides just work:

;; globally
(setq vterm-environment '("CC=clang" "CXX=clang++"))

;; or in a project's .dir-locals.el
((nil . ((vterm-environment . ("CC=clang" "CXX=clang++")))))

Ghostel has no equivalent. The extra-env argument to ghostel--spawn-pty is assembled entirely internally and is not exposed to users.

Motivating case

Emacs built with native compilation (libgccjit) sets CC=/path/to/gcc-NN and LIBRARY_PATH=... via LSEnvironment in Info.plist so that libgccjit can find a compatible compiler at runtime. These entries are injected by macOS into the Emacs process environment at launch, before any shell runs, and are then inherited by every subprocess — including shells spawned by Ghostel.

This causes CMake to pick up GCC as the C compiler while falling back to the system c++ (Clang) for C++, producing a mismatched toolchain. The fix is straightforward — prepend CC=clang CXX=clang++ (or unset both) for the shell process — but there is currently no way to do this without patching Ghostel or advising its internals.

Proposed implementation

Add a ghostel-environment defcustom (list of strings, same type as vterm-environment) and splice it into ghostel--spawn-pty alongside the existing extra-env:

(defcustom ghostel-environment nil
  "List of extra environment variables for ghostel shell processes.
Each element should be a string of the form \"VAR=VALUE\".  To unset
a variable inherited from Emacs, include just \"VAR\" with no value.
This variable is consulted after `hack-dir-local-variables', so it
can be set per-project via `.dir-locals.el'."
  :type '(repeat string)
  :group 'ghostel)

In ghostel-mode (where hack-dir-local-variables is already called), pick up the local binding:

(let ((ghostel-env (assq 'ghostel-environment dir-local-variables-alist)))
  (when ghostel-env
    (make-local-variable 'ghostel-environment)
    (setq ghostel-environment (cdr ghostel-env))))

And in ghostel--spawn-pty, prepend it:

(process-environment
 (append
  ghostel-environment
  (cons "INSIDE_EMACS=ghostel" (ghostel--terminal-env))
  extra-env
  process-environment))

The ordering (user vars first) mirrors vterm and ensures user entries take precedence over anything Ghostel injects.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions