1

How can I organize Emacs to run a specific external program with some specific environment/options for the mode or the buffer currently open.

My current solution is to use setenv before running the external program, however it seems to change the environment for all the buffers in Emacs.

Example

To work around a bug in Groovy 3.0.0 (see below) I need to set some java option before running groovysh. In terminal, I can do the following:

JAVA_OPTS=--add-opens=java.base/jdk.internal.jrtfs=ALL-UNNAMED groovysh

However, Emacs run groovysh thanks to run-groovy function from the inf-groovy.el file in groovy package. So I usually run eval-expression with:

(setenv "JAVA_OPTS" "--add-opens=java.base/jdk.internal.jrtfs=ALL-UNNAMED")

which solves the problem, the inferior Groovy process is now working.

However, all my other buffers now use this value for this environment variable which is not desirable, since I need some other specific java options for other codes. My current solution is to invoke setenv every time I change buffer.

groovysh 3.0.0 not working with jdk 11.0.6

Open a simple file in groovy mode and try to run-groovy

With Groovy 3.0.0 and JDK 11.0.6 installed it raises an error:

java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:111)
    at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:129)
Caused by: java.lang.IllegalAccessException: class org.codehaus.groovy.runtime.callsite.PlainObjectMetaMethodSite cannot access class jdk.internal.jrtfs.JrtFileSystem (in module java.base) because module java.base does not export jdk.internal.jrtfs to unnamed module @6d4e5011

It took me a while to figure out that it was a problem due to version incompatibility between groovy and java, see https://issues.apache.org/jira/browse/GROOVY-9390

Émilien
  • 70
  • 7

1 Answers1

3

See C-hv process-environment

List of overridden environment variables for subprocesses to inherit. Each element should be a string of the form ENVVARNAME=VALUE.

Entries in this list take precedence to those in the frame-local environments. Therefore, let-binding process-environment is an easy way to temporarily change the value of an environment variable, irrespective of where it comes from. To use process-environment to remove an environment variable, include only its name in the list, without "=VALUE".

This variable is set to nil when Emacs starts.

If multiple entries define the same variable, the first one always takes precedence.

Non-ASCII characters are encoded according to the initial value of locale-coding-system, i.e. the elements must normally be decoded for use.

See setenv and getenv.

Therefore you can tweak your approach like so:

(let ((process-environment process-environment))
  (push "JAVA_OPTS=--add-opens=java.base/jdk.internal.jrtfs=ALL-UNNAMED"
        process-environment)
  ;; run groovysh here
  (groovy-load-file (buffer-file-name)))
Émilien
  • 70
  • 7
phils
  • 48,657
  • 3
  • 76
  • 115
  • Thanks a lot `process-environment` seems to be the right way to go. To make it work I had to run: `(let ((process-environment process-environment)) (push "JAVA_OPTS=--add-opens=java.base/jdk.internal.jrtfs=ALL-UNNAMED" process-environment) (groovy-load-file (buffer-file-name)))` – Émilien Feb 17 '20 at 10:23