├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── examples ├── README.md ├── http-api │ ├── .gitignore │ ├── deps.edn │ ├── project.clj │ ├── resources │ │ └── not-found.html │ └── src │ │ └── http_api │ │ ├── core.clj │ │ └── handler.clj ├── jdnsmith │ ├── .gitignore │ ├── deps.edn │ ├── project.clj │ └── src │ │ └── jdnsmith │ │ └── core.clj └── nlp │ ├── .gitignore │ ├── project.clj │ ├── reflection.json │ ├── src │ └── nlp │ │ └── core.clj │ └── test.txt ├── project.clj └── src └── leiningen └── native_image.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | *.iml 13 | .cpcache 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). 3 | 4 | ## [Unreleased] 5 | 6 | ## [0.3.1] - 2019-07-25 7 | ### Added 8 | - Warning for missing `native-image` on post-RC releases of GraalVM. 9 | ### Fixed 10 | - Use `.cmd` extension on Windows hosts. 11 | 12 | ## [0.3.0] - 2018-07-15 13 | ### Added 14 | - Support more conventional `GRAALVM_HOME` path format i.e. `$GRAALVM_HOME/bin/native-image` 15 | - Use `GRAALVM_HOME` environment variable if `:graal-bin` is unspecified 16 | ### Fixed 17 | - Regression from 0.2.0: the consuming project's `:uberjar` profile was not merged by default. 18 | This default behavior can be overriden by specifying a `:native-image` profile. 19 | 20 | ## [0.2.0] - 2018-05-27 21 | ### Added 22 | - `:opts` vector to `:native-image` to specify `native-image` CLI arguments 23 | ### Changed 24 | - Compile all sources and call `native-image` with classpath, instead of building uberjar 25 | - `native-image` flag `ReportUnsupportedElementsAtRuntime` no longer enabled by default 26 | ### Fixed 27 | - Replace `-` with `_` in `:main` class names when calling `native-image` 28 | 29 | ## [0.1.0] - 2018-05-20 30 | ### Added 31 | - Support for building native images from uberjars via GraalVM 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Taylor Wood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lein-native-image 2 | 3 | A Leiningen plugin for generating [GraalVM](https://www.graalvm.org) native images from your project. 4 | 5 | The `lein native-image` command compiles your project then uses GraalVM's 6 | [`native-image`](https://www.graalvm.org/docs/reference-manual/aot-compilation/) to build a native image. 7 | 8 | [![Clojars Project](https://img.shields.io/clojars/v/io.taylorwood/lein-native-image.svg)](https://clojars.org/io.taylorwood/lein-native-image) 9 | 10 | For deps.edn projects, try [clj.native-image](https://github.com/taylorwood/clj.native-image). 11 | 12 | ## Prerequisites 13 | 14 | * This plugin depends on [GraalVM](https://www.graalvm.org/downloads/) to build native images. 15 | 16 | **NOTE:** As of GraalVM 19.0.0, `native-image` is no longer included by default: 17 | > Native Image was extracted from the base GraalVM distribution. Currently it is available as an early adopter plugin. To install it, run: `gu install native-image`. After this additional step, the `native-image` executable will be in the `bin` directory, as for the previous releases. 18 | 19 | ``` 20 | ➜ $GRAALVM_HOME/bin/gu install native-image 21 | Downloading: Component catalog from www.graalvm.org 22 | Processing component archive: Native Image 23 | Downloading: Component native-image: Native Image from github.com 24 | Installing new component: Native Image licence files (org.graalvm.native-image, version 19.0.0) 25 | ``` 26 | 27 | * Your project.clj must set a `:main` namespace w/entrypoint and support AOT compilation: 28 | ```clojure 29 | :main ^:skip-aot my-app.core 30 | ``` 31 | 32 | ## Examples 33 | 34 | See the [examples](examples) directory for projects that can be compiled to native images with GraalVM: 35 | 36 | * [jdnsmith](examples/jdnsmith) - CLI command to read JSON from stdin and write EDN to stdout. 37 | * [http-api](examples/http-api) - Basic HTTP server using Ring, Compojure, http-kit. 38 | * [nlp](examples/nlp) - CLI command to analyze sentiment of text using StanfordNLP. Includes examples of reflection hints and delaying class initialization. 39 | * [clojurl](https://github.com/taylorwood/clojurl) - cURL-like tool using clojure.spec, HTTPS, hiccup. 40 | 41 | ## Usage 42 | 43 | 1. Configure your project with a custom image name, path to GraalVM's home directory or `native-image` path, 44 | or `native-image` CLI options: 45 | ```clojure 46 | (defproject my-app "0.1.0" 47 | :plugins [[io.taylorwood/lein-native-image "0.3.1"]] ;; or in ~/.lein/profiles.clj 48 | 49 | :native-image {:name "my-app" ;; name of output image, optional 50 | :graal-bin "/path/to/graalvm/" ;; path to GraalVM home, optional 51 | :opts ["--verbose"]} ;; pass-thru args to GraalVM native-image, optional 52 | 53 | ;; optionally set profile-specific :native-image overrides 54 | :profiles {:test ;; e.g. lein with-profile +test native-image 55 | {:native-image {:opts ["--report-unsupported-elements-at-runtime" 56 | "--initialize-at-build-time" 57 | "--verbose"]}} 58 | 59 | :native-image ;; used by default 60 | {:jvm-opts ["-Dclojure.compiler.direct-linking=true"]}}) 61 | ``` 62 | 63 | `:native-image` config keys: 64 | - `:name` is an optional name for the generated native image. 65 | - The `:graal-bin` path can be specified as a string or resolved from an environment variable 66 | using a keyword e.g. `:env/GRAALVM_HOME`. 67 | If `:graal-bin` is unspecified, the `GRAALVM_HOME` environment variable will be used by default. 68 | - `:opts` is an optional vector of arguments to `native-image`; see its 69 | [documentation](https://www.graalvm.org/docs/reference-manual/aot-compilation/#image-generation-options) for more. 70 | 71 | Note: task-specific `:native-image` profile will be merged in by default, or the `:uberjar` profile 72 | if that doesn't exist. 73 | 74 | You can also specify these in your Leiningen user profile `~/.lein/profiles.clj`: 75 | ```clojure 76 | {:user {:plugins [[io.taylorwood/lein-native-image "0.3.1"]] 77 | :native-image {:graal-bin "/path/to/graalvm-ce-19.0.0/Contents/Home/bin"}}} 78 | ``` 79 | 80 | 1. Build a native image from your project: 81 | ``` 82 | ➜ lein native-image 83 | Compiling my-app.core 84 | Build on Server(pid: 36212, port: 26681) 85 | classlist: 332.89 ms 86 | (cap): 1,289.90 ms 87 | 8<---------------------- 88 | write: 932.61 ms 89 | [total]: 11,789.08 ms 90 | Created native image /path/to/my-app/target/my-app 91 | ``` 92 | 93 | 1. Execute the native image: 94 | ``` 95 | ➜ ./target/my-app with optional args 96 | Hello, World! 97 | ``` 98 | 99 | ## Caveats 100 | 101 | The primary benefits to using a GraalVM native image are faster startup, lower memory requirements, 102 | and smaller distribution footprint (no JDK/JRE required). This doesn't necessarily mean the same code 103 | will _run_ faster than it would on the JVM. 104 | GraalVM Community Edition and Enterprise Edition also have different performance characteristics. 105 | 106 | GraalVM's native image capabilities have evolved across many release candidates. Several AOT issues have been fixed since 1.0.0-RC1. 107 | GraalVM and Substrate VM's support for AOT compilation and native images has [limitations](https://github.com/oracle/graal/blob/master/substratevm/LIMITATIONS.md), 108 | and there are [unsupported features](https://github.com/oracle/graal/blob/master/substratevm/REFLECTION.md). 109 | This release and its example projects were tested with GraalVM 19.0.0 Community Edition. 110 | 111 | GraalVM 19.0.0 (first non-RC release) changes the default class-initialization behavior of `native-image`. 112 | Now you must specify `--initialize-at-build-time` explicitly in your `native-image` options. 113 | 114 | There is a [known issue](https://dev.clojure.org/jira/browse/CLJ-1472) where usages of `clojure.core/locking` macro will fail compilation. 115 | Clojure 1.10 depends on a version of clojure.spec that uses `locking`. 116 | See [this commit](https://github.com/taylorwood/clojurl/commit/12b96b5e9a722b372f153436b1f6827709d0f2ab) for an example workaround. 117 | 118 | When the `--report-unsupported-elements-at-runtime` flag is set, 119 | some `native-image` AOT compilation issues will be deferred as runtime exceptions. 120 | You can try specifying this flag if `native-image` compilation fails. 121 | To avoid unexpected errors at runtime, don't use this flag for "production" builds. 122 | 123 | Set `--enable-url-protocols=http` to use HTTP libraries. 124 | HTTPS is available as of 1.0.0-RC7 (e.g. `--enable-url-protocols=http,https`) 125 | but [requires additional configuration](https://github.com/oracle/graal/blob/master/substratevm/URL-PROTOCOLS.md#https-support). 126 | 127 | Specifying `:jvm-opts ["-Dclojure.compiler.direct-linking=true"]` might allow for better 128 | compile-time optimizations. 129 | 130 | This plugin doesn't shutdown GraalVM `native-image` build servers after builds, so that subsequent 131 | builds are slightly faster. You can set `:opts ["--no-server"]` to not spawn a build server at 132 | all, or use GraalVM's `native-image` command directly to manage build server(s). 133 | 134 | ### References 135 | 136 | [GraalVM Native Image AOT Compilation](https://www.graalvm.org/docs/reference-manual/aot-compilation/) 137 | 138 | [Native Clojure with GraalVM](https://www.innoq.com/en/blog/native-clojure-and-graalvm/) 139 | 140 | [Instant Netty Startup using GraalVM](https://medium.com/graalvm/instant-netty-startup-using-graalvm-native-image-generation-ed6f14ff7692) (and [source](https://github.com/cstancu/netty-native-demo)) 141 | 142 | ## Contributing 143 | 144 | You'll need Leiningen and GraalVM installed to build and test the plugin. 145 | 146 | Issues and PRs are welcome! 147 | 148 | ## License 149 | 150 | Copyright © 2019 Taylor Wood. 151 | 152 | Distributed under the MIT License. 153 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Example Projects 2 | 3 | Some sample Leiningen projects that have been tested with `lein-native-image` to produce working GraalVM native images. 4 | 5 | Run `lein native-image` from an example project directory and execute the generated image. 6 | 7 | ## jdnsmith 8 | 9 | A CLI tool that reads JSON from stdin then writes it to stdout in EDN format. 10 | 11 | ``` 12 | ➜ echo "{\"foo\": [{\"bar\": 1.33}]}" | ./target/jdn 13 | {:foo [{:bar 1.33}]} 14 | ``` 15 | 16 | ## http-api 17 | 18 | A simple web server using http-kit + Ring + Compojure. Build the native image, then start it: 19 | ``` 20 | ➜ ./target/server 21 | Hello, Web! 22 | ``` 23 | 24 | You can now make requests to endpoints that in turn make HTTP requests to other sites and... 25 | - return the response in Hiccup/EDN format: 26 | ``` 27 | ➜ curl localhost:3000/hick/clojuredocs.org 28 | ("" [:html {} [:head {} [:meta {:content "width=device-width, maximum-scale=1.0", :name "viewport"}] [:meta {:content "yes", :name "apple-mobile-web-app-capable"}] [:meta {:content "default", :name "apple-mobile-web-app-status-bar-style"}] [:meta {:content "ClojureDocs" ... 29 | ``` 30 | - compute the frequency of each character in the response: 31 | ``` 32 | ➜ curl localhost:3000/freq/clojuredocs.org 33 | {"frequencies":{" ":1320,"!":11,"A":15,"a":2440,"\"":1392,"B":12,"b":295,"#":66,"C":50,"c":1160,"D":44,"d":574,"❤":1,"%":12,"E":26,"e":1330,"&":144,"F":28,"f":460,"❦":1,"'":44,"G":47,"g":422,"(":44,"H":7,"h":512,")":44,"I":6,"i":1000,"\n":55,"*":3,"J":6,"j":134,"+":2,"K":5,"k":156,",":46,"L":17,"l":1045,"-":345,"M":7,"m":582,".":342,"N":12,"n":981,"/":1156,"O":8,"o":1022,"0":168,"P":48,"p":753,"1":158,"Q":3,"q":15,"2":277,"R":11,"r":1292,"3":228,"S":7,"s":1674,"4":198,"T":13,"t":1431,"5":112,"U":4,"u":542,"6":125,"V":3,"v":526,"7":135,"W":2,"w":263,"8":141,"X":2,"x":42,"9":118,"Y":3,"y":233,"∙":6,":":224,"Z":2,"z":20,";":183,"[":13,"{":11,"<":1012,"|":7,"=":924,"]":13,"}":11,">":1015,"^":1,"?":94,"_":15},"timestamp":"2018-05-27T21:15:52.438Z"}% 34 | ``` 35 | 36 | The project's `core.clj` also demonstrates a workaround for dealing with `native-image` limitations related to runtime reflection. 37 | 38 | ## nlp 39 | 40 | A CLI tool to do sentiment analysis on text inputs. Pipe some text into it and get a sentiment index. 41 | ``` 42 | ➜ echo "Oh wow, I can't believe how great this is." | ./nlp 43 | 4.0 44 | ➜ echo "This is the worst thing I've ever seen." | ./nlp 45 | 0.0 46 | ``` 47 | -------------------------------------------------------------------------------- /examples/http-api/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | pom.xml 3 | pom.xml.asc 4 | *.log 5 | *.lein-* 6 | -------------------------------------------------------------------------------- /examples/http-api/deps.edn: -------------------------------------------------------------------------------- 1 | {:deps {org.clojure/clojure {:mvn/version "1.9.0"} 2 | http-kit {:mvn/version "2.3.0"} 3 | ring/ring-core {:mvn/version "1.6.3"} 4 | ring/ring-json {:mvn/version "0.4.0"} 5 | cheshire {:mvn/version "5.7.1"} 6 | compojure {:mvn/version "1.6.1"} 7 | clj-http-lite {:mvn/version "0.3.0"} 8 | hickory {:mvn/version "0.7.1"}} 9 | :paths ["src" "resources"] 10 | :aliases {:native-image 11 | {:main-opts ["-m clj.native-image http-api.core" 12 | "--enable-url-protocols=http,https" 13 | "--report-unsupported-elements-at-runtime" 14 | "--initialize-at-build-time"] 15 | :jvm-opts ["-Dclojure.compiler.direct-linking=true"] 16 | :extra-deps 17 | {clj.native-image 18 | {:git/url "https://github.com/taylorwood/clj.native-image.git" 19 | :sha "7708e7fd4572459c81f6a6b8e44c96f41cdd92d4"}}}}} 20 | -------------------------------------------------------------------------------- /examples/http-api/project.clj: -------------------------------------------------------------------------------- 1 | (defproject http-api "0.1.0-SNAPSHOT" 2 | :dependencies [[org.clojure/clojure "1.9.0"] 3 | [http-kit "2.3.0"] 4 | [ring/ring-core "1.6.3"] 5 | [ring/ring-json "0.4.0"] 6 | [compojure "1.6.1"] 7 | [clj-http-lite "0.3.0"] 8 | [hickory "0.7.1"]] 9 | :plugins [[io.taylorwood/lein-native-image "0.3.1"]] 10 | :target-path "target/%s" 11 | :native-image {:graal-bin :env/GRAALVM_HOME 12 | :opts ["-H:EnableURLProtocols=http" 13 | "--report-unsupported-elements-at-runtime" ;; ignore native-image build errors 14 | "--initialize-at-build-time" 15 | "--verbose"] 16 | :name "server"} 17 | :main http-api.core 18 | :profiles {:dev {:dependencies [[org.clojure/test.check "0.9.0"]]} 19 | :native-image {:jvm-opts ["-Dclojure.compiler.direct-linking=true"]} 20 | :uberjar {:aot :all}}) 21 | -------------------------------------------------------------------------------- /examples/http-api/resources/not-found.html: -------------------------------------------------------------------------------- 1 |

404 Not Found

2 | -------------------------------------------------------------------------------- /examples/http-api/src/http_api/core.clj: -------------------------------------------------------------------------------- 1 | (ns http-api.core 2 | (:require 3 | [http-api.handler :as handler] 4 | [org.httpkit.server :as srv]) 5 | (:import (sun.util.logging PlatformLogger PlatformLogger$Level)) 6 | (:gen-class)) 7 | 8 | (def ^:private logger 9 | "This throwaway static/compile-time invocation is a workaround for runtime 10 | reflection of sun.util.logging.{LoggingSupport|PlatformLogger}." 11 | (.isLoggable (PlatformLogger/getLogger "dummy") PlatformLogger$Level/ALL)) 12 | 13 | (defn -main [& args] 14 | (println "Hello, Web!") 15 | (srv/run-server #'handler/app {:port 3000})) 16 | -------------------------------------------------------------------------------- /examples/http-api/src/http_api/handler.clj: -------------------------------------------------------------------------------- 1 | (ns http-api.handler 2 | (:require 3 | [clj-http.lite.client :as http] 4 | [clojure.java.io :as io] 5 | [compojure.core :refer :all] 6 | [compojure.route :as route] 7 | [hickory.core :as hick] 8 | [ring.middleware.json :as json] 9 | [ring.util.response :as resp]) 10 | (:import (java.time Instant))) 11 | 12 | (defroutes my-routes 13 | (GET "/hick/:site" [site] 14 | (-> (http/get (str "http://" site)) 15 | (:body) 16 | (hick/parse) 17 | (hick/as-hiccup) 18 | (pr-str) 19 | (resp/response) 20 | (resp/content-type "application/edn"))) 21 | 22 | (GET "/freq/:site" [site] 23 | (resp/response 24 | {:frequencies (-> (http/get (str "http://" site)) 25 | (:body) 26 | (frequencies)) 27 | :timestamp (str (Instant/now))}))) 28 | 29 | (def app 30 | (routes 31 | (wrap-routes #'my-routes json/wrap-json-response) 32 | (route/not-found {:status 404 :body (slurp (io/resource "not-found.html"))}))) 33 | -------------------------------------------------------------------------------- /examples/jdnsmith/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | pom.xml 3 | pom.xml.asc 4 | *.log 5 | *.lein-* 6 | -------------------------------------------------------------------------------- /examples/jdnsmith/deps.edn: -------------------------------------------------------------------------------- 1 | {:deps {org.clojure/clojure {:mvn/version "1.9.0"} 2 | org.clojure/data.json {:mvn/version "0.2.6"}} 3 | :aliases {:native-image 4 | {:main-opts ["-m clj.native-image jdnsmith.core" 5 | "--report-unsupported-elements-at-runtime" 6 | "--initialize-at-build-time" 7 | "-H:Name=json2edn"] 8 | :jvm-opts ["-Dclojure.compiler.direct-linking=true"] 9 | :extra-deps 10 | {clj.native-image 11 | {:git/url "https://github.com/taylorwood/clj.native-image.git" 12 | :sha "7708e7fd4572459c81f6a6b8e44c96f41cdd92d4"}}}}} 13 | -------------------------------------------------------------------------------- /examples/jdnsmith/project.clj: -------------------------------------------------------------------------------- 1 | (defproject jdnsmith "0.1.0-SNAPSHOT" 2 | :plugins [[io.taylorwood/lein-native-image "0.3.1"]] 3 | :dependencies [[org.clojure/clojure "1.9.0"] 4 | [org.clojure/data.json "0.2.6"]] 5 | :main jdnsmith.core 6 | :target-path "target/%s" 7 | :native-image {:graal-bin :env/GRAALVM_HOME 8 | :opts ["--verbose" 9 | "--report-unsupported-elements-at-runtime" 10 | "--initialize-at-build-time"] 11 | :name "jdn"} 12 | :profiles {:dev {:global-vars {*warn-on-reflection* true 13 | *assert* true}} 14 | :native-image {:jvm-opts ["-Dclojure.compiler.direct-linking=true"]} 15 | :uberjar {:aot :all}}) 16 | -------------------------------------------------------------------------------- /examples/jdnsmith/src/jdnsmith/core.clj: -------------------------------------------------------------------------------- 1 | (ns jdnsmith.core 2 | "Reads JSON from stdin and writes its EDN representation to stdout." 3 | (:require [clojure.data.json :as json] 4 | [clojure.edn :as edn]) 5 | (:gen-class)) 6 | 7 | (defn -main [& args] 8 | (prn (json/read *in* :key-fn keyword))) 9 | -------------------------------------------------------------------------------- /examples/nlp/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | pom.xml 3 | pom.xml.asc 4 | *.log 5 | *.lein-* 6 | -------------------------------------------------------------------------------- /examples/nlp/project.clj: -------------------------------------------------------------------------------- 1 | (defproject nlp "0.1.0-SNAPSHOT" 2 | :plugins [[io.taylorwood/lein-native-image "0.3.1"]] 3 | :dependencies [[org.clojure/clojure "1.9.0"] 4 | [edu.stanford.nlp/stanford-corenlp "3.9.1"] 5 | [edu.stanford.nlp/stanford-corenlp "3.9.1" :classifier "models"] 6 | [org.slf4j/slf4j-nop "1.7.12"]] 7 | :main ^:skip-aot nlp.core 8 | :native-image {:name "nlp" 9 | :opts ["-H:ReflectionConfigurationFiles=reflection.json" 10 | "--report-unsupported-elements-at-runtime" 11 | "--initialize-at-run-time=edu.stanford.nlp.trees.international.pennchinese.ChineseEnglishWordMap$SingletonHolder,edu.stanford.nlp.process.WordShapeClassifier$DistributionalClusters" 12 | "--initialize-at-build-time"]} 13 | :target-path "target/%s" 14 | :profiles {:uberjar {:aot :all} 15 | :native-image {:jvm-opts ["-Dclojure.compiler.direct-linking=true"]}}) 16 | -------------------------------------------------------------------------------- /examples/nlp/reflection.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "java.util.HashSet", 4 | "methods": [ 5 | { 6 | "name": "", 7 | "parameterTypes": [] 8 | } 9 | ], 10 | "allDeclaredConstructors": true, 11 | "allPublicConstructors": true, 12 | "allDeclaredMethods": true, 13 | "allPublicMethods": true 14 | }, 15 | { 16 | "name": "java.util.HashMap", 17 | "methods": [ 18 | { 19 | "name": "", 20 | "parameterTypes": [] 21 | } 22 | ], 23 | "allDeclaredConstructors": true, 24 | "allPublicConstructors": true, 25 | "allDeclaredMethods": true, 26 | "allPublicMethods": true 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /examples/nlp/src/nlp/core.clj: -------------------------------------------------------------------------------- 1 | (ns nlp.core 2 | (:require [clojure.string :as cs]) 3 | (:import (edu.stanford.nlp.ling CoreAnnotations$SentencesAnnotation) 4 | (edu.stanford.nlp.neural.rnn RNNCoreAnnotations) 5 | (edu.stanford.nlp.pipeline StanfordCoreNLP) 6 | (edu.stanford.nlp.sentiment SentimentCoreAnnotations$SentimentAnnotatedTree 7 | SentimentCoreAnnotations$SentimentClass) 8 | (edu.stanford.nlp.trees Tree) 9 | (edu.stanford.nlp.util TypesafeMap) 10 | (java.util Properties)) 11 | (:gen-class)) 12 | 13 | (set! *warn-on-reflection* true) 14 | 15 | (defn build-pipeline [annotators] 16 | (let [props (doto (Properties.) 17 | (.put "annotators" (cs/join "," (map name annotators))))] 18 | (StanfordCoreNLP. props true))) 19 | 20 | (def default-annotators [:tokenize :ssplit :pos :parse :sentiment]) 21 | 22 | (defn get-type [^TypesafeMap anns klass] (.get anns klass)) 23 | 24 | (defn sentence->sentiment-class [s] 25 | (let [^Tree tree (get-type s SentimentCoreAnnotations$SentimentAnnotatedTree)] 26 | (RNNCoreAnnotations/getPredictedClass tree))) 27 | 28 | (defn sentence->sentiment-label [s] 29 | (get-type s SentimentCoreAnnotations$SentimentClass)) 30 | 31 | (defn text->sentences [text ^StanfordCoreNLP nlp] 32 | (let [annotation (.process nlp text)] 33 | (get-type annotation CoreAnnotations$SentencesAnnotation))) 34 | 35 | (def default-pipeline (build-pipeline default-annotators)) 36 | 37 | (comment 38 | (->> (text->sentences "This is so really great? I'm not sure." default-pipeline) 39 | (map sentence->sentiment-class)) 40 | (->> (text->sentences "This is so really great? I'm not sure." default-pipeline) 41 | (map sentence->sentiment-label))) 42 | 43 | (defn sentiment-index [text] 44 | (let [sentences (text->sentences text default-pipeline) 45 | sentiment-classes (map sentence->sentiment-class sentences)] 46 | (when (seq sentiment-classes) 47 | (float (/ (apply + sentiment-classes) 48 | (count sentiment-classes)))))) 49 | 50 | (defn -main [& args] 51 | (let [text (if (seq args) 52 | (apply str args) 53 | (slurp *in*))] 54 | (println (sentiment-index text)))) 55 | -------------------------------------------------------------------------------- /examples/nlp/test.txt: -------------------------------------------------------------------------------- 1 | I can't believe how awesome this is. It's really great. Just super. 2 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject io.taylorwood/lein-native-image "0.3.1" 2 | :description "A Leiningen plugin for generating GraalVM native images from your project." 3 | :url "https://github.com/taylorwood/lein-native-image" 4 | :license {:name "The MIT License" 5 | :url "http://opensource.org/licenses/MIT"} 6 | :eval-in-leiningen true) 7 | -------------------------------------------------------------------------------- /src/leiningen/native_image.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.native-image 2 | "Builds a native image from project uberjar using GraalVM." 3 | (:require [clojure.java.io :as io] 4 | [clojure.string :as cs] 5 | [leiningen.compile :as compile] 6 | [leiningen.core.classpath :as classpath] 7 | [leiningen.core.eval :as eval] 8 | [leiningen.core.main :refer [debug info warn exit]] 9 | [leiningen.core.project :as project]) 10 | (:import (java.io File))) 11 | 12 | (defn- absolute-path [f & fs] 13 | (.getAbsolutePath ^File (apply io/file f fs))) 14 | 15 | (def native-image-cmd 16 | (if (-> (System/getProperty "os.name") (cs/starts-with? "Windows")) 17 | "native-image.cmd" ;; Windows native-image has .cmd extension 18 | "native-image")) 19 | 20 | (defn- native-image-path [bin] 21 | (cond 22 | (nil? bin) 23 | native-image-cmd ;; assumed to be on PATH 24 | 25 | (keyword? bin) 26 | (if (= "env" (namespace bin)) 27 | (native-image-path (System/getenv (name bin))) 28 | (native-image-path (name bin))) 29 | 30 | (string? bin) 31 | (if (cs/ends-with? bin native-image-cmd) 32 | bin 33 | (let [paths [(io/file bin "bin" native-image-cmd) 34 | (io/file bin native-image-cmd)]] 35 | (debug "Looking for native-image command at these paths:" (cs/join File/pathSeparator (map str paths))) 36 | (or (some->> paths 37 | (filter #(.exists %)) 38 | (first) 39 | (absolute-path)) 40 | (do (warn "Couldn't find native-image command. You may need to install it with `gu install native-image`.") 41 | (exit -1 "Couldn't find native-image command"))))) 42 | 43 | :else bin)) 44 | 45 | (defn- build-native-image 46 | "Executes native-image (bin-path) with opts, specifying a classpath, 47 | main/entrypoint class, and destination path. Returns native-image exit code." 48 | [bin-path opts cp main dest] 49 | (debug "Building native image" dest "with classpath" cp) 50 | (let [all-args (cond-> [] 51 | (seq opts) (into opts) 52 | dest (conj (format "-H:Name=%s" dest)) 53 | cp (into ["-cp" cp]) 54 | main (conj main))] 55 | (apply eval/sh bin-path all-args))) 56 | 57 | (defn native-image 58 | "Create a native image of your project using GraalVM's native-image." 59 | [project & _args] 60 | (let [profile (first (filter #(get-in project [:profiles %]) [:native-image :uberjar])) 61 | project (if profile 62 | (project/merge-profiles project [profile]) 63 | project) 64 | _ (compile/compile project :all) 65 | config (:native-image project) 66 | entrypoint (-> (name (:main project)) 67 | (cs/replace #"\-" "_")) 68 | dest-path (absolute-path 69 | (:target-path project) 70 | (or (:name config) 71 | (format "%s-%s" (:name project) (:version project)))) 72 | exit-code (build-native-image 73 | (native-image-path (or (:graal-bin config) 74 | (System/getenv "GRAALVM_HOME"))) 75 | (:opts config) 76 | (->> (classpath/get-classpath project) 77 | (filter #(.exists (io/file %))) 78 | (cs/join File/pathSeparatorChar)) 79 | entrypoint 80 | dest-path)] 81 | (if (zero? exit-code) 82 | (info "Created native image" dest-path) 83 | (do (warn "Failed to create native image") 84 | (exit exit-code "native-image failed with exit code" exit-code))))) 85 | --------------------------------------------------------------------------------