Well, it is really strange:
diff --git a/src-java/babashka/impl/Graal.java b/src-java/babashka/impl/Graal.java
index 9754185..604b5f6 100644
--- a/src-java/babashka/impl/Graal.java
+++ b/src-java/babashka/impl/Graal.java
@@ -18,23 +18,22 @@ public class Graal {
@Override
public List<String> getHeaderFiles() {
- return Collections.singletonList("<stdlib.h>");
+ return Collections.singletonList("<stdio.h>");
}
}
@CFunction
- private static native int setenv(CCharPointer name, CCharPointer value, int overwrite);
+ private static native int puts(CCharPointer name);
// API
public static int setEnv(String name, String value) {
- int ret = 0;
+ int ret = -1;
System.out.println("setenv" + " " + name + " " + value);
try (CCharPointerHolder nameHolder = CTypeConversion.toCString(name);
CCharPointerHolder valueHolder = CTypeConversion.toCString(value)) {
- ret = setenv(nameHolder.get(), valueHolder.get(), 1);
- System.out.println(System.getenv(name));
+ ret = puts(nameHolder.get());
+ System.out.println(Integer.toString(ret));
}
- System.out.println(System.getenv(name));
return ret;
}
user=> (babashka.utils/set-env "this is the text that should be printed" "")
:setting "this is the text that should be printed" ""
setenv this is the text that should be printed
this is the text that should be printed
40
40
nil
Definitively seems to be working, but the setenv
does seems to do its job but it doesn't actually set the variable :thinking_face:In the latest commit on the set-env
branch this now works:
$ ./bb -e "(require '[babashka.os :as os])" -e '(prn (os/set-env "FOO" "BAR")) (prn (os/get-env "FOO"))'
true
"BAR"
So, changing environment variables in Java isn't supported. But this now works in an experimental branch (called set-env
) 👿
$ ./bb -e "(require '[babashka.os :as os])" -e '(prn (os/set-env "FOO" "BAR")) (prn (os/get-env "FOO"))'
true
"BAR"
Yet, it doesn't compose well with System/getenv, since the Java implementation caches the environment variables on the first call to such methodsNice! FOO
should also be set outside of bb, right? So echo $FOO
returns "BAR"
?
I don't think that is how it works
a process cannot set the environment of the parent process I think
Ah ok so it would be useful for processes relying on env vars invoked from within the babashka process, is that right?
yeah, although I don't know if processes invoked from babashka using processbuilder will even see the effect, since Java has its own way of handling this
ah good, that seems to work:
./bb -e "(require '[babashka.os :as os])" -e '(prn (os/set-env "dude" "1337")) @(babashka.process/process ["env"] {:inherit true})'
but you can already set those env vars as an arg to babashka.process
so for that use case it doesn't really matter
Maybe for nested process calls that you dont control :thinking_face:
Well, this cache actually makes sense since there is no way to set environment variables in JDK
Good to know that it works
I discovered also a Windows edge case. They don't support setenv
, only _putenv
;)
which looks like _putenv("FOO=bar");
no problem, can work around it ;)
@borkdude Could we also have unsetenv
wrapper?
but all in all it may not be such a good idea to include this, although it works
@thiagokokada sure, we can support it if it works with GraalVM. But why would we do so?
For my usecase, I would like to set environment variables for the current process since I use them for some things While I could use another mechanism to have this "state" (like an atom), the subprocess also needs to inherit those environment variables So to use atom+`babashka.process`, I would probably need to always inject this environment variables to every subprocess (not exactly bad, but this gets old quickly)
@thiagokokada we could support this, but it can be confusing when people also use System/getenv
so as a rule: when you use os/set-env
you should also use os/get-env
We could document this no :thinking_face: ?
but if a library already uses System/getenv
it doesn't work as expected for them
e.g. (os/set-env "JAVA_CMD" "foo/java")
followed by (clojure "something")
would not work, since clojure
uses System/getenv
Huh... Good point
In bb tasks you could accomplish setting an env variable with (shell {:extra-env {"FOO" "BAR"}} "bb some-other-task")
just invoke bb another time
Huh... You gave me a good idea
To have a task that starts the program
With the correct environment variables set
Well, I think it will not work for this case
Since I need to parse the filename from the current script too
@thiagokokada Can you explain what you mean by this?
Well, it is actually complicated
We have a binary (let's call foo
) that is symlinked multiple times including the country info
So foo-br
is "foo for Brazil", foo-mx
is "foo for Mexico"
So I have to get the current script filename, parse it and set the environment variable (`FOO_COUNTRY=br` for foo-br
)
Nowadays we use the (in)famous #_shell code here
hack
There is some alternatives, I could create a separate file that calls the main one and just set this environment variable
But after analyzing the code, the actual code works well enough and I don't think it would have any advantage of changing this now
Just reducing the usage of shell, but it is already small enough
(BTW, the most usage of the shell in this case is actually another shell script that I use to bootstrap babashka if not installed, this script can't be written in Babashka anyway since I would have a classic chicken-egg problem)
So is there any change you want or is it fine like it is?
IMO, I would like to have something like babashka.os/set-env
, but only if we could make it work with System/getenv
, otherwise I think the drawbacks are worse than the benefits
we could patch System/getenv
but this would not work with calls that don't go through the interpreter
so my initial hunch is not to hack it in, unless we see clear benefits
I think we can leave as is, if I (or someone else) find some case that it can't be workaround without changing the current env we could add with all the warnings and bells
(I feel like that I may hit this issue in the future again, but I also feel that this would be a good time to rethink the way do some things)
@borkdude Sorry about all the inconvenience about this, at least we now know that this is possible (with caveats)
@thiagokokada Absolutely no problem, I just started investigating to see if it was possible at all. And now I learned a bunch about C interop ;)
1🙌Yeah, I found it fantastic that it is relatively easy to do C interop in GraalVM, and also the binary is still static (at least when using C stdlib
)
yeah, I learned that when you do JNI the binary is no longer static
this is a benefit of using the C interop by GraalVM, that musl binaries can still be fully static
1👍The talk I did back in February at the Graal 2021 workshop at CGO is finally available: Babashka: a native Clojure interpreter for scripting https://youtu.be/Yjeh57eE9rg
1