Fork me on GitHub
#babashka
<
2020-05-03
>
Mno11:05:36

I'm not sure where else to ask since it's a fairly specific question (maybe in the graalvm channel later)

Mno11:05:29

I have a script that works just fine if I run it with bb and that is also set up to be compile-able (as far as I can tell) but when I try to create a native image it tells me an assertion failed:

Execution error (AssertionError) at clojure.tools.cli/compile-option-specs (cli.cljc:282).

Mno11:05:37

Assert failed: (distinct?* (remove nil? (map :short-opt %)))

Mno11:05:33

I looked into the error report and it said it had an issue expanding on line 163 which corresponds to:

(when-not (System/getProperty "babashka.main")
  (apply -main *command-line-args*))

Mno11:05:25

so I removed that and tried compiling it manually, but then it just tells meit cant find the main entry point.

Error: Main entry point class 'nativity' not found.
com.oracle.svm.core.util.UserError$UserException: Main entry point class 'nativity' not found.

Mno11:05:42

I'd appreciate any suggestions on where to even start looking.

borkdude11:05:13

The error suggests that you are trying to call tools.cli at compile time

borkdude11:05:29

> Execution error (AssertionError) at clojure.tools.cli/compile-option-specs (cli.cljc:282)

borkdude11:05:47

So maybe you are trying to do that on the top level in a namespace. Move it inside a function

Mno11:05:02

I only use parse-opts in the -main function, could that be the issue?

Mno11:05:05

the cli-options vector is defined in the top-level namespace but that doesn't use anything from http://tools.li

Mno11:05:40

I noticed that it seems to run itself before compiling..

Marianos-MacBook-Pro:nativity mariano$ ./nativity.clj which.clj -m untouched
reading file
making directory
making modified script-file
making deps file
compiling native binary
Compiling nativity
Compiling which
/usr/bin/which
[which:60487]    classlist:   7,439.09 ms,  1.32 GB
[which:60487]        (cap):   1,191.32 ms,  1.32 GB
[which:60487]        setup:   2,911.96 ms,  1.32 GB

borkdude11:05:27

this indicates that you are executing the main function at compile time

borkdude11:05:02

so this code:

(when-not (System/getProperty "babashka.main")
  (apply -main *command-line-args*))

borkdude11:05:11

does exactly thst

Mno11:05:34

I thought that checked if the systemproperty was there

Mno11:05:39

and then ran it if it was

borkdude11:05:04

yes, that's what it does. so what does it do when the system property is not set?

Mno11:05:37

I don't recall setting that property at any point..

borkdude11:05:54

so, what happens then?

Mno11:05:11

it runs regardless?

borkdude11:05:00

this line is there to make the script execute as a script, when babashka.main is not set. so if you set babashka.main, it won't

borkdude11:05:25

you can add a check to it, to see if you're running with graalvm native-image or something else

Mno11:05:45

hmmm okay I'll solve that separately, because when I remove it still fails to find main for some reason

borkdude11:05:20

how do you create the uberjar for compilation with native-image again?

Mno11:05:04

deps.edn file and an untouched version of the src script in the src folder?

Mno11:05:26

then clj -A:native-image, so I'm not really sure how any of that works

borkdude11:05:34

in clj native-image I think you have to say where the main method is

Mno11:05:39

(defn generate-deps-file [name-space native-image-name]
 {:deps {'cheshire {:mvn/version "5.10.0"}
       'org.clojure/tools.cli {:mvn/version "1.0.194"}
       'org.clojure/clojure {:mvn/version "1.10.2-alpha1"}
       'org.clojure/core.async {:mvn/version "1.1.587"}
       'com.cognitect/transit-clj {:mvn/version "1.0.324"}
       'bencode {:mvn/version "0.2.5"}
       'org.clojure/data.csv {:mvn/version "1.0.0"}}
 :aliases {:native-image
          {:main-opts [(str "-m clj.native-image " name-space)
                       "--initialize-at-build-time "
                       ;; optional native image name override
                       (str "-H:Name=" native-image-name)
                       "-H:+ReportExceptionStackTraces"]
           :jvm-opts ["-Dclojure.compiler.direct-linking=true"]
           :extra-deps
           {'clj.native-image
            {:git/url ""
             :sha "7708e7fd4572459c81f6a6b8e44c96f41cdd92d4"}}}}})

Mno11:05:05

I thought I did with the deps file

borkdude11:05:57

I don't know how the clj native tool works, but when I compile it with leiningen, I produce an uberjar that has a main method. So you can execute it with java -jar foo.jar

borkdude12:05:01

And I feed that into GraalVM

borkdude12:05:50

I found it much simpler to get the tooling out of the way and just use GraalVM directly

borkdude12:05:54

So my advice would be to study how the tools you are using work individually

Mno12:05:22

That would probably the overall smarter move

Mno12:05:13

I'll yolo it for a bit more and then do exactly that.. probably starting with trying to compile with java first

Mno12:05:19

Thanks man!

Mno12:05:54

as usual it turns out I'm dumb, and I just forgot to add (:gen-class) in the ns expr

Mno12:05:11

now I have to figure out how to make it not run if it's being compiled

borkdude12:05:47

You add a check like (find-ns 'babashka.classpath) to determine if you're not running inside babashka

Mno12:05:31

oh I think I'll replace the existing one then!

Mno12:05:50

turns out the binary doesn't work so I have to fix that same assertion error one anyway

borkdude12:05:03

the existing one is there for a reason

borkdude12:05:40

to determine the difference between bb -m foo.bar or bb foo/bar.clj

Mno12:05:36

oh.. ok I'll leave both and modify the examples to include this check as well so it doesn't run for no reason

Mno13:05:08

Well I'm not exactly sure why, but tools.cli doesn't allow compiling (it fails an assertion) if you have a 2 short flags that are not distinct. (even if those short flags are an empty string)

Mno13:05:36

After figuring that out I finally made it so nativity can make a native binary out of itself! Probably pointless but I think it's cool.

aw_yeah 1
Darin Douglass17:05:50

as promised, here's a light psql library for those that don't want to maintain their own jdbc-enabled bb build: https://github.com/DarinDouglass/clj-psql

❤️ 7
Darin Douglass17:05:38

➜ bb --classpath ~/.m2/repository/douglass/clj-psql/0.1.2-SNAPSHOT/clj-psql-0.1.2-SNAPSHOT.jar
Babashka v0.0.88-2 REPL.
Use :repl/quit or :repl/exit to quit the REPL.
Clojure rocks, Bash reaches.

user=> (require '[psql.core :as psql])
nil
user=> (def conn {:host "localhost",
                  :port 5432,
                  :username "postgres",
                  :password "",
                  :name "postgres"})
#'user/conn
user=> (psql/query conn :grades {})
({:name "Suzy Butterbean", :subject "Math", :grade "100", :comment "N/A"} {:name "Bobby Tables", :subject "Math", :grade "100", :comment "N/A"})
user=>

borkdude18:05:18

@ddouglass Does it also take vectors like ["select * from address where id = ?" 2]?

Darin Douglass20:05:34

At the moment this would translate to (psql/query conn :address {:id 2})

borkdude20:05:23

right, that's already quite powerful!

Darin Douglass20:05:02

Replacing the simple form of “just give me your query” with the vector approach would be pretty simple if it feels more natural

borkdude21:05:46

I was thinking compatibility with next.jdbc would be nice

Darin Douglass21:05:38

Yeah. We can have all three supported. Ez Pz

Darin Douglass13:05:12

user> (psql/query conn ["select * from grades where name = ? and grade = ?" "Bobby Tables" 100])
({:name "Bobby Tables",
  :subject "Math",
  :grade "100",
  :comment "N/A"})
user> 

Darin Douglass20:05:00

It doesn’t. There’s a way to add “variables” like that through psql, but i wanted to start small

Darin Douglass20:05:20

Even so, you still need to format correctly to avoid SQL injection, I haven’t found a good way to guard against that without having access to jdbc, etc.