core: rework environment variables and PATH management

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.
This commit is contained in:
syl20bnr 2018-06-25 02:16:27 -04:00
parent 666671a881
commit 6220ace290
16 changed files with 237 additions and 120 deletions

View File

@ -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
@ -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
@ -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
@ -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

View File

@ -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
@ -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))

69
core/core-env.el Normal file
View File

@ -0,0 +1,69 @@
;;; core-env.el --- Spacemacs Core File
;;
;; Copyright (c) 2012-2018 Sylvain Benner & Contributors
;;
;; Author: Sylvain Benner <sylvain.benner@gmail.com>
;; 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)

View File

@ -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")

View File

@ -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)
@ -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))

View File

@ -0,0 +1,97 @@
;;; load-env-vars.el --- Load environment variables from files -*- lexical-binding: t; -*-
;; Copyright (C) 2018 Jorge Dias
;; Author: Jorge Dias <jorge@mrdias.com>
;; 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

View File

@ -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'

View File

@ -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]]
@ -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]]
@ -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
@ -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
@ -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

View File

@ -6,6 +6,7 @@
- [[#what-is-the-official-pronunciation-of-spacemacs][What is the official pronunciation of Spacemacs?]]
- [[#why-do-you-call-this-a-distribution-i-dont-see-any-spacemacs-executable][Why do you call this a "distribution", I don't see any "Spacemacs" executable?]]
- [[#why-are-packages-installed-with-package-install-automatically-deleted-by-spacemacs-when-it-boots][Why are packages installed with =package-install= automatically deleted by Spacemacs when it boots?]]
- [[#environment-variables-or-path-are-not-set-properly][Environment variables or PATH are not set properly]]
- [[#how-to-fix-package-download-errors-when-installing-spacemacs-][How to fix package download errors when installing Spacemacs ?]]
- [[#how-to-fix-symbols-value-as-variable-is-void-errors-on-startup][How to fix =Symbol's value as variable is void= errors on startup?]]
- [[#how-to-fix-wrong-type-argument-arrayp-nil-errors-on-startup][How to fix =(wrong-type-argument arrayp nil)= errors on startup?]]
@ -85,6 +86,12 @@ To install packages that does not belong to any Spacemacs layers, you can:
To create a new configuration layer see the [[https://github.com/syl20bnr/spacemacs/blob/master/doc/QUICK_START.org][quick start guide]] for more info.
** Environment variables or PATH are not set properly
If you use Emacs GUI and don't launch if from a terminal then edit the
environment variables in the =env= file. You can open this file with
~SPC f e e~. More information in the =Environment variables= section of the
[[https://github.com/syl20bnr/spacemacs/blob/master/doc/DOCUMENTATION.org][documentation]].
** How to fix package download errors when installing Spacemacs ?
Since 0.105.0 HTTPS protocol is used by default to download packages. If your
environment does not allow HTTPS to reach ELPA repositories then you can start

View File

@ -75,7 +75,7 @@
(progn
(setq aya-persist-snippets-dir
(or auto-completion-private-snippets-directory
(concat configuration-layer-private-directory "snippets/")))
(concat spacemacs-private-directory "snippets/")))
(spacemacs/declare-prefix "iS" "auto-yasnippet")
(spacemacs/set-leader-keys
"iSc" 'aya-create
@ -234,7 +234,7 @@
(let* ((spacemacs--auto-completion-dir
(configuration-layer/get-layer-local-dir 'auto-completion))
(emacs-directory-snippets-dir (concat
configuration-layer-private-directory
spacemacs-private-directory
"snippets/"))
(spacemacs-layer-snippets-dir (expand-file-name
"snippets"

View File

@ -10,7 +10,7 @@
;;; License: GPLv3
(defvar templates-private-directory
(concat configuration-layer-private-directory "templates")
(concat spacemacs-private-directory "templates")
"Configurable private templates directory.")
(defvar templates-use-default-templates t

View File

@ -81,6 +81,3 @@ if used there.")
("iedit-insert" "firebrick1" (bar . 2)))
"Colors assigned to evil states with cursor definitions.
To add your own, use `spacemacs/add-evil-curosr'.")
(defvar spacemacs-env-vars-file (concat spacemacs-cache-directory ".env-vars")
"Cache file for exec-path variable.")

View File

@ -9,39 +9,6 @@
;;
;;; License: GPLv3
(defun spacemacs/loadenv ()
"Gets and sets all the environment variables from user shell."
(interactive)
(spacemacs-buffer/message "Importing environment variables..")
(require 'async nil t)
(async-start
`(lambda ()
,(async-inject-variables
"\\`dotspacemacs-import-env-vars-shell-file-name\\'")
(if dotspacemacs-import-env-vars-shell-file-name
(let ((shell-file-name
dotspacemacs-import-env-vars-shell-file-name))
(split-string (shell-command-to-string "env") "\n"))
(split-string (shell-command-to-string "env") "\n")))
(lambda (envvars)
(spacemacs-buffer/message "Imported environment variables:")
(dolist (env envvars)
(if (string-match "^[a-zA-Z_]+[a-zA-Z0-9_]*=" env)
(let* ((var (split-string env "="))
(k (car var))
(v (cadr var)))
(spacemacs-buffer/message " - %s=%s" k v)
(when (string-equal "PATH" k)
(let ((paths (split-string v path-separator)))
(dolist (p paths)
(add-to-list 'exec-path p))))
(setenv k v))))
;; be sure we keep the default shell in this Emacs session
;; this is to prevent potential issues with packages which could
;; expect a default shell
(setenv "SHELL" shell-file-name))))

View File

@ -18,12 +18,12 @@
(bind-key :step bootstrap)
(diminish :step bootstrap)
(evil :step bootstrap)
(spacemacs-environment :step bootstrap :location built-in)
(hydra :step bootstrap)
(use-package :step bootstrap)
(which-key :step bootstrap)
;; pre packages, initialized aftert the bootstrap packages
;; these packages can use use-package
(dotenv-mode :step pre)
(evil-evilified-state :location local :step pre :protected t)
(pcre2el :step pre)
(holy-mode :location local :step pre)
@ -42,6 +42,10 @@
(when (not (configuration-layer/package-used-p 'spaceline))
(add-hook 'after-load-functions 'spacemacs/diminish-hook)))
(defun spacemacs-bootstrap/init-dotenv-mode ()
(use-package dotenv-mode
:defer t))
(defun spacemacs-bootstrap/init-bind-map ()
(require 'bind-map)
(bind-map spacemacs-default-map
@ -493,10 +497,6 @@
(which-key-mode)
(spacemacs|diminish which-key-mode "" " K"))
(defun spacemacs-bootstrap/init-spacemacs-environment ()
(when dotspacemacs-import-env-vars-from-shell
(spacemacs/loadenv)))
;; pre packages
(defun spacemacs-bootstrap/init-evil-evilified-state ()

View File

@ -203,6 +203,9 @@
"fei" 'spacemacs/find-user-init-file
"fed" 'spacemacs/find-dotfile
"feD" 'spacemacs/ediff-dotfile-and-template
"fee" 'spacemacs/edit-env
"feE" 'spacemacs/load-env
"fe C-e" 'spacemacs/force-init-env
"feR" 'dotspacemacs/sync-configuration-layers
"fev" 'spacemacs/display-and-copy-version
"feU" 'configuration-layer/update-packages

View File

@ -2389,7 +2389,7 @@
((spacemacs/update-progress-bar nil ((:output nil))))
(configuration-layer//configure-packages-2 `(,(oref pkg :name)))
(push (file-name-as-directory
(concat configuration-layer-private-directory "local/pkg"))
(concat spacemacs-private-directory "local/pkg"))
expected-load-path)
(should (equal expected-load-path load-path)))))