```
Looks bleak. Assuming you've got Pomegranate on your classpath already, you can
do this though:
```clojure
=> (use '[cemerick.pomegranate :only (add-dependencies)])
nil
=> (add-dependencies :coordinates '[[incanter "1.2.3"]]
:repositories (merge cemerick.pomegranate.aether/maven-central
{"clojars" "https://clojars.org/repo"}))
;...add-dependencies returns full dependency graph...
=> (require '(incanter core stats charts))
nil
```
Now you can analyze and chart away, Incanter having been added to your runtime.
Note that `add-dependencies` may crunch along for a while — it may need to
download dependencies, so you're waiting on the network. All resolved
dependencies are stored in the default local repository (`~/.m2/repository`),
and if they are found there, then they are not downloaded.
The arguments to `add-dependencies` look like Leiningen-style notation, and they
are.
Please note that **there are a number of scenarios in which `add-dependencies`
will not work, or will not work as you'd expect**. Many of these are due to the
nature of JVM classloaders (e.g. adding jars containing conflicting versions of
a particular dependency will rarely end well), which Pomegranate does not
currently attempt to hide. Thus, `add-classpath` and `add-dependencies` should
be considered escape hatches to be used when necessary, rather than a regular
part of your development workflow.
#### `URLClassLoader` modifiability
Starting with pomegranate `1.0.0`, `java.net.URLClassLoader`s are not modifiable
by default. This is to accommodate new reflection policy starting with JDK 9
(which currently issues an ugly warning on reflective access to non-public
methods, but future releases will simply fail). If you are in a situation where
you are using pomegranate but do not have a `clojure.lang.DynamicClassLoader`
visible in your classloader hierarchy, you will need to explicitly enable the
`java.net.URLClassLoader` support in
[dynapath](https://github.com/tobias/dynapath) (upon which pomegranate relies
for such things).
## Status of Aether support
Pomegranate is being used by [Leiningen v2.x](https://leiningen.org) as
its sole dependency resolution library. This has prompted rapid
maturation of the scope and quality of Aether support. That said,
specific API points remain subject to change as we find the right
abstractions and conventions.
#### Supported features
* dependency resolution
* common dependency graph/hierarchy manipulation ops
* local installation
* remote deployment
* repository authentication for all of the above
* HTTP proxy configuration
* offline mode
* transfer listeners (with a sane Clojure fn veneer)
#### Not there yet
* repository listeners
* options to retrieve a single artifact (e.g. for obtaining source/javadoc)
* tests; there's halfway decent coverage, but nowhere near the kind of
comprehensive combinatorial testing that maven dependency resolution demands
## Changelog
See the `CHANGES.md` file at the top level of the repo.
## License
Copyright © 2011-2017 [Chas Emerick](https://cemerick.com) and all other
contributors.
Licensed under the EPL. (See the file epl-v10.html.)
pomegranate-clojure-1.0.0/epl-v10.html 0000664 0000000 0000000 00000031163 13177100761 0017534 0 ustar 00root root 0000000 0000000
Eclipse Public License - Version 1.0
Eclipse Public License - v 1.0
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR
DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a) in the case of the initial Contributor, the initial
code and documentation distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program
originate from and are distributed by that particular Contributor. A
Contribution 'originates' from a Contributor if it was added to the
Program by such Contributor itself or anyone acting on such
Contributor's behalf. Contributions do not include additions to the
Program which: (i) are separate modules of software distributed in
conjunction with the Program under their own license agreement, and (ii)
are not derivative works of the Program.
"Contributor" means any person or entity that distributes
the Program.
"Licensed Patents" mean patent claims licensable by a
Contributor which are necessarily infringed by the use or sale of its
Contribution alone or when combined with the Program.
"Program" means the Contributions distributed in accordance
with this Agreement.
"Recipient" means anyone who receives the Program under
this Agreement, including all Contributors.
2. GRANT OF RIGHTS
a) Subject to the terms of this Agreement, each
Contributor hereby grants Recipient a non-exclusive, worldwide,
royalty-free copyright license to reproduce, prepare derivative works
of, publicly display, publicly perform, distribute and sublicense the
Contribution of such Contributor, if any, and such derivative works, in
source code and object code form.
b) Subject to the terms of this Agreement, each
Contributor hereby grants Recipient a non-exclusive, worldwide,
royalty-free patent license under Licensed Patents to make, use, sell,
offer to sell, import and otherwise transfer the Contribution of such
Contributor, if any, in source code and object code form. This patent
license shall apply to the combination of the Contribution and the
Program if, at the time the Contribution is added by the Contributor,
such addition of the Contribution causes such combination to be covered
by the Licensed Patents. The patent license shall not apply to any other
combinations which include the Contribution. No hardware per se is
licensed hereunder.
c) Recipient understands that although each Contributor
grants the licenses to its Contributions set forth herein, no assurances
are provided by any Contributor that the Program does not infringe the
patent or other intellectual property rights of any other entity. Each
Contributor disclaims any liability to Recipient for claims brought by
any other entity based on infringement of intellectual property rights
or otherwise. As a condition to exercising the rights and licenses
granted hereunder, each Recipient hereby assumes sole responsibility to
secure any other intellectual property rights needed, if any. For
example, if a third party patent license is required to allow Recipient
to distribute the Program, it is Recipient's responsibility to acquire
that license before distributing the Program.
d) Each Contributor represents that to its knowledge it
has sufficient copyright rights in its Contribution, if any, to grant
the copyright license set forth in this Agreement.
3. REQUIREMENTS
A Contributor may choose to distribute the Program in object code
form under its own license agreement, provided that:
a) it complies with the terms and conditions of this
Agreement; and
b) its license agreement:
i) effectively disclaims on behalf of all Contributors
all warranties and conditions, express and implied, including warranties
or conditions of title and non-infringement, and implied warranties or
conditions of merchantability and fitness for a particular purpose;
ii) effectively excludes on behalf of all Contributors
all liability for damages, including direct, indirect, special,
incidental and consequential damages, such as lost profits;
iii) states that any provisions which differ from this
Agreement are offered by that Contributor alone and not by any other
party; and
iv) states that source code for the Program is available
from such Contributor, and informs licensees how to obtain it in a
reasonable manner on or through a medium customarily used for software
exchange.
When the Program is made available in source code form:
a) it must be made available under this Agreement; and
b) a copy of this Agreement must be included with each
copy of the Program.
Contributors may not remove or alter any copyright notices contained
within the Program.
Each Contributor must identify itself as the originator of its
Contribution, if any, in a manner that reasonably allows subsequent
Recipients to identify the originator of the Contribution.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain
responsibilities with respect to end users, business partners and the
like. While this license is intended to facilitate the commercial use of
the Program, the Contributor who includes the Program in a commercial
product offering should do so in a manner which does not create
potential liability for other Contributors. Therefore, if a Contributor
includes the Program in a commercial product offering, such Contributor
("Commercial Contributor") hereby agrees to defend and
indemnify every other Contributor ("Indemnified Contributor")
against any losses, damages and costs (collectively "Losses")
arising from claims, lawsuits and other legal actions brought by a third
party against the Indemnified Contributor to the extent caused by the
acts or omissions of such Commercial Contributor in connection with its
distribution of the Program in a commercial product offering. The
obligations in this section do not apply to any claims or Losses
relating to any actual or alleged intellectual property infringement. In
order to qualify, an Indemnified Contributor must: a) promptly notify
the Commercial Contributor in writing of such claim, and b) allow the
Commercial Contributor to control, and cooperate with the Commercial
Contributor in, the defense and any related settlement negotiations. The
Indemnified Contributor may participate in any such claim at its own
expense.
For example, a Contributor might include the Program in a commercial
product offering, Product X. That Contributor is then a Commercial
Contributor. If that Commercial Contributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Contributor's responsibility
alone. Under this section, the Commercial Contributor would have to
defend claims against the other Contributors related to those
performance claims and warranties, and if a court requires any other
Contributor to pay any damages as a result, the Commercial Contributor
must pay those damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION,
ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
responsible for determining the appropriateness of using and
distributing the Program and assumes all risks associated with its
exercise of rights under this Agreement , including but not limited to
the risks and costs of program errors, compliance with applicable laws,
damage to or loss of data, programs or equipment, and unavailability or
interruption of operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. GENERAL
If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further action
by the parties hereto, such provision shall be reformed to the minimum
extent necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against any entity
(including a cross-claim or counterclaim in a lawsuit) alleging that the
Program itself (excluding combinations of the Program with other
software or hardware) infringes such Recipient's patent(s), then such
Recipient's rights granted under Section 2(b) shall terminate as of the
date such litigation is filed.
All Recipient's rights under this Agreement shall terminate if it
fails to comply with any of the material terms or conditions of this
Agreement and does not cure such failure in a reasonable period of time
after becoming aware of such noncompliance. If all Recipient's rights
under this Agreement terminate, Recipient agrees to cease use and
distribution of the Program as soon as reasonably practicable. However,
Recipient's obligations under this Agreement and any licenses granted by
Recipient relating to the Program shall continue and survive.
Everyone is permitted to copy and distribute copies of this
Agreement, but in order to avoid inconsistency the Agreement is
copyrighted and may only be modified in the following manner. The
Agreement Steward reserves the right to publish new versions (including
revisions) of this Agreement from time to time. No one other than the
Agreement Steward has the right to modify this Agreement. The Eclipse
Foundation is the initial Agreement Steward. The Eclipse Foundation may
assign the responsibility to serve as the Agreement Steward to a
suitable separate entity. Each new version of the Agreement will be
given a distinguishing version number. The Program (including
Contributions) may always be distributed subject to the version of the
Agreement under which it was received. In addition, after a new version
of the Agreement is published, Contributor may elect to distribute the
Program (including its Contributions) under the new version. Except as
expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
rights or licenses to the intellectual property of any Contributor under
this Agreement, whether expressly, by implication, estoppel or
otherwise. All rights in the Program not expressly granted under this
Agreement are reserved.
This Agreement is governed by the laws of the State of New York and
the intellectual property laws of the United States of America. No party
to this Agreement will bring a legal action under this Agreement more
than one year after the cause of action arose. Each party waives its
rights to a jury trial in any resulting litigation.
pomegranate-clojure-1.0.0/pom.xml 0000664 0000000 0000000 00000007605 13177100761 0017003 0 ustar 00root root 0000000 0000000
4.0.0
com.cemerick
pomegranate
1.0.0
pomegranate
http://github.com/cemerick/pomegranate
org.clojure
pom.contrib
0.0.20
Chas Emerick
http://cemerick.com
cemerick@snowtide.com
-5
scm:git:git@github.com:cemerick/pomegranate.git
scm:git:git@github.com:cemerick/pomegranate.git
git@github.com:cemerick/pomegranate.git
1.3.0
3.5.0
1.0.3
2.12
org.apache.maven
maven-resolver-provider
${mavenVersion}
org.apache.maven.resolver
maven-resolver-transport-file
${resolverVersion}
org.apache.maven.resolver
maven-resolver-transport-http
${resolverVersion}
org.apache.maven.resolver
maven-resolver-transport-wagon
${resolverVersion}
org.apache.maven.resolver
maven-resolver-connector-basic
${resolverVersion}
org.tcrawley
dynapath
1.0.0
org.apache.maven.wagon
wagon-provider-api
${wagonVersion}
org.apache.maven.wagon
wagon-http
${wagonVersion}
org.apache.maven.wagon
wagon-ssh
${wagonVersion}
true
org.apache.httpcomponents
httpclient
4.5.3
org.slf4j
slf4j-simple
1.7.22
test
src/main/clojure
pomegranate-clojure-1.0.0/src/ 0000775 0000000 0000000 00000000000 13177100761 0016245 5 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/src/main/ 0000775 0000000 0000000 00000000000 13177100761 0017171 5 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/src/main/clojure/ 0000775 0000000 0000000 00000000000 13177100761 0020634 5 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/src/main/clojure/cemerick/ 0000775 0000000 0000000 00000000000 13177100761 0022416 5 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/src/main/clojure/cemerick/pomegranate.clj 0000664 0000000 0000000 00000012741 13177100761 0025417 0 ustar 00root root 0000000 0000000 (ns cemerick.pomegranate
(:import (clojure.lang DynamicClassLoader)
(java.net URL URLClassLoader))
(:require [clojure.java.io :as io]
[cemerick.pomegranate.aether :as aether]
[dynapath.util :as dp])
(:refer-clojure :exclude (add-classpath)))
;; call-method pulled from clojure.contrib.reflect, (c) 2010 Stuart Halloway & Contributors
(defn- call-method
"Calls a private or protected method.
params is a vector of classes which correspond to the arguments to
the method e
obj is nil for static methods, the instance object otherwise.
The method-name is given a symbol or a keyword (something Named)."
[klass method-name params obj & args]
(-> klass (.getDeclaredMethod (name method-name)
(into-array Class params))
(doto (.setAccessible true))
(.invoke obj (into-array Object args))))
(defn classloader-hierarchy
"Returns a seq of classloaders, with the tip of the hierarchy first.
Uses the current thread context ClassLoader as the tip ClassLoader
if one is not provided."
([] (classloader-hierarchy (.. Thread currentThread getContextClassLoader)))
([tip]
(->> tip
(iterate #(.getParent %))
(take-while boolean))))
(defn modifiable-classloader?
"Returns true iff the given ClassLoader is of a type that satisfies
the dynapath.dynamic-classpath/DynamicClasspath protocol, and it can
be modified."
[cl]
(dp/addable-classpath? cl))
(defn add-classpath
"A corollary to the (deprecated) `add-classpath` in clojure.core. This implementation
requires a java.io.File or String path to a jar file or directory, and will attempt
to add that path to the right classloader (with the search rooted at the current
thread's context classloader)."
([jar-or-dir classloader]
(if-not (dp/add-classpath-url classloader (.toURL (.toURI (io/file jar-or-dir))))
(throw (IllegalStateException. (str classloader " is not a modifiable classloader")))))
([jar-or-dir]
(let [classloaders (classloader-hierarchy)]
(if-let [cl (last (filter modifiable-classloader? classloaders))]
(add-classpath jar-or-dir cl)
(throw (IllegalStateException. (str "Could not find a suitable classloader to modify from "
classloaders)))))))
(defn add-dependencies
"Resolves a set of dependencies, optionally against a set of additional Maven
repositories, and adds all of the resulting artifacts (jar files) to the
current runtime via `add-classpath`:
(add-dependencies :classloader your-classloader
:coordinates '[[incanter \"1.2.3\"]]
:repositories (merge cemerick.pomegranate.aether/maven-central
{\"clojars\" \"https://clojars.org/repo\"}))
Note that the `:classloader` kwarg is optional; if not provided then resolved
dependencies will be added to the closest modifiable classloader in the
current thread's hierarchy, as per `add-classpath`.
Otherwise, acceptable arguments are the same as those for
`cemerick.pomegranate.aether/resolve-dependencies`; returns the dependency graph
returned from that function.
Note that Maven central is used as the sole repository if none are specified.
If :repositories are provided, then you must merge in the `maven-central` map from
the cemerick.pomegranate.aether namespace yourself."
[& args]
(let [classloader (-> (apply hash-map args)
:classloader
; replace with some-> when we bump the clojure dep
(#(when % [%])))
deps (apply aether/resolve-dependencies args)]
(doseq [artifact-file (aether/dependency-files deps)]
(apply add-classpath artifact-file classloader))
deps))
(defn get-classpath
"Returns the effective classpath (i.e. _not_ the value of
(System/getProperty \"java.class.path\") as a seq of URL strings.
Produces the classpath from all classloaders by default, or from a
collection of classloaders if provided. This allows you to easily look
at subsets of the current classloader hierarchy, e.g.:
(get-classpath (drop 2 (classloader-hierarchy)))"
([classloaders]
(->> (reverse classloaders)
(mapcat #(dp/classpath-urls %))
(map str)))
([] (get-classpath (classloader-hierarchy))))
(defn classloader-resources
"Returns a sequence of [classloader url-seq] pairs representing all
of the resources of the specified name on the classpath of each
classloader. If no classloaders are given, uses the
classloader-heirarchy, in which case the order of pairs will be
such that the first url mentioned will in most circumstances match
what clojure.java.io/resource returns."
([classloaders resource-name]
(for [classloader (reverse classloaders)]
[classloader (enumeration-seq
(.getResources ^ClassLoader classloader resource-name))]))
([resource-name] (classloader-resources (classloader-hierarchy) resource-name)))
(defn resources
"Returns a sequence of URLs representing all of the resources of the
specified name on the effective classpath. This can be useful for
finding name collisions among items on the classpath. In most
circumstances, the first of the returned sequence will be the same
as what clojure.java.io/resource returns."
([classloaders resource-name]
(distinct (mapcat second (classloader-resources classloaders resource-name))))
([resource-name] (resources (classloader-hierarchy) resource-name)))
pomegranate-clojure-1.0.0/src/main/clojure/cemerick/pomegranate/ 0000775 0000000 0000000 00000000000 13177100761 0024720 5 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/src/main/clojure/cemerick/pomegranate/aether.clj 0000664 0000000 0000000 00000114745 13177100761 0026676 0 ustar 00root root 0000000 0000000 (ns cemerick.pomegranate.aether
(:refer-clojure :exclude [type proxy])
(:require [clojure.java.io :as io]
clojure.set
[clojure.string :as str]
clojure.stacktrace)
(:import (org.eclipse.aether RepositorySystem)
(org.eclipse.aether.transport.wagon WagonTransporterFactory
WagonProvider)
(org.eclipse.aether.transport.file FileTransporterFactory)
(org.eclipse.aether.transfer TransferListener)
(org.eclipse.aether.artifact Artifact)
(org.eclipse.aether.spi.connector RepositoryConnectorFactory)
(org.eclipse.aether.spi.connector.transport TransporterFactory)
(org.eclipse.aether.repository Proxy Authentication
RepositoryPolicy LocalRepository RemoteRepository RemoteRepository$Builder
MirrorSelector)
(org.eclipse.aether.util.repository DefaultProxySelector AuthenticationBuilder)
(org.eclipse.aether.graph Dependency Exclusion DependencyNode)
(org.eclipse.aether.collection CollectRequest)
(org.eclipse.aether.resolution DependencyRequest ArtifactRequest
ArtifactResult VersionRequest)
(org.eclipse.aether.artifact DefaultArtifact ArtifactProperties)
(org.eclipse.aether.util.artifact SubArtifact)
(org.eclipse.aether.deployment DeployRequest)
(org.eclipse.aether.installation InstallRequest)
(org.eclipse.aether.util.version GenericVersionScheme)
(org.eclipse.aether.connector.basic BasicRepositoryConnectorFactory)
(org.eclipse.aether.impl DefaultServiceLocator$ErrorHandler)
(org.apache.maven.repository.internal MavenRepositorySystemUtils)))
(def ^{:private true} default-local-repo
(io/file (System/getProperty "user.home") ".m2" "repository"))
(def maven-central {"central" "https://repo1.maven.org/maven2/"})
;; Using HttpWagon (which uses apache httpclient) because the "LightweightHttpWagon"
;; (which just uses JDK HTTP) reliably flakes if you attempt to resolve SNAPSHOT
;; artifacts from an HTTPS password-protected repository (like a nexus instance)
;; when other un-authenticated repositories are included in the resolution.
;; My theory is that the JDK HTTP impl is screwing up connection pooling or something,
;; and reusing the same connection handle for the HTTPS repo as it used for e.g.
;; central, without updating the authentication info.
;; In any case, HttpWagon is what Maven 3 uses, and it works.
(def ^{:private true} wagon-factories
(atom {"https" #(org.apache.maven.wagon.providers.http.HttpWagon.)
"http" #(throw (Exception. "Tried to use insecure HTTP repository."))}))
(defn register-wagon-factory!
"Registers a new no-arg factory function for the given scheme. The function
must return an implementation of org.apache.maven.wagon.Wagon."
[scheme factory-fn]
(swap! wagon-factories (fn [m]
(when-let [fn (and (not= scheme "http") (m scheme))]
(println (format "Warning: replacing existing support for %s repositories (%s) with %s" scheme fn factory-fn)))
(assoc m scheme factory-fn))))
(deftype PomegranateWagonProvider []
WagonProvider
(release [_ wagon])
(lookup [_ role-hint]
(when-let [f (get @wagon-factories role-hint)]
(try
(f)
(catch Exception e
(clojure.stacktrace/print-cause-trace e)
(throw e))))))
(deftype TransferListenerProxy [listener-fn]
TransferListener
(transferCorrupted [_ e] (listener-fn e))
(transferFailed [_ e] (listener-fn e))
(transferInitiated [_ e] (listener-fn e))
(transferProgressed [_ e] (listener-fn e))
(transferStarted [_ e] (listener-fn e))
(transferSucceeded [_ e] (listener-fn e)))
(defn- transfer-event
[^org.eclipse.aether.transfer.TransferEvent e]
;; INITIATED, STARTED, PROGRESSED, CORRUPTED, SUCCEEDED, FAILED
{:type (-> e .getType .name str/lower-case keyword)
;; :get :put
:method (-> e .getRequestType str/lower-case keyword)
:transferred (.getTransferredBytes e)
:error (.getException e)
:data-buffer (.getDataBuffer e)
:data-length (.getDataLength e)
:resource (let [r (.getResource e)]
{:repository (.getRepositoryUrl r)
:name (.getResourceName r)
:file (.getFile r)
:size (.getContentLength r)
:transfer-start-time (.getTransferStartTime r)
:trace (.getTrace r)})})
(defn- default-listener-fn
[{:keys [type method transferred resource error] :as evt}]
(let [{:keys [name size repository transfer-start-time]} resource]
(case type
:started (do
(print (case method :get "Retrieving" :put "Sending")
name
(if (neg? size)
""
(format "(%sk)" (Math/round (double (max 1 (/ size 1024)))))))
(when (< 70 (+ 10 (count name) (count repository)))
(println) (print " "))
(println (case method :get "from" :put "to") repository))
(:corrupted :failed) (when error (println (.getMessage error)))
nil)))
(defn- repository-system
[]
(let [error-handler (clojure.core/proxy [DefaultServiceLocator$ErrorHandler] []
(serviceCreationFailed [type-clazz impl-clazz ^Throwable e]
(clojure.stacktrace/print-cause-trace e)))]
(.getService
(doto (MavenRepositorySystemUtils/newServiceLocator)
(.setService TransporterFactory WagonTransporterFactory)
(.setService WagonProvider PomegranateWagonProvider)
(.addService RepositoryConnectorFactory BasicRepositoryConnectorFactory)
(.addService TransporterFactory FileTransporterFactory)
(.setErrorHandler error-handler))
RepositorySystem)))
(defn- construct-transfer-listener
[transfer-listener]
(cond
(instance? TransferListener transfer-listener) transfer-listener
(= transfer-listener :stdout)
(TransferListenerProxy. (comp default-listener-fn transfer-event))
(fn? transfer-listener)
(TransferListenerProxy. (comp transfer-listener transfer-event))
:else (TransferListenerProxy. (fn [_]))))
(defn repository-session
[{:keys [repository-system local-repo offline? transfer-listener mirror-selector]}]
(let [session (org.apache.maven.repository.internal.MavenRepositorySystemUtils/newSession)]
(doto session
(.setLocalRepositoryManager (.newLocalRepositoryManager
repository-system
session
(LocalRepository.
(io/file (or local-repo default-local-repo)))))
(.setMirrorSelector mirror-selector)
(.setOffline (boolean offline?))
(.setTransferListener (construct-transfer-listener transfer-listener)))))
(def update-policies {:daily RepositoryPolicy/UPDATE_POLICY_DAILY
:always RepositoryPolicy/UPDATE_POLICY_ALWAYS
:never RepositoryPolicy/UPDATE_POLICY_NEVER})
(def checksum-policies {:fail RepositoryPolicy/CHECKSUM_POLICY_FAIL
:ignore RepositoryPolicy/CHECKSUM_POLICY_IGNORE
:warn RepositoryPolicy/CHECKSUM_POLICY_WARN})
(defn- policy
[policy-settings enabled?]
(RepositoryPolicy.
(boolean enabled?)
(update-policies (:update policy-settings :daily))
(checksum-policies (:checksum policy-settings :fail))))
(defn- set-policies
[repo-builder settings]
(doto repo-builder
(.setSnapshotPolicy (policy settings (:snapshots settings true)))
(.setReleasePolicy (policy settings (:releases settings true)))))
(defn- authentication
[{:keys [username password passphrase private-key-file] :as settings}]
(-> (AuthenticationBuilder.)
(.addUsername username)
(.addPassword password)
(.addPrivateKey private-key-file passphrase)
.build))
(defn- set-authentication
[repo-builder {:keys [username password passphrase private-key-file] :as settings}]
(if (or username password private-key-file passphrase)
(.setAuthentication repo-builder (authentication settings))
repo-builder))
(defn- set-proxy
[repo-builder {:keys [type host port non-proxy-hosts]
:or {type "http"}
:as proxy}]
(if (and host port)
(let [prx-sel (doto (DefaultProxySelector.)
(.add (Proxy. type host port (authentication proxy))
non-proxy-hosts))
prx (.getProxy prx-sel (.build repo-builder))] ; ugg.
;; Don't know how to get around "building" the repo for this
(.setProxy repo-builder prx))
repo-builder))
(defn make-repository
"Produces an Aether RemoteRepository instance from Pomegranate-style repository information"
[[id settings] proxy]
(let [settings-map (if (string? settings)
{:url settings}
settings)]
(.build
(doto (RemoteRepository$Builder. (and id (name id))
(:type settings-map "default")
(str (:url settings-map)))
(set-policies settings-map)
(set-authentication settings-map)
(set-proxy proxy)))))
(defn- group
[group-artifact]
(or (namespace group-artifact) (name group-artifact)))
(defn- coordinate-string
"Produces a coordinate string with a format of
:[:[:]]:>
given a lein-style dependency spec. :extension defaults to jar."
[[group-artifact version & {:keys [classifier extension] :or {extension "jar"}}]]
(->> [(group group-artifact) (name group-artifact) extension classifier version]
(remove nil?)
(interpose \:)
(apply str)))
(defn- exclusion
[[group-artifact & {:as opts}]]
(Exclusion.
(group group-artifact)
(name group-artifact)
(:classifier opts "*")
(:extension opts "*")))
(defn- normalize-exclusion-spec [spec]
(if (symbol? spec)
[spec]
spec))
(defn- artifact
[[group-artifact version & {:keys [scope optional exclusions]} :as dep-spec]]
(DefaultArtifact. (coordinate-string dep-spec)))
(defn dependency
"Produces an Aether Dependency instance from Pomegranate-style dependency information"
[[group-artifact version & {:keys [scope optional exclusions]
:as opts
:or {scope "compile"
optional false}}
:as dep-spec]]
(Dependency. (artifact dep-spec)
scope
optional
(map (comp exclusion normalize-exclusion-spec) exclusions)))
(declare dep-spec*)
(defn- exclusion-spec
"Given an Aether Exclusion, returns a lein-style exclusion vector with the
:exclusion in its metadata."
[^Exclusion ex]
(with-meta (-> ex bean dep-spec*) {:exclusion ex}))
(defn- dep-spec
"Given an Aether Dependency, returns a lein-style dependency vector with the
:dependency and its corresponding artifact's :file in its metadata."
[^Dependency dep]
(let [artifact (.getArtifact dep)]
(-> (merge (bean dep) (bean artifact))
dep-spec*
(with-meta {:dependency dep :file (.getFile artifact)}))))
(defn- dep-spec*
"Base function for producing lein-style dependency spec vectors for dependencies
and exclusions."
[{:keys [groupId artifactId version classifier extension scope optional exclusions]
:or {version nil
scope "compile"
optional false
exclusions nil}}]
(let [group-artifact (apply symbol (if (= groupId artifactId)
[artifactId]
[groupId artifactId]))]
(vec (concat [group-artifact]
(when version [version])
(when (and (seq classifier)
(not= "*" classifier))
[:classifier classifier])
(when (and (seq extension)
(not (#{"*" "jar"} extension)))
[:extension extension])
(when optional [:optional true])
(when (not= scope "compile")
[:scope scope])
(when (seq exclusions)
[:exclusions (vec (map exclusion-spec exclusions))])))))
(defn- create-artifact
[files artifact]
(if-let [file (get files artifact)]
(-> (coordinate-string artifact)
DefaultArtifact.
(.setFile (io/file file)))
(throw (IllegalArgumentException. (str "No file provided for artifact " artifact)))))
(defn deploy-artifacts
"Deploy the artifacts kwarg to the repository kwarg.
:files - map from artifact vectors to file paths or java.io.File objects
where the file to be deployed for each artifact is to be found
An artifact vector is e.g.
'[group/artifact \"1.0.0\"] or
'[group/artifact \"1.0.0\" :extension \"pom\"].
All artifacts should have the same version and group and artifact IDs
:repository - {name url} | {name settings}
settings:
:url - URL of the repository
:snapshots - use snapshots versions? (default true)
:releases - use release versions? (default true)
:username - username to log in with
:password - password to log in with
:passphrase - passphrase to log in wth
:private-key-file - private key file to log in with
:update - :daily (default) | :always | :never
:checksum - :fail (default) | :ignore | :warn
:local-repo - path to the local repository (defaults to ~/.m2/repository)
:transfer-listener - same as provided to resolve-dependencies
:proxy - proxy configuration, can be nil, the host scheme and type must match
:host - proxy hostname
:type - http (default) | http | https
:port - proxy port
:non-proxy-hosts - The list of hosts to exclude from proxying, may be null
:username - username to log in with, may be null
:password - password to log in with, may be null
:passphrase - passphrase to log in wth, may be null
:private-key-file - private key file to log in with, may be null"
[& {:keys [files repository local-repo transfer-listener proxy repository-session-fn]}]
(when (empty? files)
(throw (IllegalArgumentException. "Must provide valid :files to deploy-artifacts")))
(when (->> (keys files)
(map (fn [[ga v]] [(if (namespace ga) ga (symbol (str ga) (str ga))) v]))
set
count
(< 1))
(throw (IllegalArgumentException.
(str "Provided artifacts have varying version, group, or artifact IDs: " (keys files)))))
(let [system (repository-system)
session ((or repository-session-fn
repository-session)
{:repository-system system
:local-repo local-repo
:offline? false
:transfer-listener transfer-listener})]
(.deploy system session
(doto (DeployRequest.)
(.setArtifacts (vec (map (partial create-artifact files) (keys files))))
(.setRepository (first (map #(make-repository % proxy) repository)))))))
(defn install-artifacts
"Deploy the file kwarg using the coordinates kwarg to the repository kwarg.
:files - same as with deploy-artifacts
:local-repo - path to the local repository (defaults to ~/.m2/repository)
:transfer-listener - same as provided to resolve-dependencies"
[& {:keys [files local-repo transfer-listener repository-session-fn]}]
(let [system (repository-system)
session ((or repository-session-fn
repository-session)
{:repository-system system
:local-repo local-repo
:offline? false
:transfer-listener transfer-listener})]
(.install system session
(doto (InstallRequest.)
(.setArtifacts (vec (map (partial create-artifact files) (keys files))))))))
(defn- artifacts-for
"Takes a coordinates map, an a map from partial coordinates to "
[coordinates file-map]
(zipmap (map (partial into coordinates) (keys file-map)) (vals file-map)))
(defn- optional-artifact
"Takes a coordinates map, an a map from partial coordinates to "
[artifact-coords path]
(when path {artifact-coords path}))
(defn deploy
"Deploy the jar-file kwarg using the pom-file kwarg and coordinates
kwarg to the repository kwarg.
:coordinates - [group/name \"version\"]
:artifact-map - a map from partial coordinates to file path or File
:jar-file - a file pointing to the jar
:pom-file - a file pointing to the pom
:repository - {name url} | {name settings}
settings:
:url - URL of the repository
:snapshots - use snapshots versions? (default true)
:releases - use release versions? (default true)
:username - username to log in with
:password - password to log in with
:passphrase - passphrase to log in wth
:private-key-file - private key file to log in with
:update - :daily (default) | :always | :never
:checksum - :fail (default) | :ignore | :warn
:local-repo - path to the local repository (defaults to ~/.m2/repository)
:transfer-listener - same as provided to resolve-dependencies
:proxy - proxy configuration, can be nil, the host scheme and type must match
:host - proxy hostname
:type - http (default) | http | https
:port - proxy port
:non-proxy-hosts - The list of hosts to exclude from proxying, may be null
:username - username to log in with, may be null
:password - password to log in with, may be null
:passphrase - passphrase to log in wth, may be null
:private-key-file - private key file to log in with, may be null"
[& {:keys [coordinates artifact-map jar-file pom-file] :as opts}]
(when (empty? coordinates)
(throw
(IllegalArgumentException. "Must provide valid :coordinates to deploy")))
(apply deploy-artifacts
(apply concat (assoc opts
:files (artifacts-for
coordinates
(merge
artifact-map
(optional-artifact [:extension "pom"] pom-file)
(optional-artifact [] jar-file)))))))
(defn install
"Install the artifacts specified by the jar-file or file-map and pom-file
kwargs using the coordinates kwarg.
:coordinates - [group/name \"version\"]
:artifact-map - a map from partial coordinates to file path or File
:jar-file - a file pointing to the jar
:pom-file - a file pointing to the pom
:local-repo - path to the local repository (defaults to ~/.m2/repository)
:transfer-listener - same as provided to resolve-dependencies"
[& {:keys [coordinates artifact-map jar-file pom-file] :as opts}]
(when (empty? coordinates)
(throw
(IllegalArgumentException. "Must provide valid :coordinates to install")))
(apply install-artifacts
(apply concat (assoc opts
:files (artifacts-for
coordinates
(merge
artifact-map
(optional-artifact [:extension "pom"] pom-file)
(optional-artifact [] jar-file)))))))
(defn- dependency-graph
([node]
(reduce (fn [g ^DependencyNode n]
(if-let [dep (.getDependency n)]
(update-in g [(dep-spec dep)]
clojure.set/union
(->> (.getChildren n)
(map #(.getDependency %))
(map dep-spec)
set))
g))
{}
(tree-seq (constantly true)
#(seq (.getChildren %))
node))))
(defn- mirror-selector-fn
"Default mirror selection function. The first argument should be a map
like that described as the :mirrors argument in resolve-dependencies.
The second argument should be a repository spec, also as described in
resolve-dependencies. Will return the mirror spec that matches the
provided repository spec."
[mirrors {:keys [name url snapshots releases]}]
(let [mirrors (filter (fn [[matcher mirror-spec]]
(or
(and (string? matcher) (or (= matcher name) (= matcher url)))
(and (instance? java.util.regex.Pattern matcher)
(or (re-matches matcher name) (re-matches matcher url)))))
mirrors)]
(case (count mirrors)
0 nil
1 (-> mirrors first second)
(if (some nil? (map second mirrors))
;; wildcard override
nil
(throw (IllegalArgumentException.
(str "Multiple mirrors configured to match repository " {name url} ": "
(into {} (map #(update-in % [1] select-keys [:name :url]) mirrors)))))))))
(defn- mirror-selector
"Returns a MirrorSelector that delegates matching of mirrors to given remote repositories
to the provided function. Any returned repository specifications are turned into
RemoteRepository instances, and configured to use the provided proxy."
[mirror-selector-fn proxy]
(reify MirrorSelector
(getMirror [_ repo]
(let [repo-spec {:name (.getId repo)
:url (.getUrl repo)
:snapshots (-> repo (.getPolicy true) .isEnabled)
:releases (-> repo (.getPolicy false) .isEnabled)}
{:keys [name repo-manager content-type] :as mirror-spec}
(mirror-selector-fn repo-spec)]
(when-let [mirror (and mirror-spec (make-repository [name mirror-spec] proxy))]
(-> (RemoteRepository$Builder. mirror)
(.setMirroredRepositories [repo])
(.setRepositoryManager (boolean repo-manager))
(.setContentType (or content-type "default"))
(.build)))))))
(defn resolve-artifacts*
"Resolves artifacts for the coordinates kwarg, using repositories from the
`:repositories` kwarg.
Retrieval of dependencies can be disabled by providing `:retrieve false` as a
kwarg.
Returns an sequence of either `org.eclipse.aether.VersionResult`
if `:retrieve false`, or `org.eclipse.aether.ArtifactResult` if
`:retrieve true` (the default).
If you don't want to mess with the Aether implementation classes, then use
`resolve-artifacts` instead.
:coordinates - [[group/name \"version\" & settings] ..]
settings:
:extension - the maven extension (type) to require
:classifier - the maven classifier to require
:scope - the maven scope for the dependency (default \"compile\")
:optional - is the dependency optional? (default \"false\")
:exclusions - which sub-dependencies to skip : [group/name & settings]
settings:
:classifier (default \"*\")
:extension (default \"*\")
:repositories - {name url ..} | {name settings ..}
(defaults to {\"central\" \"https://repo1.maven.org/maven2/\"}
settings:
:url - URL of the repository
:snapshots - use snapshots versions? (default true)
:releases - use release versions? (default true)
:username - username to log in with
:password - password to log in with
:passphrase - passphrase to log in wth
:private-key-file - private key file to log in with
:update - :daily (default) | :always | :never
:checksum - :fail (default) | :ignore | :warn
:local-repo - path to the local repository (defaults to ~/.m2/repository)
:offline? - if true, no remote repositories will be contacted
:transfer-listener - the transfer listener that will be notifed of dependency
resolution and deployment events.
Can be:
- nil (the default), i.e. no notification of events
- :stdout, corresponding to a default listener implementation that writes
notifications and progress indicators to stdout, suitable for an
interactive console program
- a function of one argument, which will be called with a map derived from
each event.
- an instance of org.eclipse.aether.transfer.TransferListener
:proxy - proxy configuration, can be nil, the host scheme and type must match
:host - proxy hostname
:type - http (default) | http | https
:port - proxy port
:non-proxy-hosts - The list of hosts to exclude from proxying, may be null
:username - username to log in with, may be null
:password - password to log in with, may be null
:passphrase - passphrase to log in wth, may be null
:private-key-file - private key file to log in with, may be null
:mirrors - {matches settings ..}
matches - a string or regex that will be used to match the mirror to
candidate repositories. Attempts will be made to match the
string/regex to repository names and URLs, with exact string
matches preferred. Wildcard mirrors can be specified with
a match-all regex such as #\".+\". Excluding a repository
from mirroring can be done by mapping a string or regex matching
the repository in question to nil.
settings include these keys, and all those supported by :repositories:
:name - name/id of the mirror
:repo-manager - whether the mirror is a repository manager"
[& {:keys [repositories coordinates files retrieve local-repo
transfer-listener offline? proxy mirrors repository-session-fn]
:or {retrieve true}}]
(let [repositories (or repositories maven-central)
system (repository-system)
mirror-selector-fn (memoize (partial mirror-selector-fn mirrors))
mirror-selector (mirror-selector mirror-selector-fn proxy)
session ((or repository-session-fn
repository-session)
{:repository-system system
:local-repo local-repo
:offline? offline?
:transfer-listener transfer-listener
:mirror-selector mirror-selector})
deps (->> coordinates
(map #(if-let [local-file (get files %)]
(-> (artifact %)
(.setProperties
{ArtifactProperties/LOCAL_PATH
(.getPath (io/file local-file))}))
(artifact %)))
vec)
repositories (vec (map #(let [repo (make-repository % proxy)]
(-> session
(.getMirrorSelector)
(.getMirror repo)
(or repo)))
repositories))]
(if retrieve
(.resolveArtifacts
system session (map #(ArtifactRequest. % repositories nil) deps))
(doall
(for [dep deps]
(.resolveVersion
system session (VersionRequest. dep repositories nil)))))))
(defn resolve-artifacts
"Same as `resolve-artifacts*`, but returns a sequence of dependencies; each
artifact's metadata contains the source Aether result object, and the
artifact's :file on disk."
[& args]
(let [{:keys [coordinates]} (apply hash-map args)]
(->> (apply resolve-artifacts* args)
(map
(fn [coord result]
{:pre [coord result]}
(let [m (when (instance? ArtifactResult result)
{:file (.. ^ArtifactResult result getArtifact getFile)})]
(with-meta coord
(merge {:result result} m))))
coordinates))))
(defn- add-version-from-managed-coord
"Given an entry from a coordinates vector, and the corresponding entry from the
managed coordinates vector, update the version number in the coordinate with the
value from the managed coordinate."
[coord managed-coord]
(if-let [managed-version (second managed-coord)]
(vec (concat [(first coord) managed-version]
(nthrest coord 2)))
(throw (IllegalArgumentException. (str "Provided artifact is missing a version: " coord)))))
(defn- coordinates-match?
[[dep version & opts] [sdep sversion & sopts]]
(let [om (apply hash-map opts)
som (apply hash-map sopts)]
(and
(= (group dep)
(group sdep))
(= (name dep)
(name sdep))
(= (:extension om "jar")
(:extension som "jar"))
(= (:classifier om)
(:classifier som)))))
(defn- find-managed-coord
"Given an entry from a coordinates vector, and a managed coordinates vector, find
the entry in the managed coordinates vector (if any) that matches the coordinate."
[coord managed-coords]
(first (filter #(coordinates-match? coord %) managed-coords)))
(defn- add-version-from-managed-coords-if-missing
"Given a managed coordinates vector and an entry from a coordinates vector, check
to see if the coordinate specifies a version string, and if not, update it with
the version string from the managed coordinates (if it is defined)."
[managed-coords coord]
(if (nil? (second coord))
(add-version-from-managed-coord coord (find-managed-coord coord managed-coords))
coord))
(defn merge-versions-from-managed-coords
"Given a vector of coordinates (e.g. [[group/name <\"version\"> & settings] ..])
where the version field is optional or can be nil, and a vector of managed coordinates,
returns an updated vector of coordinates with version numbers merged in from the
managed-coordinates vector as necessary."
[coordinates managed-coordinates]
(vec (map (partial add-version-from-managed-coords-if-missing managed-coordinates)
coordinates)))
(defn- coords->Dependencies
"Converts a coordinates vector to the maven representation, as Dependency objects."
[files coordinates]
(->> coordinates
(map #(if-let [local-file (get files %)]
(.setArtifact (dependency %)
(-> (dependency %)
.getArtifact
(.setProperties {ArtifactProperties/LOCAL_PATH
(.getPath (io/file local-file))})))
(dependency %)))
vec))
(defn resolve-dependencies*
"Collects dependencies for the coordinates kwarg, using repositories from the
`:repositories` kwarg.
Retrieval of dependencies can be disabled by providing `:retrieve false` as a kwarg.
Returns an instance of either `org.eclipse.aether.collection.CollectResult` if
`:retrieve false` or `org.eclipse.aether.resolution.DependencyResult` if
`:retrieve true` (the default). If you don't want to mess with the Aether
implementation classes, then use `resolve-dependencies` instead.
:coordinates - [[group/name <\"version\"> & settings] ..]
settings:
:extension - the maven extension (type) to require
:classifier - the maven classifier to require
:scope - the maven scope for the dependency (default \"compile\")
:optional - is the dependency optional? (default \"false\")
:exclusions - which sub-dependencies to skip : [group/name & settings]
settings:
:classifier (default \"*\")
:extension (default \"*\")
:managed-coordinates - [[group/name \"version\"] ..]
Used to determine version numbers for any entries in `:coordinates` that
don't explicitly specify them.
:repositories - {name url ..} | {name settings ..}
(defaults to {\"central\" \"https://repo1.maven.org/maven2/\"}
settings:
:url - URL of the repository
:snapshots - use snapshots versions? (default true)
:releases - use release versions? (default true)
:username - username to log in with
:password - password to log in with
:passphrase - passphrase to log in wth
:private-key-file - private key file to log in with
:update - :daily (default) | :always | :never
:checksum - :fail (default) | :ignore | :warn
:local-repo - path to the local repository (defaults to ~/.m2/repository)
:offline? - if true, no remote repositories will be contacted
:transfer-listener - the transfer listener that will be notifed of dependency
resolution and deployment events.
Can be:
- nil (the default), i.e. no notification of events
- :stdout, corresponding to a default listener implementation that writes
notifications and progress indicators to stdout, suitable for an
interactive console program
- a function of one argument, which will be called with a map derived from
each event.
- an instance of org.eclipse.aether.transfer.TransferListener
:proxy - proxy configuration, can be nil, the host scheme and type must match
:host - proxy hostname
:type - http (default) | http | https
:port - proxy port
:non-proxy-hosts - The list of hosts to exclude from proxying, may be null
:username - username to log in with, may be null
:password - password to log in with, may be null
:passphrase - passphrase to log in wth, may be null
:private-key-file - private key file to log in with, may be null
:mirrors - {matches settings ..}
matches - a string or regex that will be used to match the mirror to
candidate repositories. Attempts will be made to match the
string/regex to repository names and URLs, with exact string
matches preferred. Wildcard mirrors can be specified with
a match-all regex such as #\".+\". Excluding a repository
from mirroring can be done by mapping a string or regex matching
the repository in question to nil.
settings include these keys, and all those supported by :repositories:
:name - name/id of the mirror
:repo-manager - whether the mirror is a repository manager"
[& {:keys [repositories coordinates managed-coordinates files retrieve local-repo
transfer-listener offline? proxy mirrors repository-session-fn]
:or {retrieve true}}]
(let [repositories (or repositories maven-central)
system (repository-system)
mirror-selector-fn (memoize (partial mirror-selector-fn mirrors))
mirror-selector (mirror-selector mirror-selector-fn proxy)
session ((or repository-session-fn
repository-session)
{:repository-system system
:local-repo local-repo
:offline? offline?
:transfer-listener transfer-listener
:mirror-selector mirror-selector})
coordinates (merge-versions-from-managed-coords coordinates managed-coordinates)
deps (coords->Dependencies files coordinates)
managed-deps (coords->Dependencies files managed-coordinates)
collect-request (doto (CollectRequest. deps
managed-deps
(vec (map #(let [repo (make-repository % proxy)]
(-> session
(.getMirrorSelector)
(.getMirror repo)
(or repo)))
repositories)))
(.setRequestContext "runtime"))]
(if retrieve
(.resolveDependencies system session (DependencyRequest. collect-request nil))
(.collectDependencies system session collect-request))))
(defn resolve-dependencies
"Same as `resolve-dependencies*`, but returns a graph of dependencies; each
dependency's metadata contains the source Aether Dependency object, and
the dependency's :file on disk. Please refer to `resolve-dependencies*` for details
on usage, or use it if you need access to Aether dependency resolution objects."
[& args]
(-> (apply resolve-dependencies* args)
.getRoot
dependency-graph))
(defn dependency-files
"Given a dependency graph obtained from `resolve-dependencies`, returns a seq of
files from the dependencies' metadata."
[graph]
(->> graph keys (map (comp :file meta)) (remove nil?)))
(defn- exclusion= [spec1 spec2]
(let [[dep & opts] (normalize-exclusion-spec spec1)
[sdep & sopts] (normalize-exclusion-spec spec2)
om (apply hash-map opts)
som (apply hash-map sopts)]
(and (= (group dep)
(group sdep))
(= (name dep)
(name sdep))
(= (:extension om "*")
(:extension som "*"))
(= (:classifier om "*")
(:classifier som "*"))
spec2)))
(defn- exclusions-match? [excs sexcs]
(if-let [ex (first excs)]
(if-let [match (some (partial exclusion= ex) sexcs)]
(recur (next excs) (remove #{match} sexcs))
false)
(empty? sexcs)))
(defn within?
"Determines if the first coordinate would be a version in the second
coordinate. The first coordinate is not allowed to contain a
version range."
[[dep version & opts :as coord] [sdep sversion & sopts :as scoord]]
(let [om (apply hash-map opts)
som (apply hash-map sopts)]
(and (coordinates-match? coord scoord)
(= (:scope om "compile")
(:scope som "compile"))
(= (:optional om false)
(:optional som false))
(exclusions-match? (:exclusions om) (:exclusions som))
(or (= version sversion)
(if-let [[_ ver] (re-find #"^(.*)-SNAPSHOT$" sversion)]
(re-find (re-pattern (str "^" ver "-\\d+\\.\\d+-\\d+$"))
version)
(let [gsv (GenericVersionScheme.)
vc (.parseVersionConstraint gsv sversion)
v (.parseVersion gsv version)]
(.containsVersion vc v)))))))
(defn dependency-hierarchy
"Returns a dependency hierarchy based on the provided dependency graph
(as returned by `resolve-dependencies`) and the coordinates that should
be the root(s) of the hierarchy. Siblings are sorted alphabetically."
[root-coordinates dep-graph]
(let [root-specs (map (comp dep-spec dependency) root-coordinates)
hierarchy (for [root (filter
#(some (fn [root] (within? % root)) root-specs)
(keys dep-graph))]
[root (dependency-hierarchy (dep-graph root) dep-graph)])]
(when (seq hierarchy)
(into (sorted-map-by #(apply compare (map coordinate-string %&))) hierarchy))))
pomegranate-clojure-1.0.0/src/test/ 0000775 0000000 0000000 00000000000 13177100761 0017224 5 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/src/test/clojure/ 0000775 0000000 0000000 00000000000 13177100761 0020667 5 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/src/test/clojure/cemerick/ 0000775 0000000 0000000 00000000000 13177100761 0022451 5 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/src/test/clojure/cemerick/pomegranate/ 0000775 0000000 0000000 00000000000 13177100761 0024753 5 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/src/test/clojure/cemerick/pomegranate/aether_test.clj 0000664 0000000 0000000 00000101070 13177100761 0027753 0 ustar 00root root 0000000 0000000 (ns cemerick.pomegranate.aether-test
(:require [cemerick.pomegranate.aether :as aether]
[clojure.java.io :as io])
(:use [clojure.test]))
(deftest dependency-roundtripping
(are [x] (= x (#'aether/dep-spec (#'aether/dependency x)))
'[ring "1.0.0" :optional true]
'[com.cemerick/pomegranate "0.0.1" :classifier "sources"]
'[demo/demo2 "1.0.0" :exclusions [[demo :classifier "jdk5"]]]))
(def tmp-dir (io/file (System/getProperty "java.io.tmpdir") "pomegranate-test-tmp"))
(def tmp-remote-repo-dir (.getAbsolutePath (io/file tmp-dir "remote-repo")))
(def tmp-local-repo-dir (io/file tmp-dir "local-repo"))
(def tmp-local-repo2-dir (io/file tmp-dir "local-repo2"))
(def test-remote-repo {"central" "https://repo1.maven.org/maven2/"})
(def test-repo {:test-repo "file://test-repo"})
(def tmp-remote-repo {"tmp-remote-repo" (str "file://" tmp-remote-repo-dir)})
(defn delete-recursive
[dir]
(when (.isDirectory dir)
(doseq [file (.listFiles dir)]
(delete-recursive file)))
(.delete dir))
(defn- clear-tmp
[f]
(delete-recursive (io/file tmp-dir)) (f))
(use-fixtures :each clear-tmp)
(defn file-path-eq [file1 file2]
(= (.getAbsolutePath file1)
(.getAbsolutePath file2)))
(deftest live-resolution
(let [deps '[[commons-logging "1.1"]]
graph '{[javax.servlet/servlet-api "2.3"] nil,
[avalon-framework "4.1.3"] nil,
[logkit "1.0.1"] nil,
[log4j "1.2.12"] nil,
[commons-logging "1.1"]
#{[javax.servlet/servlet-api "2.3"] [avalon-framework "4.1.3"]
[logkit "1.0.1"] [log4j "1.2.12"]}}
hierarchy '{[commons-logging "1.1"]
{[avalon-framework "4.1.3"] nil,
[javax.servlet/servlet-api "2.3"] nil,
[log4j "1.2.12"] nil,
[logkit "1.0.1"] nil}}]
(is (= graph (aether/resolve-dependencies :coordinates deps :retrieve false :local-repo tmp-local-repo-dir)))
(is (not (some #(-> % .getName (.endsWith ".jar")) (file-seq tmp-local-repo-dir))))
(doseq [[dep _] (aether/resolve-dependencies :coordinates deps :local-repo tmp-local-repo-dir)]
(is (-> dep meta :file))
(is (-> dep meta :file .exists)))
(is (some #(-> % .getName (.endsWith ".jar")) (file-seq tmp-local-repo-dir)))
(is (= hierarchy (aether/dependency-hierarchy deps graph)))))
(deftest live-artifact-resolution
(let [deps '[[commons-logging "1.1"]]]
(is (= deps (aether/resolve-artifacts
:coordinates deps :retrieve false
:local-repo tmp-local-repo-dir)))
(is (= 1 (count (aether/resolve-artifacts
:coordinates '[[demo "1.0.0"]] :retrieve false
:files {'[demo "1.0.0"] (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar")}
:local-repo tmp-local-repo-dir))))
(is (not (some #(-> % .getName (.endsWith ".jar"))
(file-seq tmp-local-repo-dir))))
(doseq [dep (aether/resolve-artifacts
:coordinates deps :local-repo tmp-local-repo-dir)]
(is (-> dep meta :file))
(is (-> dep meta :file .exists)))
(is (some #(-> % .getName (.endsWith ".jar"))
(file-seq tmp-local-repo-dir)))))
(deftest impl-detail-types
(let [args [:coordinates '[[commons-logging "1.1"]] :local-repo tmp-local-repo-dir]]
(is (instance? org.eclipse.aether.resolution.DependencyResult
(apply aether/resolve-dependencies* args)))
(is (instance? org.eclipse.aether.collection.CollectResult
(apply aether/resolve-dependencies* :retrieve false args)))))
(deftest resolve-deps-with-mirror
(let [deps (aether/resolve-dependencies :repositories {"clojars" "https://clojars.org/repo"}
:coordinates '[[javax.servlet/servlet-api "2.5"]]
:mirrors {"clojars" {:url "https://maven-central.storage-download.googleapis.com/repos/central/data"}}
:local-repo tmp-local-repo-dir)]
(is (= 1 (count deps)))
(is (= (.getAbsolutePath (io/file tmp-dir "local-repo" "javax" "servlet" "servlet-api" "2.5" "servlet-api-2.5.jar"))
(.getAbsolutePath (first (aether/dependency-files deps)))))))
(deftest resolve-deps-with-wildcard-mirror
(let [deps (aether/resolve-dependencies :repositories {"clojars" "https://clojars.org/repo"}
:coordinates '[[javax.servlet/servlet-api "2.5"]]
:mirrors {#".+" {:url "https://maven-central.storage-download.googleapis.com/repos/central/data"}}
:local-repo tmp-local-repo-dir)]
(is (= 1 (count deps)))
(is (= (.getAbsolutePath (io/file tmp-dir "local-repo" "javax" "servlet" "servlet-api" "2.5" "servlet-api-2.5.jar"))
(.getAbsolutePath (first (aether/dependency-files deps)))))))
(deftest resolve-deps-with-wildcard-override-mirror
(let [deps (aether/resolve-dependencies :repositories test-remote-repo
:coordinates '[[javax.servlet/servlet-api "2.5"]]
:mirrors {#".+" {:url "https://clojars.org/repo"}
(ffirst test-remote-repo) nil}
:local-repo tmp-local-repo-dir)]
(is (= 1 (count deps)))
(is (= (.getAbsolutePath (io/file tmp-dir "local-repo" "javax" "servlet" "servlet-api" "2.5" "servlet-api-2.5.jar"))
(.getAbsolutePath (first (aether/dependency-files deps)))))))
(deftest resolve-deps
(let [deps (aether/resolve-dependencies :repositories test-repo
:coordinates '[[demo/demo "1.0.0"]]
:local-repo tmp-local-repo-dir)]
(is (= 1 (count deps)))
(is (= (.getAbsolutePath (io/file tmp-dir "local-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar"))
(.getAbsolutePath (first (aether/dependency-files deps)))))))
(deftest resolve-deps-with-deps
(let [deps (aether/resolve-dependencies :repositories test-repo
:coordinates '[[demo/demo2 "1.0.0"]]
:local-repo tmp-local-repo-dir)
files (aether/dependency-files deps)]
(is (= 2 (count files)))
(is (= 1 (count (filter #(file-path-eq % (io/file tmp-dir "local-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar"))
files))))
(is (= 1 (count (filter #(file-path-eq % (io/file tmp-dir "local-repo" "demo" "demo2" "1.0.0" "demo2-1.0.0.jar"))
files))))))
(deftest resolve-unmanaged-dependencies
(let [deps (aether/resolve-dependencies
:repositories {}
:coordinates '[[demo "1.0.0"]]
:files {'[demo "1.0.0"] (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar")}
:local-repo tmp-local-repo-dir)
files (aether/dependency-files deps)]
(is (= 1 (count files)))
(is (= nil (:file (meta (first files)))))))
(deftest resolve-deps-with-exclusions
(let [deps (aether/resolve-dependencies :repositories test-repo
:coordinates
'[[demo/demo2 "1.0.0" :exclusions [demo/demo]]]
:local-repo tmp-local-repo-dir)]
(is (= 1 (count deps)))
(is (= (.getAbsolutePath (io/file tmp-dir "local-repo" "demo" "demo2" "1.0.0" "demo2-1.0.0.jar"))
(.getAbsolutePath (first (aether/dependency-files deps)))))))
(deftest resolve-deps-with-classifiers
(let [deps (aether/resolve-dependencies :repositories test-repo
:coordinates
'[[demo/demo "1.0.1" :classifier "test"]]
:local-repo tmp-local-repo-dir)]
(is (= 1 (count deps)))
(is (= (.getAbsolutePath (io/file tmp-dir "local-repo" "demo" "demo" "1.0.1" "demo-1.0.1-test.jar"))
(.getAbsolutePath (first (aether/dependency-files deps)))))))
(deftest resolve-managed-dependencies
(testing "supports coordinates w/o version number, with managed coordinates"
(let [deps (aether/resolve-dependencies
:repositories test-repo
:coordinates '[[demo/demo]]
:managed-coordinates '[[demo/demo "1.0.0"]]
:local-repo tmp-local-repo-dir)
files (aether/dependency-files deps)]
(is (= 1 (count files)))
(is (= 1 (count (filter #(file-path-eq % (io/file tmp-dir "local-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar"))
files))))))
(testing "supports coordinates w/o version number, with managed coordinates, w/o group-id"
(let [deps (aether/resolve-dependencies
:repositories test-repo
:coordinates '[[demo/demo]]
:managed-coordinates '[[demo "1.0.0"]]
:local-repo tmp-local-repo-dir)
files (aether/dependency-files deps)]
(is (= 1 (count files)))
(is (= 1 (count (filter #(file-path-eq % (io/file tmp-dir "local-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar"))
files)))))
(let [deps (aether/resolve-dependencies
:repositories test-repo
:coordinates '[[demo]]
:managed-coordinates '[[demo/demo "1.0.0"]]
:local-repo tmp-local-repo-dir)
files (aether/dependency-files deps)]
(is (= 1 (count files)))
(is (= 1 (count (filter #(file-path-eq % (io/file tmp-dir "local-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar"))
files))))))
(testing "supports coordinates w/nil version number, with managed coordinates"
(let [deps (aether/resolve-dependencies
:repositories test-repo
:coordinates '[[demo/demo nil]]
:managed-coordinates '[[demo/demo "1.0.0"]]
:local-repo tmp-local-repo-dir)
files (aether/dependency-files deps)]
(is (= 1 (count files)))
(is (= 1 (count (filter #(file-path-eq % (io/file tmp-dir "local-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar"))
files))))))
(testing "supports coordinates w/nil version number and kwargs, with managed coordinates"
(let [deps (aether/resolve-dependencies
:repositories test-repo
:coordinates '[[demo/demo2 nil :exclusions [demo/demo]]]
:managed-coordinates '[[demo/demo2 "1.0.0"]]
:local-repo tmp-local-repo-dir)
files (aether/dependency-files deps)]
(is (= 1 (count files)))
(is (= 1 (count (filter #(file-path-eq % (io/file tmp-dir "local-repo" "demo" "demo2" "1.0.0" "demo2-1.0.0.jar"))
files))))))
(testing "error if missing version number w/o managed coordinates"
(is (thrown-with-msg? IllegalArgumentException #"Provided artifact is missing a version: \[demo/demo\]"
(aether/resolve-dependencies
:repositories test-repo
:coordinates '[[demo/demo]]
:local-repo tmp-local-repo-dir))))
(testing "error if nil version number w/o managed coordinates"
(is (thrown-with-msg? IllegalArgumentException #"Provided artifact is missing a version: \[demo/demo nil\]"
(aether/resolve-dependencies
:repositories test-repo
:coordinates '[[demo/demo nil]]
:local-repo tmp-local-repo-dir))))
(testing "coordinates version number overrides managed coordinates version"
(let [deps (aether/resolve-dependencies
:repositories test-repo
:coordinates '[[demo/demo "1.0.0"]]
:managed-coordinates '[[demo/demo "0.0.1"]]
:local-repo tmp-local-repo-dir)
files (aether/dependency-files deps)]
(is (= 1 (count files)))
(is (= 1 (count (filter #(file-path-eq % (io/file tmp-dir "local-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar"))
files))))))
(testing "managed coordinates version is honored for transitive deps"
(let [deps (aether/resolve-dependencies
:repositories test-repo
:coordinates '[[demo/demo2 "1.0.0"]]
:managed-coordinates '[[demo/demo "1.0.1"]]
:local-repo tmp-local-repo-dir)
files (aether/dependency-files deps)]
(is (= 2 (count files)))
(is (= 1 (count (filter #(file-path-eq % (io/file tmp-dir "local-repo" "demo" "demo2" "1.0.0" "demo2-1.0.0.jar"))
files))))
(is (= 1 (count (filter #(file-path-eq % (io/file tmp-dir "local-repo" "demo" "demo" "1.0.1" "demo-1.0.1.jar"))
files))))))
(testing "unused entries in managed coordinates are not resolved"
(let [deps (aether/resolve-dependencies
:repositories test-repo
:coordinates '[[demo/demo]]
:managed-coordinates '[[demo/demo "1.0.0"]
[demo/demo2 "1.0.0"]]
:local-repo tmp-local-repo-dir)
files (aether/dependency-files deps)]
(is (= 1 (count files)))
(is (= 1 (count (filter #(file-path-eq % (io/file tmp-dir "local-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar"))
files))))))
(testing "exclusions in managed coordinates are honored"
(let [deps (aether/resolve-dependencies
:repositories test-repo
:coordinates '[[demo/demo2]]
:managed-coordinates '[[demo/demo2 "1.0.0" :exclusions [demo/demo]]]
:local-repo tmp-local-repo-dir)
files (aether/dependency-files deps)]
(is (= 1 (count files)))
(is (= 1 (count (filter #(file-path-eq % (io/file tmp-dir "local-repo" "demo" "demo2" "1.0.0" "demo2-1.0.0.jar"))
files))))))
(testing "classifiers in managed coordinates are honored"
(let [deps (aether/resolve-dependencies
:repositories test-repo
:coordinates '[[demo/demo]
[demo/demo nil :classifier "test"]]
:managed-coordinates '[[demo/demo "1.0.0"]
[demo/demo "1.0.1" :classifier "test"]]
:local-repo tmp-local-repo-dir)
files (aether/dependency-files deps)]
(is (= 2 (count files)))
(is (= 1 (count (filter #(file-path-eq % (io/file tmp-dir "local-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar"))
files))))
(is (= 1 (count (filter #(file-path-eq % (io/file tmp-dir "local-repo" "demo" "demo" "1.0.1" "demo-1.0.1-test.jar"))
files)))))))
(deftest deploy-jar
(aether/deploy :coordinates '[group/artifact "1.0.0"]
:jar-file (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar")
:repository tmp-remote-repo
:local-repo tmp-local-repo-dir)
(is (= 3 (count (.list (io/file tmp-remote-repo-dir "group" "artifact" "1.0.0"))))))
(deftest deploy-jar-with-pom
(aether/deploy :coordinates '[group/artifact "1.0.0"]
:jar-file (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar")
:pom-file (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.pom")
:repository tmp-remote-repo
:local-repo tmp-local-repo-dir)
(is (= 6 (count (.list (io/file tmp-remote-repo-dir "group" "artifact" "1.0.0"))))))
(deftest deploy-jar-with-artifact-map
(let [repo-file (partial io/file "test-repo" "demo" "demo" "1.0.0")]
(aether/deploy
:coordinates '[group/artifact "1.0.0"]
:artifact-map {[] (repo-file "demo-1.0.0.pom")
[:extension "pom"] (repo-file "demo-1.0.0.pom")}
:repository tmp-remote-repo
:local-repo tmp-local-repo-dir))
(is (= 6 (count (.list (io/file tmp-remote-repo-dir "group" "artifact" "1.0.0"))))))
(deftest install-jar
(aether/install :coordinates '[group/artifact "1.0.0"]
:jar-file (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar")
:pom-file (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.pom")
:local-repo tmp-local-repo-dir)
(is (= 3 (count (.list (io/file tmp-local-repo-dir "group" "artifact" "1.0.0"))))))
(deftest install-jar-with-artifact-map
(let [repo-file (partial io/file "test-repo" "demo" "demo" "1.0.0")]
(aether/install
:coordinates '[group/artifact "1.0.0"]
:artifact-map {[] (repo-file "demo-1.0.0.jar")
[:extension "pom"] (repo-file "demo-1.0.0.pom")}
:local-repo tmp-local-repo-dir))
(is (= 3 (count (.list (io/file tmp-local-repo-dir "group" "artifact" "1.0.0"))))))
(java.lang.System/setProperty "aether.checksums.forSignature" "true")
(deftest deploy-artifacts
(aether/deploy-artifacts
:artifacts '[[demo "1.0.0"]
[demo "1.0.0" :extension "jar.asc"]
[demo "1.0.0" :extension "pom"]
[demo "1.0.0" :extension "pom.asc"]]
;; note: the .asc files in the test-repo are dummies, but it doesn't matter for this test
:files {'[demo "1.0.0"] (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar")
'[demo "1.0.0" :extension "jar.asc"] (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar.asc")
'[demo "1.0.0" :extension "pom"] (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.pom")
'[demo "1.0.0" :extension "pom.asc"] (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.pom.asc")}
:repository tmp-remote-repo
:local-repo tmp-local-repo-dir)
(is (= #{"demo-1.0.0.pom.md5"
"demo-1.0.0.pom.sha1"
"demo-1.0.0.pom"
"demo-1.0.0.pom.asc.md5"
"demo-1.0.0.pom.asc.sha1"
"demo-1.0.0.pom.asc"
"demo-1.0.0.jar.md5"
"demo-1.0.0.jar.sha1"
"demo-1.0.0.jar"
"demo-1.0.0.jar.asc.md5"
"demo-1.0.0.jar.asc.sha1"
"demo-1.0.0.jar.asc"}
(set (.list (io/file tmp-remote-repo-dir "demo" "demo" "1.0.0")))) "Should deploy correctly demo \"1.0.0\"")
(is (= '{[demo "1.0.0"] nil}
(aether/resolve-dependencies :repositories tmp-remote-repo
:coordinates
'[[demo "1.0.0"]]
:local-repo tmp-local-repo2-dir)))
(is (= '{[demo "1.0.0" :extension "pom"] nil}
(aether/resolve-dependencies :repositories tmp-remote-repo
:coordinates
'[[demo "1.0.0" :extension "pom"]]
:local-repo tmp-local-repo2-dir)))
(is (= '{[demo "1.0.0" :extension "jar.asc"] nil}
(aether/resolve-dependencies :repositories tmp-remote-repo
:coordinates
'[[demo "1.0.0" :extension "jar.asc"]]
:local-repo tmp-local-repo2-dir)))
(is (= '{[demo "1.0.0" :extension "pom.asc"] nil}
(aether/resolve-dependencies :repositories tmp-remote-repo
:coordinates
'[[demo "1.0.0" :extension "pom.asc"]]
:local-repo tmp-local-repo2-dir))))
(deftest install-artifacts
(aether/install-artifacts
:artifacts '[[demo "1.0.0"]
[demo "1.0.0" :extension "jar.asc"]
[demo "1.0.0" :extension "pom"]
[demo "1.0.0" :extension "pom.asc"]]
;; note: the .asc files in the test-repo are dummies, but it doesn't matter for this test
:files {'[demo "1.0.0"] (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar")
'[demo "1.0.0" :extension "jar.asc"] (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar.asc")
'[demo "1.0.0" :extension "pom"] (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.pom")
'[demo "1.0.0" :extension "pom.asc"] (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.pom.asc")}
:local-repo tmp-local-repo-dir)
(is (= #{"demo-1.0.0.jar"
"demo-1.0.0.pom"
"demo-1.0.0.jar.asc"
"demo-1.0.0.pom.asc"
"_remote.repositories"}
(set (.list (io/file tmp-local-repo-dir "demo" "demo" "1.0.0"))))))
(deftest deploy-exceptions
(is (thrown-with-msg? IllegalArgumentException #"Provided artifacts have varying"
(aether/deploy-artifacts
:artifacts '[[demo "1.0.0"]
[group/demo "1.0.0" :extension "jar.asc"]]
:files {'[demo "1.0.0"] (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar")
'[group/demo "1.0.0" :extension "jar.asc"]
(io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar.asc")}
:repository tmp-remote-repo
:local-repo tmp-local-repo-dir)))
(is (thrown-with-msg? IllegalArgumentException #"Provided artifacts have varying version, group, or artifact IDs"
(aether/deploy-artifacts
:artifacts '[[demo "1.0.0"]
[demo/artifact "1.0.0" :extension "jar.asc"]]
:files {'[demo "1.0.0"] (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar")
'[demo/artifact "1.0.0" :extension "jar.asc"]
(io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar.asc")}
:repository tmp-remote-repo
:local-repo tmp-local-repo-dir)))
(is (thrown-with-msg? IllegalArgumentException #"Provided artifacts have varying version, group, or artifact IDs"
(aether/deploy-artifacts
:artifacts '[[demo "1.0.0"]
[demo "1.1.0" :extension "jar.asc"]]
:files {'[demo "1.0.0"] (io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar")
'[demo "1.1.0" :extension "jar.asc"]
(io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar.asc")}
:repository tmp-remote-repo
:local-repo tmp-local-repo-dir)))
(is (thrown-with-msg? IllegalArgumentException #"Provided artifacts have varying version, group, or artifact IDs"
(aether/deploy-artifacts
:files {'[demo "1.0.0"]
(io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar")
'[demo "1.1.0" :extension "jar.asc"]
(io/file "test-repo" "demo" "demo" "1.0.0" "demo-1.0.0.jar")}
:repository tmp-remote-repo
:local-repo tmp-local-repo-dir))))
(deftest within?-comparisons
(is (aether/within? '[demo "0.0.1"]
'[demo "0.0.1"]))
(is (aether/within? '[demo "0.0.1"]
'[demo/demo "0.0.1"]))
(is (aether/within? '[demo/demo "0.0.1"]
'[demo "0.0.1"]))
(is (aether/within? '[demo "0.0.1"]
'[demo "[0.0.1,2.0.0)"]))
(is (not (aether/within? '[demo "2.0.0"]
'[demo "[0.0.1,2.0.0)"])))
(is (not (aether/within? '[demo "0.0.1"]
'[demo "(0.0.1,2.0.0)"])))
(is (aether/within? '[demo "0.0.1-SNAPSHOT"]
'[demo/demo "0.0.1-SNAPSHOT"]))
(is (aether/within? '[demo "0.0.1-SNAPSHOT"]
'[demo "0.0.1-SNAPSHOT"]))
(is (aether/within? '[demo "0.0.1-20120403.012847-1"]
'[demo "0.0.1-SNAPSHOT"]))
(is (not (aether/within? '[demo "0.0.1-SNAPSHOT"]
'[demo "0.0.1-20120403.012847-10"])))
(is (aether/within? '[demo "0.0.1"]
'[demo "0.0.1" :extension "jar"]))
(is (aether/within? '[demo "0.0.1" :extension "jar"]
'[demo "0.0.1"]))
(is (not (aether/within? '[demo "0.0.1" :extension "pom"]
'[demo "0.0.1"])))
(is (not (aether/within? '[demo "0.0.1"]
'[demo "0.0.1":extension "pom"])))
(is (aether/within? '[demo "0.0.1" :classifier "sources"]
'[demo "0.0.1" :classifier "sources"]))
(is (not (aether/within? '[demo "0.0.1"]
'[demo "0.0.1" :classifier "sources"])))
(is (not (aether/within? '[demo "0.0.1" :classifier "sources"]
'[demo "0.0.1"])))
(is (aether/within? '[demo "0.0.1"]
'[demo "0.0.1" :scope "compile"]))
(is (aether/within? '[demo "0.0.1" :scope "compile"]
'[demo "0.0.1"]))
(is (aether/within? '[demo "0.0.1" :scope "compile"]
'[demo "0.0.1" :scope "compile"]))
(is (not (aether/within? '[demo "0.0.1" :scope "compile"]
'[demo "0.0.1" :scope "test"])))
(is (not (aether/within? '[demo "0.0.1" :scope "test"]
'[demo "0.0.1" :scope "compile"])))
(is (aether/within? '[demo "0.0.1"]
'[demo "0.0.1" :optional false]))
(is (aether/within? '[demo "0.0.1" :optional false]
'[demo "0.0.1"]))
(is (aether/within? '[demo "0.0.1" :optional true]
'[demo "0.0.1" :optional true]))
(is (not (aether/within? '[demo "0.0.1" :optional true]
'[demo "0.0.1"])))
(is (not (aether/within? '[demo "0.0.1"]
'[demo "0.0.1":optional true])))
(is (aether/within? '[demo "0.0.1" :exclusions []]
'[demo "0.0.1"]))
(is (aether/within? '[demo "0.0.1"]
'[demo "0.0.1" :exclusions []]))
(is (aether/within? '[demo "0.0.1" :exclusions [[demo2]]]
'[demo "0.0.1" :exclusions [[demo2]]]))
(is (not (aether/within? '[demo "0.0.1" :exclusions [[demo2]]]
'[demo "0.0.1"])))
(is (not (aether/within? '[demo "0.0.1"]
'[demo "0.0.1" :exclusions [[demo2]]])))
(is (not (aether/within? '[demo "0.0.1" :exclusions [demo2]]
'[demo "0.0.1"])))
(is (not (aether/within? '[demo "0.0.1"]
'[demo "0.0.1" :exclusions [demo2]])))
(is (aether/within? '[demo "0.0.1" :exclusions [[demo2]
[demo3]]]
'[demo "0.0.1" :exclusions [[demo2]
[demo3]]]))
(is (aether/within? '[demo "0.0.1" :exclusions [[demo3]
[demo2]]]
'[demo "0.0.1" :exclusions [[demo2]
[demo3]]]))
(is (not (aether/within? '[demo "0.0.1" :exclusions [[demo2]]]
'[demo "0.0.1" :exclusions [[demo2]
[demo3]]])))
(is (not (aether/within? '[demo "0.0.1" :exclusions [[demo2]
[demo3]]]
'[demo "0.0.1" :exclusions [[demo2]]])))
(is (aether/within? '[demo "0.0.1" :exclusions [[demo2]]]
'[demo "0.0.1" :exclusions [[demo2 :classifier "*"]]]))
(is (aether/within? '[demo "0.0.1" :exclusions [[demo2 :classifier "*"]]]
'[demo "0.0.1" :exclusions [[demo2]]]))
(is (not (aether/within?
'[demo "0.0.1" :exclusions [[demo2 :classifier "*"]]]
'[demo "0.0.1" :exclusions [[demo2 :classifier "sources"]]])))
(is (not (aether/within?
'[demo "0.0.1" :exclusions [[demo2 :classifier "sources"]]]
'[demo "0.0.1" :exclusions [[demo2 :classifier "*"]]])))
(is (aether/within? '[demo "0.0.1" :exclusions [[demo2]]]
'[demo "0.0.1" :exclusions [[demo2 :extension "*"]]]))
(is (aether/within? '[demo "0.0.1" :exclusions [[demo2 :extension "*"]]]
'[demo "0.0.1" :exclusions [[demo2]]]))
(is (not (aether/within?
'[demo "0.0.1" :exclusions [[demo2 :extension "*"]]]
'[demo "0.0.1" :exclusions [[demo2 :extension "jar"]]])))
(is (not (aether/within?
'[demo "0.0.1" :exclusions [[demo2 :extension "jar"]]]
'[demo "0.0.1" :exclusions [[demo2 :extension "*"]]]))))
(deftest dependency-hierarchy-matching
(let [coords '[[demo/demo2 "[0.0.1,2.0.0)"]
[tester "0.1.0-SNAPSHOT"]]
deps (aether/resolve-dependencies
:repositories test-repo
:coordinates coords
:local-repo tmp-local-repo-dir)]
(is (= {['demo/demo2 "1.0.0"] {['demo "1.0.0"] nil}
['tester "0.1.0-20120403.012847-1"] nil}
(aether/dependency-hierarchy coords deps)))))
(deftest make-repository-proxy-settings
(let [repo (first test-remote-repo)
proxy {:type "https"
:host "squid"
:port 3128}]
(testing "plain proxy"
(let [repo-proxy (->> proxy (aether/make-repository repo) .getProxy bean)]
(is (= proxy (select-keys repo-proxy (keys proxy))))
(is (not (:authentication repo-proxy)))))
(testing "authentication"
(let [repo-proxy (->> (assoc proxy :username "me" :password "123456")
(aether/make-repository repo)
.getProxy
bean)]
(is (= proxy (select-keys repo-proxy (keys proxy))))
(is (:authentication repo-proxy))))))
;; taken from the test suite for the pendantic lib by Nelson Morris
(defn get-versions [name repo]
(let [name (symbol name)]
(map second (filter #(= name (first %)) (keys repo)))))
(defn make-pom-string [name version deps]
(str "
4.0.0
" name "
" name "
jar
" version "
" name ""
(if-not (empty? deps)
(apply str
""
(clojure.string/join "\n"
(for [[n v] deps]
(str "
" n "
"n"
"v"
")))
""))
" "))
(defn make-metadata [name versions]
(str "
" name "
" name "
"
(clojure.string/join "\n"
(for [v versions]
(str ""v"")))
"
20120810193549
"))
(def fake-repo
'{[a "1"] []
[a "2"] []
[aa "2"] [[a "2"]]})
(deftest register-fake-wagon
(aether/register-wagon-factory!
"fake"
#(reify org.apache.maven.wagon.Wagon
(getRepository [_]
(proxy [org.apache.maven.wagon.repository.Repository] []))
(^void connect [_
^org.apache.maven.wagon.repository.Repository _
^org.apache.maven.wagon.authentication.AuthenticationInfo _
^org.apache.maven.wagon.proxy.ProxyInfoProvider _])
(disconnect [_])
(removeTransferListener [_ _])
(addTransferListener [_ _])
(setTimeout [_ _])
(setInteractive [_ _])
(get [_ name file]
(let [[n _ version] (clojure.string/split name #"/")]
(if (= name (str n "/" n "/maven-metadata.xml"))
(if-let [versions (get-versions n fake-repo)]
(spit file (make-metadata n versions))
(spit file ""))
(if-let [deps (fake-repo [(symbol n) version])]
(if (re-find #".pom$" name)
(spit file (make-pom-string n version deps))
(spit file ""))
(throw (org.apache.maven.wagon.ResourceDoesNotExistException. ""))))))))
(let [tmp-local-repo-dir (io/file tmp-dir "local-repo")]
(aether/resolve-dependencies :coordinates '[[a "1"]]
:repositories {"test-repo"
{:url "fake://ss"
:checksum :ignore}}
:local-repo tmp-local-repo-dir)
(is (= #{"local-repo" "a" "1" "a-1.pom" "_remote.repositories" "a-1.jar"}
(set (map (memfn getName) (file-seq tmp-local-repo-dir)))))))
(comment
"tests needed for:
repository authentication
repository policies
dependency options (scope/optional)
exclusion options (classifier/extension)")
pomegranate-clojure-1.0.0/src/test/clojure/cemerick/pomegranate_test.clj 0000664 0000000 0000000 00000001213 13177100761 0026501 0 ustar 00root root 0000000 0000000 (ns cemerick.pomegranate-test
(:require [cemerick.pomegranate :as p]
clojure.java.io)
(:use clojure.test))
(deftest resources
(is (= (first (p/resources "META-INF/MANIFEST.MF"))
(clojure.java.io/resource "META-INF/MANIFEST.MF")))
; the last classloader should be ext, for e.g. $JAVA_HOME/lib/ext/*
(is (->> (p/resources [(last (p/classloader-hierarchy))] "META-INF/MANIFEST.MF")
(map str)
(filter #(.contains % "clojure"))
empty?))
(is (->> (p/resources (butlast (p/classloader-hierarchy)) "META-INF/MANIFEST.MF")
(map str)
(filter #(.contains % "clojure"))
seq))) pomegranate-clojure-1.0.0/test-repo/ 0000775 0000000 0000000 00000000000 13177100761 0017400 5 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/test-repo/demo/ 0000775 0000000 0000000 00000000000 13177100761 0020324 5 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/test-repo/demo/demo/ 0000775 0000000 0000000 00000000000 13177100761 0021250 5 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.0/ 0000775 0000000 0000000 00000000000 13177100761 0021704 5 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.0/demo-1.0.0.jar 0000664 0000000 0000000 00000000000 13177100761 0023746 0 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.0/demo-1.0.0.jar.asc 0000664 0000000 0000000 00000000171 13177100761 0024524 0 ustar 00root root 0000000 0000000 This file here only to test deployment operations; the fact that this isn't a signature file shouldn't cause any issues.
pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.0/demo-1.0.0.jar.asc.md5 0000664 0000000 0000000 00000000065 13177100761 0025212 0 ustar 00root root 0000000 0000000 b5f91153fbd3dc8bc29e98c73d46655f demo-1.0.0.jar.asc
pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.0/demo-1.0.0.jar.asc.sha1 0000664 0000000 0000000 00000000075 13177100761 0025362 0 ustar 00root root 0000000 0000000 d94055dca4e0f2bcc455e278287dcf0e7c4034e1 demo-1.0.0.jar.asc
pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.0/demo-1.0.0.jar.md5 0000664 0000000 0000000 00000000040 13177100761 0024436 0 ustar 00root root 0000000 0000000 d41d8cd98f00b204e9800998ecf8427e pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.0/demo-1.0.0.jar.sha1 0000664 0000000 0000000 00000000050 13177100761 0024606 0 ustar 00root root 0000000 0000000 da39a3ee5e6b4b0d3255bfef95601890afd80709 pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.0/demo-1.0.0.pom 0000664 0000000 0000000 00000000704 13177100761 0024000 0 ustar 00root root 0000000 0000000
4.0.0
demo
demo
1.0.0
POM was created from install:install-file
pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.0/demo-1.0.0.pom.asc 0000664 0000000 0000000 00000000171 13177100761 0024543 0 ustar 00root root 0000000 0000000 This file here only to test deployment operations; the fact that this isn't a signature file shouldn't cause any issues.
pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.0/demo-1.0.0.pom.asc.md5 0000664 0000000 0000000 00000000065 13177100761 0025231 0 ustar 00root root 0000000 0000000 b5f91153fbd3dc8bc29e98c73d46655f demo-1.0.0.pom.asc
pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.0/demo-1.0.0.pom.asc.sha1 0000664 0000000 0000000 00000000075 13177100761 0025401 0 ustar 00root root 0000000 0000000 d94055dca4e0f2bcc455e278287dcf0e7c4034e1 demo-1.0.0.pom.asc
pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.0/demo-1.0.0.pom.md5 0000664 0000000 0000000 00000000040 13177100761 0024455 0 ustar 00root root 0000000 0000000 5689f1eaf926240885eb9976e5269e5d pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.0/demo-1.0.0.pom.sha1 0000664 0000000 0000000 00000000050 13177100761 0024625 0 ustar 00root root 0000000 0000000 eb4ff44542c69c5271390dad76a96bfd15b4858a pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.1/ 0000775 0000000 0000000 00000000000 13177100761 0021705 5 ustar 00root root 0000000 0000000 pomegranate-clojure-1.0.0/test-repo/demo/demo/1.0.1/demo-1.0.1-test.jar 0000664 0000000 0000000 00000002621 13177100761 0024740 0 ustar 00root root 0000000 0000000 PK
EFH META-INF/PK EFH META-INF/MANIFEST.MFMLK-.
K-*ϳR03r*)uRH.(LNzdT[r9&:$&g*&)Yr9%gd!LI(-VIrr PK#n PK
EFH META-INF/maven/PK
EFH META-INF/maven/demo/PK
EFH META-INF/maven/demo/demo/PK :FH META-INF/maven/demo/demo/pom.xmlSn0+wV= JZRի.!l?D*
~f
ƺjkxRMjڳіf'((Fqh(Y=n |QHE|+:7)F[02:8'\U_LHmBaYw[NrVs,kz77^ ~e`eS3+~MUZ3ج܄=۫lzܻ=z\$ldY7\KꖙtQLȄgm?X}DCi