If I want to use babashka for project automation scripting, in a cross-platform way, would using the jar be wise?
Currently getting this error, but maybe I need to use a newer version of Java?
[brandon@brandon-desktop calva]$ java -jar bb.jar -h
Exception in thread "main" java.lang.UnsupportedClassVersionError: sci/impl/Reflector has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
[brandon@brandon-desktop calva]$ java -version
openjdk version "1.8.0_275"
OpenJDK Runtime Environment (build 1.8.0_275-b01)
OpenJDK 64-Bit Server VM (build 25.275-b01, mixed mode)
Yeah, in this case the jar was byte compiled with JDK 11
Thanks
Is it only runnable with that specific version? I tried the above with jdk 15 and got IllegalArgumentException: Uknown signal: PIPE
Hum, it should be runnable with JDK 15
Thinking about it, I'm not sure the cross-platform / include-the-tool-in-project really is a big concern. Devs already need other tools to work on a given project. ๐
I managed to make a GH action windows build compiling the native image ๐ https://github.com/clojure-lsp/clojure-lsp/blob/master/.github/workflows/release.yml#L235-L279
Worst case you could build a static binary with graal maybe? The babashka docker file and scripts are pretty manageable
Thanks. It seems for dev use it makes sense to just require the developer of Calva install the bb binary for their machine.
@brandon.ringe I'm not sure if the .jar adds anything beyond a normal clojure project. babashka is basically a clojure interpreter + a set of libraries. So using it from the JVM kind of defeats the purpose of good startup time.
Maybe you could explain your use case more. If this is just used as an alternative to clojure
inside of Calva I suggest using the deps.clj
jar.
@pez ^
Latest Calva actually is using deps.clj
for this (on Windows). Have I then introduced a requirement for some special version of Java?
As for @brandon.ringeโs use case it is for a script during Calva development. Weโll probably just require that people have babashska installed to use the script.
I thought you are using bb.exe
It added a bit too many megabytes to the extension, so I switched to deps.clj.
Makes sense
As for scripting for development. I think people should just install bb for Calva dev. It's available for every major OS
Or you can make your scripts portable with clojure
by adding a deps.edn
so people can run the scripts using that if they don't have bb and don't want to use it.
But maybe it's ok to be opinionated for dev mode
> As for scripting for development. I think people should just install bb forย Calvaย dev. Itโs available for every major OS Yes, thatโs where we are going.
Is it possible to test a process that accepts stdin and produces to stdout via babashka?
@borkdude Is it possible to run a babashka script (with bb interpreter on header) from windows?
windows doesn't support this, so you have to run it like bb thescript.clj
๐
my idea is to create some kind of integration test for clojure-lsp, where I run the process, send some json and expect other json
Is it possible with https://github.com/babashka/process?
I'm trying something like that:
#!/usr/bin/env bb
(require '[babashka.process :refer [process $ check]])
(with-out-str (check (process '[clojure-lsp]
{:in "{}\r\n" :out *out*})))
but it seems to keep listen to the process
Oh, just found the cat
demo, I'll check
@ericdallo you, you can add :in
and :out
args to a process
I'm trying with something like this:
(def clojure-lsp-proc (process '[clojure-lsp]))
(when-not (.isAlive (:proc clojure-lsp-proc))
(println "TODO error")
(System/exit 1))
(def stdin (io/writer (:in clojure-lsp-proc)))
(binding [*out* stdin]
(println "{}\r\n"))
(.close stdin)
(println (slurp (:out clojure-lsp-proc)))
I'd need something that I can print to stdin and keep open and sync listen for stdout after each stdin input
oh, the issue is with slurp, since it's waiting for process finish
but the process will keep going, I only need the output response
is the output EDN?
no, server should respond with something like:
Content-Type: 232
{"some":"json"}
so you can read a couple of lines of text?
or first some content type line and then a json value?
you can easily do this without waiting for the process to end
yeah, I need to read the first line, then an new line and then a json
the issue is: server can return a json on that pattern anytime, and I just want to parse it, check if is the json I want or ignore it
so the server receives an input in that pattern and can start respondinhg with multiple responses following that pattern
For some reason it keeps waiting for the stout stream:
(def clojure-lsp-proc (process '[./clojure-lsp]))
(when-not (.isAlive (:proc clojure-lsp-proc))
(println "TODO error")
(System/exit 1))
(def stdin (io/writer (:in clojure-lsp-proc)))
(def stout (io/reader (:out clojure-lsp-proc)))
(binding [*out* stdin]
(println "Content-Length: 3")
(println "")
(println "{}"))
(println (with-open [rdr stout]
(reduce conj [] (line-seq rdr))))
this is because line-seq will consume until EOF and if your process is still alive and waiting for more input, you won't get an EOF
hum got it
what's the best approach here so? It'd be great if I could say, read next 3 lines (Content Type, newline and the json content)
yeah, so do (read-line)
twice, while binding *in*
to the stdout an then read one JSON value from the stream
(def clojure-lsp-proc (process '[./clojure-lsp]))
;; (def clojure-lsp-proc (process '[cat]))
(when-not (.isAlive (:proc clojure-lsp-proc))
(println "TODO error")
(System/exit 1))
(def stdin (io/writer (:in clojure-lsp-proc)))
(def stout (io/reader (:out clojure-lsp-proc)))
(binding [*out* stdin]
(println "Content-Length: 3")
(println "")
(println "{}"))
(binding [*in* stout]
(println (read-line))
(println (read-line))
(println (read-line)))
(println "Done!")
Right?
Still freezesBut does it print the three lines?
Try 2 lines. Maybe the third line doesn't have a newline?
No, its frozen ๐
I changed to
(binding [*in* stout]
(println "->" (read-line))
(println "->" (read-line))
(println "->" (read-line)))
and nothing is printed, so the read-line is not reading
Oh, also there is a time for the server start to respond (2-3s) Not sure if it's related
@ericdallo try :out :inherit
, this will print the output to stdout directly
for debugging
Not sure I did something wrong:
(def clojure-lsp-proc (process '[./clojure-lsp]
{:out :inherit}))
(def stdin (io/writer (:in clojure-lsp-proc)))
(def stout (io/reader (:out clojure-lsp-proc)))
(binding [*out* stdin]
(println "Content-Length: 3")
(println "")
(println "{}"))
(binding [*in* stout]
(println "->" (read-line))
(println "->" (read-line))
(println "->" (read-line)))
(println "Done!")
:
-> nil
-> nil
-> nil
Done!
in this case you should not read from stdout, since it's already redirected
it basically does nothing
oh, alright
so commenting the binding, it starts, print the Done and finish
it seems is not waiting for the server start
because your script isn't waiting for something
it just terminates
Try (Thread/sleep 10000)
or something
it just wait for the sleep then end, prints nothing
(def clojure-lsp-proc (process '[./clojure-lsp]
{:out :inherit}))
(def stdin (io/writer (:in clojure-lsp-proc)))
;; (def stout (io/reader (:out clojure-lsp-proc)))
(binding [*out* stdin]
(println "Content-Length: 3")
(println "")
(println "{}"))
;; (binding [*in* stout]
;; (println "->" (read-line))
;; (println "->" (read-line))
;; (println "->" (read-line)))
(println "Done!")
(Thread/sleep 20000)
if it doesn't print anything, then something is probably wrong. Add some debugging by printing to stderr from the process. Also use :err :inherit
.
oh, yeah, the :err :inherit
worked ๐
sorry, I forgot to mention that a {}
should return an error, but I though it was considered output anyway
I'll build a valid LSP spec json and test it
Ok, it worked:
Now I need to remove the debugging out inherit and handle that without sleeping somehow
Ok, with this:
(def clojure-lsp-proc (process '[./clojure-lsp]))
(def stdin (io/writer (:in clojure-lsp-proc)))
(def stout (io/reader (:out clojure-lsp-proc)))
(binding [*out* stdin]
(println "Content-Length: 59")
(println "")
(println "{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"params\":{},\"id\":1}"))
(binding [*in* stout]
(println "->" (read-line))
(println "->" (read-line))
(println "->" (read-line)))
(println "Done!")
I get:For some reason the last read-line is not being printed
Maybe because the json isn't ending in a newline?
can you try (cheshire.json/parse-stream *in*)
instead of the third read-line?
Hum, I imagine cheshire.json/
is not bundled with bb?
it can't find the require
eh cheshire.core, typo
Yeah, it seems to work ๐
thanks for the help @borkdude!
๐
bb is really a game changer
before trying bb, I searched a little bit on how to read process output dynamically and send input via bash
and I need to say that is a pretty mess hahaha