Skip to content
Browse files

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

  • Loading branch information...
2 parents 238c5d0 + b9a0187 commit 1d202aa63e9f063b61039666097b0a7e896354b8 @danlarkin danlarkin committed Dec 1, 2009
View
19 README.md
@@ -37,7 +37,7 @@ versions see "Hacking" below.
$ lein deps # install dependencies in lib/
- $ lein test [PRED] # run the project's tests, optionally filtered on PRED
+ $ lein test [TESTS] # run the tests in the TESTS namespaces, or all tests
$ lein compile # ahead-of-time compile into classes/
@@ -51,7 +51,7 @@ versions see "Hacking" below.
$ lein pom # output a pom.xml file for interop with Maven
- $ lein install # install in local repo (currently requires mvn)
+ $ lein install # install in local repository
$ lein help [TASK] # show a list of tasks or help for a given TASK
@@ -62,6 +62,7 @@ versions see "Hacking" below.
Place a project.clj file in the project root that looks something like this:
(defproject leiningen "0.5.0-SNAPSHOT"
+ :description "A build tool designed not to set your hair on fire."
:dependencies [[org.clojure/clojure "1.1.0-alpha-SNAPSHOT"]
[org.clojure/clojure-contrib "1.0-SNAPSHOT"]
[ant/ant-launcher "1.6.2"]
@@ -135,13 +136,17 @@ self-installed in ~/.m2.
The [mailing list](http://groups.google.com/group/clojure) and the
leiningen or clojure channels on Freenode are the best places to
-bring up questions or suggestions.
+bring up questions or suggestions. If you're planning on adding a
+feature or fixing a nontrivial bug, please discuss it first to avoid
+duplicating effort.
Contributions are preferred as either Github pull requests or using
-"git format-patch" as described at http://clojure.org/patches. Please
-use standard indentation with no tabs, trailing whitespace, or lines
-longer than 80 columns. If you've got some time on your hands, reading
-http://mumble.net/~campbell/scheme/style.txt wouldn't hurt either.
+"git format-patch" as is requested [for contributing to Clojure
+itself](http://clojure.org/patches). Please use standard indentation
+with no tabs, trailing whitespace, or lines longer than 80 columns. If
+you've got some time on your hands, reading this [style
+guide](http://mumble.net/~campbell/scheme/style.txt) wouldn't hurt
+either.
Leiningen is extensible; you can define new tasks in plugins. Add your
plugin as a dev-dependency of your project, and you'll be able to call
View
22 bin/lein
@@ -1,15 +1,9 @@
#!/bin/bash
-# TODO: this gives us a trailing colon
VERSION="1.0.0-SNAPSHOT"
LIBS="$(find -H lib/ -mindepth 2> /dev/null 1 -maxdepth 1 -print0 | tr \\0 \:)"
CLASSPATH="src/:classes/:$LIBS"
-LEIN_JAR=$HOME/.m2/repository/leiningen/leiningen/$VERSION/leiningen-$VERSION.jar
-
-# this needs to exist before the JVM is launched apparently
-if [ -r project.clj ]; then
- mkdir -p classes
-fi
+LEIN_JAR=$HOME/.m2/repository/leiningen/leiningen/$VERSION/leiningen-$VERSION-standalone.jar
# normalize $0 on certain BSDs
if [ "$(dirname $0)" = "." ]; then
@@ -34,10 +28,14 @@ BIN_DIR="$(dirname "$SCRIPT")"
if [ -r "$BIN_DIR/../src/leiningen/core.clj" ]; then
# Running from source checkout
LEIN_DIR="$(dirname "$BIN_DIR")"
- CLASSPATH="$LEIN_DIR/src:$LEIN_DIR/classes:$LEIN_DIR/lib:$LEIN_JAR:$CLASSPATH"
+ LEIN_LIBS="$(find -H $LEIN_DIR/lib/ -mindepth 2> /dev/null 1 -maxdepth 1 -print0 | tr \\0 \:)"
+ CLASSPATH="$LEIN_DIR/src:$LEIN_LIBS:$CLASSPATH"
- if [ ! -r "lib/leiningen*jar" -a ! -r "$LEIN_JAR" -a "$1" != "self-install" ]; then
- echo "Leiningen is not installed. Please run \"lein self-install\"."
+ if [ "$LEIN_LIBS" = "" -a "$1" != "self-install" ]; then
+ echo "Your Leiningen development checkout is missing its dependencies."
+ echo "Please download a stable version of Leiningen to fetch the deps."
+ echo "See the \"Hacking\" section of the readme for details."
+ exit 1
fi
else
# Not running from a checkout
@@ -65,12 +63,12 @@ if [ "$1" = "compile" -o "$1" = "jar" -o "$1" = "uberjar" ]; then
fi
if [ "$1" = "repl" ]; then
- # If repl used leiningen.core then there'd be no way to bootstrap AOT
+ # TODO: use rlwrap if present
java -cp "$CLASSPATH" clojure.main
elif [ "$1" = "self-install" ]; then
echo "Downloading Leiningen now..."
mkdir -p `dirname "$LEIN_JAR"`
- LEIN_URL=http://repo.technomancy.us/leiningen-$VERSION.jar
+ LEIN_URL="http://repo.technomancy.us/leiningen-$VERSION-standalone.jar"
if type -p curl >/dev/null 2>&1; then
exec curl -o "$LEIN_JAR" "$LEIN_URL"
else
View
2 lein-swank/project.clj
@@ -1,3 +1,3 @@
-(defproject lein-swank "1.0.0-SNAPSHOT"
+(defproject leiningen/lein-swank "1.0.0-SNAPSHOT"
:dependencies [[leiningen "1.0.0-SNAPSHOT"]
[org.clojure/swank-clojure "1.0"]])
View
2 project.clj
@@ -8,4 +8,4 @@
[org.clojure/clojure-contrib "1.0-SNAPSHOT"]
[ant/ant-launcher "1.6.2"]
[org.apache.maven/maven-ant-tasks "2.0.10"]]
- :dev-dependencies [[org.clojure/swank-clojure "1.0"]])
+ :dev-dependencies [[leiningen/lein-swank "1.0.0-SNAPSHOT"]])
View
2 sample/project.clj
@@ -1,4 +1,4 @@
-(defproject nomnomnom "1.0-SNAPSHOT"
+(defproject nomnomnom "0.5.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.1.0-alpha-SNAPSHOT"]
[rome/rome "0.9"]
[org.ccil.cowan.tagsoup/tagsoup "1.2"]])
View
18 src/leiningen/clean.clj
@@ -3,9 +3,19 @@
(:use [clojure.contrib.java-utils :only [file delete-file
delete-file-recursively]]))
+(defn empty-directory
+ "Recursively delete all the files in f, but not f itself.
+Raise an exception if any deletion fails unless silently is true."
+ [f & [silently]]
+ (let [f (file f)]
+ (when (.isDirectory f)
+ (doseq [child (.listFiles f)]
+ (delete-file-recursively child silently)))))
+
(defn clean [project & args]
+ (println "Cleaning up")
(delete-file (str (:root project) "/" (:name project) ".jar") true)
- (doseq [d ["classes" "lib"]]
- (println "Cleaning " d)
- (doseq [f (.listFiles (file (:root project) d))]
- (delete-file-recursively f true))))
+ (delete-file (str (:root project) "/" (:name project) "-standalone.jar") true)
+ (doseq [d [(:compile-path project)
+ (str (:root project) "/lib")]]
+ (empty-directory (file d) true)))
View
48 src/leiningen/core.clj
@@ -1,24 +1,31 @@
(ns leiningen.core
- (:use [clojure.contrib.with-ns]))
+ (:use [clojure.contrib.with-ns])
+ (:import [java.io File]))
(def project nil)
(defmacro defproject [project-name version & args]
;; This is necessary since we must allow defproject to be eval'd in
;; any namespace due to load-file; we can't just create a var with
;; def or we would not have access to it once load-file returned.
- `(do (alter-var-root #'project
- (fn [_#] (assoc (apply hash-map (quote ~args))
+ `(do
+ (let [m# (apply hash-map (quote ~args))
+ root# ~(.getParent (java.io.File. *file*))]
+ (alter-var-root #'project
+ (fn [_#] (assoc m#
:name ~(name project-name)
:group ~(or (namespace project-name)
(name project-name))
:version ~version
- :root ~(.getParent (java.io.File. *file*)))))
- (def ~(symbol (name project-name)) project)))
+ :compile-path (or (:compile-path m#)
+ (str root# "/classes/"))
+ :root root#))))
+ (def ~(symbol (name project-name)) project)))
;; So it doesn't need to be fully-qualified in project.clj
(with-ns 'clojure.core (use ['leiningen.core :only ['defproject]]))
+;; TODO: prompt to run "new" if no project file is found
(defn read-project
([file] (load-file file)
project)
@@ -28,27 +35,28 @@
(def no-project-needed #{"new" "help"})
-(defn command-not-found [command project & _]
- (println command "is not a command. Use \"help\" to list all commands.")
+(defn task-not-found [task project & _]
+ (println task "is not a task. Use \"help\" to list all task.")
(System/exit 1))
-(defn resolve-command [command]
- (let [command-ns (symbol (str "leiningen." command))
- command (symbol command)]
+(defn resolve-task [task]
+ (let [task-ns (symbol (str "leiningen." task))
+ task (symbol task)]
(try
- (require command-ns)
- (ns-resolve command-ns command)
+ (require task-ns)
+ (ns-resolve task-ns task)
(catch java.io.FileNotFoundException e
- (partial command-not-found command)))))
+ (partial task-not-found task)))))
(defn main [args-string]
- (let [[command & args] (.split args-string " ")
- command (or (aliases command) command)
- project (if (no-project-needed command)
+ (let [[task & args] (.split args-string " ")
+ task (or (aliases task) task)
+ project (if (no-project-needed task)
(first args)
- (read-project))]
- (binding [*compile-path* (or (:compile-path project)
- (str (:root project) "/classes/"))]
- (apply (resolve-command command) project args))
+ (read-project))
+ compile-path (:compile-path project)]
+ (when compile-path (.mkdirs (File. compile-path)))
+ (binding [*compile-path* compile-path]
+ (apply (resolve-task task) project args))
;; In case tests or some other task started any:
(shutdown-agents)))
View
24 src/leiningen/help.clj
@@ -2,29 +2,33 @@
"Display a list of tasks or help for a given task."
(:use [clojure.contrib.find-namespaces :only [find-namespaces-on-classpath]]))
-(def tasks (filter #(re-find #"^leiningen\.(?!core)" (name %))
- (find-namespaces-on-classpath)))
+(def tasks (set (filter #(re-find #"^leiningen\.(?!core)" (name %))
+ (find-namespaces-on-classpath))))
-;; affected by clojure ticket #130: bug of AOT'd namespaces losing metadata
-(defn help-for [task]
+(defn help-for
+ "Help for a task is stored in its docstring, or if that's not present,
+ in its namespace."
+ [task]
(let [task-ns (symbol (str "leiningen." task))
_ (require task-ns)
task (ns-resolve task-ns (symbol task))]
- (println (or (:doc (meta task))
- (:doc (meta (find-ns task-ns)))))))
+ (or (:doc (meta task))
+ (:doc (meta (find-ns task-ns))))))
+;; affected by clojure ticket #130: bug of AOT'd namespaces losing metadata
(defn help-summary-for [task-ns]
(require task-ns)
(let [task-name (last (.split (name task-ns) "\\."))]
- (println task-name (apply str (repeat (- 7 (count task-name)) " "))
- "-" (:doc (meta (find-ns task-ns))))))
+ (str task-name (apply str (repeat (- 8 (count task-name)) " "))
+ " - " (:doc (meta (find-ns task-ns))))))
(defn help [project & [task]]
(if task
- (help-for task)
+ (println (help-for task))
(do (println "Leiningen is a build tool for Clojure.\n")
(println "Several tasks are available:")
(doseq [task-ns tasks]
- (help-summary-for task-ns))
+ ;; (println (help-summary-for task-ns))
+ (println " " (last (.split (name task-ns) "\\."))))
(println "\nRun lein help $TASK for details.")
(println "See http://github.com/technomancy/leiningen as well."))))
View
55 src/leiningen/install.clj
@@ -1,20 +1,41 @@
(ns leiningen.install
- "Install the project in your local repository. Currently requires Maven."
+ "Install the project and its dependencies in your local repository."
(:use [leiningen.jar :only [jar]]
- [leiningen.pom :only [pom]]
- [clojure.contrib.shell-out :only [sh with-sh-dir]])
- (:import [org.apache.maven.artifact.ant InstallTask Pom]))
+ [leiningen.pom :only [make-model]]
+ [clojure.contrib.java-utils :only [file]])
+ (:import [org.apache.maven.artifact.installer ArtifactInstaller]
+ [org.apache.maven.settings MavenSettingsBuilder]
+ [org.apache.maven.artifact.repository ArtifactRepositoryFactory]
+ [org.apache.maven.artifact.factory ArtifactFactory]
+ [org.apache.maven.artifact.repository.layout
+ ArtifactRepositoryLayout]
+ [org.codehaus.plexus.embed Embedder]))
-(defn install
- "Install the project and its dependencies into ~/.m2/repository using Maven."
- [project & args]
- (let [jarfile (jar project)]
- (pom project "pom-generated.xml" true)
- ;; TODO: use maven-ant-tasks InstallTask with in-memory Pom object
- (with-sh-dir (:root project)
- (try (sh "mvn" "install:install-file" "-DpomFile=pom-generated.xml"
- (str "-Dfile=" jarfile))
- (println "Installed" (:group project) "/" (:name project))
- (catch java.io.IOException _
- (.write *err* "Currently maven must be present to install.\n")
- (System/exit 1))))))
+;; Welcome to the absurdist self-parodying world of Dependency Injection
+(def container (.getContainer (doto (Embedder.) (.start))))
+
+(defn make-settings []
+ (.buildSettings (.lookup container MavenSettingsBuilder/ROLE)))
+
+(defn make-local-repo []
+ (let [path (.getLocalRepository (make-settings))
+ url (if (.startsWith path "file:") path (str "file://" path))]
+ (-> (.lookup container ArtifactRepositoryFactory/ROLE)
+ (.createDeploymentArtifactRepository
+ "local" url (.lookup container ArtifactRepositoryLayout/ROLE "default")
+ true))))
+
+(defn make-artifact [model]
+ (.createArtifactWithClassifier
+ (.lookup container ArtifactFactory/ROLE)
+ (.getGroupId model)
+ (.getArtifactId model)
+ (.getVersion model)
+ (.getPackaging model)
+ nil))
+
+(defn install [project & args]
+ (let [jarfile (file (jar project))
+ artifact (make-artifact (make-model project))
+ installer (.lookup container ArtifactInstaller/ROLE)]
+ (.install installer jarfile artifact (make-local-repo))))
View
3 src/leiningen/pom.clj
@@ -4,8 +4,7 @@
[clojure.contrib.java-utils :only [file as-properties]])
(:import [java.io StringWriter]
[org.apache.maven.model Model Parent Dependency Repository Scm]
- [org.apache.maven.project MavenProject]
- [org.apache.maven.artifact.ant Pom]))
+ [org.apache.maven.project MavenProject]))
(def #^{:doc "A notice to place at the bottom of generated files."} disclaimer
"\n<!-- This file was autogenerated by the Leiningen build tool.
View
36 src/leiningen/test.clj
@@ -5,15 +5,6 @@
[clojure.contrib.java-utils :only [file]]
[clojure.contrib.find-namespaces :only [find-namespaces-in-dir]]))
-(defonce old-test-var test-var)
-
-(defn test-var-matching [pred var]
- (when (pred (meta var))
- (old-test-var var)))
-
-(defn merge-predicates [preds]
- (fn [t] (every? #(% t) preds)))
-
(let [orig-report report
aggregates (ref [])]
(defn lein-report [event]
@@ -30,23 +21,14 @@
a))
@aggregates))))
-(defn run-matching [project preds]
- (binding [test-var (partial test-var-matching (merge-predicates preds))]
- (doseq [n (find-namespaces-in-dir
- (file (:root project) "test"))]
+(defn test
+ "Run the project's tests. Accept a list of namespaces for which to run all
+tests for. If none are given, runs them all."
+ [project & namespaces]
+ ;; TODO: System/exit appropriately (depends on Clojure ticket #193)
+ (doseq [n (if (empty? namespaces)
+ (find-namespaces-in-dir (file (:root project) "test"))
+ (map symbol namespaces))]
(require n)
(binding [report lein-report]
- (run-tests n)))
- (super-summary)))
-
-(defn test
- "Run the projects tests. Accept a list of predicates called with each test
-var's metadata. Does not support anonymous fns; works best with keywords."
- [project & args]
- ;; TODO: eval args; if they're namespaces, run their tests; if they're
- ;; ifn? then use them as predicates
- (let [preds (if (empty? args)
- [identity]
- (map (comp eval read-string) args))]
- ;; TODO: System/exit appropriately (depends on Clojure ticket #193)
- (run-matching project preds)))
+ (run-tests n))))
View
2 src/leiningen/uberjar.clj
@@ -24,6 +24,7 @@
(for [file (enumeration-seq (.entries in))
:when (not (skip-pred file))]
(do
+ (.setCompressedSize file -1) ; some jars report size incorrectly
(.putNextEntry out file)
(copy (.getInputStream in file) out)
(.closeEntry out)
@@ -46,6 +47,7 @@
(with-open [out (-> (file (:root project)
(str (:name project) "-standalone.jar"))
(FileOutputStream.) (ZipOutputStream.))]
+ ;; TODO: any way to make sure we skip dev dependencies?
(let [deps (->> (file-seq (file (:root project) "lib"))
(filter #(.endsWith (.getName %) ".jar"))
(cons (file (:root project) (str (:name project) ".jar"))))
View
16 test/test_install.clj
@@ -0,0 +1,16 @@
+(ns test-install
+ (:use [leiningen.core :only [read-project defproject]]
+ [leiningen.install] :reload-all)
+ (:use [clojure.test]
+ [clojure.contrib.java-utils :only [delete-file-recursively file]]
+ [clojure.contrib.shell-out :only [with-sh-dir sh]]))
+
+(def m2-dir (file (System/getProperty "user.home") ".m2" "repository"
+ "nomnomnom" "nomnomnom" "0.5.0-SNAPSHOT"))
+
+(defonce test-project (read-project "sample/project.clj"))
+
+(deftest test-install
+ (delete-file-recursively m2-dir true)
+ (install test-project)
+ (is (not (empty? (.listFiles m2-dir)))))
View
16 todo.org
@@ -6,20 +6,26 @@
Leiningen TODOs
* For 1.0
-** TODO Remove install task dependency on having Maven installed :Phil:
-** TODO Use -Xbootclasspath where possible
-** DONE Don't write manifest, pom, etc. to disk when jarring :Dan:
-** TODO Don't put uberjar in ~/.m2
+** DONE Remove install task dependency on having Maven installed :Phil:
+** TODO Use -Xbootclasspath where possible :Dan:
+** DONE Don't write manifest, pom, etc. to disk when jarring :Dan:
+** DONE Don't put uberjar in ~/.m2 :Phil:
** TODO Perform compilation in either a subprocess or with a separate classloader
-** TODO Allow test task to take namespaces as an argument
+** DONE Allow test task to take namespaces as an argument
** TODO System/exit appropriately when testing based on pass/fail
* Post 1.0
** TODO doc generation
** TODO user config file for activating plugins across all projects
** TODO write shell wrappers
** TODO advise existing tasks from plugins
+** Plugin Ideas:
+*** Graph output for dependencies between namespaces
+*** Start web server for web-related projects
+*** Multi-module builds
* Low Priority
** TODO Slim jar task
+** TODO Source jar task
+** TODO Run failed tests task
** TODO Remove duplication between deps.clj and pom.clj
** TODO Resources directory added to classpath? (for properties, etc)
*** This would be a good way for clojure code to have access to lein's version

0 comments on commit 1d202aa

Please sign in to comment.
Something went wrong with that request. Please try again.