Skip to content

Commit

Permalink
core: rework environment variables and PATH management
Browse files Browse the repository at this point in the history
See updated DOCUMENTATION.org and FAQ.org for more info.

* add core-env.el
* add library load-env-vars.el
* add bootstrap package dotenv-mode.el
* remove spacemacs-environment from bootstrap layer
* remove dotspacemacs variable dotspacemacs-import-env-vars-from-shell
* remove dotspacemacs variable dotspacemacs-improt-env-vars-shell-file-name
* add new key binding SPC f e e to open spacemacs.env file
* add new key binding SPC f e E to reload environment variable from env file
* add new key binding SPC f e C-e to re-initialize the env file from shell.
  • Loading branch information
syl20bnr committed Jun 25, 2018
1 parent 666671a commit 6220ace
Show file tree
Hide file tree
Showing 16 changed files with 237 additions and 120 deletions.
10 changes: 3 additions & 7 deletions core/core-configuration-layer.el
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@
(expand-file-name (concat spacemacs-start-directory "layers/"))
"Spacemacs layers directory.")

(defconst configuration-layer-private-directory
(expand-file-name (concat spacemacs-start-directory "private/"))
"Spacemacs private layers base directory.")

(defconst configuration-layer-private-layer-directory
(let ((dotspacemacs-layer-dir
(when dotspacemacs-directory
Expand All @@ -49,7 +45,7 @@
(if (and dotspacemacs-directory
(file-exists-p dotspacemacs-layer-dir))
dotspacemacs-layer-dir
configuration-layer-private-directory))
spacemacs-private-directory))
"Spacemacs default directory for private layers.")

(defconst configuration-layer-lock-file
Expand Down Expand Up @@ -1395,7 +1391,7 @@ discovery."
;; layers shipped with spacemacs
(list configuration-layer-directory)
;; layers in private folder ~/.emacs.d/private
(list configuration-layer-private-directory)
(list spacemacs-private-directory)
;; layers in dotdirectory
;; this path may not exist, so check if it does
(when dotspacemacs-directory
Expand Down Expand Up @@ -1976,7 +1972,7 @@ RNAME is the name symbol of another existing layer."
nil))
((eq 'local location)
(let ((dir (if (eq 'dotfile owner)
configuration-layer-private-directory
spacemacs-private-directory
(let* ((owner (configuration-layer/get-layer owner)))
(when owner (oref owner :dir))))))
(if dir
Expand Down
17 changes: 2 additions & 15 deletions core/core-dotspacemacs.el
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,6 @@ exists. Otherwise, fallback to ~/.spacemacs"))
`+distributions'. For now available distributions are `spacemacs-base'
or `spacemacs'.")

(defvar dotspacemacs-import-env-vars-from-shell (and (display-graphic-p)
(or (eq system-type 'darwin)
(eq system-type 'gnu/linux)
(eq window-system 'x)))
"If non-nil then Spacemacs will import your PATH and environment variables
from your default shell on startup. This is enabled by default for macOS users
and X11 users.")

(defvar dotspacemacs-import-env-vars-shell-file-name nil
"If nil then use the default shell is used to fetch the environment variables.
Set this variable to a different shell executable path to import the environment
variables from this shell. Note that `file-shell-name' is preserved and always
points to the default shell. For instance to use your fish shell environment
variables set this variable to `/usr/local/bin/fish'.")

(defvar dotspacemacs-enable-emacs-pdumper nil
"If non-nil then enable support for the portable dumper. You'll need
to compile Emacs 27 from source following the instructions in file
Expand Down Expand Up @@ -535,6 +520,8 @@ Called with `C-u C-u' skips `dotspacemacs/user-config' _and_ preleminary tests."
(setq dotspacemacs-editing-style
(dotspacemacs//read-editing-style-config
dotspacemacs-editing-style))
;; reload environment variables
(spacemacs/load-env)
;; try to force a redump when reloading the configuration
(let ((spacemacs-force-dump t))
(configuration-layer/load))
Expand Down
69 changes: 69 additions & 0 deletions core/core-env.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
;;; core-env.el --- Spacemacs Core File
;;
;; Copyright (c) 2012-2018 Sylvain Benner & Contributors
;;
;; Author: Sylvain Benner <[email protected]>
;; URL: https://github.com/syl20bnr/spacemacs
;;
;; This file is not part of GNU Emacs.
;;
;;; License: GPLv3

(require 'core-dotspacemacs)
(require 'load-env-vars)

(defvar spacemacs-env-vars-file
(concat (or dotspacemacs-directory spacemacs-private-directory) "spacemacs.env")
"Absolute path to the env file where environment variables are set.")

(defun spacemacs/init-env (&optional force)
"Attempt to fetch the environment variables from the users shell.
This solution is far from perfect and we should not rely on this function
a lot. We use it only to initialize the env file when it does not exist
yet.
If FORCE is non-nil then force the initialization of the file, note that the
current contents of the file will be overwritten."
(when (or force (not (file-exists-p spacemacs-env-vars-file)))
(with-temp-file spacemacs-env-vars-file
(let ((shell-command-switch "-ic"))
(insert (shell-command-to-string "env"))))
(spacemacs-buffer/warning
(concat "Spacemacs tried to import your environment variables from "
"your shell and saved them to `%s'. "
"Please check that the values are correct by calling "
"`spacemacs/edit-env' function and update the file if needed. "
"If you later need to add environment variables add them to this "
"file.")
spacemacs-env-vars-file)))

(defun spacemacs/force-init-env ()
"Forces a reinitialization of environment variables."
(interactive)
(spacemacs/init-env t))

(defun spacemacs/edit-env ()
"Open the env file for edition."
(interactive)
(if (file-exists-p spacemacs-env-vars-file)
(progn
(find-file spacemacs-env-vars-file)
(when (fboundp 'dotenv-mode)
(dotenv-mode)))
(message "Cannot file env file `%s'" spacemacs-env-vars-file)))

(defun spacemacs/load-env (&optional force)
"Load the environment variables from the env file.
If FORCE is non-nil then force the loading of environment variables from env
file."
(interactive "P")
(when (or force (and (display-graphic-p)
(or (eq system-type 'darwin)
(eq system-type 'gnu/linux)
(eq window-system 'x)))))
(if (file-exists-p spacemacs-env-vars-file)
(progn
(load-env-vars spacemacs-env-vars-file)
(message "Environment variables loaded."))
(message "Cannot file env file `%s'" spacemacs-env-vars-file)))

(provide 'core-env)
3 changes: 3 additions & 0 deletions core/core-load-paths.el
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
(defconst spacemacs-core-directory
(expand-file-name (concat spacemacs-start-directory "core/"))
"Spacemacs core directory.")
(defconst spacemacs-private-directory
(expand-file-name (concat spacemacs-start-directory "private/"))
"Spacemacs private directory.")
(defconst spacemacs-info-directory
(expand-file-name (concat spacemacs-core-directory "info/"))
"Spacemacs info files directory")
Expand Down
4 changes: 4 additions & 0 deletions core/core-spacemacs.el
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

(require 'subr-x nil 'noerror)
(require 'core-emacs-backports)
(require 'core-env)
(require 'page-break-lines)
(require 'core-hooks)
(require 'core-debug)
Expand Down Expand Up @@ -145,6 +146,9 @@ the final step of executing code in `emacs-startup-hook'.")
;; check for new version
(if dotspacemacs-mode-line-unicode-symbols
(setq-default spacemacs-version-check-lighter "[⇪]"))
;; load environment variables
(spacemacs/init-env)
(spacemacs/load-env)
;; install the dotfile if required
(dotspacemacs/maybe-install-dotfile))

Expand Down
97 changes: 97 additions & 0 deletions core/libs/load-env-vars.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
;;; load-env-vars.el --- Load environment variables from files -*- lexical-binding: t; -*-

;; Copyright (C) 2018 Jorge Dias

;; Author: Jorge Dias <[email protected]>
;; URL: https://github.com/diasjorge/emacs-load-env-vars
;; Keywords: lisp
;; Version: 0.0.2
;; Package-Requires: ((emacs "24"))

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; This package allows you set environment variables loaded from a
;; file with bash style variable declarations.
;; Supported syntax:
;;
;; export KEY=VALUE
;; KEY=VALUE
;; KEY='VALUE'
;; KEY="VALUE"
;; # Comment lines are ignored
;; KEY=VALUE # Inline comments are ignored
;; KEY: VALUE
;;
;; Updates for Spacemacs:
;; - set `exec-path' from PATH

;;; Code:

(defvar load-env-vars-env-var-regexp
(rx
line-start
(0+ space)
(optional "export" (0+ space)) ;; optional export
(group (1+ (in "_" alnum))) ;; key
(or
(and (0+ space) "=" (0+ space))
(and ":" (1+ space))) ;; separator
(or
(and "'" (group (0+ (or "\\'" (not (any "'"))))) "'") ;; single quoted value
(and ?\" (group (0+ (or "\\\"" (not (any "\""))))) ?\") ;; double quoted value
(group (1+ (not (in "#" "\n" space)))) ;; unquoted value
)
(0+ space)
(optional "#" (0+ any))
)
"Regexp to match env vars in file."
)

(defun load-env-vars-re-seq (regexp)
"Get a list of all REGEXP matches in a buffer."
(save-excursion
(goto-char (point-min))
(save-match-data
(let (matches)
(while (re-search-forward regexp nil t)
(push (list (match-string-no-properties 1) (or (match-string-no-properties 2) (match-string-no-properties 3) (match-string-no-properties 4))) matches))
matches))))

(defun load-env-vars-extract-env-vars ()
"Extract environment variable name and value from STRING."
(load-env-vars-re-seq load-env-vars-env-var-regexp))

(defun load-env-vars-set-env (env-vars)
"Set envariable variables from key value lists from ENV-VARS."
(dolist (element env-vars)
(let ((key (car element)) (value (cadr element)))
(when (string-equal "PATH" key)
(let ((paths (split-string value path-separator)))
(dolist (p paths)
(add-to-list 'exec-path p))))
(setenv key value))))

;;;###autoload
(defun load-env-vars (file-path)
"Load environment variables found in FILE-PATH."
(interactive "fEnvironment variables file: ")
(with-temp-buffer
(insert-file-contents file-path)
(let ((env-vars (load-env-vars-extract-env-vars)))
(load-env-vars-set-env env-vars))))

(provide 'load-env-vars)
;;; load-env-vars.el ends here
17 changes: 0 additions & 17 deletions core/templates/.spacemacs.template
Original file line number Diff line number Diff line change
Expand Up @@ -155,23 +155,6 @@ It should only modify the values of Spacemacs settings."
;; If non-nil output loading progress in `*Messages*' buffer. (default nil)
dotspacemacs-verbose-loading nil

;; If non-nil then Spacemacs will import your PATH and environment variables
;; from your default shell on startup. This is enabled by default for macOS
;; users and X11 users.
dotspacemacs-import-env-vars-from-shell (and (display-graphic-p)
(or (eq system-type 'darwin)
(eq system-type 'gnu/linux)
(eq window-system 'x)))

;; If nil then use the default shell is used to fetch the environment
;; variables. Set this variable to a different shell executable path to
;; import the environment variables from this shell. Note that
;; `file-shell-name' is preserved and always points to the default shell. For
;; instance to use your fish shell environment variables set this variable to
;; `/usr/local/bin/fish'.
;; (default nil)
dotspacemacs-import-env-vars-shell-file-name nil

;; Specify the startup banner. Default value is `official', it displays
;; the official spacemacs logo. An integer value is the index of text
;; banner, `random' chooses a random text banner in `core/banners'
Expand Down
76 changes: 40 additions & 36 deletions doc/DOCUMENTATION.org
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
- [[#differences-between-vim-evil-and-spacemacs][Differences between Vim, Evil and Spacemacs]]
- [[#the-vim-surround-case][The vim-surround case]]
- [[#evil-plugins][Evil plugins]]
- [[#environment-variables-and-path][Environment variables and PATH]]
- [[#binding-keys][Binding keys]]
- [[#gui-elements][GUI Elements]]
- [[#color-themes][Color themes]]
Expand Down Expand Up @@ -94,7 +95,6 @@
- [[#layout-key-bindings][Layout key bindings]]
- [[#workspaces][Workspaces]]
- [[#workspace-key-bindings][Workspace key bindings]]
- [[#environment-variables-and-path-from-the-shell][Environment variables and PATH from the shell]]
- [[#commands][Commands]]
- [[#vim-key-bindings][Vim key bindings]]
- [[#escaping][Escaping]]
Expand Down Expand Up @@ -1000,6 +1000,32 @@ Spacemacs ships with the following evil plugins:
| [[https://github.com/bling/evil-visualstar][evil-visualstar]] | search for current selection with ~*~ |
| [[https://github.com/jaypei/emacs-neotree][NeoTree]] | mimic [[https://github.com/scrooloose/nerdtree][NERD Tree]] |

* Environment variables and PATH
If you launch Emacs from a shell then Emacs process will inherit the environment
variables from your shell so you won't get any issue.

On the other hand if you launch Emacs from a launcher without using a shell then
Emacs may not see your environment variables. This is especially true for macOS
users but it can happen to GNU/Linux too.

To fix this issue Spacemacs first tries to import your shell environment
variable by calling =<shell> -ic env=. It then writes the result to the
=spacemacs.env= file whose location is =~/.emacs.d/private/env= or
=~/.spacemacs.d/env= directory depending on what you are using.

Then Spacemacs loads the environment variables from the =spacemacs.env= file
directly. It won't call your shell anymore at startup. If you need to add new
environment variables or modify them you'll need to edit the =spacemacs.env=
file. You can open this file with ~SPC f e e~ or call the function
=spacemacs/edit-env=.

At any point in time you can force an import of your shell environment variables
with ~SPC f e C-e~. Note that this function will overwrite the contents of your
=spacemacs.env= file.

You can reload the environment variables from the =spacemacs.env= file with ~SPC
f e E~.

* Binding keys
Key sequences are bound to commands in Emacs in various keymaps. The most basic
map is the =global-map=. Setting a key binding in the =global-map= is achieved
Expand Down Expand Up @@ -1717,31 +1743,6 @@ There are also some handy globally available key bindings related to workspaces:
| ~gT~ | go to previous workspace |
| ~SPC b W~ | go to workspace and window by buffer |

* Environment variables and PATH from the shell
To import your environment variable and =PATH= at startup set the variable
=dotspacemacs-import-env-vars-from-shell= to non-nil.

This should be necessary only to macOS users and X11 users using the GNU
build of Emacs.

#+BEGIN_EXAMPLE emacs-lisp
(setq-default dotspacemacs-import-env-vars-from-shell (and (display-graphic-p)
(or (eq system-type 'darwin)
(eq window-system 'x))))
#+END_EXAMPLE

At anytime you can import the environment variables by calling the function
=spacemacs/loadenv=.

You can define a different shell than the default shell to look for environment
variables by setting the variable =dotspacemacs-import-env-vars-shell-file-name=
in your dotfile. Note that =shell-file-name= is not modified by this value.

#+BEGIN_EXAMPLE emacs-lisp
(defun dotspacemacs/init ()
(setq dotspacemacs-import-env-vars-shell-file-name "/usr/local/bin/fish"))
#+END_EXAMPLE

* Commands
** Vim key bindings
Spacemacs is based on =Vim= modal user interface to navigate and edit text. If
Expand Down Expand Up @@ -2385,16 +2386,19 @@ Frame manipulation commands (start with ~F~):
Convenient key bindings are located under the prefix ~SPC f e~ to quickly
navigate between =Emacs= and Spacemacs specific files.

| Key Binding | Description |
|-------------+-----------------------------------------------------------------|
| ~SPC f e d~ | open the spacemacs dotfile (=~/.spacemacs=) |
| ~SPC f e D~ | open =ediff= buffer of =~/.spacemacs= and =.spacemacs.template= |
| ~SPC f e f~ | discover the =FAQ= |
| ~SPC f e i~ | open the all mighty =init.el= |
| ~SPC f e l~ | locate an Emacs library |
| ~SPC f e R~ | resync the dotfile with spacemacs |
| ~SPC f e U~ | update packages |
| ~SPC f e v~ | display and copy the spacemacs version |
| Key Binding | Description |
|---------------+----------------------------------------------------------------------|
| ~SPC f e d~ | open the spacemacs dotfile (=~/.spacemacs=) |
| ~SPC f e D~ | open =ediff= buffer of =~/.spacemacs= and =.spacemacs.template= |
| ~SPC f e e~ | open the =spacemacs.env= file where environment variables are set |
| ~SPC f e E~ | reload the environment variables by reading the =spacemacs.env= file |
| ~SPC f e C-e~ | reinitialize the =spacemacs.env= file from shell |
| ~SPC f e f~ | discover the =FAQ= |
| ~SPC f e i~ | open the all mighty =init.el= |
| ~SPC f e l~ | locate an Emacs library |
| ~SPC f e R~ | resync the dotfile with spacemacs |
| ~SPC f e U~ | update packages |
| ~SPC f e v~ | display and copy the spacemacs version |

**** Browsing files in completion buffer
In =vim= style and =hybrid= style with the variable
Expand Down
Loading

0 comments on commit 6220ace

Please sign in to comment.