diff --git a/.circleci/images/primary/build.sh b/.circleci/images/primary/build.sh index f2fdff8..12c4dc2 100755 --- a/.circleci/images/primary/build.sh +++ b/.circleci/images/primary/build.sh @@ -11,16 +11,17 @@ spec_alpha_version="0.2.177-SNAPSHOT" set -xeo pipefail +rep_home=$PWD + # Build a patched Clojure (see CLJ-1472) cd /tmp git clone https://github.com/clojure/clojure.git cd clojure git checkout "$clojure_commit_hash" -# dead link -#curl https://dev.clojure.org/jira/secure/attachment/18767/clj-1472-3.patch |patch -p1 -curl 'https://api.media.atlassian.com/file/2af127ec-44e4-4bcd-9e0d-40fa2c737c00/binary?client=44579ec4-9d32-4a41-b3c1-ceed2de443de&collection=&dl=true&token=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiI0NDU3OWVjNC05ZDMyLTRhNDEtYjNjMS1jZWVkMmRlNDQzZGUiLCJhY2Nlc3MiOnsidXJuOmZpbGVzdG9yZTpmaWxlOjcyMjc5YjNiLTgwNDctNGE0YS04MjhhLWZkNWM2MjUwMGI0MCI6WyJyZWFkIl0sInVybjpmaWxlc3RvcmU6ZmlsZToyYWYxMjdlYy00NGU0LTRiY2QtOWUwZC00MGZhMmM3MzdjMDAiOlsicmVhZCJdLCJ1cm46ZmlsZXN0b3JlOmZpbGU6OTAwNzcyNzItYmEwNC00NTFiLTk3MTgtNTA1OTNlOGRmOGU1IjpbInJlYWQiXSwidXJuOmZpbGVzdG9yZTpmaWxlOjcxYjY4ZmRkLTg5NTMtNGQ2Ny04ZGQ1LWEzY2Y0Yjg3ZTY3YiI6WyJyZWFkIl0sInVybjpmaWxlc3RvcmU6ZmlsZTpiOGFiOWNiMy1iMDdlLTQ2MzctOWM4ZC1lODZmYWUwNjFmY2EiOlsicmVhZCJdLCJ1cm46ZmlsZXN0b3JlOmZpbGU6M2NiOWYxM2ItYTg1MC00YTI5LWI0ZjgtNTc4MWI4NDQ0ZTdkIjpbInJlYWQiXX0sImV4cCI6MTU3MjYxNTAzNiwibmJmIjoxNTcyNjE0MDc2fQ.fG1uECS3YhkSIYqGgcEZvIahpxGDcNCNN0JAdPaJINw' | patch -p1 - +# [CLJ-1472] The locking macro fails bytecode verification on Graal native-image and ART runtime +# https://clojure.atlassian.net/browse/CLJ-1472 +patch -p1 < $rep_home/CLJ-1472-3.patch mvn -Dmaven.test.skip install diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c30c6f0..0b508c8 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -4,6 +4,9 @@ Change Log https://github.com/eraserhd/rep/compare/v0.1.2...HEAD[Unreleased] ---------------------------------------------------------------- +=== Added + +* Allow streaming code from STDIN https://github.com/eraserhd/rep/compare/v0.1.1...v0.1.2[v0.1.2] ---------------------------------------------------------------- diff --git a/CLJ-1472-3.patch b/CLJ-1472-3.patch new file mode 100644 index 0000000..7a1e88c --- /dev/null +++ b/CLJ-1472-3.patch @@ -0,0 +1,58 @@ +From c9db05268075ff63e9a12cb41c92399faa45ac8f Mon Sep 17 00:00:00 2001 +From: Adam Clements +Date: Wed, 12 Dec 2018 12:24:46 -0600 +Subject: [PATCH] CLJ-1472 Use Java synchronized block for locking macro + +--- + src/clj/clojure/core.clj | 6 +----- + src/jvm/clojure/lang/Util.java | 9 +++++++++ + 2 files changed, 10 insertions(+), 5 deletions(-) + +diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj +index 8e98e072..01c7a002 100644 +--- a/src/clj/clojure/core.clj ++++ b/src/clj/clojure/core.clj +@@ -1648,15 +1648,11 @@ (defmacro locking + "Executes exprs in an implicit do, while holding the monitor of x. + Will release the monitor of x in all circumstances." + {:added "1.0"} + [x & body] + `(let [lockee# ~x] +- (try +- (monitor-enter lockee#) +- ~@body +- (finally +- (monitor-exit lockee#))))) ++ (clojure.lang.Util/lock lockee# (^{:once true} fn* [] ~@body)))) + + (defmacro .. + "form => fieldName-symbol or (instanceMethodName-symbol args*) + + Expands into a member access (.) of the first member on the first +diff --git a/src/jvm/clojure/lang/Util.java b/src/jvm/clojure/lang/Util.java +index e3be3e8c..89d3a1e9 100644 +--- a/src/jvm/clojure/lang/Util.java ++++ b/src/jvm/clojure/lang/Util.java +@@ -242,10 +242,19 @@ static public RuntimeException sneakyThrow(Throwable t) { + @SuppressWarnings("unchecked") + static private void sneakyThrow0(Throwable t) throws T { + throw (T) t; + } + ++/** ++ * Invoke f under lockee monitor ++ */ ++static public Object lock(final Object lockee, final IFn f) { ++ synchronized(lockee) { ++ return f.invoke(); ++ } ++} ++ + static public Object loadWithClass(String scriptbase, Class loadFrom) throws IOException, ClassNotFoundException{ + Var.pushThreadBindings(RT.map(new Object[] { Compiler.LOADER, loadFrom.getClassLoader() })); + try { + return RT.var("clojure.core", "load").invoke(scriptbase); + } +-- +2.17.2 (Apple Git-113) + diff --git a/README.adoc b/README.adoc index e238ab1..4d5e572 100644 --- a/README.adoc +++ b/README.adoc @@ -98,6 +98,12 @@ This issue is being tracked https://dev.clojure.org/jira/browse/CLJ-1472[here] and https://github.com/oracle/graal/issues/861[here]. +Contributors +------------ + +- Jason M. Felice +- Arne Brasseur + License ------- diff --git a/rep.1.adoc b/rep.1.adoc index 196f5e1..f9ff0f1 100644 --- a/rep.1.adoc +++ b/rep.1.adoc @@ -17,6 +17,8 @@ Unlike other nREPL clients, `rep` does not try to maintain a persistent connection, meaning that thread-local variables and bindings like `*e` and `*1` will not persist across invocations of `rep`. +If `EXPR` is omitted then code is read from STDIN. + == OPTIONS *--*:: End of options. Useful to send code which starts with a dash. diff --git a/src/rep/core.clj b/src/rep/core.clj index bd5b158..0764e81 100644 --- a/src/rep/core.clj +++ b/src/rep/core.clj @@ -1,10 +1,9 @@ (ns rep.core - (:require - [clojure.tools.cli :as cli] - [nrepl.core :as nrepl] - [rep.format :as format]) - (:import - (java.io File)) + (:require [clojure.tools.cli :as cli] + [clojure.edn :as edn] + [nrepl.core :as nrepl] + [rep.format :as format]) + (:import (java.io File)) (:gen-class)) (defn- take-until @@ -35,7 +34,7 @@ ([result] (rf result)) ([result input] (when (contains? input key) - (let [^java.io.Writer out (case fd + (let [^java.io.Writer out (case (long fd) 1 *out* 2 *err*) text ^String (format/format format input)] @@ -175,31 +174,51 @@ (let [^String dir (System/getProperty "user.dir")] (loop [option-value (:port (:options opts))] (if-some [[_ ^String filename :as x] (re-matches #"^@(.*)" option-value)] - (if (.isAbsolute (File. filename)) - (recur (slurp filename)) - (recur (slurp (str (File. dir filename))))) + (let [file (File. filename) + file (if (.isAbsolute file) + file + (File. dir filename))] + (if (.exists file) + (recur (slurp file)) + (do + (binding [*out* *err*] + (println "Can't read nREPL port, no such file:" (str file))) + (System/exit 2)))) (if-some [[_ host port] (re-matches #"(.*):(.*)" option-value)] [:host host :port (Long/parseLong port)] [:port (Long/parseLong option-value)]))))) +(defn edn-seq [^java.io.BufferedReader rdr] + (let [edn-val (edn/read {:eof ::EOF} rdr)] + (when (not (identical? ::EOF edn-val)) + (cons edn-val (lazy-seq (edn-seq rdr)))))) + +(defn eval-form [options session code] + (let [msg-seq (session (merge (:line options) + (:send options) + {:op (:op options) + :ns (:namespace options) + :code code}))] + (transduce (comp (until-status "done") + (printing (:print options)) + report-exceptions) + null-reducer + {:exit-code 0} + msg-seq))) + (defmethod command :eval [{:keys [options arguments] :as opts}] (let [conn (apply nrepl/connect (nrepl-connect-args opts)) client (nrepl/client conn 60000) session (nrepl/client-session client) - msg-seq (session (merge (:line options) - (:send options) - {:op (:op options) - :ns (:namespace options) - :code (apply str arguments)})) - result (transduce - (comp - (until-status "done") - (printing (:print options)) - report-exceptions) - null-reducer - {:exit-code 0} - msg-seq) + code-seq (if (empty? arguments) + (map #(binding [*print-dup* true] (pr-str %)) (edn-seq *in*)) + [(apply str arguments)]) + result (loop [[code & code-seq] code-seq] + (let [result (eval-form options session code)] + (if (and (= 0 (:exit-code result)) (seq code-seq)) + (recur code-seq) + result))) ^java.io.Closeable cc conn] (.close cc) (:exit-code result)))