How to reference environmental variable or home dir in project.clj - clojure

Is there a way to programmatically insert the name of my home directory into file paths in Leiningen's project.clj?
I run a Leiningen project on different machines, where I have different home directories. The project uses jar files that are not managed by Maven; I download 'em and put them in a directory that's relative to my home directory, or copy them into the Leiningen project. The second option works but is undesirable.
An easy way to use the first option--keeping the jar files somewhere else--is to add a soft link to the "somewhere else" directory in my Leiningen directory. That works, but the link has to be different on each machine, so I can't include the link file in the git repository, and I'd rather include everything in the git repo.
Isn't there a way to use environmental variables, or otherwise refer to my home directory, in my project.clj file? I've gone through the sample project file and have not found a solution, so far.
I thought I could just construct path strings at run time--what's in project.clj is just Clojure code, after all. Since hard-coding my home directory into project.clj works without any trouble:
:resource-paths [/Users/myhomedir/dist/mason/jar/mason.19.jar")]
I figured I could do this:
:resource-paths [(str (System/getenv "HOME") "/dist/mason/jar/mason.19.jar")]
However, Leiningen doesn't like this at all:
java.lang.IllegalArgumentException: No implementation of method: :as-file of protocol: #'clojure.java.io/Coercions found for class: clojure.lang.PersistentList
Changing [...] to (vector ...) gives the same error but with 'clojure.lang.Symbol` at the end.

In the name of repeatability you'd better have the jar installed in the local maven repository and have it added to the project.clj :dependencies so it's fetched from there. But you said those jars won't be managed by maven, so here we go:
defproject is a macro and it allows to use unquoting to do arbitrary evaluation. It does it by calling the internal fn unquote-project. So you can do the following:
:resource-paths [~(str (System/getenv "HOME") "/dist/mason/jar/mason.19.jar")]

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.

Is there a way to add a second src folder to leiningen?

I am trying to generate documentation for my source code using a templating library.
Currently my directory structure looks like
---src
---test
---project.clj
I would like to make a third folder called docs, in addition to src and test, which will have my documentation related namespaces. I would like to only include the namespaces in src in my final uberjar. I tried simply adding a third folder. I ran the repl and then required my docgen namespace in the docs folder, but got the following error.
(require '[<>.<>.docgen :as docgen] :reload)
FileNotFoundException Could not locate <>/<>/docgen__init.class or foundry/schema/docgen.clj on classpath. clojure.lang.RT.load (RT.java:456)
Is there a way to add the docs folder to the classpath of a certain profile so it is not part of uberjar?
Solved:
Added a profile in my project.clj, with the source-paths key. I use the with-profile lein command with +docgen
:profiles {
:docgen {
:dependencies [[]]
:source-paths ["docs"]
}
}

leinigen repl with profile

This question is a follow up to How does one pre-load a clojure file in the leiningen repl?.
My ~/.lein/profiles.clj looks as follows:
{
:user {:source-paths ["C:/Users/username/.lein/src"] }
}
My ~/.lein/src/user.clj might look as follows:
(ns user)
(println "user file loaded")
When I run lein repl within a folder containing a project.clj, then the user.clj file is executed, but when I run lein repl from another folder it doesn't load my user profile. Is there a workaround for this or is this behavior by design? In fact, I know that Leinigen is indeed loading my profile.clj (even when there is no project.clj) because there are also other things inside (taken from the excellent pimp my repl article). Leinigen is just having problems with my additional source-paths setting.
One other question related to this is the fact that I need to specify the full path of the folder for my user.clj file : "C:/Users/username/.lein/src". When I change that to "~/.lein/src" leiningen fails to load my file.
It sounds like you just want some code loaded for your lein repl sessions. This is done with the :init key of the :repl-options in your profiles.clj. You can load-file other files in init if you want to organize in that fashion.
{:user
{:repl-options
{:init (load-file "path-to-file/user.clj")}
...}}
Note: If you are using Windows-style path delimiters /, you'll need to escape them //.

Clojure load files

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.)

Can not get clojure-contrib to load - FileNotFoundException

I have tried putting clojure-contrib.jar various places on my filesystem, I have tried manually specifying the classpath when launching the REPL, I have tried extracting the jar and putting the clj files on the classpath, nothing seems to work. I continue getting something like this:
java.io.FileNotFoundException: Could not locate clojure/contrib/str_utils__init.
class or clojure/contrib/str_utils.clj on classpath: (NO_SOURCE_FILE:0)
When I execute (println (seq (.getURLs (java.lang.ClassLoader/getSystemClassLoader)))) to see the classpath, it does include clojure-contrib:
(#<URL file:/C:/Program%20Files/Java/jre1.6.0_06/lib/clojure-1.0.0.
jar> ... #<URL file:/C:/Program%20Files/Java/jre1.6.0_06/lib/clojure-contrib.jar>)
When I make a .clj file of my own and put it on the classpath I am able to use or require it and have access to its functions as I'd expect.
If I move a .clj file - str-utils.clj for instance - to the pwd (which is on the classpath) it sorta works - it at leasts finds the file, although it generally can't do anything with it because of dependencies on other functions buried in clojure-contrib which it can't find.
Any ideas why this wouldn't work for clojure-contrib?
If you compiled your own clojure-contrib jar, you probably used the main branch which is not 1.0 compatible.
Follow the Using clojure-contrib with Clojure 1.0 doc to checkout the 1.0 tagged revision of clojure-contrib.
There is also a clojure-contrib git branch that maintains 1.0 compatibility.