Skip to content

Commit

Permalink
Include bin scripts in generated jars when specified in :shell-wrapper.
Browse files Browse the repository at this point in the history
  • Loading branch information
technomancy committed Aug 10, 2010
1 parent 0966229 commit 8012a50
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 32 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ lib/
leiningen*.jar
lein-swank/lein-swank*.jar
autodoc/
/sample/pom.xml
2 changes: 1 addition & 1 deletion bin/lein
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ if [ -r "$BIN_DIR/../src/leiningen/core.clj" ]; then
# Running from source checkout
LEIN_DIR="$(dirname "$BIN_DIR")"
LEIN_LIBS="$(find -H $LEIN_DIR/lib -mindepth 2> /dev/null 1 -maxdepth 1 -print0 | tr \\0 \:)"
CLASSPATH="$LEIN_DIR/src:$LEIN_LIBS:$CLASSPATH:$LEIN_JAR"
CLASSPATH="$LEIN_DIR/src:$LEIN_DIR/resources:$LEIN_LIBS:$CLASSPATH:$LEIN_JAR"

if [ "$LEIN_LIBS" = "" -a "$1" != "self-install" -a ! -r "$LEIN_JAR" ]; then
echo "Leiningen is missing its dependencies. Please run \"lein self-install\"."
Expand Down
4 changes: 3 additions & 1 deletion sample/project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@
[rome/rome ~(str "0." "9")]
[org.ccil.cowan.tagsoup/tagsoup "1.2"]]
:main nom.nom.nom
:warn-on-reflection true)
:warn-on-reflection true
:shell-wrapper {:main nom.nom.nom
:bin "bin/nom"})
87 changes: 63 additions & 24 deletions src/leiningen/jar.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,64 @@
"Create a jar containing the compiled code and original source."
(:require [leiningen.compile :as compile])
(:use [leiningen.pom :only [make-pom make-pom-properties]]
[clojure.contrib.io :only [to-byte-array copy file]]
[clojure.contrib.string :only [join replace-re]])
[clojure.contrib.io :only [to-byte-array copy file slurp*]]
[clojure.contrib.string :only [join replace-re split]])
(:import [java.util.jar Manifest JarEntry JarOutputStream]
[java.util.regex Pattern]
[java.io BufferedOutputStream FileOutputStream
ByteArrayInputStream]))

(def bin-template (-> (.getContextClassLoader (Thread/currentThread))
(.getResourceAsStream "script-template")
(slurp*)))

(defn- classpath-path [group name version]
(format "$HOME/.m2/repository/%s/%s/%s/%s-%s.jar"
group name version name version))

(defn- script-classpath-for [project]
(str (classpath-path (:group project) (:name project) (:version project)) ":"
(join ":" (for [[dep version] (:dependencies project)
:let [group (or (namespace dep) (name dep))
group (.replaceAll group "\\." "/")]]
(classpath-path group (name dep) version)))))

(defn- shell-wrapper-bin [project]
(or (:bin (:shell-wrapper project)
(format "bin/%s" (:name project)))))

(defn- shell-wrapper-filespecs [project]
(when (:shell-wrapper project)
(let [main (or (:main (:shell-wrapper project)) (:main project))
bin (shell-wrapper-bin project)]
[{:type :bytes
:path bin
:bytes (.getBytes (format bin-template
(script-classpath-for project)
main))}])))

(def default-manifest
{"Created-By" (str "Leiningen " (System/getProperty "leiningen.version"))
"Built-By" (System/getProperty "user.name")
"Build-Jdk" (System/getProperty "java.version")})

(defn make-manifest [project]
(defn make-manifest [project & [extra-entries]]
(Manifest.
(ByteArrayInputStream.
(to-byte-array
(reduce (fn [manifest [k v]]
(str manifest "\n" k ": " v))
"Manifest-Version: 1.0"
(merge default-manifest (:manifest project)
(when (:shell-wrapper project)
{"Leiningen-shell-wrapper" (shell-wrapper-bin project)})
(when-let [main (:main project)]
{"Main-Class" (.replaceAll (str main) "-" "_")})))))))

(defn manifest-map [manifest]
(let [attrs (.getMainAttributes manifest)]
(zipmap (map str (keys attrs)) (vals attrs))))

(defn unix-path [path]
(.replaceAll path "\\\\" "/"))

Expand Down Expand Up @@ -59,11 +94,13 @@
(defmethod copy-to-jar nil [project jar-os spec])

(defn write-jar [project out-filename filespecs]
(with-open [jar-os (JarOutputStream. (BufferedOutputStream.
(FileOutputStream. out-filename))
(make-manifest project))]
(doseq [filespec filespecs]
(copy-to-jar project jar-os filespec))))
(let [manifest (make-manifest project)]
(with-open [jar-os (-> out-filename
(FileOutputStream.)
(BufferedOutputStream.)
(JarOutputStream. manifest))]
(doseq [filespec filespecs]
(copy-to-jar project jar-os filespec)))))

(defn get-default-jar-name [project]
(or (:jar-name project)
Expand All @@ -81,22 +118,24 @@
(str (:name project) \- (:version project) "-standalone.jar")))

(defn- filespecs [project]
[{:type :bytes
:path (format "META-INF/maven/%s/%s/pom.xml"
(:group project)
(:name project))
:bytes (make-pom project)}
{:type :bytes
:path (format "META-INF/maven/%s/%s/pom.properties"
(:group project)
(:name project))
:bytes (make-pom-properties project)}
(when (and (:resources-path project)
(.exists (file (:resources-path project))))
{:type :path :path (:resources-path project)})
{:type :path :path (:compile-path project)}
{:type :path :path (:source-path project)}
{:type :path :path (str (:root project) "/project.clj")}])
(concat
[{:type :bytes
:path (format "META-INF/maven/%s/%s/pom.xml"
(:group project)
(:name project))
:bytes (make-pom project)}
{:type :bytes
:path (format "META-INF/maven/%s/%s/pom.properties"
(:group project)
(:name project))
:bytes (make-pom-properties project)}
(when (and (:resources-path project)
(.exists (file (:resources-path project))))
{:type :path :path (:resources-path project)})
{:type :path :path (:compile-path project)}
{:type :path :path (:source-path project)}
{:type :path :path (str (:root project) "/project.clj")}]
(shell-wrapper-filespecs project)))

(defn jar
"Create a $PROJECT-$VERSION.jar file containing the compiled .class files as
Expand Down
33 changes: 27 additions & 6 deletions test/test_jar.clj
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
(ns test-jar
(:use [clojure.test]
[leiningen.core :only [defproject]]
[leiningen.jar]))
[clojure.contrib.io :only [slurp*]]
[leiningen.core :only [defproject read-project]]
[leiningen.jar])
(:import [java.util.jar JarFile]))

(defproject mock-project "1.0" :main foo.one-two.three-four.bar)
(defproject mock-project "1.0" :main foo.one-two.three-four.bar
:manifest {"hello" "world"})

(deftest test-manifest
(let [manifest (manifest-map (make-manifest mock-project))]
(is (= {"Main-Class" "foo.one_two.three_four.bar", "hello" "world"}
(select-keys manifest ["hello" "Main-Class"])))))

(def sample-project (binding [*ns* (the-ns 'leiningen.core)]
(read-project "sample/project.clj")))

(deftest test-jar
(is (= "foo.one_two.three_four.bar"
(.getValue (.getMainAttributes (make-manifest mock-project))
"Main-Class"))))
(let [jar-file (JarFile. (jar sample-project))
manifest (manifest-map (.getManifest jar-file))
bin (slurp* (.getInputStream jar-file (.getEntry jar-file "bin/nom")))]
(is (= "bin/nom" (manifest "Leiningen-shell-wrapper")))
(is (re-find #"org/clojure/clojure/1.1.0-master-SNAPSHOT/" bin))
(is (re-find #"use 'nom\.nom\.nom\)\(apply -main .command-line-args." bin))
(is (re-find #"\$HOME/\.m2/repository/rome/rome/0.9/rome-0\.9\.jar" bin))))

(deftest test-no-bin-jar
(let [jar-file (JarFile. (jar (dissoc sample-project :shell-wrapper)))
manifest (manifest-map (.getManifest jar-file))]
(is (nil? (.getEntry jar-file "bin/nom")))
(is (nil? (manifest "Leiningen-shell-wrapper")))))

0 comments on commit 8012a50

Please sign in to comment.