Created
September 17, 2012 04:19
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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