Also known as CLJ-322
Why do we AOT compile?
- Interop
- To consume a Java API
- Concrete derivation (gen-class)
- Naming/packaging conventions
- To expose an API to Java code
- deftype/record/protocol/interface
- Deployment environments
- Special classloaders, e.g. OSGi, Eclipse
- To consume a Java API
- Application delivery
- Main method (gen-class)
- Mitigated by "-m" option to clojure.main
- Bytecode post-processing
- Android/Dalvik
- Faster startup
- Only an issue in constrained environments like Android
- Hide source code from consumers
- Main method (gen-class)
- Syntax check & reflection warnings
- Doesn't require writing files to disk
- But still a common use case for invoking the compiler
- Some tools support emitting .class files to temp dir
- e.g. Mark Derricutt's clojure-maven-plugin
What do we want to AOT-compile?
- Just a few things
- For the Interop case
- Or to shorten build times
- Not currently supported
- Everything
- For the "Application delivery", "Syntax check", and "reflection warnings" cases
- Currently supported by
clojure.core/compile
Current behavior
- Why is AOT-compilation transitive?
- Clojure does not support separate compilation
- All symbols must resolved at compile time
compile-files
is a boolean
- Clojure does not support separate compilation
- The effects of transitive compilation when AOT-comipiling a namespace "A"
- When namespace A loads namespace B within the same project
- Namespace B is AOT-compiled and emitted
- When namespace A loads library C from external JAR
- Library C's namespaces are AOT-compiled and emitted alongside A's
- When namespace A loads namespace B within the same project
- Work-arounds
- Build tools: filter unwanted .class files from JAR
- Ant, Maven, scripting
- Clojure itself does this, e.g. "slim" JAR
- Tedious
- Not commonly used
- Wastes time during builds to AOT-compile things which will be deleted
- Write Java
- Fairly common
- Build tools: filter unwanted .class files from JAR
Proposed solutions
Finer-grained control of the compilation process
- A. Global flag for transitive/non-transitive compilation
- Chas Emerick's patch
- New system property "clojure.compiler.transitive"
- When true (default), preserves current behavior
- When false, only namespaces give as arguments to `compile` are emitted
- Doesn't handle classes created by deftype/record/etc.
- Chas Emerick's patch
- B. Specify exactly which .class files should be emitted
- Stuart Sierra's patch
- Specified as a Set of Strings, but could be any function, e.g. regex
- Stuart Sierra's patch
- C. Global flag for compiling "interop" forms
- Is there a universal definition of what an "interop" form is?
- gen-class, obviously
- defprotocol?
- deftype/defrecord?
- Does not require maintaining list of .class files to emit
- Could provide a convenient defaults for the common case
- Macros like "deftype" can opt in with metadata
- Is there a universal definition of what an "interop" form is?
- D. Metadata to control AOT-compilation
- Permits fine-grained control
- In-line with source code
- How to support multiple AOT-compilation strategies in a single build
- E.g. dev build only AOT-compiles interop forms, production AOT-compiles everything
Separate writing class files from compilation
- E. Compiler holds references to generated bytecode
- Not currently implemented in any patch
- After compilation, call function to emit .class files
- Will use lots of memory!
- Or will it? The JVM already has this stuff in memory
- Must disable in production!
- Brings us closer to a Smalltalk-like "program image"
Labels:
1 Comment
Hide/Show CommentsNov 29, 2011
Stuart Sierra
Trying to think about how
*compile-interop-forms*
might work, I came up with the following:Option One
The
*compile-files*
Var can be one of:false
(default, do not emit anything)true
(emit everything):interop
(only emit per rules below)Pros:
*compile-files* true
do not breakCons:
Option Two
The
*compile-files*
Var can be one of:false
(default, do not emit anything)true
(emit everything):emit-file
(only emit per rules below)^:emit-file true
metadata^:emit-file true
metadataPros:
*compile-files* true
do not breakCons:
^:emit-file
is redundant?