From fb7f984389fb8816fae8c0e9988a7816e5c3bfe0 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sun, 26 Dec 2021 17:26:35 +0100 Subject: [PATCH] [#1110] Load tasks and deps from other bb.edn file (#1117) Co-authored-by: Bob --- .circleci/config.yml | 4 +- .circleci/script/release | 2 +- .clj-kondo/babashka/fs/config.edn | 1 + .dir-locals.el | 2 + appveyor.yml | 6 +- script/lib_tests/run_all_libtests.bat | 6 +- src/babashka/impl/deps.clj | 17 +- src/babashka/impl/tasks.clj | 29 +- src/babashka/main.clj | 441 +++++++++--------- .../lib_tests/clojure/data/json_test.clj | 2 +- test-resources/lib_tests/table/core_test.clj | 2 +- test/babashka/bb_edn_test.clj | 45 +- test/babashka/main_test.clj | 30 +- test/babashka/test_utils.clj | 29 +- test/babashka/uberjar_test.clj | 21 +- 15 files changed, 333 insertions(+), 304 deletions(-) create mode 100644 .clj-kondo/babashka/fs/config.edn create mode 100644 .dir-locals.el diff --git a/.circleci/config.yml b/.circleci/config.yml index 577cea0f..99d82219 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -58,8 +58,8 @@ jobs: java -jar $jar script/reflection.clj reflection="babashka-$VERSION-reflection.json" - BABASHKA_EDN=".build/bb.edn" java -jar "$jar" release-artifact "$jar" - BABASHKA_EDN=".build/bb.edn" java -jar "$jar" release-artifact "$reflection" + java -jar "$jar" --config .build/bb.edn --deps-root . release-artifact "$jar" + java -jar "$jar" --config .build/bb.edn --deps-root . release-artifact "$reflection" - store_artifacts: path: /tmp/release destination: release diff --git a/.circleci/script/release b/.circleci/script/release index b4a142e6..7c2f4cc9 100755 --- a/.circleci/script/release +++ b/.circleci/script/release @@ -24,7 +24,7 @@ tar zcvf "$archive" bb # bbk cd - -BABASHKA_EDN=".build/bb.edn" ./bb release-artifact "/tmp/release/$archive" +./bb --config .build/bb.edn --deps-root . release-artifact "/tmp/release/$archive" ## cleanup diff --git a/.clj-kondo/babashka/fs/config.edn b/.clj-kondo/babashka/fs/config.edn new file mode 100644 index 00000000..23f36094 --- /dev/null +++ b/.clj-kondo/babashka/fs/config.edn @@ -0,0 +1 @@ +{:lint-as {babashka.fs/with-temp-dir clojure.core/let}} diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 00000000..9671ce52 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,2 @@ +((clojure-mode + (cider-clojure-cli-aliases . "test"))) diff --git a/appveyor.yml b/appveyor.yml index 56e2df13..23665955 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -62,11 +62,7 @@ build_script: jar -cMf %zip% bb.exe - set BABASHKA_EDN=.build/bb.edn - - bb release-artifact %zip% - - set BABASHKA_EDN= + bb --config .build/bb.edn --deps-root . release-artifact %zip% set BABASHKA_CLASSPATH= diff --git a/script/lib_tests/run_all_libtests.bat b/script/lib_tests/run_all_libtests.bat index 831d9020..6c36ba62 100644 --- a/script/lib_tests/run_all_libtests.bat +++ b/script/lib_tests/run_all_libtests.bat @@ -4,8 +4,4 @@ set EDN=lib_tests.edn .\bb -f script/lib_tests/bb_edn_from_deps.clj %EDN% -set BABASHKA_EDN=%EDN% - -%BB_CMD% -f test-resources/lib_tests/babashka/run_all_libtests.clj %* - -set BABASHKA_EDN= +%BB_CMD% --config %EDN% --deps-root . -f test-resources/lib_tests/babashka/run_all_libtests.clj %* diff --git a/src/babashka/impl/deps.clj b/src/babashka/impl/deps.clj index 815d1930..ccccf2b9 100644 --- a/src/babashka/impl/deps.clj +++ b/src/babashka/impl/deps.clj @@ -1,6 +1,8 @@ (ns babashka.impl.deps (:require [babashka.deps :as bdeps] + [babashka.fs :as fs] [babashka.impl.classpath :as cp] + [babashka.impl.common :refer [bb-edn]] [borkdude.deps :as deps] [clojure.string :as str] [sci.core :as sci])) @@ -58,8 +60,19 @@ ([deps-map] (add-deps deps-map nil)) ([deps-map {:keys [:aliases :env :extra-env :force]}] (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 :raw :min-bb-version))] + (let [paths (let [deps-root (:deps-root @bb-edn) + deps-root (fs/absolutize deps-root) + ;; cwd (fs/absolutize ".") + ;; rel (fs/relativize cwd f) + paths (mapv #(str (fs/file deps-root %)) paths)] + paths)] + (cp/add-classpath (str/join cp/path-sep paths)))) + (when-let [deps-map (not-empty (dissoc deps-map + ;; paths are added manually above + :paths + ;; extra-paths are transformed to :deps in task handling + :extra-paths + :tasks :raw :min-bb-version))] (binding [*print-namespace-maps* false] (let [deps-map (assoc-in deps-map [:aliases :org.babashka/defaults] {:replace-paths [] ;; babashka sets paths manually diff --git a/src/babashka/impl/tasks.clj b/src/babashka/impl/tasks.clj index d6cfd13a..7cdef088 100644 --- a/src/babashka/impl/tasks.clj +++ b/src/babashka/impl/tasks.clj @@ -229,8 +229,7 @@ (defn format-task [init extra-paths extra-deps requires prog] (format " -%s ;; extra-paths -%s ;; extra-deps +%s ;; deps (ns %s %s) (require '[babashka.tasks]) @@ -251,12 +250,12 @@ %s " - (if (seq extra-paths) - (format "(babashka.classpath/add-classpath \"%s\")" (str/join cp/path-sep extra-paths)) - "") - (if (seq extra-deps) - (format "(babashka.deps/add-deps '%s)" (pr-str {:deps extra-deps})) - "") + (let [deps (cond-> {} + (seq extra-deps) (assoc :deps extra-deps) + (seq extra-paths) (assoc :paths extra-paths))] + (if (seq deps) + (format "(babashka.deps/add-deps '%s)" (pr-str deps)) + "")) @rand-ns (if (seq requires) (format "(:require %s)" (str/join " " requires)) @@ -271,13 +270,13 @@ depends (:depends task)] (when (contains? processing task-name) (throw (ex-info (str "Cyclic task: " task-name) {}))) - (loop [deps (seq depends)] - (let [deps (remove #(contains? @processed %) deps) - order (vec (mapcat #(target-order tasks % processed (conj processing task-name)) deps))] - (if-not (contains? @processed task-name) - (do (vswap! processed conj task-name) - (conj order task-name)) - order)))))) + (let [deps (seq depends) + deps (remove #(contains? @processed %) deps) + order (vec (mapcat #(target-order tasks % processed (conj processing task-name)) deps))] + (if-not (contains? @processed task-name) + (do (vswap! processed conj task-name) + (conj order task-name)) + order))))) #_(defn tasks->dependees [task-names tasks] (let [tasks->depends (zipmap task-names (map #(:depends (get tasks %)) task-names))] diff --git a/src/babashka/main.clj b/src/babashka/main.clj index 6dde9961..5428ebf0 100644 --- a/src/babashka/main.clj +++ b/src/babashka/main.clj @@ -137,6 +137,8 @@ Global opts: --debug Print debug information and internal stacktrace in case of exception. --force Passes -Sforce to deps.clj, forcing recalculation of the classpath. --init Load file after any preloads and prior to evaluation/subcommands. + --config Replacing bb.edn with file. Relative paths are resolved relative to file. + --deps-root Treat dir as root of relative paths in config. Help: @@ -632,21 +634,27 @@ Use bb run --help to show this help output. (if options (case (first options) ("--classpath" "-cp") (recur (nnext options) (assoc opts-map :classpath (second options))) + ("--debug" - "--verbose" ;; renamed to --debug - ) (recur (next options) (assoc opts-map :debug true)) + "--verbose") + ;; renamed to --debug + (recur (next options) (assoc opts-map :debug true)) + ("--init") (recur (nnext options) (assoc opts-map :init (second options))) + + ("--config") + (recur (nnext options) (assoc opts-map :config (second options))) + + ("--deps-root") + (recur (nnext options) (assoc opts-map :deps-root (second options))) [options opts-map]) [options opts-map]))) (defn parse-opts ([options] (parse-opts options nil)) ([options opts-map] - (let [[options opts-map] (if opts-map - [options opts-map] - (parse-global-opts options)) - opt (first options) + (let [opt (first options) tasks (into #{} (map str) (keys (:tasks @common/bb-edn)))] (if-not opt opts-map ;; FILE > TASK > SUBCOMMAND @@ -743,228 +751,231 @@ Use bb run --help to show this help output. (binding [*out* *err*] (println "[babashka] WARNING: clojure.core.specs.alpha is removed from the classpath, unless you explicitly add the dependency.")) nil))) - main (if (and jar (not main)) - (when-let [res (cp/getResource - (cp/loader jar) - ["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 - 'load-file (sci-namespaces/core-var 'load-file load-file*)))) - :env env - :features #{:bb :clj} - :classes classes/class-map - :imports classes/imports - :load-fn load-fn - :uberscript uberscript - ;; :readers core/data-readers - :reify-fn reify-fn - :proxy-fn proxy-fn} - opts (addons/future opts) - sci-ctx (sci/init opts) - _ (vreset! common/ctx sci-ctx) - preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim)) - [expressions exit-code] - (cond expressions [expressions 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]) - run (if (:run-help cli-opts) - [(print-run-help) 0] - (do - (System/setProperty "babashka.task" (str run)) - (tasks/assemble-task run - (:parallel-tasks cli-opts)))) - file (try [[(read-file file)] nil] - (catch Exception e - (error-handler e {:expression expressions - :debug debug - :preloads preloads - :init init - :loader (:loader @cp/cp-state)})))) - expression (str/join " " expressions) ;; this might mess with the locations... - exit-code - ;; handle preloads - (if exit-code exit-code - (do (when preloads - (sci/binding [sci/file ""] - (try - (sci/eval-string* sci-ctx preloads) - (catch Throwable e - (error-handler e {:expression expression - :debug debug - :preloads preloads - :init init - :loader (:loader @cp/cp-state)}))))) - nil)) - exit-code - ;; handle --init - (if exit-code exit-code - (do (when init - (try - (load-file* init) - (catch Throwable e - (error-handler e {:expression expression - :debug debug - :preloads preloads - :init init - :loader (:loader @cp/cp-state)})))) - nil)) - ;; socket REPL is start asynchronously. when no other args are - ;; provided, a normal REPL will be started as well, which causes the - ;; process to wait until SIGINT - _ (when socket-repl (start-socket-repl! socket-repl sci-ctx)) - exit-code - (or exit-code - (second - (cond version-opt - [(print-version) 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] - nrepl [(start-nrepl! nrepl sci-ctx) 0] - uberjar [nil 0] - list-tasks [(tasks/list-tasks sci-ctx) 0] - print-deps [(print-deps/print-deps (:print-deps-format cli-opts)) 0] - uberscript - [nil (do (uberscript/uberscript {:ctx sci-ctx - :expressions expressions}) - 0)] - expressions - ;; execute code - (sci/binding [sci/file abs-path] - (try + main (if (and jar (not main)) + (when-let [res (cp/getResource + (cp/loader jar) + ["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 + 'load-file (sci-namespaces/core-var 'load-file load-file*)))) + :env env + :features #{:bb :clj} + :classes classes/class-map + :imports classes/imports + :load-fn load-fn + :uberscript uberscript + ;; :readers core/data-readers + :reify-fn reify-fn + :proxy-fn proxy-fn} + opts (addons/future opts) + sci-ctx (sci/init opts) + _ (vreset! common/ctx sci-ctx) + preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim)) + [expressions exit-code] + (cond expressions [expressions 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]) + run (if (:run-help cli-opts) + [(print-run-help) 0] + (do + (System/setProperty "babashka.task" (str run)) + (tasks/assemble-task run + (:parallel-tasks cli-opts)))) + file (try [[(read-file file)] nil] + (catch Exception e + (error-handler e {:expression expressions + :debug debug + :preloads preloads + :init init + :loader (:loader @cp/cp-state)})))) + expression (str/join " " expressions) ;; this might mess with the locations... + exit-code + ;; handle preloads + (if exit-code exit-code + (do (when preloads + (sci/binding [sci/file ""] + (try + (sci/eval-string* sci-ctx preloads) + (catch Throwable e + (error-handler e {:expression expression + :debug debug + :preloads preloads + :init init + :loader (:loader @cp/cp-state)}))))) + nil)) + exit-code + ;; handle --init + (if exit-code exit-code + (do (when init + (try + (load-file* init) + (catch Throwable e + (error-handler e {:expression expression + :debug debug + :preloads preloads + :init init + :loader (:loader @cp/cp-state)})))) + nil)) + ;; socket REPL is start asynchronously. when no other args are + ;; provided, a normal REPL will be started as well, which causes the + ;; process to wait until SIGINT + _ (when socket-repl (start-socket-repl! socket-repl sci-ctx)) + exit-code + (or exit-code + (second + (cond version-opt + [(print-version) 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] + nrepl [(start-nrepl! nrepl sci-ctx) 0] + uberjar [nil 0] + list-tasks [(tasks/list-tasks sci-ctx) 0] + print-deps [(print-deps/print-deps (:print-deps-format cli-opts)) 0] + uberscript + [nil (do (uberscript/uberscript {:ctx sci-ctx + :expressions expressions}) + 0)] + expressions + ;; execute code + (sci/binding [sci/file abs-path] + (try ; when evaluating expression(s), add in repl-requires so things like ; pprint and dir are available - (sci/eval-form sci-ctx `(apply require (quote ~clojure-main/repl-requires))) - (loop [] - (let [in (read-next *in*)] - (if (identical? ::EOF in) - [nil 0] ;; done streaming - (let [res [(let [res - (sci/binding [sci/file (or @sci/file "") - input-var in - core/command-line-args command-line-args] - (sci/eval-string* sci-ctx expression))] - ;; return value printing - (when (and (some? res) - (or (not run) - (:prn cli-opts))) - (if-let [pr-f (cond shell-out println - edn-out prn)] - (if (sequential? res) - (doseq [l res - :while (not (pipe-signal-received?))] - (pr-f l)) - (pr-f res)) - (prn res)))) 0]] - (if stream? - (recur) - res))))) - (catch Throwable e - (error-handler e {:expression expression - :debug debug - :preloads preloads - :loader (:loader @cp/cp-state)})))) - clojure [nil (if-let [proc (bdeps/clojure command-line-args)] - (-> @proc :exit) - 0)] - :else [(repl/start-repl! sci-ctx) 0])) - 1)] - (flush) - (when uberscript - (let [uberscript-out uberscript] - (spit uberscript-out "") ;; reset file - (doseq [s (distinct @uberscript-sources)] - (spit uberscript-out s :append true)) - (spit uberscript-out preloads :append true) - (spit uberscript-out expression :append true))) - (when uberjar - (if-let [cp (cp/get-classpath)] - (uberjar/run {:dest uberjar - :jar :uber - :classpath cp - :main-class main - :verbose debug}) - (throw (Exception. "The uberjar task needs a classpath.")))) - exit-code)))) + (sci/eval-form sci-ctx `(apply require (quote ~clojure-main/repl-requires))) + (loop [] + (let [in (read-next *in*)] + (if (identical? ::EOF in) + [nil 0] ;; done streaming + (let [res [(let [res + (sci/binding [sci/file (or @sci/file "") + input-var in + core/command-line-args command-line-args] + (sci/eval-string* sci-ctx expression))] + ;; return value printing + (when (and (some? res) + (or (not run) + (:prn cli-opts))) + (if-let [pr-f (cond shell-out println + edn-out prn)] + (if (sequential? res) + (doseq [l res + :while (not (pipe-signal-received?))] + (pr-f l)) + (pr-f res)) + (prn res)))) 0]] + (if stream? + (recur) + res))))) + (catch Throwable e + (error-handler e {:expression expression + :debug debug + :preloads preloads + :loader (:loader @cp/cp-state)})))) + clojure [nil (if-let [proc (bdeps/clojure command-line-args)] + (-> @proc :exit) + 0)] + :else [(repl/start-repl! sci-ctx) 0])) + 1)] + (flush) + (when uberscript + (let [uberscript-out uberscript] + (spit uberscript-out "") ;; reset file + (doseq [s (distinct @uberscript-sources)] + (spit uberscript-out s :append true)) + (spit uberscript-out preloads :append true) + (spit uberscript-out expression :append true))) + (when uberjar + (if-let [cp (cp/get-classpath)] + (uberjar/run {:dest uberjar + :jar :uber + :classpath cp + :main-class main + :verbose debug}) + (throw (Exception. "The uberjar task needs a classpath.")))) + exit-code)))) (defn satisfies-min-version? [min-version] -(let [[major-current minor-current patch-current] version-data - [major-min minor-min patch-min] (parse-version min-version)] -(or (> major-current major-min) - (and (= major-current major-min) - (or (> minor-current minor-min) - (and (= minor-current minor-min) - (>= patch-current patch-min))))))) + (let [[major-current minor-current patch-current] version-data + [major-min minor-min patch-min] (parse-version min-version)] + (or (> major-current major-min) + (and (= major-current major-min) + (or (> minor-current minor-min) + (and (= minor-current minor-min) + (>= patch-current patch-min))))))) (defn main [& args] -(let [bb-edn-file (or (System/getenv "BABASHKA_EDN") - "bb.edn") - bb-edn (or (when (fs/exists? bb-edn-file) - (let [raw-string (slurp bb-edn-file) - edn (edn/read-string raw-string) - edn (assoc edn :raw raw-string)] - (vreset! common/bb-edn edn))) - ;; tests may have modified bb-edn - @common/bb-edn) - min-bb-version (:min-bb-version bb-edn)] -(when min-bb-version - (when-not (satisfies-min-version? min-bb-version) - (binding [*out* *err*] - (println (str "WARNING: this project requires babashka " - min-bb-version " or newer, but you have: " version)))))) -(let [opts (parse-opts args)] -(exec opts))) + (let [[args global-opts] (parse-global-opts args) + bb-edn-file (or (:config global-opts) + "bb.edn") + bb-edn (when (fs/exists? bb-edn-file) + (let [raw-string (slurp bb-edn-file) + edn (edn/read-string raw-string) + edn (assoc edn + :raw raw-string + :file bb-edn-file + :deps-root + (or (:deps-root global-opts) + (str (fs/parent bb-edn-file))))] + (vreset! common/bb-edn edn))) + min-bb-version (:min-bb-version bb-edn)] + (when min-bb-version + (when-not (satisfies-min-version? min-bb-version) + (binding [*out* *err*] + (println (str "WARNING: this project requires babashka " + min-bb-version " or newer, but you have: " version))))) + (exec (parse-opts args global-opts)))) (def musl? -"Captured at compile time, to know if we are running inside a + "Captured at compile time, to know if we are running inside a statically compiled executable with musl." -(and (= "true" (System/getenv "BABASHKA_STATIC")) -(= "true" (System/getenv "BABASHKA_MUSL")))) + (and (= "true" (System/getenv "BABASHKA_STATIC")) + (= "true" (System/getenv "BABASHKA_MUSL")))) (defmacro run [args] -(if musl? -;; When running in musl-compiled static executable we lift execution of bb -;; inside a thread, so we have a larger than default stack size, set by an -;; argument to the linker. See https://github.com/oracle/graal/issues/3398 -`(let [v# (volatile! nil) - f# (fn [] - (vreset! v# (apply main ~args)))] - (doto (Thread. nil f# "main") - (.start) - (.join)) - @v#) -`(apply main ~args))) + (if musl? + ;; When running in musl-compiled static executable we lift execution of bb + ;; inside a thread, so we have a larger than default stack size, set by an + ;; argument to the linker. See https://github.com/oracle/graal/issues/3398 + `(let [v# (volatile! nil) + f# (fn [] + (vreset! v# (apply main ~args)))] + (doto (Thread. nil f# "main") + (.start) + (.join)) + @v#) + `(apply main ~args))) (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)) - last-iteration (dec n)] - (dotimes [i n] - (if (< i last-iteration) - (with-out-str (apply main args)) - (do (run args) - (binding [*out* *err*] - (println "ran" n "times")))))) -(let [exit-code (run args)] - (System/exit exit-code)))) + [& 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)) + last-iteration (dec n)] + (dotimes [i n] + (if (< i last-iteration) + (with-out-str (apply main args)) + (do (run args) + (binding [*out* *err*] + (println "ran" n "times")))))) + (let [exit-code (run args)] + (System/exit exit-code)))) ;;;; Scratch diff --git a/test-resources/lib_tests/clojure/data/json_test.clj b/test-resources/lib_tests/clojure/data/json_test.clj index f07f5105..61ac8380 100644 --- a/test-resources/lib_tests/clojure/data/json_test.clj +++ b/test-resources/lib_tests/clojure/data/json_test.clj @@ -414,7 +414,7 @@ (is (= x (json/read-str (with-out-str (json/pprint x))))))) (deftest pretty-print-nonescaped-unicode - (is (= "\"\u1234\u4567\"\n" + (is (= (str "\"\u1234\u4567\"" (System/lineSeparator)) (with-out-str (json/pprint "\u1234\u4567" :escape-unicode false))))) diff --git a/test-resources/lib_tests/table/core_test.clj b/test-resources/lib_tests/table/core_test.clj index fa9d0fe5..c2e609bf 100644 --- a/test-resources/lib_tests/table/core_test.clj +++ b/test-resources/lib_tests/table/core_test.clj @@ -15,7 +15,7 @@ +---+---+ | 3 | 4 | +---+---+ - ") "\n") + ") (System/lineSeparator)) (with-out-str (table [["1" "2"] ["3" "4"]]))))) (deftest test-table-with-vecs-in-vec diff --git a/test/babashka/bb_edn_test.clj b/test/babashka/bb_edn_test.clj index 26b63846..fc0328e6 100644 --- a/test/babashka/bb_edn_test.clj +++ b/test/babashka/bb_edn_test.clj @@ -1,12 +1,14 @@ (ns babashka.bb-edn-test (:require [babashka.fs :as fs] + [babashka.impl.classpath :as cp] [babashka.impl.common :as common] [babashka.main :as main] [babashka.test-utils :as test-utils] [clojure.edn :as edn] [clojure.string :as str] - [clojure.test :as test :refer [deftest is testing]])) + [clojure.test :as test :refer [deftest is testing]] + [sci.core :as sci])) (defn bb [& args] (let [args (map str args) @@ -220,7 +222,10 @@ t1 (System/currentTimeMillis) delta-parallel (- t1 t0)] (is (= tree s)) - (is (< delta-parallel delta-sequential)))))) + (when (>= (doto (-> (Runtime/getRuntime) (.availableProcessors)) + (prn)) + 2) + (is (< delta-parallel delta-sequential))))))) (testing "exception" (test-utils/with-config '{:tasks {a (Thread/sleep 10000) b (do (Thread/sleep 10) @@ -337,20 +342,24 @@ (is (= "uberjar" (:file (main/parse-opts ["uberjar"])))) (finally (fs/delete "uberjar")))))) -(deftest min-bb-version - (when-not test-utils/native? - (vreset! common/bb-edn '{:min-bb-version "300.0.0"}) - (let [sw (java.io.StringWriter.)] - (binding [*err* sw] - (main/main "-e" "nil")) - (is (str/includes? (str sw) - "WARNING: this project requires babashka 300.0.0 or newer, but you have: "))))) +(deftest min-bb-version-test + (fs/with-temp-dir [dir {}] + (let [config (str (fs/file dir "bb.edn"))] + (spit config '{:min-bb-version "300.0.0"}) + (let [sw (java.io.StringWriter.)] + (binding [*err* sw] + (main/main "--config" config "-e" "nil")) + (is (str/includes? (str sw) + "WARNING: this project requires babashka 300.0.0 or newer, but you have: ")))))) -;; TODO: -;; Do we want to support the same parsing as the clj CLI? -;; Or do we want `--aliases :foo:bar` -;; Let's wait for a good use case -#_(deftest alias-deps-test - (test-utils/with-config '{:aliases {:medley {:deps {medley/medley {:mvn/version "1.3.0"}}}}} - (is (= '{1 {:id 1}, 2 {:id 2}} - (bb "-A:medley" "-e" "(require 'medley.core)" "-e" "(medley.core/index-by :id [{:id 1} {:id 2}])"))))) +(deftest classpath-other-bb-edn-test + (fs/with-temp-dir [dir {}] + (let [config (str (fs/file dir "bb.edn"))] + (spit config '{:paths ["src"] + :tasks {cp (prn (babashka.classpath/get-classpath))}}) + (let [out (bb "--config" config "cp") + entries (cp/split-classpath out) + entry (first entries)] + (is (= 1 (count entries))) + (is (= (fs/parent config) (fs/parent entry))) + (is (str/ends-with? entry "src")))))) diff --git a/test/babashka/main_test.clj b/test/babashka/main_test.clj index 7453d769..a3a3f687 100644 --- a/test/babashka/main_test.clj +++ b/test/babashka/main_test.clj @@ -18,30 +18,34 @@ :eof nil} (apply test-utils/bb (when (some? input) (str input)) (map str args))))) +(defn parse-opts [args] + (let [[args global-opts] (main/parse-global-opts args)] + (main/parse-opts args global-opts))) + (deftest parse-opts-test (is (= "1667" - (:nrepl (main/parse-opts ["--nrepl-server"])))) + (:nrepl (parse-opts ["--nrepl-server"])))) (is (= "1666" - (:socket-repl (main/parse-opts ["--socket-repl"])))) + (:socket-repl (parse-opts ["--socket-repl"])))) (is (= {:nrepl "1667", :classpath "src"} - (main/parse-opts ["--nrepl-server" "-cp" "src"]))) + (parse-opts ["--nrepl-server" "-cp" "src"]))) (is (= {:nrepl "1667", :classpath "src"} - (main/parse-opts ["-cp" "src" "nrepl-server"]))) + (parse-opts ["-cp" "src" "nrepl-server"]))) (is (= {:socket-repl "1666", :expressions ["123"]} - (main/parse-opts ["--socket-repl" "-e" "123"]))) + (parse-opts ["--socket-repl" "-e" "123"]))) (is (= {:socket-repl "1666", :expressions ["123"]} - (main/parse-opts ["--socket-repl" "1666" "-e" "123"]))) + (parse-opts ["--socket-repl" "1666" "-e" "123"]))) (is (= {:nrepl "1666", :expressions ["123"]} - (main/parse-opts ["--nrepl-server" "1666" "-e" "123"]))) + (parse-opts ["--nrepl-server" "1666" "-e" "123"]))) (is (= {:classpath "src" :uberjar "foo.jar"} - (main/parse-opts ["--classpath" "src" "uberjar" "foo.jar"]))) + (parse-opts ["--classpath" "src" "uberjar" "foo.jar"]))) (is (= {:classpath "src" :uberjar "foo.jar" :debug true} - (main/parse-opts ["--debug" "--classpath" "src" "uberjar" "foo.jar"]))) - (is (= "src" (:classpath (main/parse-opts ["--classpath" "src"])))) - (is (:debug (main/parse-opts ["--debug"]))) + (parse-opts ["--debug" "--classpath" "src" "uberjar" "foo.jar"]))) + (is (= "src" (:classpath (parse-opts ["--classpath" "src"])))) + (is (:debug (parse-opts ["--debug"]))) (is (= 123 (bb nil "(println 123)"))) (is (= 123 (bb nil "-e" "(println 123)"))) (is (= 123 (bb nil "--eval" "(println 123)"))) @@ -54,8 +58,8 @@ (let [v (bb nil "--describe")] (is (:babashka/version v)) (is (:feature/xml v))) - (is (= {:force? true} (main/parse-opts ["--force"]))) - (is (= {:main "foo", :command-line-args '("-h")} (main/parse-opts ["-m" "foo" "-h"])))) + (is (= {:force? true} (parse-opts ["--force"]))) + (is (= {:main "foo", :command-line-args '("-h")} (parse-opts ["-m" "foo" "-h"])))) (deftest version-test (is (= [1 0 0] (main/parse-version "1.0.0-SNAPSHOT"))) diff --git a/test/babashka/test_utils.clj b/test/babashka/test_utils.clj index 3b086746..fb55ca3e 100644 --- a/test/babashka/test_utils.clj +++ b/test/babashka/test_utils.clj @@ -47,13 +47,10 @@ (defn bb-jvm [input-or-opts & args] (reset! cp/cp-state nil) (reset! main/env {}) - (if-let [path *bb-edn-path*] - (let [raw (slurp path)] - (vreset! common/bb-edn - (assoc (edn/read-string raw) - :raw raw))) - (vreset! common/bb-edn nil)) - (let [os (java.io.StringWriter.) + (vreset! common/bb-edn nil) + (let [args (cond-> args *bb-edn-path* + (->> (list* "--config" *bb-edn-path* "--deps-root" "."))) + os (java.io.StringWriter.) es (if-let [err (:err input-or-opts)] err (java.io.StringWriter.)) in (if (string? input-or-opts) @@ -76,7 +73,9 @@ (apply main/main args)))] (if (zero? res) (do - (println (str es)) ;; flush stderr + (let [err (str es)] + (when-not (str/blank? err) + (println err))) ;; flush stderr (normalize (str os))) (do (println (str os)) @@ -89,14 +88,12 @@ (vars/bindRoot sci/err *err*))))) (defn bb-native [input & args] - (let [res (p/process (into ["./bb"] args) - (cond-> {:in input - :out :string - :err :string} - *bb-edn-path* - (assoc - :extra-env (assoc (into {} (System/getenv)) - "BABASHKA_EDN" *bb-edn-path*)))) + (let [args (cond-> args *bb-edn-path* + (->> (list* "--config" *bb-edn-path* "--deps-root" "."))) + res (p/process (into ["./bb"] args) + {:in input + :out :string + :err :string}) res (deref res) exit (:exit res) error? (pos? exit)] diff --git a/test/babashka/uberjar_test.clj b/test/babashka/uberjar_test.clj index 581a863d..66d94a13 100644 --- a/test/babashka/uberjar_test.clj +++ b/test/babashka/uberjar_test.clj @@ -45,16 +45,6 @@ (is (= "(\"42\")\n" (tu/bb nil "--jar" path "-m" "my.main-main" "42"))) (is (= "(\"42\")\n" (tu/bb nil "--classpath" path "-m" "my.main-main" "42"))) (is (= "(\"42\")\n" (tu/bb nil path "42")))))) - - ; this test fails the windows native test in CI - (when-not main/windows? - (testing "throw on empty classpath" - (let [tmp-file (java.io.File/createTempFile "uber" ".jar") - path (.getPath tmp-file)] - (.deleteOnExit tmp-file) - (is (thrown-with-msg? - Exception #"classpath" - (tu/bb nil "uberjar" path "-m" "my.main-main")))))) (testing "ignore empty entries on classpath" (let [tmp-file (java.io.File/createTempFile "uber" ".jar") path (.getPath tmp-file) @@ -63,3 +53,14 @@ (tu/bb nil "--classpath" empty-classpath "uberjar" path "-m" "my.main-main") ;; Only a manifest entry is added (is (< (count-entries path) 3))))) + +(deftest throw-on-empty-classpath + ;; this test fails the windows native test in CI + (when-not main/windows? + (testing "throw on empty classpath" + (let [tmp-file (java.io.File/createTempFile "uber" ".jar") + path (.getPath tmp-file)] + (.deleteOnExit tmp-file) + (is (thrown-with-msg? + Exception #"classpath" + (tu/bb nil "uberjar" path "-m" "my.main-main")))))))