diff --git a/project.clj b/project.clj index 0347a567..5f98126c 100644 --- a/project.clj +++ b/project.clj @@ -67,8 +67,7 @@ :feature/hiccup :feature/test-check :feature/spec-alpha - {:dependencies [[clj-commons/conch "0.9.2"] - [com.clojure-goes-fast/clj-async-profiler "0.4.1"] + {:dependencies [[com.clojure-goes-fast/clj-async-profiler "0.4.1"] [com.opentable.components/otj-pg-embedded "0.13.3"]]}] :uberjar {:global-vars {*assert* false} :jvm-opts ["-Dclojure.compiler.direct-linking=true" diff --git a/src/babashka/impl/classpath.clj b/src/babashka/impl/classpath.clj index 14a0339c..8c4cb1fe 100644 --- a/src/babashka/impl/classpath.clj +++ b/src/babashka/impl/classpath.clj @@ -28,17 +28,18 @@ resource-paths))) (defn path-from-jar - [^java.io.File jar-file resource-paths {:keys [:url?]}] - (with-open [jar (JarFile. jar-file)] - (some (fn [path] - (when-let [entry (.getEntry jar path)] - (if url? - ;; manual conversion, faster than going through .toURI - (java.net.URL. "jar" nil - (str "file:" (.getAbsolutePath jar-file) "!/" path)) - {:file path - :source (slurp (.getInputStream jar entry))}))) - resource-paths))) + [^java.io.File jar-file resource-paths opts] + (let [url? (:url? opts)] + (with-open [jar (JarFile. jar-file)] + (some (fn [path] + (when-let [entry (.getEntry jar path)] + (if url? + ;; manual conversion, faster than going through .toURI + (java.net.URL. "jar" nil + (str "file:" (.getAbsolutePath jar-file) "!/" path)) + {:file path + :source (slurp (.getInputStream jar entry))}))) + resource-paths)))) (deftype JarFileResolver [jar-file] IResourceResolver @@ -57,8 +58,10 @@ (getResources [_ resource-paths opts] (keep #(getResource % resource-paths opts) entries))) +(def path-sep (System/getProperty "path.separator")) + (defn loader [^String classpath] - (let [parts (.split classpath (System/getProperty "path.separator")) + (let [parts (.split classpath path-sep) entries (map part->entry parts)] (Loader. entries))) @@ -88,7 +91,7 @@ (fn [{:keys [:cp]}] (let [new-cp (if-not cp extra-classpath - (str cp (System/getProperty "path.separator") extra-classpath))] + (str cp path-sep extra-classpath))] {:loader (loader new-cp) :cp new-cp}))) nil) @@ -96,7 +99,7 @@ (defn split-classpath "Returns the classpath as a seq of strings, split by the platform specific path separator." - ([^String cp] (vec (.split cp (System/getProperty "path.separator"))))) + ([^String cp] (vec (.split cp path-sep)))) (defn get-classpath "Returns the current classpath as set by --classpath, BABASHKA_CLASSPATH and add-classpath." diff --git a/src/babashka/impl/clojure/core.clj b/src/babashka/impl/clojure/core.clj index 0ef0c81b..a0f3e946 100644 --- a/src/babashka/impl/clojure/core.clj +++ b/src/babashka/impl/clojure/core.clj @@ -20,6 +20,8 @@ ret#)) (def data-readers (sci/new-dynamic-var '*data-readers* nil)) +(def command-line-args (sci/new-dynamic-var '*command-line-args* nil)) +(def warn-on-reflection (sci/new-dynamic-var '*warn-on-reflection* false)) (defn read+string "Added for compatibility. Must be used with @@ -59,4 +61,6 @@ 'default-data-readers default-data-readers 'xml-seq (copy-core-var xml-seq) 'read+string (fn [& args] - (apply read+string @common/ctx args))}) + (apply read+string @common/ctx args)) + '*command-line-args* command-line-args + '*warn-on-reflection* warn-on-reflection}) diff --git a/src/babashka/impl/deps.clj b/src/babashka/impl/deps.clj index ad642f3e..08d6ff20 100644 --- a/src/babashka/impl/deps.clj +++ b/src/babashka/impl/deps.clj @@ -57,11 +57,20 @@ then used to resolve dependencies in babashka." ([deps-map] (add-deps deps-map nil)) ([deps-map {:keys [:aliases]}] - (let [args ["-Spath" "-Sdeps" (str deps-map)] - args (cond-> args - aliases (conj (str "-A:" (str/join ":" aliases)))) - cp (with-out-str (apply deps/-main args))] - (cp/add-classpath cp)))) + (when-let [paths (:paths deps-map)] + (cp/add-classpath (str/join cp/path-sep paths))) + (when-let [deps-map (not-empty (dissoc deps-map :paths :tasks))] + (let [deps-map (assoc-in deps-map [:aliases :org.babashka/defaults] + '{:replace-paths [] ;; babashka sets paths manually + :classpath-overrides {org.clojure/clojure "" + org.clojure/spec.alpha "" + org.clojure/core.specs.alpha ""}}) + args ["-Spath" "-Sdeps" (str deps-map)] + args (conj args (str "-A:" (str/join ":" (cons ":org.babashka/defaults" aliases)))) + cp (with-out-str (apply deps/-main args)) + cp (str/trim cp) + cp (str/replace cp (re-pattern (str cp/path-sep "+$")) "")] + (cp/add-classpath cp))))) (defn clojure "Starts clojure similar to CLI. Use `rlwrap bb` for `clj`-like invocation. diff --git a/src/babashka/main.clj b/src/babashka/main.clj index 6aa8dc1f..17a055d0 100644 --- a/src/babashka/main.clj +++ b/src/babashka/main.clj @@ -2,6 +2,7 @@ {:no-doc true} (:refer-clojure :exclude [error-handler]) (:require + [babashka.fs :as fs] [babashka.impl.bencode :refer [bencode-namespace]] [babashka.impl.cheshire :refer [cheshire-core-namespace]] [babashka.impl.classes :as classes] @@ -74,185 +75,100 @@ ;; echo '1' | java -agentlib:native-image-agent=config-output-dir=/tmp -jar target/babashka-xxx-standalone.jar '...' ;; with the java provided by GraalVM. -(defn parse-opts [options] - (let [opts (loop [options options - opts-map {}] - (if options - (let [opt (first options)] - (case opt - ("--") (assoc opts-map :command-line-args (next options)) - ("--clojure") (assoc opts-map :clojure true - :opts (rest options)) - ("--version") {:version true} - ("--help" "-h" "-?") {:help? true} - ("--verbose")(recur (next options) - (assoc opts-map - :verbose? true)) - ("--describe") (recur (next options) - (assoc opts-map - :describe? true)) - ("--stream") (recur (next options) - (assoc opts-map - :stream? true)) - ("-i") (recur (next options) - (assoc opts-map - :shell-in true)) - ("-I") (recur (next options) - (assoc opts-map - :edn-in true)) - ("-o") (recur (next options) - (assoc opts-map - :shell-out true)) - ("-O") (recur (next options) - (assoc opts-map - :edn-out true)) - ("-io") (recur (next options) - (assoc opts-map - :shell-in true - :shell-out true)) - ("-iO") (recur (next options) - (assoc opts-map - :shell-in true - :edn-out true)) - ("-Io") (recur (next options) - (assoc opts-map - :edn-in true - :shell-out true)) - ("-IO") (recur (next options) - (assoc opts-map - :edn-in true - :edn-out true)) - ("--classpath", "-cp") - (let [options (next options)] - (recur (next options) - (assoc opts-map :classpath (first options)))) - ("--uberscript") - (let [options (next options)] - (recur (next options) - (assoc opts-map - :uberscript (first options)))) - ("--uberjar") - (let [options (next options)] - (recur (next options) - (assoc opts-map - :uberjar (first options)))) - ("-f" "--file") - (let [options (next options)] - (recur (next options) - (assoc opts-map - :file (first options)))) - ("--jar" "-jar") - (let [options (next options)] - (recur (next options) - (assoc opts-map - :jar (first options)))) - ("--repl") - (let [options (next options)] - (recur (next options) - (assoc opts-map - :repl true))) - ("--socket-repl") - (let [options (next options) - opt (first options) - opt (when (and opt (not (str/starts-with? opt "-"))) - opt) - options (if opt (next options) - options)] - (recur options - (assoc opts-map - :socket-repl (or opt "1666")))) - ("--nrepl-server") - (let [options (next options) - opt (first options) - opt (when (and opt (not (str/starts-with? opt "-"))) - opt) - options (if opt (next options) - options)] - (recur options - (assoc opts-map - :nrepl (or opt "1667")))) - ("--eval", "-e") - (let [options (next options)] - (recur (next options) - (update opts-map :expressions (fnil conj []) (first options)))) - ("--main", "-m") - (let [options (next options)] - (recur (next options) - (assoc opts-map :main (first options)))) - (if (some opts-map [:file :jar :socket-repl :expressions :main]) - (assoc opts-map - :command-line-args options) - (let [trimmed-opt (str/triml opt) - c (.charAt trimmed-opt 0)] - (case c - (\( \{ \[ \* \@ \#) - (-> opts-map - (update :expressions (fnil conj []) (first options)) - (assoc :command-line-args (next options))) - (assoc opts-map - (if (str/ends-with? opt ".jar") - :jar :file) opt - :command-line-args (next options))))))) - opts-map))] - opts)) - (def version (str/trim (slurp (io/resource "BABASHKA_VERSION")))) (defn print-version [] (println (str "babashka v" version))) +(def bb-edn + (volatile! nil)) -(defn print-help [] +(defn command? [x] + (case x + ("clojure" + "version" + "help" + "doc" + "tasks" + "uberjar" + "uberscript" + "repl" + "socket-repl" + "nrepl-server" + "describe") true + false)) + +(defn print-error [& msgs] + (binding [*out* *err*] + (apply println msgs))) + +(defn print-help [_ctx _command-line-args] (println (str "Babashka v" version)) ;; (println (str "sci v" (str/trim (slurp (io/resource "SCI_VERSION"))))) (println) - (println "Options must appear in the order of groups mentioned below.") (println " Help: - --help, -h or -? Print this help text. - --version Print the current version of babashka. - --describe Print an EDN map with information about this version of babashka. - -Evaluation: - - -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. - --verbose Print debug information and entire stacktrace in case of exception. + help, -h or -? Print this help text. + version Print the current version of babashka. + describe Print an EDN map with information about this version of babashka. + doc Print docstring of var or namespace. Requires namespace if necessary. REPL: - --repl Start REPL. Use rlwrap for history. - --socket-repl Start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666). - --nrepl-server Start nREPL server. Specify port (e.g. 1667) or host and port separated by colon (e.g. 127.0.0.1:1667). + Usage: [port/host] [eval-opts] + Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666). -In- and output flags: - - -i Bind *input* to a lazy seq of lines from stdin. - -I Bind *input* to a lazy seq of EDN values from stdin. - -o Write lines to stdout. - -O Write EDN values to stdout. - --stream Stream over lines or EDN values from stdin. Combined with -i or -I *input* becomes a single value per iteration. - -Uberscript: - - --uberscript Collect preloads, -e, -f and -m and all required namespaces from the classpath into a single file. - -Uberjar: - - --uberjar Similar to --uberscript but creates jar file. + repl Start REPL. Use rlwrap for history. When invoking bb with no args, this is the default command. + socket-repl Start socket REPL. + nrepl-server Start nREPL server. Clojure: - --clojure [args...] Invokes clojure. Takes same args as the official clojure CLI. + clojure [args...] Invokes clojure. Takes same args as the official clojure CLI. + +Packaging: + + uberscript [eval-opts] Collect preloads, -e, -f and -m and all required namespaces from the classpath into a single file. + + uberjar [eval-opts] Similar to --uberscript but creates jar file. + +Evaluation: + + -e, --eval Evaluate an expression. + -f, --file Evaluate a file. + -cp, --classpath Classpath to use. + -m, --main Call the -main function from a namespace or call a fully qualified var. + --verbose Print debug information and entire stacktrace in case of exception. + +In- and output flags: + + -i Bind *input* to a lazy seq of lines from stdin. + -I Bind *input* to a lazy seq of EDN values from stdin. + -o Write lines to stdout. + -O Write EDN values to stdout. + --stream Stream over lines or EDN values from stdin. Combined with -i or -I *input* becomes a single value per iteration. If the first argument is not any of the above options, then it treated as a file if it exists, or as an expression otherwise. Everything after that is bound to *command-line-args*. Use -- to separate script command line args from bb command line args. -")) +") + [nil 0]) + +(defn print-doc [ctx command-line-args] + (let [arg (first command-line-args)] + (if (sci/eval-string* ctx (format " +(when (or (resolve '%1$s) + (if (simple-symbol? '%1$s) + (try (require '%1$s) true + (catch Exception e nil)) + (requiring-resolve '%1$s))) + (clojure.repl/doc %1$s) + true)" arg)) + [nil 0] + [(print-error "No docstring found for var:" arg) 1])) + ,) (defn print-describe [] (println @@ -301,8 +217,6 @@ Use -- to separate script command line args from bb command line args. (str/replace x #"^#!.*" "")) (throw (Exception. (str "File does not exist: " file)))))) -(def reflection-var (sci/new-dynamic-var '*warn-on-reflection* false)) - (defn load-file* [f] (let [f (io/file f) s (slurp f)] @@ -319,7 +233,7 @@ Use -- to separate script command line args from bb command line args. nrepl-opts (assoc nrepl-opts :debug dev? :describe {"versions" {"babashka" version}} - :thread-bind [reflection-var])] + :thread-bind [core/warn-on-reflection])] (nrepl-server/start-server! ctx nrepl-opts) (binding [*out* *err*] (println "For more info visit: https://book.babashka.org/#_nrepl"))) @@ -477,27 +391,156 @@ Use -- to separate script command line args from bb command line args. (defn shell-seq [in] (line-seq (java.io.BufferedReader. in))) -(defn main - [& args] - (handle-pipe!) - (handle-sigint!) +(defn parse-opts [options] + (let [opt (first options)] + (cond (and (command? opt) + (not (fs/regular-file? opt))) + (recur (cons (str "--" opt) (next options))) + :else + (let [opts (loop [options options + opts-map {}] + (if options + (let [opt (first options)] + (case opt + ("--") (assoc opts-map :command-line-args (next options)) + ("--clojure") (assoc opts-map :clojure true + :command-line-args (rest options)) + ("--version") {:version true} + ("--help" "-h" "-?" "help") + {:help true + :command-line-args (rest options)} + ("--doc") + {:doc true + :command-line-args (rest options)} + ("--verbose") (recur (next options) + (assoc opts-map + :verbose? true)) + ("--describe") (recur (next options) + (assoc opts-map + :describe? true)) + ("--stream") (recur (next options) + (assoc opts-map + :stream? true)) + ("-i") (recur (next options) + (assoc opts-map + :shell-in true)) + ("-I") (recur (next options) + (assoc opts-map + :edn-in true)) + ("-o") (recur (next options) + (assoc opts-map + :shell-out true)) + ("-O") (recur (next options) + (assoc opts-map + :edn-out true)) + ("-io") (recur (next options) + (assoc opts-map + :shell-in true + :shell-out true)) + ("-iO") (recur (next options) + (assoc opts-map + :shell-in true + :edn-out true)) + ("-Io") (recur (next options) + (assoc opts-map + :edn-in true + :shell-out true)) + ("-IO") (recur (next options) + (assoc opts-map + :edn-in true + :edn-out true)) + ("--classpath", "-cp") + (let [options (next options)] + (recur (next options) + (assoc opts-map :classpath (first options)))) + ("--uberscript") + (let [options (next options)] + (recur (next options) + (assoc opts-map + :uberscript (first options)))) + ("--uberjar") + (let [options (next options)] + (recur (next options) + (assoc opts-map + :uberjar (first options)))) + ("-f" "--file") + (let [options (next options)] + (recur (next options) + (assoc opts-map + :file (first options)))) + ("--jar" "-jar") + (let [options (next options)] + (recur (next options) + (assoc opts-map + :jar (first options)))) + ("--repl") + (let [options (next options)] + (recur (next options) + (assoc opts-map + :repl true))) + ("--socket-repl") + (let [options (next options) + opt (first options) + opt (when (and opt (not (str/starts-with? opt "-"))) + opt) + options (if opt (next options) + options)] + (recur options + (assoc opts-map + :socket-repl (or opt "1666")))) + ("--nrepl-server") + (let [options (next options) + opt (first options) + opt (when (and opt (not (str/starts-with? opt "-"))) + opt) + options (if opt (next options) + options)] + (recur options + (assoc opts-map + :nrepl (or opt "1667")))) + ("--eval", "-e") + (let [options (next options)] + (recur (next options) + (update opts-map :expressions (fnil conj []) (first options)))) + ("--main", "-m",) + (let [options (next options)] + (recur (next options) + (assoc opts-map :main (first options)))) + ;; fallback + (if (some opts-map [:file :jar :socket-repl :expressions :main]) + (assoc opts-map + :command-line-args options) + (let [trimmed-opt (str/triml opt) + c (.charAt trimmed-opt 0)] + (case c + (\( \{ \[ \* \@ \#) + (-> opts-map + (update :expressions (fnil conj []) (first options)) + (assoc :command-line-args (next options))) + (assoc opts-map + (if (str/ends-with? opt ".jar") + :jar :file) opt + :command-line-args (next options))))))) + opts-map))] + opts)))) + +(def env (atom {})) + +(defn exec [opts] (binding [*unrestricted* true] - (sci/binding [reflection-var false + (sci/binding [core/warn-on-reflection @core/warn-on-reflection core/data-readers @core/data-readers sci/ns @sci/ns] (let [{version-opt :version :keys [:shell-in :edn-in :shell-out :edn-out - :help? :file :command-line-args + :help :file :command-line-args :expressions :stream? :repl :socket-repl :nrepl :verbose? :classpath :main :uberscript :describe? - :jar :uberjar :clojure] :as opts} - (parse-opts args) - _ (when clojure - (if-let [proc (deps/clojure (:opts opts))] - (-> @proc :exit (System/exit)) - (System/exit 0))) + :jar :uberjar :clojure + :doc]} + opts _ (when verbose? (vreset! common/verbose? true)) _ (do ;; set properties (when main (System/setProperty "babashka.main" main)) @@ -516,7 +559,6 @@ Use -- to separate script command line args from bb command line args. :else (edn/read {:readers edn-readers} *in*)))))) uberscript-sources (atom ()) - env (atom {}) classpath (or classpath (System/getenv "BABASHKA_CLASSPATH")) _ (when classpath @@ -546,15 +588,11 @@ Use -- to separate script command line args from bb command line args. ["META-INF/MANIFEST.MF"] {:url? true})] (cp/main-ns res)) main) - ;; TODO: pull more of these values to compile time opts {:aliases aliases :namespaces (-> namespaces (assoc 'clojure.core (assoc core-extras - '*command-line-args* - (sci/new-dynamic-var '*command-line-args* command-line-args) - '*warn-on-reflection* reflection-var 'load-file load-file*)) (assoc-in ['clojure.java.io 'resource] (fn [path] @@ -579,8 +617,15 @@ Use -- to separate script command line args from bb command line args. preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim)) [expressions exit-code] (cond expressions [expressions nil] - main [[(format "(ns user (:require [%1$s])) (apply %1$s/-main *command-line-args*)" - main)] nil] + main + (let [sym (symbol main) + ns? (namespace sym) + ns (or ns? sym) + var-name (if ns? + (name sym) + "-main")] + [[(format "(ns user (:require [%1$s])) (apply %1$s/%2$s *command-line-args*)" + ns var-name)] nil]) file (try [[(read-file file)] nil] (catch Exception e (error-handler e {:expression expressions @@ -610,8 +655,8 @@ Use -- to separate script command line args from bb command line args. (second (cond version-opt [(print-version) 0] - help? - [(print-help) 0] + help (print-help sci-ctx command-line-args) + doc (print-doc sci-ctx command-line-args) describe? [(print-describe) 0] repl [(repl/start-repl! sci-ctx) 0] @@ -626,7 +671,8 @@ Use -- to separate script command line args from bb command line args. [nil 0] ;; done streaming (let [res [(let [res (sci/binding [sci/file (or @sci/file "") - input-var in] + input-var in + core/command-line-args command-line-args] (sci/eval-string* sci-ctx expression))] (when (some? res) (if-let [pr-f (cond shell-out println @@ -645,6 +691,9 @@ Use -- to separate script command line args from bb command line args. :verbose? verbose? :preloads preloads :loader (:loader @cp/cp-state)})))) + clojure [nil (if-let [proc (deps/clojure command-line-args)] + (-> @proc :exit) + 0)] uberscript [nil 0] :else [(repl/start-repl! sci-ctx) 0])) 1)] @@ -664,8 +713,21 @@ Use -- to separate script command line args from bb command line args. :verbose verbose?})) exit-code)))) +(defn main [& args] + (let [bb-edn-file (or (System/getenv "BABASHKA_EDN") + "bb.edn")] + (when (fs/exists? bb-edn-file) + (let [edn (edn/read-string (slurp bb-edn-file))] + (vreset! bb-edn edn))) + ;; we mutate the atom from tests as well, so despite the above it can contain a bb.edn + (when-let [bb-edn @bb-edn] (deps/add-deps bb-edn))) + (let [opts (parse-opts args)] + (exec opts))) + (defn -main [& args] + (handle-pipe!) + (handle-sigint!) (if-let [dev-opts (System/getenv "BABASHKA_DEV")] (let [{:keys [:n]} (if (= "true" dev-opts) {:n 1} (edn/read-string dev-opts)) diff --git a/test-resources/bb-edn/user.clj b/test-resources/bb-edn/user.clj new file mode 100644 index 00000000..c8726eb6 --- /dev/null +++ b/test-resources/bb-edn/user.clj @@ -0,0 +1,10 @@ +(ns user + (:require [babashka.process :as p] + [clojure.string :as str])) + +(defn bash [& args] + ;; (prn :cmd *command-line-args*) + (-> (p/process ["bash" "-c" (str/join " " args)] + {:inherit true}) + p/check) + nil) diff --git a/test-resources/task_scripts/tasks.clj b/test-resources/task_scripts/tasks.clj new file mode 100644 index 00000000..2fadb15d --- /dev/null +++ b/test-resources/task_scripts/tasks.clj @@ -0,0 +1,11 @@ +(ns tasks + "This is task ns docstring.") + +(defn -main + "Main docstring" + [& args] + args) + +(defn foo + "Foo docstring" + []) diff --git a/test/babashka/bb_edn_test.clj b/test/babashka/bb_edn_test.clj new file mode 100644 index 00000000..f99d54b2 --- /dev/null +++ b/test/babashka/bb_edn_test.clj @@ -0,0 +1,38 @@ +(ns babashka.bb-edn-test + (:require + [babashka.fs :as fs] + [babashka.test-utils :as test-utils] + [clojure.edn :as edn] + [clojure.string :as str] + [clojure.test :as test :refer [deftest is]])) + +(defn bb [& args] + (edn/read-string + {:readers *data-readers* + :eof nil} + (apply test-utils/bb nil (map str args)))) + +(defmacro with-config [cfg & body] + `(let [temp-dir# (fs/create-temp-dir) + bb-edn-file# (fs/file temp-dir# "bb.edn")] + (binding [*print-meta* true] + (spit bb-edn-file# ~cfg)) + (binding [test-utils/*bb-edn-path* (str bb-edn-file#)] + ~@body))) + +(deftest doc-test + (with-config {:paths ["test-resources/task_scripts"]} + (is (str/includes? (apply test-utils/bb nil + (map str ["doc" "tasks"])) + "This is task ns docstring.")) + (is (str/includes? (apply test-utils/bb nil + (map str ["doc" "tasks/foo"])) + "Foo docstring")) + (is (str/includes? (apply test-utils/bb nil + (map str ["doc" "tasks/-main"])) + "Main docstring")))) + +(deftest deps-test + (with-config '{:deps {medley/medley {:mvn/version "1.3.0"}}} + (is (= '{1 {:id 1}, 2 {:id 2}} + (bb "-e" "(require 'medley.core)" "-e" "(medley.core/index-by :id [{:id 1} {:id 2}])"))))) diff --git a/test/babashka/impl/nrepl_server_test.clj b/test/babashka/impl/nrepl_server_test.clj index 9f18d0ca..08dddee0 100644 --- a/test/babashka/impl/nrepl_server_test.clj +++ b/test/babashka/impl/nrepl_server_test.clj @@ -195,7 +195,7 @@ :features #{:bb}}) nrepl-opts)] (reset! server-state server)) - (let [pb (ProcessBuilder. ["./bb" "--nrepl-server" "0.0.0.0:1668"]) + (let [pb (ProcessBuilder. ["./bb" "nrepl-server" "0.0.0.0:1668"]) _ (.redirectError pb ProcessBuilder$Redirect/INHERIT) ;; _ (.redirectOutput pb ProcessBuilder$Redirect/INHERIT) ;; env (.environment pb) diff --git a/test/babashka/impl/socket_repl_test.clj b/test/babashka/impl/socket_repl_test.clj index 7df4e857..8c99f64a 100644 --- a/test/babashka/impl/socket_repl_test.clj +++ b/test/babashka/impl/socket_repl_test.clj @@ -51,7 +51,7 @@ (vreset! common/ctx ctx) (start-repl! "0.0.0.0:1666" ctx)) (do (vreset! server-process - (p/process ["./bb" "--socket-repl" "localhost:1666"])) + (p/process ["./bb" "socket-repl" "localhost:1666"])) (w/wait-for-port "localhost" 1666))) (Thread/sleep 50) (is (socket-command "(+ 1 2 3)" "user=> 6")) diff --git a/test/babashka/main_test.clj b/test/babashka/main_test.clj index d48dea65..cc6931a5 100644 --- a/test/babashka/main_test.clj +++ b/test/babashka/main_test.clj @@ -7,21 +7,10 @@ [clojure.java.io :as io] [clojure.java.shell :refer [sh]] [clojure.string :as str] - [clojure.test :as test :refer [deftest is testing *report-counters*]] + [clojure.test :as test :refer [deftest is testing]] [flatland.ordered.map :refer [ordered-map]] [sci.core :as sci])) -(defmethod clojure.test/report :begin-test-var [m] - (println "===" (-> m :var meta :name)) - (println)) - -(defmethod clojure.test/report :end-test-var [_m] - (let [{:keys [:fail :error]} @*report-counters*] - (when (and (= "true" (System/getenv "BABASHKA_FAIL_FAST")) - (or (pos? fail) (pos? error))) - (println "=== Failing fast") - (System/exit 1)))) - (defn bb [input & args] (edn/read-string {:readers *data-readers* diff --git a/test/babashka/test_utils.clj b/test/babashka/test_utils.clj index 547d8a3b..5c4fe66a 100644 --- a/test/babashka/test_utils.clj +++ b/test/babashka/test_utils.clj @@ -2,14 +2,33 @@ (:require [babashka.impl.classpath :as cp] [babashka.main :as main] - [me.raynes.conch :refer [let-programs] :as sh] + [babashka.process :as p] + [clojure.edn :as edn] + [clojure.test :as test :refer [*report-counters*]] [sci.core :as sci] [sci.impl.vars :as vars])) (set! *warn-on-reflection* true) +(def ^:dynamic *bb-edn-path* nil) + +(defmethod clojure.test/report :begin-test-var [m] + (println "===" (-> m :var meta :name)) + (println)) + +(defmethod clojure.test/report :end-test-var [_m] + (let [{:keys [:fail :error]} @*report-counters*] + (when (and (= "true" (System/getenv "BABASHKA_FAIL_FAST")) + (or (pos? fail) (pos? error))) + (println "=== Failing fast") + (System/exit 1)))) + (defn bb-jvm [input-or-opts & args] (reset! cp/cp-state nil) + (reset! main/env {}) + (if-let [path *bb-edn-path*] + (vreset! main/bb-edn (edn/read-string (slurp path))) + (vreset! main/bb-edn nil)) (let [os (java.io.StringWriter.) es (if-let [err (:err input-or-opts)] err (java.io.StringWriter.)) @@ -30,26 +49,33 @@ (if (string? input-or-opts) (with-in-str input-or-opts (apply main/main args)) (apply main/main args)))] + ;; (prn :err (str es)) (if (zero? res) (str os) - (throw (ex-info (str es) - {:stdout (str os) - :stderr (str es)}))))) + (do + (println (str os)) + (throw (ex-info (str es) + {:stdout (str os) + :stderr (str es)})))))) (finally (when (string? input-or-opts) (vars/bindRoot sci/in *in*)) (vars/bindRoot sci/out *out*) (vars/bindRoot sci/err *err*))))) (defn bb-native [input & args] - (let-programs [bb "./bb"] - (try (if input - (apply bb (conj (vec args) - {:in input})) - (apply bb args)) - (catch Exception e - (let [d (ex-data e) - err-msg (or (:stderr (ex-data e)) "")] - (throw (ex-info err-msg d))))))) + (let [res (p/process (into ["./bb"] args) + (cond-> {:in input + :out :string + :err :string} + *bb-edn-path* + (assoc + :env (assoc (into {} (System/getenv)) + "BABASHKA_EDN" *bb-edn-path*)))) + res (deref res) + exit (:exit res) + error? (pos? exit)] + (if error? (throw (ex-info (or (:err res) "") {})) + (:out res)))) (def bb (case (System/getenv "BABASHKA_TEST_ENV") diff --git a/test/babashka/uberjar_test.clj b/test/babashka/uberjar_test.clj index cbdd070f..5bb89dac 100644 --- a/test/babashka/uberjar_test.clj +++ b/test/babashka/uberjar_test.clj @@ -1,7 +1,6 @@ (ns babashka.uberjar-test (:require [babashka.test-utils :as tu] - [clojure.edn :as edn] [clojure.string :as str] [clojure.test :as t :refer [deftest is testing]])) @@ -10,7 +9,7 @@ path (.getPath tmp-file)] (.deleteOnExit tmp-file) (testing "uberjar" - (tu/bb nil "--classpath" "test-resources/babashka/uberjar/src" "-m" "my.main-main" "--uberjar" path) + (tu/bb nil "uberjar" path "--classpath" "test-resources/babashka/uberjar/src" "-m" "my.main-main") (is (= "(\"1\" \"2\" \"3\" \"4\")\n" (tu/bb nil "--jar" path "1" "2" "3" "4"))) (is (= "(\"1\" \"2\" \"3\" \"4\")\n" @@ -25,5 +24,5 @@ (let [tmp-file (java.io.File/createTempFile "uber" ".jar") path (.getPath tmp-file)] (.deleteOnExit tmp-file) - (tu/bb nil "--classpath" "test-resources/babashka/uberjar/src" "--uberjar" path) + (tu/bb nil "uberjar" path "--classpath" "test-resources/babashka/uberjar/src") (is (str/includes? (tu/bb "(+ 1 2 3)" path) "6")))))