Compare commits

...

64 commits

Author SHA1 Message Date
Michiel Borkent
a3f7cebd15 do 2021-03-25 11:54:31 +01:00
Michiel Borkent
c2829fe34d :do 2021-03-25 11:41:42 +01:00
Michiel Borkent
035167ab11 TODO 2021-03-25 11:23:57 +01:00
Michiel Borkent
e4d82b87be Fix help 2021-03-25 11:19:21 +01:00
Michiel Borkent
6e58278f9d rewrite to hiccup 2021-03-25 11:09:22 +01:00
Michiel Borkent
f7457e64d2 rewrite tests to new syntax 2021-03-25 10:54:07 +01:00
Michiel Borkent
29f4452fef Improve error message 2021-03-24 17:04:01 +01:00
Michiel Borkent
4617076104 :task/args 2021-03-23 11:18:35 +01:00
Michiel Borkent
6abe9a6851 Add sorting and task/description 2021-03-23 11:15:01 +01:00
Michiel Borkent
b69ec8fc17 Don't throw 2021-03-23 10:52:13 +01:00
Michiel Borkent
5a1b69953b Merge branch 'master' into bb.edn 2021-03-22 20:41:36 +01:00
Michiel Borkent
3c0900563c Merge branch 'master' into bb.edn 2021-03-22 17:27:20 +01:00
Michiel Borkent
be47d673f5 shortcut 2021-03-22 12:03:32 +01:00
Michiel Borkent
289c589215 Merge branch 'master' into bb.edn 2021-03-22 11:42:43 +01:00
Michiel Borkent
3e860722f0 fix tests 2021-03-20 21:11:50 +01:00
Michiel Borkent
f554a32dff fix tests 2021-03-20 21:11:43 +01:00
Michiel Borkent
a8319d42c1 main 2021-03-20 20:47:21 +01:00
Michiel Borkent
423789a8a9 main 2021-03-20 20:47:03 +01:00
Michiel Borkent
4a58ad8c9f wip 2021-03-20 18:18:45 +01:00
Michiel Borkent
9b9c6f0d36 wip 2021-03-20 17:16:03 +01:00
Michiel Borkent
7cc4867212 help 2021-03-20 16:44:44 +01:00
Michiel Borkent
3750ea0459 wip 2021-03-20 14:49:41 +01:00
Michiel Borkent
5b9e3c3267 wip 2021-03-20 14:47:04 +01:00
Michiel Borkent
200caef226 wip 2021-03-20 14:33:44 +01:00
Michiel Borkent
ff3d1e56ba wip 2021-03-20 14:17:02 +01:00
Michiel Borkent
f9f6fa8621 test 2021-03-20 14:11:09 +01:00
Michiel Borkent
612edd6fc9 do test 2021-03-20 13:55:39 +01:00
Michiel Borkent
343c363319 wip 2021-03-20 13:26:09 +01:00
Michiel Borkent
6e4313700f Test macro [skip ci] 2021-03-20 13:22:33 +01:00
Michiel Borkent
a44f07a665 test 2021-03-20 13:13:28 +01:00
Michiel Borkent
91106b5401 Get rid of conch 2021-03-20 12:30:33 +01:00
Michiel Borkent
e6b211a030 Get rid of conch 2021-03-20 12:30:25 +01:00
Michiel Borkent
e6905173b6 wip 2021-03-19 18:31:47 +01:00
Michiel Borkent
6a1d48460e wip 2021-03-19 18:25:01 +01:00
Michiel Borkent
07f5e811e6 script 2021-03-19 13:01:41 +01:00
Michiel Borkent
70eb717d21 Reset paths 2021-03-19 12:56:03 +01:00
Michiel Borkent
8f93826072 wip 2021-03-19 12:14:04 +01:00
Michiel Borkent
ca22e8fd04 wip [skip ci] 2021-03-19 11:53:15 +01:00
Michiel Borkent
5317020a24 Merge branch 'master' into bb.edn 2021-03-19 10:51:51 +01:00
Michiel Borkent
aae8464fb9 wip [skip ci] 2021-03-18 11:35:01 +01:00
Michiel Borkent
c5ebffa7bf wip [skip ci] 2021-03-18 11:31:41 +01:00
Michiel Borkent
f0bdc28888 wip [skip ci] 2021-03-17 21:31:35 +01:00
Michiel Borkent
240c90a179 Merge branch 'master' into bb.edn 2021-03-17 21:16:45 +01:00
Michiel Borkent
6515ef771a wip [skip ci] 2021-03-17 19:50:50 +01:00
Michiel Borkent
1160f44d22 wip [skip ci] 2021-03-17 16:59:33 +01:00
Michiel Borkent
644634be1d wip [skip ci] 2021-03-17 16:36:25 +01:00
Michiel Borkent
60e25043d4 wip [skip ci] 2021-03-17 14:40:21 +01:00
Michiel Borkent
40e7b739e9 wip [skip ci] 2021-03-17 14:08:08 +01:00
Michiel Borkent
9051d3aac7 fix tests [skip ci] 2021-03-17 13:48:21 +01:00
Michiel Borkent
afed99ad1b wip [skip ci] 2021-03-17 13:05:04 +01:00
Michiel Borkent
44f80f2a28 wip [skip ci] 2021-03-17 12:25:26 +01:00
Michiel Borkent
90443eed80 wip [skip ci] 2021-03-16 22:06:38 +01:00
Michiel Borkent
ef4969e54c wip [skip ci] 2021-03-16 22:01:30 +01:00
Michiel Borkent
e0dd41d631 args 2021-03-16 18:05:36 +01:00
Michiel Borkent
55f38db119 wip 2021-03-16 17:51:44 +01:00
Michiel Borkent
4d77ee6446 foo 2021-03-16 17:38:50 +01:00
Michiel Borkent
d99f813fe8 wip 2021-03-16 17:22:05 +01:00
Michiel Borkent
adc5987162 wip 2021-03-16 11:51:54 +01:00
Michiel Borkent
ce62b47085 fix tests 2021-03-16 11:48:46 +01:00
Michiel Borkent
ae397e20e7 wip 2021-03-16 11:44:31 +01:00
Michiel Borkent
23834a5302 Foo 2021-03-16 11:35:46 +01:00
Michiel Borkent
18219526e2 Implememnt babashka/args 2021-03-16 11:23:58 +01:00
Michiel Borkent
57aaed524d fallback 2021-03-16 10:56:03 +01:00
Michiel Borkent
1f1b6cfde1 Support task keyword 2021-03-16 10:43:51 +01:00
12 changed files with 624 additions and 199 deletions

4
bb.edn.bak Normal file
View file

@ -0,0 +1,4 @@
{:paths ["test-resources/task_scripts"]
:tasks {:main-task {:task/type :main
:main tasks ;; this calls tasks/-main
:args [1 2 3]}}}

View file

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

View file

@ -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."

View file

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

View file

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

View file

@ -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]
@ -33,6 +34,7 @@
[babashka.impl.test :as t]
[babashka.impl.tools.cli :refer [tools-cli-namespace]]
[babashka.nrepl.server :as nrepl-server]
[babashka.process :as p]
[babashka.wait :as wait]
[clojure.edn :as edn]
[clojure.java.io :as io]
@ -74,140 +76,33 @@
;; 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
(atom nil))
(defn print-help []
(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 "
(defn decode-task [task]
(let [task-key (first task)
args (rest task)
maybe-opts (first args)]
(if (map? maybe-opts)
{:task task-key
:opts maybe-opts
:args (rest args)}
{:task task-key
:args args})))
(defn print-help [ctx command-line-args]
(if (empty? command-line-args)
(do
(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.
@ -252,7 +147,36 @@ If the first argument is not any of the above options, then it treated as a file
Everything after that is bound to *command-line-args*.
Use -- to separate script command line args from bb command line args.
"))
")
[nil 0]) ;; end do
(let [k (first command-line-args)
k (keyword (subs k 1))
task (get-in @bb-edn [:tasks k])
{:keys [:args]} (decode-task task)
main (first args)
help-text (:help (meta task))]
(if help-text
[(println help-text) 0]
(if main
(let [main (if (simple-symbol? main)
(symbol (str main) "-main")
main)]
(if-let [doc (sci/eval-string* ctx (format "(some-> (requiring-resolve '%s) meta :doc)" main))]
[(println doc) 0]
[(println "No help found for task:" k) 1]))
[(println "No help found for task:" k) 1]))
,)) ;; end if
,) ;; end defn
(defn print-tasks [tasks]
(let [tasks (into (sorted-map) tasks)]
(println "The following tasks are available:")
(println)
(doseq [[k v] tasks]
(println k (:task/description v)))
(println)
(println "Run bb :help <task> to view help of a specific task.")
[nil 0]))
(defn print-describe []
(println
@ -301,8 +225,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 +241,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 +399,221 @@ 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!)
(declare resolve-task)
(defn error [msg exit]
(binding [*out* *err*]
(println msg)
{:exec (fn [] [nil exit])}))
(defn parse-opts [options]
(let [fst (when options (first options))
key? (when fst (str/starts-with? fst ":"))
keys (when key? (rest (str/split fst #":")))
expanded (when (and key? (> (count keys) 1))
(into [:do] (map (comp vector keyword) keys)))
k (when (and key? (not expanded))
(keyword (first keys)))
task? (or expanded k)
bb-edn (when task? @bb-edn)
tasks (when (and task? bb-edn)
(:tasks bb-edn))
user-task (when tasks (get tasks k))]
(cond user-task
(resolve-task tasks user-task {:command-line-args (next options)})
expanded (resolve-task tasks expanded nil)
: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" ":clojure") (assoc opts-map :clojure true
:command-line-args (rest options))
("--version" ":version") {:version true}
("--help" "-h" "-?" ":help") {:help true
:command-line-args (rest options)}
(":tasks") {:tasks tasks
:command-line-args (rest options)}
("--verbose")(recur (next options)
(assoc opts-map
:verbose? true))
("--describe" ":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" ":uberscript")
(let [options (next options)]
(recur (next options)
(assoc opts-map
:uberscript (first options))))
("--uberjar" ":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" ":repl")
(let [options (next options)]
(recur (next options)
(assoc opts-map
:repl true)))
("--socket-repl" ":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" ":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", ":main")
(let [options (next options)]
(recur (next options)
(assoc opts-map :main (first options))))
#_#_(":do")
(let [options (next options)
options (into [] (comp (partition-by #(or
(= % ":do")
(= % ":and-do")
(= % ":or-do"))))
options)]
{:do options})
#_#_(":invoke")
{:exec-src
(pr-str '(if-let [f (requiring-resolve (symbol (first *command-line-args*)))]
(apply f (rest *command-line-args*))
(throw (Exception. (str "Var not found: " (first *command-line-args*)
" " (babashka.classpath/get-classpath))))))
:command-line-args (next 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)))
(if (fs/exists? opt)
(assoc opts-map
(if (str/ends-with? opt ".jar")
:jar :file) opt
:command-line-args (next options))
(error (str (if (str/starts-with? opt ":")
"Task does not exist: "
"File does not exist: ") opt) 1)))))))
opts-map))]
opts))))
(defn resolve-task [tasks task {:keys [:command-line-args]}]
(let [{:keys [:task :opts :args]} (decode-task task)]
opts ;; not used
(case task
:babashka
(let [cmd-line-args args]
(parse-opts (seq (map str (concat cmd-line-args command-line-args)))))
:shell
(let [args (into (vec args) command-line-args)]
{:exec (fn []
[nil
(-> (p/process args {:inherit true})
deref
:exit)])})
:main
(let [main-arg (first args)
cmd-line-args (rest args)]
(parse-opts (seq (map str (concat ["--main" main-arg] cmd-line-args command-line-args)))))
:do
{:do (map #(resolve-task tasks % nil) args)}
;; default
(if-let [t (get tasks task)]
(resolve-task tasks t nil)
(error (str "No such task: " task) 1)))))
(def should-load-inits?
"if true, then we should still load preloads and user.clj"
(volatile! true))
(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
:exec-src :tasks]
exec-fn :exec}
opts
_ (when verbose? (vreset! common/verbose? true))
_ (do ;; set properties
(when main (System/setProperty "babashka.main" main))
@ -516,7 +632,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 +661,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]
@ -576,11 +687,18 @@ Use -- to separate script command line args from bb command line args.
opts (addons/future opts)
sci-ctx (sci/init opts)
_ (vreset! common/ctx sci-ctx)
preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim))
preloads (when @should-load-inits? (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
@ -591,15 +709,28 @@ Use -- to separate script command line args from bb command line args.
exit-code
;; handle preloads
(if exit-code exit-code
(do (when preloads
(sci/binding [sci/file "<preloads>"]
(try
(sci/eval-string* sci-ctx preloads)
(catch Throwable e
(error-handler e {:expression expression
:verbose? verbose?
:preloads preloads
:loader (:loader @cp/cp-state)})))))
(do (when @should-load-inits?
(when preloads
(sci/binding [sci/file "<preloads>"]
(try
(sci/eval-string* sci-ctx preloads)
(catch Throwable e
(error-handler e {:expression expression
:verbose? verbose?
:preloads preloads
:loader (:loader @cp/cp-state)})))))
(when @cp/cp-state
(when-let [{:keys [:file :source]}
(cp/source-for-namespace (:loader @cp/cp-state) "user" nil)]
(sci/binding [sci/file file]
(try
(sci/eval-string* sci-ctx source)
(catch Throwable e
(error-handler e {:expression expression
:verbose? verbose?
:preloads preloads
:loader (:loader @cp/cp-state)}))))))
(vreset! should-load-inits? false))
nil))
;; socket REPL is start asynchronously. when no other args are
;; provided, a normal REPL will be started as well, which causes the
@ -610,8 +741,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)
tasks (print-tasks tasks)
describe?
[(print-describe) 0]
repl [(repl/start-repl! sci-ctx) 0]
@ -626,7 +757,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 "<expr>")
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 +777,16 @@ Use -- to separate script command line args from bb command line args.
:verbose? verbose?
:preloads preloads
:loader (:loader @cp/cp-state)}))))
exec-fn (exec-fn)
exec-src [(let [res (sci/binding [sci/file (or @sci/file "<task>")
core/command-line-args command-line-args]
(sci/eval-string* sci-ctx exec-src))]
(when (some? res)
(prn res)))
0]
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 +806,30 @@ 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))]
(reset! 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)]
(if-let [do-opts (:do opts)]
(reduce (fn [prev-exit opts]
;; (prn :prev prev-exit)
;; (prn :opts opts)
(if (pos? prev-exit)
(reduced prev-exit)
(exec opts)))
0
do-opts)
(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))

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)

62
test-resources/bb.edn Normal file
View file

@ -0,0 +1,62 @@
{:paths ["test-resources/bb-edn"] ;; no-brainer to support this
:deps {medley/medley {:mvn/version "1.3.0"}} ;; no-brainer to support this
;; here comes the part that is currently uncertain
:tasks {:count-files {:task/type :shell
:args ["bash" "-c" "ls | wc -l"]}
:bash {:task/type :babashka
:args [:invoke user/bash]}
:eval-plus {:task/type :babashka
:args [-e (apply + (map (fn [i]
(Integer/parseInt i))
*command-line-args*))]}
:describe {:task/type :babashka
:args [:clojure -Sdescribe]}
:do-bash {:task/type :babashka
:args [:do :bash "ls | wc -l"
:__ :eval-plus 1 2 3]}
:all {:task/type :babashka
:args [:do :count-files
:__ :bash "ls | wc -l"
:__ :eval-plus 1 2 3
:__ :describe]}
;;;;
:never-failing-shell
{:task/type :babashka
;; we are executing a shell task, yet we are continueing despite the error code
:args [-e (-> (do (deref (babashka.process/process ["ls" "foobar"] {:inherit true}))
(deref (babashka.process/process ["echo" "done"] {:inherit true}))
nil))]}
:print-hello {:task/type :babashka
:args [-e (println :hello)]}
:always-failing-shell
{:task/type :babashka
;; we are executing a shell task, yet we are continueing despite the error code
:args [-e (babashka.process/check (babashka.process/process ["ls" "foobar"] {:inherit true}))]}
:composed-never-failing {:task/type :babashka
:args [:do :never-failing-shell
:__ :print-hello]}
:composed-failing {:task/type :babashka
:args [:do :always-failing-shell
:__ :print-hello]}
,
;;;; help
:cool-task {:task/type :babashka
:args ["-e" "(+ 1 2 3)"]
:task/help "Usage: bb :cool-task
Sum up the numbers 1, 2 and 3."}
,
;;;; context (not implemented)
;; the problem with this is the DSL nature which is very opiniated and
;; maybe not so nice to learn for people who only want to write Clojure
:put-answer-in-ctx {:type/task :babashka
:args [:eval-plus 1 2 3]
:out [:answer]}
:print-answer {:type/task :babashka
:in {:sum [:answer]}
:args [-e (println $sum)]}
}
}

16
test-resources/setup.clj Normal file
View file

@ -0,0 +1,16 @@
(defn eval-plus [& args]
(apply + (map (fn [i]
(Integer/parseInt i))
*command-line-args*)))
(defn tree []
(babashka.deps/clojure ["-Stree"]))
(defn all [& args]
(apply eval-plus args)
(tree))
(defn bash [& args]
(babashka.process/process (into ["bash"] args)))

View file

@ -0,0 +1,8 @@
(ns tasks)
(defn -main
"Usage: just pass some args.
This is the main main function in this namespace."
[& args]
args)

View file

@ -0,0 +1,133 @@
(ns babashka.bb-edn-test
{:clj-kondo/config '{:linters {:unresolved-symbol {:exclude [working?]}}}}
(: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 testing]]))
(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")]
(spit bb-edn-file# ~cfg)
(binding [test-utils/*bb-edn-path* (str bb-edn-file#)]
~@body)))
(deftest task-cli-test
(with-config {}
(is (thrown-with-msg?
Exception #"Task does not exist: :sum"
(bb :sum)))))
(deftest babashka-task-test
(with-config {:tasks {:sum [:babashka "-e" "(+ 1 2 3)"]}}
(let [res (bb :sum)]
(is (= 6 res)))))
(deftest shell-task-test
(let [temp-dir (fs/create-temp-dir)
temp-file (fs/create-file (fs/path temp-dir "temp-file.txt"))]
(with-config {:tasks {:clean [:shell "rm" (str temp-file)]}}
(is (fs/exists? temp-file))
(bb :clean)
(is (not (fs/exists? temp-file))))))
(deftest sequential-task-test
(testing ":and-do"
(let [temp-dir (fs/create-temp-dir)
temp-file (fs/create-file (fs/path temp-dir "temp-file.txt"))]
(with-config {:tasks {:sum [:babashka "-e" "(+ 1 2 3)"]
:all [:do
[:shell "rm" (str temp-file)]
[:sum]]}}
(is (fs/exists? temp-file))
(let [res (bb :all)]
(is (= 6 res)))
(is (not (fs/exists? temp-file)))))
#_(testing ":and-do shortcut"
(let [temp-dir (fs/create-temp-dir)
temp-file (fs/create-file (fs/path temp-dir "temp-file.txt"))]
(with-config {:tasks {:clean [:shell :clean:sum]
:sum [:babashka "-e" "(+ 1 2 3)"]
:all [:babashka :clean:sum]}}
(is (fs/exists? temp-file))
(let [res (bb :clean:sum)]
(is (= 6 res)))
(is (not (fs/exists? temp-file)))))))
#_(testing ":do always continuing"
(with-config {:tasks {:sum-1 [:babashka "-e" "(do (+ 4 5 6) nil)"]
:sum-2 [:babashka "-e" "(+ 1 2 3)"]
:all [:babashka :sum-1:sum2]}}
(is (= 6 (bb :all))))
#_(with-config {:tasks {:div-by-zero [:babashka "-e" "(/ 1 0)"]
:sum [:babashka "-e" "(+ 1 2 3)"]
:all[:babashka :div-by-zero:sum] }}
(is (= 6 (bb :all)))))
(testing "task fails when one of subtask fails"
(with-config {:tasks {:div-by-zero [:babashka "-e" "(/ 1 0)"]
:sum [:babashka "-e" "(+ 1 2 3)"]
:all [:babashka :div-by-zero:sum]}}
(is (thrown-with-msg? Exception #"Divide"
(bb :all)))))
#_(testing ":or-do short-cutting"
(with-config {:tasks {:sum-1 [:babashka "-e" "(+ 1 2 3)"]
:sum-2 [:babashka "-e" "(+ 4 5 6)"]
:all [:or [:sum1] [:sum2]]}}
(is (= 6 (bb :all)))))
#_(testing ":or-do succeeding after failing"
(with-config {:tasks {:div-by-zero [:babashka "-e" "(/ 1 0)"]
:sum [:babashka "-e" "(+ 1 2 3)"]
:all [:babashka [:or [:div-by-zero] [:sum]]]}}
(is (= 6 (bb :all))))))
(deftest prioritize-user-task-test
(is (map? (bb :describe)))
(with-config {:tasks {:describe [:babashka "-e" "(+ 1 2 3)"]}}
(is (= 6 (bb :describe)))))
(deftest help-task-test
(with-config "{:tasks {:cool-task
^{:help \"Usage: bb :cool-task
Addition is a pretty advanced topic. Let us start with the identity element
0. ...\"}
[:babashka -e (+ 1 2 3)]}}"
(is (str/includes? (apply test-utils/bb nil
(map str [:help :cool-task]))
"Usage: bb :cool-task"))))
#_(deftest list-tasks-test
(with-config {:tasks {:cool-task-1 {:task/type :babashka
:task/args ["-e" "(+ 1 2 3)"]
:task/description "Return the sum of 1, 2 and 3."
:task/help "Usage: bb :cool-task
Addition is a pretty advanced topic. Let us start with the identity element
0. ..."}
:cool-task-2 {:task/type :babashka
:task/description "Return the sum of 4, 5 and 6."
:task/args ["-e" "(+ 4 5 6)"]
:task/help "Usage: bb :cool-task
Addition is a pretty advanced topic. Let us start with the identity element
0. ..."}}}
(let [res (apply test-utils/bb nil
(map str [:tasks]))]
(is (str/includes? res "The following tasks are available:"))
(is (str/includes? res ":cool-task-1 Return the"))
(is (str/includes? res ":cool-task-2 Return the")))))
(deftest main-task-test
(with-config {:paths ["test-resources/task_scripts"]
:tasks {:main-task [:main 'tasks 1 2 3]}}
(is (= '("1" "2" "3") (bb :main-task)))
(let [res (apply test-utils/bb nil
(map str [:help :main-task]))]
(is (str/includes? res "Usage: just pass some args")))))

View file

@ -2,14 +2,20 @@
(: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]
[sci.core :as sci]
[sci.impl.vars :as vars]))
(set! *warn-on-reflection* true)
(def ^:dynamic *bb-edn-path* nil)
(defn bb-jvm [input-or-opts & args]
(reset! cp/cp-state nil)
(reset! main/env {})
(when-let [path *bb-edn-path*]
(reset! main/bb-edn (edn/read-string (slurp path))))
(let [os (java.io.StringWriter.)
es (if-let [err (:err input-or-opts)]
err (java.io.StringWriter.))
@ -30,26 +36,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")