This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-10-06
Channels
- # aleph (15)
- # announcements (2)
- # babashka (121)
- # beginners (62)
- # biff (6)
- # cherry (2)
- # cider (51)
- # clerk (30)
- # cljs-dev (5)
- # clojure (77)
- # clojure-austin (2)
- # clojure-europe (10)
- # clojure-germany (6)
- # clojure-nl (1)
- # clojure-norway (19)
- # clojure-romania (1)
- # clojure-uk (3)
- # clojurescript (16)
- # core-typed (7)
- # cursive (17)
- # datomic (12)
- # deps-new (11)
- # emacs (7)
- # events (2)
- # fulcro (5)
- # honeysql (2)
- # hyperfiddle (32)
- # introduce-yourself (1)
- # jobs-discuss (2)
- # membrane (18)
- # missionary (2)
- # music (5)
- # polylith (7)
- # reagent (26)
- # releases (5)
- # testing (32)
- # tools-build (14)
- # tools-deps (7)
- # xtdb (8)
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 || ...
).The example on the wiki doesn't make sense. The exit code matters. You can submit an improvement to the wiki if you want.
yeah this might be good:
(or (try (shell "foo") (catch Exception _ nil)) (shell "bar"))
Oh nice, didn't think of using try/catch like that. Thank you!
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
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 (?)
(btw this sorcery probably doesn't work in bb.exe, does it?)
> 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
This works by virtue of zip files allowing to have a prepended blob which is just ignored
in such case it might work even in windows :thinking_face:
If the self-inspection of the binary can be done very cheaply, I'd be willing to look into it
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
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 🙂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
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"
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"Btw on windows: ✅
C:\foo>bb -e "(-> (java.lang.ProcessHandle/current) .info .command .get)"
"C:\\foo\\bb.exe"
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 methat probably only works on zip content only, not bb+zip
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))
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" ? 😄Yup. It's a bit surprising. I mean.. twice the size, that's a bit too much.
as long as (-> (java.lang.ProcessHandle/current) .info .command .get)
works reliably, can just use that
> 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"
so far so good
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
))))
)
worth a try 🤞
@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
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 :)
and the speed of "building" is amazing, right? 🙂
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
👏 ❤️I'll try it with windows .exe in the evening. Update: works fine.
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
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.
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.
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?
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.
> • 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
@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
@U01LFP3LA6P no worries man. I'm glad I could help a bit
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)
There was a concept of overlays inside executables in old good 90s when I was developing using Borland Pascal. Dunno what happened since then.
@UBLU3FQRZ This is exactly what all of the above is about and how self-contained bb executables work
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.
My Strange Loop talk just got published: https://twitter.com/strangeloop_stl/status/1710305086914662439
Been waiting for this. @U0479UCF48H told me it covers the relationship between bb and sci.
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.
the different thing is that the source code came from :deps {fernando/abrao {:local/root "lib/testing"}}
, is that affect something?
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.
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
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
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)
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
where is
> the different thing is that the source code came from :deps {fernando/abrao {:local/root "lib/testing"}}
, is that affect something?
@U11BV7MTK ignore this, was only a test
sorry, but I can't debug this unless you share the project on github. you can use -Sforce
to recalculate the classpath
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
> 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
@U04V15CAJ Worked with -Sforce