From 6f318ba6e076db2cd9a2d4d5e52f1c715f6b4991 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 1 Dec 2019 13:50:32 +0100 Subject: [PATCH 01/34] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7f52bcaf..49274ec5 100644 --- a/README.md +++ b/README.md @@ -605,6 +605,7 @@ src ├── impl │ ├── tools │ │ └── cli.clj +... ``` ## Thanks From a227b71d2a82c9734424f24a9269b6036d9f99bb Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sat, 7 Dec 2019 11:44:57 +0100 Subject: [PATCH 02/34] sci: support for dynamic vars --- sci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sci b/sci index 9ab0b1d1..ced0ec80 160000 --- a/sci +++ b/sci @@ -1 +1 @@ -Subproject commit 9ab0b1d181605e4c78eb8a90a75982df5c822514 +Subproject commit ced0ec80e018edf4c606079427fcd25b4f3a458e From 02d9a315cbfd64e9bcca5328302c9b6e4874167f Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sat, 7 Dec 2019 11:48:57 +0100 Subject: [PATCH 03/34] sci: support for dynamic vars --- reflection.json | 4 +- src/babashka/impl/clojure/core.clj | 75 +----------------------------- src/babashka/impl/socket_repl.clj | 31 ++++++------ src/babashka/impl/utils.clj | 9 ++++ src/babashka/main.clj | 19 +++++--- test/babashka/main_test.clj | 27 +++++++---- test/babashka/test_utils.clj | 24 ++++++---- 7 files changed, 77 insertions(+), 112 deletions(-) create mode 100644 src/babashka/impl/utils.clj diff --git a/reflection.json b/reflection.json index f7253b1e..bec56a76 100644 --- a/reflection.json +++ b/reflection.json @@ -25,7 +25,9 @@ }, { "name":"java.io.BufferedReader", - "allPublicMethods":true + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true }, { "name": "java.lang.Class", diff --git a/src/babashka/impl/clojure/core.clj b/src/babashka/impl/clojure/core.clj index b998ca02..896d06a2 100644 --- a/src/babashka/impl/clojure/core.clj +++ b/src/babashka/impl/clojure/core.clj @@ -2,65 +2,8 @@ {:no-doc true} (:refer-clojure :exclude [future])) -(defn future - [_ _ & body] - `(~'future-call (fn [] ~@body))) - -(defn __close!__ [^java.io.Closeable x] - (.close x)) - -(defn with-open* - [_ _ bindings & body] - (cond - (= (count bindings) 0) `(do ~@body) - (symbol? (bindings 0)) `(let ~(subvec bindings 0 2) - (try - (with-open ~(subvec bindings 2) ~@body) - (finally - (~'__close!__ ~(bindings 0))))) - :else (throw (IllegalArgumentException. - "with-open only allows Symbols in bindings")))) - -(defn binding* - "This macro only works with symbols that evaluate to vars themselves. See `*in*` and `*out*` below." - [_ _ bindings & body] - `(do - (let [] - (push-thread-bindings (hash-map ~@bindings)) - (try - ~@body - (finally - (pop-thread-bindings)))))) - -;; this works now! -"(def w (java.io.StringWriter.)) (push-thread-bindings {clojure.core/*out* w}) (try (println \"hello\") (finally (pop-thread-bindings))) (prn \">\" (str w))" - -;; this also works now! "(def w (java.io.StringWriter.)) (binding [clojure.core/*out* w] (println \"hello\")) (str w)" - -(defn with-out-str* - [_ _ & body] - `(let [s# (java.io.StringWriter.)] - (binding [*out* s#] - ~@body - (str s#)))) - -(defn with-in-str* - [_ _ s & body] - `(with-open [s# (-> (java.io.StringReader. ~s) clojure.lang.LineNumberingPushbackReader.)] - (binding [*in* s#] - ~@body))) - (def core-extras - {'*in* #'*in* - '*out* #'*out* - 'binding (with-meta binding* {:sci/macro true}) - 'file-seq file-seq - 'future-call future-call - 'future (with-meta future {:sci/macro true}) - 'future-cancel future-cancel - 'future-cancelled? future-cancelled? - 'future-done? future-done? - 'future? future? + {'file-seq file-seq 'agent agent 'send send 'send-off send-off @@ -68,18 +11,4 @@ 'deliver deliver 'shutdown-agents shutdown-agents 'slurp slurp - 'spit spit - 'pmap pmap - 'pr pr - 'prn prn - 'print print - 'println println - 'println-str println-str - 'pop-thread-bindings pop-thread-bindings - 'push-thread-bindings push-thread-bindings - 'flush flush - 'read-line read-line - '__close!__ __close!__ - 'with-open (with-meta with-open* {:sci/macro true}) - 'with-out-str (with-meta with-out-str* {:sci/macro true}) - 'with-in-str (with-meta with-in-str* {:sci/macro true})}) + 'spit spit}) diff --git a/src/babashka/impl/socket_repl.clj b/src/babashka/impl/socket_repl.clj index 53cd465a..fe713acf 100644 --- a/src/babashka/impl/socket_repl.clj +++ b/src/babashka/impl/socket_repl.clj @@ -6,7 +6,9 @@ [clojure.java.io :as io] [clojure.string :as str] [clojure.tools.reader.reader-types :as r] - [sci.impl.interpreter :refer [opts->ctx eval-form]] + [sci.core :as sci] + [sci.impl.interpreter :refer [eval-form]] + [sci.impl.opts :refer [init]] [sci.impl.parser :as parser])) (set! *warn-on-reflection* true) @@ -32,17 +34,20 @@ v)) request-exit)) :eval (fn [expr] - (let [ret (eval-form (update sci-ctx - :env - (fn [env] - (swap! env update-in [:namespaces 'clojure.core] - assoc - '*1 *1 - '*2 *2 - '*3 *3 - '*e *e) - env)) - expr)] + (let [ret (sci/with-bindings {sci/in *in* + sci/out *out* + sci/err *err*} + (eval-form (update sci-ctx + :env + (fn [env] + (swap! env update-in [:namespaces 'clojure.core] + assoc + '*1 *1 + '*2 *2 + '*3 *3 + '*e *e) + env)) + expr))] ret)) :need-prompt (fn [] true) :prompt #(printf "%s=> " (-> sci-ctx :env deref :current-ns))))) @@ -54,7 +59,7 @@ [(first parts) (Integer. ^String (second parts))]) host+port (if-not host (str "localhost:" port) host+port) - sci-ctx (opts->ctx sci-opts) + sci-ctx (init sci-opts) socket (server/start-server {:address host :port port diff --git a/src/babashka/impl/utils.clj b/src/babashka/impl/utils.clj new file mode 100644 index 00000000..55387bc3 --- /dev/null +++ b/src/babashka/impl/utils.clj @@ -0,0 +1,9 @@ +(ns babashka.impl.utils + {:no-doc true} + (:require [sci.core :as sci])) + +(defn eval-string [expr ctx] + (sci/with-bindings {sci/out *out* + sci/in *in* + sci/err *err*} + (sci/eval-string expr ctx))) diff --git a/src/babashka/main.clj b/src/babashka/main.clj index b9e2e29e..2d3a568e 100644 --- a/src/babashka/main.clj +++ b/src/babashka/main.clj @@ -10,12 +10,13 @@ [babashka.impl.pipe-signal-handler :refer [handle-pipe! pipe-signal-received?]] [babashka.impl.socket-repl :as socket-repl] [babashka.impl.tools.cli :refer [tools-cli-namespace]] + [babashka.impl.utils :refer [eval-string]] [babashka.wait :as wait] [clojure.edn :as edn] [clojure.java.io :as io] [clojure.java.shell :as shell] [clojure.string :as str] - [sci.core :as sci]) + [sci.addons :as addons]) (:gen-class)) (set! *warn-on-reflection* true) @@ -146,10 +147,10 @@ Everything after that is bound to *command-line-args*.")) (defn load-file* [ctx file] (let [s (slurp file)] - (sci/eval-string s ctx))) + (eval-string s ctx))) (defn eval* [ctx form] - (sci/eval-string (pr-str form) ctx)) + (eval-string (pr-str form) ctx)) (defn start-socket-repl! [address ctx read-next] (let [ctx (update ctx :bindings assoc @@ -163,6 +164,10 @@ Everything after that is bound to *command-line-args*.")) (defn exit [n] (throw (ex-info "" {:bb/exit-code n}))) +;; (sci/set-var-root! sci/*in* *in*) +;; (sci/set-var-root! sci/*out* *out*) +;; (sci/set-var-root! sci/*err* *err*) + (defn main [& args] (handle-pipe!) @@ -215,6 +220,7 @@ Everything after that is bound to *command-line-args*.")) 'java.lang.AssertionError AssertionError 'java.lang.Boolean Boolean 'java.io.BufferedWriter java.io.BufferedWriter + 'java.io.BufferedReader java.io.BufferedReader 'java.lang.Class Class 'java.lang.Double Double 'java.lang.Exception Exception @@ -247,8 +253,9 @@ Everything after that is bound to *command-line-args*.")) System java.lang.System Thread java.lang.Thread}} ctx (update ctx :bindings assoc 'eval #(eval* ctx %) - 'load-file #(load-file* ctx %)) - _preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim) (sci/eval-string ctx)) + 'load-file #(load-file* ctx %)) + ctx (addons/future ctx) + _preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim) (eval-string ctx)) exit-code (or #_(binding [*out* *err*] @@ -269,7 +276,7 @@ Everything after that is bound to *command-line-args*.")) {:sci/deref! true})) in)] (if (identical? ::EOF in) [nil 0] ;; done streaming - (let [res [(let [res (sci/eval-string expr ctx)] + (let [res [(let [res (eval-string expr ctx)] (when (some? res) (if-let [pr-f (cond shell-out println edn-out prn)] diff --git a/test/babashka/main_test.clj b/test/babashka/main_test.clj index af5c342d..92d3807b 100644 --- a/test/babashka/main_test.clj +++ b/test/babashka/main_test.clj @@ -6,10 +6,11 @@ [clojure.java.shell :refer [sh]] [clojure.string :as str] [clojure.test :as test :refer [deftest is testing]] - [clojure.java.io :as io])) + [clojure.java.io :as io] + [sci.core :as sci])) (defn bb [input & args] - (edn/read-string (apply test-utils/bb (str input) (map str args)))) + (edn/read-string (apply test-utils/bb (when (some? input) (str input)) (map str args)))) (deftest parse-opts-test (is (= {:expression "(println 123)"} @@ -82,6 +83,9 @@ "(map-indexed #(-> [%1 %2]) *in*)") (bb "(keep #(when (re-find #\"(?i)clojure\" (second %)) (first %)) *in*)")))))) +(deftest println-test + (is (= "hello\n" (test-utils/bb nil "(println \"hello\")")))) + (deftest input-test (testing "bb doesn't wait for input if *in* isn't used" (is (= "2\n" (with-out-str (main/main "(inc 1)")))))) @@ -93,8 +97,10 @@ (is (not-empty s)))) (let [out (java.io.StringWriter.) err (java.io.StringWriter.) - exit-code (binding [*out* out *err* err] - (main/main "--time" "(println \"Hello world!\") (System/exit 42)"))] + exit-code (sci/with-bindings {sci/out out + sci/err err} + (binding [*out* out *err* err] + (main/main "--time" "(println \"Hello world!\") (System/exit 42)")))] (is (= (str out) "Hello world!\n")) (is (re-find #"took" (str err))) (is (= 42 exit-code)))) @@ -110,12 +116,7 @@ "Usage:")))) (deftest ssl-test - (let [graalvm-home (System/getenv "GRAALVM_HOME") - lib-path (format "%1$s/jre/lib:%1$s/jre/lib/amd64" graalvm-home) - ;; _ (prn "lib-path" lib-path) - resp (bb nil (format "(System/setProperty \"java.library.path\" \"%s\") - (slurp \"https://www.google.com\")" - lib-path))] + (let [resp (bb nil "(slurp \"https://www.google.com\")")] (is (re-find #"doctype html" resp)))) (deftest stream-test @@ -288,3 +289,9 @@ (java.nio.file.Files/copy p p' (into-array [java.nio.file.StandardCopyOption/REPLACE_EXISTING]))))))" temp-path)) (is (.exists f2)))) + +(deftest future-print-test + (testing "the root binding of sci/*out*" + (is (= "hello" (bb nil "@(future (prn \"hello\"))")))) + + ) diff --git a/test/babashka/test_utils.clj b/test/babashka/test_utils.clj index b5996386..5a42415d 100644 --- a/test/babashka/test_utils.clj +++ b/test/babashka/test_utils.clj @@ -1,19 +1,25 @@ (ns babashka.test-utils (:require [babashka.main :as main] - [me.raynes.conch :refer [let-programs] :as sh])) + [me.raynes.conch :refer [let-programs] :as sh] + [sci.core :as sci])) (set! *warn-on-reflection* true) (defn bb-jvm [input & args] - (let [es (java.io.StringWriter.) - os (java.io.StringWriter.)] - (binding [*err* es - *out* os] - (let [res (if input - (with-in-str input - (apply main/main args)) - (apply main/main args))] + (let [os (java.io.StringWriter.) + es (java.io.StringWriter.) + is (when input + (java.io.StringReader. input)) + bindings-map (cond-> {sci/out os + sci/err es} + is (assoc sci/in is))] + (sci/with-bindings bindings-map + (let [res (binding [*out* os + *err* es] + (if input + (with-in-str input (apply main/main args)) + (apply main/main args)))] (if (zero? res) (str os) (throw (ex-info (str es) From 9af881fb180deaa9ddea58f629215fc4152d6eb7 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sat, 7 Dec 2019 11:56:32 +0100 Subject: [PATCH 04/34] sci --- sci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sci b/sci index ced0ec80..cdd47d00 160000 --- a/sci +++ b/sci @@ -1 +1 @@ -Subproject commit ced0ec80e018edf4c606079427fcd25b4f3a458e +Subproject commit cdd47d000563aa102aa4cfd1103ba61f651b18d3 From f1feec799c7c6b5db6dcbb7174c93aba3150ea68 Mon Sep 17 00:00:00 2001 From: Jakub Holy Date: Sat, 7 Dec 2019 16:27:31 +0100 Subject: [PATCH 05/34] Fix #142 - add NIO/FileAttribute etc. (#143) --- reflection.json | 18 ++++++++++++++++++ src/babashka/main.clj | 3 +++ 2 files changed, 21 insertions(+) diff --git a/reflection.json b/reflection.json index bec56a76..a41c3fcd 100644 --- a/reflection.json +++ b/reflection.json @@ -124,6 +124,24 @@ "name":"java.lang.UNIXProcess", "allPublicMethods":true }, + { + "name":"java.nio.file.attribute.FileAttribute", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, + { + "name":"java.nio.file.attribute.PosixFilePermission", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, + { + "name":"java.nio.file.attribute.PosixFilePermissions", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, { "name":"java.nio.file.Path", "allPublicMethods":true diff --git a/src/babashka/main.clj b/src/babashka/main.clj index 2d3a568e..e8b8010a 100644 --- a/src/babashka/main.clj +++ b/src/babashka/main.clj @@ -235,6 +235,9 @@ Everything after that is bound to *command-line-args*.")) 'java.lang.System System 'java.lang.Thread Thread 'sun.nio.fs.UnixPath sun.nio.fs.UnixPath + 'java.nio.file.attribute.FileAttribute java.nio.file.attribute.FileAttribute + 'java.nio.file.attribute.PosixFilePermission java.nio.file.attribute.PosixFilePermission + 'java.nio.file.attribute.PosixFilePermissions java.nio.file.attribute.PosixFilePermissions 'java.nio.file.CopyOption java.nio.file.CopyOption 'java.nio.file.FileAlreadyExistsException java.nio.file.FileAlreadyExistsException 'java.nio.file.Files java.nio.file.Files From 25b8eb761bb841402e9d7097ec13342c2a734be5 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sat, 7 Dec 2019 16:48:12 +0100 Subject: [PATCH 06/34] sci --- sci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sci b/sci index cdd47d00..f1778710 160000 --- a/sci +++ b/sci @@ -1 +1 @@ -Subproject commit cdd47d000563aa102aa4cfd1103ba61f651b18d3 +Subproject commit f1778710e94d4e12dfce7dccb6ee0a301eccefcc From bd78a8522128877a4c712d862942d2ee064016c1 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sat, 7 Dec 2019 21:30:40 +0100 Subject: [PATCH 07/34] Simplify socket REPL tests --- test/babashka/impl/socket_repl_test.clj | 70 ++++++++++++------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/test/babashka/impl/socket_repl_test.clj b/test/babashka/impl/socket_repl_test.clj index 7b7f41ac..2edae210 100644 --- a/test/babashka/impl/socket_repl_test.clj +++ b/test/babashka/impl/socket_repl_test.clj @@ -4,19 +4,26 @@ [babashka.test-utils :as tu] [clojure.java.shell :refer [sh]] [clojure.string :as str] - [clojure.test :as t :refer [deftest is testing]])) + [clojure.test :as t :refer [deftest is testing]] + [clojure.java.io :as io])) -(def mac? - (str/includes? - (str/lower-case (System/getProperty "os.name")) - "mac")) +(set! *warn-on-reflection* true) -(defn socket-command [expr] - (let [expr (format "echo \"%s\n:repl/exit\" | nc 127.0.0.1 1666" - (pr-str expr)) - ret (sh "bash" "-c" - expr)] - (:out ret))) +(defn socket-command [expr expected] + (with-open [socket (java.net.Socket. "127.0.0.1" 1666) + reader (io/reader socket) + sw (java.io.StringWriter.) + writer (io/writer socket)] + (binding [*out* writer] + (println (str expr)) + (println ":repl/exit\n")) + (loop [] + (when-let [l (.readLine ^java.io.BufferedReader reader)] + (binding [*out* sw] + (println l)) + (when-not (str/includes? l expected) + (recur)))) + (str sw))) (deftest socket-repl-test (try @@ -35,36 +42,19 @@ (while (not (zero? (:exit (sh "bash" "-c" "lsof -t -i:1666")))))) - (is (str/includes? (socket-command '(+ 1 2 3)) - "user=> 6")) - (testing "ctrl-d exits normally, doesn't print nil" - (is (str/ends-with? (:out (sh "bash" "-c" - (if mac? ;; mac doesn't support -q - "echo \"(inc 1336)\" | nc 127.0.0.1 1666" - "echo \"(inc 1336)\" | nc -q 1 127.0.0.1 1666"))) - "1337\nuser=> "))) + (is (socket-command '(+ 1 2 3) "user=> 6")) (testing "*in*" - (is (str/includes? (socket-command "*in*") - "[1 2 3]"))) + (is (socket-command "*in*" "[1 2 3]"))) (testing "*command-line-args*" - (is (str/includes? (socket-command '*command-line-args*) - "\"a\" \"b\" \"c\""))) + (is (socket-command '*command-line-args* "\"a\" \"b\" \"c\""))) (testing "&env" - (socket-command '(defmacro bindings [] (mapv #(list 'quote %) (keys &env)))) - (socket-command '(defn bar [x y z] (bindings))) - (is (str/includes? (socket-command '(bar 1 2 3)) - "[x y z]"))) + (socket-command '(defmacro bindings [] (mapv #(list 'quote %) (keys &env))) "") + (socket-command '(defn bar [x y z] (bindings)) "") + (is (socket-command '(bar 1 2 3) "[x y z]"))) (testing "reader conditionals" - (is (str/includes? (let [ret (sh "bash" "-c" - (format "echo \"%s\n:repl/exit\" | nc 127.0.0.1 1666" - "#?(:bb 1337 :clj 8888)"))] - (:out ret)) - "1337"))) + (is (socket-command "#?(:bb 1337 :clj 8888)" "1337"))) (testing "*1, *2, *3, *e" - (is (= 2 (count (re-seq #"1\n" (let [ret (sh "bash" "-c" - (format "echo \"%s\n*1\n:repl/exit\" | nc 127.0.0.1 1666" - "1"))] - (:out ret))))))) + (is (socket-command "1\n*1" "1"))) (finally (if tu/jvm? (stop-repl!) @@ -75,4 +65,12 @@ (comment (socket-repl-test) + (stop-repl!) + (start-repl! "0.0.0.0:1666" {:bindings {(with-meta '*in* + {:sci/deref! true}) + (delay [1 2 3]) + '*command-line-args* + ["a" "b" "c"]} + :env (atom {})}) + (socket-command "(+ 1 2 3)" "6") ) From d67c21f64686fcaa99f6a393bd2b56d2900c292f Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sat, 7 Dec 2019 22:17:26 +0100 Subject: [PATCH 08/34] socket repl tests --- test/babashka/impl/socket_repl_test.clj | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/babashka/impl/socket_repl_test.clj b/test/babashka/impl/socket_repl_test.clj index 2edae210..b43eb5c8 100644 --- a/test/babashka/impl/socket_repl_test.clj +++ b/test/babashka/impl/socket_repl_test.clj @@ -23,7 +23,11 @@ (println l)) (when-not (str/includes? l expected) (recur)))) - (str sw))) + (let [s (str sw)] + (is (str/includes? s expected) + (format "\"%s\" does not contain \"%s\"" + s expected)) + s))) (deftest socket-repl-test (try @@ -65,6 +69,7 @@ (comment (socket-repl-test) + (t/run-tests) (stop-repl!) (start-repl! "0.0.0.0:1666" {:bindings {(with-meta '*in* {:sci/deref! true}) From 612e6f38fb80cef66177f98592937dba2f245063 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sat, 7 Dec 2019 22:18:07 +0100 Subject: [PATCH 09/34] v0.0.38 --- resources/BABASHKA_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/BABASHKA_VERSION b/resources/BABASHKA_VERSION index 0d2182d0..e4a35e8b 100644 --- a/resources/BABASHKA_VERSION +++ b/resources/BABASHKA_VERSION @@ -1 +1 @@ -0.0.38-SNAPSHOT +0.0.38 From a5be01c740f426e74ed774902b059482f4ac9130 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sat, 7 Dec 2019 23:10:34 +0100 Subject: [PATCH 10/34] Socket repl tests fix --- sci | 2 +- test/babashka/impl/socket_repl_test.clj | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sci b/sci index f1778710..60778bfa 160000 --- a/sci +++ b/sci @@ -1 +1 @@ -Subproject commit f1778710e94d4e12dfce7dccb6ee0a301eccefcc +Subproject commit 60778bfaabf4fbed635d9490bac88b86fdfb0e4d diff --git a/test/babashka/impl/socket_repl_test.clj b/test/babashka/impl/socket_repl_test.clj index b43eb5c8..df9f4f62 100644 --- a/test/babashka/impl/socket_repl_test.clj +++ b/test/babashka/impl/socket_repl_test.clj @@ -21,8 +21,7 @@ (when-let [l (.readLine ^java.io.BufferedReader reader)] (binding [*out* sw] (println l)) - (when-not (str/includes? l expected) - (recur)))) + (recur))) (let [s (str sw)] (is (str/includes? s expected) (format "\"%s\" does not contain \"%s\"" @@ -46,15 +45,15 @@ (while (not (zero? (:exit (sh "bash" "-c" "lsof -t -i:1666")))))) - (is (socket-command '(+ 1 2 3) "user=> 6")) + (is (socket-command "(+ 1 2 3)" "user=> 6")) (testing "*in*" (is (socket-command "*in*" "[1 2 3]"))) (testing "*command-line-args*" (is (socket-command '*command-line-args* "\"a\" \"b\" \"c\""))) (testing "&env" - (socket-command '(defmacro bindings [] (mapv #(list 'quote %) (keys &env))) "") - (socket-command '(defn bar [x y z] (bindings)) "") - (is (socket-command '(bar 1 2 3) "[x y z]"))) + (socket-command "(defmacro bindings [] (mapv #(list 'quote %) (keys &env)))" "bindings") + (socket-command "(defn bar [x y z] (bindings))" "bar") + (is (socket-command "(bar 1 2 3)" "[x y z]"))) (testing "reader conditionals" (is (socket-command "#?(:bb 1337 :clj 8888)" "1337"))) (testing "*1, *2, *3, *e" @@ -69,7 +68,8 @@ (comment (socket-repl-test) - (t/run-tests) + (dotimes [_ 1000] + (t/run-tests)) (stop-repl!) (start-repl! "0.0.0.0:1666" {:bindings {(with-meta '*in* {:sci/deref! true}) From 8304f41f3f52178b216bbf56208d09770029ca7d Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 8 Dec 2019 12:04:12 +0100 Subject: [PATCH 11/34] bump version --- resources/BABASHKA_RELEASED_VERSION | 2 +- resources/BABASHKA_VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/BABASHKA_RELEASED_VERSION b/resources/BABASHKA_RELEASED_VERSION index 1435d6cf..e4a35e8b 100644 --- a/resources/BABASHKA_RELEASED_VERSION +++ b/resources/BABASHKA_RELEASED_VERSION @@ -1 +1 @@ -0.0.37 +0.0.38 diff --git a/resources/BABASHKA_VERSION b/resources/BABASHKA_VERSION index e4a35e8b..3dceb212 100644 --- a/resources/BABASHKA_VERSION +++ b/resources/BABASHKA_VERSION @@ -1 +1 @@ -0.0.38 +0.0.39-SNAPSHOT From c2037c0ed852766da28133c5aad2447b38a5981a Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 8 Dec 2019 12:46:06 +0100 Subject: [PATCH 12/34] [#137] Add REPL prompt at startup --- resources/BABASHKA_VERSION | 2 +- src/babashka/impl/repl.clj | 54 +++++++++++++++++++++++++++++++ src/babashka/impl/socket_repl.clj | 50 ++-------------------------- src/babashka/main.clj | 21 ++++++++++-- test/babashka/impl/repl_test.clj | 35 ++++++++++++++++++++ test/babashka/main_test.clj | 8 +---- 6 files changed, 113 insertions(+), 57 deletions(-) create mode 100644 src/babashka/impl/repl.clj create mode 100644 test/babashka/impl/repl_test.clj diff --git a/resources/BABASHKA_VERSION b/resources/BABASHKA_VERSION index 3dceb212..12a74d75 100644 --- a/resources/BABASHKA_VERSION +++ b/resources/BABASHKA_VERSION @@ -1 +1 @@ -0.0.39-SNAPSHOT +0.0.39 diff --git a/src/babashka/impl/repl.clj b/src/babashka/impl/repl.clj new file mode 100644 index 00000000..68b461fb --- /dev/null +++ b/src/babashka/impl/repl.clj @@ -0,0 +1,54 @@ +(ns babashka.impl.repl + {:no-doc true} + (:require + [babashka.impl.clojure.main :as m] + [clojure.java.io :as io] + [clojure.string :as str] + [clojure.tools.reader.reader-types :as r] + [sci.core :as sci] + [sci.impl.interpreter :refer [eval-form]] + [sci.impl.opts :refer [init]] + [sci.impl.parser :as parser])) + +(defn repl + "REPL with predefined hooks for attachable socket server." + [sci-ctx] + (let [in (r/indexing-push-back-reader (r/push-back-reader *in*))] + (m/repl + :init #(do (println "Babashka" + (str "v" (str/trim (slurp (io/resource "BABASHKA_VERSION")))) + "REPL.") + (println "Use :repl/quit or :repl/exit to quit the REPL.") + (println "Clojure rocks, Bash reaches.") + (println)) + :read (fn [_request-prompt request-exit] + (if (r/peek-char in) ;; if this is nil, we reached EOF + (let [v (parser/parse-next in #{:bb} {:current (-> sci-ctx :env deref :current-ns)})] + (if (or (identical? :repl/quit v) + (identical? :repl/exit v) + (identical? :edamame.impl.parser/eof v)) + request-exit + v)) + request-exit)) + :eval (fn [expr] + (let [ret (sci/with-bindings {sci/in *in* + sci/out *out* + sci/err *err*} + (eval-form (update sci-ctx + :env + (fn [env] + (swap! env update-in [:namespaces 'clojure.core] + assoc + '*1 *1 + '*2 *2 + '*3 *3 + '*e *e) + env)) + expr))] + ret)) + :need-prompt (fn [] true) + :prompt #(printf "%s=> " (-> sci-ctx :env deref :current-ns))))) + +(defn start-repl! [ctx] + (let [sci-ctx (init ctx)] + (repl sci-ctx))) diff --git a/src/babashka/impl/socket_repl.clj b/src/babashka/impl/socket_repl.clj index fe713acf..13195287 100644 --- a/src/babashka/impl/socket_repl.clj +++ b/src/babashka/impl/socket_repl.clj @@ -2,56 +2,12 @@ {:no-doc true} (:require [babashka.impl.clojure.core.server :as server] - [babashka.impl.clojure.main :as m] - [clojure.java.io :as io] + [babashka.impl.repl :as repl] [clojure.string :as str] - [clojure.tools.reader.reader-types :as r] - [sci.core :as sci] - [sci.impl.interpreter :refer [eval-form]] - [sci.impl.opts :refer [init]] - [sci.impl.parser :as parser])) + [sci.impl.opts :refer [init]])) (set! *warn-on-reflection* true) -(defn repl - "REPL with predefined hooks for attachable socket server." - [sci-ctx] - (let [in (r/indexing-push-back-reader (r/push-back-reader *in*))] - (m/repl - :init #(do (println "Babashka" - (str "v" (str/trim (slurp (io/resource "BABASHKA_VERSION")))) - "REPL.") - (println "Use :repl/quit or :repl/exit to quit the REPL.") - (println "Clojure rocks, Bash reaches.") - (println)) - :read (fn [_request-prompt request-exit] - (if (r/peek-char in) ;; if this is nil, we reached EOF - (let [v (parser/parse-next in #{:bb} {:current (-> sci-ctx :env deref :current-ns)})] - (if (or (identical? :repl/quit v) - (identical? :repl/exit v) - (identical? :edamame.impl.parser/eof v)) - request-exit - v)) - request-exit)) - :eval (fn [expr] - (let [ret (sci/with-bindings {sci/in *in* - sci/out *out* - sci/err *err*} - (eval-form (update sci-ctx - :env - (fn [env] - (swap! env update-in [:namespaces 'clojure.core] - assoc - '*1 *1 - '*2 *2 - '*3 *3 - '*e *e) - env)) - expr))] - ret)) - :need-prompt (fn [] true) - :prompt #(printf "%s=> " (-> sci-ctx :env deref :current-ns))))) - (defn start-repl! [host+port sci-opts] (let [parts (str/split host+port #":") [host port] (if (= 1 (count parts)) @@ -64,7 +20,7 @@ {:address host :port port :name "bb" - :accept babashka.impl.socket-repl/repl + :accept babashka.impl.repl/repl :args [sci-ctx]})] (println "Babashka socket REPL started at" host+port) socket)) diff --git a/src/babashka/main.clj b/src/babashka/main.clj index e8b8010a..23f7d41c 100644 --- a/src/babashka/main.clj +++ b/src/babashka/main.clj @@ -8,6 +8,7 @@ [babashka.impl.conch :refer [conch-namespace]] [babashka.impl.csv :as csv] [babashka.impl.pipe-signal-handler :refer [handle-pipe! pipe-signal-received?]] + [babashka.impl.repl :as repl] [babashka.impl.socket-repl :as socket-repl] [babashka.impl.tools.cli :refer [tools-cli-namespace]] [babashka.impl.utils :refer [eval-string]] @@ -65,6 +66,11 @@ (recur (rest options) (assoc opts-map :file (first options)))) + ("--repl") + (let [options (rest options)] + (recur (rest options) + (assoc opts-map + :repl true))) ("--socket-repl") (let [options (rest options)] (recur (rest options) @@ -127,6 +133,7 @@ --stream: stream over lines or EDN values from stdin. Combined with -i or -I *in* becomes a single value per iteration. -e, --eval : evaluate an expression -f, --file : evaluate a file + --repl: start REPL --socket-repl: start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666). --time: print execution time before exiting. @@ -152,6 +159,13 @@ Everything after that is bound to *command-line-args*.")) (defn eval* [ctx form] (eval-string (pr-str form) ctx)) +(defn start-repl! [ctx read-next] + (let [ctx (update ctx :bindings assoc + (with-meta '*in* + {:sci/deref! true}) + (read-next))] + (repl/start-repl! ctx))) + (defn start-socket-repl! [address ctx read-next] (let [ctx (update ctx :bindings assoc (with-meta '*in* @@ -176,7 +190,9 @@ Everything after that is bound to *command-line-args*.")) (let [t0 (System/currentTimeMillis) {:keys [:version :shell-in :edn-in :shell-out :edn-out :help? :file :command-line-args - :expression :stream? :time? :socket-repl :verbose?] :as _opts} + :expression :stream? :time? + :repl :socket-repl + :verbose?] :as _opts} (parse-opts args) read-next (fn [*in*] (if (pipe-signal-received?) @@ -268,6 +284,7 @@ Everything after that is bound to *command-line-args*.")) [(print-version) 0] help? [(print-help) 0] + repl [(start-repl! ctx #(read-next *in*)) 0] socket-repl [(start-socket-repl! socket-repl ctx #(read-next *in*)) 0] :else (try @@ -292,7 +309,7 @@ Everything after that is bound to *command-line-args*.")) (if stream? (recur (read-next *in*)) res))))) - [(print-help) 1])) + [(start-repl! ctx #(read-next *in*)) 0])) (catch Throwable e (binding [*out* *err*] (let [d (ex-data e) diff --git a/test/babashka/impl/repl_test.clj b/test/babashka/impl/repl_test.clj new file mode 100644 index 00000000..8adbde77 --- /dev/null +++ b/test/babashka/impl/repl_test.clj @@ -0,0 +1,35 @@ +(ns babashka.impl.repl-test + (:require + [babashka.impl.repl :refer [start-repl!]] + [clojure.string :as str] + [clojure.test :as t :refer [deftest is]])) + +(set! *warn-on-reflection* true) + +(defn repl! [] + (start-repl! {:bindings {(with-meta '*in* + {:sci/deref! true}) + (delay [1 2 3]) + '*command-line-args* + ["a" "b" "c"]} + :env (atom {})})) + +(defn assert-repl [expr expected] + (is (str/includes? (with-out-str + (with-in-str (str expr "\n:repl/quit") + (repl!))) expected))) + +(deftest repl-test + (assert-repl "(+ 1 2 3)" "6") + (assert-repl "(defn foo [] (+ 1 2 3)) (foo)" "6") + (assert-repl "(defn foo [] (+ 1 2 3)) (foo)" "6") + (assert-repl "1\n(inc *1)" "2") + (assert-repl "1\n(dec *1)(+ *2 *2)" "2") + (assert-repl "1\n(dec *1)(+ *2 *2)" "2") + (assert-repl "*command-line-args*" "[\"a\" \"b\" \"c\"]") + (assert-repl "*in*" "[1 2 3]")) + +;;;; Scratch + +(comment + ) diff --git a/test/babashka/main_test.clj b/test/babashka/main_test.clj index 92d3807b..f8251035 100644 --- a/test/babashka/main_test.clj +++ b/test/babashka/main_test.clj @@ -107,13 +107,7 @@ (deftest malformed-command-line-args-test (is (thrown-with-msg? Exception #"File does not exist: non-existing\n" - (bb nil "-f" "non-existing"))) - (testing "no arguments prints help" - (is (str/includes? - (try (test-utils/bb nil) - (catch clojure.lang.ExceptionInfo e - (:stdout (ex-data e)))) - "Usage:")))) + (bb nil "-f" "non-existing")))) (deftest ssl-test (let [resp (bb nil "(slurp \"https://www.google.com\")")] From b735ae62cc4f52a6b6819fc0058814adcda3bc72 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 8 Dec 2019 12:51:19 +0100 Subject: [PATCH 13/34] README --- README.md | 4 +++- src/babashka/main.clj | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 49274ec5..ea8289aa 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ You may also download a binary from [Github](https://github.com/borkdude/babashk ## Usage ``` shellsession -Usage: bb [ -i | -I ] [ -o | -O ] [ --stream ] ( -e | -f | --socket-repl [:] ) +Usage: bb [ -i | -I ] [ -o | -O ] [--verbose] [ --stream ] ( -e | -f | --repl | --socket-repl [:] ) Options: @@ -136,9 +136,11 @@ Options: -I: bind *in* to a lazy seq of EDN values from stdin. -o: write lines to stdout. -O: write EDN values to stdout. + --verbose: print entire stacktrace in case of exception. --stream: stream over lines or EDN values from stdin. Combined with -i or -I *in* becomes a single value per iteration. -e, --eval : evaluate an expression -f, --file : evaluate a file + --repl: start REPL --socket-repl: start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666). --time: print execution time before exiting. diff --git a/src/babashka/main.clj b/src/babashka/main.clj index 23f7d41c..488f983c 100644 --- a/src/babashka/main.clj +++ b/src/babashka/main.clj @@ -110,12 +110,12 @@ (defn print-version [] (println (str "babashka v"(str/trim (slurp (io/resource "BABASHKA_VERSION")))))) -(def usage-string "Usage: bb [ -i | -I ] [ -o | -O ] [--verbose] [ --stream ] ( -e | -f | --socket-repl [:] )") +(def usage-string "Usage: bb [ -i | -I ] [ -o | -O ] [--verbose] [ --stream ] ( -e | -f | --repl | --socket-repl [:] )") (defn print-usage [] (println usage-string)) (defn print-help [] - (println (str "babashka v" (str/trim (slurp (io/resource "BABASHKA_VERSION"))))) + (println (str "Babashka v" (str/trim (slurp (io/resource "BABASHKA_VERSION"))))) ;; (println (str "sci v" (str/trim (slurp (io/resource "SCI_VERSION"))))) (println) (print-usage) From 09e16cc8a4b8332d94eb01f3dea7fd12e0843db6 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 8 Dec 2019 12:57:00 +0100 Subject: [PATCH 14/34] bump version --- resources/BABASHKA_RELEASED_VERSION | 2 +- resources/BABASHKA_VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/BABASHKA_RELEASED_VERSION b/resources/BABASHKA_RELEASED_VERSION index e4a35e8b..12a74d75 100644 --- a/resources/BABASHKA_RELEASED_VERSION +++ b/resources/BABASHKA_RELEASED_VERSION @@ -1 +1 @@ -0.0.38 +0.0.39 diff --git a/resources/BABASHKA_VERSION b/resources/BABASHKA_VERSION index 12a74d75..cc02078c 100644 --- a/resources/BABASHKA_VERSION +++ b/resources/BABASHKA_VERSION @@ -1 +1 @@ -0.0.39 +0.0.40-SNAPSHOT From 3c5581fa3478f889433a1403bbe7b3bdf11c3cdb Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Tue, 10 Dec 2019 09:44:41 +0100 Subject: [PATCH 15/34] [#147] Add cheshire --- README.md | 6 ++++-- project.clj | 3 ++- src/babashka/impl/cheshire.clj | 14 ++++++++++++++ src/babashka/main.clj | 7 +++++-- 4 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 src/babashka/impl/cheshire.clj diff --git a/README.md b/README.md index ea8289aa..b5d2fba6 100644 --- a/README.md +++ b/README.md @@ -151,8 +151,9 @@ Everything after that is bound to *command-line-args*. The `clojure.core` functions are accessible without a namespace alias. The following namespaces are required by default and available through the -pre-defined aliases. You may use `require` + `:as` and/or `:refer` on these -namespaces. If not all vars are available, they are enumerated explicitly. +pre-defined aliases in the `user` namespace. You may use `require` + `:as` +and/or `:refer` on these namespaces. If not all vars are available, they are +enumerated explicitly. - `clojure.string` aliased as `str` - `clojure.set` aliased as `set` @@ -169,6 +170,7 @@ namespaces. If not all vars are available, they are enumerated explicitly. aliased as `conch` - [`clojure.tools.cli`](https://github.com/clojure/tools.cli) aliased as `tools.cli` - [`clojure.data.csv`](https://github.com/clojure/data.csv) aliased as `csv` +- [`cheshire.core`](https://github.com/dakrone/cheshire) aliased as `json` The following Java classes are available: diff --git a/project.clj b/project.clj index d87c4355..2c27951a 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,8 @@ [borkdude/edamame "0.0.9-alpha.2"] [org.clojure/core.async "0.4.500"] [org.clojure/tools.cli "0.4.2"] - [org.clojure/data.csv "0.1.4"]] + [org.clojure/data.csv "0.1.4"] + [cheshire "5.9.0"]] :profiles {:test {:dependencies [[clj-commons/conch "0.9.2"]]} :uberjar {:global-vars {*assert* false} :jvm-opts ["-Dclojure.compiler.direct-linking=true" diff --git a/src/babashka/impl/cheshire.clj b/src/babashka/impl/cheshire.clj new file mode 100644 index 00000000..21faa7ba --- /dev/null +++ b/src/babashka/impl/cheshire.clj @@ -0,0 +1,14 @@ +(ns babashka.impl.cheshire + {:no-doc true} + (:require [cheshire.core :as json])) + +(def cheshire-core-namespace + {'encode json/encode + 'generate-string json/generate-string + 'encode-stream json/encode-stream + 'generate-stream json/generate-stream + 'encode-smile json/encode-smile + 'generate-smile json/generate-smile + 'decode json/decode + 'parse-string json/parse-string + 'decode-smile json/decode-smile}) diff --git a/src/babashka/main.clj b/src/babashka/main.clj index 488f983c..73ae4579 100644 --- a/src/babashka/main.clj +++ b/src/babashka/main.clj @@ -2,6 +2,7 @@ {:no-doc true} (:require [babashka.impl.async :refer [async-namespace]] + [babashka.impl.cheshire :refer [cheshire-core-namespace]] [babashka.impl.clojure.core :refer [core-extras]] [babashka.impl.clojure.java.io :refer [io-namespace]] [babashka.impl.clojure.stacktrace :refer [print-stack-trace]] @@ -215,7 +216,8 @@ Everything after that is bound to *command-line-args*.")) io clojure.java.io conch me.raynes.conch.low-level async clojure.core.async - csv clojure.data.csv} + csv clojure.data.csv + json cheshire.core} :namespaces {'clojure.core (assoc core-extras '*command-line-args* command-line-args) 'clojure.tools.cli tools-cli-namespace @@ -227,7 +229,8 @@ Everything after that is bound to *command-line-args*.")) 'clojure.java.io io-namespace 'me.raynes.conch.low-level conch-namespace 'clojure.core.async async-namespace - 'clojure.data.csv csv/csv-namespace} + 'clojure.data.csv csv/csv-namespace + 'cheshire.core cheshire-core-namespace} :bindings {'java.lang.System/exit exit ;; override exit, so we have more control 'System/exit exit} :env env From 76d2b0c3ec41ea7200f7bc20e1a965427899a1d8 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Tue, 10 Dec 2019 09:45:41 +0100 Subject: [PATCH 16/34] v0.0.40 --- resources/BABASHKA_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/BABASHKA_VERSION b/resources/BABASHKA_VERSION index cc02078c..4fe2fe84 100644 --- a/resources/BABASHKA_VERSION +++ b/resources/BABASHKA_VERSION @@ -1 +1 @@ -0.0.40-SNAPSHOT +0.0.40 From b09e6f3c4d6c9f21f22b860719d4d0f056e465a1 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Tue, 10 Dec 2019 09:57:59 +0100 Subject: [PATCH 17/34] bump version --- resources/BABASHKA_RELEASED_VERSION | 2 +- resources/BABASHKA_VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/BABASHKA_RELEASED_VERSION b/resources/BABASHKA_RELEASED_VERSION index 12a74d75..4fe2fe84 100644 --- a/resources/BABASHKA_RELEASED_VERSION +++ b/resources/BABASHKA_RELEASED_VERSION @@ -1 +1 @@ -0.0.39 +0.0.40 diff --git a/resources/BABASHKA_VERSION b/resources/BABASHKA_VERSION index 4fe2fe84..d806e51f 100644 --- a/resources/BABASHKA_VERSION +++ b/resources/BABASHKA_VERSION @@ -1 +1 @@ -0.0.40 +0.0.41-SNAPSHOT From c5d67681581b9b78eff354f8bca41cd089fe1d85 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Thu, 12 Dec 2019 12:18:24 +0100 Subject: [PATCH 18/34] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b5d2fba6..995b0eb6 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![CircleCI](https://circleci.com/gh/borkdude/babashka/tree/master.svg?style=shield)](https://circleci.com/gh/borkdude/babashka/tree/master) [![Clojars Project](https://img.shields.io/clojars/v/borkdude/babashka.svg)](https://clojars.org/borkdude/babashka) [![cljdoc badge](https://cljdoc.org/badge/borkdude/babashka)](https://cljdoc.org/d/borkdude/babashka/CURRENT) +[![project chat](https://img.shields.io/badge/slack-join_chat-brightgreen.svg)](https://app.slack.com/client/T03RZGPFR/CLX41ASCS) + A Clojure [babushka](https://en.wikipedia.org/wiki/Headscarf) for the grey areas of Bash. From a74be0ad1a98104fbeb1384f2991ba0d0c3d5233 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Thu, 12 Dec 2019 23:07:35 +0100 Subject: [PATCH 19/34] [#146] support --classpath / -cp and --main / -m (#150) --- .circleci/script/release | 6 +- .gitignore | 3 + README.md | 115 ++++++--- sci | 2 +- script/test | 13 +- src-bash/bbk | 219 ++++++++++++++++++ src/babashka/impl/classpath.clj | 62 +++++ src/babashka/main.clj | 62 +++-- .../src_for_classpath_test/env/env_ns.clj | 4 + .../babashka/src_for_classpath_test/foo.jar | Bin 0 -> 492 bytes .../src_for_classpath_test/my/impl.cljc | 5 + .../src_for_classpath_test/my/main.clj | 5 + .../src_for_classpath_test/my_script.bb | 4 + test/babashka/classpath_test.clj | 27 +++ test/babashka/main_test.clj | 3 +- 15 files changed, 470 insertions(+), 60 deletions(-) create mode 100755 src-bash/bbk create mode 100644 src/babashka/impl/classpath.clj create mode 100644 test-resources/babashka/src_for_classpath_test/env/env_ns.clj create mode 100644 test-resources/babashka/src_for_classpath_test/foo.jar create mode 100644 test-resources/babashka/src_for_classpath_test/my/impl.cljc create mode 100644 test-resources/babashka/src_for_classpath_test/my/main.clj create mode 100644 test-resources/babashka/src_for_classpath_test/my_script.bb create mode 100644 test/babashka/classpath_test.clj diff --git a/.circleci/script/release b/.circleci/script/release index fa085248..87d81d3a 100755 --- a/.circleci/script/release +++ b/.circleci/script/release @@ -3,14 +3,16 @@ rm -rf /tmp/release mkdir -p /tmp/release cp bb /tmp/release +cp src-bash/bbk /tmp/release + VERSION=$(cat resources/BABASHKA_VERSION) cd /tmp/release ## release binary as zip archive -zip "babashka-$VERSION-$BABASHKA_PLATFORM-amd64.zip" bb +zip "babashka-$VERSION-$BABASHKA_PLATFORM-amd64.zip" bb bbk ## cleanup -rm bb +rm bb bbk diff --git a/.gitignore b/.gitignore index 2a556140..a96a9d65 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ pom.xml.asc /bb .clj-kondo/.cache !java/src/babashka/impl/LockFix.class +!test-resources/babashka/src_for_classpath_test/foo.jar +.cpcache +deps.edn diff --git a/README.md b/README.md index 995b0eb6..da953657 100644 --- a/README.md +++ b/README.md @@ -311,6 +311,83 @@ $ cat script.clj ("hello" "1" "2" "3") ``` +## Preloads + +The environment variable `BABASHKA_PRELOADS` allows to define code that will be +available in all subsequent usages of babashka. + +``` shellsession +BABASHKA_PRELOADS='(defn foo [x] (+ x 2))' +BABASHKA_PRELOADS=$BABASHKA_PRELOADS' (defn bar [x] (* x 2))' +export BABASHKA_PRELOADS +``` + +Note that you can concatenate multiple expressions. Now you can use these functions in babashka: + +``` shellsession +$ bb '(-> (foo *in*) bar)' <<< 1 +6 +``` + +You can also preload an entire file using `load-file`: + +``` shellsession +export BABASHKA_PRELOADS='(load-file "my_awesome_prelude.clj")' +``` + +Note that `*in*` is not available in preloads. + +## Classpath + +Babashka accepts a `--classpath` option that will be used to search for +namespaces and load them: + +``` clojure +$ cat src/my/namespace.clj +(ns my.namespace) +(defn -main [& _args] + (println "Hello from my namespace!")) + +$ bb --classpath src --main my.namespace +Hello from my namespace! +``` + +Note that you can use the `clojure` tool to produce classpaths and download dependencies: + +``` shellsession +$ cat deps.edn +{:deps + {my_gist_script + {:git/url "https://gist.github.com/borkdude/263b150607f3ce03630e114611a4ef42" + :sha "cfc761d06dfb30bb77166b45d439fe8fe54a31b8"}}} + + +$ CLASSPATH=$(clojure -Spath) +$ bb --classpath "$CLASSPATH" --main my-gist-script +Hello from gist script! +``` + +The `bbk` shell script is a thin wrapper around the `clojure` tool, so you can +use Babashka projects in a similar way: + +``` shellsession +$ bbk -m my-gist-script +Hello from gist script! +``` + +The script will call `bb` with the `--classpath` argument as a result of calling +`clojure`. + +If there is no `--classpath` argument, the `BABASHKA_CLASSPATH` environment +variable will be used if set: + +``` shellsession +$ export BABASHKA_CLASSPATH=$(clojure -Spath) +$ export BABASHKA_PRELOADS="(require '[my-gist-script])" +$ bb "(my-gist-script/-main)" +Hello from gist script! +``` + ## Parsing command line arguments Babashka ships with `clojure.tools.cli`: @@ -348,32 +425,6 @@ $ ./bb example.clj babashka doesn't support in-ns yet! ``` -## Preloads - -The environment variable `BABASHKA_PRELOADS` allows to define code that will be -available in all subsequent usages of babashka. - -``` shellsession -BABASHKA_PRELOADS='(defn foo [x] (+ x 2))' -BABASHKA_PRELOADS=$BABASHKA_PRELOADS' (defn bar [x] (* x 2))' -export BABASHKA_PRELOADS -``` - -Note that you can concatenate multiple expressions. Now you can use these functions in babashka: - -``` shellsession -$ bb '(-> (foo *in*) bar)' <<< 1 -6 -``` - -You can also preload an entire file using `load-file`: - -``` shellsession -export BABASHKA_PRELOADS='(load-file "my_awesome_prelude.clj")' -``` - -Note that `*in*` is not available in preloads. - ## Socket REPL Start the socket REPL like this: @@ -448,16 +499,8 @@ Differences with Clojure: - A subset of Java classes are supported. -- Only the `clojure.core`, `clojure.set` and `clojure.string` namespaces are - available from Clojure. - -- There is no classpath and no support for loading code from Maven/Clojars - dependencies. However, you can use `load-file` to load external code from - disk. - -- `require` does not load files; it only provides a way to create different - aliases for included namespaces, which makes it easier to make scripts - portable between the JVM and babashka. +- Only the `clojure.core`, `clojure.set`, `clojure.string` and `clojure.walk` + namespaces are available from Clojure. - Interpretation comes with overhead. Therefore tight loops are likely slower than in Clojure on the JVM. diff --git a/sci b/sci index 60778bfa..968734ac 160000 --- a/sci +++ b/sci @@ -1 +1 @@ -Subproject commit 60778bfaabf4fbed635d9490bac88b86fdfb0e4d +Subproject commit 968734ac60a2410f0b716df3829a5287f263e8f0 diff --git a/script/test b/script/test index e631756a..58f31d5f 100755 --- a/script/test +++ b/script/test @@ -1,6 +1,15 @@ #!/usr/bin/env bash set -eo pipefail -export BABASHKA_PRELOADS='(defn __bb__foo [] "foo") (defn __bb__bar [] "bar")' +BABASHKA_PRELOADS="" +BABASHKA_CLASSPATH="" +lein test "$@" -lein test +BABASHKA_PRELOADS='(defn __bb__foo [] "foo") (defn __bb__bar [] "bar")' +BABASHKA_PRELOADS_TEST=true +lein test :only babashka.main-test/preloads-test + +BABASHKA_PRELOADS="(require '[env-ns])" +BABASHKA_CLASSPATH_TEST=true +BABASHKA_CLASSPATH="test-resources/babashka/src_for_classpath_test/env" +lein test :only babashka.classpath-test/classpath-env-test diff --git a/src-bash/bbk b/src-bash/bbk new file mode 100755 index 00000000..38b6c36e --- /dev/null +++ b/src-bash/bbk @@ -0,0 +1,219 @@ +#!/usr/bin/env bash + +set -e + +function join { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; } + +# Extract opts +print_classpath=false +describe=false +verbose=false +force=false +repro=false +tree=false +pom=false +resolve_tags=false +help=false +resolve_aliases=() +classpath_aliases=() +main_aliases=() +all_aliases=() +while [ $# -gt 0 ] +do + case "$1" in + -J*) + shift + ;; + -R*) + resolve_aliases+=("${1:2}") + shift + ;; + -C*) + classpath_aliases+=("${1:2}") + shift + ;; + -O*) + shift + ;; + -M*) + main_aliases+=("${1:2}") + shift + ;; + -A*) + all_aliases+=("${1:2}") + shift + ;; + -Sdeps) + shift + deps_data="${1}" + shift + ;; + -Scp) + shift + force_cp="${1}" + shift + ;; + -Spath) + print_classpath=true + shift + ;; + -Sverbose) + verbose=true + shift + ;; + -Sdescribe) + describe=true + shift + ;; + -Sforce) + force=true + shift + ;; + -Srepro) + repro=true + shift + ;; + -Stree) + tree=true + shift + ;; + -Spom) + pom=true + shift + ;; + -Sresolve-tags) + resolve_tags=true + shift + ;; + -S*) + echo "Invalid option: $1" + exit 1 + ;; + -h|--help|"-?") + if [[ ${#main_aliases[@]} -gt 0 ]] || [[ ${#all_aliases[@]} -gt 0 ]]; then + break + else + help=true + shift + fi + ;; + *) + break + ;; + esac +done + +# Find clojure executable +set +e +CLOJURE_CMD=$(type -p clojure) +set -e +if [[ ! -n "$CLOJURE_CMD" ]]; then + >&2 echo "Couldn't find 'clojure'." + >&2 echo "You can launch Babashka directly using 'bb'." + >&2 echo "To use 'bbk', please ensure 'clojure' is installed and on" + >&2 echo "your path. See https://clojure.org/guides/getting_started" + exit 1 +fi + +if "$help"; then + cat <<-END +Usage: bbk [dep-opt*] [bb-opt*] [arg*] + +The bbk script is a runner for Babashka which ultimately constructs and +invokes a command-line of the form: + +bb --classpath classpath [bb-opt*] [*args] + + The dep-opts are used to build the classpath using the clojure tool: + -Ralias... Concatenated resolve-deps aliases, ex: -R:bench:1.9 + -Calias... Concatenated make-classpath aliases, ex: -C:dev + -Malias... Concatenated main option aliases, ex: -M:test + -Aalias... Concatenated aliases of any kind, ex: -A:dev:mem + -Sdeps EDN Deps data to use as the final deps file + -Spath Compute classpath and echo to stdout only + -Scp CP Do NOT compute or cache classpath, use this one instead + -Srepro Ignore the ~/.clojure/deps.edn config file + -Sforce Force recomputation of the classpath (don't use the cache) + -Spom Generate (or update existing) pom.xml with deps and paths + -Stree Print dependency tree + -Sresolve-tags Resolve git coordinate tags to shas and update deps.edn + -Sverbose Print important path info to console + -Sdescribe Print environment and command parsing info as data + + Additionally, for compatibility with clojure, -Jopt and -Oalias... dep-opts + are accepted but ignored. + +Babashka options: +END + bb -h | tail -n +9 + exit 0 +fi + +# Execute resolve-tags command +if "$resolve_tags"; then + "$CLOJURE_CMD" -Sresolve-tags + exit +fi + +clojure_args=() +if [[ -n "$deps_data" ]]; then + clojure_args+=("-Sdeps" "$deps_data") +fi +if [[ ${#resolve_aliases[@]} -gt 0 ]]; then + clojure_args+=("-R$(join '' ${resolve_aliases[@]})") +fi +if [[ ${#classpath_aliases[@]} -gt 0 ]]; then + clojure_args+=("-C$(join '' ${classpath_aliases[@]})") +fi +if [[ ${#main_aliases[@]} -gt 0 ]]; then + clojure_args+=("-M$(join '' ${main_aliases[@]})") +fi +if [[ ${#all_aliases[@]} -gt 0 ]]; then + clojure_args+=("-A$(join '' ${all_aliases[@]})") +fi +if "$repro"; then + clojure_args+=("-Srepro") +fi +if "$force"; then + clojure_args+=("-Sforce") +fi + +if "$pom"; then + if "$verbose"; then + clojure_args+=("-Sverbose") + fi + "$CLOJURE_CMD" "${clojure_args[@]}" -Spom +elif "$describe"; then + if "$verbose"; then + clojure_args+=("-Sverbose") + fi + "$CLOJURE_CMD" "${clojure_args[@]}" -Sdescribe +elif "$tree"; then + if "$verbose"; then + clojure_args+=("-Sverbose") + fi + "$CLOJURE_CMD" "${clojure_args[@]}" -Stree +else + set -f + if [[ -n "$force_cp" ]]; then + cp="$force_cp" + else + if "$verbose"; then + "$CLOJURE_CMD" "${clojure_args[@]}" -Sverbose -e nil + fi + cp=`"$CLOJURE_CMD" "${clojure_args[@]}" -Spath` + fi + if "$print_classpath"; then + echo $cp + else + if [[ ${#main_aliases[@]} -gt 0 ]] || [[ ${#all_aliases[@]} -gt 0 ]]; then + # Attempt to extract the main cache filename by parsing the output of -Sverbose + cp_file=`"$CLOJURE_CMD" "${clojure_args[@]}" -Sverbose -Spath | grep cp_file | cut -d = -f 2 | sed 's/^ *//g'` + main_file="${cp_file%.cp}.main" + fi + if [[ -e "$main_file" ]]; then + main_cache_opts=($(cat "$main_file")) + fi + exec bb --classpath "$cp" "${main_cache_opts[@]}" "$@" + fi +fi diff --git a/src/babashka/impl/classpath.clj b/src/babashka/impl/classpath.clj new file mode 100644 index 00000000..1b16611f --- /dev/null +++ b/src/babashka/impl/classpath.clj @@ -0,0 +1,62 @@ +(ns babashka.impl.classpath + {:no-doc true} + (:require [clojure.java.io :as io] + [clojure.string :as str]) + (:import [java.util.jar JarFile JarFile$JarFileEntry])) + +(set! *warn-on-reflection* true) + +(defprotocol IResourceResolver + (getResource [this path])) + +(deftype DirectoryResolver [path] + IResourceResolver + (getResource [this resource-path] + (let [f (io/file path resource-path)] + (when (.exists f) + (slurp f))))) + +(defn path-from-jar + [^java.io.File jar-file path] + (with-open [jar (JarFile. jar-file)] + (let [entries (enumeration-seq (.entries jar)) + entry (some (fn [^JarFile$JarFileEntry x] + (let [nm (.getName x)] + (when (and (not (.isDirectory x)) (= path nm)) + (slurp (.getInputStream jar x))))) entries)] + entry))) + +(deftype JarFileResolver [path] + IResourceResolver + (getResource [this resource-path] + (path-from-jar path resource-path))) + +(defn part->entry [part] + (if (str/ends-with? part ".jar") + (JarFileResolver. (io/file part)) + (DirectoryResolver. (io/file part)))) + +(deftype Loader [entries] + IResourceResolver + (getResource [this resource-path] + (some #(getResource % resource-path) entries))) + +(defn loader [^String classpath] + (let [parts (.split classpath (System/getProperty "path.separator")) + entries (map part->entry parts)] + (Loader. entries))) + +(defn source-for-namespace [loader namespace] + (let [ns-str (name namespace) + ^String ns-str (munge ns-str) + path (.replace ns-str "." (System/getProperty "file.separator")) + paths (map #(str path %) [".bb" ".clj" ".cljc"])] + (some #(getResource loader %) paths))) + +;;;; Scratch + +(comment + (def l (loader "src:/Users/borkdude/.m2/repository/cheshire/cheshire/5.9.0/cheshire-5.9.0.jar")) + (source-for-namespace l 'babashka.impl.cheshire) + (source-for-namespace l 'cheshire.core) + ) diff --git a/src/babashka/main.clj b/src/babashka/main.clj index 73ae4579..392436b4 100644 --- a/src/babashka/main.clj +++ b/src/babashka/main.clj @@ -13,6 +13,7 @@ [babashka.impl.socket-repl :as socket-repl] [babashka.impl.tools.cli :refer [tools-cli-namespace]] [babashka.impl.utils :refer [eval-string]] + [babashka.impl.classpath :as cp] [babashka.wait :as wait] [clojure.edn :as edn] [clojure.java.io :as io] @@ -81,7 +82,15 @@ (let [options (rest options)] (recur (rest options) (assoc opts-map :expression (first options)))) - (if (some opts-map [:file :socket-repl :expression]) + ("--classpath", "-cp") + (let [options (rest options)] + (recur (rest options) + (assoc opts-map :classpath (first options)))) + ("--main", "-m") + (let [options (rest options)] + (recur (rest options) + (assoc opts-map :main (first options)))) + (if (some opts-map [:file :socket-repl :expression :main]) (assoc opts-map :command-line-args options) (if (and (not= \( (first (str/trim opt))) @@ -111,7 +120,10 @@ (defn print-version [] (println (str "babashka v"(str/trim (slurp (io/resource "BABASHKA_VERSION")))))) -(def usage-string "Usage: bb [ -i | -I ] [ -o | -O ] [--verbose] [ --stream ] ( -e | -f | --repl | --socket-repl [:] )") +(def usage-string "Usage: bb [ -i | -I ] [ -o | -O ] [ --stream ] [--verbose] + [ ( --classpath | -cp ) ] [ ( --main | -m ) ] + ( -e | -f | --repl | --socket-repl [:] ) + [ arg* ]") (defn print-usage [] (println usage-string)) @@ -123,20 +135,21 @@ (println) (println "Options:") (println " - --help, -h or -?: print this help text. - --version: print the current version of babashka. - - -i: bind *in* to a lazy seq of lines from stdin. - -I: bind *in* to a lazy seq of EDN values from stdin. - -o: write lines to stdout. - -O: write EDN values to stdout. - --verbose: print entire stacktrace in case of exception. - --stream: stream over lines or EDN values from stdin. Combined with -i or -I *in* becomes a single value per iteration. - -e, --eval : evaluate an expression - -f, --file : evaluate a file - --repl: start REPL - --socket-repl: start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666). - --time: print execution time before exiting. + --help, -h or -? Print this help text. + --version Print the current version of babashka. + -i Bind *in* to a lazy seq of lines from stdin. + -I Bind *in* to a lazy seq of EDN values from stdin. + -o Write lines to stdout. + -O Write EDN values to stdout. + --verbose Print entire stacktrace in case of exception. + --stream Stream over lines or EDN values from stdin. Combined with -i or -I *in* becomes a single value per iteration. + -e, --eval Evaluate an expression. + -f, --file Evaluate a file. + -cp, --classpath Classpath to use. + -m, --main Call the -main function from namespace with args. + --repl Start REPL + --socket-repl Start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666). + --time Print execution time before exiting. If neither -e, -f, or --socket-repl are specified, then the first argument that is not parsed as a option is treated as a file if it exists, or as an expression otherwise. Everything after that is bound to *command-line-args*.")) @@ -193,7 +206,8 @@ Everything after that is bound to *command-line-args*.")) :help? :file :command-line-args :expression :stream? :time? :repl :socket-repl - :verbose?] :as _opts} + :verbose? :classpath + :main] :as _opts} (parse-opts args) read-next (fn [*in*] (if (pipe-signal-received?) @@ -208,6 +222,13 @@ Everything after that is bound to *command-line-args*.")) :else (edn/read *in*)))))) env (atom {}) + classpath (or classpath + (System/getenv "BABASHKA_CLASSPATH")) + loader (when classpath + (cp/loader classpath)) + load-fn (when classpath + (fn [{:keys [:namespace]}] + (cp/source-for-namespace loader namespace))) ctx {:aliases '{tools.cli 'clojure.tools.cli edn clojure.edn wait babashka.wait @@ -273,11 +294,16 @@ Everything after that is bound to *command-line-args*.")) File java.io.File String java.lang.String System java.lang.System - Thread java.lang.Thread}} + Thread java.lang.Thread} + :load-fn load-fn} ctx (update ctx :bindings assoc 'eval #(eval* ctx %) 'load-file #(load-file* ctx %)) ctx (addons/future ctx) _preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim) (eval-string ctx)) + expression (if main + (format "(ns user (:require [%1$s])) (apply %1$s/-main *command-line-args*)" + main) + expression) exit-code (or #_(binding [*out* *err*] diff --git a/test-resources/babashka/src_for_classpath_test/env/env_ns.clj b/test-resources/babashka/src_for_classpath_test/env/env_ns.clj new file mode 100644 index 00000000..bc600eec --- /dev/null +++ b/test-resources/babashka/src_for_classpath_test/env/env_ns.clj @@ -0,0 +1,4 @@ +(ns env-ns) + +(defn foo [] + "env!") diff --git a/test-resources/babashka/src_for_classpath_test/foo.jar b/test-resources/babashka/src_for_classpath_test/foo.jar new file mode 100644 index 0000000000000000000000000000000000000000..870949e7c0e6cdac776bb633be69a6f56b20763c GIT binary patch literal 492 zcmWIWW@Zs#;Nak3Fj&&<&wvCt8CV#6T|*poJ^kGD|D9rBU}gyLX6FE@V1g>>wOP*EktjPXU^s$H$>;Qe|y^rmI#)B}z0oS8ZUC$1bP0P>M zOU}u Date: Fri, 13 Dec 2019 10:58:44 +0100 Subject: [PATCH 20/34] sci: fully qualify symbols in syntax-quote --- sci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sci b/sci index 968734ac..8379f901 160000 --- a/sci +++ b/sci @@ -1 +1 @@ -Subproject commit 968734ac60a2410f0b716df3829a5287f263e8f0 +Subproject commit 8379f90120e53ce693fbc8151f4c48b03ffe46f0 From 33b7b85ab04174e8cdd9cf5668ece6e633c8783a Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 13 Dec 2019 13:53:17 +0100 Subject: [PATCH 21/34] sci: #184 --- sci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sci b/sci index 8379f901..9dd1cfa2 160000 --- a/sci +++ b/sci @@ -1 +1 @@ -Subproject commit 8379f90120e53ce693fbc8151f4c48b03ffe46f0 +Subproject commit 9dd1cfa2536f5941e0de6351321cb2d17f8e40c6 From fa8eb1af2ba5e07acbd6e5e5a1038091c8ee8413 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 13 Dec 2019 18:07:23 +0100 Subject: [PATCH 22/34] Remove mention of bbk script for now --- README.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/README.md b/README.md index da953657..9e303de4 100644 --- a/README.md +++ b/README.md @@ -367,17 +367,6 @@ $ bb --classpath "$CLASSPATH" --main my-gist-script Hello from gist script! ``` -The `bbk` shell script is a thin wrapper around the `clojure` tool, so you can -use Babashka projects in a similar way: - -``` shellsession -$ bbk -m my-gist-script -Hello from gist script! -``` - -The script will call `bb` with the `--classpath` argument as a result of calling -`clojure`. - If there is no `--classpath` argument, the `BABASHKA_CLASSPATH` environment variable will be used if set: From 87c0cfba349e77db2b373521dbe54a3070848b09 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 13 Dec 2019 18:13:35 +0100 Subject: [PATCH 23/34] don't release bbk yet --- .circleci/script/release | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/script/release b/.circleci/script/release index 87d81d3a..2d32f514 100755 --- a/.circleci/script/release +++ b/.circleci/script/release @@ -3,7 +3,7 @@ rm -rf /tmp/release mkdir -p /tmp/release cp bb /tmp/release -cp src-bash/bbk /tmp/release +# cp src-bash/bbk /tmp/release VERSION=$(cat resources/BABASHKA_VERSION) @@ -11,8 +11,8 @@ cd /tmp/release ## release binary as zip archive -zip "babashka-$VERSION-$BABASHKA_PLATFORM-amd64.zip" bb bbk +zip "babashka-$VERSION-$BABASHKA_PLATFORM-amd64.zip" bb # bbk ## cleanup -rm bb bbk +rm bb # bbk From 4f4e32520a76b33a8c3adb387af9acbbe176f1cb Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 13 Dec 2019 18:16:13 +0100 Subject: [PATCH 24/34] usage --- README.md | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 9e303de4..d75ffa00 100644 --- a/README.md +++ b/README.md @@ -127,24 +127,28 @@ You may also download a binary from [Github](https://github.com/borkdude/babashk ## Usage ``` shellsession -Usage: bb [ -i | -I ] [ -o | -O ] [--verbose] [ --stream ] ( -e | -f | --repl | --socket-repl [:] ) +Usage: bb [ -i | -I ] [ -o | -O ] [ --stream ] [--verbose] + [ ( --classpath | -cp ) ] [ ( --main | -m ) ] + ( -e | -f | --repl | --socket-repl [:] ) + [ arg* ] Options: - --help, -h or -?: print this help text. - --version: print the current version of babashka. - - -i: bind *in* to a lazy seq of lines from stdin. - -I: bind *in* to a lazy seq of EDN values from stdin. - -o: write lines to stdout. - -O: write EDN values to stdout. - --verbose: print entire stacktrace in case of exception. - --stream: stream over lines or EDN values from stdin. Combined with -i or -I *in* becomes a single value per iteration. - -e, --eval : evaluate an expression - -f, --file : evaluate a file - --repl: start REPL - --socket-repl: start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666). - --time: print execution time before exiting. + --help, -h or -? Print this help text. + --version Print the current version of babashka. + -i Bind *in* to a lazy seq of lines from stdin. + -I Bind *in* to a lazy seq of EDN values from stdin. + -o Write lines to stdout. + -O Write EDN values to stdout. + --verbose Print entire stacktrace in case of exception. + --stream Stream over lines or EDN values from stdin. Combined with -i or -I *in* becomes a single value per iteration. + -e, --eval Evaluate an expression. + -f, --file Evaluate a file. + -cp, --classpath Classpath to use. + -m, --main Call the -main function from namespace with args. + --repl Start REPL + --socket-repl Start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666). + --time Print execution time before exiting. If neither -e, -f, or --socket-repl are specified, then the first argument that is not parsed as a option is treated as a file if it exists, or as an expression otherwise. Everything after that is bound to *command-line-args*. From 3ee867b2df569312ed9fcd906dd0526556667dc4 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 13 Dec 2019 18:31:28 +0100 Subject: [PATCH 25/34] v0.0.41 --- resources/BABASHKA_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/BABASHKA_VERSION b/resources/BABASHKA_VERSION index d806e51f..89a67e8c 100644 --- a/resources/BABASHKA_VERSION +++ b/resources/BABASHKA_VERSION @@ -1 +1 @@ -0.0.41-SNAPSHOT +0.0.41 From 69f02e29aaed5fa66e29a6f71f4811503a84e6a3 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 13 Dec 2019 18:39:31 +0100 Subject: [PATCH 26/34] version bump --- resources/BABASHKA_RELEASED_VERSION | 2 +- resources/BABASHKA_VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/BABASHKA_RELEASED_VERSION b/resources/BABASHKA_RELEASED_VERSION index 4fe2fe84..89a67e8c 100644 --- a/resources/BABASHKA_RELEASED_VERSION +++ b/resources/BABASHKA_RELEASED_VERSION @@ -1 +1 @@ -0.0.40 +0.0.41 diff --git a/resources/BABASHKA_VERSION b/resources/BABASHKA_VERSION index 89a67e8c..d996dab7 100644 --- a/resources/BABASHKA_VERSION +++ b/resources/BABASHKA_VERSION @@ -1 +1 @@ -0.0.41 +0.0.42-SNAPSHOT From bfea861cbaa8c3b0fe1a2265c2a72e8ac0518592 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 13 Dec 2019 18:42:38 +0100 Subject: [PATCH 27/34] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d75ffa00..cc584fd9 100644 --- a/README.md +++ b/README.md @@ -372,7 +372,7 @@ Hello from gist script! ``` If there is no `--classpath` argument, the `BABASHKA_CLASSPATH` environment -variable will be used if set: +variable will be used: ``` shellsession $ export BABASHKA_CLASSPATH=$(clojure -Spath) From 33c0a76a992262a41dac4403d87c8e85988f9ea9 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 13 Dec 2019 20:59:09 +0100 Subject: [PATCH 28/34] Add outdated example --- README.md | 16 +++++++++++++++- examples/outdated.clj | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100755 examples/outdated.clj diff --git a/README.md b/README.md index cc584fd9..873569dc 100644 --- a/README.md +++ b/README.md @@ -630,7 +630,7 @@ less ### Portable tree command -See [examples/tree.clj](https://github.com/borkdude/babashka/blob/8afb87142e0e4da8b6f912cfd7daf9c30b805ab3/examples/tree.clj). +See [examples/tree.clj](https://github.com/borkdude/babashka/blob/master/examples/tree.clj). ``` shellsession $ clojure -Sdeps '{:deps {org.clojure/tools.cli {:mvn/version "0.4.2"}}}' examples/tree.clj src @@ -650,6 +650,20 @@ src ... ``` +### List outdated maven dependencies + +See [examples/outdated.clj](https://github.com/borkdude/babashka/blob/master/examples/outdated.clj). + +``` shellsession +$ cat /tmp/deps.edn +{:deps {cheshire {:mvn/version "5.8.1"} + clj-http {:mvn/version "3.4.0"}}} + +$ examples/outdated.clj /tmp/deps.edn +clj-http/clj-http can be upgraded from 3.4.0 to 3.10.0 +cheshire/cheshire can be upgraded from 5.8.1 to 5.9.0 +``` + ## Thanks - [adgoji](https://www.adgoji.com/) for financial support diff --git a/examples/outdated.clj b/examples/outdated.clj new file mode 100755 index 00000000..1dfa7eef --- /dev/null +++ b/examples/outdated.clj @@ -0,0 +1,34 @@ +#!/usr/bin/env bb + +(require '[clojure.edn :as edn]) +(require '[clojure.java.shell :refer [sh]]) +(require '[clojure.string :as str]) + +(def deps (-> (slurp (or (first *command-line-args*) + "deps.edn")) + edn/read-string + :deps)) +(def with-release (zipmap (keys deps) + (map #(assoc % :mvn/version "RELEASE") + (vals deps)))) + +(defn deps->versions [deps] + (let [res (sh "clojure" "-Sdeps" (str {:deps deps}) "-Stree") + tree (:out res) + lines (str/split tree #"\n") + top-level (remove #(str/starts-with? % " ") lines) + deps (map #(str/split % #" ") top-level)] + (reduce (fn [acc [dep version]] + (assoc acc dep version)) + {} + deps))) + +(def version-map (deps->versions deps)) +(def new-version-map (deps->versions with-release)) + +(doseq [[dep version] version-map + :let [new-version (get new-version-map dep)] + :when (not= version new-version)] + (println dep "can be upgraded from" version "to" new-version)) + +;; Inspired by an idea from @seancorfield on Clojurians Slack From dce1029c2789f1225eefc5aa1eee4d71c3db3e38 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 13 Dec 2019 21:00:20 +0100 Subject: [PATCH 29/34] credits --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 873569dc..65771518 100644 --- a/README.md +++ b/README.md @@ -653,6 +653,7 @@ src ### List outdated maven dependencies See [examples/outdated.clj](https://github.com/borkdude/babashka/blob/master/examples/outdated.clj). +Inspired by an idea from [@seancorfield](https://github.com/seancorfield). ``` shellsession $ cat /tmp/deps.edn From f26458d63db05b22e0ff6df91aa0fc740ad2b1e7 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 15 Dec 2019 09:40:54 +0100 Subject: [PATCH 30/34] sci: fix bug with map literal in syntax-quote --- sci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sci b/sci index 9dd1cfa2..25b0628b 160000 --- a/sci +++ b/sci @@ -1 +1 @@ -Subproject commit 9dd1cfa2536f5941e0de6351321cb2d17f8e40c6 +Subproject commit 25b0628b546e08fe7fec0bb09168e286b3685d91 From 1974a2362d056cf2d35fe81194ab73a6e90e8fba Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 15 Dec 2019 20:55:16 +0100 Subject: [PATCH 31/34] Update sci: fix for nested syntax quote --- project.clj | 2 +- sci | 2 +- src/babashka/impl/repl.clj | 2 +- test/babashka/impl/socket_repl_test.clj | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/project.clj b/project.clj index 2c27951a..4703e378 100644 --- a/project.clj +++ b/project.clj @@ -11,7 +11,7 @@ :resource-paths ["resources" "sci/resources"] :dependencies [[org.clojure/clojure "1.10.1"] [org.clojure/tools.reader "1.3.2"] - [borkdude/edamame "0.0.9-alpha.2"] + [borkdude/edamame "0.0.10-alpha.2"] [org.clojure/core.async "0.4.500"] [org.clojure/tools.cli "0.4.2"] [org.clojure/data.csv "0.1.4"] diff --git a/sci b/sci index 25b0628b..07d28ee5 160000 --- a/sci +++ b/sci @@ -1 +1 @@ -Subproject commit 25b0628b546e08fe7fec0bb09168e286b3685d91 +Subproject commit 07d28ee572e90a629e01b10aa5b98cb33ccdc1e5 diff --git a/src/babashka/impl/repl.clj b/src/babashka/impl/repl.clj index 68b461fb..d97cec3a 100644 --- a/src/babashka/impl/repl.clj +++ b/src/babashka/impl/repl.clj @@ -23,7 +23,7 @@ (println)) :read (fn [_request-prompt request-exit] (if (r/peek-char in) ;; if this is nil, we reached EOF - (let [v (parser/parse-next in #{:bb} {:current (-> sci-ctx :env deref :current-ns)})] + (let [v (parser/parse-next sci-ctx in)] (if (or (identical? :repl/quit v) (identical? :repl/exit v) (identical? :edamame.impl.parser/eof v)) diff --git a/test/babashka/impl/socket_repl_test.clj b/test/babashka/impl/socket_repl_test.clj index df9f4f62..085561dd 100644 --- a/test/babashka/impl/socket_repl_test.clj +++ b/test/babashka/impl/socket_repl_test.clj @@ -36,7 +36,8 @@ (delay [1 2 3]) '*command-line-args* ["a" "b" "c"]} - :env (atom {})}) + :env (atom {}) + :features #{:bb}}) (future (sh "bash" "-c" "echo '[1 2 3]' | ./bb --socket-repl 0.0.0.0:1666 a b c"))) From 33ba782f223af649e99931018b07619507c3ef20 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 15 Dec 2019 20:55:52 +0100 Subject: [PATCH 32/34] v0.0.42 --- resources/BABASHKA_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/BABASHKA_VERSION b/resources/BABASHKA_VERSION index d996dab7..54a00221 100644 --- a/resources/BABASHKA_VERSION +++ b/resources/BABASHKA_VERSION @@ -1 +1 @@ -0.0.42-SNAPSHOT +0.0.42 From 973a616f9322582b9df9a368f65f09643d8373cf Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 15 Dec 2019 21:05:00 +0100 Subject: [PATCH 33/34] Version bump. --- resources/BABASHKA_RELEASED_VERSION | 2 +- resources/BABASHKA_VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/BABASHKA_RELEASED_VERSION b/resources/BABASHKA_RELEASED_VERSION index 89a67e8c..54a00221 100644 --- a/resources/BABASHKA_RELEASED_VERSION +++ b/resources/BABASHKA_RELEASED_VERSION @@ -1 +1 @@ -0.0.41 +0.0.42 diff --git a/resources/BABASHKA_VERSION b/resources/BABASHKA_VERSION index 54a00221..382bf853 100644 --- a/resources/BABASHKA_VERSION +++ b/resources/BABASHKA_VERSION @@ -1 +1 @@ -0.0.42 +0.0.43-SNAPSHOT From 06eca1c79652e2e81f54da2e4e538ac0e737952e Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Mon, 16 Dec 2019 13:10:38 +0100 Subject: [PATCH 34/34] Fully qualify async thread-call --- src/babashka/impl/async.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/babashka/impl/async.clj b/src/babashka/impl/async.clj index afb04b06..61022448 100644 --- a/src/babashka/impl/async.clj +++ b/src/babashka/impl/async.clj @@ -4,7 +4,7 @@ (defn thread [_ _ & body] - `(~'async/thread-call (fn [] ~@body))) + `(~'clojure.core.async/thread-call (fn [] ~@body))) (def async-namespace {'