Fork me on GitHub
#babashka
<
2021-02-02
>
bringe00:02:38

If I want to use babashka for project automation scripting, in a cross-platform way, would using the jar be wise?

bringe00:02:19

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

ericdallo00:02:32

Yeah, in this case the jar was byte compiled with JDK 11

👍 3
bringe00:02:33

Is it only runnable with that specific version? I tried the above with jdk 15 and got IllegalArgumentException: Uknown signal: PIPE

ericdallo01:02:55

Hum, it should be runnable with JDK 15

bringe00:02:48

[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)

bringe01:02:33

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. 😄

jaide04:02:39

Worst case you could build a static binary with graal maybe? The babashka docker file and scripts are pretty manageable

bringe20:02:54

Thanks. It seems for dev use it makes sense to just require the developer of Calva install the bb binary for their machine.

borkdude08:02:54

@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.

borkdude08:02:41

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.

pez12:02:49

Latest Calva actually is using deps.clj for this (on Windows). Have I then introduced a requirement for some special version of Java?

pez12:02:34

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.

borkdude12:02:46

I thought you are using bb.exe

pez12:02:48

It added a bit too many megabytes to the extension, so I switched to deps.clj.

borkdude12:02:44

Makes sense

borkdude12:02:14

As for scripting for development. I think people should just install bb for Calva dev. It's available for every major OS

borkdude12:02:28

Or you can make your scripts portable with clojure by adding a deps.edn

borkdude12:02:37

so people can run the scripts using that if they don't have bb and don't want to use it.

borkdude12:02:29

But maybe it's ok to be opinionated for dev mode

pez13:02:15

> 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.

ericdallo16:02:38

Is it possible to test a process that accepts stdin and produces to stdout via babashka?

ericdallo16:02:00

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

ericdallo17:02:28

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*})))

ericdallo17:02:33

but it seems to keep listen to the process

ericdallo17:02:42

Oh, just found the cat demo, I'll check

borkdude17:02:13

@UKFSJSM38 you, you can add :in and :out args to a process

ericdallo17:02:18

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)))

ericdallo17:02:00

I'd need something that I can print to stdin and keep open and sync listen for stdout after each stdin input

ericdallo17:02:02

oh, the issue is with slurp, since it's waiting for process finish

ericdallo17:02:14

but the process will keep going, I only need the output response

borkdude17:02:58

is the output EDN?

ericdallo17:02:59

no, server should respond with something like:

ericdallo17:02:13

Content-Type: 232

{"some":"json"}

borkdude17:02:27

so you can read a couple of lines of text?

borkdude17:02:46

or first some content type line and then a json value?

borkdude17:02:57

you can easily do this without waiting for the process to end

ericdallo17:02:54

yeah, I need to read the first line, then an new line and then a json

ericdallo18:02:59

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

ericdallo18:02:25

so the server receives an input in that pattern and can start respondinhg with multiple responses following that pattern

ericdallo18:02:25

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))))

borkdude18:02:05

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

ericdallo18:02:06

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)

borkdude18:02:00

yeah, so do (read-line) twice, while binding *in* to the stdout an then read one JSON value from the stream

ericdallo18:02:49

(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 freezes

borkdude18:02:40

But does it print the three lines?

borkdude18:02:54

Try 2 lines. Maybe the third line doesn't have a newline?

ericdallo19:02:43

No, its frozen 😕

ericdallo19:02:24

I changed to

(binding [*in* stout]
  (println "->" (read-line))
  (println "->" (read-line))
  (println "->" (read-line)))

ericdallo19:02:39

and nothing is printed, so the read-line is not reading

ericdallo19:02:14

Oh, also there is a time for the server start to respond (2-3s) Not sure if it's related

borkdude19:02:17

@UKFSJSM38 try :out :inherit, this will print the output to stdout directly

👍 3
borkdude19:02:22

for debugging

ericdallo19:02:18

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!

borkdude19:02:43

in this case you should not read from stdout, since it's already redirected

borkdude19:02:20

it basically does nothing

ericdallo19:02:09

so commenting the binding, it starts, print the Done and finish

ericdallo19:02:32

it seems is not waiting for the server start

borkdude19:02:03

because your script isn't waiting for something

borkdude19:02:10

it just terminates

borkdude19:02:28

Try (Thread/sleep 10000) or something

ericdallo19:02:57

it just wait for the sleep then end, prints nothing

ericdallo19:02:13

(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)

borkdude19:02:36

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.

ericdallo19:02:17

oh, yeah, the :err :inherit worked 😄

ericdallo19:02:44

sorry, I forgot to mention that a {} should return an error, but I though it was considered output anyway

ericdallo19:02:55

I'll build a valid LSP spec json and test it

ericdallo19:02:31

Ok, it worked:

ericdallo19:02:51

Now I need to remove the debugging out inherit and handle that without sleeping somehow

ericdallo19:02:25

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:

ericdallo19:02:46

For some reason the last read-line is not being printed

borkdude19:02:30

Maybe because the json isn't ending in a newline?

borkdude19:02:33

can you try (cheshire.json/parse-stream *in*) instead of the third read-line?

ericdallo19:02:53

Hum, I imagine cheshire.json/ is not bundled with bb?

ericdallo19:02:59

it can't find the require

borkdude19:02:15

eh cheshire.core, typo

👍 3
ericdallo19:02:22

Yeah, it seems to work 🎉

ericdallo19:02:41

thanks for the help @U04V15CAJ!

ericdallo19:02:59

bb is really a game changer

ericdallo20:02:32

before trying bb, I searched a little bit on how to read process output dynamically and send input via bash

ericdallo20:02:51

and I need to say that is a pretty mess hahaha

ericdallo13:02:00

@U04V15CAJ Is it possible to run a babashka script (with bb interpreter on header) from windows?

borkdude13:02:41

windows doesn't support this, so you have to run it like bb thescript.clj