PageRenderTime 51ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/src/cake/project.clj

https://github.com/kulasama/cake
Clojure | 237 lines | 208 code | 28 blank | 1 comment | 25 complexity | 95209ad50efc00478167cdb8ebfb185f MD5 | raw file
  1. (ns cake.project
  2. (:use cake
  3. [cake.deps :only [deps]]
  4. [classlojure :only [wrap-ext-classloader classlojure eval-in get-classpath base-classloader append-classpath!]]
  5. [bake.core :only [debug?]]
  6. [cake.file :only [file global-file path-string]]
  7. [uncle.core :only [fileset-seq]]
  8. [clojure.string :only [split join trim-newline]]
  9. [clojure.java.shell :only [sh]]
  10. [useful.utils :only [adjoin]]
  11. [useful.map :only [update into-map]]
  12. [useful.fn :only [given]]
  13. [clojure.java.io :only [reader]])
  14. (:import [java.io File]))
  15. (defn- path-file [path]
  16. (if-let [[_ dir] (and (string? path) (re-matches #"(.*)/\*" path))]
  17. (fileset-seq {:dir dir :includes "*.jar"})
  18. [(file path)]))
  19. (defn- path-files [path]
  20. (when path
  21. (cond (string? path) (mapcat path-file (split path (re-pattern File/pathSeparator)))
  22. (sequential? path) (mapcat path-file path)
  23. :else (path-file path))))
  24. (defn- to-urls [path]
  25. (map (fn [file]
  26. (str "file:" (.getPath file)
  27. (if (.isDirectory file) "/" "")))
  28. (path-files path)))
  29. (defn classpath [& paths]
  30. (mapcat to-urls (into [(System/getProperty "bake.path")
  31. (mapcat *project* [:source-path :test-path
  32. :resources-path :dev-resources-path
  33. :compile-path :test-compile-path])
  34. (deps :dependencies)
  35. (deps :dev-dependencies)
  36. (get *config* "project.classpath")
  37. (path-string (global-file "lib/dev/*"))]
  38. paths)))
  39. (defn ext-classpath []
  40. (mapcat to-urls (deps :ext-dependencies)))
  41. (defn make-classloader [& paths]
  42. (when (:ext-dependencies *project*)
  43. (wrap-ext-classloader (ext-classpath)))
  44. (if-let [cl (classlojure (apply classpath paths))]
  45. (doto cl
  46. (eval-in '(do (require 'cake)
  47. (require 'bake.io)
  48. (require 'bake.reload)
  49. (require 'clojure.main))))
  50. (prn paths)))
  51. (defn set-classpath!
  52. "Set the JVM classpath property to the current clojure classloader."
  53. [classloader]
  54. (System/setProperty "java.class.path" (join ":" (get-classpath classloader))))
  55. (defn append-dev-dependencies! []
  56. (apply append-classpath! base-classloader
  57. (mapcat to-urls (deps :dev-dependencies))))
  58. (defn reset-classloader! []
  59. (alter-var-root #'*classloader*
  60. (fn [cl]
  61. (when cl (eval-in cl '(shutdown-agents)))
  62. (when-let [classloader (make-classloader)]
  63. (set-classpath! classloader)
  64. classloader))))
  65. (defn reset-test-classloader! []
  66. (alter-var-root #'test-classloader
  67. (fn [_] (make-classloader (deps :test-dependencies)))))
  68. (defn reset-classloaders! []
  69. (reset-classloader!)
  70. (reset-test-classloader!))
  71. (defn reload []
  72. (when *classloader*
  73. (try
  74. (eval-in *classloader* '(bake.reload/reload))
  75. (catch Exception _ (reset-classloader!))))
  76. (when test-classloader
  77. (try
  78. (eval-in test-classloader '(bake.reload/reload))
  79. (catch Exception _ (reset-test-classloader!)))))
  80. (defmacro with-classloader [paths & forms]
  81. `(binding [*classloader* (make-classloader ~@paths)]
  82. ~@forms))
  83. (defmacro with-test-classloader [& forms]
  84. (if (= "true" (get *config* "disable-test-classloader"))
  85. `(do ~@forms)
  86. `(binding [*classloader* test-classloader]
  87. ~@forms)))
  88. (defn- quote-if
  89. "We need to quote the binding keys so they are not evaluated within the bake
  90. syntax-quote and the binding values so they are not evaluated in the
  91. project/project-eval syntax-quote. This function makes that possible."
  92. [pred bindings]
  93. (reduce
  94. (fn [v form]
  95. (if (pred (count v))
  96. (conj v (list 'quote form))
  97. (conj v form)))
  98. [] bindings))
  99. (defn- separate-bindings
  100. "Separate bindings based on whether their value is a Java core type or not, because Java types
  101. should be passed directly to the project classloader, while other values should be serialized."
  102. [bindings]
  103. (reduce (fn [b [sym val]]
  104. (if (and (class val) (.getClassLoader (class val)))
  105. (update b 0 conj sym val)
  106. (update b 1 assoc sym val)))
  107. [[] {}]
  108. (partition 2 bindings)))
  109. (defn- shared-bindings []
  110. `[~'cake/*current-task* '~*current-task*
  111. ~'cake/*project-root* '~*project-root*
  112. ~'cake/*project* '~*project*
  113. ~'cake/*context* '~*context*
  114. ~'cake/*script* '~*script*
  115. ~'cake/*opts* '~*opts*
  116. ~'cake/*pwd* '~*pwd*
  117. ~'cake/*env* '~*env*
  118. ~'cake/*vars* '~*vars*])
  119. ;; TODO: this function is insane. make it sane.
  120. (defn project-eval [ns-forms bindings body]
  121. (let [[let-bindings object-bindings] (separate-bindings bindings)
  122. temp-ns (gensym "bake")
  123. form
  124. `(do (ns ~temp-ns
  125. (:use ~'cake)
  126. ~@ns-forms)
  127. (fn [ins# outs# ~@(keys object-bindings)]
  128. (try
  129. (clojure.main/with-bindings
  130. (bake.io/with-streams ins# outs#
  131. (binding ~(shared-bindings)
  132. (let ~(quote-if odd? let-bindings)
  133. ~@body))))
  134. (finally
  135. (remove-ns '~temp-ns)))))]
  136. (try (apply eval-in *classloader*
  137. `(clojure.main/with-bindings (eval '~form))
  138. *ins* *outs* (vals object-bindings))
  139. (catch Throwable e
  140. (println "error evaluating:")
  141. (prn (if (next body) (cons `do body) (first body)))
  142. (throw e)))))
  143. (defmacro bake
  144. "Execute code in a your project classloader. Bindings allow passing state to the project
  145. classloader. Namespace forms like use and require must be specified before bindings."
  146. {:arglists '([ns-forms* bindings body*])}
  147. [& forms]
  148. (let [[ns-forms [bindings & body]] (split-with (complement vector?) forms)]
  149. `(project-eval '~ns-forms ~(quote-if even? bindings) '~body)))
  150. (defn group [dep]
  151. (if ('#{clojure clojure-contrib} dep)
  152. "org.clojure"
  153. (some #(% dep) [namespace name])))
  154. (defn add-group [dep]
  155. (symbol (group dep) (name dep)))
  156. (defn dep-map [deps]
  157. (let [[deps default-opts] (split-with (complement keyword?) deps)]
  158. (into {}
  159. (for [[dep version & opts] deps]
  160. [(add-group dep) (-> (adjoin (into-map default-opts) (into-map opts))
  161. (assoc :version version)
  162. (update :exclusions (partial map add-group)))]))))
  163. (defmulti get-version identity)
  164. (defmethod get-version :git [_]
  165. (:out (sh "git" "describe" "--tags" "--abbrev=0")))
  166. (defmethod get-version :hg [_]
  167. (-> ".hgtags" reader line-seq last (.split " ") last))
  168. (defmethod get-version :default [r]
  169. (println "No pre-defined get-version method for that key."))
  170. (defn- assoc-path
  171. ([opts key default]
  172. (let [path (or (get opts key) default)]
  173. (assoc opts key (if (string? path)
  174. [path]
  175. (vec path)))))
  176. ([opts key base-key suffix]
  177. (assoc-path opts key (vec (map #(str (file % suffix))
  178. (get opts base-key))))))
  179. (defn create [project-name opts]
  180. (let [base-version (:version opts)
  181. version (trim-newline
  182. (if (string? base-version)
  183. base-version
  184. (get-version base-version)))
  185. artifact (name project-name)
  186. artifact-version (str artifact "-" version)]
  187. (-> opts
  188. (assoc :artifact-id artifact
  189. :group-id (group project-name)
  190. :version version
  191. :name (or (:name opts) artifact)
  192. :aot (or (:aot opts) (:namespaces opts))
  193. :context (symbol (or (get *config* "project.context") (:context opts) "dev"))
  194. :jar-name (or (:jar-name opts) artifact-version)
  195. :war-name (or (:war-name opts) artifact-version)
  196. :uberjar-name (or (:uberjar-name opts) (str artifact-version "-standalone"))
  197. :dev-dependencies (dep-map (concat (:dev-dependencies opts) (:dev-deps opts)))
  198. :ext-dependencies (dep-map (concat (:ext-dependencies opts) (:ext-deps opts)))
  199. :test-dependencies (dep-map (concat (:test-dependencies opts) (:test-deps opts)))
  200. :dependencies (dep-map (concat (:dependencies opts) (:deps opts)
  201. (:native-dependencies opts) (:native-deps opts))))
  202. (assoc-path :source-path "src")
  203. (assoc-path :test-path "test")
  204. (assoc-path :resources-path "resources")
  205. (assoc-path :library-path "lib")
  206. (assoc-path :dev-resources-path "dev")
  207. (assoc-path :compile-path "classes")
  208. (assoc-path :test-compile-path :test-path "classes")
  209. (given (:java-source-path opts) update :source-path conj (:java-source-path opts)))))