Skip to content

Instantly share code, notes, and snippets.

@billrobertson42
Created September 17, 2012 04:19
Show Gist options
  • Save billrobertson42/3735513 to your computer and use it in GitHub Desktop.
Save billrobertson42/3735513 to your computer and use it in GitHub Desktop.
Auto create stub functions for Java when aot compiling Clojure. This is used to build Warscore (http://www.warscore.net)
(ns autostub
(require [clojure.string :as string]))
(set! *compile-path* "build/classes")
(defn export? [[var-symbol var-value]]
(and (fn? (var-get var-value)) ;; functions only
(not (.contains (str var-symbol) "->")))) ;; ignore record auto generated functions
(defn stub-fun-name [name]
(str "-" (clojure.lang.Compiler/munge name)))
(defn stub-funs [target-ns [var-symbol _ ]]
(let [orig-name (str (name target-ns) "/" (name var-symbol))
stub-name (stub-fun-name (name var-symbol))]
(list 'defn stub-name [] orig-name)))
(defn export-decls [[var-symbol _ ]]
(let [munged-name (clojure.lang.Compiler/munge (str var-symbol))]
(str "#^{:static true} [" munged-name "[] clojure.lang.IFn]")))
(defn generate [target-ns]
(require target-ns)
(let [export-functions (filter export? (ns-publics target-ns))
stubs (map (partial stub-funs target-ns) export-functions)
exports (map export-decls export-functions)]
(with-out-str
(println "(ns " (str target-ns ".stubs"))
(println " (:require [" (str target-ns) "])")
(println " (:gen-class")
(println " :methods [")
(doseq [export exports]
(println " " export))
(println "]))")
(println)
(doseq [stub stubs]
(println stub))
(println))))
(defn create-dirs! [base dirs]
(if-let [dir-name (first dirs)]
(let [file (java.io.File. base dir-name)]
(recur (java.io.File. base dir-name) (rest dirs)))
(do
(println "mkdirs" base (.mkdirs base))
base)))
(defn source-file [target-ns]
(java.io.File.
(create-dirs! *compile-path* (string/split (str target-ns) #"\."))
"stubs.clj"))
(defn compile-stubs [target-ns]
(let [source (generate target-ns)
file-name (.getPath (source-file target-ns))
dest-ns (symbol (str target-ns ".stubs"))]
(spit file-name source)
(println "generated" file-name)
(println "compiling" dest-ns)
(compile dest-ns)))
;; example usage - aot compile sample.ns1 and sample.ns2
(doseq [file ['sample.ns1
'sample.ns2]]
(println "Compiling" file)
(compile file))
;; then generate stubs for sample.ns1
(doseq [file ['sample.ns1]]
(compile-stubs file))
---------------- Using from within Java --------------
public static IFn function(String namespace, String function) {
if(Main.devMode()) {
// when working with source
return (IFn)RT.var(namespace, function);
}
else {
// when working with aot class files
try {
String classname = namespace+".stubs";
Class clazz = Class.forName(classname);
String fun = clojure.lang.Compiler.munge(function);
Method m = clazz.getDeclaredMethod(fun);
return (IFn)m.invoke(null);
}
catch(Exception x) {
throw new IllegalArgumentException("Unable to lookup stub: "+
namespace+"/"+function, x);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment