leiningen run a AOT compiled java class - clojure

Context
If I want Lein 1.7 to start out with a particular class loaded, I do:
:repl-init init.init
Now, instead of having lein load up a *.clj file, I want lein to do the equiv of "java Foo", where Foo is classes/Foo.class
Question:
My project.clj look alike:
(defproject ...
:aot [Foo]
???? )
What do I put in ???? to make "lein repl" startup by executing "java Foo" ?
Thanks!

If I understand your question, you can still use :repl-init for this by having a call to the class you want pre-loaded in a namespace that gets loaded by the repl. in this example i'm using println statement as a standin for the setup you want done :-)
project.clj:
(defproject foooo "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.3.0"]]
:aot [foooo.core]
:main foooo.core
:repl-init foooo.core)
core.clj
(ns foooo.core
(:gen-class))
(println "setting up for fun")
(defn -main [])
compiling:
arthur#a:~/foooo$ lein compile
Compiling foooo.core
Compilation succeeded.
running:
arthur#a:~/foooo$ CLASSPATH=./lib/clojure-1.3.0.jar:./classes/ java foooo.core 1
setting up for fun
or:
arthur#a:~/foooo$ lein run
setting up for fun
REPLing:
arthur#a:~/foooo$ lein repl
REPL started; server listening on localhost port 63392
setting up for fun
foooo.core=>

Related

How do I compile and use a class defined with gen-class in the repl and/or tests?

Clojure noob. I'm unable to get a basic example with :gen-class working.
$ lein new app pizza-parlor
; project.clj
(defproject pizza-parlor "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.10.1"]]
:main ^:skip-aot pizza-parlor.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all
:jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})
; src/pizza_parlor/Deliverator.clj
(ns pizza-parlor.Deliverator
(:gen-class))
(defn -deliver [pizza]
(println "pipin' hot"))
$ lein repl
pizza-parlor.core=> (require 'pizza-parlor.Deliverator)
nil
pizza-parlor.core=> (def d (pizza-parlor.Deliverator.))
Syntax error (ClassNotFoundException) compiling new at (/tmp/form-init10318668819087633543.clj:1:1).
pizza-man.Deliverator
pizza-man.core=> (import pizza-parlor.Deliverator)
Execution error (ClassNotFoundException) at java.net.URLClassLoader/findClass (URLClassLoader.java:435).
pizza-parlor.Deliverator
I've tried the :aot option in project.clj and run lein compile to generate classes in target/default, but I get the same error.
What is the correct way to define a Java class via gen-class and then use it in the repl alongside the rest of my project code, or a test that I can run with lein test?
You have to pay attention to your names/folders with a dash - in it.
A clojure namespace of "some-clojure-namespace" will correspond to "some_clojure_namespace" in java world (and in folder structure)
So in your particular case :
; src/pizza-parlor/Deliverator.clj
(ns pizza-parlor.Deliverator
should be in "pizza_parlor" folder so :
; src/pizza_parlor/Deliverator.clj
(ns pizza-parlor.Deliverator
And then
(ns pizza-parlor.Deliverator)
would correspond to a (java) import :
(import pizza_parlor.Deliverator)
(note the underscore here)
But as stated by #Alan Thompson you do not need to create classes and import them in clojure. You can require the namespace and call it like he described. You want to generate Java Class and import them mostly if you have to interop with a java lib or so.
You can also use gen-class directly to generate any Java class that you would need like, see many example here
You are misunderstanding how to execute a function in the REPL.
You do not need to "create a class" in Clojure. Just start up a repl and invoke a function. There are several choices:
(ns demo.core
; (:gen-class) ; uncomment to make runnable JAR file
)
(defn -main []
(println "main - enter")
)
As the comment says, you only need the (:gen-class) expression in the ns form if you want to create an executable JAR file.
Method 1: Switch to the desired namespace and run the function:
~/expr/demo > lein repl
demo.core=> (in-ns 'demo.core) ; single-quote is required
demo.core=> (-main)
main - enter
Method 2: Just invoke the function with a fully-qualified symbol:
~/expr/demo > lein repl
demo.core=> (demo.core/-main) ; no quote!
main - enter
This will only work with the "main" ns, identified by the expression
:main demo.core
in project.clj.
Method 3: Require the namespace, then invoke the function with a fully-qualified symbol:
> lein repl
demo.core=> (require 'demo.core) ; single-quote is required
demo.core=> (demo.core/-main)
main - enter
This will work for any namespace, even demo.core if the REPL places you in the user namespace.
Method #4: Build an executable uberjar
You must ensure that (:gen-class) is present in the (ns ...) form AND that you have :main demo.core in your project.clj. Then
~/expr/demo > lein uberjar
Created /home/alan/expr/demo/target/uberjar/demo-art-0.1.0-SNAPSHOT.jar
Created /home/alan/expr/demo/target/uberjar/demo-art-0.1.0-SNAPSHOT-standalone.jar
~/expr/demo > java -jar /home/alan/expr/demo/target/uberjar/demo-art-0.1.0-SNAPSHOT-standalone.jar
main - enter
If you see an error message like this:
~/expr/demo > java -jar /home/alan/expr/demo/target/uberjar/demo-art-0.1.0-SNAPSHOT-standalone.jar
Error: Could not find or load main class demo.core
Caused by: java.lang.ClassNotFoundException: demo.core
then you forgot to include the (:gen-class) in the ns form.
Look here for more details on gen-class.
Update
If you really want to create a Java class from within Clojure code, you need the function gen-class , not the ns expression. See:
examples
reference guide
API docs
Update #2
Do you really need to generate a Java class from Clojure code? It might be easier to just write a Java class in a *.java source code file. Leiningen is perfectly able to compile mixed Clojure & Java source code into a single executable. This might be the easiest way to go.

How to Run Jetty Example with Ring in Clojure

I am following along with this example on creating a simple web service in Clojure using ring and jetty.
I have this in my project.clj:
(defproject ws-example "0.0.1"
:description "REST datastore interface."
:dependencies
[[org.clojure/clojure "1.5.1"]
[ring/ring-jetty-adapter "0.2.5"]
[ring-json-params "0.1.0"]
[compojure "0.4.0"]
[clj-json "0.5.3"]]
:dev-dependencies
[[lein-run "1.0.0-SNAPSHOT"]])
This in script/run.clj
(use 'ring.adapter.jetty)
(require '[ws-example.web :as web])
(run-jetty #'web/app {:port 8080})
And this in src/ws_example/web.clj
(ns ws-example.web
(:use compojure.core)
(:use ring.middleware.json-params)
(:require [clj-json.core :as json]))
(defn json-response [data & [status]]
{:status (or status 200)
:headers {"Content-Type" "application/json"}
:body (json/generate-string data)})
(defroutes handler
(GET "/" []
(json-response {"hello" "world"}))
(PUT "/" [name]
(json-response {"hello" name})))
(def app
(-> handler
wrap-json-params))
However, when I execute:
lein run script/run.clj
I get this error:
No :main namespace specified in project.clj.
Why am I getting this and how do I fix it?
You're getting this error because the purpose of lein run (according to lein help run) is to "Run the project's -main function." You don't have a -main function in your ws-example.web namespace, nor do you have a :main specified in your project.clj file, which is what lein run is complaining about.
To fix this, you have a few options. You could move the run-jetty code to a new -main function of the ws-example.web function and then say lein run -m ws-example.web. Or you could do that and also add a line :main ws-example.web to project.clj and then just say lein run. Or you could try using the lein exec plugin to execute a file, rather than a namespace.
For more info, check out the Leiningen Tutorial.
You have to put that (run-jetty) stuff into a -main somewhere and then add it to the project.clj like
:main ws-example.core)
From lein help run:
USAGE: lein run -m NAMESPACE[/MAIN_FUNCTION] [ARGS...]
Calls the main function in the specified namespace.
So, you would need to put your script.clj somewhere on the project source path and then call it as:
lein run -m script

How to define project.clj for both lein run and lein repl to work?

I'm new to Clojure, and I don't quite understand how to write my project.clj so it works for both lein repl and lein run. Here it is (whole path: ~/my-project/project.clj):
(defproject my-project "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.3.0"]]
:main my-project.core/hello
)
Then I have my ~/my-project/src/my_project/core.clj file
(ns my-project.core)
(defn hello []
(println "Hello world!")
)
lein run works just fine but I get a FileNotFoundException when running lein repl:
~/my-project$ lein run
Hello world!
~/my-project$ lein repl
REPL started; server listening on localhost port 42144
FileNotFoundException Could not locate hello__init.class or hello.clj on classpath: clojure.lang.RT.load (RT.java:430)
clojure.core=>
How should I edit the project.clj to solve this? Or do I have to call lein repl in a different way?
Thanks in advance.
EDIT: tried with lein deps and lein compile, but still the same error
~/my-project$ lein version
Leiningen 1.7.1 on Java 1.6.0_27 OpenJDK Client VM
~/my-project$ lein deps
Copying 1 file to /home/yasin/Programming/Clojure/my-project/lib
~/my-project$ lein compile
No namespaces to :aot compile listed in project.clj.
~/my-project$ lein repl
REPL started; server listening on localhost port 41945
FileNotFoundException Could not locate hello__init.class or hello.clj on classpath: clojure.lang.RT.load (RT.java:430)
One thing you could do to get it to work would be to change core.clj to:
(ns my-project.core
(:gen-class))
(defn hello []
(println "Hello world!"))
(defn -main []
(hello))
And edit the project.clj to:
(defproject my-project "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.3.0"]]
:main my-project.core)
The (:gen-class) will tell the compiler to generate a Java class for the namespace, and the :main directive in project.clj will tell lein run to run the main method on the class, which is given by -main. Why lein repl was failing to find my-project.core/hello is unclear to me, but I don't know much about leiningen internals.

Lein uberjar fails on simple '-main' usage, but perhaps I don't understand all it's doing?

I've created a simple example using:
%> lein new lein-check
I've only modified the code so that it now has a 'main':
(defproject lein-check "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:main "lein-check.core"
:dependencies [[org.clojure/clojure "1.4.0"]])
(ns lein-check.core
(:gen-class))
(defn -main
"I don't do a whole lot."
[& args]
(println "Hello, World!"))
But the first time I run lein uberjar even after a clean I get this:
Compiling lein-check.core
Exception in thread "main" java.lang.ClassCastException:
java.lang.String cannot be cast to clojure.lang.Symbol
at clojure.core$find_ns.invoke(core.clj:3659)
at clojure.core$load_one.invoke(core.clj:5228)
at clojure.core$compile$fn__4895.invoke(core.clj:5426)
at clojure.core$compile.invoke(core.clj:5425)
at user$eval7.invoke(NO_SOURCE_FILE:1)
at clojure.lang.Compiler.eval(Compiler.java:6511)
at clojure.lang.Compiler.eval(Compiler.java:6501)
at clojure.lang.Compiler.eval(Compiler.java:6477)
at clojure.core$eval.invoke(core.clj:2797)
at clojure.main$eval_opt.invoke(main.clj:297)
at clojure.main$initialize.invoke(main.clj:316)
at clojure.main$null_opt.invoke(main.clj:349)
at clojure.main$main.doInvoke(main.clj:427)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:419)
at clojure.lang.AFn.applyToHelper(AFn.java:163)
at clojure.lang.Var.applyTo(Var.java:532)
at clojure.main.main(main.java:37)
Compilation failed: Subprocess failed
Running a follow up uberjar though successfully creates the jars:
Created ....\lein-check-0.1.0-SNAPSHOT.jar
Including lein-check-0.1.0-SNAPSHOT.jar
Including clojure-1.4.0.jar
Created ....\lein-check-0.1.0-SNAPSHOT-standalone.jar
Is uberjar sometimes running the main after a completion? Is it supposed to do that? I'm not quite sure if this is a user error, a lein bug or a clojure bug.
The problem is in the line
:main "lein-check.core"
It should be
:main lein-check.core
You're using a string instead of a symbol.
Well it turns out that the Lein Sample Project show the :main as a symbol and not a string. Once this was updated then running lein uberjar works as expected.
This line in particular:
:main "lein-check.core"
changed to:
:main 'lein-check.core

command line arguments with leiningen

I have recently started out with clojure, and I am using leiningen to create a small project. I am having troubles in getting leiningen to behave with command line arguments. The following is my src/project/core.clj
(ns project.core
(:gen-class))
(defn -main [& args]
(println (apply str args)))
and my project.clj
(defproject project "1.0.0-SNAPSHOT"
:description "FIXME: write"
:main project.core
:dependencies [[org.clojure/clojure "1.2.0"]
[org.clojure/clojure-contrib "1.2.0"]])
Now when I run lein run arg1 arg2, it gives me this error:
Exception in thread "main" java.lang.Exception: Unable to resolve symbol: arg1 in this context (NO_SOURCE_FILE:1)
at clojure.lang.Compiler.analyze(Compiler.java:5205)
at clojure.lang.Compiler.analyze(Compiler.java:5151)
at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3057)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5371)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.analyze(Compiler.java:5151)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:4670)
at clojure.lang.Compiler$FnMethod.parse(Compiler.java:4328)
at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3173)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5367)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.eval(Compiler.java:5421)
at clojure.lang.Compiler.eval(Compiler.java:5415)
at clojure.lang.Compiler.eval(Compiler.java:5391)
at clojure.core$eval.invoke(core.clj:2382)
at clojure.main$eval_opt.invoke(main.clj:235)
at clojure.main$initialize.invoke(main.clj:254)
at clojure.main$null_opt.invoke(main.clj:279)
at clojure.main$main.doInvoke(main.clj:354)
at clojure.lang.RestFn.invoke(RestFn.java:422)
at clojure.lang.Var.invoke(Var.java:369)
at clojure.lang.AFn.applyToHelper(AFn.java:165)
at clojure.lang.Var.applyTo(Var.java:482)
at clojure.main.main(main.java:37)
Caused by: java.lang.Exception: Unable to resolve symbol: arg1 in this context
at clojure.lang.Compiler.resolveIn(Compiler.java:5677)
at clojure.lang.Compiler.resolve(Compiler.java:5621)
at clojure.lang.Compiler.analyzeSymbol(Compiler.java:5584)
at clojure.lang.Compiler.analyze(Compiler.java:5172)
... 23 more
However, if I do a lein uberjar and then do java -jar project-1.0.0-SNAPSHOT-standalone.jar arg1 arg2, I get the correct output.
arg1arg2
It isn't very comfortable to have to create the uberjar to run it every time while development, is there a better way?
This looks like it's caused by a bug that's been fixed in git. The fix will be in 1.4.2, which should be out in a few days. In the mean time, you can use workarounds discussed here: http://groups.google.com/group/clojure/msg/a8160b23a5019a12
From lein-run: "Args will be passed on as *command-line-args*"
So you will have to use those. The example on the site shows how. If you now what arguments you are passing you an also use :run-aliases to specify those in your project.clj. Again, the mentioned site has all the information.
My sample project.clj
(defproject addressbook "1.0.0-SNAPSHOT"
:description "FIXME: write"
:main addressbook.core
:run-aliases {:addressbook [addressbook.core -main "arg1"]}
:dependencies [[org.clojure/clojure "1.2.0"]
[org.clojure/clojure-contrib "1.2.0"]]
:dev-dependencies [[lein-run "1.0.0"]])
And the test code:
(ns addressbook.core
(:gen-class))
(defn -main [& [args]]
(if args (println args)))
Both "lein run addressbook foo" as "lein uberjar" work for me.