Merge pull request #2126 from cprice404/feature/master/2067-support-for-managed-dependencies
(#2067) add support for `managed-dependencies`
This commit is contained in:
commit
17fa738fd8
15 changed files with 379 additions and 49 deletions
105
doc/MANAGED_DEPS.md
Normal file
105
doc/MANAGED_DEPS.md
Normal 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.
|
|
@ -250,10 +250,12 @@
|
|||
|
||||
(def ^:private get-dependencies-memoized
|
||||
(memoize
|
||||
(fn [dependencies-key {:keys [repositories local-repo offline? update
|
||||
(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)))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -686,12 +686,13 @@
|
|||
(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
|
||||
(classpath/resolve-managed-dependencies
|
||||
dependencies-key managed-dependencies-key repos-project
|
||||
:add-classpath? true)))
|
||||
(doseq [wagon-file (-> (.getContextClassLoader (Thread/currentThread))
|
||||
(.getResources "leiningen/wagons.clj")
|
||||
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))))))
|
||||
|
|
|
@ -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))]))))
|
||||
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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)))))))
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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])))))))
|
|
@ -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)))))
|
22
test_projects/managed-deps/project.clj
Normal file
22
test_projects/managed-deps/project.clj
Normal 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
|
||||
])
|
Loading…
Reference in a new issue