Merge pull request #2126 from cprice404/feature/master/2067-support-for-managed-dependencies

(#2067) add support for `managed-dependencies`
This commit is contained in:
Chris Price 2016-07-12 11:33:12 -07:00 committed by GitHub
commit 17fa738fd8
15 changed files with 379 additions and 49 deletions

105
doc/MANAGED_DEPS.md Normal file
View file

@ -0,0 +1,105 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
# Managed Dependencies With Leiningen
Maven (and now Leiningen) provides a capability called "Dependency Management".
The idea is to provide a way to specify a version number for common library
dependencies in a single location, and re-use those version numbers from other
discrete maven/lein projects. This makes it easy to, e.g., update your `clj-time`
dependency across a large number of projects without having to be mindful
of every common dependency version across all of your libraries.
When using `:pedantic? :abort` in your projects, to ensure that you are producing
a consist and predictable build, it can be very cumbersome to play the "dependency
version whack-a-mole" game that arises whenever an upstream library bumps a version
of one of its dependencies. `:managed-dependencies` can help alleviate this issue
by allowing you to keep the dependency version numbers centralized.
## `:managed-dependencies`
The `:managed-dependencies` section of your `project.clj` file is just like the
regular `:dependencies` section, with two exceptions:
1. It does not actually introduce any dependencies to your project. It only says,
"hey leiningen, if you encounter one of these dependencies later, here are the
versions that you should fall back to if the version numbers aren't explicitly
specified."
2. It allows the version number to be omitted from the `:dependencies` section,
for any artifact that you've listed in your `:managed-dependencies` section.
Here's an example:
```clj
(defproject superfun/happyslide "1.0.0-SNAPSHOT"
:description "A Clojure project with managed dependencies"
:min-lein-version "2.7.0"
:managed-dependencies [[clj-time "0.12.0"]
[me.raynes/fs "1.4.6"]
[ring/ring-codec "1.0.1"]]
:dependencies [[clj-time]
[me.raynes/fs]])
```
In the example above, the final, resolved project will end up using the specified
versions of `clj-time` and `me.raynes/fs`. It will not have an actual dependency
on `ring/ring-codec` at all, since that is not mentioned in the "real" `:dependencies`
section.
This feature is not all that useful on its own, because in the example above,
we're specifying the `:managed-dependencies` and `:dependencies` sections right
alongside one another, and you could just as easily include the version numbers
directly in the `:dependencies` section. The feature becomes more powerful
when your build workflow includes some other way of sharing the `:managed-dependencies`
section across multiple projects.
## Lein "parent" projects
One way of leveraging `:managed-dependencies` across multiple projects is to use
the [`lein-parent` plugin](https://github.com/achin/lein-parent). This plugin
will allow you to define a single "parent" project that is inherited by multiple
"child" projects; e.g.:
```clj
(defproject superfun/myparent "1.0.0"
:managed-dependencies [[clj-time "0.12.0"]
[me.raynes/fs "1.4.6"]
[ring/ring-codec "1.0.1"]])
(defproject superfun/kid-a "1.0.0-SNAPSHOT"
:parent-project [:coords [superfun/myparent "1.0.0"]
:inherits [:managed-dependencies]]
:dependencies [[clj-time]
[me.raynes/fs]])
(defproject superfun/kid-b "1.0.0-SNAPSHOT"
:parent-project [:coords [superfun/myparent "1.0.0"]
:inherits [:managed-dependencies]]
:dependencies [[clj-time]
[ring/ring-codec]])
```
In this example, we've consolidated the task of managing common version dependencies
in the parent project, and defined two child projects that will inherit those
dependency versions from the parent without needing to specify them explicitly.
This makes it easier to ensure that all of your projects are using the same versions
of your common dependencies, which can help make sure that your uberjar builds are
more predictable and repeatable.
## Other ways to share 'managed-dependencies'
Since the `defproject` form is a macro, it would be possible to write other plugins
that generated the value for a `:managed-dependencies` section dynamically. That
could provide other useful ways to take advantage of the `:managed-dependencies`
functionality without needing to explicitly populate that section in all of your
`project.clj` files.
## Future integration
It is likely that the functionality provided by the `lein-parent` plugin may integrated
into the leiningen core in a future release; for now we have added only the `:managed-dependencies`
functionality because it is necessary in order for the plugin to leverage it. We
will be experimenting with different ideas for implementation / API in plugins and
making sure that we find an API that works well before submitting for inclusion
into core leiningen.

View file

@ -250,10 +250,12 @@
(def ^:private get-dependencies-memoized
(memoize
(fn [dependencies-key {:keys [repositories local-repo offline? update
checksum mirrors] :as project}
(fn [dependencies-key managed-dependencies-key
{:keys [repositories local-repo offline? update
checksum mirrors] :as project}
{:keys [add-classpath? repository-session-fn] :as args}]
{:pre [(every? vector? (get project dependencies-key))]}
{:pre [(every? vector? (get project dependencies-key))
(every? vector? (get project managed-dependencies-key))]}
(try
((if add-classpath?
pomegranate/add-dependencies
@ -264,6 +266,7 @@
:repositories (->> repositories
(map add-repo-auth)
(map (partial update-policies update checksum)))
:managed-coordinates (get project managed-dependencies-key)
:coordinates (get project dependencies-key)
:mirrors (->> mirrors
(map add-repo-auth)
@ -407,13 +410,17 @@
#(-> % aether/repository-session
(pedantic/use-transformer ranges overrides))))
(defn ^:internal get-dependencies [dependencies-key project & args]
(defn ^:internal get-dependencies [dependencies-key managed-dependencies-key
project & args]
(let [ranges (atom []), overrides (atom [])
session (pedantic-session project ranges overrides)
args (assoc (apply hash-map args) :repository-session-fn session)
trimmed (select-keys project [dependencies-key :repositories :checksum
:local-repo :offline? :update :mirrors])
deps-result (get-dependencies-memoized dependencies-key trimmed args)]
trimmed (select-keys project [dependencies-key managed-dependencies-key
:repositories :checksum :local-repo :offline?
:update :mirrors])
deps-result (get-dependencies-memoized dependencies-key
managed-dependencies-key
trimmed args)]
(pedantic-do (:pedantic? project) @ranges @overrides)
deps-result))
@ -486,16 +493,21 @@
(doseq [[_ {:keys [native-prefix file]}] snap-deps]
(extract-native-dep! native-path file native-prefix))))))
(defn resolve-dependencies
(defn resolve-managed-dependencies
"Delegate dependencies to pomegranate. This will ensure they are
downloaded into ~/.m2/repository and that native components of
dependencies have been extracted to :native-path. If :add-classpath?
is logically true, will add the resolved dependencies to Leiningen's
classpath.
Supports inheriting 'managed' dependencies, e.g. to allow common dependency
versions to be specified from an alternate location in the project file, or
from a parent project file.
Returns a seq of the dependencies' files."
[dependencies-key {:keys [native-path] :as project} & rest]
(let [dependencies-tree (apply get-dependencies dependencies-key project rest)
[dependencies-key managed-dependencies-key project & rest]
(let [dependencies-tree (apply get-dependencies dependencies-key
managed-dependencies-key project rest)
jars (->> dependencies-tree
(aether/dependency-files)
(filter #(re-find #"\.(jar|zip)$" (.getName %))))]
@ -504,13 +516,49 @@
(extract-native-dependencies project jars dependencies-tree))
jars))
(defn ^:deprecated resolve-dependencies
"Delegate dependencies to pomegranate. This will ensure they are
downloaded into ~/.m2/repository and that native components of
dependencies have been extracted to :native-path. If :add-classpath?
is logically true, will add the resolved dependencies to Leiningen's
classpath.
Returns a seq of the dependencies' files.
NOTE: deprecated in favor of `resolve-managed-dependencies`."
[dependencies-key project & rest]
(let [managed-dependencies-key (if (= dependencies-key :dependencies)
:managed-dependencies)]
(apply resolve-managed-dependencies dependencies-key managed-dependencies-key project rest)))
(defn merge-versions-from-managed-coords
[deps managed-deps]
;; NOTE: there is a new function in the 0.3.1 release of pomegranate that
;; is needed here, but was accidentally marked as private. Calling it
;; via the symbol dereference for now, but this can be changed to a
;; regular function call once https://github.com/cemerick/pomegranate/pull/74
;; is merged.
(#'aether/merge-versions-from-managed-coords deps managed-deps))
(defn managed-dependency-hierarchy
"Returns a graph of the project's dependencies.
Supports inheriting 'managed' dependencies, e.g. to allow common dependency
versions to be specified from an alternate location in the project file, or
from a parent project file."
[dependencies-key managed-dependencies-key project & options]
(if-let [deps-list (merge-versions-from-managed-coords
(get project dependencies-key)
(get project managed-dependencies-key))]
(aether/dependency-hierarchy deps-list
(apply get-dependencies dependencies-key
managed-dependencies-key
project options))))
(defn dependency-hierarchy
"Returns a graph of the project's dependencies."
[dependencies-key project & options]
(if-let [deps-list (get project dependencies-key)]
(aether/dependency-hierarchy deps-list
(apply get-dependencies dependencies-key
project options))))
(apply managed-dependency-hierarchy dependencies-key nil project options))
(defn- normalize-path [root path]
(let [f (io/file path) ; http://tinyurl.com/ab5vtqf
@ -533,7 +581,7 @@
(seq
(->> (filter ext-dependency? (:dependencies project))
(assoc project :dependencies)
(resolve-dependencies :dependencies)
(resolve-managed-dependencies :dependencies :managed-dependencies)
(map (memfn getAbsolutePath)))))
(defn ^:internal checkout-deps-paths
@ -561,7 +609,8 @@
(:resource-paths project)
[(:compile-path project)]
(checkout-deps-paths project)
(for [dep (resolve-dependencies :dependencies project)]
(for [dep (resolve-managed-dependencies
:dependencies :managed-dependencies project)]
(.getAbsolutePath dep)))
:when path]
(normalize-path (:root project) path)))

View file

@ -83,7 +83,7 @@
((juxt :source-paths :test-paths :resource-paths) project))]
(.mkdirs (io/file path))))
(write-pom-properties project)
(classpath/resolve-dependencies :dependencies project)
(classpath/resolve-managed-dependencies :dependencies :managed-dependencies project)
(run-prep-tasks project)
(deliver @prep-blocker true)
(reset! prep-blocker (promise)))
@ -221,7 +221,7 @@
(defn ^:internal classpath-arg [project]
(let [classpath-string (string/join java.io.File/pathSeparatorChar
(classpath/get-classpath project))
agent-tree (classpath/get-dependencies :java-agents project)
agent-tree (classpath/get-dependencies :java-agents nil project)
;; Seems like you'd expect dependency-files to walk the whole tree
;; here, but it doesn't, which is what we want. but maybe a bug?
agent-jars (aether/dependency-files (aether/dependency-hierarchy
@ -334,7 +334,7 @@
(when (:debug project)
(System/setProperty "clojure.debug" "true"))
;; :dependencies are loaded the same way as plugins in eval-in-leiningen
(project/load-plugins project :dependencies)
(project/load-plugins project :dependencies :managed-dependencies)
(doseq [path (classpath/get-classpath project)]
(pomegranate/add-classpath path))
(doseq [opt (get-jvm-args project)

View file

@ -686,13 +686,14 @@
(def ^:private registered-wagon-files (atom #{}))
(defn load-plugins
([project key]
(when (seq (get project key))
([project dependencies-key managed-dependencies-key]
(when (seq (get project dependencies-key))
(ensure-dynamic-classloader)
(let [repos-project (update-in project [:repositories] meta-merge
(:plugin-repositories project))]
(classpath/resolve-dependencies key repos-project
:add-classpath? true)))
(classpath/resolve-managed-dependencies
dependencies-key managed-dependencies-key repos-project
:add-classpath? true)))
(doseq [wagon-file (-> (.getContextClassLoader (Thread/currentThread))
(.getResources "leiningen/wagons.clj")
(enumeration-seq))
@ -701,6 +702,7 @@
(aether/register-wagon-factory! hint (eval factory))
(swap! registered-wagon-files conj wagon-file))
project)
([project dependencies-key] (load-plugins project dependencies-key nil))
([project] (load-plugins project :plugins)))
(defn plugin-vars [project type]

View file

@ -2,7 +2,6 @@
(:use [clojure.test]
[leiningen.core.classpath])
(:require [clojure.java.io :as io]
[clojure.set :as set]
[leiningen.core.user :as user]
[leiningen.test.helper :as lthelper]
[leiningen.core.project :as project]))
@ -17,7 +16,8 @@
(defn m2-file [f]
(io/file (System/getProperty "user.home") ".m2" "repository" f))
(def project {:dependencies '[[org.clojure/clojure "1.3.0"]
(def project {:managed-dependencies '[[org.clojure/clojure "1.3.0"]]
:dependencies '[[org.clojure/clojure]
[ring/ring-core "1.0.0"
:exclusions [commons-codec]]]
:checkout-deps-shares [:source-paths :resource-paths
@ -38,7 +38,9 @@
(m2-file "ring/ring-core/1.0.0/ring-core-1.0.0.jar")
(m2-file (str "commons-fileupload/commons-fileupload/1.2.1/"
"commons-fileupload-1.2.1.jar"))}
(set (resolve-dependencies :dependencies project)))))
(set (resolve-managed-dependencies :dependencies
:managed-dependencies
project)))))
(deftest test-dependency-hierarchy
(doseq [f (reverse (file-seq (io/file (:root project))))]
@ -49,7 +51,9 @@
{[commons-fileupload "1.2.1"] nil
[commons-io "1.4"] nil
[javax.servlet/servlet-api "2.5"] nil}}
(dependency-hierarchy :dependencies project))))
(managed-dependency-hierarchy :dependencies
:managed-dependencies
project))))
(def directories
(vec (map lthelper/pathify

View file

@ -8,7 +8,8 @@
[leiningen.core.project :as project])
(:import (java.io File)))
(def project {:dependencies '[[org.clojure/clojure "1.3.0"]]
(def project {:managed-dependencies '[[org.clojure/clojure "1.3.0"]]
:dependencies '[[org.clojure/clojure]]
:root "/tmp/lein-sample-project"
:repositories project/default-repositories
:target-path "/tmp/lein-sample-project/target"

View file

@ -55,6 +55,22 @@
;; LWJGL stores natives in the root of the jar; this
;; :native-prefix will extract them.
:native-prefix ""]]
;; "Managed Dependencies" are a concept borrowed from maven pom files; see
;; https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Management
;; Managed dependencies allow you to specify a desired version number for a dependency
;; *if* the dependency exists (often transitively), but a managed dependency
;; will not actually cause the described artifact to be a dependency on its own.
;; This feature is most useful in combination with some other mechanism for
;; defining a "parent project"; e.g. you can have a "parent project" that specifies
;; managed dependencies for common libraries that you use frequently in your other
;; projects, and then the downstream/child projects can specify a normal dependency on
;; those libraries *without specifying a version number*, and thus will inherit
;; the version number from the parent. This provides a simpler means of keeping
;; common dependency versions in sync across a large number of clojure libraries.
;; For more info see ./doc/MANAGED_DEPS.md and https://github.com/achin/lein-parent
:managed-dependencies [[clj-time "0.12.0"]
[me.raynes/fs "1.4.6"]]
;; What to do in the case of version issues. Defaults to :ranges, which
;; warns when version ranges are present anywhere in the dependency tree,
;; but can be set to true to warn for both ranges and overrides, or :abort

View file

@ -2,13 +2,10 @@
"Download all dependencies."
(:require [leiningen.core.classpath :as classpath]
[leiningen.core.main :as main]
[leiningen.core.eval :as eval]
[leiningen.core.project :as project]
[leiningen.core.user :as user]
[leiningen.core.utils :as utils]
[cemerick.pomegranate.aether :as aether]
[clojure.pprint :as pp]
[clojure.java.io :as io])
[cemerick.pomegranate.aether :as aether])
(:import (org.sonatype.aether.resolution DependencyResolutionException)))
(defn- walk-deps
@ -69,8 +66,8 @@
(def tree-command
"A mapping from the tree-command to the dependency key it should print a tree
for."
{":tree" :dependencies
":plugin-tree" :plugins})
{":tree" [:dependencies :managed-dependencies]
":plugin-tree" [:plugins nil]})
@ -121,16 +118,22 @@ force them to be updated, use `lein -U $TASK`."
(let [project (project/merge-profiles
project
[{:pedantic? (quote ^:displace warn)}])
hierarchy (classpath/dependency-hierarchy
(tree-command command)
[dependencies-key managed-dependencies-key] (tree-command command)
hierarchy (classpath/managed-dependency-hierarchy
dependencies-key
managed-dependencies-key
project)]
(walk-deps hierarchy print-dep))
(= command ":verify")
(if (user/gpg-available?)
(walk-deps (classpath/dependency-hierarchy :dependencies project)
(walk-deps (classpath/managed-dependency-hierarchy
:dependencies
:managed-dependencies
project)
(partial verify project))
(main/abort (str "Could not verify - gpg not available.\n"
"See `lein help gpg` for how to setup gpg.")))
:else (classpath/resolve-dependencies :dependencies project))
:else (classpath/resolve-managed-dependencies
:dependencies :managed-dependencies project))
(catch DependencyResolutionException e
(main/abort (.getMessage e))))))

View file

@ -330,6 +330,7 @@
(project/unmerge-profiles profile-kws)
(project/merge-profiles [:test])
relativize)
managed-deps (:managed-dependencies test-project)
deps (:dependencies test-project)]
(list
[:project {:xsi:schemaLocation
@ -354,6 +355,8 @@
;; TODO: this results in lots of duplicate entries
(xml-tags :build [project test-project])
(xml-tags :repositories (:repositories project))
(xml-tags :dependencyManagement
(xml-tags :dependencies (distinct-key dep-key managed-deps)))
(xml-tags :dependencies (distinct-key dep-key deps))
(and (:pom-addition project) (:pom-addition project))]))))

View file

@ -177,7 +177,8 @@ be deactivated."
(let [whitelisted (select-keys project project/whitelist-keys)
project (-> (project/unmerge-profiles project [:default])
(merge whitelisted))
deps (->> (classpath/resolve-dependencies :dependencies project)
deps (->> (classpath/resolve-managed-dependencies
:dependencies :managed-dependencies project)
(filter #(.endsWith (.getName %) ".jar")))
jars (cons (io/file jar) deps)]
(write-components project jars out)))

View file

@ -1,13 +1,14 @@
(ns leiningen.test.deps
(:use [clojure.test]
[leiningen.deps]
[leiningen.test.helper :only [sample-project m2-dir native-project
[leiningen.test.helper :only [sample-project m2-dir m2-file native-project
managed-deps-project
delete-file-recursively]])
(:require [clojure.java.io :as io]
[leiningen.core.main :as main]
[leiningen.core.classpath :as classpath]
[leiningen.core.utils :as utils]
[leiningen.core.eval :as eval]))
[leiningen.core.eval :as eval]
[leiningen.core.classpath :as classpath]
[cemerick.pomegranate.aether :as aether]))
(deftest ^:online test-deps
(let [sample-deps [["rome" "0.9"] ["jdom" "1.0"]]]
@ -128,3 +129,77 @@
(set (for [f (rest (file-seq (io/file (first (eval/native-arch-paths
native-project)))))]
(.getName f))))))
(defn coordinates-match?
[dep1 dep2]
;; NOTE: there is a new function in the 0.3.1 release of pomegranate that
;; is useful here, but it is private. Calling it via the symbol dereference
;; for now, but might consider making it public upstream. Haven't done so
;; yet since it is only used for tests.
(#'aether/coordinates-match? dep1 dep2))
(deftest ^:online test-managed-deps
(let [is-clojure-dep? #(#{'org.clojure/clojure
'org.clojure/tools.nrepl}
(first %))
remove-clojure-deps #(remove is-clojure-dep? %)
managed-deps (remove-clojure-deps (:managed-dependencies managed-deps-project))
;; find deps from normal "deps" section which explicitly specify their
;; version number rather than inheriting it from managed-deps
versioned-unmanaged-deps (filter
(fn [dep]
(and (> (count dep) 1)
(string? (nth dep 1))
(not (is-clojure-dep? dep))))
(:dependencies managed-deps-project))
;; the list of final, used deps w/versions
merged-deps (remove-clojure-deps
(classpath/merge-versions-from-managed-coords
(:dependencies managed-deps-project)
(:managed-dependencies managed-deps-project)))
;; the list of deps from the managed deps section that aren't used
unused-managed-deps (-> (remove
(fn [dep]
(or (some (partial coordinates-match? dep) merged-deps)
;; special-casing to remove tools.reader, which is a common transitive dep
;; of two of our normal dependencies
(= 'org.clojure/tools.reader (first dep))))
managed-deps))
;; deps that have classifiers
classified-deps (filter
#(some #{:classifier} %)
merged-deps)]
;; make sure the sample data has some unmanaged deps, some unused managed deps,
;; and some classified deps, for completeness
(is (seq versioned-unmanaged-deps))
(is (seq unused-managed-deps))
(is (seq classified-deps))
;; delete all of the existing artifacts for merged deps
(doseq [[n v] merged-deps]
(delete-file-recursively (m2-dir n v) :silently))
;; delete all of the artifacts for the managed deps too
(doseq [[n v] managed-deps]
(delete-file-recursively (m2-dir n v) :silently))
;; delete all copies of tools.reader so we know that the managed dependency
;; for it is taking precedence
(delete-file-recursively (m2-dir 'org.clojure/tools.reader) :silently)
(deps managed-deps-project)
;; artifacts should be available for all merged deps
(doseq [[n v] merged-deps]
(is (.exists (m2-dir n v)) (str n " was not downloaded (missing dir '" (m2-dir n v) "').")))
;; artifacts should *not* have been downloaded for unused managed deps
(doseq [[n v] unused-managed-deps]
(is (not (.exists (m2-dir n v))) (str n " was unexpectedly downloaded (found unexpected dir '" (m2-dir n v) "').")))
;; artifacts with classifiers should be available
(doseq [[n v _ classifier] classified-deps]
(let [f (m2-file n v classifier)]
(is (.exists f) (str f " was not downloaded."))))
;; check tools.reader explicitly, since it is our special transitive dependency
(let [tools-reader-versions (into [] (.listFiles (m2-dir 'org.clojure/tools.reader)))]
(is (= 1 (count tools-reader-versions)))
(is (= (first tools-reader-versions) (m2-dir 'org.clojure/tools.reader
(->> managed-deps
(filter
(fn [dep] (= 'org.clojure/tools.reader (first dep))))
first
second)))))))

View file

@ -2,7 +2,8 @@
(:require [leiningen.core.project :as project]
[leiningen.core.user :as user]
[leiningen.core.test.helper :as helper]
[clojure.java.io :as io])
[clojure.java.io :as io]
[clojure.string :as str])
(:import (java.io ByteArrayOutputStream PrintStream FileDescriptor
FileOutputStream)))
@ -11,9 +12,16 @@
(def tmp-dir (System/getProperty "java.io.tmpdir"))
(defn m2-dir [n v]
(io/file local-repo
(if (string? n) n (or (namespace n) (name n))) (name n) v))
(defn m2-dir
([n]
(let [group (-> (if (string? n) n (or (namespace n) (name n)))
(str/replace "." "/"))]
(io/file local-repo group (name n))))
([n v]
(io/file (m2-dir n) v)))
(defn m2-file [n v classifier]
(io/file (m2-dir n v) (str (name n) "-" v "-" classifier ".jar")))
(defn read-test-project-with-user-profiles [name user-profiles]
(with-redefs [user/profiles (constantly user-profiles)]
@ -65,6 +73,8 @@
(def with-classifiers-project (read-test-project "with-classifiers"))
(def managed-deps-project (read-test-project "managed-deps"))
(defn abort-msg
"Catches main/abort thrown by calling f on its args and returns its error
message."

View file

@ -4,7 +4,8 @@
[leiningen.pom :only [make-pom pom snapshot?]]
[leiningen.core.user :as user]
[leiningen.test.helper
:only [sample-project sample-profile-meta-project]
:only [sample-project sample-profile-meta-project
managed-deps-project]
:as lthelper])
(:require [clojure.data.xml :as xml]
[leiningen.core.project :as project]
@ -352,3 +353,33 @@
(testing "Version containing anything else is not a snapshot "
(is (not (snapshot? {:version "foo"})))
(is (not (snapshot? nil)))))
(deftest test-managed-dependencies
(let [xml (xml/parse-str
(make-pom managed-deps-project))]
(testing "normal dependencies are written to pom properly"
(is (= ["org.clojure" "rome" "ring" "ring" "commons-codec" "commons-math"
"org.clojure" "org.clojure"]
(map #(first-in % [:dependency :groupId])
(deep-content xml [:project :dependencies]))))
(is (= ["clojure" "rome" "ring" "ring-codec" "commons-codec" "commons-math"
"tools.emitter.jvm" "tools.namespace"]
(map #(first-in % [:dependency :artifactId])
(deep-content xml [:project :dependencies]))))
(is (= [nil nil nil nil "1.6" nil "0.1.0-beta5" "0.3.0-alpha3"]
(map #(first-in % [:dependency :version])
(deep-content xml [:project :dependencies])))))
(testing "managed dependencies are written to pom properly"
(is (= ["org.clojure" "rome" "ring" "ring" "commons-math" "ring" "org.clojure"]
(map #(first-in % [:dependency :groupId])
(deep-content xml [:project :dependencyManagement :dependencies]))))
(is (= ["clojure" "rome" "ring" "ring-codec" "commons-math" "ring-defaults"
"tools.reader"]
(map #(first-in % [:dependency :artifactId])
(deep-content xml [:project :dependencyManagement :dependencies]))))
(is (= ["1.3.0" "0.9" "1.0.0" "1.0.1" "1.2" "0.2.1" "1.0.0-beta3"]
(map #(first-in % [:dependency :version])
(deep-content xml [:project :dependencyManagement :dependencies]))))
(is (= [nil nil nil nil "sources" nil nil]
(map #(first-in % [:dependency :classifier])
(deep-content xml [:project :dependencyManagement :dependencies])))))))

View file

@ -6,7 +6,8 @@
[clojure.xml :as xml]
[leiningen.test.helper :refer [sample-no-aot-project
uberjar-merging-project
provided-project]])
provided-project
managed-deps-project]])
(:import (java.io File FileOutputStream)
(java.util.zip ZipFile)))
@ -64,3 +65,10 @@
_ (uberjar provided-project)]
(is (= 1 (:exit (sh "java" "-jar" filename))))
(is (= 0 (:exit (sh "java" bootclasspath "-jar" filename))))))
(deftest test-uberjar-managed-dependencies
(uberjar managed-deps-project)
(let [filename (str "test_projects/managed-deps/target/"
"mgmt-0.99.0-SNAPSHOT-standalone.jar")
uberjar-file (File. filename)]
(is (= true (.exists uberjar-file)))))

View file

@ -0,0 +1,22 @@
(def clj-version "1.3.0")
(defproject mgmt "0.99.0-SNAPSHOT"
:description "A test project"
:managed-dependencies [[~(symbol "org.clojure" "clojure") ~clj-version]
[rome ~(str "0." "9")]
[ring/ring "1.0.0"]
[ring/ring-codec "1.0.1"]
[commons-math/commons-math "1.2" :classifier "sources"]
[ring/ring-defaults "0.2.1"]
[org.clojure/tools.reader "1.0.0-beta3"]]
:dependencies [[org.clojure/clojure]
[rome/rome nil]
[ring]
[ring/ring-codec nil :exclusions [commons-codec]]
[commons-codec "1.6"]
[commons-math nil :classifier "sources"]
[org.clojure/tools.emitter.jvm "0.1.0-beta5"] ; depends on tools.reader 0.8.5
[org.clojure/tools.namespace "0.3.0-alpha3"] ; depends on tools.reader 0.10.0
])