bb.edn: first iteration

This commit is contained in:
Michiel Borkent 2021-03-28 17:30:44 +02:00 committed by GitHub
parent 2bc935d3d8
commit d71c3622ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 382 additions and 232 deletions

View file

@ -67,8 +67,7 @@
:feature/hiccup :feature/hiccup
:feature/test-check :feature/test-check
:feature/spec-alpha :feature/spec-alpha
{:dependencies [[clj-commons/conch "0.9.2"] {:dependencies [[com.clojure-goes-fast/clj-async-profiler "0.4.1"]
[com.clojure-goes-fast/clj-async-profiler "0.4.1"]
[com.opentable.components/otj-pg-embedded "0.13.3"]]}] [com.opentable.components/otj-pg-embedded "0.13.3"]]}]
:uberjar {:global-vars {*assert* false} :uberjar {:global-vars {*assert* false}
:jvm-opts ["-Dclojure.compiler.direct-linking=true" :jvm-opts ["-Dclojure.compiler.direct-linking=true"

View file

@ -28,17 +28,18 @@
resource-paths))) resource-paths)))
(defn path-from-jar (defn path-from-jar
[^java.io.File jar-file resource-paths {:keys [:url?]}] [^java.io.File jar-file resource-paths opts]
(with-open [jar (JarFile. jar-file)] (let [url? (:url? opts)]
(some (fn [path] (with-open [jar (JarFile. jar-file)]
(when-let [entry (.getEntry jar path)] (some (fn [path]
(if url? (when-let [entry (.getEntry jar path)]
;; manual conversion, faster than going through .toURI (if url?
(java.net.URL. "jar" nil ;; manual conversion, faster than going through .toURI
(str "file:" (.getAbsolutePath jar-file) "!/" path)) (java.net.URL. "jar" nil
{:file path (str "file:" (.getAbsolutePath jar-file) "!/" path))
:source (slurp (.getInputStream jar entry))}))) {:file path
resource-paths))) :source (slurp (.getInputStream jar entry))})))
resource-paths))))
(deftype JarFileResolver [jar-file] (deftype JarFileResolver [jar-file]
IResourceResolver IResourceResolver
@ -57,8 +58,10 @@
(getResources [_ resource-paths opts] (getResources [_ resource-paths opts]
(keep #(getResource % resource-paths opts) entries))) (keep #(getResource % resource-paths opts) entries)))
(def path-sep (System/getProperty "path.separator"))
(defn loader [^String classpath] (defn loader [^String classpath]
(let [parts (.split classpath (System/getProperty "path.separator")) (let [parts (.split classpath path-sep)
entries (map part->entry parts)] entries (map part->entry parts)]
(Loader. entries))) (Loader. entries)))
@ -88,7 +91,7 @@
(fn [{:keys [:cp]}] (fn [{:keys [:cp]}]
(let [new-cp (let [new-cp
(if-not cp extra-classpath (if-not cp extra-classpath
(str cp (System/getProperty "path.separator") extra-classpath))] (str cp path-sep extra-classpath))]
{:loader (loader new-cp) {:loader (loader new-cp)
:cp new-cp}))) :cp new-cp})))
nil) nil)
@ -96,7 +99,7 @@
(defn split-classpath (defn split-classpath
"Returns the classpath as a seq of strings, split by the platform "Returns the classpath as a seq of strings, split by the platform
specific path separator." specific path separator."
([^String cp] (vec (.split cp (System/getProperty "path.separator"))))) ([^String cp] (vec (.split cp path-sep))))
(defn get-classpath (defn get-classpath
"Returns the current classpath as set by --classpath, BABASHKA_CLASSPATH and add-classpath." "Returns the current classpath as set by --classpath, BABASHKA_CLASSPATH and add-classpath."

View file

@ -20,6 +20,8 @@
ret#)) ret#))
(def data-readers (sci/new-dynamic-var '*data-readers* nil)) (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 (defn read+string
"Added for compatibility. Must be used with "Added for compatibility. Must be used with
@ -59,4 +61,6 @@
'default-data-readers default-data-readers 'default-data-readers default-data-readers
'xml-seq (copy-core-var xml-seq) 'xml-seq (copy-core-var xml-seq)
'read+string (fn [& args] '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})

View file

@ -57,11 +57,20 @@
then used to resolve dependencies in babashka." then used to resolve dependencies in babashka."
([deps-map] (add-deps deps-map nil)) ([deps-map] (add-deps deps-map nil))
([deps-map {:keys [:aliases]}] ([deps-map {:keys [:aliases]}]
(let [args ["-Spath" "-Sdeps" (str deps-map)] (when-let [paths (:paths deps-map)]
args (cond-> args (cp/add-classpath (str/join cp/path-sep paths)))
aliases (conj (str "-A:" (str/join ":" aliases)))) (when-let [deps-map (not-empty (dissoc deps-map :paths :tasks))]
cp (with-out-str (apply deps/-main args))] (let [deps-map (assoc-in deps-map [:aliases :org.babashka/defaults]
(cp/add-classpath cp)))) '{: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 (defn clojure
"Starts clojure similar to CLI. Use `rlwrap bb` for `clj`-like invocation. "Starts clojure similar to CLI. Use `rlwrap bb` for `clj`-like invocation.

View file

@ -2,6 +2,7 @@
{:no-doc true} {:no-doc true}
(:refer-clojure :exclude [error-handler]) (:refer-clojure :exclude [error-handler])
(:require (:require
[babashka.fs :as fs]
[babashka.impl.bencode :refer [bencode-namespace]] [babashka.impl.bencode :refer [bencode-namespace]]
[babashka.impl.cheshire :refer [cheshire-core-namespace]] [babashka.impl.cheshire :refer [cheshire-core-namespace]]
[babashka.impl.classes :as classes] [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 '...' ;; echo '1' | java -agentlib:native-image-agent=config-output-dir=/tmp -jar target/babashka-xxx-standalone.jar '...'
;; with the java provided by GraalVM. ;; 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")))) (def version (str/trim (slurp (io/resource "BABASHKA_VERSION"))))
(defn print-version [] (defn print-version []
(println (str "babashka v" 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 "Babashka v" version))
;; (println (str "sci v" (str/trim (slurp (io/resource "SCI_VERSION"))))) ;; (println (str "sci v" (str/trim (slurp (io/resource "SCI_VERSION")))))
(println) (println)
(println "Options must appear in the order of groups mentioned below.")
(println " (println "
Help: Help:
--help, -h or -? Print this help text. help, -h or -? Print this help text.
--version Print the current version of babashka. version Print the current version of babashka.
--describe Print an EDN map with information about this version of babashka. describe Print an EDN map with information about this version of babashka.
doc <var|ns> Print docstring of var or namespace. Requires namespace if necessary.
Evaluation:
-e, --eval <expr> Evaluate an expression.
-f, --file <path> Evaluate a file.
-cp, --classpath Classpath to use.
-m, --main <ns> Call the -main function from namespace with args.
--verbose Print debug information and entire stacktrace in case of exception.
REPL: REPL:
--repl Start REPL. Use rlwrap for history. Usage: <repl-command> [port/host] [eval-opts]
--socket-repl Start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666). 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).
In- and output flags: repl Start REPL. Use rlwrap for history. When invoking bb with no args, this is the default command.
socket-repl Start socket REPL.
-i Bind *input* to a lazy seq of lines from stdin. nrepl-server Start nREPL server.
-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 <file> Collect preloads, -e, -f and -m and all required namespaces from the classpath into a single file.
Uberjar:
--uberjar <jar> Similar to --uberscript but creates jar file.
Clojure: 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 <file> [eval-opts] Collect preloads, -e, -f and -m and all required namespaces from the classpath into a single file.
uberjar <jar> [eval-opts] Similar to --uberscript but creates jar file.
Evaluation:
-e, --eval <expr> Evaluate an expression.
-f, --file <path> Evaluate a file.
-cp, --classpath Classpath to use.
-m, --main <ns|var> 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. 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*. Everything after that is bound to *command-line-args*.
Use -- to separate script command line args from bb 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 [] (defn print-describe []
(println (println
@ -301,8 +217,6 @@ Use -- to separate script command line args from bb command line args.
(str/replace x #"^#!.*" "")) (str/replace x #"^#!.*" ""))
(throw (Exception. (str "File does not exist: " file)))))) (throw (Exception. (str "File does not exist: " file))))))
(def reflection-var (sci/new-dynamic-var '*warn-on-reflection* false))
(defn load-file* [f] (defn load-file* [f]
(let [f (io/file f) (let [f (io/file f)
s (slurp 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 nrepl-opts (assoc nrepl-opts
:debug dev? :debug dev?
:describe {"versions" {"babashka" version}} :describe {"versions" {"babashka" version}}
:thread-bind [reflection-var])] :thread-bind [core/warn-on-reflection])]
(nrepl-server/start-server! ctx nrepl-opts) (nrepl-server/start-server! ctx nrepl-opts)
(binding [*out* *err*] (binding [*out* *err*]
(println "For more info visit: https://book.babashka.org/#_nrepl"))) (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] (defn shell-seq [in]
(line-seq (java.io.BufferedReader. in))) (line-seq (java.io.BufferedReader. in)))
(defn main (defn parse-opts [options]
[& args] (let [opt (first options)]
(handle-pipe!) (cond (and (command? opt)
(handle-sigint!) (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] (binding [*unrestricted* true]
(sci/binding [reflection-var false (sci/binding [core/warn-on-reflection @core/warn-on-reflection
core/data-readers @core/data-readers core/data-readers @core/data-readers
sci/ns @sci/ns] sci/ns @sci/ns]
(let [{version-opt :version (let [{version-opt :version
:keys [:shell-in :edn-in :shell-out :edn-out :keys [:shell-in :edn-in :shell-out :edn-out
:help? :file :command-line-args :help :file :command-line-args
:expressions :stream? :expressions :stream?
:repl :socket-repl :nrepl :repl :socket-repl :nrepl
:verbose? :classpath :verbose? :classpath
:main :uberscript :describe? :main :uberscript :describe?
:jar :uberjar :clojure] :as opts} :jar :uberjar :clojure
(parse-opts args) :doc]}
_ (when clojure opts
(if-let [proc (deps/clojure (:opts opts))]
(-> @proc :exit (System/exit))
(System/exit 0)))
_ (when verbose? (vreset! common/verbose? true)) _ (when verbose? (vreset! common/verbose? true))
_ (do ;; set properties _ (do ;; set properties
(when main (System/setProperty "babashka.main" main)) (when main (System/setProperty "babashka.main" main))
@ -516,7 +559,6 @@ Use -- to separate script command line args from bb command line args.
:else :else
(edn/read {:readers edn-readers} *in*)))))) (edn/read {:readers edn-readers} *in*))))))
uberscript-sources (atom ()) uberscript-sources (atom ())
env (atom {})
classpath (or classpath classpath (or classpath
(System/getenv "BABASHKA_CLASSPATH")) (System/getenv "BABASHKA_CLASSPATH"))
_ (when 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})] ["META-INF/MANIFEST.MF"] {:url? true})]
(cp/main-ns res)) (cp/main-ns res))
main) main)
;; TODO: pull more of these values to compile time ;; TODO: pull more of these values to compile time
opts {:aliases aliases opts {:aliases aliases
:namespaces (-> namespaces :namespaces (-> namespaces
(assoc 'clojure.core (assoc 'clojure.core
(assoc core-extras (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*)) 'load-file load-file*))
(assoc-in ['clojure.java.io 'resource] (assoc-in ['clojure.java.io 'resource]
(fn [path] (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)) preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim))
[expressions exit-code] [expressions exit-code]
(cond expressions [expressions nil] (cond expressions [expressions nil]
main [[(format "(ns user (:require [%1$s])) (apply %1$s/-main *command-line-args*)" main
main)] nil] (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] file (try [[(read-file file)] nil]
(catch Exception e (catch Exception e
(error-handler e {:expression expressions (error-handler e {:expression expressions
@ -610,8 +655,8 @@ Use -- to separate script command line args from bb command line args.
(second (second
(cond version-opt (cond version-opt
[(print-version) 0] [(print-version) 0]
help? help (print-help sci-ctx command-line-args)
[(print-help) 0] doc (print-doc sci-ctx command-line-args)
describe? describe?
[(print-describe) 0] [(print-describe) 0]
repl [(repl/start-repl! sci-ctx) 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 [nil 0] ;; done streaming
(let [res [(let [res (let [res [(let [res
(sci/binding [sci/file (or @sci/file "<expr>") (sci/binding [sci/file (or @sci/file "<expr>")
input-var in] input-var in
core/command-line-args command-line-args]
(sci/eval-string* sci-ctx expression))] (sci/eval-string* sci-ctx expression))]
(when (some? res) (when (some? res)
(if-let [pr-f (cond shell-out println (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? :verbose? verbose?
:preloads preloads :preloads preloads
:loader (:loader @cp/cp-state)})))) :loader (:loader @cp/cp-state)}))))
clojure [nil (if-let [proc (deps/clojure command-line-args)]
(-> @proc :exit)
0)]
uberscript [nil 0] uberscript [nil 0]
:else [(repl/start-repl! sci-ctx) 0])) :else [(repl/start-repl! sci-ctx) 0]))
1)] 1)]
@ -664,8 +713,21 @@ Use -- to separate script command line args from bb command line args.
:verbose verbose?})) :verbose verbose?}))
exit-code)))) 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 (defn -main
[& args] [& args]
(handle-pipe!)
(handle-sigint!)
(if-let [dev-opts (System/getenv "BABASHKA_DEV")] (if-let [dev-opts (System/getenv "BABASHKA_DEV")]
(let [{:keys [:n]} (if (= "true" dev-opts) {:n 1} (let [{:keys [:n]} (if (= "true" dev-opts) {:n 1}
(edn/read-string dev-opts)) (edn/read-string dev-opts))

View file

@ -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)

View file

@ -0,0 +1,11 @@
(ns tasks
"This is task ns docstring.")
(defn -main
"Main docstring"
[& args]
args)
(defn foo
"Foo docstring"
[])

View file

@ -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}])")))))

View file

@ -195,7 +195,7 @@
:features #{:bb}}) :features #{:bb}})
nrepl-opts)] nrepl-opts)]
(reset! server-state server)) (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) _ (.redirectError pb ProcessBuilder$Redirect/INHERIT)
;; _ (.redirectOutput pb ProcessBuilder$Redirect/INHERIT) ;; _ (.redirectOutput pb ProcessBuilder$Redirect/INHERIT)
;; env (.environment pb) ;; env (.environment pb)

View file

@ -51,7 +51,7 @@
(vreset! common/ctx ctx) (vreset! common/ctx ctx)
(start-repl! "0.0.0.0:1666" ctx)) (start-repl! "0.0.0.0:1666" ctx))
(do (vreset! server-process (do (vreset! server-process
(p/process ["./bb" "--socket-repl" "localhost:1666"])) (p/process ["./bb" "socket-repl" "localhost:1666"]))
(w/wait-for-port "localhost" 1666))) (w/wait-for-port "localhost" 1666)))
(Thread/sleep 50) (Thread/sleep 50)
(is (socket-command "(+ 1 2 3)" "user=> 6")) (is (socket-command "(+ 1 2 3)" "user=> 6"))

View file

@ -7,21 +7,10 @@
[clojure.java.io :as io] [clojure.java.io :as io]
[clojure.java.shell :refer [sh]] [clojure.java.shell :refer [sh]]
[clojure.string :as str] [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]] [flatland.ordered.map :refer [ordered-map]]
[sci.core :as sci])) [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] (defn bb [input & args]
(edn/read-string (edn/read-string
{:readers *data-readers* {:readers *data-readers*

View file

@ -2,14 +2,33 @@
(:require (:require
[babashka.impl.classpath :as cp] [babashka.impl.classpath :as cp]
[babashka.main :as main] [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.core :as sci]
[sci.impl.vars :as vars])) [sci.impl.vars :as vars]))
(set! *warn-on-reflection* true) (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] (defn bb-jvm [input-or-opts & args]
(reset! cp/cp-state nil) (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.) (let [os (java.io.StringWriter.)
es (if-let [err (:err input-or-opts)] es (if-let [err (:err input-or-opts)]
err (java.io.StringWriter.)) err (java.io.StringWriter.))
@ -30,26 +49,33 @@
(if (string? input-or-opts) (if (string? input-or-opts)
(with-in-str input-or-opts (apply main/main args)) (with-in-str input-or-opts (apply main/main args))
(apply main/main args)))] (apply main/main args)))]
;; (prn :err (str es))
(if (zero? res) (if (zero? res)
(str os) (str os)
(throw (ex-info (str es) (do
{:stdout (str os) (println (str os))
:stderr (str es)}))))) (throw (ex-info (str es)
{:stdout (str os)
:stderr (str es)}))))))
(finally (finally
(when (string? input-or-opts) (vars/bindRoot sci/in *in*)) (when (string? input-or-opts) (vars/bindRoot sci/in *in*))
(vars/bindRoot sci/out *out*) (vars/bindRoot sci/out *out*)
(vars/bindRoot sci/err *err*))))) (vars/bindRoot sci/err *err*)))))
(defn bb-native [input & args] (defn bb-native [input & args]
(let-programs [bb "./bb"] (let [res (p/process (into ["./bb"] args)
(try (if input (cond-> {:in input
(apply bb (conj (vec args) :out :string
{:in input})) :err :string}
(apply bb args)) *bb-edn-path*
(catch Exception e (assoc
(let [d (ex-data e) :env (assoc (into {} (System/getenv))
err-msg (or (:stderr (ex-data e)) "")] "BABASHKA_EDN" *bb-edn-path*))))
(throw (ex-info err-msg d))))))) res (deref res)
exit (:exit res)
error? (pos? exit)]
(if error? (throw (ex-info (or (:err res) "") {}))
(:out res))))
(def bb (def bb
(case (System/getenv "BABASHKA_TEST_ENV") (case (System/getenv "BABASHKA_TEST_ENV")

View file

@ -1,7 +1,6 @@
(ns babashka.uberjar-test (ns babashka.uberjar-test
(:require (:require
[babashka.test-utils :as tu] [babashka.test-utils :as tu]
[clojure.edn :as edn]
[clojure.string :as str] [clojure.string :as str]
[clojure.test :as t :refer [deftest is testing]])) [clojure.test :as t :refer [deftest is testing]]))
@ -10,7 +9,7 @@
path (.getPath tmp-file)] path (.getPath tmp-file)]
(.deleteOnExit tmp-file) (.deleteOnExit tmp-file)
(testing "uberjar" (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" (is (= "(\"1\" \"2\" \"3\" \"4\")\n"
(tu/bb nil "--jar" path "1" "2" "3" "4"))) (tu/bb nil "--jar" path "1" "2" "3" "4")))
(is (= "(\"1\" \"2\" \"3\" \"4\")\n" (is (= "(\"1\" \"2\" \"3\" \"4\")\n"
@ -25,5 +24,5 @@
(let [tmp-file (java.io.File/createTempFile "uber" ".jar") (let [tmp-file (java.io.File/createTempFile "uber" ".jar")
path (.getPath tmp-file)] path (.getPath tmp-file)]
(.deleteOnExit 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"))))) (is (str/includes? (tu/bb "(+ 1 2 3)" path) "6")))))