14

When I evaluate (use 'hello) to load hello.clj, the REPL complains with the following error:

java.io.FileNotFoundException: Could not locate hello__init.class or hello.clj on classpath:  (NO_SOURCE_FILE:0)

I'm invoking the REPL like this:

java -cp "/Library/Java/Extensions/servlet-api-2.5-20081211.jar:... lots of jarfiles ...:/Library/Java/Extensions/clojure-contrib.jar:/Library/Java/Extensions/clojure-1.0.0.jar:./classes/:." jline.ConsoleRunner clojure.lang.Repl

Reading around, this looks like the file isn't being found in the PWD, but I've added . to the path with no success :-(.

Running with Java 1.6 on OS X 10.6.

I'm sure I'm being an idiot, can someone hit me with a LART?

EDIT: I also tried the ClojureX distro, and got the same results.

afternoon
  • 1,285
  • 16
  • 25
  • What output do you get from: (println (seq (.getURLs (java.lang.ClassLoader/getSystemClassLoader)))) – Timothy Pratley Nov 26 '09 at 22:06
  • From my Clojure dir (/Users/ben/Documents/Code/Clojure), I get this: (# ... more urls for jarfiles ... # # #) – afternoon Nov 27 '09 at 13:44
  • since I didn't see this explicitely stated anywhere yet and it might have relevance where is the hello.clj file located? also what happend if you run (compile 'hello)? – Jeremy Wall Nov 29 '09 at 07:15
  • hello.clj is in my PWD (which for all of my experiments has been ~/Documents/Code/Clojure/). – afternoon Nov 29 '09 at 16:34
  • (compile 'hello) gives the same error as (use 'hello), namely: java.io.FileNotFoundException: Could not locate hello__init.class or hello.clj on classpath: (NO_SOURCE_FILE:0) – afternoon Nov 29 '09 at 16:36
  • Right, two more questions, although I really feel at a loss as to what could be happening. Firstly, which version of Clojure are you using? Secondly, does all this still happen with a minimal classpath (just clojure.jar and a minimal .clj file in $PWD, like the 'hello' example in my answer?)? BTW, clojure.lang.Repl is apparently deprecated in newer builds, with clojure.main to be used instead. – Michał Marczyk Dec 22 '09 at 21:26

5 Answers5

16

If hello.clj is in $DIR and $DIR is on your classpath, then hello.clj needs to start with (ns hello). If it's in $DIR/$SUBDIR and $DIR is on your classpath, then hello.clj needs to start with (ns $SUBDIR.hello). Generally, your filename structure and the ns name structure must match, with filename separator replaced by . in ns name and any _s in the filename corresponding to -s in the ns name. The name of the actual file needs to be the final component (possibly the only component, if the file's containing dir is on the classpath) of the namespace name.

EDIT:

An extended example. No information beyond what I've written above, so please skip over it if that was enough for you; however I know that getting CP right was quite painful for me in the beginning, so I decided I'd write this out in a step by step fashion so that someone, somewhere might perhaps be spared that particular `learning experience' ;-).

Say this is your namespace definition:

;;; in file "hello.clj"
(ns hello)

(defn hello []
  (println "Hello!"))

Then if you put the directory containing hello.clj on your classpath, you're good to go and a (use 'hello) at the REPL should do what you want.

If, on the other hand, you do this:

;;; in file "hello.clj"
(ns my-namespace)
;;; ...

or this:

;;; in file "my-filename.clj"
(ns hello)
;;; ...

-- that is, if you introduce a mismatch between the name of the file and the name of the namespace, Clojure won't be able to find your namespace.

Also, if you put hello.clj in /path/to/code, but what you have on your classpath is actually /path/to, i.e. the parent directory of /path/to/code, you need to do this:

;;; in file "/path/to/code/hello.clj"
(ns code.hello)
;;; ...

Then you'll be able to (use 'code.hello).

Finally, if you call your file my_namespace.clj, you need to call your ns my-namespace (and the other way around). _s in namespace names and -s in filenames should not be used.

Michał Marczyk
  • 83,634
  • 13
  • 201
  • 212
  • Thanks very much for going into so much detail here. I ran through all your examples and still had no luck. I think my system must be broken somehow. – afternoon Dec 22 '09 at 20:25
  • Sure. But, um... it still doesn't work!? At this point I'm inclined to share your suspicions. I'll post any follow-up questions as comments to the question, if I can think of any... Good luck! – Michał Marczyk Dec 22 '09 at 21:23
  • 2
    I'm surprised that Clojure is taking Java's double-encoding of syntactic package declaration and physical directory path one step further, now requiring the file name itself to also be part of the namespace -- even though there need not be any classes generated on behalf of a particular file. In CL, packages are divorced from source files. In Clojure, are namespaces supposed to be divorceable from files? Should a namespace be able to span multiple contributing source files? The Clojure documentation is mute on this point. – seh Dec 27 '09 at 17:19
  • 1
    You can always use `load-file` to break down a namespace into several chunks `#include "foo.h"` style. I've yet to see a case where this is particularly useful, though; as far as I can tell, a few sub-namespaces would tend to accomplish much the same thing in a cleaner way (and there's no reason not to re-export their publics from a single namespace meant to serve as the access point for consumers of the code). As for the filesystem path-classpath coupling, I rather think it's a good idea. Haskell & Python are two examples of languages arguably well served by this choice. – Michał Marczyk Dec 27 '09 at 20:09
  • 1
    What was very useful for me was that "_s in namespace names and -s in filenames should not be used." I didn't realize that and had been using '-' in my file names and as a result was getting the file not found messages. – Jerry Mindek Oct 29 '13 at 18:26
3

when I'm working from the repl and want to load files i find i have to call something like this first:

(add-classpath "file:///home/arthur/.../src/")
(add-classpath "file:///home/arthur/.../build/")

before the repl can find them on the classpath. I put these in a file that is not included in the jar file along with a statement that reloads everything from the other files. When I build a jar file i find I don't need to do this.

Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
  • 2
    Please don't do that! add-classpath is *only* for experimenting. The Right Way for a regular setup is as the OP did: set up the classpath on startup of the JVM. Adding paths with add-classpath is regularly broken and will come back to haunt you. This is just the way the JVM is designed. – kotarak Nov 27 '09 at 06:11
  • When I use add-classpath as suggested, it does at least work well enough to find hello.clj and load it. It seems add-classpath is doing something that the env CLASSPATH variable is not. – afternoon Nov 27 '09 at 13:47
3

Does hello.clj contain a (ns some-namespace) statement? If so, then same_namespace is appended to the each element of the CLASSPATH before looking for hello.clj

Hugo Duncan
  • 426
  • 3
  • 3
  • I didn't know that, thanks! I have a couple of test files. One defines a namespace, the other does not. Neither work unfortunately. I moved the namespace-defining test to a subdir of the classpath, but still no luck. – afternoon Nov 28 '09 at 11:43
  • I should have said the parent elements of 'some-namespace'. That is a file with (ns some.namespace) should be named 'namespace.clj' in a subdirectory 'some'. – Hugo Duncan Dec 03 '09 at 03:04
1

I've defined an alias (in .bash_profile) for loading the REPL:

alias clojure='CLASSPATH=$HOME/git/clojure/clojure.jar:$HOME/git/clojure-contrib/clojure-contrib.jar:.:classes rlwrap java clojure.lang.Repl'
Michiel de Mare
  • 41,982
  • 29
  • 103
  • 134
1

I have the following lines on my .emacs:

(setq swank-clojure-jar-path "~/src/clojure/clojure.jar"
      swank-clojure-extra-classpaths (append 
                      (directory-files "~/src/compojure/deps" t ".jar$")
                      (list
                       "~/src/swank-clojure/src/main/clojure"
                       "~/src/clojure-contrib/clojure-contrib.jar"
                       "~/src/clj/.")))

I load the programe snake.clj which is at ~/src/clj and eval it (C-x h which selects the entire buffer and then C-c C-c to compile). The programs creates a namespace by name snake. Now, from emacs/slime, I do

(use 'snake)

That's it.Now invoke any function defined in the namespace.

Your problem may be that the directory on which you have hello.clj may not be on your classpath. Also make sure you name the namespace properly. If inside hello.clj, you named your name space as hello, then you do (use 'hello).