Marius Olsthoorn
Marius Olsthoorn

Reputation: 404

How can I set environment variables to a buffer-local scope in emacs

In Emacs, I want to vary the values of my environment variables in different buffers.

My emacs environment depends on environment variables (flymake, compile etc), however I want to be able to be able to have multiple projects open at once in one emacs session but these projects might have conflicting environments.

For example something like different INCLUDE_PATH environment variables for flymake.

Upvotes: 10

Views: 3912

Answers (5)

lawlist
lawlist

Reputation: 13457

Here is an example where I create a local process-environment without necessarily making it buffer-local. The advantage is that the settings only affect the running process and they disappear once the process ends. In this example, I set the timzezone environmental variable and call the function with (funcall my-start-process ....) and everything else is just like start-process in terms of arguments and so forth.

(let* ((my-start-process
         (lambda (name buffer program &rest program-args)
           (unless (fboundp 'make-process)
             (error "Emacs was compiled without subprocess support"))
           (let* (
               (temp (mapcar 'concat process-environment))
               (newenv
                 (cond
                   ((equal (car (cdr (current-time-zone))) "PDT")
                     (setenv-internal temp "TZ" "UTC+7" t))
                   ((equal (car (cdr (current-time-zone))) "PST")
                     (setenv-internal temp "TZ" "UTC+8" t))))
               (process-environment (or newenv temp)))
             (apply #'make-process
              (append (list :name name :buffer buffer)
              (when program
                (list :command (cons program program-args))))))))
         (proc (funcall my-start-process ...))))

Upvotes: 0

gensym
gensym

Reputation: 231

It may be possible to use dynamic binding for those variables.

Dynamic binding and Dynamic scoping are a bit hard to explain, for explanations see http://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding and http://en.wikipedia.org/wiki/Scope_(computer_science)#Dynamic_scoping.

Upvotes: 0

user4815162342
user4815162342

Reputation: 155036

You can do this by making process-environment buffer-local:

(defun setup-some-mode-env ()
  (make-local-variable 'process-environment)
  ;; inspect buffer-file-name and add stuff to process-environment as necessary
  ...)
(add-hook 'some-major-mode 'setup-some-mode-env)

A more elaborate example is this code that imports the Guile environment setup created by an external script. The script is designed to be "sourced" in the shell, but here its result gets imported into a single Emacs buffer:

(defun my-guile-setup ()
  (make-local-variable 'process-environment)
  (with-temp-buffer
    (call-process "bash" nil t nil "-c"
          "source ~/work/guileenv; env | egrep 'GUILE|LD_LIBRARY_PATH'")
    (goto-char (point-min))
    (while (not (eobp))
      (setq process-environment
        (cons (buffer-substring (point) (line-end-position))
          process-environment))
      (forward-line 1))))

(add-hook 'guile-hook 'my-guile-setup)

Upvotes: 18

Joost Diepenmaat
Joost Diepenmaat

Reputation: 17773

I put the following in .dir-locals.el at the root of the tree where I want to define some environment vars:

;; variables local to this directory and its children
((nil . ((eval . (setenv "SOME_VARIABLE" "TRUE")))))

This will warn the first time you open a file in that directory tree. After you accept, the given environment var will be defined for each buffer you open there.

Upvotes: 2

Lindydancer
Lindydancer

Reputation: 26104

One solution would be to temporary change the environment when you spawn an external command. The command will inherit the current environment. Remember that Emacs is a single-treaded application, so we don't have to worry about race conditions etc.

You can pick one of two ways of doing this:

1) Write you own functions like my-compile that changes the environment temporarily and calls the normal compile command.

2) Modify the low-level process functions and ensure that they modify the environment accordingly. Typically, you can do this with defadvice.

Upvotes: 1

Related Questions