babashka / bbin

Install any Babashka script or project with one command
MIT License
139 stars 9 forks source link

Allow custom wrappers scripts via an install hook? #45

Closed jeroenvandijk closed 1 year ago

jeroenvandijk commented 1 year ago

Thank you for creating this project. I did some tests and things work in a very clean way.

I'm trying to minimize the startup time of my cli tool. When I install it with bbin it adds around 40ms extra due to how the wrapper script works (invoking bb one more time). By writing my own wrapper script I could keep the total around 40ms, but this would limit the distribution options (only exact url or local file). Maybe it is possible to have bbin provide a hook that would be called during installation in order to create a custom wrapper script?

You might reason 40ms is not that much, but I'm working on a command line tool that I intend to use regularly and as a part of a chain of other binaries. Sometimes the binary is even executed multiple times in one invocation. This means that every addition in latency quickly adds up.

Maybe related to #18 and #40

I'll do some experimentation with my own wrapper script and report back later.

rads commented 1 year ago

We used to generate Bash wrappers but we moved to Babashka wrappers for Windows support. We removed the original Bash wrappers so we only have to maintain one format for the time being.

It's possible this may change in the future once the code matures a bit more. Until then I suggest creating a Bash script manually to invoke bb -Sdeps:

#!/usr/bin/env bash
bb -Sdeps '
{:deps {com.github.rads/watch {:git/url "https://github.com/rads/watch.git"
                               :git/tag "v0.0.4"
                               :git/sha "d5f36aa54e685f42f9592a7f3dd28badc3588c08"}}}
' -m rads.watch -- "$@"
$ chmod +x watch
$ ./watch --help
Usage: watch [utility]

Run arbitrary commands when files change.

Examples:
  ls | watch
jeroenvandijk commented 1 year ago

Thank you for the feedback. Removing the bash wrappers make sense.

I have found an alternative solution that I'll publish later. Basically I've played around with requiring-resolve and being lazy about loading deps and parsing code. I now have a babashka task that compiles my project into one file. Most namespaces are put in strings and are only eval-ed when required. This saves quite some time in most cases, and by putting it in one file I can use bbin install compiled-output.clj. I believe there are other options, e.g. with jar files, but I haven't tried this yet.

Maybe one of these strategies could be formalised as a part of the install process of bbin, maybe not. I'll post a link to my example later.

jeroenvandijk commented 1 year ago

Small update, some first tests seem to suggest that creating an uberjar (instead of inlining the code), and running from that is equivalent in boottime to the inlining option. I have the following bb.edn for the tree options:

{:deps {deps/local {:local/root "."}}

 :tasks
 {install (shell "bbin" "install" ".")

  generate-inline-script tasks/generate-inline-script

  install-inline (do (run 'generate-inline-script)
                     (shell "bbin" "install" "target/inline.clj" "--as" "aws.console"))

  install-jar (do 
    (shell "bb" "uberjar" "target/uberjar.jar")

    (spit "target/uberjar-inline.clj" (clojure.string/join "\n" [

    '(require '[babashka.classpath :refer [add-classpath]])

    (list 'add-classpath (str (System/getenv "PWD") "/target/uberjar.jar"))
    '(require '[aws.console])
    '(apply aws.console/-main *command-line-args*)

    ]))
    (shell "bbin" "install" "target/uberjar-inline.clj" "--as" "aws.console")

  )
 }

 :bbin/bin {aws.console {:main-opts ["-f" "src/aws/console.clj"]}}
 }

To me the uberjar route sounds like an interesting optimisation option for bbin. It would require to put the uberjar somewhere, but I'm guessing this would work on all operating systems.

jeroenvandijk commented 1 year ago

I'm closing this issue, it's a little too broad in hindsight and after some testing I found that the uberjar has the best boottime. I had a discussion about this in Slack. The jar wrapper script would need to be patched to not use exec, I'll create an issue for this later.