How to compile and import Java class with lein-figwheel? - clojure

I added :java-source-paths ["src/java"] to my project.clj, lein uberjar works but when running lein figwheel I get the following error:
Figwheel: Cutting some fruit, just a sec ...
Figwheel: Validating the configuration found in project.clj
Figwheel: Configuration Valid :)
java.lang.ClassNotFoundException: projectc.java.gcloud.DataStore, compiling:(projectc/gcloud/datastore.clj:1:1)
I added this to the ':uberjar` section
:prep-tasks ["javac" "compile" ["cljsbuild" "once" "min"]]
Any clues to how to solve this?

When Figwheel (and the ClojureScript compiler) compiles your code, it will process all clj files that it is given, to compile macros that are in clj files. It looks like Figwheel is trying to compile a file which is using a class which isn't on the Classpath? Is it possible to include that class, or exclude the Clojure file from the Figwheel compilation? Perhaps try building that Java project as a JAR and including it as a dependency as a test?
It's a little hard to tell with not very much information, this issue also looks like it might be relevant?

Related

Could not locate clojure/data/json: How do I get my REPL to see this (and similar) dependencies

I am using lein repl without a project so there is no project.clj.
I am running Leiningen 2.8.1 on Java 1.8.0_191 OpenJDK 64-Bit Server VM.
When I require a Clojure dependency that I assume should just work - like clojure.data.json - I notice that it is not in my .m2 directory. Is that why I am getting a FileNotFoundException Could not locate clojure/data/json__init.class or clojure/data/js
on.clj on classpath? I can't find my other Clojure dependencies there either so I don't know where they reside and if this dependancy should be in .m2 or not.
I understand the error message but without knowing its location or even knowing how to properly add it to the CLASSPATH for the REPL to see it, I remain stuck.
Is this a dependency that I still need to install? If so, how do I install it without going through a project?
I don't understand the JVM as I am new to it, so add a little extra information in your answer.
I have looked at this, this, this, this and this. I don't know if I am overlooking anything so your help will really be appreciated.
I am using lein run without a project so there is no project.clj.
If you're using Leiningen, this'll be much easier if you create a project.clj file that declares your dependencies. Leiningen will read project.clj and handle fetching any missing dependencies to your local Maven repository, and add them to your classpath when you start your REPL/application. (lein run doesn't work for me in a directory without a project.clj; I get an error: No :main namespace specified in project.clj.. Did you mean lein repl?)
When I require a Clojure dependency that I assume should just work - like clojure.data.json - I notice that it is not in my .m2 directory.
clojure.data.json doesn't ship with Clojure — it's a separate dependency that must be fetched and added to your classpath in order to use it. The classpath tells the JVM where to look when it loads class files. Leiningen will do both of these things for you if you declare the dependency in project.clj:
:dependencies [[org.clojure/clojure "1.10.0"]
[org.clojure/data.json "0.2.6"]]
You can also use the lein deps command if you only want to fetch dependencies.
You can create a new/blank Leiningen project with lein new project_name_goes_here. It will have a project.clj with a few boilerplate entries and a :dependencies key where you can declare dependencies.
I understand the error message but without knowing its location or even knowing how to properly add it to the CLASSPATH for the REPL to see it, I remain stuck. Is this a dependency that I still need to install? If so, how do I install it without going through a project?
You could manually download it from the internet, then manually add its path to your classpath, but if you're already using Leiningen it's much easier to add a line to a project.clj file and have Leiningen handle this for you.
If using a project.clj file w/Leiningen isn't an option, there are other ways to use Clojure and resolve dependencies/build a classpath at runtime. Boot accommodates this workflow, you can use Leiningen like this with a little added effort, as well as the newer tools.deps tooling. There are examples of each in this ClojureVerse thread, but note that some of these approaches are doing essentially the same thing as declaring the dependency in a file — instead declaring them as CLI arguments.
For example, using Clojure CLI tooling:
$ clj -Sdeps "{:deps {org.clojure/data.json {:mvn/version \"0.2.6\"}}}"
Clojure 1.9.0
user=> (require '[clojure.data.json :as json])
nil
user=> (json/write-str {:foo "bar"})
"{\"foo\":\"bar\"}"
user=> (System/getProperty "java.class.path")
"src:
/Users/me/.m2/repository/org/clojure/clojure/1.9.0/clojure-1.9.0.jar:
/Users/me/.m2/repository/org/clojure/data.json/0.2.6/data.json-0.2.6.jar:
/Users/me/.m2/repository/org/clojure/spec.alpha/0.1.143/spec.alpha-0.1.143.jar:
/Users/me/.m2/repository/org/clojure/core.specs.alpha/0.1.24/core.specs.alpha-0.1.24.jar"
You could create a deps.edn file containing {:deps {org.clojure/data.json {:mvn/version \"0.2.6\"}}} in the same directory, and clj would read that, resolve the dependencies if necessary, and build the classpath accordingly.
This is a great opportunity to use lein try. Once you add it to your ~/.lein/profiles.clj, you'd simply run: lein try org.clojure/data.json and you'll be greeted with a running REPL with that dependency just a require away.

Clojure not requiring a cljc file

I need to share a namespace between my Clojure (Garden) and my ClojureScript (Reagent).
Currently the project folder looks like this:
src/
clj/
name/
css.clj
cljs/
name/
core.cljs
cljc/
name/
config.cljc
The config.cljc file has the following namespace: (ns name.config).
I've tried to reference this namespace from inside clj/name/css.clj with a require.
(ns name.css
(:require [name.config :as config]))
However, this results in a compile error from Garden.
Caused by: java.io.FileNotFoundException: Could not locate name/config__init.class or name/config.clj on classpath.
I guess it's not even checking for cljc files.
I added "src/cljc" to the :source-paths vector in project.clj and :garden :builds but I get the same error even after restarting the build processes.
I see this behaviour on Clojure 1.7.0 and 1.8.0.
It might also be worth mentioning that it works without issues in ClojureScript (with Figwheel handling the build). I can require and use the new namespace without problems.
It seems like I must be missing something really simple, because none of the documentation around .cljc files even mentions requiring them.
Check if you’re using Clojure 1.7 or above in your project.clj. This error message:
Caused by: java.io.FileNotFoundException: Could not locate name/config__init.class or name/config.clj on classpath.
indicates that you’re using Clojure 1.6 or below, as those versions of Clojure only know to look for .class or .clj files.
I got this same error when I moved a file from .clj to .cljc in my project. I did lein clean but that had no effect. Eventually I renamed the module namespace and that fixed it.
(My guess is that there was some sort of cache of compiled modules and it was referencing a module which no longer existed, but the cljc wasn't re-compiled because a module of that name was still cached.)
When I renamed the module namespace it worked, with no other changes to the code.

Clojure.org documentation on compilation and gen-class

I am reading the documention on clojure.org about compilation, the last part gen-class examples. I do the examples and then when trying to run it as java app with: java -cp ./classes:clojure.jar clojure.examples.hello Fred in the terminal i get : Error: Could not find or load main class clojure.examples.hello. Can someone help?
Can someone introduce where to learn about gen-class and :gen-class, i find not much documentation on web
The command java -cp ./classes:clojure.jar tst.core from your base+system+user+dave is almost correct. The java.lang.NoClassDefFoundError: clojure/lang/IFn error is because the JVM cannot find the Clojure classes as there is no clojure.jar file in the base+system+user+dave directory, so you need to specify the correct path for the clojure.jar file.
As you are using lein, it downloads your project dependencies to your local repository. One of the dependencies will be Clojure itself, so assuming you are on iOS/Linux and that your lein project.clj has a dependency with clojure 1.7.0, the command to run from the base+system+user+dave directory will be:
java -cp ./classes:~/.m2/repository/org/clojure/clojure/1.7.0/clojure-1.7.0.jar tst.core
As this gets quite annoying once you have more than one dependency, I would suggest to use lein uberjar that will create a file in the target directory called your-project-name-standalone.jar that will have all required classes, so to run it from the command line go to the target directory and run something like :
java -cp tst-standalone.jar tst.core
To understand more about how the classpath works in the JVM, you can start with Wikipedia

Leiningen compile does nothing

(leiningen v 2.5.1)
Leiningen compile seems to be doing absolutely nothing, even with a new project. My steps are
lein new default foo
cd foo
lein compile
ls | grep target # nada
lein jar # does create a target, but with an empty .jar. Still no .class files
EDIT
I also tried with the :all :aot flags, still no dice.
What next?
You need to add :aot :all into your project.clj (Note, you have it vice-versa in your edit). After that compile task will compile all your namespaces. You can also name only those namespaces you want to compile.
As a side note, also in resulting jar the Clojure namespaces does not need to be in compiled form. This is the role of :aot key to control which namespaces will get compiled.
Have a look in your project.clj file. You will see there is very little there. The lein command your using to setup the project is a bare minimum definition. It has very little in it. You might do better with the 'app' template to start with. You also need to look at the lein documentation to see what goes into the project.clj file. Google for some lein templates to see what other templates are available. I would then select one and an initial simple project to play with.
Note that you don't need to run lein compile to just get started with clojure and writing some codes/expressions to play with - run lein repl instead.

Using lwjgl in Leiningen/Clojure

Solution
(1) (println (. System getProperty "java.library.path"))
This gives me a list of places java looks for native extensions.
Then, I took the lwjgl native extensions, and put them there.
Things that didn't work for me (probably because I used them incorrectly)
(*) setting :native-path
(*) setting :native-dependencies
Problem
My setup:
(lein deps; echo "====="; cat project.clj; echo "====="; cat src/main.clj; echo "====="; lein repl) &> log
contents of "log"
Copying 10 files to /Volumes/ramdisk/fail/lib
=====
(defproject
mincase "0.0.1"
:dependencies [[org.clojure/clojure "1.4.0"]
[org.lwjgl.lwjgl/lwjgl "2.8.2"] ]
:repositories {"local" "/Users/x/z/maven_repo"}
:jvm-opts ["-Xms4g" "-Xmx4g"]
:repl-init main
)
=====
(ns main
(:import org.lwjgl.opengl.Display))
=====
REPL started; server listening on localhost port 31235
UnsatisfiedLinkError no lwjgl in java.library.path java.lang.ClassLoader.loadLibrary (ClassLoader.java:1860)
clojure.core=>
Note -- I had already done a "lein deps", so the lwjgl library has been pulled into maven. What I don't understand are:
(*) how do I get access to lwjgl from Clojure?
(*) more importantly, how do I debug which step this whole thing has gone wrong at?
$ find lib
lib
lib/clojure-1.4.0.jar
lib/jinput-2.0.5.jar
lib/jinput-platform-2.0.5-natives-linux.jar
lib/jinput-platform-2.0.5-natives-osx.jar
lib/jinput-platform-2.0.5-natives-windows.jar
lib/jutils-1.0.0.jar
lib/lwjgl-2.8.2.jar
lib/lwjgl-platform-2.8.2-natives-linux.jar
lib/lwjgl-platform-2.8.2-natives-osx.jar
lib/lwjgl-platform-2.8.2-natives-windows.jar
So it appears that lwjgl has been pulled in.
What are the steps I should try to figure out which step I went wrong on?
Thanks!
Dropping this note here since google found this post for my similar question.
The Leiningen folks have now addressed this: https://github.com/technomancy/leiningen/issues/898
If you get version 2.1.0 or later, it has the fix. See the bug for details.
UPDATE: (Aug 2013)
I have a project on github I use for experimentation with lwjgl here: https://github.com/rogerallen/hello_lwjgl
I'm also using LWJGL in my shadertone project here: https://github.com/overtone/shadertone
Because Shadertone is a library, I found I needed to package up the natives myself to get it to install reasonably for projects that depend on shadertone.
If anyone has some pull with the LWJGL folks, it sure would be nice if they just put natives into appropriate subdirectories as lein expects in their clojars releases.
Looks like a problem with your LD_LIBRARY_PATH. Are you including the correct .dll or .so files?
You probably need to add something like :native-dependencies [[lwjgl "2.8.2"]] to your project.clj.
Alternatively, you could try setting the right value from your shell:
export LD_LIBRARY_PATH=/home/username/lwjgl-2.8.2/native/linux/
It's a bit confusing why Display is refusing to import, though other classes in the same jar file import properly
(import '[org.lwjgl.opengl Util WindowsAWTGLCanvasPeerInfo])
I suspect that this jar file is broken, perhaps you could try a different version.
I tried debuggin this by running
cd lib
jar xf lwjgl-2.8.2.jar
cd org/lwjgl/opengl/
and then trying to load various classes i see there.
lein swank also does tab completion which can help in exploring classes without extracting them from the shell.
Ran into this today, solved it a bit differently by adding the native directory to :jvm-opts in project.clj:
(defproject projectname "0.0.1-SNAPSHOT"
:description "my project"
:jvm-opts ["-Djava.library.path=native/linux"]
:dependencies [[org.clojure/clojure "1.4.0"]])
I copied the jar files from the latest lwjgl release into lib and copied the native directory into the project root. Seems to work so far:
user=> (import org.lwjgl.opengl.Display)
org.lwjgl.opengl.Display
But I am only just getting started. Anyway, hope this helps someone else :)