Versions Compared


  • This line was added.
  • This line was removed.
  • Formatting was changed.


Example builds we may want to produce (examples, not finalized):

  • "dev" build
    • More detailed error messages that would affect performance if included in default build
    • Skip locals clearing or other features to aid in debugging
    • Other features that may aid in development but affect performance
  • "dyn" build
    • Similar to what you get now with Clojure 1.6
  • "prod" build
    • Direct linking (static vars are inlined to be static fn calls) to gain greater perf
    • Trim metadata (omit docstrings for speed and space savings)
  • "experimental" build A place to provide experimental compiler features that we want users to evaluate for the default buildDirect linking
    • Off - allows redefs, good for interactive dev tooling or modifying Clojure internals
    • On - better performance and optimization for long-running more static programs
  • Lazy vars
    • On - faster bootstrap for scripts or dev reloading
    • Off - faster var invocation for long-running programs
  • Metadata (:doc, :file, :line, :added) - removing metadata makes .class files smaller and faster to load, but removes metadata for doc
    • Full - elide nothing
    • Minimal - elide #{:doc, :file, :line, :added}
  • Dev assistance
    • Validation and error messages that can guide development at the expense of runtime performance

Build profiles

Below are some proposed build variants.

ProfileDescriptionCompiled?Direct linking?Lazy vars?MetadataDev assistance?
devFor developmentYesOffOnFullOn
dynamicFor dynamic production useYesOffOffMinimalOff
staticFor static production useYesOnOffMinimalOff
slimSource onlyNon/an/an/an/a
1.8 clojure jarFor comparisonYesOnOffFullOff

OPEN: Are these the right profiles and the right settings?

OPEN: Which one is the default no-classifier?


Clojure users:

  • Build profile artifacts should be available in public Maven repositories for use with existing build systems (Leiningen, Maven, Boot), alternatives include:
    • Different artifacts:  [org.clojure/clojure-foo "1.68.0"]
    • Different qualifiers: [org.clojure/clojure "1.68.0-foo"]
    • Different classifiers: [org.clojure/clojure "1.68.0" :classifier "foo"] - seems like the best choice
  • Some features may require further compiler flags or metadata to activate even if using the specialized build
    • For example, you may need to supply ^:static special var metadata on a function to get access to this a feature
    • Ideally, use of the special feature should be simply ignored if not using the specialized build (or a warning should direct you)


  • Produce multiple output jars each time we build a version (snapshot or release)
  • The jars will contain different versions of the classes
  • Specialized build flags or env variables will need to be set during the build for each variant
  • Testing should occur for each variant
Integration / release infrastructure:
  • Hudson CI server must be able to build and test custom builds as part of the dependency matrix
  • Hudson CI server must be able to perform releases for all of the variants
  • CI must be able to test contribs against custom builds


  • The compiler will need a standard way to determine which variant is active to make decisions during compilation.

Usage Scenarios

Clojure dyn (AOT)

  • Same as now, can redef calls to core Clojure fns both externally and internally
  • Metadata available

Clojure prod (AOT)

  • Clojure code is compiled with direct linking
  • Internal calls from one Clojure core function to another cannot be redefined (due to AOT)
  • External code can redef calls to core Clojure fns
  • Some metadata is trimmed (docstrings) to save space and loading time
Application dyn
  • Same as now (but use of Clojure core depends on which version you use as above)
Application prod (source) 
  • As source is loaded and compiled, internal references are statically linked
  • External calls to application functions can be redefined 
  • Internal calls to application functions can be 
Clojure prod (AOT) + Application prod AOT

Clojure prod (AOT) + Application dyn source

Clojure prod (AOT) + Library prod source + Application prod source


How do these scenarios affect what the end Application can do?  How do intermediate libraries choose what they depend on and publicize what they're doing to users? 

A particular ns could choose different choices for how to link other namespaces, its own namespace, or even some functions in its own namespace. Leaving a small number of "main loop" fns dynamic could allow for runtime system patching.


External builds

Builds that use Clojure may also need to make these kinds of compiler choices to produce their own build profiles. 

It would be useful to produce a guide on how to enable combinations of compiler options to produce builds of different flavors for libraries with the common build tools.

Implementation options

Hardest part of this is getting the build to sign and release all of the artifacts together.

Full build would consist of:

  • javac Java source
  • jar slim jar (without class files)
  • filter resources - update
  • repeat for each variant
    • aot compile Clojure source (using Compiler we just built)
    • jar compiled clojure jar (with class files)
    • javac test Java
    • aot compile test Clojure
    • run clojure.test runner 
    • run generative test runner
  • jar source files
  • javadoc generate
  • jar javadoc files
  • zip package of full distribution
  • install to local repo
  • deploy
    • sign all artifacts
    • push all artifacts to Maven central
    • git tag release
    • update version to next SNAPSHOT on prod deploy

OPEN: which implementation would be best? 

Maven custom assembly

  • The Maven assembly plugin allows for arbitrary customization of the artifacts created by the build. However, creating multiple artifacts with different Maven coordinates will likely require multiple builds to produce each artifact for deployment. This has issues with signing and release.
  • How-to in Maven
    • Create one Maven profile per output artifact, in each profile:
      • Set a property that can be used as the <classifier> in the maven-jar-plugin
      • Set <activation><active-by-default> to false
      • Set system property compiler flags that need to be active during Clojure compilation for the foo variant
      • Turn off generation of the -sources and -javadoc classifier artifacts in these variant builds
    • Run the release once per profile - this can be automated in Maven with a matrix build
    • Much more difficult to produce N variants as part of a single build (in a way that works with CI well)


  • Boot provides a great deal of customization capability and might also be a good option for this level of customization
  • Unknown: signed deployment to Maven central repos? Yes:
    • Need to figure out how to load encrypted creds on CI server - might require some hackery
  • In theory, this seems feasible with existing plugins, unknown what complications would arise

Custom script (ant, make, etc)

  • Would prefer something with greater familiarity to Clojure devs as patch developers need to run the build too
  • How-to in Ant
    • Define property that can be set to activate a particular variant
    • Set that property when running the build

Coordinate Research

Maven coordinate options

  • Different artifactId
    • Releasing different artifacts would probably require running the build multiple times - there are probably some complexities in the CI and release process as a result.
    • NOT recommended
  • Different qualifier
    • Qualifiers are really designed to give you more information about the purpose of a released version (alpha, beta, GA, build number), so this would be departing from standard Maven expectations.
    • NOT recommended
  • Different classifier
    • Classifiers are designed for this use case - they allow you to create variants of the same group/artifact/version specialized for a particular environment (JDK variants is one example given)
    • Lein and Maven can both specify a classifier - in lein with [org.clojure/clojure "1.6.0" :classifier "foo"]
    • It seems good that the main build and variant share Maven co-ords - there is then a standard way to get from main artifact to (for example) the dev variant for tooling.
    • How-to in Maven
    • How-to in Ant
      • Define property that can be set to activate a particular variant
      • Set that property when running the build

Maven custom assembly

  • The Maven assembly plugin allows for arbitrary customization of the artifacts created by the build. However, creating multiple artifacts with different Maven coordinates will likely require multiple builds to produce each artifact for deployment. 

Custom script (ant, make, etc)