Add leiningen.core.eval; still needs plenty of work.
This commit is contained in:
parent
91d7a283f9
commit
8131832fdd
1 changed files with 162 additions and 0 deletions
162
leiningen-core/src/leiningen/core/eval.clj
Normal file
162
leiningen-core/src/leiningen/core/eval.clj
Normal file
|
@ -0,0 +1,162 @@
|
|||
(ns leiningen.core.eval
|
||||
(:require [clojure.java.io :as io]
|
||||
[clojure.string :as string]
|
||||
[leiningen.core.user :as user]
|
||||
[leiningen.core.classpath :as classpath])
|
||||
(:import (java.io PushbackReader)))
|
||||
|
||||
(defn- get-by-pattern
|
||||
"Gets a value from map m, but uses the keys as regex patterns, trying
|
||||
to match against k instead of doing an exact match."
|
||||
[m k]
|
||||
(m (first (drop-while #(nil? (re-find (re-pattern %) k))
|
||||
(keys m)))))
|
||||
|
||||
(def ^{:private true} native-names
|
||||
{"Mac OS X" :macosx "Windows" :windows "Linux" :linux
|
||||
"FreeBSD" :freebsd "OpenBSD" :openbsd
|
||||
"amd64" :x86_64 "x86_64" :x86_64 "x86" :x86 "i386" :x86
|
||||
"arm" :arm "SunOS" :solaris "sparc" :sparc "Darwin" :macosx})
|
||||
|
||||
(defn get-os
|
||||
"Returns a keyword naming the host OS."
|
||||
[]
|
||||
(get-by-pattern native-names (System/getProperty "os.name")))
|
||||
|
||||
(defn get-arch
|
||||
"Returns a keyword naming the host architecture"
|
||||
[]
|
||||
(get-by-pattern native-names (System/getProperty "os.arch")))
|
||||
|
||||
(defn native-arch-path
|
||||
"Path to the os/arch-specific directory containing native libs."
|
||||
[project]
|
||||
(when (and (get-os) (get-arch))
|
||||
(io/file (:native-path project) (name (get-os)) (name (get-arch)))))
|
||||
|
||||
(defn platform-nullsink []
|
||||
(io/file (if (= :windows (get-os))
|
||||
"NUL"
|
||||
"/dev/null")))
|
||||
|
||||
(defn- as-str [x]
|
||||
(if (instance? clojure.lang.Named x)
|
||||
(name x)
|
||||
(str x)))
|
||||
|
||||
(defn- d-property [[k v]]
|
||||
(format "-D%s=%s" (as-str k) v))
|
||||
|
||||
(defn ^{:internal true} get-jvm-args [project]
|
||||
(let [native-arch-path (native-arch-path project)]
|
||||
`(~@(let [opts (System/getenv "JVM_OPTS")]
|
||||
(when (seq opts) [opts]))
|
||||
~@(:jvm-opts project)
|
||||
~@(:jvm-opts (user/settings))
|
||||
~@(map d-property {:clojure.compile.path (:compile-path project)
|
||||
(str (:name project) ".version") (:version project)
|
||||
:clojure.debug (boolean (or (System/getenv "DEBUG")
|
||||
(:debug project)))})
|
||||
~@(when (and native-arch-path (.exists native-arch-path))
|
||||
[(d-property [:java-library-path native-arch-path])]))))
|
||||
|
||||
(defn- injected-forms []
|
||||
(with-open [rdr (-> "robert/hooke.clj" io/resource io/reader PushbackReader.)]
|
||||
`(do (ns ~'leiningen.util.injected)
|
||||
~@(doall (take 6 (rest (repeatedly #(read rdr)))))
|
||||
(ns ~'user))))
|
||||
|
||||
(defn get-readable-form [java project form init]
|
||||
(let [form `(do ~init
|
||||
~(:project-init project)
|
||||
~@(let [user-clj (io/file (user/leiningen-home) "user.clj")]
|
||||
(if (.exists user-clj)
|
||||
[(list 'load-file (str user-clj))]))
|
||||
~(injected-forms)
|
||||
(set! ~'*warn-on-reflection*
|
||||
~(:warn-on-reflection project))
|
||||
(try ~form
|
||||
;; non-daemon threads will prevent process from exiting;
|
||||
;; see http://tinyurl.com/2ueqjkl
|
||||
(finally
|
||||
(when (and ~(:shutdown-agents project false)
|
||||
(not= "1.5" (System/getProperty
|
||||
"java.specification.version"))
|
||||
;; ~(not *interactive?*)
|
||||
)
|
||||
(shutdown-agents)))))]
|
||||
;; work around java's command line handling on windows
|
||||
;; http://bit.ly/9c6biv This isn't perfect, but works for what's
|
||||
;; currently being passed; see
|
||||
;; http://www.perlmonks.org/?node_id=300286 for some of the
|
||||
;; landmines involved in doing it properly
|
||||
(if (and (= (get-os) :windows) (not (:eval-in-leiningen project)))
|
||||
(pr-str (pr-str form))
|
||||
(pr-str form))))
|
||||
|
||||
(defn prep [{:keys [compile-path checksum-deps] :as project} skip-auto-compile]
|
||||
;; (when (and (not (or ;; *skip-auto-compile*
|
||||
;; skip-auto-compile)) compile-path
|
||||
;; (empty? (.list (io/file compile-path))))
|
||||
;; (binding [;; *silently* true
|
||||
;; ]
|
||||
;; (compile project)))
|
||||
;; (when (or (empty? (find-deps-files project)) checksum-deps)
|
||||
;; (deps project))
|
||||
;; (when compile-path
|
||||
;; (.mkdirs (io/file compile-path)))
|
||||
)
|
||||
|
||||
(defn eval-in-leiningen [project form-string]
|
||||
;; bootclasspath workaround: http://dev.clojure.org/jira/browse/CLJ-673
|
||||
(require '[clojure walk repl])
|
||||
(require '[clojure.java io shell browse])
|
||||
(when (:debug project)
|
||||
(System/setProperty "clojure.debug" "true"))
|
||||
;; need to at least pretend to return an exit code
|
||||
(try (binding [*warn-on-reflection* (:warn-on-reflection project), *ns* *ns*]
|
||||
(eval (read-string form-string)))
|
||||
0
|
||||
(catch Exception e
|
||||
(.printStackTrace e)
|
||||
1)))
|
||||
|
||||
(defn- pump [reader out]
|
||||
(let [buffer (make-array Character/TYPE 1000)]
|
||||
(loop [len (.read reader buffer)]
|
||||
(when-not (neg? len)
|
||||
(.write out buffer 0 len)
|
||||
(.flush out)
|
||||
(Thread/sleep 100)
|
||||
(recur (.read reader buffer))))))
|
||||
|
||||
;; clojure.java.shell/sh doesn't let you stream out/err
|
||||
(defn sh [& cmd]
|
||||
(let [proc (.exec (Runtime/getRuntime) (into-array cmd))]
|
||||
(with-open [out (io/reader (.getInputStream proc))
|
||||
err (io/reader (.getErrorStream proc))]
|
||||
(let [pump-out (doto (Thread. #(pump out *out*)) .start)
|
||||
pump-err (doto (Thread. #(pump err *err*)) .start)]
|
||||
(.join pump-out)
|
||||
(.join pump-err))
|
||||
(.waitFor proc))))
|
||||
|
||||
(defn eval-in-subprocess [project form-string]
|
||||
(apply sh `(~(or (System/getenv "JAVA_CMD") "java")
|
||||
"-cp" ~(string/join java.io.File/pathSeparatorChar
|
||||
(classpath/get-classpath project))
|
||||
~@(get-jvm-args project)
|
||||
"clojure.main" "-e" ~form-string)))
|
||||
|
||||
(defn eval-in-project
|
||||
"Executes form in an isolated classloader with the classpath and compile path
|
||||
set correctly for the project. If the form depends on any requires, put them
|
||||
in the init arg to avoid the Gilardi Scenario: http://technomancy.us/143"
|
||||
[project form & [handler skip-auto-compile init]]
|
||||
(when skip-auto-compile
|
||||
(println "WARNING: eval-in-project's skip-auto-compile arg is deprecated."))
|
||||
(prep project skip-auto-compile)
|
||||
(let [form-string (get-readable-form nil project form init)]
|
||||
(if (:eval-in-leiningen project)
|
||||
(eval-in-leiningen project form-string)
|
||||
(eval-in-subprocess project form-string))))
|
Loading…
Reference in a new issue