Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add load-project, and make distill print changes #7

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,46 @@ vector of the `:user` profile in `~/.lein/profiles.clj`.

## Usage

### Reloading project.clj

If you modify the dependencies in your `project.clj` file, you can load the
modified dependencies with `load-project`.

This will add all non-conflicting dependency changes. Only new
dependencies are considered non-conflicting. New versions of existing
dependencies are not loaded. Removed dependencies are not unloaded.

### Adding Ad-Hoc Dependencies

To add a dependency to the classpath, use the `distill` function, passing a
leiningen style dependency vector.

```clj
(alembic.still/distill '[org.clojure/tools.logging "0.2.0"])
```
You can pass a sequence of dependencies to add, or just a single
dependency as in the example above.

If the dependency is added to the classpath, `distill` returns a sequence of
maps, where each map represents a dependent jar. Those jars without a current
version on the classpath will be added to the classpath. The jars with a
version already on the classpath are not added to the classpath, and the
currently loaded version is reported on the :current-version key.
`distill` prints the dependencies added to the classpath, and those
not added due to conflicts.

The distill function returns nil, with no side-effects, if the dependency is
already on the classpath.
The distill function returns with no side-effects, if the dependency's
jars are already on the classpath.

By default, `distill` uses the repositories in the current lein project. You
can override this by passing a map of lein style repository information to the
`:repositories` option. The `project-repositories` function can be used to
obtain the lein project repositories, should you want to adapt these to pass as
an `:repositories` argument.


For programmatic use, `distill*` returns a sequence of maps, where
each map represents a dependent jar. Those jars without a current
version on the classpath will be added to the classpath. The jars
with a version already on the classpath are not added to the
classpath, and the currently loaded version is reported on the
:current-version key.

You can query the dependencies that have been added with the
`dependencies-added` function, which returns a sequence of leiningen style
dependency vectors.
Expand Down
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject alembic "0.1.5-SNAPSHOT"
(defproject alembic "0.2.0-SNAPSHOT"
:description "A jar distiller"
:url "https://github.com/pallet/alembic"
:license {:name "Eclipse Public License"
Expand Down
168 changes: 126 additions & 42 deletions src/alembic/still.clj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4388202
[classlojure.core :refer [base-classloader classlojure ext-classloader]
:as classlojure]
[clojure.java.io :refer [copy file reader resource]]
[clojure.pprint :refer [pprint]]
[dynapath.util :as util])
(:import
java.util.Properties))
Expand All @@ -27,8 +28,7 @@ http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4388202
{:pre [(.endsWith jar-path ".jar")]}
(let [jar-url (resource jar-path)
f (java.io.File/createTempFile
(subs jar-path 0 (- (count jar-path) 4)) ".jar")
]
(subs jar-path 0 (- (count jar-path) 4)) ".jar")]
(.deleteOnExit f)
(with-open [is (.getContent jar-url)]
(copy is f))
Expand Down Expand Up @@ -88,8 +88,8 @@ http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4388202
([]
(project-repositories the-still)))

(defn resolve-dependency
[still dependency repositories]
(defn resolve-dependencies
[still dependencies repositories]
(classlojure/eval-in
(:alembic-classloader @still)
`(mapv
Expand All @@ -99,7 +99,7 @@ http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4388202
:jar (-> dep# meta :file apath#)}))
(keys
(aether/resolve-dependencies
:coordinates '[~dependency]
:coordinates '~(vec dependencies)
:repositories ~repositories)))))

(defn properties-path
Expand Down Expand Up @@ -145,41 +145,55 @@ http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4388202
(doseq [{:keys [coords current-version]} dep-jars
:let [[artifact version] coords]]
(when (and current-version (not= current-version version))
(println "WARN:" artifact "version" version "requested, but "
(println "WARN:" artifact "version" version "requested, but"
current-version "already on classpath."))))

(defn conflicting-version?
"Predicate to check for a conflicting version."
[{:keys [coords current-version]}]
(and current-version (not= current-version (second coords))))

(defn add-dep-jars
"Add any non-conflicting dependency jars. Returns the sequence of
dependency jar maps of the loaded jars."
[still dep-jars]
(let [{:keys [classloader]} @still]
(doseq [{:keys [coords current-version jar]} dep-jars
:let [[artifact version] coords]]
(when-not current-version
(util/add-classpath-url classloader (.toURL (file jar)))))))


(defn add-dependency
"Add a dependency to the classpath. Returns a sequence of maps, each
containing a :coords vector, a :jar path and possibly
a :current-version string. If the dependency is already on the
classpath, returns nil. If the optional parameter :verbose is
(let [{:keys [classloader]} @still
deps (remove :current-version dep-jars)]
(doseq [{:keys [jar]} deps]
(util/add-classpath-url classloader (.toURL (file jar))))
deps))

(defn add-dependencies
"Add dependencies to the classpath. Returns a sequence of maps, each
containing a `:coords` vector, a `:jar` path and possibly a
`:current-version` string. If the optional parameter :verbose is
true (the default), then WARN messages will be printed to the console
if a version of a library is requested and the classpath already
contains a different version of the same library"
[still dependency repositories
& {:keys [verbose] :as opts :or {verbose true}}]
(when-not (meta-inf-properties-url still dependency)
(let [dep-jars (->> (resolve-dependency still dependency repositories)
(current-dep-versions still))]
(when verbose (warn-mismatch-versions dep-jars))
(add-dep-jars still dep-jars)
(swap! still (fn [m]
(-> m
(update-in [:dependencies] conj dependency)
(update-in [:jars] assoc dependency dep-jars))))
dep-jars)))
contains a different version of the same library."
[still dependencies repositories {:keys [verbose] :as opts
:or {verbose true}}]
(let [dep-jars (->> (resolve-dependencies still dependencies repositories)
(current-dep-versions still))]
(when verbose (warn-mismatch-versions dep-jars))
(add-dep-jars still dep-jars)
(swap! still (fn [m]
(-> m
(update-in [:dependencies]
#(distinct (concat % dependencies)))
(update-in [:jars]
#(distinct (concat % dep-jars))))))
dep-jars))

(defn distill
"Add a dependency to the classpath.
(defn print-coords
"Pretty print the dependency coordinates of a sequence of dependencies."
[deps]
(pprint (vec (sort-by first (map :coords deps)))))

(defn distill*
"Add dependencies to the classpath. Returns a sequence of dependency maps.

`dependencies` can be a coordinate vector, or a sequence of such
vectors.

`:repositories`
: specify a map of leiningen style repository definitions to be used when
Expand All @@ -194,12 +208,83 @@ contains a different version of the same library"
: specifies whether WARN messages should be printed to the console if
a version of library is requests and there is already a different
version of the same library in the classpath. Defaults to true"
[dependency & {:keys [repositories still verbose]
[dependencies {:keys [repositories still verbose]
:or {still the-still
verbose true}}]
(let [repositories (into {} (or repositories
(project-repositories still)))]
(add-dependency still dependency repositories :verbose verbose)))
(add-dependencies
still
(if (every? vector? dependencies) dependencies [dependencies])
repositories
{:verbose verbose})))

(defn distill
"Add dependencies to the classpath.

`dependencies` can be a coordinate vector, or a sequence of such vectors.

`:repositories`
: specify a map of leiningen style repository definitions to be used when
resolving. Defaults to the repositories specified in the current lein
project.

`:still`
: specifies an alembic still to use. This would be considered advanced
usage (see the tests for an example).

`:verbose`
: specifies whether WARN messages should be printed to the console if
a version of library is requests and there is already a different
version of the same library in the classpath. Defaults to true"
[dependencies & {:keys [repositories still verbose]
:or {still the-still
verbose true}
:as options}]
(let [dep-jars (distill* dependencies options)
loaded (remove conflicting-version? dep-jars)
conflicting (filter conflicting-version? dep-jars)]
(when (seq loaded)
(println "Loaded dependencies:")
(print-coords loaded))
(when (seq conflicting)
(println
"Dependencies not loaded due to conflict with previous jars :")
(print-coords conflicting))))

(defn load-project*
"Load project.clj dependencies. Returns a vector of jars required
for the dependencies. Loads any of the jars that are not conflicting
with versions already on the classpath."
[still project-file {:keys [verbose] :as options}]
(let [[dependencies repositories]
(classlojure/eval-in
(:alembic-classloader @still)
`(do
(require '[leiningen.core.project :as ~'project])
(let [project# (leiningen.core.project/read ~project-file)]
[(:dependencies project#)
(:repositories project#)])))]
(add-dependencies still dependencies (into {} repositories) options)))

(defn load-project
"Load project.clj dependencies. Prints the dependency jars that are
loaded, and those that were not loaded due to conflicts."
([still project-file]
(let [dep-jars (load-project* still project-file {:verbose true})
loaded (remove conflicting-version? dep-jars)
conflicting (filter conflicting-version? dep-jars)]
(when (seq loaded)
(println "Loaded dependencies:")
(print-coords loaded))
(when (seq conflicting)
(println
"Dependencies not loaded due to conflict with previous jars :")
(print-coords conflicting))))
([still]
(load-project still "project.clj"))
([]
(load-project the-still)))

(defn dependencies-added
([still]
Expand All @@ -209,12 +294,11 @@ contains a different version of the same library"
(defn dependency-jars
([still]
(:jars @still))
([] (dependencies-added the-still)))
([] (dependency-jars the-still)))

(defn conflicting-versions
"Return a sequence of possibly conflicting versions of jars required for the
specified dependency (dependency must have been added with `add-dependency`)."
([dependency still]
(filter :current-version (get (dependency-jars still) dependency)))
([dependency]
(conflicting-versions dependency the-still)))
"Return a sequence of possibly conflicting versions of jars required
for dependencies by the still)."
([still]
(filter conflicting-version? (dependency-jars still)))
([] (conflicting-versions the-still)))
6 changes: 2 additions & 4 deletions test/alembic/still_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,5 @@
"tools.logging on the classpath")
(is (= [tools-logging] (still/dependencies-added still))
"distilled dependency listed")
(is (= 1 (count (still/conflicting-versions tools-logging still)))
"possible distilled dependency conflict listed"))
(let [deps2 (still/distill tools-logging :still still)]
(is (nil? deps2) "Not loaded again"))))
(is (= 1 (count (still/conflicting-versions still)))
"possible distilled dependency conflict listed"))))