tools-build

AustinP 2021-09-27T22:26:31.358700Z

Hello! I'm working in a monorepo and trying to uberjar our projects using tools.build . I've gotten the uberjar compiling and built, but running it with java -jar , I keep hitting a java.lang.ClassNotFoundException. Could not find or load main class monorepo.base.core . I'm defining my main namespace in build.clj and passing it to :main in the uber fn, the pom.xml thats generated seems to be correct, and all the classes and files are present and correctly structured in target/classes as needed for it to compile. Unpacking the jar after its built I've confirmed the -main namespace is present, but still it cant be found despite my best efforts. Is there something obvious I'm missing here? My only guess is creating a source pom.xml that defines a main, to pass into write-pom , as I dont see Main-Class in the generated pom. But the attribute is obviously being set somewhere with the uber fn when it builds. Here is my build.clj, pretty much straight from the tools.build guide.

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

(def lib 'monorepo/project)
(def main 'monorepo.base.namespace_with_mainfn)
(def version (format "1.2.%s" (b/git-count-revs nil)))
(def class-dir "target/classes")
(def basis (b/create-basis {:project "deps.edn"}))
(def uber-file (format "target/%s-%s-standalone.jar" (name lib) version))


(defn clean [_]
      (b/delete {:path "target"}))

(defn prep [_]
      (b/write-pom {:class-dir class-dir
                    :lib lib
                    :version version
                    :basis basis
                    :src-dirs ["src"]})
      (b/copy-dir {:src-dirs ["../../bases/base" "."]
                   :target-dir class-dir}))

(defn uber [_]
      (b/compile-clj {:basis basis
                      :src-dirs ["src" "resources"]
                      :class-dir class-dir})
      (b/uber {:class-dir class-dir
               :main main
               :uber-file uber-file
               :basis basis}))

Alex Miller (Clojure team) 2021-09-27T22:30:38.359300Z

the class not found is monorepo.base.core - your main namespace is monorepo.base.namespace_with_mainfn so those do not seem directly related

Alex Miller (Clojure team) 2021-09-27T22:31:00.359700Z

just going from the error, do you see monorepo.base.core in your uberjar?

Alex Miller (Clojure team) 2021-09-27T22:32:02.360100Z

re "I dont see `Main-Class` in the generated pom" - do you mean in the META-INF/MANIFEST.MF ?

Alex Miller (Clojure team) 2021-09-27T22:32:58.360800Z

the pom is not used by Java to do anything, so is irrelevant here

AustinP 2021-09-27T22:39:41.362400Z

Apologies I mistyped, the error message is indeed with the defined monorepo.base.namespace_with_mainfn . I do see monorepo.base.namespace_with_mainfn in my uberjar after its built

Alex Miller (Clojure team) 2021-09-27T22:40:17.363Z

a .class?

Alex Miller (Clojure team) 2021-09-27T22:41:28.364100Z

that is, the uberjar should have monorepo/base/namespace_with_mainfn.class in it

AustinP 2021-09-27T22:41:29.364200Z

yes, its inside the jar as a .class file

Alex Miller (Clojure team) 2021-09-27T22:42:13.365500Z

and inside META-INF/MANIFEST.MF ?

AustinP 2021-09-27T22:42:41.366100Z

I dont have a MANIFEST.MF under my META-INF. contents is: META-INF/maven/monorepo/project/pom.xml and pom.properties

Alex Miller (Clojure team) 2021-09-27T22:43:28.366800Z

well something doesn't match up here. uber should always write a META-INF/MANIFEST.MF into the uber jar, and I don't know how it would be finding that class name otherwise

Alex Miller (Clojure team) 2021-09-27T22:43:55.367100Z

you won't see this in the target/classes dir, only in the uberjar

Alex Miller (Clojure team) 2021-09-27T22:44:43.367800Z

jar tf target/project-V-standalone.jar META-INF/MANIFEST.MF should verify it exists

Alex Miller (Clojure team) 2021-09-27T22:44:51.368Z

(whatever V you have)

AustinP 2021-09-27T22:48:33.368800Z

oh sorry, yes inside the uberjar I have MANIFEST.MF and Main-Class is defined there correctly as monorepo.base.namespace_with_mainfn

Alex Miller (Clojure team) 2021-09-27T22:49:01.369200Z

well, then I don't understand the error. what's the exact command you're running?

AustinP 2021-09-27T22:50:20.370400Z

just java -jar target/project-standalone.jar

AustinP 2021-09-27T22:50:49.371300Z

after running clj -T:build clean/prep/uber

Alex Miller (Clojure team) 2021-09-27T22:51:27.371600Z

can you dump the full output?

Alex Miller (Clojure team) 2021-09-27T22:51:34.371800Z

from the java command?

AustinP 2021-09-27T22:53:08.373300Z

Its just this short one

Error: Could not find or load main class monorepo.base.namespace_with_mainfn
Caused by: java.lang.ClassNotFoundException: monorepo.base.namespace_with_mainfn

Alex Miller (Clojure team) 2021-09-27T22:53:44.373900Z

and you see jar tf target/project-standalone.jar monorepo/base/namespace_with_mainfn.class ?

Alex Miller (Clojure team) 2021-09-27T23:06:50.374Z

is reminded of debugging things via faxed log printouts with someone in a scif

AustinP 2021-09-27T23:11:11.377100Z

waiting for it to uber again to see if this was the issue! It is there, but its under /classes/monorepo/base/namespace_with_mainfn.class , added classes in front of my main def in build.clj 🤞

AustinP 2021-09-27T23:12:16.378200Z

Thinking I incorrectly assumed :class-dir was ignoring/handling that. will update when it finishes

Alex Miller (Clojure team) 2021-09-27T23:13:46.378700Z

oh, well that's definitely not the right place, but that doesn't sound like the right fix

Alex Miller (Clojure team) 2021-09-27T23:15:29.379400Z

I don't understand how you're getting that from the script above

Alex Miller (Clojure team) 2021-09-27T23:16:20.379800Z

you should maybe clean target if you haven't been doing that, too

AustinP 2021-09-27T23:18:10.381200Z

ahhh, yes finally finished

Error: Could not find or load main class classes.monorepo.project.namespace_with_mainfn
Caused by: java.lang.NoClassDefFoundError: monorepo/project/namespace_with_mainfn (wrong name: classes/monorepo/project/namespace_with_mainfn)

AustinP 2021-09-27T23:18:33.381700Z

have been cleaning target each time. full clean/prep/uber

Alex Miller (Clojure team) 2021-09-27T23:19:38.382600Z

the wrong path is being included in the uber jar, but I don't understand how you're getting that with the script above.

2021-09-27T23:21:21.384700Z

(def main 'monorepo.base.namespace_with_mainfn) is just wrong

2021-09-27T23:21:43.385100Z

main is just the namespace with the main defn

2021-09-27T23:21:58.385600Z

no path stuff, no extra bits at the front

Alex Miller (Clojure team) 2021-09-27T23:22:46.386600Z

hard for me to tell whether that is correct or not without knowing what the namespaces are

AustinP 2021-09-27T23:23:02.387Z

that is just the namespace, as its shown at the top of the file with my -main function.

Alex Miller (Clojure team) 2021-09-27T23:23:15.387600Z

yeah, so I don't think that is the wrong part

Alex Miller (Clojure team) 2021-09-27T23:24:05.389100Z

the wrong part is that you are getting a classes/ prefix dir included but I can't tell whether this class is coming from some other part of the monorepo via a local dep or from this part as you have not provided enough information

Alex Miller (Clojure team) 2021-09-27T23:24:59.389500Z

What's in ../../bases/base? What's in .? What's in target/classes after the copy? after the compile?

Alex Miller (Clojure team) 2021-09-27T23:25:16.389800Z

the write-pom is not needed and can be removed

Alex Miller (Clojure team) 2021-09-27T23:26:54.391300Z

what's in your deps.edn? are you using local/root deps?

AustinP 2021-09-27T23:40:01.399500Z

This is a polylith monorepo, so the top level folder structure is

▾ workspace
  ▸ bases
  ▸ components
  ▸ development
  ▸ projects
Im in projects/thisProject using the deps.edn there that defines thisProject's base, components, etc. So in the copy-dir I go up 2 directories to the workspace, and back into bases/thisProjectsBase to get all the actual src code. "." is thisProject's folder that has the deps.edn and some other config files needed for compile. After the copy-dir , target/classes contains the folders from /bases/thisProjectsBase which are /src /cljs /resources , along with /node_modules /classes deps.edn shadow-cljs.edn etc that are in thisProject's folder

AustinP 2021-09-27T23:44:53.402300Z

after compile it looks the same. and the uberjar is built as expected under thisProject/target

2021-09-27T23:50:29.403500Z

why does classes contain a classes dir?

2021-09-27T23:50:45.403800Z

ah, word wrapped, node_modules/classes

2021-09-27T23:53:02.406400Z

that does seem suspicious, it doesn't seem like node_modules should have a classes subdir, but I don't know enough about node and can't see how that would end up effecting the uberjar

2021-09-27T23:54:56.407800Z

oh

AustinP 2021-09-27T23:55:04.408200Z

no they actually are two separate ones. target/classes is where everything in the copy-dir is going, and shouldnt affect much. thisProject has a /classes folder that contains all the dependencies from deps.edn, so when I copy-dir "." , which is my thisProject dir, it copies over /classes as well into target/classes

2021-09-27T23:57:32.409900Z

what are the paths in your deps.edn? I bet your basis, is pointing to stuff up the directory tree, in conflict with the result of the copying everything into .

2021-09-27T23:58:48.411200Z

your deps.edn presumably has like a local dep on ../bases/subproject

2021-09-27T23:59:26.411600Z

(in which case I am not sure what the copy is for)