tools-build 2023-06-13

Hello all, I'm trying to use into deps.edn in :deps with a local lib like my-lib\lib {:local/root "../../libs/my-lib"} . I have an alias build to make uberjar:

:build {:extra-deps {io.github.clojure/tools.build {:git/tag "v0.9.4" :git/sha "76b78fe"}
                     org.clojure/tools.deps.alpha  {:mvn/version "0.15.1254"}}
        :ns-default build}
but when I look at target/classes I didn't see any references of my-lib . What am I missing in this process? I could run it from repl but I could not generate a .jar that include it. Thank you

Alex Miller (Clojure team) 2023-06-13T13:07:11.778429Z

What’s in your build.clj?

Thank you for your time. This is my build.clj

(ns build
  (:require [clojure.tools.build.api :as b]))

(def lib 'myapp/myapp-api)
(def main 'myapp.main)
(def version-format "1.0.%s")
(def basis (b/create-basis {:project "deps.edn"}))
(def class-dir "target/classes")

(defn uberjar-name [mode lib]
  (let [lib-name (name lib)
        lib-name-with-mode (format "%s-%s" lib-name (name mode))]
    (str "target/" lib-name-with-mode "." (format version-format (b/git-count-revs nil)) ".jar")))

(defn clean [system]
  (println "\nCleaning target...")
  (b/delete {:path "target"})
  system)

(defn resources-env [mode]
  (let [type (name mode)]
    ["src" "resources" "target/classes" (str "config/" type) (str "env/clj/modeling-api/" type)]))

(defn sources-env [mode]
  (let [type (name mode)]
    ["src" (str "env/clj/modeling-api/" type)]))

(defn- uber-opts [{:keys [mode] :as opts}]
  (assoc opts
         :main main
         :basis basis
         :class-dir class-dir
         :src-dirs (sources-env mode)
         :ns-compile [main]))

(defn uber [{:keys [mode] :as system}]
  (let [uber-file (uberjar-name mode lib)
        opts (merge (uber-opts system) {:uber-file uber-file})]
    (clean system)
    (b/copy-dir {:src-dirs (resources-env mode)
                 :target-dir class-dir})
    (println "Compiling...")
    (b/compile-clj opts)
    (println "Building uberjar" (str uber-file "..."))
    (b/uber opts))
  system)

what is the dependency from deps.edn :local/root to build.clj? What should I consider into compile-clj ? Should I copy the sources from libs too?

Alex Miller (Clojure team) 2023-06-13T13:23:31.084609Z

to the last question, generally, no

Alex Miller (Clojure team) 2023-06-13T13:26:34.227409Z

the dependency comes in two ways - 1) the build.clj creates a basis using your project deps.edn - that's what specifies the dependencies of your uber jar

Alex Miller (Clojure team) 2023-06-13T13:27:09.804179Z

2) also, when you compile, it will be using the classpath of your dependencies per the basis to compile your code to classes

Alex Miller (Clojure team) 2023-06-13T13:28:04.793559Z

this is assuming that compiling your main loads and uses the code in my-lib/lib

Alex Miller (Clojure team) 2023-06-13T13:28:28.860959Z

if so, that load should compile and emit classes from my-lib

Alex Miller (Clojure team) 2023-06-13T13:28:55.871939Z

so I guess that's my first question - does main load and/or use code from my-lib?

Yes, because it execute some parts from my-lib before execute the main load

if I don't include the my-lib, it will not run

I tried to use :deps/prep but no lucky

Alex Miller (Clojure team) 2023-06-13T13:34:08.669829Z

that's a different purpose altogether (really for others using your project as a dep)

Alex Miller (Clojure team) 2023-06-13T13:34:28.059439Z

do you see any classes in target/classes?

Yes, but not that came from my-lib

maybe have I to include some dependency into code to tell the compiler to include the lib?

Alex Miller (Clojure team) 2023-06-13T13:45:13.745139Z

presumably your deps.edn has it in :deps ? if so, that's being included in the basis created in build.clj and used by compile-clj

Yes, it is in :deps

Alex Miller (Clojure team) 2023-06-13T13:46:31.548389Z

compilation is transitive based on loading the namespace you specify (which you have as main ). compilation is a side effect of loading, so whatever main loads (in it's ns form) will also be loaded and thus compiled. does main's ns load something from my-lib?

It loads dynamically from aero and integrant, not direct.

maybe should I force ns require the lib?

Alex Miller (Clojure team) 2023-06-13T13:53:07.240919Z

ah, you don't need to change your code, but you do need to tell the build what needs to be compiled if you are dynamically loading

Alex Miller (Clojure team) 2023-06-13T13:53:37.383459Z

so add any dynamically loaded namespaces to :ns-compile

oh, great Alex, I got it. I'll include it over there and give a try. I'll update the results. Thank you !

Hello Alex, it worked, thank you very much, I'd appreciate your help.

😎 1

An alternative approach is to expand :src-dirs in copy-dir to also include any directories from the :classpath-roots of the basis -- essentially a "transitive" source inclusion -- which brings in every potential source file, so you don't have to catch every single dynamically loaded namespace in :ns-compile. I've used that approach with monorepos (and not necessarily doing AOT compilation).

Alex Miller (Clojure team) 2023-06-13T18:21:59.755149Z

I would not recommend that as a matter of course

I know, and it was never a default. But if you are not AOT'ing your uberjar for whatever reason, you do need it πŸ™‚

so, I was thinking why I had to include into :ns-compile 2 libs that it's not related to my-lib to work? :ns-compile [main 'malli.core 'malli.registry]

that's a .jar dependency?

Alex Miller (Clojure team) 2023-06-13T20:04:14.365399Z

doesn't matter if it's a jar or files. anything you want to compile has to be loaded.

Alex Miller (Clojure team) 2023-06-13T20:05:33.173869Z

most loading happens via namespace declarations and transitive loading is included. anything that's dynamically loaded will not be included so needs to be explicitly compiled.

πŸ‘ 1