Skip to content

Instantly share code, notes, and snippets.

@ztellman
Created September 7, 2015 23:52
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save ztellman/68281ba8b84544293d5b to your computer and use it in GitHub Desktop.
(ns virgil
(:require
[clojure.java.io :as io]
[clojure.string :as str]
[filevents.core :as fe])
(:import
[javax.tools
DiagnosticCollector
ForwardingJavaFileManager
JavaCompiler
JavaFileObject$Kind
SimpleJavaFileObject
StandardJavaFileManager
ToolProvider]
[clojure.lang
DynamicClassLoader]
[java.io
ByteArrayOutputStream]))
(defn source-object
[class-name source]
(proxy [SimpleJavaFileObject]
[(java.net.URI/create (str "string:///"
(.replace ^String class-name \. \/)
(. JavaFileObject$Kind/SOURCE extension)))
JavaFileObject$Kind/SOURCE]
(getCharContent [_] source)))
(defn class-object
"Returns a JavaFileObject to store a class file's bytecode."
[class-name baos]
(proxy [SimpleJavaFileObject]
[(java.net.URI/create (str "string:///"
(.replace ^String class-name \. \/)
(. JavaFileObject$Kind/CLASS extension)))
JavaFileObject$Kind/CLASS]
(openOutputStream [] baos)))
(defn class-manager
[cl manager baos]
(proxy [ForwardingJavaFileManager] [manager]
(getClassLoader [location]
cl)
(getJavaFileForOutput [location class-name kind sibling]
(class-object class-name baos))))
(defn compile-java
[class-name source]
(let [compiler (ToolProvider/getSystemJavaCompiler)
diag (DiagnosticCollector.)
baos (ByteArrayOutputStream.)
cl (clojure.lang.RT/makeClassLoader)
mgr (class-manager cl (.getStandardFileManager compiler nil nil nil) baos)
task (.getTask compiler nil mgr diag nil nil [(source-object class-name source)])]
(if (.call task)
(.defineClass ^DynamicClassLoader cl class-name (.toByteArray baos) nil)
(throw
(RuntimeException.
(apply str
(interleave (.getDiagnostics diag) (repeat "\n\n"))))))))
(defn watch [& directories]
(doseq [d directories]
(let [prefix (.getCanonicalPath (io/file d))]
(fe/watch
(fn [action ^java.io.File file]
(let [path (.getCanonicalPath file)]
(when (.endsWith path ".java")
(let [path' (.substring path (count prefix) (- (count path) 5))
classname (->> (str/split path' #"/")
(remove empty?)
(interpose ".")
(apply str))]
(try
(println "\ncompiling" classname)
(compile-java classname (slurp file))
(doseq [ns (all-ns)]
(when (->> (ns-imports ns)
vals
(some #(= classname (.getCanonicalName %))))
(require (symbol (str ns)) :reload)))
(catch Throwable e
(.printStackTrace e)
(println (.getMessage e))))))))
d))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment