Fork me on GitHub
#babashka
<
2023-10-06
>
Karl Xaver12:10:54

I think there's an error in the wiki at https://github.com/babashka/babashka/wiki/Bash-and-Babashka-equivalents#or-operator These don't seem equivalent as suggested:

> echo a || echo b          
a

> bb '(babashka.process/shell {:continue true} "echo a") (babashka.process/shell "echo b")'
a
b
{:proc ...}
Some bb-novice attempts: The equivalent would rather be something like
(or (= 0 (:exit (shell {:continue true} "echo a")))
    (shell "echo b"))
;; a

(or (= 0 (:exit (shell {:continue true} "ls -iwillfail")))
    (shell "echo b"))
;; ls: invalid line width: 'illfail'
;; b
BUT, there's a cornercase: when the program does not exist ProcessBuilder throws...
(or (= 0 (:exit (shell {:continue true} "iwillthrow")))
    (shell "echo b"))
;; java.io.IOException
;; error=2, No such file or directory
in contrast to bash, which will still execute the second command:
> iwillthrow || echo b
iwillthrow: command not found
b
So
(try
  (shell "iwillthrow")
  (catch Exception e
    (shell "echo b")))
;; b
might be a better fit here? But it's not easily chainable like bashs or (e.g. a || b || c || ...).

borkdude12:10:31

The example on the wiki doesn't make sense. The exit code matters. You can submit an improvement to the wiki if you want.

borkdude12:10:49

The example will be more verbose in bb for sure

borkdude12:10:33

yeah this might be good:

(or (try (shell "foo") (catch Exception _ nil)) (shell "bar"))

borkdude12:10:22

already changed in wiki, thanks for reporting

Karl Xaver12:10:38

Oh nice, didn't think of using try/catch like that. Thank you!

borkdude12:10:24

This is pretty interesting. To make a self-contained executable you can do this:

$ cat src/foo.clj
(ns foo)

(defn -main [& args]
  (prn args (+ 1 2 3)))

$ bb -cp src uberjar foo.jar -m foo

$ cat $(which bb) foo.jar > mybb.jar

$ ./mybb.jar ./mybb.jar 1 2 3

Tomas Brejla10:10:31

Neat trick. Did you have to modify bb somehow in advance to support this? How is it possible that the bb binary correctly finds out the mybb.jar, given that it's placed after the actual bb binary content? It'd be awesome if you could just do $ ./mybb.jar 1 2 3, but that wouldn't be easy to do, right? bb binary would probably have to somehow self-inspect itself, realize that there's "an uberjar glued to itself" and behave accordingly (?)

Tomas Brejla10:10:18

(btw this sorcery probably doesn't work in bb.exe, does it?)

borkdude10:10:43

> It'd be awesome if you could just do $ ./mybb.jar 1 2 3, but that wouldn't be easy to do, right? bb binary would probably have to somehow self-inspect itself Indeed, that would be more awesome. I don't know if it works on Windows, my Windows machine currently doesn't power up

borkdude10:10:05

This works by virtue of zip files allowing to have a prepended blob which is just ignored

🤯 2
borkdude10:10:23

and by virtue of executables allowing to have an appended blob as well, apparently

Tomas Brejla10:10:38

in such case it might work even in windows :thinking_face:

borkdude10:10:50

if Windows executables allow this, yes

borkdude10:10:28

If the self-inspection of the binary can be done very cheaply, I'd be willing to look into it

borkdude10:10:49

if it does not affect startup time very negatively

borkdude10:10:14

is there and end marker of a zip file, for example? then inspecting the very last bytes may be the check, don't know yet

Tomas Brejla13:10:16

https://games.greggman.com/game/zip-rant/ (interesting article 😄)

4.3.1 A ZIP file MUST contain an "end of central directory record". A ZIP file containing only an "end of central directory record" is considered an empty ZIP file. Files MAY be added or replaced within a ZIP file, or deleted. A ZIP file MUST have only one "end of central directory record". Other records defined in this specification MAY be used as needed to support storage requirements for individual ZIP files.
...
0x50 0x4b 0x03 0x04   // a local file record
0x50 0x4b 0x01 0x02   // a central directory file record
0x50 0x4b 0x06 0x06   // an end of central directory record
Peeking into foo.jar generated by babashka in hexedit, there's a 0x50 0x4b 0x05 0x06 sequence instead of 0x50 0x4b 0x06 0x06 . According to https://github.com/kornelski/7z/blob/main/CPP/7zip/Archive/Zip/ZipRegister.cpp#L12-L17, the 05 06 is Ecd, while 06 06 is Ecd64 . So perhaps just 2 different "flavors" of "End Of Central Directory" information. This sequence is then followed by additional 18 bytes (it's very questionable how this might look in different jar files etc). I'd say some heurestic could be implemented such as looking into last <X> bytes of the executable and trying to find either Ecd or Ecd64. But it's questionable whether it'll be good enough. And whether it's worth trying (if people would enjoy having the option of generating standalo binaries this way). + there is still that open question about that windows .exe files, whether all this approach works at all or not. Interesting technique though. Bit hacky, but I still like it 🙂

Tomas Brejla16:10:42

btw just tried bb.exe + foo.jar and it seems to work as well

c:\foo>type src\foo.clj
(ns foo)

(defn -main [& args]
  (prn args (+ 1 2 3)))

c:\foo>bb -cp src uberjar foo.jar -m foo

c:\foo>bb --version
babashka v1.3.185

c:\foo>copy /b bb.exe + foo.jar mybb.jar
bb.exe
foo.jar
        1 file(s) copied.

c:\foo>mybb.jar mybb.jar 1 2 3
("1" "2" "3") 6

🎉 1
borkdude17:10:37

Very promising

borkdude18:10:50

so the only thing left is some cheap heuristics to see if there is a jar/zip file appended. maybe the first heuristic could be: the executable name is not bb

$ bb -e '(-> (java.lang.ProcessHandle/current) .info .command .get)'
"/Users/borkdude/dev/babashka/bb"

Tomas Brejla18:10:19

so the only thing left..Yes, this and.. • re-evaluate if this functionality is something you really want to support "forever". 😉 Other than that... I like the first heurestics idea 😄 (ie bb or bb.exe) Btw there's one issue I had on windows.. The scenario described ^^^ didn't work for me at all first. Then I realized I had bb.exe installed via scoop. Ie. it was sitting on my %PATH% somewhere, pointing to C:\Users\brdloush\scoop\shims\bb.exe When I do: copy /b C:\Users\brdloush\scoop\shims\bb.exe + foo.jar mybb.jar then the execution of mybb.jar fails with:

C:\foo>mybb.jar mybb.jar 1 2 3
Cannot open shim file for read.
Could not read shim file.
After I realized the problem was with scoop version of babashka, I simply downloaded the windows binary directly from the babashka release page on github - and with that exe, it worked fine. Not sure if this is scoop-only problem - there may be similar issues in other platforms and/or "package-managers"

Tomas Brejla18:10:49

Btw on windows:

C:\foo>bb -e "(-> (java.lang.ProcessHandle/current) .info .command .get)"
"C:\\foo\\bb.exe"

borkdude18:10:57

the bb.exe in scoop is perhaps a symlink or so?

borkdude18:10:28

I got this code from Stackoverflow to detect if the file is a zip file:

(ns buber)

(import [ File RandomAccessFile IOException])

(defn is-archive? [f]
  (let [file-signature (atom 0)]
    (try
      (with-open [raf (RandomAccessFile. f "r")]
        (reset! file-signature (.readInt raf)))
      (catch IOException _
        ;; Handle the exception if you like
        (prn :dude)))
    (prn @file-signature)
    (or (= @file-signature 1347093252)
        (= @file-signature 1347093766)
        (= @file-signature 1347094280))))

(-> (File. "./buber") #_(java.lang.ProcessHandle/current) #_#_#_#_.info .command .get (File.)
    is-archive? prn)
but it didn't work for me

Tomas Brejla18:10:18

that probably only works on zip content only, not bb+zip

borkdude18:10:10

ok, this might work then:

(let [is-zip (try (.entries (java.util.zip.ZipFile. (first args)))
                    true
                    (catch Exception _ nil))]
    (prn "Is zip:" is-zip))

Tomas Brejla18:10:17

no idea what additional stuff the scoop version of bb.exe has inside it, but it's whopping 126MB.

C:\foo>C:\Users\brdloush\scoop\shims\bb.exe --version
babashka v1.3.182

C:\foo>dir C:\foo>C:\Users\brdloush\scoop\shims\bb.exe --version

C:\foo>dir C:\Users\brdloush\scoop\shims\bb.exe
 Volume in drive C has no label.
 Volume Serial Number is 009D-E031

 Directory of C:\Users\brdloush\scoop\shims

05.08.2023  10:50           126 464 bb.exe
In contrast, the downloaded + extracted bb.exe from github releases only has
28.09.2023  14:14        64 466 944 bb.exe
Perhaps scoop does some of its own "glue some stuff to the end of the binary" ? 😄

borkdude18:10:46

haha I don't know, perhaps ask in #C01JDDEBRU1

Tomas Brejla18:10:23

Yup. It's a bit surprising. I mean.. twice the size, that's a bit too much.

borkdude18:10:39

as long as (-> (java.lang.ProcessHandle/current) .info .command .get) works reliably, can just use that

Tomas Brejla18:10:09

> as long as (-> (java.lang.ProcessHandle/current) .info .command .get) works reliably, can just use that Ok, we have mac (you're on mac, right?), windows build from GH releases.. Here's Ubuntu with bb installed via brew

➜  ~ uname --hardware-platform --kernel-name --kernel-version --operating-system && bb -e '(-> (java.lang.ProcessHandle/current) .info .command .get)'          
Linux #33~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Sep  7 10:33:52 UTC 2 x86_64 GNU/Linux
"/home/linuxbrew/.linuxbrew/Cellar/babashka/1.3.184/bin/bb"

Tomas Brejla18:10:45

so far so good

borkdude18:10:07

All together now, this could be the handling of the self-invoking jar:

(ns buber
  (:require [clojure.string :as str]
            [babashka.fs :as fs]))

(defn -main [& args]
  (let [cmd (-> (java.lang.ProcessHandle/current) .info .command .get)]
    (when-not (str/ends-with cmd (if (fs/windows?)
                                   "bb.exe"
                                   "bb"))
      (let [is-zip? (try (with-open [zip-file (java.util.zip.ZipFile. cmd)]
                           (.entries zip-file))
                         true
                         (catch Exception _ nil))]
        (when is-zip?
          ;; invoke uberjar main function of cmd
          ))))
  )

Tomas Brejla18:10:48

worth a try 🤞

borkdude18:10:24

Ah, just (java.util.zip.ZipFile. cmd) is already sufficient for the check

nice 1
borkdude09:10:31

@U01LFP3LA6P Pushed the functionality to master. When current CI build finishes, these binaries should work as self-invoking jars: https://github.com/babashka/babashka/releases/tag/untagged-2718428094ee3428b0e6

❤️ 1
borkdude09:10:10

The nice thing about this is that you can build self-invoking jar-binaries all on linux, no need to build on OS-specific CI. Just download bb for each OS and append the jar, that's it :)

Tomas Brejla10:10:05

and the speed of "building" is amazing, right? 🙂

borkdude10:10:09

Indeed, sub-second :)

Tomas Brejla12:10:07

just gave it a try and it works fine on linux

➜  ./bb --version                   
babashka v1.3.186-SNAPSHOT
➜  bb -cp src uberjar foo.jar -m foo
➜  cat bb foo.jar > foo             
➜  chmod +x foo                     
➜  ./foo 1 2 3
("1" "2" "3") 6
👏 ❤️

❤️ 1
Tomas Brejla12:10:52

I'll try it with windows .exe in the evening. Update: works fine.

minikomi23:10:30

Lovely to see this coming together in real time

littleli08:10:06

Tomáši 🙂 in scoop\shims directory, it's only a stub executable. it has size of 126KB ! not MB! true binary is in different directory scoop\apps\babashka\current

thanks 1
littleli08:10:49

if you want to do the bundling with bb.exe you have to avoid the stub, since it only captures execution environment and runs the big binary.

littleli09:10:58

Reason for this is practical, one of the goals of scoop is to avoid poluting of your environment. They went with these shims which are on the single location where all the applications can be put on the single path.

littleli09:10:03

something like: PATH=%PATH%:SCOOP_SHIMS_DIR%

Tomas Brejla09:10:51

Thanks for bumping this thread @U042D1SQ56U 🙏. I realized that there was one more important information in this thread - that I wasn't able to use the bb.exe installed via scoop succesfully. So I asked @UBLU3FQRZ (who's maintaining https://github.com/littleli/scoop-clojure) whether he has some idea why it's so. Thanks for explaining, @UBLU3FQRZ! Yeah, it's 126KB, not MB 😄, Pitty I haven't noticed that. If I did, I'd probably get the idea it's sort of link/loader of the true bb exe sitting somewhere else. Now it makes sense. @U04V15CAJ should it be mentioned in the documentation part of this new feature that with SCOOP (+ possibly other packagers if they use similar approach as scoop), people should be extra-sure that they're using the "true bb binary" and not just some shim, when gluing the binary + jar?

Tomas Brejla09:10:59

Btw I didn't know about the concept of shims. Here seems to be some nice summary describing the benefts https://docs.chocolatey.org/en-us/features/shim#benefits It's from chocolatey, not scoop, but I guess the shim idea is the same.

1
borkdude09:10:11

> • The exe can be called from powershell, bash, cmd.exe, or other shells just like you would call the target. This is one of the bigger benefits of deps.clj for Windows as well instead the powershell module

borkdude09:10:58

@U01LFP3LA6P The documentation suggests downloading a version of bb from Github releases, I think this is the safest approach since you are not coupled to the bb you have installed, nor the bb for your own operating system

❤️ 1
littleli09:10:31

@U01LFP3LA6P no worries man. I'm glad I could help a bit

borkdude09:10:30

I bet someone could make a library to make self-contained uberjar binaries for each os. It involves downloading the versions from Github releases and then appending the uberjar, making the file executable, done! Possibly: upload to Github releases (https://github.com/borkdude/gh-release-artifact)

❤️ 1
truestory 1
littleli09:10:35

There was a concept of overlays inside executables in old good 90s when I was developing using Borland Pascal. Dunno what happened since then.

littleli09:10:32

Basically payloads appended after the code part of the program.

borkdude09:10:46

@UBLU3FQRZ This is exactly what all of the above is about and how self-contained bb executables work

Tomas Brejla09:10:23

And it feels surprisingly useful for things like babashka (or anything that supports interpreted execution with something like SCI). Using this payload appending approach, you can have CI pipeline step that generates all the executables for combinations of OS/CPU_platform.. and it can be executed anywhere + executes in couple of seconds.

👍 1
borkdude14:10:59

My Strange Loop talk just got published: https://twitter.com/strangeloop_stl/status/1710305086914662439

🙌 10
🎉 15
babashka 2
gratitude-thank-you 1
Ingy döt Net15:10:08

Been waiting for this. @U0479UCF48H told me it covers the relationship between bb and sci.

borkdude15:10:29

it does

👍 1
Ingy döt Net16:10:43

Just finished a 2X speed. Very good talk. 🙂

🙏 1
1
mmer10:10:56

Really helpful to see how SCI relates to CLJ/CLJS.

fabrao15:10:50

Hello, Is babaska have some caching? I'm trying to use my code with babashka but if I change something into my code and try to execute again, it seems that is not reflecting.

borkdude15:10:30

without a repro, can't say

fabrao15:10:37

let me try to reproduce into simple scenario because my project is huge.

fabrao16:10:25

the different thing is that the source code came from :deps {fernando/abrao {:local/root "lib/testing"}}, is that affect something?

borkdude16:10:06

classpaths are cached

fabrao16:10:30

is there any way to force not cache?

fabrao16:10:58

or remove the cache, where is located?

borkdude16:10:05

it's still not clear what your problem exactly is/was though. classpaths are cached so you don't have to shell out to java to calculate the classpath.

borkdude16:10:25

it does NOT mean that bb does not see code changes on your classpath, or course

borkdude16:10:35

so if you change your local lib, bb will see that

fabrao16:10:57

that's the problem what is happening

fabrao16:10:42

I make a change into local lib, and it's not seeing the change

borkdude16:10:53

it could also be that you don't use unique coordinates for one and the same library and another version of your library is also on the classpath

borkdude16:10:23

can you paste the output of bb -e '(babaska.classpath/get-classpath)' ?

fabrao16:10:43

bb -e '(babaska.classpath/get-classpath)'
----- Error --------------------------------------------------------------------
Type:     clojure.lang.ExceptionInfo
Message:  Could not resolve symbol: babaska.classpath/get-classpath
Data:     {:type :sci/error, :line 1, :column 1, :file "<expr>", :phase "analysis"}
Location: <expr>:1:1
Phase:    analysis

borkdude16:10:58

fix the obvious typo

borkdude16:10:26

babaska -> babashka

fabrao16:10:39

:melting_face: sorry

fabrao16:10:12

the code is here /home/fabrao/radiant/core-modeling-api/libs/modeling-common/src

borkdude16:10:03

ok, and can you make a top level change in one of the clojure files that gets required in that directory, like (ns foo) (prn :hello)

fabrao16:10:00

I did, but it's getting the version before I changed

fabrao16:10:28

OH, something is strange , the source code of classpath is came from other source

borkdude16:10:44

I suspected as much

fabrao16:10:05

/home/fabrao/radiant/reviews/core-modeling-api -> current

borkdude16:10:15

it should be lib/testing?

borkdude16:10:54

can you do bb clojure -Sdeps-file bb.edn -Stree?

fabrao16:10:54

it should be /home/fabrao/radiant/reviews/core-modeling-api/libs/modeling-common/src

borkdude16:10:01

and paste the output here

borkdude16:10:11

assuming that your deps are in bb.edn

borkdude16:10:16

and also paste your bb.edn here

borkdude16:10:10

and does that look good or not?

dpsutton16:10:59

i see schema-registry-v2 on the classpath twice under different coordinates. So if you are seeing stale specs, you’ve got that lib there twice and it’s nondeterministic which one will be found at run time

👍 1
fabrao16:10:07

this is in the right place, but the classpath not

borkdude16:10:20

can you please paste your bb.edn as well

fabrao16:10:28

why this bb -e '(babaska.classpath/get-classpath)' is showing something different?

dpsutton16:10:28

where is > the different thing is that the source code came from :deps {fernando/abrao {:local/root "lib/testing"}}, is that affect something?

fabrao16:10:14

@U11BV7MTK ignore this, was only a test

borkdude16:10:01

sorry, but I can't debug this unless you share the project on github. you can use -Sforce to recalculate the classpath

fabrao16:10:30

-Sforce where?

fabrao16:10:47

sorry, I can't share the project

borkdude16:10:03

usually these problems arise when people use multiple names like fernando/abrao and com.foo.bar/baz for the same library so the library appears multiple times on the classpath and you don't know which one gets loaded

borkdude16:10:11

bb -Sforce ...

fabrao16:10:26

ok, I'll try it

dpsutton16:10:42

> why this bb -e '(babaska.classpath/get-classpath)' is showing something different? what exactly is different? and if it’s a classpath, not just “the whole classpath is different” but exactly which items on the classpath are different or not as expected

fabrao16:10:41

@U04V15CAJ Worked with -Sforce

fabrao16:10:48

thank you !!!

borkdude16:10:14

we still haven't found the root cause of the problem though

dpsutton16:10:42

did you change the deps of the local root referenced lib?

dpsutton16:10:46

(the message seems to be gone)

borkdude16:10:33

what version of bb are you running

fabrao16:10:03

babashka v1.3.182

borkdude16:10:28

maybe try the very newest, it uses the latest tools deps, just in case

fabrao16:10:45

ok, thank you

steveb8n18:10:43

Q: does bb have a way to make a web socket connection and handle messages?

Chip19:10:50

To the naïve observer, the list of what bb doesn’t do would be shorter than the list of what it does.

😆 2