Skip to content

Commit

Permalink
Merge branch 'master' of git://github.com/technomancy/leiningen
Browse files Browse the repository at this point in the history
  • Loading branch information
danlarkin committed Dec 1, 2009
2 parents 238c5d0 + b9a0187 commit 1d202aa
Show file tree
Hide file tree
Showing 14 changed files with 158 additions and 107 deletions.
19 changes: 12 additions & 7 deletions README.md
Expand Up @@ -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/

Expand All @@ -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

Expand All @@ -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"]
Expand Down Expand Up @@ -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
Expand Down
22 changes: 10 additions & 12 deletions 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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion 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"]])
2 changes: 1 addition & 1 deletion project.clj
Expand Up @@ -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"]])
2 changes: 1 addition & 1 deletion 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"]])
18 changes: 14 additions & 4 deletions src/leiningen/clean.clj
Expand Up @@ -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)))
48 changes: 28 additions & 20 deletions 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)
Expand All @@ -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)))
24 changes: 14 additions & 10 deletions src/leiningen/help.clj
Expand Up @@ -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."))))
55 changes: 38 additions & 17 deletions 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))))
3 changes: 1 addition & 2 deletions src/leiningen/pom.clj
Expand Up @@ -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.
Expand Down
36 changes: 9 additions & 27 deletions src/leiningen/test.clj
Expand Up @@ -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]
Expand All @@ -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))))
2 changes: 2 additions & 0 deletions src/leiningen/uberjar.clj
Expand Up @@ -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)
Expand All @@ -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"))))
Expand Down

0 comments on commit 1d202aa

Please sign in to comment.