├── .gitignore ├── .gitattributes ├── dev └── user.clj ├── scripts ├── clean ├── build_js ├── build_native ├── build_native.clj ├── clean.clj └── build_js.clj ├── graal └── reflect.json ├── src └── com │ └── github │ └── lxbr │ ├── ludwig.cljs │ └── ludwig │ ├── internal │ └── jsc.clj │ ├── build.clj │ ├── engine.clj │ ├── native_image.clj │ └── main.clj ├── deps.edn ├── README.md └── epl-v10.html /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .cpcache 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | epl-v10.html linguist-documentation 2 | -------------------------------------------------------------------------------- /dev/user.clj: -------------------------------------------------------------------------------- 1 | (ns user 2 | (:require [compliment.core])) 3 | -------------------------------------------------------------------------------- /scripts/clean: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clojure scripts/clean.clj 4 | -------------------------------------------------------------------------------- /scripts/build_js: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clojure scripts/build_js.clj 4 | -------------------------------------------------------------------------------- /scripts/build_native: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clojure -Anative-image scripts/build_native.clj 4 | -------------------------------------------------------------------------------- /scripts/build_native.clj: -------------------------------------------------------------------------------- 1 | (ns build-native 2 | (:require [com.github.lxbr.ludwig.native-image :as ni])) 3 | 4 | (ni/build (System/getenv "GRAALVM_HOME")) 5 | 6 | (System/exit 0) 7 | 8 | 9 | -------------------------------------------------------------------------------- /graal/reflect.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name" : "[Lcom.kenai.jffi.Type;", 4 | "allDeclaredConstructors" : true 5 | }, 6 | { 7 | "name" : "clojure.lang.LineNumberingPushbackReader", 8 | "methods" : [ 9 | { "name" : "read" }, 10 | { "name" : "unread" }, 11 | { "name" : "readLine" } 12 | ] 13 | } 14 | ] 15 | -------------------------------------------------------------------------------- /scripts/clean.clj: -------------------------------------------------------------------------------- 1 | (ns clean 2 | (:require [clojure.java.io :as io])) 3 | 4 | (defn clean-dir 5 | [path] 6 | (let [file (io/file path)] 7 | (when (.exists file) 8 | (->> (rseq (vec (file-seq file))) 9 | (run! io/delete-file))))) 10 | 11 | (clean-dir "classes") 12 | (clean-dir "out") 13 | 14 | (System/exit 0) 15 | -------------------------------------------------------------------------------- /scripts/build_js.clj: -------------------------------------------------------------------------------- 1 | (ns build-js 2 | (:require [clojure.java.io :as io] 3 | [com.github.lxbr.ludwig.build :as b]) 4 | (:import java.util.zip.GZIPOutputStream)) 5 | 6 | (b/build-js) 7 | 8 | (with-open [os (io/output-stream "out/ludwig.js.gz") 9 | gzos (GZIPOutputStream. os)] 10 | (io/copy (io/file "out/ludwig.js") gzos)) 11 | 12 | (System/exit 0) 13 | -------------------------------------------------------------------------------- /src/com/github/lxbr/ludwig.cljs: -------------------------------------------------------------------------------- 1 | (ns com.github.lxbr.ludwig 2 | (:require [cljs.js])) 3 | 4 | (def compiler-state 5 | (cljs.js/empty-state)) 6 | 7 | (defn ^:export eval-str 8 | [string] 9 | (let [result (atom nil)] 10 | (cljs.js/eval-str 11 | compiler-state 12 | string 13 | "" 14 | {:eval cljs.js/js-eval 15 | ;; :source-map true 16 | :context :expr} 17 | #(let [{:keys [value error]} %] 18 | (reset! result (or error value)))) 19 | (pr-str @result))) 20 | -------------------------------------------------------------------------------- /src/com/github/lxbr/ludwig/internal/jsc.clj: -------------------------------------------------------------------------------- 1 | (ns com.github.lxbr.ludwig.internal.jsc 2 | (:require [com.github.lxbr.jsc.util :as util] 3 | [clojure.java.io :as io] 4 | [clojure.edn :as edn])) 5 | 6 | (defn generate-bindings 7 | [] 8 | (->> (io/resource "deps.edn") 9 | (slurp) 10 | (edn/read-string) 11 | (:ludwig/headers-path) 12 | (util/generate-bindings-for-lib-and-headers nil))) 13 | 14 | (defmacro create-bindings! 15 | [] 16 | (generate-bindings)) 17 | 18 | (create-bindings!) 19 | -------------------------------------------------------------------------------- /src/com/github/lxbr/ludwig/build.clj: -------------------------------------------------------------------------------- 1 | (ns com.github.lxbr.ludwig.build 2 | (:require [cljs.build.api :as cljs])) 3 | 4 | (defn build-js 5 | [] 6 | (cljs/build 7 | "src" 8 | {:output-to "out/ludwig.js" 9 | :output-dir "out" 10 | :optimizations :simple 11 | :verbose true 12 | :static-fns true 13 | :optimize-constants false 14 | :parallel-build true 15 | :dump-core true 16 | :source-map false 17 | :npm-deps false 18 | :process-shim false 19 | :fn-invoke-direct true 20 | :cache-analysis true})) 21 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src" "."] 2 | :deps {org.clojure/clojure {:mvn/version "1.9.0"} 3 | org.clojure/clojurescript {:mvn/version "1.10.339"} 4 | org.clojure/tools.deps.alpha {:mvn/version "0.6.474"} 5 | com.github.lxbr/jsc {:git/url "https://github.com/lxbr/jsc.git" 6 | :sha "4c018f9dc8db1bdae92a4cf3dcb6753db7dae20b"}} 7 | :aliases 8 | {:native-image {:override-deps 9 | {org.clojure/tools.reader 10 | {:git/url "https://github.com/lxbr/tools.reader" 11 | :sha "bcdeec6d32feecc7f2c50f3f8b2f07b466e6c26f"}}} 12 | :local {:override-deps {com.github.lxbr/jsc {:local/root "../jsc"}}}} 13 | 14 | :ludwig/headers-path 15 | "/System/Library/Frameworks/JavaScriptCore.framework/Headers" 16 | 17 | :ludwig/lib-path 18 | "/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore" 19 | 20 | ;; choose the appropriate resource from 21 | ;; jar -tf ~/.m2/repository/com/github/jnr/jffi/1.2.17/jffi-1.2.17-native.jar 22 | :ludwig/libjffi-resource 23 | "jni/Darwin/libjffi-1.2.jnilib"} 24 | -------------------------------------------------------------------------------- /src/com/github/lxbr/ludwig/engine.clj: -------------------------------------------------------------------------------- 1 | (ns com.github.lxbr.ludwig.engine 2 | (:require [com.github.lxbr.ludwig.internal.jsc :as jsc])) 3 | 4 | (defn create-context 5 | [lib] 6 | (jsc/JSGlobalContextCreate lib 0)) 7 | 8 | (defonce null (long-array 0)) 9 | 10 | (defn value-to-string 11 | [lib ctx value] 12 | (let [string (jsc/JSValueToStringCopy lib ctx value null) 13 | bytes (byte-array 14 | (jsc/JSStringGetMaximumUTF8CStringSize lib string)) 15 | len (jsc/JSStringGetUTF8CString lib string bytes (alength bytes))] 16 | (String. bytes 0 (dec len) "UTF-8"))) 17 | 18 | (defn object-get-prop 19 | [lib ctx object ^String prop-name] 20 | (let [prop (jsc/JSStringCreateWithUTF8CString 21 | lib 22 | (.getBytes prop-name "UTF-8")) 23 | value (jsc/JSObjectGetProperty lib ctx object prop null)] 24 | (jsc/JSStringRelease lib prop) 25 | value)) 26 | 27 | (defn get-trace 28 | [lib ctx error] 29 | (let [message (object-get-prop lib ctx error "message") 30 | stack (object-get-prop lib ctx error "stack")] 31 | (str 32 | (value-to-string lib ctx message) 33 | "\n" 34 | (value-to-string lib ctx stack)))) 35 | 36 | (defn eval-script 37 | [lib ctx ^String script] 38 | (let [script-ref (jsc/JSStringCreateWithUTF8CString 39 | lib 40 | (.getBytes script "UTF-8")) 41 | err (long-array 1) 42 | ret (jsc/JSEvaluateScript lib ctx script-ref 0 0 0 err)] 43 | (jsc/JSStringRelease lib script-ref) 44 | (when-not (zero? (first err)) 45 | (println (get-trace lib ctx (first err)))) 46 | ret)) 47 | -------------------------------------------------------------------------------- /src/com/github/lxbr/ludwig/native_image.clj: -------------------------------------------------------------------------------- 1 | (ns com.github.lxbr.ludwig.native-image 2 | (:require [clojure.main] 3 | [clojure.java.io :as io] 4 | [clojure.java.shell :as sh] 5 | [clojure.tools.reader] 6 | [clojure.tools.deps.alpha :as deps] 7 | [clojure.edn] 8 | [com.github.lxbr.jsc.util] 9 | [com.github.lxbr.ludwig.engine] 10 | [com.github.lxbr.ludwig.main]) 11 | (:import java.io.File)) 12 | 13 | (defn build 14 | [graal-home-path] 15 | (println "Graal Home at" graal-home-path) 16 | (.mkdirs (io/file *compile-path*)) 17 | (let [prefixes #{"com.github.lxbr.jsc.util" 18 | "com.github.lxbr.effing" 19 | "com.github.lxbr.ludwig.main" 20 | "com.github.lxbr.ludwig.engine" 21 | "com.github.lxbr.ludwig.internal.jsc" 22 | "clojure.tools.reader"}] 23 | (binding [*compiler-options* {:direct-linking true}] 24 | (print "AOT compiling Clojure sources...") 25 | (flush) 26 | (->> (all-ns) 27 | (map (comp name ns-name)) 28 | (filter #(some (fn [^String prefix] (.startsWith % prefix)) prefixes)) 29 | (run! (comp compile symbol))) 30 | (println "DONE"))) 31 | (println "Starting native build...") 32 | (let [classpath (-> '{:deps {org.clojure/clojure {:mvn/version "1.9.0"} 33 | org.clojure/clojurescript {:mvn/version "1.10.339"} 34 | com.github.jnr/jffi {:mvn/version "1.2.17"} 35 | com.github.jnr/jffi$native {:mvn/version "1.2.17"}}} 36 | (deps/resolve-deps nil) 37 | (deps/make-classpath [*compile-path* "."] nil)) 38 | libjffi-path (-> (io/resource "deps.edn") 39 | (slurp) 40 | (clojure.edn/read-string) 41 | (:ludwig/libjffi-resource)) 42 | {:keys [out err exit]} 43 | (sh/sh (str graal-home-path File/separator "bin/native-image") 44 | "--no-server" 45 | "--verbose" 46 | #_"--report-unsupported-elements-at-runtime" 47 | "-H:Name=ludwig" 48 | "-H:+JNI" 49 | "-H:ReflectionConfigurationFiles=graal/reflect.json" 50 | (format "-H:IncludeResources=(.*/out/ludwig.js.gz)|(%s)" libjffi-path) 51 | "-cp" classpath 52 | "com.github.lxbr.ludwig.Main")] 53 | (if (zero? exit) 54 | (println out) 55 | (println err)))) 56 | -------------------------------------------------------------------------------- /src/com/github/lxbr/ludwig/main.clj: -------------------------------------------------------------------------------- 1 | (ns com.github.lxbr.ludwig.main 2 | (:require [clojure.main] 3 | [com.github.lxbr.ludwig.engine :as jsc] 4 | [clojure.java.io :as io] 5 | [clojure.tools.reader :as reader] 6 | [clojure.tools.reader.reader-types :as reader-types] 7 | [cljs.tagged-literals] 8 | [clojure.edn] 9 | [clojure.string]) 10 | (:import clojure.lang.LineNumberingPushbackReader 11 | java.util.zip.GZIPInputStream 12 | java.io.File) 13 | (:gen-class 14 | :name com.github.lxbr.ludwig.Main)) 15 | 16 | (set! *warn-on-reflection* true) 17 | 18 | (def libjffi-path 19 | (-> (io/resource "deps.edn") 20 | (slurp) 21 | (clojure.edn/read-string) 22 | (:ludwig/libjffi-resource))) 23 | 24 | (defn load-libjffi! 25 | [] 26 | (let [suffix (subs libjffi-path (clojure.string/last-index-of libjffi-path ".")) 27 | tmp (File/createTempFile "libjffi-1.2" suffix)] 28 | (.deleteOnExit tmp) 29 | (with-open [is (io/input-stream (io/resource libjffi-path))] 30 | (io/copy is tmp)) 31 | (System/load (.getAbsolutePath tmp)))) 32 | 33 | (def lib-path 34 | (-> (io/resource "deps.edn") 35 | (slurp) 36 | (clojure.edn/read-string) 37 | (:ludwig/lib-path))) 38 | 39 | (defn repl-init 40 | [] 41 | (println "Enter :cljs/quit to exit the REPL")) 42 | 43 | (defn repl-eval 44 | [lib ctx string] 45 | (let [escaped-string (pr-str string)] 46 | (->> (str "com.github.lxbr.ludwig.eval_str(" escaped-string ")") 47 | (jsc/eval-script lib ctx) 48 | (jsc/value-to-string lib ctx)))) 49 | 50 | (defn repl-read 51 | [request-prompt request-exit] 52 | (if-some [result ({:line-start request-prompt :stream-end request-exit} 53 | (clojure.main/skip-whitespace *in*))] 54 | result 55 | (let [[form string] 56 | (binding [reader/*read-eval* false 57 | reader/*data-readers* cljs.tagged-literals/*cljs-data-readers*] 58 | (reader/read+string 59 | (reader-types/source-logging-push-back-reader *in*) 60 | {:features #{:cljs} 61 | :read-cond :allow}))] 62 | (if (= :cljs/quit form) 63 | request-exit 64 | string)))) 65 | 66 | (defn repl-caught 67 | [^Throwable t] 68 | (prn t)) 69 | 70 | (defn repl-print 71 | [value] 72 | (println value)) 73 | 74 | (defn repl-prompt 75 | [] 76 | (print "cljs.user=> ")) 77 | 78 | ;; copied from clojure.main 79 | ;; native image generation struggles with dynamic class loading 80 | ;; we need to remove all references to code that can trigger 81 | ;; dynamic class loading: eval (obviously), read (via *read-eval* in LispReader/tools.reader) 82 | (defn repl 83 | [options] 84 | (let [{:keys [init need-prompt prompt flush read eval print caught] 85 | :or {need-prompt (if (instance? LineNumberingPushbackReader *in*) 86 | #(.atLineStart ^LineNumberingPushbackReader *in*) 87 | #(identity true)) 88 | flush flush}} 89 | options 90 | request-prompt (Object.) 91 | request-exit (Object.) 92 | read-eval-print 93 | (fn [] 94 | (try 95 | (let [input (clojure.main/with-read-known (read request-prompt request-exit))] 96 | (or (#{request-prompt request-exit} input) 97 | (let [value (eval input)] 98 | (print value) 99 | (set! *3 *2) 100 | (set! *2 *1) 101 | (set! *1 value)))) 102 | (catch Throwable e 103 | (caught e) 104 | (set! *e e))))] 105 | (clojure.main/with-bindings 106 | (try 107 | (init) 108 | (catch Throwable e 109 | (caught e) 110 | (set! *e e))) 111 | (prompt) 112 | (flush) 113 | (loop [] 114 | (when-not 115 | (try (identical? (read-eval-print) request-exit) 116 | (catch Throwable e 117 | (caught e) 118 | (set! *e e) 119 | nil)) 120 | (when (need-prompt) 121 | (prompt) 122 | (flush)) 123 | (recur)))))) 124 | 125 | (defn -main 126 | [& args] 127 | (load-libjffi!) 128 | (let [lib (com.kenai.jffi.Library/openLibrary 129 | lib-path 130 | (bit-or com.kenai.jffi.Library/LOCAL 131 | com.kenai.jffi.Library/LAZY)) 132 | init-script (with-open [is (-> (io/resource "out/ludwig.js.gz") 133 | (io/input-stream)) 134 | gzis (GZIPInputStream. is)] 135 | (slurp gzis)) 136 | ctx (jsc/create-context lib)] 137 | (jsc/eval-script lib ctx init-script) 138 | (repl 139 | {:init repl-init 140 | :prompt repl-prompt 141 | :eval (fn [value] (repl-eval lib ctx value)) 142 | :read repl-read 143 | :caught repl-caught 144 | :print repl-print}))) 145 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ludwig 2 | 3 | Planck, Max Karl Ernst **Ludwig** Planck! 4 | 5 | ## What is this? 6 | 7 | `Ludwig` is an experiment. It takes the JavaScriptCore engine, covers it in 8 | Clojure bindings, melts it via a radioactive REPL and shoots it 9 | through the steaming hot pipes of GraalVM's native-image tool. 10 | 11 | The result is a red-hot liquid, it flows whereever it wants and if you 12 | try to touch it, you might get burned. 13 | 14 | ## Again, what is this thing? 15 | 16 | This project primarily tries to show how to build non trivial applications 17 | in Clojure and compile them down to native code through GraalVM. This is 18 | not and likely will never be a replacement for 19 | [planck](https://github.com/planck-repl/planck) or 20 | [lumo](https://github.com/anmonteiro/lumo). 21 | 22 | The path to libjffi, JavaScriptCore and its headers is set in `deps.edn`. 23 | `Ludwig` works on macos out of the box 24 | but may work on other systems when the aforementioned paths are set appropriately. 25 | If you want to build JavaScriptCore from source, see 26 | [this](https://github.com/WebKit/webkit) and 27 | [this](http://constellation.github.io/blog/2016/05/02/how-to-build-javascriptcore-on-your-machine/). 28 | 29 | ## Now, how do I run it? 30 | 31 | ### On the JVM 32 | 33 | 1. At the project root run `scripts/build_js`. 34 | 2. Then start a REPL and chant the magic spell... 35 | 36 | ```clojure 37 | (require 'com.github.lxbr.ludwig.main) 38 | 39 | (in-ns 'com.github.lxbr.ludwig.main) 40 | 41 | (-main) 42 | ``` 43 | 44 | ...and a self-hosted ClojureScript REPL will appear. 45 | 46 | ### As native binary 47 | 48 | This project has been developed with *GraalVM Community Edition 1.0 RC6*. 49 | 50 | 1. [Download](https://www.graalvm.org/downloads/) GraalVM. 51 | 2. Set the environment variable `GRAALVM_HOME` (see *Preparation* in [example](https://www.graalvm.org/docs/examples/java-simple-stream-benchmark/)). 52 | 3. At the project root run `scripts/build_js`. 53 | 4. Then run `scripts/build_native`. 54 | 5. After the scripts finished, call `./ludwig`. 55 | 56 | * Does compilation fail? 57 | * Try uncommenting the `--report-unsupported-elements-at-runtime` flag in the `com.github.lxbr.ludwig.native-image` namespace. 58 | * Compilation will take longer and the binary will be bigger. 59 | 60 | * Is macos asking you to install Java 6? 61 | * Have a look at this: https://support.apple.com/kb/dl1572 62 | 63 | ## Clojure and GraalVM 64 | 65 | Compiling Clojure sources to native code works surprisingly well, 66 | except when it doesn't. Here are a few things to keep in mind. 67 | 68 | * make sure you compile all necessary Clojure files to class files 69 | * remember to create the directory pointed to by `clojure.core/*compile-path*` 70 | * then use `clojure.core/compile` on namespace symbols 71 | * using the direct-linking option is a good idea 72 | * don't forget to put the compile path into the classpath argument to 73 | the native-image tool 74 | 75 | * code that calls `ClassLoader.defineClass` is problematic 76 | * build aborts with error 77 | * avoid error by passing `--report-unsupported-elements-at-runtime` flag 78 | * but build time and artifact size go up by 2x-3x 79 | * current solution: comment out all code paths that can reach `defineClass` 80 | * not always easy/possible 81 | * cannot comment out LispReader's read-eval functionality 82 | because then Clojure itself doesn't build anymore 83 | * needed to fork tools.reader for `Ludwig` to work 84 | 85 | * debugging Java is hard 86 | * not a GraalVM specific issue 87 | * debugging private and final fields/methods in 3rd party libs is a pain 88 | * trial and error is incredibly slow 89 | * native-image (for `Ludwig`) can take a minute or more to build the artifact 90 | * the feedback loop is almost non-existent 91 | * try functionality in Clojure at the REPL before compiling to native 92 | * this gives you a tighter feedback loop and some confidence that native 93 | compilation will succeed 94 | 95 | * reflection is the enemy 96 | * type hint everything you can 97 | * what you cannot type hint, you put into [reflect.json](https://github.com/oracle/graal/blob/master/substratevm/REFLECTION.md) 98 | 99 | * static/at most ones initialization 100 | * initialization code is run at compile time 101 | * static initializers 102 | * static variable definitions 103 | * this is a feature(!) to move computation from runtime to compile time 104 | * the problem: code can be initialized at compile time and never at runtime 105 | * this can cause trouble, be careful with static initialization (singleton pattern, etc.) 106 | * native libraries must be loaded in the entry point method not in static initializers 107 | * unless it produces constants that don't depend on time, avoid creating vars via `def` 108 | * `defn` is fine, unless it closes over values that depend on time 109 | * instead initialize state in the entry point method 110 | * this guarantees your code will run when invoking the binary 111 | 112 | * documentation for GraalVM and native-image is in flux 113 | * some features are mainly documented in github issues, 114 | no proper documentation yet, the project is fast moving 115 | 116 | * integration of classpath resources into binary artifact 117 | * `-H:IncludeResources=` 118 | * when you deal with resources from jars, works as expected given relative paths 119 | * separate regex patterns by pipes `(jni/Darwin/libjffi-1.2.jnilib)|...` 120 | * when you want to include a file from the filesystem 121 | * the regex is applied to the absolute path 122 | * a regex of `file.bin` does not work for the file `/Users/foobar/project/file.bin` 123 | * use the regex `.*/file.bin` instead 124 | * resources can then be loaded as usual, e.g. via `clojure.java.io/resource` 125 | 126 | * proxy classes (code gen in general) 127 | * JNR (and JNA) did not work because of JVM runtime code gen 128 | * for that reason `Ludwig` uses `jffi` directly 129 | 130 | * callbacks from native code to JVM still need JNI 131 | * callbacks (called *Closure*s) in `jffi` work in the REPL 132 | * but give you segfaults when compiled to a native binary 133 | * the reason is unclear, maybe some static initialization issue 134 | 135 | * tools.deps helps with building classpath argument to native-image tool 136 | 137 | ## Future Experiments 138 | 139 | * compile ClojureScript Compiler and Google Closure Compiler to native code 140 | * no need for self hosting => better perf? 141 | * see https://github.com/oracle/graal/issues/428 142 | * and https://gist.github.com/ChadKillingsworth/137e8064a9933379726b4d23254a3214 143 | 144 | * use Rhino/Nashorn/GraalJS as evaluation environment 145 | * solves the "callback via JNI" problem?! 146 | 147 | * mimic NodeJS 148 | * binary that runs JS with support for IO 149 | * access to JVM library eco-system 150 | * how does threading work? 151 | * how useful are concurrently running JS engine instances? 152 | 153 | * GraalVM uses libffi itself, should we use that instead of jffi? 154 | * would make GraalVM a dependency 155 | * better integration for callbacks? 156 | 157 | * when the program fails we return a non-zero exit code 158 | * **and** print the app-state as edn 159 | * we can then analyze that data in a Clojure REPL 160 | * starting the program with a debug flag, we can pass that data back into the program 161 | and reproduce the app-state and the bug 162 | 163 | ## Should I use native-image in all my Clojure projects? 164 | 165 | **No**, GraalVM is still in the release candidate stage, failing code is costly 166 | because compilation and debugging are slow, many issues still have to be found. 167 | **But** the results are immensely promising. Being able to write high level code 168 | and then compile it down to a relatively small binary that needs little runtime memory 169 | is a dream come true. With Clojure we can now reach JVM, JS and Native! 170 | 171 | GraalVM promises a lot of value, we should keep an eye on it. 172 | 173 | ## License 174 | 175 | This project is licensed under the terms of the Eclipse Public License 1.0 (EPL). 176 | -------------------------------------------------------------------------------- /epl-v10.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Eclipse Public License - Version 1.0 8 | 25 | 26 | 27 | 28 | 29 | 30 |

Eclipse Public License - v 1.0

31 | 32 |

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 33 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR 34 | DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS 35 | AGREEMENT.

36 | 37 |

1. DEFINITIONS

38 | 39 |

"Contribution" means:

40 | 41 |

a) in the case of the initial Contributor, the initial 42 | code and documentation distributed under this Agreement, and

43 |

b) in the case of each subsequent Contributor:

44 |

i) changes to the Program, and

45 |

ii) additions to the Program;

46 |

where such changes and/or additions to the Program 47 | originate from and are distributed by that particular Contributor. A 48 | Contribution 'originates' from a Contributor if it was added to the 49 | Program by such Contributor itself or anyone acting on such 50 | Contributor's behalf. Contributions do not include additions to the 51 | Program which: (i) are separate modules of software distributed in 52 | conjunction with the Program under their own license agreement, and (ii) 53 | are not derivative works of the Program.

54 | 55 |

"Contributor" means any person or entity that distributes 56 | the Program.

57 | 58 |

"Licensed Patents" mean patent claims licensable by a 59 | Contributor which are necessarily infringed by the use or sale of its 60 | Contribution alone or when combined with the Program.

61 | 62 |

"Program" means the Contributions distributed in accordance 63 | with this Agreement.

64 | 65 |

"Recipient" means anyone who receives the Program under 66 | this Agreement, including all Contributors.

67 | 68 |

2. GRANT OF RIGHTS

69 | 70 |

a) Subject to the terms of this Agreement, each 71 | Contributor hereby grants Recipient a non-exclusive, worldwide, 72 | royalty-free copyright license to reproduce, prepare derivative works 73 | of, publicly display, publicly perform, distribute and sublicense the 74 | Contribution of such Contributor, if any, and such derivative works, in 75 | source code and object code form.

76 | 77 |

b) Subject to the terms of this Agreement, each 78 | Contributor hereby grants Recipient a non-exclusive, worldwide, 79 | royalty-free patent license under Licensed Patents to make, use, sell, 80 | offer to sell, import and otherwise transfer the Contribution of such 81 | Contributor, if any, in source code and object code form. This patent 82 | license shall apply to the combination of the Contribution and the 83 | Program if, at the time the Contribution is added by the Contributor, 84 | such addition of the Contribution causes such combination to be covered 85 | by the Licensed Patents. The patent license shall not apply to any other 86 | combinations which include the Contribution. No hardware per se is 87 | licensed hereunder.

88 | 89 |

c) Recipient understands that although each Contributor 90 | grants the licenses to its Contributions set forth herein, no assurances 91 | are provided by any Contributor that the Program does not infringe the 92 | patent or other intellectual property rights of any other entity. Each 93 | Contributor disclaims any liability to Recipient for claims brought by 94 | any other entity based on infringement of intellectual property rights 95 | or otherwise. As a condition to exercising the rights and licenses 96 | granted hereunder, each Recipient hereby assumes sole responsibility to 97 | secure any other intellectual property rights needed, if any. For 98 | example, if a third party patent license is required to allow Recipient 99 | to distribute the Program, it is Recipient's responsibility to acquire 100 | that license before distributing the Program.

101 | 102 |

d) Each Contributor represents that to its knowledge it 103 | has sufficient copyright rights in its Contribution, if any, to grant 104 | the copyright license set forth in this Agreement.

105 | 106 |

3. REQUIREMENTS

107 | 108 |

A Contributor may choose to distribute the Program in object code 109 | form under its own license agreement, provided that:

110 | 111 |

a) it complies with the terms and conditions of this 112 | Agreement; and

113 | 114 |

b) its license agreement:

115 | 116 |

i) effectively disclaims on behalf of all Contributors 117 | all warranties and conditions, express and implied, including warranties 118 | or conditions of title and non-infringement, and implied warranties or 119 | conditions of merchantability and fitness for a particular purpose;

120 | 121 |

ii) effectively excludes on behalf of all Contributors 122 | all liability for damages, including direct, indirect, special, 123 | incidental and consequential damages, such as lost profits;

124 | 125 |

iii) states that any provisions which differ from this 126 | Agreement are offered by that Contributor alone and not by any other 127 | party; and

128 | 129 |

iv) states that source code for the Program is available 130 | from such Contributor, and informs licensees how to obtain it in a 131 | reasonable manner on or through a medium customarily used for software 132 | exchange.

133 | 134 |

When the Program is made available in source code form:

135 | 136 |

a) it must be made available under this Agreement; and

137 | 138 |

b) a copy of this Agreement must be included with each 139 | copy of the Program.

140 | 141 |

Contributors may not remove or alter any copyright notices contained 142 | within the Program.

143 | 144 |

Each Contributor must identify itself as the originator of its 145 | Contribution, if any, in a manner that reasonably allows subsequent 146 | Recipients to identify the originator of the Contribution.

147 | 148 |

4. COMMERCIAL DISTRIBUTION

149 | 150 |

Commercial distributors of software may accept certain 151 | responsibilities with respect to end users, business partners and the 152 | like. While this license is intended to facilitate the commercial use of 153 | the Program, the Contributor who includes the Program in a commercial 154 | product offering should do so in a manner which does not create 155 | potential liability for other Contributors. Therefore, if a Contributor 156 | includes the Program in a commercial product offering, such Contributor 157 | ("Commercial Contributor") hereby agrees to defend and 158 | indemnify every other Contributor ("Indemnified Contributor") 159 | against any losses, damages and costs (collectively "Losses") 160 | arising from claims, lawsuits and other legal actions brought by a third 161 | party against the Indemnified Contributor to the extent caused by the 162 | acts or omissions of such Commercial Contributor in connection with its 163 | distribution of the Program in a commercial product offering. The 164 | obligations in this section do not apply to any claims or Losses 165 | relating to any actual or alleged intellectual property infringement. In 166 | order to qualify, an Indemnified Contributor must: a) promptly notify 167 | the Commercial Contributor in writing of such claim, and b) allow the 168 | Commercial Contributor to control, and cooperate with the Commercial 169 | Contributor in, the defense and any related settlement negotiations. The 170 | Indemnified Contributor may participate in any such claim at its own 171 | expense.

172 | 173 |

For example, a Contributor might include the Program in a commercial 174 | product offering, Product X. That Contributor is then a Commercial 175 | Contributor. If that Commercial Contributor then makes performance 176 | claims, or offers warranties related to Product X, those performance 177 | claims and warranties are such Commercial Contributor's responsibility 178 | alone. Under this section, the Commercial Contributor would have to 179 | defend claims against the other Contributors related to those 180 | performance claims and warranties, and if a court requires any other 181 | Contributor to pay any damages as a result, the Commercial Contributor 182 | must pay those damages.

183 | 184 |

5. NO WARRANTY

185 | 186 |

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS 187 | PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 188 | OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, 189 | ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY 190 | OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely 191 | responsible for determining the appropriateness of using and 192 | distributing the Program and assumes all risks associated with its 193 | exercise of rights under this Agreement , including but not limited to 194 | the risks and costs of program errors, compliance with applicable laws, 195 | damage to or loss of data, programs or equipment, and unavailability or 196 | interruption of operations.

197 | 198 |

6. DISCLAIMER OF LIABILITY

199 | 200 |

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT 201 | NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, 202 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING 203 | WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF 204 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 205 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR 206 | DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 207 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

208 | 209 |

7. GENERAL

210 | 211 |

If any provision of this Agreement is invalid or unenforceable under 212 | applicable law, it shall not affect the validity or enforceability of 213 | the remainder of the terms of this Agreement, and without further action 214 | by the parties hereto, such provision shall be reformed to the minimum 215 | extent necessary to make such provision valid and enforceable.

216 | 217 |

If Recipient institutes patent litigation against any entity 218 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 219 | Program itself (excluding combinations of the Program with other 220 | software or hardware) infringes such Recipient's patent(s), then such 221 | Recipient's rights granted under Section 2(b) shall terminate as of the 222 | date such litigation is filed.

223 | 224 |

All Recipient's rights under this Agreement shall terminate if it 225 | fails to comply with any of the material terms or conditions of this 226 | Agreement and does not cure such failure in a reasonable period of time 227 | after becoming aware of such noncompliance. If all Recipient's rights 228 | under this Agreement terminate, Recipient agrees to cease use and 229 | distribution of the Program as soon as reasonably practicable. However, 230 | Recipient's obligations under this Agreement and any licenses granted by 231 | Recipient relating to the Program shall continue and survive.

232 | 233 |

Everyone is permitted to copy and distribute copies of this 234 | Agreement, but in order to avoid inconsistency the Agreement is 235 | copyrighted and may only be modified in the following manner. The 236 | Agreement Steward reserves the right to publish new versions (including 237 | revisions) of this Agreement from time to time. No one other than the 238 | Agreement Steward has the right to modify this Agreement. The Eclipse 239 | Foundation is the initial Agreement Steward. The Eclipse Foundation may 240 | assign the responsibility to serve as the Agreement Steward to a 241 | suitable separate entity. Each new version of the Agreement will be 242 | given a distinguishing version number. The Program (including 243 | Contributions) may always be distributed subject to the version of the 244 | Agreement under which it was received. In addition, after a new version 245 | of the Agreement is published, Contributor may elect to distribute the 246 | Program (including its Contributions) under the new version. Except as 247 | expressly stated in Sections 2(a) and 2(b) above, Recipient receives no 248 | rights or licenses to the intellectual property of any Contributor under 249 | this Agreement, whether expressly, by implication, estoppel or 250 | otherwise. All rights in the Program not expressly granted under this 251 | Agreement are reserved.

252 | 253 |

This Agreement is governed by the laws of the State of New York and 254 | the intellectual property laws of the United States of America. No party 255 | to this Agreement will bring a legal action under this Agreement more 256 | than one year after the cause of action arose. Each party waives its 257 | rights to a jury trial in any resulting litigation.

258 | 259 | 260 | 261 | --------------------------------------------------------------------------------