diff --git a/bin/lein b/bin/lein index 8cc9cdb8..f4e63848 100755 --- a/bin/lein +++ b/bin/lein @@ -119,10 +119,10 @@ if type -p curl >/dev/null 2>&1; then HTTP_CLIENT="curl $CURL_PROXY --insecure -f -L -o" fi -JAVA_CMD=${JAVA_CMD:-"java"} +export JAVA_CMD=${JAVA_CMD:-"java"} # Support $JAVA_OPTS for backwards-compatibility. -JVM_OPTS=${JVM_OPTS:-$JAVA_OPTS} +export JVM_OPTS=${JVM_OPTS:-$JAVA_OPTS} # If you're packaging this for a package manager (.deb, homebrew, etc) # you need to remove the self-install and upgrade functionality. @@ -215,9 +215,23 @@ else # doesn't exist everything will still work, it will just have a # slower JVM boot. test $CYGWIN_JLINE && stty -icanon min 1 -echo - exec $RLWRAP $JAVA_CMD -Xbootclasspath/a:"$CLOJURE_JAR" -client $JVM_OPTS \ - -Dleiningen.original.pwd="$ORIGINAL_PWD" \ - -cp "$CLASSPATH" $JLINE clojure.main -e "(use 'leiningen.core)(-main)" \ - $NULL_DEVICE "$@" - test $CYGWIN_JLINE && stty icanon echo + if [ "$1" = "trampoline" ]; then + TRAMPOLINE_COMMAND_FILE="/tmp/lein-trampoline-$$" + CMD=`$JAVA_CMD -Xbootclasspath/a:"$CLOJURE_JAR" -client $JVM_OPTS \ + -Dleiningen.original.pwd="$ORIGINAL_PWD" \ + -Dleiningen.trampoline-file=$TRAMPOLINE_COMMAND_FILE \ + -cp "$CLASSPATH" $JLINE clojure.main -e "(use 'leiningen.core)(-main)" \ + $NULL_DEVICE "$@"` + if [ -r $TRAMPOLINE_COMMAND_FILE ]; then + TRAMPOLINE_COMMAND=`cat $TRAMPOLINE_COMMAND_FILE` + rm $TRAMPOLINE_COMMAND_FILE + exec $TRAMPOLINE_COMMAND + fi + else + exec $RLWRAP $JAVA_CMD -Xbootclasspath/a:"$CLOJURE_JAR" -client $JVM_OPTS \ + -Dleiningen.original.pwd="$ORIGINAL_PWD" \ + -cp "$CLASSPATH" $JLINE clojure.main -e "(use 'leiningen.core)(-main)" \ + $NULL_DEVICE "$@" + test $CYGWIN_JLINE && stty icanon echo + fi fi diff --git a/src/leiningen/compile.clj b/src/leiningen/compile.clj index c5c1285e..1ef77436 100644 --- a/src/leiningen/compile.clj +++ b/src/leiningen/compile.clj @@ -84,9 +84,10 @@ (defn- get-raw-input-args [] (.getInputArguments (ManagementFactory/getRuntimeMXBean))) -(defn- get-input-args +(defn ^{:internal true} get-input-args "Returns a vector of input arguments, accounting for a bug in RuntimeMXBean - that splits arguments which contain spaces." + that splits arguments which contain spaces. Removes -Xbootclasspath as it + requires special handling on the called code." [] ;; RuntimeMXBean.getInputArguments() is buggy when an input argument ;; contains spaces. For an input argument of -Dprop="hello world" it @@ -95,7 +96,8 @@ (conj v arg) (conj (vec (butlast v)) (format "%s %s" (last v) arg))))] - (reduce join-broken-args [] (get-raw-input-args)))) + (remove #(re-find #"^-Xbootclasspath.+" %) + (reduce join-broken-args [] (get-raw-input-args))))) (defn- get-jvm-args [project] (concat (get-input-args) (:jvm-opts project) (:jvm-opts (user-settings)))) @@ -175,8 +177,7 @@ (.setFork java true) (.setDir java (file (:root project))) (doseq [arg (get-jvm-args project)] - (when-not (re-matches #"^-Xbootclasspath.+" arg) - (.setValue (.createJvmarg java) arg))) + (.setValue (.createJvmarg java) arg)) (.setClassname java "clojure.main") ;; to allow plugins and other tasks to customize (when handler diff --git a/src/leiningen/trampoline.clj b/src/leiningen/trampoline.clj new file mode 100644 index 00000000..7cecd50c --- /dev/null +++ b/src/leiningen/trampoline.clj @@ -0,0 +1,38 @@ +(ns leiningen.trampoline + (:refer-clojure :exclude [trampoline]) + (:use [leiningen.core :only [apply-task task-not-found abort]] + [leiningen.compile :only [get-input-args eval-in-project + get-readable-form]] + [leiningen.classpath :only [get-classpath-string]]) + (:require [clojure.string :as string])) + +(defn escape [form-string] + (format "\"%s\"" (.replaceAll form-string "\"" "\\\\\""))) + +(defn command-string [project java-cmd jvm-opts [_ form _ _ init]] + (string/join " " [java-cmd "-cp" (get-classpath-string project) + "clojure.main" "-e" + (escape (get-readable-form nil project form init))])) + +(defn write-trampoline [command] + (spit (System/getProperty "leiningen.trampoline-file") command)) + +(defn trampoline + "Calculate what needs to run in the project's process for the +provided task and run it after Leiningen's own process has exited +rather than as a subprocess of Leiningen's project. + +Use this to save memory or to work around things like Ant's stdin +issues. Not compatible with chaining." + [project task-name & args] + (let [java-cmd (format "%s/bin/java" (System/getProperty "java.home")) + jvm-opts (get-input-args) + jvm-opts (if (:debug project) + (conj jvm-opts "-Dclojure.debug=true") + jvm-opts) + eval-args (atom nil)] + (binding [eval-in-project #(do (reset! eval-args %&) 0)] + (apply-task task-name project args task-not-found)) + (if @eval-args + (write-trampoline (command-string project java-cmd jvm-opts @eval-args)) + (abort task-name "is not trampolineable."))))