Merge branch 'master' of github.com:technomancy/leiningen into printonly

This commit is contained in:
Anthony Grimes 2013-02-11 13:21:53 -08:00
commit d2f49ca6d1
13 changed files with 223 additions and 154 deletions

View file

@ -8,7 +8,7 @@
[classlojure "0.6.6"]
[useful "0.8.6"]
[robert/hooke "1.3.0"]
[com.cemerick/pomegranate "0.0.13"]]
[com.cemerick/pomegranate "0.0.14-SNAPSHOT"]]
:scm {:dir ".."}
;; This is only used when releasing Leiningen. Can't put it in a
;; profile since it must be installed using lein1

View file

@ -262,6 +262,26 @@
(.printStackTrace e)
(throw (ex-info "Classloader eval failed" {:exit-code 1}))))))
(defmethod eval-in :nrepl [project form]
(require 'clojure.tools.nrepl)
(let [port-file (io/file (:target-path project) "repl-port")
connect (resolve 'clojure.tools.nrepl/connect)
client (resolve 'clojure.tools.nrepl/client)
client-session (resolve 'clojure.tools.nrepl/client-session)
message (resolve 'clojure.tools.nrepl/message)
recv (resolve 'clojure.tools.nrepl.transport/recv)]
(if (.exists port-file)
(let [transport (connect :host "localhost"
:port (Integer. (slurp port-file)))
client (client-session (client transport Long/MAX_VALUE))]
(message client {:op "eval" :code (pr-str form)})
(doseq [{:keys [out err status]} (repeatedly #(recv transport 100))
:while (not (some #{"done" "interrupted" "error"} status))]
(when out (println out))
(when err (binding [*out* *err*] (println err)))))
;; TODO: warn that repl couldn't be used?
(eval-in (assoc project :eval-in :subprocess) form))))
(defmethod eval-in :leiningen [project form]
(when (:debug project)
(System/setProperty "clojure.debug" "true"))

View file

@ -63,6 +63,51 @@
(into [(symbol group-id artifact-id) version]))
(with-meta (meta dep)))))
(defn- displace?
"Returns true if the object is marked as displaceable"
[obj]
(-> obj meta :displace))
(defn- replace?
"Returns true if the object is marked as replaceable"
[obj]
(-> obj meta :replace))
(defn- different-priority?
"Returns true if either left has a higher priority than right or vice versa."
[left right]
(boolean
(some (some-fn nil? displace? replace?) [left right])))
(defn- pick-prioritized
"Picks the highest prioritized element of left and right and merge their
metadata."
[left right]
(cond (nil? left) right
(nil? right) left
(and (displace? left) ;; Pick the rightmost
(displace? right)) ;; if both are marked as displaceable
(with-meta right
(merge (meta left) (meta right)))
(and (replace? left) ;; Pick the rightmost
(replace? right)) ;; if both are marked as replaceable
(with-meta right
(merge (meta left) (meta right)))
(or (displace? left)
(replace? right))
(with-meta right
(merge (-> left meta (dissoc :displace))
(-> right meta (dissoc :replace))))
(or (replace? left)
(displace? right))
(with-meta left
(merge (-> right meta (dissoc :displace))
(-> left meta (dissoc :replace))))))
(declare meta-merge)
;; TODO: drop this and use read-eval syntax in 3.0
@ -126,11 +171,12 @@
(meta repos)))
(defn- add-repo [repos [id opts :as repo]]
;; TODO - we completely ignore metadata here. Should follow
;; ^:replace/^:displace conventions and merge metadata
(update-first repos #(= id (first %))
(fn [[_ existing]]
[id (meta-merge existing opts)])))
(fn [[_ existing :as original]]
(if (different-priority? repo original)
(pick-prioritized repo original)
(with-meta [id (meta-merge existing opts)]
(merge (meta original) (meta repo)))))))
(def empty-dependencies
(with-meta [] {:reduce add-dep}))
@ -264,43 +310,13 @@
:offline {:offline? true}
:debug {:debug true}}))
(defn- displace?
"Returns true if the object is marked as displaceable"
[obj]
(-> obj meta :displace))
(defn- replace?
"Returns true if the object is marked as replaceable"
[obj]
(-> obj meta :replace))
(defn- meta-merge
"Recursively merge values based on the information in their metadata."
[left right]
(cond (nil? left) right
(nil? right) left
(and (displace? left) ;; Pick the rightmost
(displace? right)) ;; if both are marked as displaceable
(with-meta right
(merge (meta left) (meta right)))
(and (replace? left) ;; Pick the rightmost
(replace? right)) ;; if both are marked as replaceable
(with-meta right
(merge (meta left) (meta right)))
(or (displace? left)
(replace? right))
(with-meta right
(merge (-> left meta (dissoc :displace))
(-> right meta (dissoc :replace))))
(or (replace? left)
(displace? right))
(with-meta left
(merge (-> right meta (dissoc :displace))
(-> left meta (dissoc :replace))))
(cond (different-priority? left right)
(pick-prioritized left right)
(-> left meta :reduce)
(-> left meta :reduce

View file

@ -253,6 +253,11 @@
[["clojars.org" "https://new-link.org/"]]}
:blue {:repositories
[["my-repo" "https://my-repo.org/"]]}
:red {:repositories
[^:replace ["my-repo" "https://my-repo.org/red"]]}
:green {:repositories
[^:displace
["my-repo" "https://my-repo.org/green"]]}
:empty {:repositories ^:replace []}}})]
(is (= default-repositories
(:repositories project)))
@ -272,6 +277,18 @@
(is (= [["clojars.org" {:url "https://new-link.org/"}]
["my-repo" {:url "https://my-repo.org/"}]]
(-> (merge-profiles project [:clojars :blue :clj-2])
:repositories)))
(is (= [["clojars.org" {:url "https://clojars.org/repo/"}]
["my-repo" {:url "https://my-repo.org/"}]]
(-> (merge-profiles project [:clojars :blue :green])
:repositories)))
(is (= [["clojars.org" {:url "https://clojars.org/repo/"}]
["my-repo" {:url "https://my-repo.org/red"}]]
(-> (merge-profiles project [:blue :clojars :red])
:repositories)))
(is (= [["my-repo" {:url "https://my-repo.org/red"}]
["clojars.org" {:url "https://new-link.org/"}]]
(-> (merge-profiles project [:empty :red :clj-2 :green])
:repositories))))))
(deftest test-global-exclusions

View file

@ -15,7 +15,7 @@
org.sonatype.aether/aether-api
org.sonatype.aether/aether-util
org.sonatype.sisu/sisu-inject-plexus]]
[reply "0.1.9" :exclusions [ring/ring-core]]
[reply "0.1.10-SNAPSHOT" :exclusions [ring/ring-core]]
;; drawbridge specifies an ancient version here, so bump it
[clj-http "0.5.8" :exclusions [crouton cheshire]]]
;; checkout-deps don't work with :eval-in :leiningen

View file

@ -47,19 +47,25 @@
(main/abort "Could not sign" file))
(str file ".asc")))
(defn signatures-for [jar-file pom-file coords]
{(into coords [:extension "jar.asc"]) (sign jar-file)
(into coords [:extension "pom.asc"]) (sign pom-file)})
(defn signature-for [extension file]
{[:extension extension] (sign file)})
(defn files-for [project repo]
(let [coords [(symbol (:group project) (:name project)) (:version project)]
jar-file (jar/jar project)
pom-file (pom/pom project)]
(merge {(into coords [:extension "jar"]) jar-file
(into coords [:extension "pom"]) pom-file}
(if (and (:sign-releases (second repo) true)
(not (.endsWith (:version project) "-SNAPSHOT")))
(signatures-for jar-file pom-file coords)))))
(defn signature-for-artifact [[coords artifact-file]]
{(apply concat
(update-in
(apply hash-map coords) [:extension]
#(str (or % "jar") ".asc")))
(sign artifact-file)})
(defn sign-for-repo? [repo]
(:sign-releases (second repo) true))
(defn files-for [project signed?]
(let [artifacts (merge {[:extension "pom"] (pom/pom project)}
(jar/jar project))]
(if (and signed? (not (.endsWith (:version project) "-SNAPSHOT")))
(reduce merge artifacts (map signature-for-artifact artifacts))
artifacts)))
(defn warn-missing-metadata [project]
(doseq [key [:description :license :url]]
@ -82,13 +88,15 @@ configure your credentials so you are not prompted on each deploy."
([project repository-name]
(warn-missing-metadata project)
(let [repo (repo-for project repository-name)
files (files-for project repo)]
files (files-for project (sign-for-repo? repo))]
(try
(main/debug "Deploying" files "to" repo)
(aether/deploy-artifacts :artifacts (keys files)
:files files
:transfer-listener :stdout
:repository [repo])
(aether/deploy
:coordinates [(symbol (:group project) (:name project))
(:version project)]
:artifact-map files
:transfer-listener :stdout
:repository [repo])
(catch org.sonatype.aether.deployment.DeploymentException e
(when main/*debug* (.printStackTrace e))
(main/abort (abort-message (.getMessage e)))))))

View file

@ -10,14 +10,13 @@
(defn install
"Install current project to the local repository."
([project]
(let [jarfile (jar/jar project)
pomfile (pom/pom project)
local-repo (:local-repo project)]
(println "Installing" jarfile)
(aether/install :coordinates [(symbol (:group project)
(:name project))
(:version project)]
:jar-file (io/file jarfile)
:pom-file (io/file pomfile)
:local-repo local-repo))))
[project]
(let [jarfiles (jar/jar project)
pomfile (pom/pom project)
local-repo (:local-repo project)]
(aether/install
:coordinates [(symbol (:group project) (:name project))
(:version project)]
:artifact-map jarfiles
:pom-file (io/file pomfile)
:local-repo local-repo)))

View file

@ -126,14 +126,15 @@
(:filespecs project)))
(defn get-jar-filename
([project uberjar?]
([project classifier]
(let [target (doto (io/file (:target-path project)) .mkdirs)
suffix (if uberjar? "-standalone.jar" ".jar")
suffix (if classifier (str "-" (name classifier) ".jar") ".jar")
;; TODO: splice in version to :jar-name
jar-name (or (project (if uberjar? :uberjar-name :jar-name))
name-kw (if (= classifier :standalone) :uberjar-name :jar-name)
jar-name (or (project name-kw)
(str (:name project) "-" (:version project) suffix))]
(str (io/file target jar-name))))
([project] (get-jar-filename project false)))
([project] (get-jar-filename project nil)))
(def whitelist-keys
"Project keys which don't affect the production of the jar should be
@ -151,6 +152,41 @@ propagated to the compilation phase and not stripped out."
(update-in project [:aot] conj (:main project))
project)))
(defn classifier-jar
"Package up all the project's classified files into a jar file.
Create a $PROJECT-$VERSION-$CLASSIFIER.jar file containing project's source
files as well as .class files if applicable. The classifier is looked up in the
project`s :classifiers map. If it's a map, it's merged like a profile. If it's a
keyword, it's looked up in :profiles before being merged."
[{:keys [target-path] :as project} classifier spec]
(let [spec (assoc (if (keyword? spec)
(-> project :profiles spec)
spec)
:target-path (.getPath (io/file target-path (name classifier))))
project (-> (project/unmerge-profiles project [:default])
(project/merge-profiles [spec])
(merge (select-keys project whitelist-keys)))]
(eval/prep project)
(let [jar-file (get-jar-filename project classifier)]
(write-jar project jar-file (filespecs project []))
(main/info "Created" (str jar-file))
jar-file)))
(defn classifier-jars
"Package up all the project's classified files into jar files.
Create a $PROJECT-$VERSION-$CLASSIFIER.jar file for each entry in the project's
:classifiers. Returns a map of :classifier/:extension coordinates to files."
[{:keys [classifiers] :as project}]
(reduce
(fn [result [classifier spec]]
(assoc result
[:classifier (name classifier) :extension "jar"]
(classifier-jar project classifier spec)))
{}
classifiers))
(defn jar
"Package up all the project's files into a jar file.
@ -168,5 +204,6 @@ With an argument, the jar will be built with an alternate main."
(let [jar-file (get-jar-filename project)]
(write-jar project jar-file (filespecs project []))
(main/info "Created" (str jar-file))
jar-file)))
(merge {[:extension "jar"] jar-file}
(classifier-jars project)))))
([project] (jar project nil)))

View file

@ -35,24 +35,45 @@
(:leiningen/repl (:profiles project) base-profile)
(:repl (:profiles project)) (:repl (user/profiles))])
(defn- handler-for [{{:keys [nrepl-middleware nrepl-handler]} :repl-options}]
(defn- init-ns [{{:keys [init-ns]} :repl-options, :keys [main]}]
(or init-ns main))
(defn- wrap-init-ns [project]
(when-let [init-ns (init-ns project)]
;; set-descriptor! currently nREPL only accepts a var
`(with-local-vars
[wrap-init-ns#
(fn [h#]
(fn [{:keys [~'session] :as msg#}]
(when-not (@~'session 'init-ns-sentinel#)
(swap! ~'session assoc (var *ns*) (create-ns '~init-ns)
'init-ns-sentinel# true))
(h# msg#)))]
(doto wrap-init-ns#
(clojure.tools.nrepl.middleware/set-descriptor!
{:requires #{(var clojure.tools.nrepl.middleware.session/session)}
:expects #{"eval"}})
(alter-var-root (constantly @wrap-init-ns#))))))
(defn- handler-for [{{:keys [nrepl-middleware nrepl-handler]} :repl-options,
:as project}]
(when (and nrepl-middleware nrepl-handler)
(main/abort "Can only use one of" :nrepl-handler "or" :nrepl-middleware))
(if nrepl-middleware
`(clojure.tools.nrepl.server/default-handler
~@(map #(if (symbol? %) (list 'var %) %) nrepl-middleware))
(or nrepl-handler '(clojure.tools.nrepl.server/default-handler))))
(let [nrepl-middleware (remove nil? (concat [(wrap-init-ns project)]
nrepl-middleware))]
(or nrepl-handler
`(clojure.tools.nrepl.server/default-handler
~@(map #(if (symbol? %) (list 'var %) %) nrepl-middleware)))))
(defn- init-requires [{{:keys [nrepl-middleware nrepl-handler]} :repl-options
:as project}
& nses]
:as project} & nses]
(let [defaults '[clojure.tools.nrepl.server complete.core]
nrepl-syms (->> (cons nrepl-handler nrepl-middleware)
(filter symbol?)
(map namespace)
(remove nil?)
(map symbol))]
(for [n (concat defaults nrepl-syms nses)]
(for [n (concat (remove nil? [(init-ns project)]) defaults nrepl-syms nses)]
(list 'quote n))))
(defn- start-server [project host port ack-port & [headless?]]
@ -62,7 +83,6 @@
:handler ~(handler-for project))
port# (-> server# deref :ss .getLocalPort)]
(when ~headless? (println "nREPL server started on port" port#))
(do ~(if headless? (-> project :repl-options :init)))
(spit ~(str (io/file (:target-path project) "repl-port")) port#)
(.deleteOnExit (io/file ~(:target-path project) "repl-port"))
@(promise))]
@ -70,8 +90,13 @@
(eval/eval-in-project
(project/merge-profiles project
(profiles-for project false (not headless?)))
server-starting-form
`(require ~@(init-requires project)))
`(do ~(-> project :repl-options :init)
~server-starting-form)
`(do ~@(for [n (init-requires project)]
`(try (require ~n)
(catch Throwable t#
(println "Error loading" (str ~n ":")
(or (.getMessage t#) (type t#))))))))
(eval server-starting-form))))
(defn- repl-port [project]
@ -104,12 +129,7 @@
(:repl-options project))]
(clojure.set/rename-keys
(merge
repl-options
;; TODO: make this consistent with :injections
{:init (if-let [init-ns (or (:init-ns repl-options) (:main project))]
`(do (require '~init-ns) (in-ns '~init-ns)
~(:init repl-options))
(:init repl-options))}
(dissoc repl-options :init)
(cond
attach
{:attach (if-let [host (repl-host project)]
@ -119,8 +139,7 @@
{:port (str port)}
:else
{}))
{:prompt :custom-prompt
:init :custom-init})))
{:prompt :custom-prompt})))
(defn- trampoline-repl [project]
(let [options (options-for-reply project :port (repl-port project))]
@ -176,7 +195,7 @@ and port."
(when project @prep-blocker)
(if-let [repl-port (nrepl.ack/wait-for-ack (-> project
:repl-options
(:timeout 30000)))]
(:timeout 60000)))]
(do
(println "nREPL server started on port" repl-port)
(reply/launch-nrepl (options-for-reply project :attach repl-port)))

View file

@ -133,8 +133,8 @@
(defn test
"Run the project's tests.
Marking deftest forms with metadata allows you to pick selectors to specify
a subset of your test suite to run:
Marking deftest or ns forms with metadata allows you to pick selectors to
specify a subset of your test suite to run:
(deftest ^:integration network-heavy-test
(is (= [1 2 3] (:numbers (network-operation)))))
@ -148,7 +148,11 @@ Write the selectors in project.clj:
Arguments to this task will be considered test selectors if they are keywords;
if they are symbols they will be treated as a list of test namespaces to run.
With no arguments the :default test selector is used if present, otherwise all
tests are run."
tests are run.
A default :only test-selector is available to run select tests. For example,
`lein test :only leiningen.test.test/test-default-selector` only runs the
specified test."
[project & tests]
(binding [main/*exit-process?* (if (= :leiningen (:eval-in project))
false

View file

@ -82,7 +82,7 @@ as well as defining a -main function."
(catch Exception e
(main/abort "Uberjar aborting because jar/compilation failed:"
(.getMessage e)))))
(let [standalone-filename (jar/get-jar-filename project :uberjar)]
(let [standalone-filename (jar/get-jar-filename project :standalone)]
(with-open [out (-> standalone-filename
(FileOutputStream.)
(ZipOutputStream.))]

View file

@ -28,9 +28,11 @@
(is (jar sample-no-aot-project))))
(deftest test-no-deps-jar
(let [jar-file (jar (dissoc sample-project :dependencies :main))]
(and (is (not (number? jar-file)))
(is (.exists (io/file jar-file))))))
(let [[coord jar-file] (first
(jar (dissoc sample-project :dependencies :main)))]
(is (not (number? jar-file)))
(is (.exists (io/file jar-file)))
(is (= coord [:extension "jar"]))))
(deftest overlapped-paths
(is (jar overlapped-sourcepaths-project)))

View file

@ -7,16 +7,12 @@
(deftest test-options-for-reply-empty
(let [project {}]
(is (= {:attach "127.0.0.1:9876"
:custom-init nil
:history-file history-file}
(is (= {:attach "127.0.0.1:9876" :history-file history-file}
(options-for-reply project :attach 9876)))))
(deftest test-options-for-reply-host
(let [project {:repl-options {:host "192.168.0.10"}}]
(is (= {:attach "192.168.0.10:9876"
:host "192.168.0.10"
:custom-init nil
(is (= {:attach "192.168.0.10:9876" :host "192.168.0.10"
:history-file history-file}
(options-for-reply project :attach 9876)))))
@ -24,56 +20,7 @@
(let [prompt-fn (fn [ns] "hi ")
project {:repl-options {:prompt prompt-fn}}]
(is (= {:attach "127.0.0.1:9876"
:custom-prompt prompt-fn
:custom-init nil
:history-file history-file}
(options-for-reply project :attach 9876)))))
(deftest test-options-for-reply-init
(let [init-form '(println "ohai")
project {:repl-options {:init init-form}}]
(is (= {:attach "127.0.0.1:9876"
:custom-init init-form
:history-file history-file}
(options-for-reply project :attach 9876)))))
(deftest test-options-for-reply-init-ns
(let [project {:repl-options {:init-ns 'foo.core}}]
(is (= {:attach "127.0.0.1:9876"
:init-ns 'foo.core
:custom-init '(do (clojure.core/require 'foo.core)
(clojure.core/in-ns 'foo.core)
nil)
:history-file history-file}
(options-for-reply project :attach 9876)))))
(deftest test-options-for-reply-init-ns-and-init
(let [project {:repl-options {:init-ns 'foo.core :init '(println "ohai")}}]
(is (= {:attach "127.0.0.1:9876"
:init-ns 'foo.core
:custom-init '(do (clojure.core/require 'foo.core)
(clojure.core/in-ns 'foo.core)
(println "ohai"))
:history-file history-file}
(options-for-reply project :attach 9876)))))
(deftest test-options-for-reply-main-ns
(let [project {:main 'foo.core}]
(is (= {:attach "127.0.0.1:9876"
:custom-init '(do (clojure.core/require 'foo.core)
(clojure.core/in-ns 'foo.core)
nil)
:history-file history-file}
(options-for-reply project :attach 9876)))))
(deftest test-options-for-reply-init-ns-beats-main
(let [project {:main 'foo.core :repl-options {:init-ns 'winner.here}}]
(is (= {:attach "127.0.0.1:9876"
:init-ns 'winner.here
:custom-init '(do (clojure.core/require 'winner.here)
(clojure.core/in-ns 'winner.here)
nil)
:history-file history-file}
:custom-prompt prompt-fn :history-file history-file}
(options-for-reply project :attach 9876)))))
(deftest repl-profile-in-project