0

I have Emacs open with lots of files. I want to compile within Emacs, but first I have to source some setup.sh bash script that modifies the environment. How do I do that?

This post says it's impossible. This man page does not help. This ergoemacs page targets a single environmental variable only, which make me feel like it might be possible!

I don't want to overload the Emacs compile function, as I just want to run the script once in a while, not for every compilation.

1

There is some Elisp code below that I used for years in the framework of my personal Emacs configuration. I post it here to help you but I did only a rudimentary test in emacs -Q.

After installing and running the code, the function setenv-from-bashrc should do what you want. You can call it interactively or from your Elisp code with the path of the shell script as single string argument. It parses the shell script for export statements and sets them with Elisp command setenv. It also does environment variable substitution and even calls subshells if it detects expansions like $(...) in the shell script.


(require 'cl-lib)

(defun setenv-scan-string (&optional eval)
  "Scan shell-string evaluating embedded shell commands with non-nil EVAL."
  (let ((inString (looking-at "\""))
    str-list)
    (if inString (forward-char))
    (while (and
        (not (= (point) (point-max)))
        (or inString (not (looking-at "\n")))
        (not (looking-at "\"")))
      (cond
       ((= (point) (point-max))
        (error "Reached end of buffer while scanning escape character within bash-string"))
       ((looking-at "\\\\")
        (setq str-list (cons "\\" str-list))
        (forward-char))
       ((looking-at "\\$\\$")
        (setq str-list (cons "$$" str-list))
        (forward-char 2))
       ((looking-at "\\$(")
        (setq str-list
              (cons
               (if eval
                   (substring
                    (shell-command-to-string
                     (progn
                       (forward-char)
                       (buffer-substring-no-properties
                        (1+ (point))
                        (progn
                          (forward-sexp)
                          (1- (point))))))
                    nil -1)
                 (buffer-substring-no-properties
                  (point)
                  (progn
                    (forward-char)
                    (forward-sexp)
                    (point))))
               str-list)))
       (t
        (setq str-list
              (cons
               (buffer-substring-no-properties
                (point)
                (progn
                  (or (> (skip-syntax-forward "w ") 0)
                      (forward-char))
                  (point)))
               str-list)))))
    (apply #'concat (nreverse str-list))))

(defvar setenv-from-bashrc-trafo nil
  "Filter for environment variables from shell script files (like ~/.bashrc).
Should be (lambda (var val) val) for unix-like operating systems
and maybe `setenv-from-bashrc-trafo-windows-nt' for windows-like operating systems.")

(setq setenv-from-bashrc-trafo
      (cl-case system-type
    (windows-nt 'setenv-from-bashrc-trafo-winnt)
    (t (lambda (var val) val))))

(defvar setenv-from-bashrc-winnt-trafo-exceptions nil
  "Variables that are not transformed from cygwin-directories to winnt-directories.")

(setq setenv-from-bashrc-winnt-trafo-exceptions '("DISPLAY" "CDHIST_SIZE" "CLIENT_NAME" "COLUMNS" "COMPUTERNAME" "EM_PARENT_PROCESS_ID" "G_SLICE" "LANG" "LC_LANG" "LINES" "LOCATION" "LOGONSERVER" "NO_AT_BRIDGE" "NUMBER_OF_PROCESSORS" "OS" "PAPERSIZE" "PROCESSOR_ARCHITECTURE" "PROCESSOR_IDENTIFIER" "PROCESSOR_LEVEL" "PROCESSOR_REVISION" "SESSIONNAME" "SHELL" "SYSTEMDRIVE" "SYSTEMROOT" "USERDNSDOMAIN" "USERDOMAIN" "USERNAME" "windows_tracing_flags"))

(defun setenv-from-bashrc-trafo-winnt (var val)
  "Replace `:' with `;' and afterwards `/c/' after `;' with `c:'."
  (unless (member var setenv-from-bashrc-winnt-trafo-exceptions)
    (let ((pos 0))
      (while (string-match "[^:;][a-zA-Z]:" val pos)
    (setq pos (match-end 0))
    (aset val (1- pos) ?\;))
      (setq pos 0)
      (while (string-match "\\(?:^\\|;\\)\\(/c/\\)" val pos)
    (setq pos (match-end 1))
    (setq val (replace-match "c:/" t t val 1)))))
  val)

;; Get environment variables:
(defun setenv-from-bashrc (filename)
  "Set environment variables from file FILENAME."
  (interactive "fFile name:")
  (and
   (file-exists-p filename)
   (set-buffer (find-file-noselect filename))
   (save-excursion
     (goto-char (point-min))
     (while (search-forward-regexp "^\\(export \\)?\\([[:alnum:]_]+\\)=" nil 'noErr)
       (let* ((var (match-string-no-properties 2))
          (val (funcall setenv-from-bashrc-trafo var (setenv-scan-string t))))
     (message "Set \"%s\" to \"%s\"." var val)
     (setenv var val 'substitute-env-vars)))))
  (let ((paths (split-string (getenv "PATH") ":" t)))
    (dolist (path paths)
      (add-to-list 'exec-path path))))

Test:

Emacs version: GNU Emacs 28.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.22.30, cairo version 1.15.10) of 2020-06-22

Contents of the shell script file /temp/test.sh:

export some="thing of Tobias"

export expanding="Some ${some}"

export running="$(time)"

Test run:

  • Copy above Elisp code into buffer <*scratch*> and run it with M-x eval-buffer RET
  • M-x setenv-from-bashrc RET
  • Check the environment settings with
    • M-x getenv RET some RET
      Answer: thing of Tobias
    • M-x getenv RET expanding RET
      Answer: some thing of Tobias
    • M-x getenv RET running RET
      Answer:
real    0m0.000s  
user    0m0.000s  
sys     0m0.000s

Good luck with the code! Please comment if you encounter any problems.

1
  • It seems to work since there is only simple syntax in the script!
    – Welgriv
    Jul 29 '20 at 7:32
2

How about:

(setq compile-command "bash -c '. /path/to/setup.sh; make'")
2
  • Seems great but I already thought about this but in my particular case I would like to not re-run the script at each new compilation, more like once in a while.
    – Welgriv
    Jul 28 '20 at 11:00
  • 1
    Is setup.sh expensive? If not, I'd suggest you're worrying about nothing.
    – phils
    Jul 28 '20 at 11:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.