How to list dependencies for an alias? - clojure

Assume I have this config.edn:
{:deps {org.clojure/clojure {:mvn/version "1.11.1"}}
:aliases {:dev {:extra-deps {org.clojure/tools.namespace {:mvn/version "1.3.0"}}}}}
How can I list dependencies in a way that extra-deps from dev alias are included too?
$ clj -X:dev:deps list
org.clojure/clojure 1.11.1 (EPL-1.0)
org.clojure/core.specs.alpha 0.2.62 (EPL-1.0)
org.clojure/spec.alpha 0.3.218 (EPL-1.0)

If you run clj -X:deps help/doc and scroll to the clojure.tools.cli.api/list function, you'll see that it supports an extra :aliases argument.
So you can use something like clj -X:deps list :aliases '[:dev]'.

Related

On a Clojure/Clojurescript project, does it make sense to have dependencies declared on a "shadow-cljs.edn" and a "deps.edn" file?

I have been using Clojure, ClojureScript, lein, shadow-cljs, re-frame, reagent, Emacs, and CIDER to work on a Clojure/ClojureScript dynamic web app project.
In one of the private repositories, there is a deps.edn file with the following content:
{:deps
{org.clojure/clojure {:mvn/version "1.10.3"},
reagent {:mvn/version "0.10.0"},
org.clojure/tools.logging {:mvn/version "1.1.0"},
org.clojure/clojurescript {:mvn/version "1.10.866"},
ring {:mvn/version "1.9.0"},
garden {:mvn/version "1.3.10"},
metosin/malli {:mvn/version "0.5.1"},
hiccup {:mvn/version "1.0.5"},
metasoarous/oz {:mvn/version "1.6.0-alpha35"},
re-frame {:mvn/version "0.12.0"}}
At the same time, there is another file shadow-cljs.edn with the following definition:
:dependencies
[[reagent "1.1.0"]
[re-frame "1.2.0"]
[day8.re-frame/tracing "0.6.2"]
[garden "1.3.10"]
[metosin/malli "0.8.3"]
[binaryage/devtools "1.0.3"]
[day8.re-frame/re-frame-10x "1.1.11"]]
As you see, some things such as reagent appear on both files and they use different versions!
This feels weird to me. But the project seems to work fine.
Why does this happen? Where is each version of, say, reagentused? Is there a better way to declare the dependencies?
;; UPDATE
User #EugenePakhomov gave a nice answer to this post. The code is an attempt to implement his suggestion.
Hence, I did:
{:deps
{reagent {:mvn/version "0.10.0"},
re-frame {:mvn/version "0.12.0"},
garden {:mvn/version "1.3.10"},
metosin/malli {:mvn/version "0.5.1"},
org.clojure/tools.logging {:mvn/version "1.1.0"},
org.clojure/clojurescript {:mvn/version "1.10.866"},
ring {:mvn/version "1.9.0"},
hiccup {:mvn/version "1.0.5"},
metasoarous/oz {:mvn/version "1.6.0-alpha35"},
org.clojure/clojure {:mvn/version "1.10.3"},
{:alias {:cljs-only-dependencies
reagent {:mvn/version "1.1.0"},
re-frame {:mvn/version "1.2.0"},
day8.re-frame/tracing {:mvn/version "0.6.2"},
garden {:mvn/version "1.3.10"},
metosin/malli {:mvn/version "0.8.3"},
binaryage/devtools {:mvn/version "1.0.3"},
day8.re-frame/re-frame-10x} {:mvn/version "1.1.11"}}}
:source-paths ["src" "test"]}
Is this the correct implementation of your suggestion?
Shadow-cljs uses those dependencies to build your CLJS code. And the deps.edn dependencies are used to run your CLJ code.
Those two things aren't necessarily mutually exclusive. E.g. you can have some CLJS code using some macros that are written in CLJ and that rely on some dependencies - in that case, such dependencies will have to be specified in both files.
An alternative to that layout that avoids duplication is to specify :deps true in shadow-cljs.edn and move all the dependencies from there into deps.edn. Even better, move all CLJS-only dependencies under some specific alias and use :deps {:aliases [:that-alias]} - this way, your CLJ code won't even know about CLJS-only dependencies so they won't pollute the classpath.

Leiningen wont exclude namespaces from uberjar

I have a project where I want certain parts of the code to be able to run on a local environment, and other parts which depend on libraries that only run on a remote environment. E.g.
src/app/core.clj <- can run anywhere
src/app/sandbox/remote_only.clj <- depnds on libs that only function in remote environ
where src/app/core.clj is
(ns app.core
(:gen-class))
(defn -main [] (println "Hello, World!"))
and src/app/sandbox/remote_only.clj is
(ns app.sandbox.remote-only
(:require
[uncomplicate.commons.core :refer [with-release]]
[uncomplicate.neanderthal
[native :refer [dv dge]]
[core :refer [mv mv!]]]))
I want to be able to uberjar and run the code located in core.clj on my local machine without pulling in remote_only.clj which will cause the program to fail locally. According to the docs, Leiningen should be able to accomplish this using profiles and uberjar exclusions e.g.:
(defproject app "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.10.1"]]
:profiles {:uberjar {:main app.core
:init-ns app.core
:aot :all}
:remote {:init-ns app.sandbox.remote_only
:dependencies [[uncomplicate/neanderthal "0.43.1"]]}} ; these deps will fail locally
:uberjar-exclusions [#".*sandbox.*"]
:repl-options {:init-ns app.core})
compiling the uberjar here will result in:
❯ lein uberjar
Compiling app.core
Compiling app.sandbox.remote-only
Syntax error macroexpanding at (remote_only.clj:1:1).
Execution error (FileNotFoundException) at app.sandbox.remote-only/loading (remote_only.clj:1).
Could not locate uncomplicate/commons/core__init.class, uncomplicate/commons/core.clj or uncomplicate/commons/core.cljc on classpath.
So, explicitly its trying to compile the class that was specifically excluded.
I thought that this kind of problem could be avoided by removing the :all tag from :aot compilation. E.g.:
(defproject app "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.10.1"]]
:profiles {:uberjar {:main app.core
:init-ns app.core
:aot [app.core]}
:remote {:init-ns app.sandbox.remote_only
:dependencies [[uncomplicate/neanderthal "0.43.1"]]}} ; these deps will fail locally
:uberjar-exclusions [#".*sandbox.*"]
:repl-options {:init-ns app.core})
which causes other problems:
❯ lein uberjar
Compiling app.core
Created /Users/warrenronsiek/Projects/app/target/app-0.1.0-SNAPSHOT.jar
Created /Users/warrenronsiek/Projects/app/target/app-0.1.0-SNAPSHOT-standalone.jar
~/Projects/app 5s 15:47:37
❯ java -jar ./target/app-0.1.0-SNAPSHOT.jar
Exception in thread "main" java.lang.NoClassDefFoundError: clojure/lang/Var
I've tried playing around with all kinds of :exclusions, :jar-exclusions and regexes. Variations of this question have been asked multiple times (1, 2) Nothing works.
How do I get Leiningen to compile uberjars and ignore the specified file(s)?
You are on the right track with changing :aot :all to :aot [app.core] but when you tried to run the JAR, you are running the library version instead of the whole application version:
java -jar ./target/app-0.1.0-SNAPSHOT.jar
That doesn't include Clojure or any dependencies. You want:
java -jar ./target/app-0.1.0-SNAPSHOT-standalone.jar
I haven't tried this particular setup with the "exclusions" features.
One workaround would be to create sub-projects for the "local" & "remote" parts of your app, which could then be used by a higher-level "total" project.
app
app-local
app-remote
where each of the 3 is a separate Lein project. app-local and app-remote could be developed in isolation. The top-level app project then uses has the 2 sub-projects as dependencies (and therefore only works in the remote/full environment).
The Lein feature checkouts allows you to develop in all 3 repos simultaneously, which is a huge help. It is much faster than the alternative of using lein install, which is slow, manual, repetitive, and error-prone.
P.S. Did you experiment with using lein jar instead of lein uberjar?

Why is clojure deps adding `/main/clojure` to classpath when using `:local/root`?

I am trying to link a local project to another local project that is under development using tools.deps :local/root option in deps.edn. It isn't working. I can't require the library's namespaces, and the path is correct.
The deps.edn entry looks like:
{:paths ["src" "resources"]
:deps {...
...
...
mylib {:local/root "../../../mylib"}
}}
The classpath that is generated, however, is incorrect:
../../../mylib/src/main/clojure
For some reason clojure/main is added on to the classpath for this library, and I don't know why. Then when I run clj to start the repl I am unable to load the library, and I get the FileNotFoundException.
To test that the addition of main/clojure to the lib path is the problem, I manually removed that part of the path in the cache file in .cpcache directory and was able to require the library namespaces once I'd removed clojure/main.
Does anyone know where the main/clojure is coming from and how I can stop it being added?
UPDATE
I did a new test that leads me to think this has something to do with the use of project.clj as opposed to deps.edn in the target project. In the test I had a project b with a deps.edn like this:
{:deps {a-proj {:local/root "../a-proj"}}}
While a-proj had a project.clj like this:
(defproject a-proj "0.1.0-SNAPSHOT"
:description "blah"
:url "http://example.com/FIXME"
:license {:name "The MIT Licence"
:url "https://opensource.org/licenses/MIT"}
:source-paths ["src"]
:dependencies [])
I then ran clj -Sforece -Spath and got:
~/Projects/b > clj -Sforce -Spath ethan at rembrandt.local
src:/Users/ethan/.m2/repository/org/clojure/clojure/1.10.0/clojure-1.10.0.jar:/Users/ethan/Projects/b/src:/Users/ethan/Projects/a-proj/src/main/clojure:/Users/ethan/.m2/repository/org/clojure/spec.alpha/0.2.176/spec.alpha-0.2.176.jar:/Users/ethan/.m2/repository/org/clojure/core.specs.alpha/0.2.44/core.specs.alpha-0.2.44.jar
You can see there that the /main/clojure path has been aded on. When I instead use an essentially deps.edn in a-proj:
{:deps {}}
I get the following path, which seems correct:
~/Projects/b > clj -Sforce -Spath
src:/Users/ethan/.m2/repository/org/clojure/clojure/1.10.0/clojure-1.10.0.jar:/Users/ethan/Projects/b/../a-proj/src:/Users/ethan/.m2/repository/org/clojure/spec.alpha/0.2.176/spec.alpha-0.2.176.jar:/Users/ethan/.m2/repository/org/clojure/core.specs.alpha/0.2.44/core.specs.alpha-0.2.44.jar
My deps.edn in ~/.clojure has only this:
{
:aliases {
:new {:extra-deps {seancorfield/clj-new {:mvn/version "0.9.0"}}
:main-opts ["-m" "clj-new.create"]}
:deps {:extra-deps {org.clojure/tools.deps.alpha {:mvn/version "0.5.435"}}}
:test {:extra-paths ["test"]}
}
}
project.clj is not supported as a project type by the Clojure CLI via deps.edn.

lein compile target-path configuration

I need some help with lein.
I need to compile my program to build/classes/main/
Lein append /classes to whatever folder I specify at project.clj :target-path.
For example, if I set:
:target-path "build"
files will be generated at build/classes instead of build/classes/main
If I set:
:target-path "build/classes/main"
files will be generated at build/classes/main/classes
build/classes/main/ is where my grails project compile all Groovy and Java classes before packaging the war file. My Clojure .class must be there too.
In your project.clj set :compile-path
:target-path "build"
:compile-path "%s/classes/main"

Using Medley.core lib in clojure repl

Is there a way to use the medley library directly in clojure's repl without adding it to the :dependencies in the project.clj file? Something like (use 'medley.core)?
You could add pomegranate to your $HOME/.lein/profiles.clj file
{:user {
:dependencies [[com.cemerick/pomegranate "0.3.0"]]
}}
Then, from the repl, you can require pomegranate functions with:
(use '[cemerick.pomegranate :only (add-dependencies)])
Then add your dependency like this:
(add-dependencies
:coordinates '[[incanter "1.2.3"]]
:repositories {"clojars" "http://clojars.org/repo"}))
Most of the time, Clojure libraries do use a maven compatible repository named clojars, hence the extra repository, but if the library is on the maven central, no need for the extra repository definition.
For example, bootstrap-clj is on central, so in that case, the below is enough:
(add-dependencies
:coordinates '[[com.github.sebhoss/bootstrap-clj "2.0.0"]])
Voila.