tools-build

Jungin Kwon 2022-02-23T07:58:12.190509Z

Hello ๐Ÿ™‚ Is there a way to build without the unused dependencies (or namespaces)?

Kirill Chernyshov 2022-02-23T08:24:20.098069Z

Unused dependencies is tricky to detect (not sure if this is even possible) because there are a lot of ways how dependencies can affect your program. Look at logging for example - you can add logging backend deps and even without explicit require anywhere it will be picked. For namespaces this should be done automatically as a side effect of compiling your code

๐Ÿ‘ 1
FiVo 2022-02-23T16:54:16.648439Z

Is there a way to get more verbose output from b/compile-clj. I am currently just getting. Clojure compilation failed .

FiVo 2022-02-23T17:01:18.336119Z

I am currently trying to build a couple of subprojects with a build.clj at the root. My assumption was that I could do something like

(binding [b/*project-root* subproject]
   (do stuff with tools build))
but that does not seem correct. The things under the binding are similar to what is decribed here https://clojure.org/guides/tools_build#_compiled_uberjar_application_build. So my main question is what is affected by *project-root* and what not?

FiVo 2022-02-24T08:43:55.657129Z

> what are you actually trying to do? I am trying to create an uberjar for every subproject.

FiVo 2022-02-24T08:45:33.605109Z

The project strucutre is as follows project/deps.edn

{:paths [...]
 :deps {...}}
project/subproject/deps.edn
{:paths [...]
 :deps {some-name {:local/root ".."}
        more deps ...}
and for every subproject I want to create an uberjar

seancorfield 2022-02-24T08:53:38.724339Z

This is exactly what we do at work @finn.volkel -- but bear in mind your project/deps.edn will be completely ignored when you do this -- only project/subproject/deps.edn will be taken into account.

seancorfield 2022-02-24T08:53:56.733939Z

(which is how Polylith is setup BTW)

seancorfield 2022-02-24T08:54:46.541559Z

You haven't actually stated what goes wrong / what errors you get.

FiVo 2022-02-24T08:56:39.951799Z

You mean even the deps from the root are ignored?

FiVo 2022-02-24T08:57:26.694699Z

I saw your monorepo series, but haven't read everything

seancorfield 2022-02-24T08:57:52.997649Z

There are three deps in play: root (system -- baked into t.d.a), user, and project. When you use the *project-root* binding, you are essentially changing what the project deps.edn is.

seancorfield 2022-02-24T08:58:13.691189Z

So if you use project/subproject/deps.edn then project/deps.edn is not in play at all.

FiVo 2022-02-24T09:00:17.705169Z

Yes but I am doing :local/root ".."

FiVo 2022-02-24T09:00:37.201999Z

so would that not mean it's taking back into account?

seancorfield 2022-02-24T09:01:11.019799Z

Oh, so you're trying to treat the top-level project as a dependency? Weird.

seancorfield 2022-02-24T09:01:36.203139Z

Have you tried cd subproject && clojure -Stree to see what you get?

seancorfield 2022-02-24T09:02:00.315129Z

Again, you haven't actually stated what goes wrong or what errors you are getting.

FiVo 2022-02-24T09:02:24.093769Z

currently the root only has dependencies, but might get some code later on

FiVo 2022-02-24T09:02:47.119549Z

these are shared across the subprojects

FiVo 2022-02-24T09:02:59.119629Z

> Again, you haven't actually stated what goes wrong or what errors you are getting. Will do in a minute.

FiVo 2022-02-24T09:23:52.297419Z

Here is the full build.clj

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

(def version "x.y.z")
(def class-dir "target/classes")
(def subprojects #{"foo" "bar"})

(defn- check-subproject [subproject]
  (when-not (subprojects subproject)
    (throw (IllegalArgumentException. (str "No such subproject " subproject)))))

(defn uber-file [subproject]
  (format "target/%s-%s-standalone.jar" subproject version))

(defn clean-subproject [subproject]
  (binding [b/*project-root* subproject]
    (b/delete {:path "target"})))

(defn clean [{:keys [subproject]}]
  (if subproject
    (clean-subproject (str subproject))
    (doseq [subproject subprojects]
      (clean-subproject subproject))))

(defn- uber-subproject [subproject]
  (check-subproject subproject)
  (clean-subproject subproject)
  (binding [b/*project-root* subproject]
    (let [basis (b/create-basis {:project "deps.edn"})]
      (b/copy-dir {:src-dirs ["src"]
                   :target-dir class-dir})
      (b/compile-clj {:basis basis
                      :src-dirs ["src"]
                      :class-dir class-dir})
      (b/uber {:class-dir class-dir
               :uber-file (uber-file subproject)
               :basis basis
               :main (symbol subproject)}))))

(defn uber [{:keys [subproject]}]
  (if subproject
    (uber-subproject (str subproject))
    (doseq [subproject subprojects]
      (uber-subproject subproject))))

FiVo 2022-02-24T09:29:25.829169Z

Currently only trying with one subproject. clj -T:build uber can not locate the main entrypoint file/class during the compile step.

FiVo 2022-02-24T09:34:11.219049Z

> Have you triedย `cd subproject && clojure -Stree`ย to see what you get? Yes also shows the dependency of the root deps.edn

FiVo 2022-02-24T10:14:36.127029Z

If I build an uberjar in the subproject with a build.clj in the subproject, everything works as expected.

FiVo 2022-02-24T10:36:55.898319Z

Here is a repo with the issue https://github.com/FiV0/tools.build-subproject-issue

FiVo 2022-02-24T10:37:40.680219Z

I essentially don't want a build.clj for every subproject.

FiVo 2022-02-24T10:53:26.688519Z

So also created a branch where I got it working https://github.com/FiV0/tools.build-subproject-issue/compare/working-version.

FiVo 2022-02-24T10:55:11.242989Z

Maybe I have some missing knowledge about tools.build, but I would have expected the version with *project-root* to also work.

Alex Miller (Clojure team) 2022-02-24T13:23:22.431449Z

Thanks for all the info, that helps a lot. So, when you make the basis in your subproject what's in it? Check the :classpath-roots of the basis for example

Alex Miller (Clojure team) 2022-02-24T13:24:46.581869Z

(What you have seems very reasonable, and should work, but maybe there's a bug)

seancorfield 2022-02-24T15:33:57.824499Z

@finn.volkel The problem is the relative path to the subproject -- I remember running into this and then forgetting about it. If you change the binding to this:

(binding [b/*project-root* (.getCanonicalPath (java.io.File. subproject))]
then it will all work as expected. @alexmiller Is that expected?

seancorfield 2022-02-24T15:37:17.054139Z

The relative path seems to work just fine for b/delete so it may only need the full path for the basis?

FiVo 2022-02-24T15:46:04.752149Z

ok thanks, makes sense now. I thought I could set *project-root* with respect to (System/getProperty "user.dir"), but if it needs to be an absolute path than it makes sense that my stuff fails.

seancorfield 2022-02-24T15:46:50.450959Z

We use user.dir -- it is an absolute path.

seancorfield 2022-02-24T15:47:25.930609Z

(str (System/getProperty "user.dir") "/" subproject) should work.

seancorfield 2022-02-24T15:48:29.150609Z

Yeah, that works.

FiVo 2022-02-24T15:49:37.096229Z

Yes, I just wanted to say that my assumption before was that I could do

(binding [b/*project-root* "foo"] ...)
and that would be the same as with (str (System/getProperty "user.dir") "/foo" )

Alex Miller (Clojure team) 2022-02-24T16:15:57.334079Z

so the problem is that setting a relative dir for project-root doesn't work?

seancorfield 2022-02-24T17:07:27.950739Z

@alexmiller Correct. Perhaps only for creating the basis though. delete seems to get the correct path.

Alex Miller (Clojure team) 2022-02-24T19:14:04.486879Z

well this is definitely a bug, or maybe several, still working on it

๐Ÿ‘ 1
๐Ÿ‘๐Ÿป 1
Alex Miller (Clojure team) 2022-02-24T21:16:07.494679Z

ok, give it a shot with v0.8.0 e3e3532

Alex Miller (Clojure team) 2022-02-24T21:18:16.253039Z

the bugs were totally not where I thought they were when I started, but all had to do with how compile-clj and java-command make the classpath for compilation. I believe that is now all sorted out and consistent. I want to come back and leave some better tests for this stuff still but for now, I believe this should work for you.

FiVo 2022-02-24T21:22:40.536009Z

Tested in the issue project above and it works. Will test the repo from work tomorrow.

Alex Miller (Clojure team) 2022-02-24T21:24:21.782969Z

thanks so much for the report and the sample project - that made it 100x easier to work on and I'm glad this got sorted out

seancorfield 2022-02-24T21:30:14.473809Z

I'll update our setup in an hour or so and verify it doesn't break things (and maybe I'll switch to relative paths ๐Ÿ™‚ )

FiVo 2022-02-24T21:33:45.499079Z

> thanks so much for the report and the sample project - that made it 100x easier to work on and I'm glad this got sorted out well, thanks for the quick fix

Alex Miller (Clojure team) 2022-02-24T21:45:12.886079Z

yeah, to be clear, the basis all works just as it should afaict (I spent way too long with a faulty test trying to figure that out)

seancorfield 2022-02-24T21:51:49.508089Z

Updated build-clj, updated our work repo, verified it works with the absolute path as before and also now with the relative path. Thanks, @alexmiller!

FiVo 2022-02-25T09:00:05.729659Z

Seems to also work for the project at work.

Alex Miller (Clojure team) 2022-02-25T13:05:32.225989Z

๐Ÿ‘

FiVo 2022-02-23T17:02:24.993089Z

So under the binding I was assuming I could just do everything with tools.build as if I was in the subproject.

seancorfield 2022-02-23T17:28:57.809999Z

@finn.volkel That definitely should work. That's how we deal with subprojects at work:

(binding [b/*project-root* project-dir]
          (bb/clean {})
          (b/copy-file {:src version-clj
                        :target "target/classes/ws/uberjar/release.clj"})
          (bb/uber (assoc (:uberjar aliases)
                          :compile-opts {:direct-linking true}))
          (bb/clean {}))
(`bb` is my build-clj wrapper around tools.build and b is tools.build itself)

seancorfield 2022-02-23T17:29:59.570769Z

You might also need tools.deps.alpha.util.dir/with-dir if you're also working with t.d.a directly, for example:

aliases     (with-dir (io/file project-dir) (get-project-aliases))

seancorfield 2022-02-23T17:30:22.304539Z

(that line is outside the binding BTW)

Alex Miller (Clojure team) 2022-02-23T17:33:15.019539Z

you should probably not be messing with t.d.a's stuff - tools.build is setting that

Alex Miller (Clojure team) 2022-02-23T17:33:46.358139Z

what are you actually trying to do?

seancorfield 2022-02-23T17:35:26.509359Z

(we do it specifically to get the list of aliases in the subproject's deps.edn file outside of the binding context -- I don't remember the reason we ended up doing it that way @alexmiller)

Alex Miller (Clojure team) 2022-02-23T17:37:38.644909Z

> So my main question is what is affected byย `*project-root*`ย and what not? most paths in the build will be interpreted wrt the project-root

seancorfield 2022-02-23T17:39:51.367359Z

FWIW, calling (get-project-aliases) via with-dir gives us the deps.edn data from the subproject, calling it inside the binding without with-dir gives us the top-level deps.edn data. Just tried that @alexmiller So we do need with-dir here.

seancorfield 2022-02-23T17:40:19.341369Z

That function is:

(defn- get-project-aliases []
  (let [edn-fn (juxt :root-edn :project-edn)]
    (-> (t/find-edn-maps)
        (edn-fn)
        (t/merge-edns)
        :aliases)))

seancorfield 2022-02-23T17:41:35.272019Z

aliases-via-tda (:deps :test :uberjar)
aliases-inside-binding (:poly :cfml-admin :uploader :coverage :cfml-affiliate :build :+default :runner :+httpkit :everything :test :dev :deps)

seancorfield 2022-02-23T17:41:46.522929Z

(didn't mean to derail the original thread)