A problem has traped me for quite a while when I'm exploring Clojure. I try to generate a class by invoking the compile function in REPL on a Clojure script as below:
(ns mylib.DirLister (:gen-class))
(defn -listDir [this path]
(->> path java.io.File. .listFiles (map #(.getName %))))
I saved this script to e:/temp/clj/src/mylib/DirLister.clj. The following session goes well when I specify the relative paths for the -cp option, i.e., classes are successfully generated in the classes path:
e:\temp\clj>java -cp .\src;.\classes;d:/tools/clojure-1.4.0/clojure-1.4.0.jar clojure.main
Clojure 1.4.0
user=> (compile 'mylib.DirLister)
mylib.DirLister
user=>
But when I use absolute paths, there prompt a "No such file or directory" error:
e:\>java -cp e:/temp/clj/src;e:/temp/clj/classes;d:/tools/clojure-1.4.0/clojure-1.4.0.jar clojure.main
Clojure 1.4.0
user=> (compile 'mylib.DirLister)
CompilerException java.io.IOException: No such file or directory, compiling:(mylib/DirLister.clj:1)
user=>
How come ? I mean why the absolute paths don't work but the relative paths do.
I know I could go with leiningen. but since I'm studying Clojure, I want to understand the stuff underneath before adopting this full-featured tool.
Looks to me like you have a mixture of forward-slash and backward-slashes on the command line. I'm not sure exactly what shell you are using and whether that could make a difference. But, a path with e: in it would normally require a back-slash '\'. Maybe this will help, or at least remove one potential source of problems?
Could you try to change the 2nd example to use
e:\temp\clj\src;e:\temp\clj\classes;d:\tools\clojure-1.4.0\clojure-1.4.0.jar
to see if that helps?
Finally I got the answer:
the compile function will always use the value of the built-in variable *compile-path* as the path of the output classes, which defaulted to "classes" under the current directory if you didn't set it otherwise. So the problem I came across is not really about relative or absolute path, it is about the current directory and the value of *compile-path* .
Refer to ClojureDoc .
Related
For the development of a library I started from a lein project, invoked like so:
lein new mylib
if I call lein install now, I can access my library in other projects. But trying to immidiately test the functions I wrote failed:
lein repl
...
(dir mylib.core)
Exception No namespace: mylib.core found clojure.core/the-ns (core.clj:4008)
Do I have to add something to the project.clj file maybe?
In order to use a library you must cause the code to be loaded - that it be on the classpath is not sufficient.
You can do this easily in an ns declaration in a file of course, but in the repl it can be easier to use (require '[my-lib.whatever :as w]) after which one can call (w/foo) (w/bar) etc. as expected. You can also use (in-ns 'my-lib.whatever) in order to switch to the namespace, but this will not give you a good result unless you have previously used require or use or load-file etc. to get the definitions first.
Let's say you created a new library named clj-foo.
% lein new clj-foo
Start your repl.
% cd clj-foo
% lein repl
In the repl, load the main entry point to your library and switch to its namespace.
(load-file "src/clj_foo/core.clj")
(ns clj-foo.core)
Now you're in the clj-foo.core namespace, make sure to add back in the repl ns to get things like doc available.
(use 'clojure.repl)
That's it. You're all set to start calling functions in your library. Note that other library files will be available from the clj-foo.core namespace if they were loaded by namespace declaration at the top of clj_foo/core.clj. If not, then you'll need to invoke load-file with their path as well.
If you make changes in core.clj. You can invoke load-file again to pick up the new code. As you progress, you can use cider to facilitate loading of individual functions and files. But that's for another question. :)
You need to add a dependency to use your library from another project. To do this add a vector (a tuple-2) to the vector that is the value of the :dependencies key in the project.clj file. Here's an example:
:dependencies [[org.clojure/clojure "1.7.0"]
[org.clojure/clojurescript "1.7.170"]
[org.clojure/core.async "0.2.371"]
[default-db-format "0.1.0-SNAPSHOT"]
[com.andrewmcveigh/cljs-time "0.3.14"]]
My own local library is called default-db-format. Its really no different to adding a dependency for com.andrewmcveigh/cljs-time.
As you say you can already do this, but are having trouble getting a REPL connection to the project of the library itself. When you go (in-ns 'some-path), you need the single quote in front of some-path. Note that some-path is a different thing to the name of your library.
Rather than use lein repl you can use the figwheel repl - if your project is setup with figwheel. My library has only one entry point and that is lein figwheel devcards. After that I had no problem going to a namespace and trying out a function:
cljs.user=> (in-ns 'default-db-format.core)
nil
default-db-format.core=> (check 1 2)
As noisesmith mentioned having a REPL in your IDE is the best setup. No fiddly typing just bring up pre-configured REPLs (per namespace) with the click of a button (or keystroke). Figwheel/Cursive setup instructions here.
I was also facing the same issue with the following configuration:
Leiningen 2.9.0 on Java 1.8.0_201 Java HotSpot(TM) 64-Bit Server VM
My file looks like this, and from the repl I desired to invoke the foo function
(ns cljtest.test
(:gen-class))
(defn foo [input]
(assoc {} "a" 123))
Both these approaches worked fine for me on the repl.
1)Switch to the appropriate name space:
cljtest.core=> (in-ns 'cljtest.test)
#object[clojure.lang.Namespace 0x90175dd "cljtest.test"]
cljtest.test=> (foo nil)
{"a" 123}
cljtest.test=>
2)Require the appropriate name space:
cljtest.core=> (require '[cljtest.test :as test])
nil
cljtest.core=> (test/foo nil)
{"a" 123}
cljtest.core=>
I am creating a third-party library for Clojure. In the core of this library I need to run script from the operation system. In this case I cannot use absolute path to this file like this /Users/me/src/clojure-pymorphy/src/pymorphy/pymorphy.py.
But if I use relative path script/script.py - than sometimes location of the script could be determined as wrong. It depends on the directory from which I run/use this clojure library. Because this library will be distributed as the third-party I need the way to determine the path of script.py from Clojure source code.
Project folders looks like this:
clojure-pymorphy/
src/
pymorphy/
pymorphy.py
pymorphy.clj
Script pymorphy.py is invoked from pymorphy.clj. File pymorphy.clj begins from this lines:
(ns pymorphy
(:import (java.util ArrayList)
(jep Jep)))
I've tried to find out is there a way to determine directory to pymorphy namespace at runtime and just simply add "pymorphy/pymorphy.py" to it. But after many hours of googling I come to conclusion that it's not possible.
So is there any other ways to dynamically determine path to clojure namespace at runtime?
Many thanks.
While looking at the similar questions at stackoverflow I find the solution:
(.getFile (clojure.java.io/resource "pymorphy/pymorphy.py"))
If you to run this script, so this will be something like - this will copy script to temp file, and run it. You need to create file, because if you'll pack everything into jar, then interpreter won't be able to read file..
(let [tmp (File/createTempFile "tmp" "py")
script (clojure.java.io/resource "pymorphy/pymorphy.py")]
(try
(when (and script (.exists tmp))
(with-open [os (.openStream script)]
(clojure.java.io/copy os tmp))
(run-script....))
(finally
(when (.exists tmp)
(.delete tmp)))))
Say I made a change to a Clojure library (eg. added a parameter to the request-token in clj-oauth) and want to use that changed library in my project. What's the best way to do this, short of compiling the new library as a JAR and copying that to my project lib?
I want to be able to tweak the library and my project at the same time (preferably in the REPL). If I were doing this in Ruby, I would download and 'require' the gem, then reopen that class in my own project source and add or override the methods as needed.
You can hack directly at the REPL. Suppose you've got incanter on your classpath.
Start a REPL. The first thing we need to do is bring the incanter classes into it.
user> (require 'incanter.core)
nil
Now we can see the function incanter.core/matrix?
user> (incanter.core/matrix? 2)
false
We can look at the original source code:
user> (require 'clojure.repl)
nil
user> (clojure.repl/source incanter.core/matrix?)
(defn matrix?
" Test if obj is 'derived' incanter.Matrix."
([obj] (is-matrix obj)))
nil
Let's go and screw it up:
First change to the incanter.core namespace:
user> (in-ns 'incanter.core)
#<Namespace incanter.core>
Then we can redefine it, using the old source code as a crib:
incanter.core> (defn matrix? [obj] "hello")
#'incanter.core/matrix?
Unit test:
incanter.core> (matrix? 2)
"hello"
Switch back to the user namespace:
incanter.core> (in-ns 'user)
#<Namespace user>
Try it out:
user> (matrix? 2)
; Evaluation aborted.
There is no definition of user/matrix. We redefined it in the incanter.core namespace.
user> (incanter.core/matrix? 2)
"hello"
For experimenting at the repl, it's ok just to change source files and re-compile the single file (C-C C-k in emacs), or if you're in the right namespace, just re-evaluate the definition.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Now, if we want to make our valuable change permanent and available to other projects, it depends on how everything is set up.
I use maven for dependency management, so it would be a question of modifying the source file, and then re-running the build process for the library to compile the new version and install it into the local maven repository.
With a maven project, that should be as simple as
$ mvn install
A note about version numbers:
If you do make permanent modifications and use dependency management to coordinate the differences, then you should change the version number of your library, from maybe 1.2.0 to 1.2.0-johnshack-SNAPSHOT, or something that is unlikely to collide with the real thing when you want to use an unperverted version in another project. You wouldn't want a modified version finding its way into projects where it isn't welcome.
Then you modify your own project files to make sure that you use the hacked version where you want to, and the next time you start your repl, it should pull in the last hack that you installed.
You will need to reinstall again every time you want your changes to make their way into the repository, but that's actually probably a good thing.
Unfortunately, (and it was at this point that I started to wish that I had chosen a different example) Incanter turns out to be a leiningen project which is split into sub-modules in an ad-hoc scripty sort of way, so we need to figure out how it expects to be installed. The figuring out turned out to be quite hard, although the answer is easy. Leiningen sets my hair on fire.
You can get incanter's source here:
$ git clone http://github.com/liebke/incanter.git
and the relevant source file is:
~/incanter/modules/incanter-core/src/incanter/core.clj
Modify it to break the matrix? function, and then it turns out that what you have to do is:
Change the version numbers in both the top level project.clj, and also in the submodule project.clj.
Then you run lein install in the incanter-core directory, and then again in the top-level directory, and you have to do it in that order. I don't quite understand why.
At the moment all this seems needlessly complicated. I'm (fairly) sure that it will settle down as the tools mature.
If you're using (or wouldn't mind using) cake, check out the subproject dependencies section of the README. I think it might be exactly what you're looking for.
You upload it to clojars under a different name depend on that.
I setup emacs slime/clojure as written here.
When I run (doseq [p (.getURLs (java.lang.ClassLoader/getSystemClassLoader))] (println (.getPath p))) to get classpath, I get the following.
/Users/smcho/.swank-clojure/clojure-1.1.0-master-20091202.150145-1.jar
/Users/smcho/.swank-clojure/clojure-contrib-1.1.0-master-20091212.205045-1.jar
/Users/smcho/.swank-clojure/swank-clojure-1.1.0.jar
How can I add classpath for emacs/slime for clojure?
The procedure differs depending on whether you are using slime-connect to start slime (by connect to a remote swank server, created with lein swank, for example) or you are starting slime using M-X slime.
If you're using slime-connect, you'll need to modify the classpath of the java process that is running the swank server. If you're starting the swank server using lein swank, simply add the jars you want to be part of your classpath to the project's lib directory.
On the other hand, if you're starting slime using M-X slime, the following elisp code will do the magic for you (just place it in your ~/.emacs file).
(eval-after-load "swank-clojure"
'(progn
(add-to-list 'swank-clojure-classpath
"/Users/smcho/.clojure/")
(add-to-list 'swank-clojure-classpath
"/Users/smcho/.clojure/blah.jar")))
This will add /Users/smcho/.clojure/ and /Users/smcho/.clojure/blah.jar to the classpath. (Please note that you'll either need to restart emacs or reload the .emacs file: type M-X load-library and then type .emacs on the next prompt.)
I believe the advised way to fire up a clojure reple nowadays is to use lein swank, and use \M-x slime-connect. See http://github.com/technomancy/leiningen/ for a detailed description of this tool.
Using leiningen, you can configure your project using a configuration file, project.clj. On this file you may require remotely (maven-)published jars, and by running lein deps you will get a "lib" directory with all the jars. Since the lein swank command uses that directory as a classpath, all you have to do is to add your jars to that directory.
That said, if you're still using \M-x swank-clojure-project, it will also detect your jars under that directory.
However, if you're just using a plain \M-x slime to fire a clojure repl, then I think there's no "clean" solution (aside from adding the path to your global environment's $CLASSPATH or to use some elisp voodoo - like the one in seh's answer - to change the java vm command arguments. But I believe this is only intended to do some quite basic experiments, and shouldn't be used for any project-based work (exactly for this reason!)
If you're willing to hard-code the list of Jar files, this Emacs Lisp form should suffice:
(let ((base-path "/Users/smcho/.swank-clojure"))
(setq swank-clojure-classpath
(append
swank-clojure-classpath
(mapcar (lambda (d)
(expand-file-name d base-path))
'("clojure-1.1.0-master-20091202.150145-1.jar"
"clojure-contrib-1.1.0-master-20091212.205045-1.jar"
"swank-clojure/swank-clojure-1.1.0.jar")))))
That takes your list of Jar files and adds them to the variable swank-clojure-classpath. Note that this form must be evaluated by Emacs, not SLIME's Swank. It's used by Emacs to start up the Java process that will run Clojure and Swank within it.
There are more elaborate ways to set up the class path for a project, such as including Maven-style paths below a designated project root directory.
I'm trying to set up a simple clojure project, and I'm not sure how to load files between the project. I'm sure that the answer is in the documentation, but I can't find a simple answer any where and I'm not sure where to look.
Essentially, my directory looks like this:
Clojure/
clojure/
clojure.jar
other clojure files
clojure-contrib/
clojure-contrib.jar
other contrib files
project/
main.clj
utils.clj
And I want main.clj to be something like this:
(ns project.main
(:require project.utils))
(greet)
and utils.clj to be something like this:
(ns project.utils)
(defn greet [] (println "Hello, World!"))
But that fails with:
Exception in thread "main" java.io.FileNotFoundException: Could not locate project/utils__init.class or project/utils.clj on classpath: (main.clj:1)
When I attempt to run it. My classpath includes the top Clojure/ directory and both jars. I also tried putting the project/ directory in the classpath as well, with no luck.
How do you set up a simple clojure project?
You don't mention what your environment is (i.e. Emacs/SLIME/Swank, vim/Vimclojure), so I'm going to assume you are trying to invoke it from the command line.
You need to have your Clojure/ project directory in the classpath:
java -cp path/to/clojure.jar:path/to/clojure-contrib.jar:path/to/Clojure ...
Make sure to check that paths are correct relative to current working directory. It needs to point to the root of your namespace (i.e. if running in Clojure/, the path is .).
In fact, your project layout Works On My Machine(tm), with the exception that I have use instead of require (but you should've got a different error anyway if you got to the point when Clojure could find all your files).
This answer I posted to another question should hopefully give you an idea of how your filenames should relate to namespace names for things to work. However, since your question is "how to set up a simple Clojure project", the following is a better start:
Go to GitHub and grab Leiningen.
Follow the instructions in the README. You'll end up doing something like
$ lein new my-project
$ cd my-project
# ... edit project.clj ...
$ lein deps
Hack away! You'll need to put your files in the correct places. That will mean putting your source files in the directory tree rooted at my-project/src, with your core namespace most likely residing at my-project/src/my_project/core.clj. But really, I've explained all the details in the answer linked to above, so please read it (and do leave a comment if I missed something). :-)
Leiningen will take care of the basic project layout and setting up the classpath for a REPL / swank / nailgun for you (if you haven't yet come across the latter two, you will soon -- but that's a separate topic, the swank part of which I have covered to a certain degree e.g. in this SO answer), so hopefully you'll never need to deal with the java -cp ... nonsense by hand. (The swank-related answer I linked to in the last parenthetical remark has details on how to set up swank with the correct classpath from within Emacs.)