Fork me on GitHub
#clojure
<
2022-07-28
>
Martynas Maciulevičius18:07:01

Hey. Is there a way to lazily load a namespace from a function? Is it require?

p-himik18:07:10

That would do it, yes.

p-himik18:07:29

There's also requiring-resolve, sometimes useful.

borkdude18:07:53

@U028ART884X If you're still doing this with graalvm native-image: this won't work. If not, then it's fine :)

Martynas Maciulevičius18:07:47

I did this: https://github.com/ring-clojure/ring/pull/469/files This prevents the import for my non-SSL case.

p-himik18:07:12

That's exactly where I'd use requiring-resolve. Then that line for clj-kondo wouldn't be needed at all, as well as two statements would become one.

1
1
borkdude18:07:15

@U028ART884X If this still has to do with your questions in #graalvm, this is not a plausible approach

borkdude18:07:35

lazy loading stuff will not work in graalvm and will bloat the native image as well. The solution here is usually to use a delay:

(def logger (delay ..))
and then use @logger instead of logger

Martynas Maciulevičius18:07:11

This is the same issue as in graalvm. The thing is that version 1.8.0 of that library works just fine but version 1.8.1 fails because something loads that logger when I import that exact namespace. So it's the import that fails, not my code. Or maybe that means that I'm delusional and I don't understand completely anything 😄 But well I got a response: https://github.com/ring-clojure/ring/issues/468

borkdude18:07:34

import (Java) or require (Clojure)?

borkdude18:07:24

dynamic requires mess up with graalvm compilation: don't do it ;)

Martynas Maciulevičius18:07:55

This removed class is imported: https://github.com/ring-clojure/ring/pull/469/files#diff-0f29f4241b1d903dd3fdd238246ce43555ef48543ea51188e8612e1b4f0d36bfL18 I remove it by moving it into a different namespace and lazy-loading it only when it's needed. And graalvm compilation would still fail on that method that requires that class. So I didn't fix that failure. I could add a flag though :thinking_face: In my case I'm not going to use that class at all. So I'll do the minimum possible fix and ask the maintainer if he wants that I'd also add a flag. But the flag would mean that part of the functionality wouldn't work for SSL that is served directly by the server. So by adding a flag I'd be removing functionality :thinking_face:

borkdude18:07:18

If you're going to dynamically require something, do it on the top level, but not in the middle of a function. Like this:

(def foo (when (System/getProperty "foo.bar")
           (requiring-resolve 'dude/foo))
This is fine for graalvm. But not:
(defn foo []
  (when ...))

Martynas Maciulevičius18:07:13

But I only know if I want to require when I call the method and there is no env var :thinking_face: Should I introduce an env var? That library has two methods: ssl-connector and http-connector of which both are private :thinking_face: And I don't know if I want to control that behavior via an env var. If only graalvm would set GRAALVM=true... then I could do it.

borkdude18:07:13

What I would personally do is find out why this class is initialized during build time: i.e. find out the root cause. A repro with just one clj file which narrows down the problem

borkdude18:07:50

GraalVM does have such a thing, it's `the

"com.oracle.graalvm.isaot"
system property

borkdude18:07:25

This is set during compilation

borkdude18:07:58

See if you can repro the problem in a clojure project of 1 file where you just import the class, but don't use it

Martynas Maciulevičius19:07:10

Ok. That's very weird. I really thought it's this class... I added all of these dependencies and started a ring handler in the same way as in my app:

[[org.clojure/clojure "1.11.1"]
   [ring/ring-core "1.8.1"]
   [ring/ring-jetty-adapter "1.9.1"]
   [com.fzakaria/slf4j-timbre "0.3.21"]
   [org.clojure/tools.logging "1.2.4"]]
I don't know what I'm doing apparently. And I bothered a library author. But what's strange is that that library fails on something that was introduced in that minor version on my other project. So probably I missed some kind of dependency that does something else. My main:
(ns simple.main
  (:require [ring.adapter.jetty :as jetty])
  (:import org.eclipse.jetty.util.ssl.KeyStoreScanner)
  (:gen-class))

(defn handler
  [request]
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body "Hello GraalVM"})

(defn -main []
  (println org.eclipse.jetty.util.ssl.KeyStoreScanner)
  (println "server started on: ")
  (jetty/run-jetty handler {:port 3000}))

borkdude19:07:23

And this works right?

borkdude19:07:42

Here you are using the class btw

Martynas Maciulevičius19:07:03

It works. That's why I don't know what's wrong. I have my big project and I should copy more libs until it stops working :thinking_face:

borkdude19:07:44

This is the thing with graalvm: it's better to make isolated repros and then slowly build things up

Martynas Maciulevičius19:07:53

I was commenting my namespaces in my large project and when I comment-out the jetty import then it magically compiles. There are probably some kind of bindings that happen in some kind of specific way. Some kind of static Java goodness.

Martynas Maciulevičius19:07:13

I think thought I found it. [com.google.firebase/firebase-admin "8.1.0"] If you add it as your only dependency then you'll fail your graalvm build. And they managed to release version 9.0.0 recently which doesn't have this issue on my minimal repro. So the good news is that it doesn't fail on the minimal repro on version 9.0.0. The bad news is that on my fat project it still fails no matter which version it is 🥳 So something... somehow... interacts... So much magic it's not even funny 😄 I guess I'll have to keep digging. I already dug out several of these bugs.

borkdude19:07:31

welcome to graalvm native-image!

Martynas Maciulevičius19:07:53

Maintaining babashka is no joke :thinking_face:

borkdude20:07:16

Just be very careful with adding dependencies and test image size and compilation time on every new commit that adds new dependencies/functions

borkdude20:07:33

This is what I monitor in #babashka-circleci-builds for example

Cora (she/her)20:07:38

is there a way to remove all taps?

Cora (she/her)20:07:11

I added a fn as a tap but didn't stash a reference to it so I could remove it later

Cora (she/her)20:07:41

looks like @#'clojure.core/tapset will let you access the private value

👍 1
Cora (she/her)20:07:13

and then you can call reset! on it with #{} to clear them all