A Leiningen plugin to generate GraalVM native images
Switch branches/tags
Nothing to show
Clone or download
Latest commit fe0ce2f Oct 7, 2018
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
examples Use newer native-image flag syntax Sep 29, 2018
src/leiningen Merge profiles by keyword Jul 15, 2018
.gitignore Initial commit May 20, 2018
CHANGELOG.md Bump version Jul 15, 2018
LICENSE Create LICENSE May 20, 2018
README.md Update README for HTTPS support Oct 7, 2018
project.clj Bump version Jul 15, 2018

README.md

lein-native-image

A Leiningen plugin for generating GraalVM native images from your project.

The lein native-image command compiles your project then uses GraalVM's native-image to build a native image.

Clojars Project

For deps.edn projects, try clj.native-image.

Prerequisites

  • This plugin depends on GraalVM to build native images.
  • Your project.clj must set a :main namespace w/entrypoint and support AOT compilation:
    :main ^:skip-aot my-app.core

Examples

See the examples directory for projects that can be compiled to native images with GraalVM:

  • jdnsmith - CLI command to read JSON from stdin and write EDN to stdout.
  • http-api - Basic HTTP server using Ring, Compojure, http-kit.
  • nlp - CLI command to analyze sentiment of text using StanfordNLP. Includes examples of reflection hints and delaying static initialization.

Usage

  1. Configure your project with a custom image name, path to GraalVM's home directory or native-image path, or native-image CLI options:

    (defproject my-app "0.1.0"
      :plugins [[io.taylorwood/lein-native-image "0.3.0"]]    ;; or in ~/.lein/profiles.clj
    
      :native-image {:name "my-app"                 ;; name of output image, optional
                     :graal-bin "/path/to/graalvm/" ;; path to GraalVM home, optional
                     :opts ["--verbose"]}           ;; pass-thru args to GraalVM native-image, optional
    
      ;; optionally set profile-specific :native-image overrides
      :profiles {:test    ;; e.g. lein with-profile +test native-image
                 {:native-image {:opts ["--report-unsupported-elements-at-runtime"
                                        "--verbose"]}}
    
                 :uberjar ;; used by default
                 {:aot :all
                  :native-image {:opts ["-Dclojure.compiler.direct-linking=true"]}}})

    :native-image config keys:

    • :name is an optional name for the generated native image.
    • The :graal-bin path can be specified as a string or resolved from an environment variable using a keyword e.g. :env/GRAALVM_HOME. If :graal-bin is unspecified, the GRAALVM_HOME environment variable will be used by default.
    • :opts is an optional vector of arguments to native-image; see its documentation for more.

    Note: task-specific :native-image profile will be merged in by default, or the :uberjar profile if that doesn't exist.

    You can also specify these in your Leiningen user profile ~/.lein/profiles.clj:

    {:user {:plugins [[io.taylorwood/lein-native-image "0.3.0"]]
            :native-image {:graal-bin "/path/to/graalvm-1.0.0-rc1/Contents/Home/bin"}}}
  2. Build a native image from your project:

    ➜ lein native-image
    Compiling my-app.core
    Build on Server(pid: 36212, port: 26681)
       classlist:     332.89 ms
           (cap):   1,289.90 ms
       8<----------------------
           write:     932.61 ms
         [total]:  11,789.08 ms
    Created native image /path/to/my-app/target/my-app
    
  3. Execute the native image:

    ➜ ./target/my-app with optional args
    Hello, World!
    

Caveats

The primary benefits to using a GraalVM native image are faster startup, lower memory requirements, and smaller distribution footprint (no JDK/JRE required). This doesn't necessarily mean the same code will run faster than it would on the JVM.

GraalVM and Substrate VM's support for AOT compilation and native images is evolving. There are limitations and unsupported features you will likely encounter. This release was tested with GraalVM 1.0.0-RC1 EE; some of these notes may not apply to future releases. At least one AOT issue has been fixed since 1.0.0-RC1, but you must build Substrate VM locally to get unreleased fixes.

When the :opts ["--report-unsupported-elements-at-runtime"] flag is set, some native-image AOT compilation issues will be deferred as runtime exceptions. You can try specifying this flag if native-image compilation fails. To avoid unexpected errors at runtime, don't use this flag for "production" builds.

You should set :opts ["--enable-url-protocols=http"] to use HTTP libraries. HTTPS is available as of 1.0.0-RC7 (e.g. --enable-url-protocols=http,https) but requires additional configuration.

Specifying :jvm-opts ["-Dclojure.compiler.direct-linking=true"] might allow for better compile-time optimizations.

This plugin doesn't shutdown GraalVM native-image build servers after builds, so that subsequent builds are slightly faster. You can set :opts ["--no-server"] to not spawn a build server at all, or use GraalVM's native-image directly to manage build server(s).

References

GraalVM Native Image AOT Compilation

Native Clojure with GraalVM

Instant Netty Startup using GraalVM (and source)

Contributing

You'll need Leiningen and GraalVM installed to build and test the plugin.

Issues and PRs are welcome!

License

Copyright © 2018 Taylor Wood.

Distributed under the MIT License.