diff --git a/.build/bb.edn b/.build/bb.edn index 6ec32294..125c9cc5 100644 --- a/.build/bb.edn +++ b/.build/bb.edn @@ -2,5 +2,5 @@ :deps {borkdude/gh-release-artifact #_{:local/root "../gh-release-artifact"} {:git/url "https://github.com/borkdude/gh-release-artifact" - :git/sha "ce060c12a25b552b864dc90f8fb344a2eb91ea9d"}} + :git/sha "4a9a74f0e50e897c45df8cc70684360eb30fce80"}} :tasks {release-artifact babashka.release-artifact/release}} diff --git a/.circleci/config.yml b/.circleci/config.yml index 3e0293e5..4434ec9e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -29,7 +29,7 @@ jobs: - run: name: Generate config command: | - /tmp/bbb .circleci/script/short_ci.clj > generated_config.yml + /tmp/bbb .circleci/script/gen_ci.clj > generated_config.yml - continuation/continue: configuration_path: generated_config.yml diff --git a/.circleci/script/docker.clj b/.circleci/script/docker.clj index a884c893..a64ee3f5 100644 --- a/.circleci/script/docker.clj +++ b/.circleci/script/docker.clj @@ -1,6 +1,6 @@ -(require '[clojure.string :as str] +(require '[babashka.fs :as fs] '[babashka.process :as proc] - '[babashka.fs :as fs]) + '[clojure.string :as str]) (import '[java.time Instant]) (defn read-env @@ -12,7 +12,9 @@ (def image-name "babashka/babashka") -(def image-tag (slurp "resources/BABASHKA_VERSION")) +(def ghcr-image-name "ghcr.io/babashka/babashka") + +(def image-tag (str/trim (slurp "resources/BABASHKA_VERSION"))) (def latest-tag "latest") @@ -46,6 +48,11 @@ [username password] (exec ["docker" "login" "-u" username "-p" password])) +(defn docker-login-ghcr + [username password] + (exec ["docker" "login" "ghcr.io" "-u" username "-p" password])) + +;; TODO: Remove this when Dockerhub goes off (defn build-push [image-tag platform docker-file] (println (format "Building and pushing %s Docker image(s) %s:%s" @@ -59,12 +66,25 @@ "-f" docker-file]] (exec (concat base-cmd label-args ["."])))) +(defn build-push-ghcr + [image-tag platform docker-file] + (println (format "Building and pushing %s Docker image(s) %s:%s to GHCR" + platform + ghcr-image-name + image-tag)) + (let [base-cmd ["docker" "buildx" "build" + "-t" (str ghcr-image-name ":" image-tag) + "--platform" platform + "--push" + "-f" docker-file]] + (exec (concat base-cmd label-args ["."])))) + (defn build-push-images [] (doseq [platform (str/split platforms #",")] (let [tarball-platform (str/replace platform #"\/" "-") tarball-platform (if (= "linux-arm64" tarball-platform) - "linux-aarch64" + "linux-aarch64-static" tarball-platform) tarball-path (format "/tmp/release/babashka-%s-%s.tar.gz" image-tag @@ -74,16 +94,20 @@ ; this overwrites, but this is to work around having built the uberjar/metabom multiple times (fs/copy (format "/tmp/release/%s-metabom.jar" tarball-platform) "metabom.jar" {:replace-existing true}))) (build-push image-tag platforms "Dockerfile.ci") + (build-push-ghcr image-tag platforms "Dockerfile.ci") (when-not snapshot? - (build-push latest-tag platforms "Dockerfile.ci"))) + (build-push latest-tag platforms "Dockerfile.ci") + (build-push-ghcr latest-tag platforms "Dockerfile.ci"))) (defn build-push-alpine-images "Build alpine image for linux-amd64 only (no upstream arm64 support yet)" [] (exec ["tar" "zxvf" (str "/tmp/release/babashka-" image-tag "-linux-amd64-static.tar.gz")]) (build-push (str image-tag "-alpine") "linux/amd64" "Dockerfile.alpine") + (build-push-ghcr (str image-tag "-alpine") "linux/amd64" "Dockerfile.alpine") (when-not snapshot? - (build-push "alpine" "linux/amd64" "Dockerfile.alpine"))) + (build-push "alpine" "linux/amd64" "Dockerfile.alpine") + (build-push-ghcr "alpine" "linux/amd64" "Dockerfile.alpine"))) (when (= *file* (System/getProperty "babashka.file")) (if (and (nil? (read-env "CIRCLE_PULL_REQUEST")) @@ -93,6 +117,7 @@ (println "This is a snapshot version") (println "This is a non-snapshot version")) (docker-login (read-env "DOCKERHUB_USER") (read-env "DOCKERHUB_PASS")) + (docker-login-ghcr (read-env "CONTAINER_REGISTRY_USER") (read-env "BB_GHCR_TOKEN")) (build-push-images) (build-push-alpine-images)) (println "Not publishing docker image(s)."))) diff --git a/.circleci/script/short_ci.clj b/.circleci/script/gen_ci.clj similarity index 89% rename from .circleci/script/short_ci.clj rename to .circleci/script/gen_ci.clj index f467f36b..7fadc293 100644 --- a/.circleci/script/short_ci.clj +++ b/.circleci/script/gen_ci.clj @@ -1,10 +1,12 @@ -(ns short-ci +(ns gen-ci (:require [babashka.tasks :as tasks] [clj-yaml.core :as yaml] [clojure.string :as str] [flatland.ordered.map :refer [ordered-map]])) +(def graalvm-version "22") + (defn run ([cmd-name cmd] (run cmd-name cmd nil)) @@ -80,8 +82,9 @@ :working_directory "~/repo" :environment {:LEIN_ROOT "true" :BABASHKA_PLATFORM "linux" - :GRAALVM_VERSION "22.3.0" - :GRAALVM_HOME graalvm-home} + :GRAALVM_VERSION graalvm-version + :GRAALVM_HOME graalvm-home + :BABASHKA_TEST_ENV "jvm"} :resource_class "large" :steps (gen-steps @@ -119,7 +122,7 @@ java -jar \"$jar\" --config .build/bb.edn --deps-root . release-artifact \"$refl (defn unix [shorted? static? musl? arch executor-conf resource-class graalvm-home platform] (let [env {:LEIN_ROOT "true" - :GRAALVM_VERSION "22.3.0" + :GRAALVM_VERSION graalvm-version :GRAALVM_HOME graalvm-home :BABASHKA_PLATFORM (if (= "mac" platform) "macos" @@ -154,6 +157,8 @@ java -jar \"$jar\" --config .build/bb.edn --deps-root . release-artifact \"$refl :steps (gen-steps shorted? (filter some? [:checkout + (when (contains? #{"linux" "linux-aarch64"} platform) + (run "Check max glibc version" "script/check_glibc.sh")) {:attach_workspace {:at "/tmp"}} (run "Pull Submodules" "git submodule init\ngit submodule update") {:restore_cache @@ -167,13 +172,16 @@ java -jar \"$jar\" --config .build/bb.edn --deps-root . release-artifact \"$refl (str base-install-cmd "\nsudo -E script/setup-musl") base-install-cmd))) (run "Download GraalVM" "script/install-graalvm") - (run "Build binary" "script/uberjar\nscript/compile" "30m") + #_(run "Download iprof" "curl -sLO 'https://github.com/babashka/pgo-profiles/releases/download/2023.10.11/default.iprof'") + (run "Build binary" (if (= "aarch64" arch) + "script/uberjar\nscript/compile -H:PageSize=64K # --pgo=default.iprof" + "script/uberjar\nscript/compile # --pgo=default.iprof") "30m") (run "Run tests" "script/test\nscript/run_lib_tests") (run "Release" ".circleci/script/release") {:persist_to_workspace {:root "/tmp" :paths ["release"]}} {:save_cache - {:paths ["~/.m2" "~/graalvm-ce-java11-22.3.0"] + {:paths ["~/.m2" "~/graalvm"] :key cache-key}} {:store_artifacts {:path "/tmp/release" :destination "release"}} @@ -184,9 +192,9 @@ java -jar \"$jar\" --config .build/bb.edn --deps-root . release-artifact \"$refl [shorted?] (let [docker-executor-conf {:docker [{:image "circleci/clojure:openjdk-11-lein-2.9.8-bullseye"}]} machine-executor-conf {:machine {:image "ubuntu-2004:202111-01"}} - mac-executor-conf {:macos {:xcode "14.0.0"}} - linux-graalvm-home "/home/circleci/graalvm-ce-java11-22.3.0" - mac-graalvm-home "/Users/distiller/graalvm-ce-java11-22.3.0/Contents/Home"] + mac-executor-conf {:macos {:xcode "12.5.1"}} + linux-graalvm-home (str "/home/circleci/graalvm-" graalvm-version) + mac-graalvm-home (format "/Users/distiller/graalvm-%s/Contents/Home" graalvm-version)] (ordered-map :version 2.1 :commands @@ -201,17 +209,9 @@ java -jar \"$jar\" --config .build/bb.edn --deps-root . release-artifact \"$refl :linux (unix shorted? false false "amd64" docker-executor-conf "large" linux-graalvm-home "linux") :linux-static (unix shorted? true true "amd64" docker-executor-conf "large" linux-graalvm-home "linux") - :linux-aarch64 (unix shorted? - false - false - "aarch64" - machine-executor-conf - "arm.large" - linux-graalvm-home - "linux") :linux-aarch64-static (unix shorted? true false "aarch64" machine-executor-conf "arm.large" linux-graalvm-home "linux") - :mac (unix shorted? false false "amd64" mac-executor-conf "large" mac-graalvm-home "mac") + :mac (unix shorted? false false "amd64" mac-executor-conf "macos.x86.medium.gen2" mac-graalvm-home "mac") :deploy (deploy shorted?) :docker (docker shorted?)) :workflows (ordered-map @@ -220,12 +220,11 @@ java -jar \"$jar\" --config .build/bb.edn --deps-root . release-artifact \"$refl "linux" "linux-static" "mac" - "linux-aarch64" "linux-aarch64-static" {:deploy {:filters {:branches {:only "master"}} :requires ["jvm" "linux"]}} {:docker {:filters {:branches {:only "master"}} - :requires ["linux" "linux-static" "linux-aarch64"]}}]})))) + :requires ["linux" "linux-static" "linux-aarch64-static"]}}]})))) (def skip-config {:skip-if-only [#".*.md$" diff --git a/.cirrus.yml b/.cirrus.yml index 34ebcced..270c6925 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -5,8 +5,8 @@ task: skip: "changesIncludeOnly('logo/*', '**.md')" env: LEIN_ROOT: "true" - GRAALVM_VERSION: "22.3.0" - GRAALVM_HOME: ${HOME}/graalvm-ce-java11-22.3.0/Contents/Home + GRAALVM_VERSION: "22" + GRAALVM_HOME: ${HOME}/graalvm-${GRAALVM_VERSION}/Contents/Home BABASHKA_PLATFORM: macos # used in release script BABASHKA_ARCH: aarch64 BABASHKA_TEST_ENV: native @@ -25,7 +25,8 @@ task: java -version script/uberjar - script/compile + # curl -sLO 'https://github.com/babashka/pgo-profiles/releases/download/2023.10.11/default.iprof' + script/compile # --pgo=default.iprof # script/test # script/run_lib_tests diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 66e1f6ef..5f35f824 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,7 @@ jobs: uses: actions/setup-java@v2 with: distribution: 'adopt-hotspot' - java-version: '11' + java-version: '19' - name: Install clojure tools uses: DeLaGuardo/setup-clojure@5.0 @@ -102,7 +102,7 @@ jobs: runs-on: ${{ matrix.os }} env: LEIN_ROOT: "true" - GRAALVM_VERSION: "22.3.0" + GRAALVM_VERSION: "22" BABASHKA_PLATFORM: ${{ matrix.name }} # used in release script BABASHKA_TEST_ENV: native BABASHKA_XMX: "-J-Xmx6500m" @@ -126,8 +126,8 @@ jobs: if: "matrix.static == false" uses: graalvm/setup-graalvm@v1 with: - version: '22.3.0' - java-version: '11' + java-version: '22' + distribution: 'graalvm' components: 'native-image' github-token: ${{ secrets.GITHUB_TOKEN }} @@ -135,8 +135,8 @@ jobs: if: "matrix.static == true" uses: graalvm/setup-graalvm@v1 with: - version: '22.3.0' - java-version: '11' + version: '22' + distribution: 'graalvm' components: 'native-image' native-image-musl: true github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 284c61e2..b5cf81da 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,5 @@ bb.build_artifacts.txt target .nrepl-port .DS_Store +.portal +default.iprof diff --git a/CHANGELOG.md b/CHANGELOG.md index 473540ad..db2c54f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,252 @@ A preview of the next release can be installed from ## Unreleased +- Fix [#1688](https://github.com/babashka/babashka/issues/1688): use-fixtures should add metadata to `*ns*` +- Fix [#1692](https://github.com/babashka/babashka/issues/1692): Add support for ITransientSet and org.flatland/ordered-set +- Bump org.flatland/ordered to `1.15.12`. + +## 1.3.190 (2024-04-17) + +- Fix [#1679](https://github.com/babashka/babashka/issues/1679): bump timbre and fix wrapping `timbre/log!` +- Add `java.util.concurrent.CountDownLatch` +- Add `java.lang.ThreadLocal` +- Bump `babashka.process` +- Bump httpkit to `2.8.0-RC1` +- Bump clojure to `1.11.2` +- Bump deps.clj +- Bump `babashka.cli` +- Bump `cheshire` to `5.13.0` +- Bump `http-client` to `0.4.17` + +## 1.3.189 (2024-02-22) + +- [#1660](https://github.com/babashka/babashka/issues/1660): add `:deps-root` as part of hash to avoid caching issue with `deps.clj` +- [#1632](https://github.com/babashka/babashka/issues/1632): fix `(.readPassword (System/console))` by upgrading GraalVM to `21.0.2` +- [#1661](https://github.com/babashka/babashka/issues/1661): follow symlink when reading adjacent bb.edn +- [#1665](https://github.com/babashka/babashka/issues/1665): `read-string` should use non-indexing reader for compatibilty with Clojure +- Bump edamame to 1.4.24 +- Bump http-client to 0.4.16 +- Bump babashka.cli to 0.8.57 +- Uberjar task: support reader conditional in .cljc file +- Support reader conditional in .cljc file when creating uberjar +- Add more `javax.net.ssl` classes +- [#1675](https://github.com/babashka/babashka/issues/1675): add `hash-unordered-coll` + +## 1.3.188 (2024-01-12) + +- [#1658](https://github.com/babashka/babashka/issues/1658): fix command line parsing for scripts that parse `--version` or `version` etc + +## 1.3.187 (2024-01-09) + +- Add `clojure.reflect/reflect` +- Add `java.util.ScheduledFuture`, `java.time.temporal.WeekFields` +- Support `Runnable` to be used without import +- Allow `catch` to be used as var name +- [#1646](https://github.com/babashka/babashka/issues/1646): command-line-args are dropped when file exists with same name +- [#1645](https://github.com/babashka/babashka/issues/1645): Support for `clojure.lang.LongRange` +- [#1652](https://github.com/babashka/babashka/issues/1652): allow `bb.edn` to be empty +- [#1586](https://github.com/babashka/babashka/issues/1586): warn when config file doesn't exist and `--debug` is enabled +- [#1410](https://github.com/babashka/babashka/issues/1410): better error message when exec fn doesn't exist +- Bump `babashka.cli` to `0.8.55` which contains subcommand improvements +- Bump `deps.clj` to `1.11.1.1435` +- Bump `babashka.fs` to `0.5.20` +- Compatibility with `plumbing.core` +- Compatibility with `shadow.css` by improving `tools.reader` compatibility +- [#1647](https://github.com/babashka/babashka/issues/1647): Allow capturing env vars at build time (only relevant for building bb) + +## 1.3.186 (2023-11-02) + +- [Support self-contained binaries as uberjars!](https://github.com/babashka/babashka/wiki/Self-contained-executable#uberjar) +- Add `java.security.KeyFactory`, `java.security.spec.PKCS8EncodedKeySpec`, `java.net.URISyntaxException`, `javax.crypto.spec.IvParameterSpec` +- Fix babashka.process/exec wrt `babashka.process/*defaults*` +- [#1632](https://github.com/babashka/babashka/issues/1632): Partial fix for `(.readPassword (System/console))` +- Enable producing self-contained binaries using [uberjars](https://github.com/babashka/babashka/wiki/Self-contained-executable#uberjar) +- Bump httpkit to `2.8.0-beta3` (fixes GraalVM issue with virtual threads) +- Bump `deps.clj` and `fs` +- Expose `taoensso.timbre.appenders.core` +- nREPL: implement `ns-list` op +- SCI: optimize `swap!`, `deref` and `reset!` for normal atoms (rather than user-created `IAtom`s) +- Add test for [#1639](https://github.com/babashka/babashka/issues/1639) +- Upgrade to GraalVM 21.0.1 + +## 1.3.185 (2023-09-28) + +- [#1624](https://github.com/babashka/babashka/pull/1624): Use Oracle GraalVM 21 ([@lispyclouds](https://github.com/lispyclouds)) +- Use PGO to speed up loops (now 2-3x faster for `(time (loop [val 0 cnt 10000000] (if (pos? cnt) (recur (inc val) (dec cnt)) val)))`!) +- Bump babashka.http-client to v0.4.15 +- Bump rewrite-clj to v0.1.1.47 +- [#1619](https://github.com/babashka/babashka/issues/1619): Fix reflection issue with `Thread/sleep` in `core.async/timeout` +- Support interop on `java.util.stream.IntStream` +- [#1513](https://github.com/babashka/babashka/issues/1513): Fix interop on `Thread/sleep` with numbers that aren't already longs +- Bump babashka.cli to 0.7.53 +- Fix [#babashka.nrepl/66](https://github.com/babashka/babashka.nrepl/issues/66) +- Various nREPL server improvements (classpath op, file lookup information for `cider-find-var`) +- Bump cheshire to 5.12.0 + +## 1.3.184 (2023-08-22) + +- Remove leftover debugging output from deps.clj + +## 1.3.183 (2023-08-22) + +- [#1592](https://github.com/babashka/babashka/issues/1592): expose `sci.core` in babashka +- [#1596](https://github.com/babashka/babashka/issues/1596): Fix `clojure.java.browse/browse-url` truncates URLs with multiple query parameters on Windows +- [#1599](https://github.com/babashka/babashka/issues/1599): propagate error from `run` when task does not exist +- Bump clj-yaml to `1.0.27` +- [#1604](https://github.com/babashka/babashka/issues/1604): throw `FileNotFoundException` when requiring namespace whose file cannot be found (as JVM Clojure does) +- Bump integrant CI tests +- [#1600](https://github.com/babashka/babashka/issues/1600): use pagesize of 64K on linux aarch64, so it works on Asahi linux +- Expose `selmer.parser/resolve-arg` +- [#1610](https://github.com/babashka/babashka/issues/1610): expose `babashka.http-client.websocket` namespace +- Bump `babashka.http-client` to `0.4.14` +- [#1568](https://github.com/babashka/babashka/issues/1568): warn when task overrides built-in command + +## 1.3.182 (2023-07-20) + +- [#1579](https://github.com/babashka/babashka/issues/1579): add `clojure.tools.reader/resolve-symbol` +- [#1581](https://github.com/babashka/babashka/issues/1581): `bb print-deps`: sort dependencies ([@teodorlu](https://github.com/teodorlu)) +- Upgrade `babashka.http-client` to `0.4.12`, fixes `:insecure` option +- Bump [edamame](https://github.com/borkdude/edamame) to `1.3.23`: fixes infinite loop with reader conditional expression +- Bump [Selmer](https://github.com/yogthos/Selmer) to Bumping to `1.12.59` +- Bump [deps.clj](https://github.com/borkdude/deps.clj) with more fixes which should make downloading/installation of tools jar more robust +- Add `javax.net.ssl.X509ExtendedTrustManager` class +- Bump [babashka.process](https://github.com/babashka/process): accept path or file as `:dir` argument +- Bump [hiccup](https://github.com/weavejester/hiccup) to `2.0.0-RC1` + +## 1.3.181 (2023-06-13) + +- [#1575](https://github.com/babashka/babashka/issues/1575): fix command line parsing problem with `-e` + `*command-line-args*` +- [#1576](https://github.com/babashka/babashka/issues/1576): make downloading/unzipping of deps.clj tools .zip file more robust + +## 1.3.180 (2023-05-28) + +- [#1524](https://github.com/babashka/babashka/issues/1524): Remove dynamic builds for linux-aarch64 ([@lispyclouds](https://github.com/lispyclouds)) +- [#1577](https://github.com/babashka/babashka/issues/1557): Add support for `babashka.process/exec` after namespace reload of `babashka.process` ([@lread](https://github.com/lread)) +- [#1548](https://github.com/babashka/babashka/issues/1548): shell and sh should respect `babashka.process/*defaults*` +- [#1524](https://github.com/babashka/babashka/issues/1524): deprecate (remove) linux-aarch64 dynamic binary build +- Expose `org.graalvm.nativeimage.ProcessProperties/exec` +- Bump `babashka.http-client` to `0.3.11` +- Bump `babashka.fs` to `0.4.19` +- Bump `babashka.process` to `0.5.21` + +## 1.3.179 (2023-04-26) + +- [#1544](https://github.com/babashka/babashka/issues/1544): `:local/root` in script-adjacent bb.edn should resolve relative to script +- [#1545](https://github.com/babashka/babashka/issues/1545): Adjacent `bb.edn` not respected with explicit `-f` option +- [#1546](https://github.com/babashka/babashka/issues/1546): add `.contains` for vector and lazy-seq + +## 1.3.178 (2023-04-21) + +- Fix regression with [#1541](https://github.com/babashka/babashka/issues/1541) + +## 1.3.177 (2023-04-21) + +- [#1541](https://github.com/babashka/babashka/issues/1541): respect `bb.edn` + adjacent to invoked file. This eases writing system-global scripts from + projects without using bbin. See [docs](https://book.babashka.org/#_script_adjacent_bb_edn). +- [#1523](https://github.com/babashka/babashka/pull/1523): Reduce the size of the Docker images ([@raszi](https://github.com/raszi)) +- Upgrade deps.clj to v1.11.1.1273 +- Upgrade transit-clj to 1.0.333 +- Add `java.security.cert.CertificateFactory` +- Bump clj-yaml to 1.0.26 +- Bump edamame to 1.3.21 +- Add `UnsupportedOperationException` +- Bump babashka CLI to 0.7.51 +- Bump babashka http-client to 0.2.9 +- Add `--install-exit-handlers` to native-image build to support shutdown hook + SIGTERM + +## 1.3.176 (2023-03-18) + +- Upgrade http-client to 0.1.8, fixes binary file uploads (which messed up the previous release) +- Downgrade org.flatland/ordered to 1.5.9 due to this [issue](https://github.com/clj-commons/ordered/issues/71) + +## 1.3.175 (2023-03-18) + +- [#1507](https://github.com/babashka/babashka/issues/1507): Expose methods on java.lang.VirtualThread ([@lispyclouds](https://github.com/lispyclouds)) +- [#1510](https://github.com/babashka/babashka/issues/1510): add virtual thread interop on `Thread` +- [#1511](https://github.com/babashka/babashka/issues/1511): support for domain sockets +- [#1521](https://github.com/babashka/babashka/issues/1521): push images to GHCR ([@lispyclouds](https://github.com/lispyclouds)) +- Bump edamame to 1.3.20 +- Bump deps.clj to 1.11.1.1257 +- Bump org.flatland/ordered to 1.15.10 +- Support `clojure.lang.MapEntry/create` +- clojure.core.async `go` macro now uses virtual threads +- Bump babashka.cli to 0.6.50 +- Bump http-client to 0.1.7 + +## 1.2.174 (2023-03-01) + +- Use GraalVM 22.3.1 on JDK 19.0.2. This adds virtual thread support. See [demo](https://twitter.com/borkdude/status/1572222344684531717). +- Expose more `jaxax.crypto` classes +- Add more `java.time` and related classes with the goal of supporting [juxt.tick](https://github.com/juxt/tick) ([issue](https://github.com/juxt/tick/issues/86)) +- Compatibility with [kaocha](https://github.com/lambdaisland/kaocha) test runner +- [#1000](https://github.com/babashka/babashka/issues/1000): add lib tests for xforms ([@bobisageek](https://github.com/bobisageek)) +- [#1482](https://github.com/babashka/babashka/issues/1482): make loading of libs thread safe +- [#1487](https://github.com/babashka/babashka/issues/1487): `babashka.tasks/clojure` should be supported without arguments to start a REPL +- [#1496](https://github.com/babashka/babashka/issues/1496): Add `set-agent-send-executor!` and `set-agent-send-off-executor!` +- [#1489](https://github.com/babashka/babashka/issues/1489): Don't overwrite non-empty, non-jar files when writing uberscript/uberjar ([@bobisageek](https://github.com/bobisageek)) +- [#1506](https://github.com/babashka/babashka/issues/1506): `:exec-args` in task should override `:exec-args` on fn metadata +- [#1501](https://github.com/babashka/babashka/issues/1501): equals on deftype +- Add support for `.getWatches` on atoms +- Bump `babashka.fs` to `0.3.17` +- Bump `deps.clj` to `1.11.1.1237` +- Bump `babashka.http-client` to `0.1.5` +- Bump `babashka.cli` to `0.6.46` + +## 1.1.173 (2023-02-04) + +- [#1473](https://github.com/babashka/babashka/issues/1473): support `--config` in other dir + `:local/root` ([@lispyclouds](https://github.com/lispyclouds)) +- Compatibility with `clojure.tools.namespace.repl/refresh` and `clojure.java.classpath` +- `(clojure.lang.RT/baseLoader)` now returns classloader with babashka dependencies on classpath +- Support reading tags from `data_readers.clj` and `data_readers.cljc` +- Don't exit REPL when `babashka.deps/add-deps` fails +- Fix [#1474](https://github.com/babashka/babashka/issues/1474): when `.bb` file is in different artifact, `.clj` file is loaded first if it appears first on classpath +- Support for `*loaded-libs*` and `(loaded-libs)` +- Bump rewrite-clj to `1.1.46` +- Bump http-client to `0.0.3` +- Bump fs to `0.2.15` +- Bump process to `0.4.16` + +## 1.1.172 (2023-01-23) + +- [#1472](https://github.com/babashka/babashka/issues/1472): fix tokenization of `babashka.tasks/clojure`: command was tokenized twice (regression was introduced in `1.0.168`) +- **BREAKING**: Bump `babashka.process`: change default for `:out :append` to `:out :write`. This default is undocumented so the impact should be small. + +## 1.1.171 (2023-01-23) + +- [#1467](https://github.com/babashka/babashka/issues/1467): **BREAKING**: avoid printing results, unless `--prn` is enabled (aside from `-e`, `-o` and `-O`). +- Include [http-client](https://github.com/babashka/http-client) as built-in library +- SCI: support `add-watch` on vars +- Compatibility with [eftest](https://github.com/weavejester/eftest) test runner (see [demo](https://twitter.com/borkdude/status/1616886788898885632)) +- Add classes: + - `java.util.concurrent.Callable` + - `java.util.concurrent.ExecutorService` +- Expose `clojure.main` `main` and `repl-caught` +- Switch `clojure.test/*report-counters*` to ref instead of atom for compatibility with [kaocha](https://github.com/lambdaisland/kaocha) +- Allow `java.io.OutputStream` to be proxied, for [kaocha](https://github.com/lambdaisland/kaocha) +- Support qualified method names in `proxy` and ignore namespace + +## 1.0.170 (2023-01-19) + +- [#1463](https://github.com/babashka/babashka/issues/1463): Add `java.util.jar.Attributes` class ([@jeroenvandijk](https://github.com/jeroenvandijk)) +- [#1456](https://github.com/babashka/babashka/issues/1456): allow `*warn-on-reflection*` and `*unchecked-math*` to be set in socket REPL and nREPL ([@axks](https://github.com/axks)) +- SCI: macroexpansion error location improvement +- Add compatibility with [tab](https://github.com/eerohele/tab) and [solenoid](https://github.com/adam-james-v/solenoid) +- Bump babashka.cli and babashka.fs +- New classes: + - `java.util.jar.Attributes` + - `java.util.concurrent.ThreadFactory` + - `java.lang.Thread$UncaughtExceptionHandler` + - `java.lang.Thread$UncaughtExceptionHandler` + - `java.util.concurrent.BlockingQueue` + - `java.util.concurrent.ArrayBlockingQueue` + - `java.util.concurrent.ThreadFactory` + - `java.lang.Thread$UncaughtExceptionHandler` + - `java.util.concurrent.Semaphore` +- Expose more httpkit.server functions: `with-channel`, `on-close`, `close` + +## 1.0.169 (2023-01-03) + - Implement `ns`, `lazy-seq` as macro - Support `--dev-build` flag in installation script - [#1451](https://github.com/babashka/babashka/issues/1451): Allow passing explicit file and line number to clojure.test ([@matthewdowney](https://github.com/matthewdowney)) @@ -16,6 +262,11 @@ A preview of the next release can be installed from - [#1446](https://github.com/babashka/babashka/issues/1446): add `pprint/code-dispatch` - Update zlib to version `1.2.13` ([@thiagokokada](https://github.com/thiagokokada)) - [#1454](https://github.com/babashka/babashka/issues/1454): Add `babashka.process` to `print-deps` output +- Update `deps.clj` / clojure tools to `1.11.1.1208` +- Add `reader-conditional` function +- Fix pretty printing (with `clojure.pprint`) of vars +- Upgrade built-in `spec.alpha` +- SCI performance improvements: faster JVM interop ## 1.0.168 (2022-12-07) @@ -625,7 +876,7 @@ Babashka.pods: - Add `babashka.task` `System` property [#837](https://github.com/babashka/babashka/issues/837) - Allow thread-first with `shell` like `babashka.process` [#829](https://github.com/babashka/babashka/issues/829) -## 0.4.0 +## 0.4.0 (2021-05-08) Babashka proper: @@ -635,7 +886,7 @@ Babashka proper: [jasentaa](https://github.com/rm-hull/jasentaa) parser combinator library - Update Selmer to 1.12.40 -Sci: +SCI: - Better error msg for protocol not found or class @@ -1205,6 +1456,14 @@ Details about releases prior to v0.1.0 can be found ## Breaking changes +### v1.1.172 + +- Bump `babashka.process`: change default for `:out :append` to `:out :write`. This default is undocumented so the impact should be small. + +### v1.1.171 + +- [#1467](https://github.com/babashka/babashka/issues/1467): avoid printing results, unless `--prn` is enabled (aside from `-e`, `-o` and `-O`). + ### v0.2.4 - Remove cheshire smile functions [#658](https://github.com/babashka/babashka/issues/658) diff --git a/Dockerfile b/Dockerfile index f9ad88b3..d109f1ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,25 +5,25 @@ RUN apt update RUN apt install --no-install-recommends -yy build-essential zlib1g-dev WORKDIR "/opt" -ENV GRAALVM_VERSION="22.3.0" +ENV GRAALVM_VERSION="22" ARG TARGETARCH # Do not set those directly, use TARGETARCH instead ENV BABASHKA_ARCH= ENV GRAALVM_ARCH= RUN if [ "${TARGETARCH}" = "" ] || [ "${TARGETARCH}" = "amd64" ]; then \ - export GRAALVM_ARCH=amd64; export BABASHKA_ARCH=x86_64; \ + export GRAALVM_ARCH=x64; export BABASHKA_ARCH=x86_64; \ elif [ "${TARGETARCH}" = "arm64" ]; then \ export GRAALVM_ARCH=aarch64; \ fi && \ echo "Installing GraalVM for ${GRAALVM_ARCH}" && \ - curl -sLO https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-${GRAALVM_VERSION}/graalvm-ce-java11-linux-${GRAALVM_ARCH}-${GRAALVM_VERSION}.tar.gz && \ - tar -xzf graalvm-ce-java11-linux-${GRAALVM_ARCH}-${GRAALVM_VERSION}.tar.gz && \ - rm graalvm-ce-java11-linux-${GRAALVM_ARCH}-${GRAALVM_VERSION}.tar.gz + curl -sLO https://download.oracle.com/graalvm/21/archive/graalvm-jdk-${GRAALVM_VERSION}_linux-${GRAALVM_ARCH}_bin.tar.gz + mkdir "graalvm-$GRAALVM_VERSION" + tar -xzf graalvm-jdk-${GRAALVM_VERSION}_linux-${GRAALVM_ARCH}_bin.tar.gz -C graalvm --strip-components 1 ARG BABASHKA_XMX="-J-Xmx4500m" -ENV GRAALVM_HOME="/opt/graalvm-ce-java11-${GRAALVM_VERSION}" -ENV JAVA_HOME="/opt/graalvm-ce-java11-${GRAALVM_VERSION}/bin" +ENV GRAALVM_HOME="/opt/graalvm-$GRAALVM_VERSION" +ENV JAVA_HOME="$GRAALVM_HOME/bin" ENV PATH="$JAVA_HOME:$PATH" ENV BABASHKA_XMX=$BABASHKA_XMX @@ -73,7 +73,9 @@ RUN ./script/compile FROM ubuntu:latest RUN apt-get update && apt-get install -y curl \ - && mkdir -p /usr/local/bin + && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /usr/local/bin + COPY --from=BASE /opt/target/metabom.jar /opt/babashka-metabom.jar COPY --from=BASE /opt/bb /usr/local/bin/bb CMD ["bb"] diff --git a/Dockerfile.alpine b/Dockerfile.alpine index f1b10a07..bd32c6e5 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -13,8 +13,8 @@ RUN echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswit # TODO: Run actual native tests when they are ported -RUN bb -e "(curl/get \"https://httpstat.us/200\")" # cURL http test -RUN bb -e "(require '[org.httpkit.client :as http]) (when-let [error (:error @(http/get \"https://httpstat.us/200\"))] (throw error))" # JVM http test +RUN bb -e "(curl/get \"https://clojure.org\")" # cURL http test +RUN bb -e "(require '[org.httpkit.client :as http]) (when-let [error (:error @(http/get \"https://clojure.org\"))] (throw error))" # JVM http test RUN bb -e "(.length \"Hello, Babashka\")" # Java interop test RUN bb -e "(require '[babashka.pods :as pods]) (pods/load-pod 'org.babashka/go-sqlite3 \"0.0.1\") (require '[pod.babashka.go-sqlite3 :as sqlite]) (sqlite/execute! \"/tmp/foo.db\" [\"SELECT 1 + 1\"])" # Pod test @@ -23,7 +23,7 @@ FROM alpine:3 RUN apk --no-cache add curl ca-certificates tar && \ curl -Ls https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.28-r0/glibc-2.28-r0.apk > /tmp/glibc-2.28-r0.apk && \ - apk add --allow-untrusted --force-overwrite /tmp/glibc-2.28-r0.apk + apk add --allow-untrusted --force-overwrite /tmp/glibc-2.28-r0.apk && rm /tmp/glibc-2.28-r0.apk RUN echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf COPY metabom.jar /opt/babashka-metabom.jar diff --git a/Dockerfile.ci b/Dockerfile.ci index 6d8c454b..9fb0afe6 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -2,6 +2,7 @@ FROM ubuntu:latest RUN apt-get update \ && apt-get install -y curl \ + && rm -rf /var/lib/apt/lists/* \ && mkdir -p /usr/local/bin ARG TARGETARCH diff --git a/README.md b/README.md index 75a23601..058d7fc2 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,28 @@ $ ls | bb -i '(filter fs/directory? *input*)' bb took 4ms. ``` +## Support :heart: + +You can support this project via [Github +Sponsors](https://github.com/sponsors/borkdude), +[OpenCollective](https://opencollective.com/babashka), +[Ko-fi](https://ko-fi.com/borkdude) or indirectly via [Clojurists +Together](https://www.clojuriststogether.org/). + +
+ +Top sponsors + +- [Clojurists Together](https://clojuriststogether.org/) +- [Roam Research](https://roamresearch.com/) +- [Nextjournal](https://nextjournal.com/) +- [Toyokumo](https://toyokumo.co.jp/) +- [Cognitect](https://www.cognitect.com/) +- [Kepler16](https://kepler16.com/) +- [Adgoji](https://www.adgoji.com/) + +
+ ## Babashka users See [companies](doc/companies.md) for a list of companies using babashka. @@ -82,7 +104,7 @@ and is unlikely to change. Changes may happen in other parts of babashka, although we will try our best to prevent them. Always check the release notes or [CHANGELOG.md](CHANGELOG.md) before upgrading. -### Talk +## Talk To get an overview of babashka, you can watch this talk ([slides](https://speakerdeck.com/borkdude/babashka-and-the-small-clojure-interpreter-at-clojured-2020)): @@ -93,6 +115,11 @@ To get an overview of babashka, you can watch this talk ([slides](https://speake The [babashka book](https://book.babashka.org) contains detailed information about how to get the most out of babashka scripting. +There is also the book [Babashka Babooka](https://www.braveclojure.com/quests/babooka/), +by Daniel Higginbotham, who has also helped a lot of people learn Clojure with +[Clojure for the Brave and +True](https://www.braveclojure.com/clojure-for-the-brave-and-true/). + ## Examples Read the output from a shell command as a lazy seq of strings: @@ -127,7 +154,7 @@ File `pst.clj`: ``` ``` shell -$ pst.clj +$ bb pst.clj 05:17 ``` @@ -148,11 +175,6 @@ Install: brew install borkdude/brew/babashka - - - - - Upgrade: brew upgrade babashka @@ -190,13 +212,25 @@ linux binary. ### asdf -[asdf](https://github.com/asdf-vm/asdf) is an extendable version manager for linux and macOS. +[asdf](https://github.com/asdf-vm/asdf) is an extendable version manager for linux and macOS. Note that asdf will add significant startup time to any babashka script, consider using [mise](#mise) instead. Babashka can be installed using a plugin as follows: - asdf plugin add babashka + asdf plugin add babashka https://github.com/pitch-io/asdf-babashka asdf install babashka latest +### mise + +[mise](https://mise.jdx.dev/) is a development environment setup tool for linux and macOS. + +Install: + + mise use --global babashka@latest + +Upgrade: + + mise upgrade babashka + ### Windows #### Scoop @@ -283,6 +317,12 @@ You may also download a binary from [Github](https://github.com/babashka/babashka/releases). For linux there is a static binary available which can be used on Alpine. +### CI + +- On Github Actions it's recommended to use [setup-clojure](https://github.com/DeLaGuardo/setup-clojure) with `bb: latest`. +- You can use the [installer script](https://github.com/babashka/babashka#installer-script) on any non-Windows CI system. CircleCI requires `sudo`. +- On Appveyor + Windows you can use a bit of [Powershell](https://github.com/clj-kondo/clj-kondo/blob/39b5cb2b0d3d004c005e8975b6fafe0e314eec68/appveyor.yml#L60-L64). + ## Docker Check out the image on [Docker hub](https://hub.docker.com/r/babashka/babashka/). @@ -358,6 +398,10 @@ https://babashka.org[image:https://raw.githubusercontent.com/babashka/babashka/m
+## Swag + +- [t-shirt](https://www.etsy.com/listing/1241766068/babashka-clj-kondo-nbb-shirt) + ## [Pods](https://github.com/babashka/babashka.pods) Pods are programs that can be used as a Clojure library by @@ -406,11 +450,21 @@ handling of SIGINT and SIGPIPE. This can be done by setting ## Articles, podcasts and videos -- [Babashka: How GraalVM Helped Create a Fast-Starting Scripting Environment for Clojure](https://medium.com/graalvm/babashka-how-graalvm-helped-create-a-fast-starting-scripting-environment-for-clojure-b0fcc38b0746) by Michiel Borkent +- [Blambda analyses sites](https://jmglov.net/blog/2023-01-04-blambda-analyses-sites.html) by Josh Glover +- [The wizard of HOP - How we built the web based HOP CLI Settings Editor using Babashka and Scittle](https://www.gethop.dev/post/the-wizard-of-hop-how-we-built-the-web-based-hop-cli-settings-editor-using-babashka-and-scittle) by Bingen Galartza +- [Simple TUIs with Babashka and Gum](https://rattlin.blog/bbgum.html) by Rattlin.blog +- [Babashka And Dialog Part Ii: Announcing The Bb-Dialog Library](https://www.pixelated-noise.com/blog/2023/01/20/bb-dialog-announcement/index.html) by A.C. Danvers +- [Babashka Babooka](https://www.braveclojure.com/quests/babooka/): Write Command-Line Clojure by Daniel Higginbotham +- [Re-Writing a GlobalProtect OpenConnect VPN Connect script in Babashka](https://tech.toryanderson.com/2023/01/14/re-writing-a-globalprotect-openconnect-vpn-connect-script-in-babashka/) by Tory Anderson +- [Babashka: How GraalVM Helped Create a Fast-Starting Scripting Environment for Clojure](https://medium.com/graalvm/babashka-how-graalvm-helped-create-a-fast-starting-scripting-environment-for-clojure-b0fcc38b0746) by Michiel Borkent (Japanese version [here]((https://logico-jp.io/2023/01/07/babashka-how-graalvm-helped-create-a-fast-starting-scripting-environment-for-clojure/))) - [Adding Prompts To Your Babashka Scripts With Dialog](https://www.pixelated-noise.com/blog/2022/12/09/dialog-and-babashka/index.html) by A.C. Danvers +- [Scraping an HTML dictionary with Babashka and Bootleg](https://blog.exupero.org/scraping-an-html-dictionary-with-babashka-and-bootleg/) by exupero - [Using Babashka to Get Electricity Prices](https://www.karimarttila.fi/clojure/2022/12/04/using-babashka-to-get-electricity-prices.html) by Kari Marttila +- [How to Do Things With Babashka](https://presumably.de/how-to-do-things-with-babashka.html) by Paulus Esterhazy (2022-12) +- [Using nREPL as a system interface](https://yogthos.net/posts/2022-11-26-nREPL-system-interaction.html) by Dmitri Sotnikov - [Manage git hooks with babashka tasks](https://blaster.ai/blog/posts/manage-git-hooks-w-babashka.html) by Mykhaylo Bilyanskyy - [Messing around with babashka](https://ian-muge.medium.com/messing-around-with-babashka-f181a9003faa) by Ian Muge +- Introducing [bbin](https://radsmith.com/bbin) by Radford Smith (2022-09) - [Deleting AWS Glacier vaults with babashka](https://javahippie.net/clojure/2022/07/23/deleting-aws-glacier-vaults-with-babashka.html) by Tim Zöller - [Recursive document transformations with Pandoc and Clojure](https://play.teod.eu/document-transform-pandoc-clojure/) by Teodor Heggelund - [Blambda!](https://jmglov.net/blog/2022-07-03-blambda.html) by Josh Glover @@ -497,47 +551,10 @@ commit](https://github.com/babashka/babashka/commit/02c7c51ad4b2b1ab9aa95c26a744 Thanks to all the people that contributed to babashka: -- [Adgoji](https://www.adgoji.com/) for financial support - [CircleCI](https://circleci.com/) for CI and additional support - [Nikita Prokopov](https://github.com/tonsky) for the logo - [Contributors](https://github.com/babashka/babashka/graphs/contributors) and other users posting issues with bug reports and ideas -- [Github sponsors](https://github.com/sponsors/borkdude) -- [OpenCollective sponsors](https://opencollective.com/babashka) -- [Clojurists Together](https://www.clojuriststogether.org/) - -### Code Contributors - -This project exists thanks to all the people who contribute. [[Contribute](doc/dev.md)]. - - -### Financial Contributors - -#### Github Sponsors - -- [Dig Gashinsky](https://github.com/digash) - -#### OpenCollective -Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/babashka/contribute)] - -##### Individuals - - - -##### Organizations - -Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/babashka/contribute)] - - - - - - - - - - - ## License diff --git a/appveyor.yml b/appveyor.yml index 7e60e61c..16dda71d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,13 +2,13 @@ version: "v-{build}" -image: Visual Studio 2017 +image: Visual Studio 2022 clone_folder: C:\projects\babashka environment: - GRAALVM_HOME: C:\projects\babashka\graalvm\graalvm-ce-java11-22.3.0 - JAVA_HOME: C:\projects\babashka\graalvm\graalvm-ce-java11-22.3.0 + GRAALVM_HOME: C:\projects\babashka\graalvm\graalvm-jdk-22+36.1 + JAVA_HOME: C:\projects\babashka\graalvm\graalvm-jdk-22+36.1 BABASHKA_XMX: "-J-Xmx5g" skip_commits: @@ -23,6 +23,9 @@ cache: clone_script: - cmd: git config --global core.autocrlf true + +- cmd: git config --global core.symlinks true + - ps: >- if(-not $env:APPVEYOR_PULL_REQUEST_NUMBER) { git clone -q --branch=$env:APPVEYOR_REPO_BRANCH https://github.com/$env:APPVEYOR_REPO_NAME.git $env:APPVEYOR_BUILD_FOLDER @@ -36,9 +39,12 @@ clone_script: } - cmd: git submodule update --init --recursive +- cmd: git reset --hard + build_script: +# TODO: Extract the zip by removing the top level folder to remove the hardcoded path for GRAALVM_HOME - cmd: >- - powershell -Command "if (Test-Path('graalvm')) { return } else { (New-Object Net.WebClient).DownloadFile('https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.3.0/graalvm-ce-java11-windows-amd64-22.3.0.zip', 'graalvm.zip') }" + powershell -Command "if (Test-Path('graalvm')) { return } else { (New-Object Net.WebClient).DownloadFile('https://download.oracle.com/graalvm/22/archive/graalvm-jdk-22_windows-x64_bin.zip', 'graalvm.zip') }" powershell -Command "if (Test-Path('graalvm')) { return } else { Expand-Archive graalvm.zip graalvm }" @@ -55,8 +61,6 @@ build_script: # see https://github.com/quarkusio/quarkus/pull/7663 - cmd: >- - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" - call script/uberjar.bat call script/compile.bat @@ -69,11 +73,15 @@ build_script: bb --config .build/bb.edn --deps-root . release-artifact %zip% +before_test: +- cmd: >- set BABASHKA_CLASSPATH= set BABASHKA_TEST_ENV=native - call script/test.bat +test_script: +- cmd: >- + call script/test.bat :windows call script/run_lib_tests.bat diff --git a/babashka.curl b/babashka.curl index 99e6d3ba..e936acd4 160000 --- a/babashka.curl +++ b/babashka.curl @@ -1 +1 @@ -Subproject commit 99e6d3ba7a7252284b43f9de7d91d3433ecfa8f0 +Subproject commit e936acd40544eb637b6041c7e89454b21eb7ee34 diff --git a/babashka.nrepl b/babashka.nrepl index ad763a78..edd3d613 160000 --- a/babashka.nrepl +++ b/babashka.nrepl @@ -1 +1 @@ -Subproject commit ad763a78f1bc327a493ff0b650aa5408ecbf4819 +Subproject commit edd3d613bfb9bf3adabfd0bda5c3f5c6ee85ec20 diff --git a/deps.clj b/deps.clj index 2d185718..aa143f01 160000 --- a/deps.clj +++ b/deps.clj @@ -1 +1 @@ -Subproject commit 2d185718ba2871c96e6cb4a4181d1dcf9d8fde86 +Subproject commit aa143f015469884d91f812e16b977ecccdcc2c04 diff --git a/deps.edn b/deps.edn index 692d7724..e73a12f7 100644 --- a/deps.edn +++ b/deps.edn @@ -17,10 +17,10 @@ "depstar/src" "process/src" "deps.clj/src" "deps.clj/resources" "resources" "sci/resources" - "reify/src"], - :deps {org.clojure/clojure {:mvn/version "1.11.1"}, + "impl-java/src"], + :deps {org.clojure/clojure {:mvn/version "1.11.2"}, org.babashka/sci {:local/root "sci"} - org.babashka/babashka.impl.reify {:mvn/version "0.1.3"} + org.babashka/babashka.impl.java {:mvn/version "0.1.8"} org.babashka/sci.impl.types {:mvn/version "0.0.2"} babashka/babashka.curl {:local/root "babashka.curl"} babashka/fs {:local/root "fs"} @@ -29,28 +29,30 @@ org.clojure/core.async {:mvn/version "1.6.673"}, org.clojure/tools.cli {:mvn/version "1.0.214"}, org.clojure/data.csv {:mvn/version "1.0.0"}, - cheshire/cheshire {:mvn/version "5.11.0"} + cheshire/cheshire {:mvn/version "5.13.0"} org.clojure/data.xml {:mvn/version "0.2.0-alpha8"} - clj-commons/clj-yaml {:mvn/version "0.7.169"} - com.cognitect/transit-clj {:mvn/version "1.0.329"} + clj-commons/clj-yaml {:mvn/version "1.0.27"} + com.cognitect/transit-clj {:mvn/version "1.0.333"} org.clojure/test.check {:mvn/version "1.1.1"} nrepl/bencode {:mvn/version "1.1.0"} seancorfield/next.jdbc {:mvn/version "1.1.610"} org.postgresql/postgresql {:mvn/version "42.2.18"} org.hsqldb/hsqldb {:mvn/version "2.5.1"} datascript/datascript {:mvn/version "1.0.1"} - http-kit/http-kit {:mvn/version "2.6.0-RC1"} + http-kit/http-kit {:mvn/version "2.8.0-RC1"} babashka/clojure-lanterna {:mvn/version "0.9.8-SNAPSHOT"} org.clojure/core.match {:mvn/version "1.0.0"} - hiccup/hiccup {:mvn/version "2.0.0-alpha2"} - rewrite-clj/rewrite-clj {:mvn/version "1.1.45"} - selmer/selmer {:mvn/version "1.12.50"} - com.taoensso/timbre {:mvn/version "6.0.1"} + hiccup/hiccup {:mvn/version "2.0.0-RC1"} + rewrite-clj/rewrite-clj {:mvn/version "1.1.47"} + selmer/selmer {:mvn/version "1.12.59"} + com.taoensso/timbre {:mvn/version "6.5.0"} org.clojure/tools.logging {:mvn/version "1.1.0"} org.clojure/data.priority-map {:mvn/version "1.1.0"} insn/insn {:mvn/version "0.5.2"} org.clojure/core.rrb-vector {:mvn/version "0.1.2"} - org.babashka/cli {:mvn/version "0.6.41"}} + org.babashka/cli {:mvn/version "0.8.59"} + org.babashka/http-client {:mvn/version "0.4.18"} + org.flatland/ordered {:mvn/version "1.15.12"}} :aliases {:babashka/dev {:main-opts ["-m" "babashka.main"]} :profile @@ -64,7 +66,7 @@ {:extra-paths ["process/src" "process/test" "test-resources/lib_tests"] :extra-deps {org.clj-commons/clj-http-lite {:mvn/version "0.4.392"} #_#_org.babashka/spec.alpha {:git/url "https://github.com/babashka/spec.alpha" - :sha "0dec1f88cbde74a0470b454396f09a03adb4ae39"} + :sha "0dec1f88cbde74a0470b454396f09a03adb4ae39"} lambdaisland/regal {:mvn/version "0.0.143"} cprop/cprop {:mvn/version "0.1.16"} comb/comb {:mvn/version "0.1.1"} @@ -106,7 +108,7 @@ exoscale/coax {:mvn/version "1.0.0-alpha14"} orchestra/orchestra {:mvn/version "2021.01.01-1"} expound/expound {:mvn/version "0.8.10"} - integrant/integrant {:mvn/version "0.8.0"} + integrant/integrant {:git/url "https://github.com/weavejester/integrant", :git/sha "a9fd7c02bd7201f36344b47142badc3c3ef22f88"} com.stuartsierra/dependency {:mvn/version "1.0.0"} listora/again {:mvn/version "1.0.0"} org.clojure/tools.gitlibs {:mvn/version "2.4.172"} @@ -165,7 +167,12 @@ :git/sha "1a29775a3d286f9f6fe3f979c78b6e2bf298d5ba"} com.github.rawleyfowler/sluj {:git/url "https://github.com/rawleyfowler/sluj" :git/sha "4a92e772b4e07bf127423448d4140748b5782198" - :deps/manifest :deps}} + :deps/manifest :deps} + net.cgrand/xforms {:git/url "https://github.com/cgrand/xforms" + :git/sha "550dbc150a79c6ecc148d8a7e260e10bc36321c6" + :deps/manifest :deps} + prismatic/plumbing {:git/url "https://github.com/plumatic/plumbing", + :git/sha "424bc704f2db422de34269c139a5494314b3a43b"}} :classpath-overrides {org.clojure/clojure nil org.clojure/spec.alpha nil}} :clj-nvd diff --git a/depstar b/depstar index c419b8c8..2bf9d3c9 160000 --- a/depstar +++ b/depstar @@ -1 +1 @@ -Subproject commit c419b8c82041855d55593c5b561fc7cea8234712 +Subproject commit 2bf9d3c9f15298d7dd9de033674a42f830e23d6f diff --git a/doc/build.md b/doc/build.md index 4de497e0..6f81d189 100644 --- a/doc/build.md +++ b/doc/build.md @@ -3,24 +3,24 @@ ## Prerequisites - Install [lein](https://leiningen.org/) for producing uberjars -- Download [GraalVM](https://www.graalvm.org/downloads/). Currently we use *java11-22.3.0*. +- Download [GraalVM](https://www.graalvm.org/downloads/). Currently we use *jdk-22*. - For Windows, installing Visual Studio 2019 with the "Desktop development with C++" workload is recommended. - Set `$GRAALVM_HOME` to the GraalVM distribution directory. On macOS this can look like: ``` shell - export GRAALVM_HOME=~/Downloads/graalvm-ce-java11-22.3.0/Contents/Home + export GRAALVM_HOME=~/Downloads/graalvm-jdk-21.0.0.1/Contents/Home ``` On linux: ``` shell - export GRAALVM_HOME=~/Downloads/graalvm-ce-java11-22.3.0 + export GRAALVM_HOME=~/Downloads/graalvm-jdk-21.0.0.1 ``` On Windows, from the [Visual Studio 2019 x64 Native Tools Command Prompt](https://github.com/oracle/graal/issues/2116#issuecomment-590470806) or `cmd.exe` (not Powershell): ``` - set GRAALVM_HOME=%USERPROFILE%\Downloads\graalvm-ce-java11-22.3.0 + set GRAALVM_HOME=%USERPROFILE%\Downloads\graalvm-ce-jdk-21.0.0.1 ``` If you are not running from the x64 Native Tools Command Prompt, you will need to set additional environment variables using: ``` @@ -62,7 +62,7 @@ take long to complete. ### Alternative: Build inside Docker -To build a Linux version of babashka, you can use `docker build`, enabling the +To build a Linux version of babashka, you can use `docker build`, enabling the desired features via `--build-arg` like this: ```shell @@ -113,8 +113,8 @@ Babashka supports the following feature flags: | `BABASHKA_FEATURE_ORACLEDB` | Includes the [Oracle](https://www.oracle.com/database/technologies/appdev/jdbc.html) JDBC driver | `false` | | `BABASHKA_FEATURE_DATASCRIPT` | Includes [datascript](https://github.com/tonsky/datascript) | `false` | | `BABASHKA_FEATURE_LANTERNA` | Includes [clojure-lanterna](https://github.com/babashka/clojure-lanterna) | `false` | -| `BABASHKA_FEATURE_LOGGING` | Includes [clojure.tools.logging](https://github.com/clojure/tools.logging) with [taoensso.timbre](https://github.com/ptaoussanis/timbre) as the default implementation| `true` | -| `BABASHKA_FEATURE_PRIORITY_MAP` | Includes [clojure.data.priority-map](https://github.com/clojure/data.priority-map) | `true` | +| `BABASHKA_FEATURE_LOGGING` | Includes [clojure.tools.logging](https://github.com/clojure/tools.logging) with [taoensso.timbre](https://github.com/ptaoussanis/timbre) as the default implementation| `true` | +| `BABASHKA_FEATURE_PRIORITY_MAP` | Includes [clojure.data.priority-map](https://github.com/clojure/data.priority-map) | `true` | Note that httpkit server is currently experimental, the feature flag could be toggled to `false` in a future release. diff --git a/doc/companies.md b/doc/companies.md index b068313a..fe2f79ae 100644 --- a/doc/companies.md +++ b/doc/companies.md @@ -64,6 +64,7 @@ Sponsoring via [Cognitect](https://www.cognitect.com/). - [Fluree](https://flur.ee/) - [Hi](https://www.hi.group/) - [Juxt](https://www.juxt.pro/) +- [Kleene](https://www.kleene.ai/) - [Latacora](https://www.latacora.com/) - [Metosin](https://www.metosin.fi/en/) - [Nextdoc](https://www.nextdoc.io/) diff --git a/doc/dev.md b/doc/dev.md index 872cfa8e..ba8d8ab0 100644 --- a/doc/dev.md +++ b/doc/dev.md @@ -37,7 +37,7 @@ reasons: ## Requirements -You need [lein](https://leiningen.org/) for running JVM tests and/or producing uberjars. For building binaries you need GraalVM. Currently we use java11-22.3.0. +You need [lein](https://leiningen.org/) for running JVM tests and/or producing uberjars. For building binaries you need GraalVM. Currently we use jdk-21.0.0.1 ## Clone repository diff --git a/doc/libraries.csv b/doc/libraries.csv index 7d8c22c1..398b1760 100644 --- a/doc/libraries.csv +++ b/doc/libraries.csv @@ -67,6 +67,7 @@ meta-merge/meta-merge,https://github.com/weavejester/meta-merge metosin/malli,https://github.com/metosin/malli minimallist/minimallist,https://github.com/green-coder/minimallist mvxcvi/arrangement,https://github.com/greglook/clj-arrangement +net.cgrand/xforms,https://github.com/cgrand/xforms orchestra/orchestra,https://github.com/jeaye/orchestra org.babashka/spec.alpha,https://github.com/babashka/spec.alpha org.clj-commons/clj-http-lite,https://github.com/clj-commons/clj-http-lite @@ -83,7 +84,7 @@ org.clojure/math.combinatorics,https://github.com/clojure/math.combinatorics org.clojure/math.numeric-tower,https://github.com/clojure/math.numeric-tower org.clojure/test.check,https://github.com/clojure/test.check org.clojure/tools.gitlibs,https://github.com/clojure/tools.gitlibs -org.clojure/tools.namespace,https://github.com/babashka/tools.namespace +org.clojure/tools.namespace,https://github.com/clojure/tools.namespace postmortem/postmortem,https://github.com/athos/Postmortem prismatic/schema,https://github.com/plumatic/schema progrock/progrock,https://github.com/weavejester/progrock diff --git a/doc/news.md b/doc/news.md index f5eb263c..7b3092ec 100644 --- a/doc/news.md +++ b/doc/news.md @@ -5,8 +5,234 @@ you have anything to add. Also see [#babashka](https://twitter.com/hashtag/babashka?src=hashtag_click&f=live) on Twitter. -## [2022-07](https://twitter.com/search?q=(%23babashka)%20until%3A2022-08-01%20since%3A2022-07-01&src=typed_query&f=live) +## 2023-05 ([Twitter](https://twitter.com/search?q=(%23babashka%20OR%20babashka)%20since%3A2023-05-01%20until%3A2023-06-01&src=typed_query&f=live), [Mastodon](https://mastodon.social/tags/babashka)) +### Releases + +1.3.178 + +Mostly a boring maintenance release with lib upgrades! + +### Events + +- [Babashka-conf](https://babashka.org/conf/) is happening June 10th in +Berlin. Only a few tickets left! See the [schedule](https://babashka.org/conf/schedule.html). Also you can buy a [conf t-shirt](https://www.etsy.com/listing/1475981599/babashka-conf-berlin-2023-t-shirt) now! See Nikita wearing it [here](https://twitter.com/nikitonsky/status/1658066530800742400)! + +Thanks to conference sponsors: + + + +- Babashka is going to the [Strange Loop](https://www.thestrangeloop.com/) conference! + +### Projects + +- [beep-boop](https://github.com/pesterhazy/beep-boop): Audible and visual feedback for test runs +- [panas.example](https://github.com/keychera/panas.example): All htmx examples ported to babashka! +- [utility-scripts](https://github.com/somecho/utility-scripts): A collection of helper scripts for Clojure, Java, Ledger and Taskwarrior. Written in Clojure +- [bb-scripts](https://github.com/techconative/bb-scripts): Babashka scripts for common utilities +- [Launching bb tasks from emacs](https://mastodon.social/@mykhaylo@fosstodon.org/110456087708592838) + +### Articles + +- [Clojure in security: Docker](https://www.juxt.pro/blog/clojure-in-docker/): mentions babashka and clj-kondo +- [Changing my mind: Converting a script from bash to Babashka](https://blog.agical.se/en/posts/changing-my-mind--converting-a-script-from-bash-to-babashka/) +- [How to create a really simple ClojureCLR dependency tool](https://blog.agical.se/en/posts/how-to-create-a-really-simple-clojureclr-dependency-tool/) with babashka +- [Making a resume with Node.js babashka (nbb)](https://yogthos.net/posts/2023-05-12-nbb-resume.html) + +## 2023-04 ([Twitter](https://twitter.com/search?q=(%23babashka%20OR%20babashka)%20since%3A2023-04-01%20until%3A2023-05-01&src=typed_query&f=live), [Mastodon](https://mastodon.social/tags/babashka)) + +[Babashka-conf](https://babashka.org/conf/) is happening June 10th in +Berlin. Save the date and/or submit your babashka/clojure-related talk or workshop +in the CfP! + +### Releases + +1.3.177 + +Biggest highlight: `bb.edn` is now respected relative of a script, no matter the directory you invoke it from! See [docs](https://book.babashka.org/#_script_adjacent_bb_edn). + +### Projects + +- [babashka-dl](https://github.com/mjhika/babashka-dl): simple install script for babashka on windows +- [instaparse-bb](https://github.com/babashka/instaparse-bb): Use instaparse from babashka, a new release + +### Videos + +- [Learning clojure w/ @lispyclouds](https://youtu.be/uBTRLBU-83A): a stream with teej_dv, a neovim core dev + +## 2023-03 ([Twitter](https://twitter.com/search?q=(%23babashka%20OR%20babashka)%20since%3A2023-03-01%20until%3A2023-04-01&src=typed_query&f=top), [Mastodon](https://mastodon.social/tags/babashka)) + +### Releases + +1.3.176, 1.3.175, 1.2.174: + +Biggest highlight: Switch to GraalVM 19 and enable virtual threads! + +### Videos + +- [Blambda! The sound of babashka and AWS colliding](https://pitch.com/public/03fa9c7e-2b0e-45fb-8a22-d4a4d4d79d24), by Josh Glover from Pitch! + +### Projects + +- [babashka.json](https://github.com/babashka/json): JSON abstraction library +- [martian](https://github.com/oliyh/martian) is now babashka compatible! +- [panas.reload](https://github.com/keychera/panas.reload): a hot reload for babashka serving html+css (or htmx) +- [cljs-exif-reader](https://git.sr.ht/~rwv/cljs-exif-reader): Extract information from TIFF and JPEG images (works in babashka, despite the name) + +### Jobs + +- Write babashka at [Cognician](https://twitter.com/RobStuttaford/status/1641694501793038336)! + +## 2023-02 ([Twitter](https://twitter.com/search?q=(%23babashka%20OR%20babashka)%20since%3A2023-02-01%20until%3A2023-03-01&src=typed_query&f=live), [Mastodon](https://mastodon.social/tags/babashka)) + +## Releases + +1.1.173: mostly bugfixes + +### Articles + +- [A technique for live coding simple web pages](https://github.com/whacked/cow/blob/main/a%20technique%20for%20live%20coding%20simple%20web%20pages.md) with babashka + +### Videos + +- [Stockholm Clojure Meetup Feb 23: Blambda! The sound of Babashka and Lambda colliding](https://www.youtube.com/watch?v=NfgYon96dsE) + +### Projects + +- [debux](https://github.com/philoskim/debux) is now babaskha-compatible +- [lines-of-code-bb](https://github.com/matthewdowney/linesofcode-bb): Babashka script to count lines of Clojure code, docs, comments, and more +- [deps-try](https://github.com/eval/deps-try): a babashka-script to try out Clojure libraries in rebel-readline +- [bb-dialog](https://github.com/pixelated-noise/bb-dialog) adds support for `--treeview` +- [A duckduck go CLI with babashka and (bbl)gum](https://mastodon.me.uk/@choffee/109845697304457129) +- [babashka http-client](https://github.com/babashka/http-client) now supports multipart uploads +- [sublime-pretty-edn](https://github.com/oakmac/sublime-pretty-edn): Format, Validate, Minify EDN files in Sublime Text +- [Play console tetris in babashka!](https://twitter.com/borkdude/status/1628473136969576449) +- [kaocha test runner](https://github.com/lambdaisland/kaocha) became babashka compatible! + +## 2023-01 ([Twitter](https://twitter.com/search?q=%28%23babashka%20OR%20babashka%29%20until%3A2023-02-01%20since%3A2023-01-01&src=typed_query&f=live), [Mastodon](https://mastodon.social/tags/babashka)) + +### Releases + +New releases in the past month: 1.0.170 - 1.1.173 +Release highlights: + +- Support for `data_readers.clj(c)` +- Include [http-client](https://github.com/babashka/http-client) as built-in library +- Compatibility with [clojure.tools.namespace.repl/refresh](https://github.com/clojure/tools.namespace) +- Compatibility with [clojure.java.classpath](https://github.com/clojure/java.classpath) (and other libraries which rely on `java.class.path` and `RT/baseLoader`) +- Compatibility with [eftest](https://github.com/weavejester/eftest) test runner (see demo) +- Compatibility with [cljfmt](https://github.com/weavejester/cljfmt) +- Support for `*loaded-libs*` and `(loaded-libs)` +- Support `add-watch` on vars (which adds compatibility with `potemkin.namespaces`) +- BREAKING: make printing of script results explicit with `--prn` + +### Events + +- [Babashka Workshop](https://clojure.stream/workshops/babashka) at ClojureStream with Rahul De +- [Blambda! The sound of Babashka and Lambda colliding](https://www.meetup.com/sthlm-clj/events/291204199/?utm_medium=referral&utm_campaign=share-btn_savedevents_share_modal&utm_source=twitter): sthlm.clj (Stockholm, Sweden) + +### Articles + +- [Babooka: write command line Clojure](https://www.braveclojure.com/quests/babooka/) by Daniel Higginbotham +- [Blambda analyses sites](https://jmglov.net/blog/2023-01-04-blambda-analyses-sites.html) by Josh Glover +- [Babashka: How GraalVM Helped Create a Fast-Starting Scripting Environment for Clojure](https://logico-jp.io/2023/01/07/babashka-how-graalvm-helped-create-a-fast-starting-scripting-environment-for-clojure/) in Japanese +- [The wizard of HOP - How we built the web based HOP CLI Settings Editor using Babashka and Scittle](https://www.gethop.dev/post/the-wizard-of-hop-how-we-built-the-web-based-hop-cli-settings-editor-using-babashka-and-scittle) by Bingen Galartza +- [Simple TUIs with Babashka and Gum](https://rattlin.blog/bbgum.html) by Rattlin.blog +- [Babashka And Dialog Part Ii: Announcing The Bb-Dialog Library](https://www.pixelated-noise.com/blog/2023/01/20/bb-dialog-announcement/index.html) by A.C. Danvers +- [Re-Writing a GlobalProtect OpenConnect VPN Connect script in Babashka](https://tech.toryanderson.com/2023/01/14/re-writing-a-globalprotect-openconnect-vpn-connect-script-in-babashka/) by Tory Anderson + +### Projects + +Projects that were new, had updates or were made compatible with babashka: + +- [asdf-babashka](https://github.com/pitch-io/asdf-babashka): babashka plugin for the asdf version manager +- [babashka-htmx-todoapp](https://github.com/prestancedesign/babashka-htmx-todoapp): Quick example of a todo list SPA using Babashka and HTMX +- [bblgum](https://github.com/lispyclouds/bblgum): An extremely tiny and simple wrapper around charmbracelet/gum +- [bb-dialog](https://github.com/pixelated-noise/bb-dialog): A simple wrapper library for working with dialog from Babashka +- [carve](https://github.com/borkdude/carve): Remove unused Clojure vars +- [chr](https://github.com/ThaddeusJiang/chr): native commands history report for the default terminal users +- [clj-kondo-bb](https://github.com/clj-kondo/clj-kondo-bb): Invoke clj-kondo from babashka scripts! +- [cljfmt](https://github.com/weavejester/cljfmt): A tool for formatting Clojure code +- [drepl](https://github.com/claytn/drepl): Node JS dependency-repl. The node repl you already know with easy library installations +- [instaparse-bb](https://github.com/babashka/instaparse-bb): Wrapper library aroud pod-babashka-instaparse +- [lein2deps](https://github.com/borkdude/lein2deps): Lein project.clj to deps.edn converter +- [neil](https://github.com/babashka/neil): A CLI to add common aliases and features to deps.edn-based projects +- [obsidian-babashka](https://github.com/filipesilva/obsidian-babashka): Run Obsidian Clojure(Script) codeblocks in Babashka +- [pod-babashka-buddy](https://github.com/babashka/pod-babashka-buddy): A pod around buddy core (Cryptographic Api for Clojure) +- [quickblog](https://github.com/borkdude/quickblog): Light-weight static blog engine for Clojure and babashka +- [solenoid](https://github.com/adam-james-v/solenoid): A small clojure tool for making little control UIs while using the REPL! +- [tools.bbuild](https://github.com/babashka/tools.bbuild): babashka version of tools.build +- [weather](https://gist.github.com/yogthos/f86e63b856e1413180b2262024ece977): command line util for grabbing current weather for a city using OpenWeather API + +## [2022-12](https://twitter.com/search?q=%28%23babashka%20OR%20babashka%29%20until%3A2023-01-01%20since%3A2022-12-01&src=typed_query&f=live) + +- Releases: [1.0.168](https://github.com/babashka/babashka/blob/master/CHANGELOG.md). +- [How GraalVM Helped Create a Fast-Starting Scripting Environment for Clojure](https://medium.com/graalvm/babashka-how-graalvm-helped-create-a-fast-starting-scripting-environment-for-clojure-b0fcc38b0746) +- [http-client](https://github.com/babashka/http-client): a new HTTP client library for babashka +- [How to Do Things With Babashka](https://presumably.de/how-to-do-things-with-babashka.html) by Paulus Esterhazy (2022-12) +- [Using Babashka to Get Electricity Prices](https://www.karimarttila.fi/clojure/2022/12/04/using-babashka-to-get-electricity-prices) by Kari Marttila +- [Adding prompts to your babashka scripts with dialog](https://www.pixelated-noise.com/blog/2022/12/09/dialog-and-babashka/index.html) by A.C. Danvers +- [Scraping an HTML dictionary with Babashka and Bootleg](https://blog.exupero.org/scraping-an-html-dictionary-with-babashka-and-bootleg/) by exupero +- [quickblog](https://github.com/borkdude/quickblog) v0.1.0: Light-weight static blog engine for Clojure and babashka +- [bb-excel](https://github.com/kbosompem/bb-excel): Read Excel Files in babashka scripts +- [Get paginated list of issues from gitlab with clojure/babashka](https://gist.github.com/MrGung/29d0547fe45316c3438032fd164d42c6) by Steffen Glückselig +- Install development builds: `bash <(curl https://raw.githubusercontent.com/babashka/babashka/master/install) --dev-build --dir /tmp` +- [JVM interop improvements in bb](https://twitter.com/borkdude/status/1606280110692352001) +- [A little trick to have conditional code for babashka in a .clj file without resorting to .cljc reader conditionals](https://twitter.com/borkdude/status/1599067149187764224) +- [Get Advent of Code input in babashka](https://gist.github.com/jeeger/6e39fea94ce49e33d1fa43f40cc36630) by Jan Seeger +- [Grabbing current weather for a city using OpenWeather API](https://gist.github.com/yogthos/f86e63b856e1413180b2262024ece977) by Dmitri Sotnikov + +## [2022-11](https://twitter.com/search?q=%28%23babashka%29%20until%3A2022-12-01%20since%3A2022-11-01&src=typed_query&f=live) + +- Releases: [1.0.165 - 1.0.167](https://github.com/babashka/babashka/blob/master/CHANGELOG.md). +- Registration for a [Babashka workshop](https://clojure.stream/workshops/babashka) with Rahul De at ClojureStream is now open! +- [Tutkain, a Sublime plugin for clojure based on socket REPL now with support for babashka](https://github.com/eerohele/Tutkain) +- [Manage git hooks in babashka](https://blaster.ai/blog/posts/manage-git-hooks-w-babashka.html) by Mykhaylo Bilyanskyy +- [Messing around with babashka](Messing around with Babashka) by Ian Muge +- [A babashka one liner to inspect data in portal](https://twitter.com/borkdude/status/1597505695800516609) +- [Using nREPL as a system interface](https://yogthos.net/posts/2022-11-26-nREPL-system-interaction.html) by Dmitri Sotnikov +- [deep-diff2](https://github.com/lambdaisland/deep-diff2) is now babashka-compatible! +- [A script to normalize auto-resolved keywords](https://github.com/babashka/babashka/blob/master/examples/normalize-keywords.clj) +- [Sum up page counts of books from Calibre library with babashka](https://gist.github.com/jeeger/d13159fefaee33c771be979639900ebc) by Jan Seeger +- [Tiny babashka script that returns a random clojure doc](https://gist.github.com/CarnunMP/c592cd3b6e711d56ddd4ca7832b9b251) by Carnun Marcus-Page + + +## [2022-10](https://twitter.com/search?q=%28%23babashka%29%20until%3A2022-11-01%20since%3A2022-10-01&src=typed_query&f=live) + +- Releases: [1.0.164](https://github.com/babashka/babashka/blob/master/CHANGELOG.md). +- [bb-pod-racer](https://github.com/justone/bb-pod-racer): Speed up development of Babashka pods by Nate Jones +- [Babashka tasks VSCode plugin](https://marketplace.visualstudio.com/items?itemName=fbeyer.babashka-tasks) by Ferdinand Beyer +- A [PR](https://github.com/nextjournal/clerk/pull/232) to get Clerk working in babashka +- [lein2deps](https://github.com/borkdude/lein2deps): lein to deps.edn converter +- [awyeah-api](https://github.com/grzm/awyeah-api) by Michael Glaesemann v0.8.41 is now available! aws-api for Babashka. Aw yeah! +- [bbssh](https://github.com/epiccastle/bbssh/releases/tag/v0.2.0) by Crispin Wellington, v0.2.0 released +- [safely use rsync --archive --delete to backup a directory](https://gist.github.com/stelcodes/ddc8ff53de2192dca7d3fee1081ddb77) by Stel Abrego + +## [2022-09](https://twitter.com/search?q=%28%23babashka%29%20until%3A2022-10-01%20since%3A2022-09-01&src=typed_query&f=live) + +- Releases: [0.9.162 - 0.10.163](https://github.com/babashka/babashka/blob/master/CHANGELOG.md). +- Introducing [bbin](https://radsmith.com/bbin): a tool to install babashka scripts on your system by Radford Smith +- [bbssh](https://github.com/epiccastle/bbssh): Babashka pod for SSH support by [Epic Castle](https://github.com/epiccastle) +- [Loom virtual threads are coming to babashka](https://twitter.com/borkdude/status/1572222344684531717) +- [Tiny script to cycle through pulseaudio outputs (aka sinks)](https://gist.github.com/stelcodes/7d9136a5839b645b6cd5bc829a9fe541) by Stel Abrego +- [Tetris in the console via pod-babashka-lantera](https://twitter.com/borkdude/status/1569351199404576770) +- [org-mode gets support for babashka](https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=764642f55b7a9821acbabcfa1e2d354afab99be7) +- [docs for combining babashka process with promesa](https://github.com/babashka/process#promesa) + [exoscale/interceptor](https://github.com/exoscale/interceptor) became babashka-compatible! +- [Tutkain, socket REPL Sublime plugin, gets better support for babashka](https://twitter.com/borkdude/status/1568315151404924933) +- [babashka tweepy](https://github.com/davclark/babashka-tweepy): kicking the tires on using the tweepy library from a babashka task by Dav Clark +- [aoc helper](https://github.com/jjcomer/aoc-helper) by Josh Comer +- [Dogfooding blambda part 5](https://jmglov.net/blog/2022-09-02-dogfooding-blambda-logs.html) by Josh Glover + +## [2022-08](https://twitter.com/search?q=%28%23babashka%29%20until%3A2022-09-01%20since%3A2022-08-01&src=typed_query&f=live) +- It's babashka's third birthday on August 9th 2022! +- [etaoin](https://github.com/clj-commons/etaoin), Pure Clojure Webdriver protocol implementation, is now babashka-compatible! +- [xforms](https://github.com/cgrand/xforms) is now babashka-compatible! +- [squint](https://github.com/squint-cljs/squint) and [cherry](https://github.com/squint-cljs/cherry) are CLJS-compilers that work with babashka! + +## [2022-07](https://twitter.com/search?q=%28%23babashka%29%20until%3A2022-08-01%20since%3A2022-07-01&src=typed_query&f=live) + +- Releases: [0.8.157 - 0.9.161](https://github.com/babashka/babashka/blob/master/CHANGELOG.md). - [Recursive document transformations with Pandoc and Clojure](https://play.teod.eu/document-transform-pandoc-clojure/) by Teodor Heggelund - [Babashka toolbox](https://babashka.org/toolbox/): A categorised directory of libraries and tools for Babashka - [Quickblog](https://github.com/borkdude/quickblog): Light-weight static blog engine for Clojure and babashka @@ -23,7 +249,7 @@ Twitter. - [Deleting AWS Glacier vaults with babashka](https://javahippie.net/clojure/2022/07/23/deleting-aws-glacier-vaults-with-babashka.html) by Tim Zöller -## [2022-06](https://twitter.com/search?q=(%23babashka)%20until%3A2022-07-01%20since%3A2022-06-01&src=typed_query&f=live) +## [2022-06](https://twitter.com/search?q=%28%23babashka%29%20until%3A2022-07-01%20since%3A2022-06-01&src=typed_query&f=live) - Releases: [0.8.156](https://github.com/babashka/babashka/blob/master/CHANGELOG.md). - [AWS wiki page](https://github.com/babashka/babashka/wiki/AWS) @@ -37,7 +263,7 @@ Twitter. - [Logseq bb tasks](https://github.com/logseq/bb-tasks): Reusable babashka tasks used by logseq team - [Breakneck Babashka on K8s](Breakneck Babashka on K8s) by Heow Goodman -## [2022-05](https://twitter.com/search?q=(%23babashka)%20until%3A2022-06-01%20since%3A2022-05-01&src=typed_query&f=live) +## [2022-05](https://twitter.com/search?q=%28%23babashka%29%20until%3A2022-06-01%20since%3A2022-05-01&src=typed_query&f=live) - Releases: [0.8.2](https://github.com/babashka/babashka/blob/master/CHANGELOG.md). - [Etaoin](https://github.com/clj-commons/etaoin) moved to clj-commons and now works with babashka as well. @@ -47,7 +273,7 @@ Twitter. - [Quickdoc](https://github.com/borkdude/quickdoc): (Quick and minimal API doc generation for Clojure - [Awyeah-api](https://github.com/grzm/awyeah-api) - Cognitect's aws-api for babashka -## [2022-04](https://twitter.com/search?q=(%23babashka)%20until%3A2022-05-01%20since%3A2022-04-01&src=typed_query&f=live) +## [2022-04](https://twitter.com/search?q=%28%23babashka%29%20until%3A2022-05-01%20since%3A2022-04-01&src=typed_query&f=live) - Releases: [0.8.0 - 0.8.1](https://github.com/babashka/babashka/blob/master/CHANGELOG.md). - [Babashka and Clojure](https://youtu.be/ZvOs5Ele6VE) by Rahul Dé at North Virginia Linux Users Group @@ -55,7 +281,7 @@ Twitter. - Control Chrome via devtools using [clj-chrome-devtools](https://github.com/tatut/clj-chrome-devtools/blob/master/bb.clj) which runs with bb! - Use pods directly in `bb.edn`: [tweet](https://twitter.com/borkdude/status/1510995356229767172) -## [2022-03](https://twitter.com/search?q=(%23babashka)%20until%3A2022-04-01%20since%3A2022-03-01&src=typed_query&f=live) +## [2022-03](https://twitter.com/search?q=%28%23babashka%29%20until%3A2022-04-01%20since%3A2022-03-01&src=typed_query&f=live) - Releases: [0.7.7 - 0.7.8](https://github.com/babashka/babashka/blob/master/CHANGELOG.md). - [Create a password manager with Clojure using Babashka, sqlite, honeysql and stash](https://youtu.be/jm0RXmyjRJ8) by Daniel Amber @@ -65,7 +291,7 @@ Twitter. - The [loom](https://github.com/aysylu/loom) library is now compatible [(tweet)](https://twitter.com/borkdude/status/1502237220811550723) - The [at-at](https://github.com/overtone/at-at) library is now compatible -## [2022-02](https://twitter.com/search?q=(%23babashka)%20until%3A2022-03-01%20since%3A2022-02-01&src=typed_query&f=live) +## [2022-02](https://twitter.com/search?q=%28%23babashka%29%20until%3A2022-03-01%20since%3A2022-02-01&src=typed_query&f=live) - Releases: [0.7.5 - 0.7.6](https://github.com/babashka/babashka/blob/master/CHANGELOG.md). - [Spire is available as a babashka pod](https://twitter.com/epic_castle/status/1496784352256008194) @@ -78,7 +304,7 @@ Twitter. - [Apptemplate](https://github.com/redstarssystems/apptemplate): Application project template for Clojure featuring bb tasks -## [2022-01](https://twitter.com/search?f=live&q=(%23babashka)%20until%3A2022-02-01%20since%3A2022-01-01&src=typed_query) +## [2022-01](https://twitter.com/search?f=live&q=%28%23babashka%29%20until%3A2022-02-01%20since%3A2022-01-01&src=typed_query) - Releases: [0.7.4](https://github.com/babashka/babashka/blob/master/CHANGELOG.md). - [Babashka dev builds](https://github.com/babashka/babashka-dev-builds) diff --git a/doc/projects.md b/doc/projects.md index f27bbe9e..70f4a59e 100644 --- a/doc/projects.md +++ b/doc/projects.md @@ -66,6 +66,7 @@ The following libraries and projects are known to work with babashka. - [Meander](#meander) - [Schema](#schema) - [Sluj](#sluj) + - [malli-cli](#malli-cli) - [Pods](#pods) - [Projects](#projects-1) - [babashka-test-action](#babashka-test-action) @@ -833,6 +834,10 @@ Clojure(Script) library for declarative data description and validation Sluj is a very small library for converting strings of UTF-16 text to slugs. A slug is a piece of text that is URL safe. +### [malli-cli](https://github.com/piotr-yuxuan/malli-cli) + +Configuration and CLI powertool with `metosin/malli`. + ## Pods [Babashka pods](https://github.com/babashka/babashka.pods) are programs that can diff --git a/feature-httpkit-server/babashka/impl/httpkit_server.clj b/feature-httpkit-server/babashka/impl/httpkit_server.clj index 42608208..9a087d88 100644 --- a/feature-httpkit-server/babashka/impl/httpkit_server.clj +++ b/feature-httpkit-server/babashka/impl/httpkit_server.clj @@ -15,4 +15,8 @@ 'send-checked-websocket-handshake! (copy-var server/send-checked-websocket-handshake! sns) 'send-websocket-handshake! (copy-var server/send-websocket-handshake! sns) 'as-channel (copy-var server/as-channel sns) - 'send! (copy-var server/send! sns)}) + 'send! (copy-var server/send! sns) + 'with-channel (copy-var server/with-channel sns) + 'on-close (copy-var server/on-close sns) + 'close (copy-var server/close sns)} + ) diff --git a/feature-logging/babashka/impl/logging.clj b/feature-logging/babashka/impl/logging.clj index 25e1221d..5eff64c2 100644 --- a/feature-logging/babashka/impl/logging.clj +++ b/feature-logging/babashka/impl/logging.clj @@ -13,56 +13,78 @@ (defn- fline [and-form] (:line (meta and-form))) +(defonce callsite-counter + (enc/counter)) + (defmacro log! ; Public wrapper around `-log!` - "Core low-level log macro. Useful for tooling, etc. - * `level` - must eval to a valid logging level - * `msg-type` - must eval to e/o #{:p :f nil} - * `opts` - ks e/o #{:config :?err :?ns-str :?file :?line :?base-data :spying?} - Supports compile-time elision when compile-time const vals - provided for `level` and/or `?ns-str`." - [level msg-type args & [opts]] - (have [:or nil? sequential?] args) ; To allow -> (delay [~@args]) - (let [{:keys [?ns-str] :or {?ns-str (str @sci/ns)}} opts] - ;; level, ns may/not be compile-time consts: - (when-not (timbre/-elide? level ?ns-str) - (let [{:keys [config ?err ?file ?line ?base-data spying?] - :or {config 'taoensso.timbre/*config* - ?err :auto ; => Extract as err-type v0 - ?file @sci/file - ;; NB waiting on CLJ-865: - ?line (fline &form)}} opts + "Core low-level log macro. Useful for tooling/library authors, etc. - ?file (when (not= ?file "NO_SOURCE_PATH") ?file) + * `level` - must eval to a valid logging level + * `msg-type` - must eval to e/o #{:p :f nil} + * `args` - arguments seq (ideally vec) for logging call + * `opts` - ks e/o #{:config ?err ?base-data spying? + :?ns-str :?file :?line :?column} - ;; Identifies this particular macro expansion; note that this'll - ;; be fixed for any fns wrapping `log!` (notably `tools.logging`, - ;; `slf4j-timbre`, etc.): - callsite-id - (hash [level msg-type args ; Unevaluated args (arg forms) - ?ns-str ?file ?line (rand)])] + Supports compile-time elision when compile-time const vals + provided for `level` and/or `?ns-str`. - `(taoensso.timbre/-log! ~config ~level ~?ns-str ~?file ~?line ~msg-type ~?err - (delay [~@args]) ~?base-data ~callsite-id ~spying?))))) + Logging wrapper examples: + + (defn log-wrapper-fn [& args] (timbre/log! :info :p args)) + (defmacro log-wrapper-macro [& args] (timbre/keep-callsite `(timbre/log! :info :p ~args)))" + + ([{:as opts + :keys [loc level msg-type args vargs + config ?err ?base-data spying?] + :or + {config 'taoensso.timbre/*config* + ?err :auto}}] + + (have [:or nil? sequential? symbol?] args) + (let [callsite-id (callsite-counter) + {:keys [line column]} (merge (meta &form) loc) + {:keys [ns file line column]} {:ns @sci/ns :file @sci/file :line line :column column} + ns (or (get opts :?ns-str) ns) + file (or (get opts :?file) file) + line (or (get opts :?line) line) + column (or (get opts :?column) column) + + elide? (and #_(enc/const-forms? level ns) (timbre/-elide? level ns))] + + (when-not elide? + (let [vargs-form + (or vargs + (if (symbol? args) + `(taoensso.timbre/-ensure-vec ~args) + `[ ~@args]))] + + ;; Note pre-resolved expansion + `(taoensso.timbre/-log! ~config ~level ~ns ~file ~line ~column ~msg-type ~?err + (delay ~vargs-form) ~?base-data ~callsite-id ~spying?))))) + + ([level msg-type args & [opts]] + (let [{:keys [line column]} (merge (meta &form)) + {:keys [ns file line column]} {:ns @sci/ns :file @sci/file :line line :column column} + loc {:ns ns :file file :line line :column column} + opts (assoc (conj {:loc loc} opts) + :level level, :msg-type msg-type, :args args)] + `(taoensso.timbre/log! ~opts)))) (defn make-ns [ns sci-ns ks] (reduce (fn [ns-map [var-name var]] (let [m (meta var) - no-doc (:no-doc m) doc (:doc m) arglists (:arglists m)] - (if no-doc ns-map - (assoc ns-map var-name - (sci/new-var (symbol var-name) @var - (cond-> {:ns sci-ns - :name (:name m)} - (:macro m) (assoc :macro true) - doc (assoc :doc doc) - arglists (assoc :arglists arglists))))))) + (assoc ns-map var-name + (sci/new-var (symbol var-name) @var + (cond-> {:ns sci-ns + :name (:name m)} + (:macro m) (assoc :macro true) + doc (assoc :doc doc) + arglists (assoc :arglists arglists)))))) {} (select-keys (ns-publics ns) ks))) -(def atomic-println @#'appenders/atomic-println) - (defn println-appender "Returns a simple `println` appender for Clojure/Script. Use with ClojureScript requires that `cljs.core/*print-fn*` be set. @@ -92,7 +114,7 @@ :*err* @sci/err stream)] (binding [*out* stream] - (atomic-println (force output_)))))})) + (enc/println-atomic (force output_)))))})) (def default-config (assoc-in timbre/*config* [:appenders :println] (println-appender {:stream :auto}))) @@ -127,7 +149,13 @@ 'merge-config! (sci/copy-var merge-config! tns) 'set-level! (sci/copy-var set-level! tns) 'println-appender (sci/copy-var println-appender tns) - '-log-and-rethrow-errors (sci/copy-var -log-and-rethrow-errors tns))) + '-log-and-rethrow-errors (sci/copy-var -log-and-rethrow-errors tns) + '-ensure-vec (sci/copy-var enc/ensure-vec tns))) + +(def timbre-appenders-namespace + (let [tan (sci/create-ns 'taoensso.timbre.appenders.core nil)] + {'println-appender (sci/copy-var println-appender tan) + 'spit-appender (sci/copy-var #_:clj-kondo/ignore timbre/spit-appender tan)})) ;;;; clojure.tools.logging diff --git a/feature-selmer/babashka/impl/selmer.clj b/feature-selmer/babashka/impl/selmer.clj index 32aeba3e..155c8bed 100644 --- a/feature-selmer/babashka/impl/selmer.clj +++ b/feature-selmer/babashka/impl/selmer.clj @@ -4,6 +4,7 @@ [babashka.impl.common :refer [ctx]] [sci.core :as sci] [selmer.filters :as filters] + [selmer.filter-parser :as fp] [selmer.parser] [selmer.tags :as tags] [selmer.util :as util] @@ -83,12 +84,31 @@ (apply merge) (selmer.parser/render ~s))) +(defn resolve-arg + "Resolves an arg as passed to an add-tag! handler using the provided + context-map. + + A custom tag handler will receive a seq of args as its first argument. + With this function, you can selectively resolve one or more of those args + so that if they contain literals, the literal value is returned, and if they + contain templates of any sort, which can itself have variables, filters or + tags in it, they will be returned resolved, applied and rendered. + + Example: + (resolve-arg {{header-name|upper}} {:header-name \"My Page\"}) + => \"MY PAGE\"" + [arg context-map] + (if (fp/literal? arg) + (fp/parse-literal arg) + (render arg context-map))) + (def selmer-parser-namespace (-> selmer-parser-ns (assoc 'render-file (sci/copy-var render-file spns) 'render (sci/copy-var render spns) 'render-template (sci/copy-var render-template spns) 'resolve-var-from-kw (sci/copy-var resolve-var-from-kw spns) + 'resolve-arg (sci/copy-var resolve-arg spns ) '<< (sci/copy-var << spns)))) (def stns (sci/create-ns 'selmer.tags nil)) @@ -105,7 +125,9 @@ (def selmer-filters-namespace {'add-filter! (sci/copy-var filters/add-filter! sfns) - 'remove-filter! (sci/copy-var filters/remove-filter! sfns)}) + 'remove-filter! (sci/copy-var filters/remove-filter! sfns) + 'get-filter (sci/copy-var filters/get-filter sfns) + 'filters (sci/copy-var filters/filters sfns)}) (defn turn-off-escaping! [] (sci/alter-var-root escape-variables (constantly false))) diff --git a/feature-yaml/babashka/impl/ordered.clj b/feature-yaml/babashka/impl/ordered.clj index a0c87a97..bdb9c7b3 100644 --- a/feature-yaml/babashka/impl/ordered.clj +++ b/feature-yaml/babashka/impl/ordered.clj @@ -1,9 +1,14 @@ (ns babashka.impl.ordered {:no-doc true} (:require [flatland.ordered.map :as omap] + [flatland.ordered.set :as oset] [sci.core :as sci])) (def omap-ns (sci/create-ns 'flatland.ordered.map nil)) +(def oset-ns (sci/create-ns 'flatland.ordered.set nil)) (def ordered-map-ns {'ordered-map (sci/copy-var omap/ordered-map omap-ns)}) + +(def ordered-set-ns + {'ordered-set (sci/copy-var oset/ordered-set oset-ns)}) diff --git a/fs b/fs index d3226ccc..8658cab4 160000 --- a/fs +++ b/fs @@ -1 +1 @@ -Subproject commit d3226cccd9898eba5adb50dfcc30a7223ff5c8cc +Subproject commit 8658cab4981158dfd439b55b5932a276c4b65bf2 diff --git a/reify/.dir-locals.el b/impl-java/.dir-locals.el similarity index 100% rename from reify/.dir-locals.el rename to impl-java/.dir-locals.el diff --git a/reify/bb.edn b/impl-java/bb.edn similarity index 100% rename from reify/bb.edn rename to impl-java/bb.edn diff --git a/reify/build.clj b/impl-java/build.clj similarity index 84% rename from reify/build.clj rename to impl-java/build.clj index c0493917..b2688a9c 100644 --- a/reify/build.clj +++ b/impl-java/build.clj @@ -2,8 +2,8 @@ (:require [build.reify2 :as reify2] [clojure.tools.build.api :as b])) -(def lib 'org.babashka/babashka.impl.reify) -(def version "0.1.3") +(def lib 'org.babashka/babashka.impl.java) +(def version "0.1.8") (def class-dir "target/classes") (def basis (b/create-basis {:project "deps.edn"})) (def jar-file (format "target/%s-%s.jar" (name lib) version)) @@ -14,7 +14,13 @@ (defn gen-classes [_] (reify2/gen-classes nil)) +(defn compile-java [_] + (b/javac {:src-dirs ["src-java"] + :class-dir class-dir + :basis basis})) + (defn jar [_] + (compile-java nil) (gen-classes nil) (b/write-pom {:class-dir class-dir :lib lib diff --git a/reify/build/reify2.clj b/impl-java/build/reify2.clj similarity index 100% rename from reify/build/reify2.clj rename to impl-java/build/reify2.clj diff --git a/reify/deps.edn b/impl-java/deps.edn similarity index 100% rename from reify/deps.edn rename to impl-java/deps.edn diff --git a/impl-java/src-java/babashka/impl/URLClassLoader.java b/impl-java/src-java/babashka/impl/URLClassLoader.java new file mode 100644 index 00000000..9416f2b9 --- /dev/null +++ b/impl-java/src-java/babashka/impl/URLClassLoader.java @@ -0,0 +1,94 @@ +// This file is mostly a workaround for https://github.com/oracle/graal/issues/1956 + +package babashka.impl; + +import java.util.WeakHashMap; +import java.io.*; +import java.util.Objects; +import java.net.*; +import java.util.jar.*; + +public class URLClassLoader extends java.net.URLClassLoader implements Closeable { + + private WeakHashMap + closeables = new WeakHashMap<>(); + + public URLClassLoader(java.net.URL[] urls) { + super(urls); + } + + public URLClassLoader(java.net.URL[] urls, java.net.URLClassLoader parent) { + super(urls, parent); + } + + public void _addURL(java.net.URL url) { + super.addURL(url); + } + + // calling super.getResource() returned nil in native-image + public java.net.URL getResource(String name) { + return findResource(name); + } + + // calling super.getResourceAsStream() returned nil in native-image + public InputStream getResourceAsStream(String name) { + Objects.requireNonNull(name); + URL url = getResource(name); + try { + if (url == null) { + return null; + } + URLConnection urlc = url.openConnection(); + InputStream is = urlc.getInputStream(); + if (urlc instanceof JarURLConnection) { + JarFile jar = ((JarURLConnection)urlc).getJarFile(); + synchronized (closeables) { + if (!closeables.containsKey(jar)) { + closeables.put(jar, null); + } + } + } else { + synchronized (closeables) { + closeables.put(is, null); + } + } + return is; + } catch (IOException e) { + return null; + } + } + + public java.util.Enumeration getResources(String name) throws java.io.IOException { + return findResources(name); + } + + public void close() throws IOException { + super.close(); + + java.util.List errors = new java.util.ArrayList(); + + synchronized (closeables) { + java.util.Set keys = closeables.keySet(); + for (Closeable c : keys) { + try { + c.close(); + } catch (IOException ex) { + errors.add(ex); + } + } + closeables.clear(); + } + + if (errors.isEmpty()) { + return; + } + + IOException firstEx = errors.remove(0); + + for (IOException error: errors) { + firstEx.addSuppressed(error); + } + throw firstEx; + } + +} diff --git a/reify/src/babashka/impl/reify2.clj b/impl-java/src/babashka/impl/reify2.clj similarity index 100% rename from reify/src/babashka/impl/reify2.clj rename to impl-java/src/babashka/impl/reify2.clj diff --git a/reify/src/babashka/impl/reify2/interfaces.clj b/impl-java/src/babashka/impl/reify2/interfaces.clj similarity index 87% rename from reify/src/babashka/impl/reify2/interfaces.clj rename to impl-java/src/babashka/impl/reify2/interfaces.clj index a94feec3..da99f041 100644 --- a/reify/src/babashka/impl/reify2/interfaces.clj +++ b/impl-java/src/babashka/impl/reify2/interfaces.clj @@ -31,4 +31,7 @@ java.lang.Comparable javax.net.ssl.X509TrustManager clojure.lang.LispReader$Resolver - sun.misc.SignalHandler]) + sun.misc.SignalHandler + java.util.concurrent.ThreadFactory + java.lang.Thread$UncaughtExceptionHandler + java.util.concurrent.Callable]) diff --git a/install b/install index 349405b0..0e9796cf 100755 --- a/install +++ b/install @@ -96,7 +96,11 @@ IFS='.' read -ra VER <<< "${version//-SNAPSHOT/}" vernum=$(printf "%03d%03d%03d" "${VER[0]}" "${VER[1]}" "${VER[2]}") case "$(uname -m)" in - aarch64) arch=aarch64;; + aarch64) arch=aarch64 + if [[ "$platform" == "linux" ]]; then + static_binary="true" + fi + ;; arm64) if [[ 10#$vernum -le 10#000008002 ]]; then arch="amd64" else diff --git a/pods b/pods index decf7910..717cef7a 160000 --- a/pods +++ b/pods @@ -1 +1 @@ -Subproject commit decf791000081ca9e6d2fbea9f20a0aa3fae902e +Subproject commit 717cef7af5cb1c1b091bd10e012b2e71b7b8b9bc diff --git a/process b/process index 90e4cf0b..7b025841 160000 --- a/process +++ b/process @@ -1 +1 @@ -Subproject commit 90e4cf0b0cc7856f8c39591c3350cdf156d11042 +Subproject commit 7b02584145992832c4a50f4f571b009ff093585d diff --git a/project.clj b/project.clj index f5794d71..4814baeb 100644 --- a/project.clj +++ b/project.clj @@ -11,35 +11,40 @@ "babashka.core/src" "babashka.nrepl/src" "depstar/src" "process/src" "deps.clj/src" "deps.clj/resources" - "reify/src"] + "impl-java/src"] ;; for debugging Reflector.java code: ;; :java-source-paths ["sci/reflector/src-java"] :java-source-paths ["src-java"] :resource-paths ["resources" "sci/resources"] - :test-selectors {:default (complement :windows-only) - :windows (complement :skip-windows)} - :dependencies [[org.clojure/clojure "1.11.1"] - [borkdude/edamame "1.0.16"] + :test-selectors {:default (complement (some-fn :windows-only :flaky)) + :windows (complement (some-fn :skip-windows :flaky)) + :non-flaky (complement :flaky) + :flaky :flaky} + :jvm-opts ["--enable-preview"] + :dependencies [[org.clojure/clojure "1.11.2"] + [borkdude/edamame "1.4.24"] [borkdude/graal.locking "0.0.2"] [org.clojure/tools.cli "1.0.214"] - [cheshire "5.11.0"] + [cheshire "5.13.0"] [nrepl/bencode "1.1.0"] [borkdude/sci.impl.reflector "0.0.1"] [org.babashka/sci.impl.types "0.0.2"] - [org.babashka/babashka.impl.reify "0.1.3"] + [org.babashka/babashka.impl.java "0.1.8"] [org.clojure/core.async "1.6.673"] [org.clojure/test.check "1.1.1"] [com.github.clj-easy/graal-build-time "0.1.0"] - [rewrite-clj/rewrite-clj "1.1.45"] + [rewrite-clj/rewrite-clj "1.1.47"] [insn/insn "0.5.2"] - [org.babashka/cli "0.6.41"]] + [org.babashka/cli "0.8.59"] + [org.babashka/http-client "0.4.18"]] :plugins [[org.kipz/lein-meta-bom "0.1.1"]] :metabom {:jar-name "metabom.jar"} :profiles {:feature/xml {:source-paths ["feature-xml"] :dependencies [[org.clojure/data.xml "0.2.0-alpha8"]]} :feature/yaml {:source-paths ["feature-yaml"] - :dependencies [[clj-commons/clj-yaml "0.7.169" - #_#_clj-commons/clj-yaml "0.7.110"]]} + :dependencies [[clj-commons/clj-yaml "1.0.27" + :exclusions [org.flatland/ordered]#_#_clj-commons/clj-yaml "0.7.110"] + [org.flatland/ordered "1.15.12"]]} :feature/jdbc {:source-paths ["feature-jdbc"] :dependencies [[seancorfield/next.jdbc "1.1.610"]]} :feature/sqlite [:feature/jdbc {:dependencies [[org.xerial/sqlite-jdbc "3.36.0.3"]]}] @@ -50,25 +55,25 @@ :feature/csv {:source-paths ["feature-csv"] :dependencies [[org.clojure/data.csv "1.0.0"]]} :feature/transit {:source-paths ["feature-transit"] - :dependencies [[com.cognitect/transit-clj "1.0.329"]]} + :dependencies [[com.cognitect/transit-clj "1.0.333"]]} :feature/datascript {:source-paths ["feature-datascript"] :dependencies [[datascript "1.3.10"]]} :feature/httpkit-client {:source-paths ["feature-httpkit-client"] - :dependencies [[http-kit "2.6.0-RC1"]]} + :dependencies [[http-kit "2.8.0-RC1"]]} :feature/httpkit-server {:source-paths ["feature-httpkit-server"] - :dependencies [[http-kit "2.6.0-RC1"]]} + :dependencies [[http-kit "2.8.0-RC1"]]} :feature/lanterna {:source-paths ["feature-lanterna"] :dependencies [[babashka/clojure-lanterna "0.9.8-SNAPSHOT"]]} :feature/core-match {:source-paths ["feature-core-match"] :dependencies [[org.clojure/core.match "1.0.0"]]} :feature/hiccup {:source-paths ["feature-hiccup"] - :dependencies [[hiccup/hiccup "2.0.0-alpha2"]]} + :dependencies [[hiccup/hiccup "2.0.0-RC1"]]} :feature/test-check {:source-paths ["feature-test-check"]} :feature/spec-alpha {:source-paths ["feature-spec-alpha"]} :feature/selmer {:source-paths ["feature-selmer"] - :dependencies [[selmer/selmer "1.12.50"]]} + :dependencies [[selmer/selmer "1.12.59"]]} :feature/logging {:source-paths ["feature-logging"] - :dependencies [[com.taoensso/timbre "6.0.1"] + :dependencies [[com.taoensso/timbre "6.5.0"] [org.clojure/tools.logging "1.1.0"]]} :feature/priority-map {:source-paths ["feature-priority-map"] :dependencies [[org.clojure/data.priority-map "1.1.0"]]} @@ -92,7 +97,8 @@ :feature/logging :feature/priority-map :feature/rrb-vector - {:dependencies [[com.clojure-goes-fast/clj-async-profiler "0.5.0"] + {:dependencies [[borkdude/rewrite-edn "0.4.6"] + [com.clojure-goes-fast/clj-async-profiler "0.5.0"] [com.opentable.components/otj-pg-embedded "0.13.3"] [nubank/matcher-combinators "3.6.0"]]}] :uberjar {:global-vars {*assert* false} diff --git a/resources/BABASHKA_RELEASED_VERSION b/resources/BABASHKA_RELEASED_VERSION index 6aafafcc..d6778277 100644 --- a/resources/BABASHKA_RELEASED_VERSION +++ b/resources/BABASHKA_RELEASED_VERSION @@ -1 +1 @@ -1.0.168 \ No newline at end of file +1.3.190 \ No newline at end of file diff --git a/resources/BABASHKA_VERSION b/resources/BABASHKA_VERSION index a3361b37..3a1ea432 100644 --- a/resources/BABASHKA_VERSION +++ b/resources/BABASHKA_VERSION @@ -1 +1 @@ -1.0.169-SNAPSHOT \ No newline at end of file +1.3.191-SNAPSHOT \ No newline at end of file diff --git a/resources/META-INF/babashka/deps.edn b/resources/META-INF/babashka/deps.edn index 692d7724..663c383b 100644 --- a/resources/META-INF/babashka/deps.edn +++ b/resources/META-INF/babashka/deps.edn @@ -17,10 +17,10 @@ "depstar/src" "process/src" "deps.clj/src" "deps.clj/resources" "resources" "sci/resources" - "reify/src"], - :deps {org.clojure/clojure {:mvn/version "1.11.1"}, + "impl-java/src"], + :deps {org.clojure/clojure {:mvn/version "1.11.2"}, org.babashka/sci {:local/root "sci"} - org.babashka/babashka.impl.reify {:mvn/version "0.1.3"} + org.babashka/babashka.impl.java {:mvn/version "0.1.8"} org.babashka/sci.impl.types {:mvn/version "0.0.2"} babashka/babashka.curl {:local/root "babashka.curl"} babashka/fs {:local/root "fs"} @@ -29,28 +29,30 @@ org.clojure/core.async {:mvn/version "1.6.673"}, org.clojure/tools.cli {:mvn/version "1.0.214"}, org.clojure/data.csv {:mvn/version "1.0.0"}, - cheshire/cheshire {:mvn/version "5.11.0"} + cheshire/cheshire {:mvn/version "5.13.0"} org.clojure/data.xml {:mvn/version "0.2.0-alpha8"} - clj-commons/clj-yaml {:mvn/version "0.7.169"} - com.cognitect/transit-clj {:mvn/version "1.0.329"} + clj-commons/clj-yaml {:mvn/version "1.0.27"} + com.cognitect/transit-clj {:mvn/version "1.0.333"} org.clojure/test.check {:mvn/version "1.1.1"} nrepl/bencode {:mvn/version "1.1.0"} seancorfield/next.jdbc {:mvn/version "1.1.610"} org.postgresql/postgresql {:mvn/version "42.2.18"} org.hsqldb/hsqldb {:mvn/version "2.5.1"} datascript/datascript {:mvn/version "1.0.1"} - http-kit/http-kit {:mvn/version "2.6.0-RC1"} + http-kit/http-kit {:mvn/version "2.8.0-RC1"} babashka/clojure-lanterna {:mvn/version "0.9.8-SNAPSHOT"} org.clojure/core.match {:mvn/version "1.0.0"} - hiccup/hiccup {:mvn/version "2.0.0-alpha2"} - rewrite-clj/rewrite-clj {:mvn/version "1.1.45"} - selmer/selmer {:mvn/version "1.12.50"} - com.taoensso/timbre {:mvn/version "6.0.1"} + hiccup/hiccup {:mvn/version "2.0.0-RC1"} + rewrite-clj/rewrite-clj {:mvn/version "1.1.47"} + selmer/selmer {:mvn/version "1.12.59"} + com.taoensso/timbre {:mvn/version "6.5.0"} org.clojure/tools.logging {:mvn/version "1.1.0"} org.clojure/data.priority-map {:mvn/version "1.1.0"} insn/insn {:mvn/version "0.5.2"} org.clojure/core.rrb-vector {:mvn/version "0.1.2"} - org.babashka/cli {:mvn/version "0.6.41"}} + org.babashka/cli {:mvn/version "0.8.58"} + org.babashka/http-client {:mvn/version "0.4.16"} + org.flatland/ordered {:mvn/version "1.15.12"}} :aliases {:babashka/dev {:main-opts ["-m" "babashka.main"]} :profile @@ -64,7 +66,7 @@ {:extra-paths ["process/src" "process/test" "test-resources/lib_tests"] :extra-deps {org.clj-commons/clj-http-lite {:mvn/version "0.4.392"} #_#_org.babashka/spec.alpha {:git/url "https://github.com/babashka/spec.alpha" - :sha "0dec1f88cbde74a0470b454396f09a03adb4ae39"} + :sha "0dec1f88cbde74a0470b454396f09a03adb4ae39"} lambdaisland/regal {:mvn/version "0.0.143"} cprop/cprop {:mvn/version "0.1.16"} comb/comb {:mvn/version "0.1.1"} @@ -106,7 +108,7 @@ exoscale/coax {:mvn/version "1.0.0-alpha14"} orchestra/orchestra {:mvn/version "2021.01.01-1"} expound/expound {:mvn/version "0.8.10"} - integrant/integrant {:mvn/version "0.8.0"} + integrant/integrant {:git/url "https://github.com/weavejester/integrant", :git/sha "a9fd7c02bd7201f36344b47142badc3c3ef22f88"} com.stuartsierra/dependency {:mvn/version "1.0.0"} listora/again {:mvn/version "1.0.0"} org.clojure/tools.gitlibs {:mvn/version "2.4.172"} @@ -165,7 +167,12 @@ :git/sha "1a29775a3d286f9f6fe3f979c78b6e2bf298d5ba"} com.github.rawleyfowler/sluj {:git/url "https://github.com/rawleyfowler/sluj" :git/sha "4a92e772b4e07bf127423448d4140748b5782198" - :deps/manifest :deps}} + :deps/manifest :deps} + net.cgrand/xforms {:git/url "https://github.com/cgrand/xforms" + :git/sha "550dbc150a79c6ecc148d8a7e260e10bc36321c6" + :deps/manifest :deps} + prismatic/plumbing {:git/url "https://github.com/plumatic/plumbing", + :git/sha "424bc704f2db422de34269c139a5494314b3a43b"}} :classpath-overrides {org.clojure/clojure nil org.clojure/spec.alpha nil}} :clj-nvd diff --git a/resources/META-INF/native-image/babashka/babashka/native-image.properties b/resources/META-INF/native-image/babashka/babashka/native-image.properties index 2e6e0571..ea2ccd7e 100644 --- a/resources/META-INF/native-image/babashka/babashka/native-image.properties +++ b/resources/META-INF/native-image/babashka/babashka/native-image.properties @@ -7,7 +7,7 @@ Args=-H:+ReportExceptionStackTraces \ -H:IncludeResources=src/babashka/.* \ -H:IncludeResources=SCI_VERSION \ -H:Log=registerResource:3 \ - -H:EnableURLProtocols=http,https,jar \ + --enable-url-protocols=http,https,jar,unix \ --enable-all-security-services \ -H:+JNI \ --no-server \ @@ -26,4 +26,31 @@ Args=-H:+ReportExceptionStackTraces \ -H:ServiceLoaderFeatureExcludeServices=javax.sound.midi.spi.SoundbankReader \ -H:ServiceLoaderFeatureExcludeServices=javax.sound.midi.spi.MidiFileWriter \ -H:ServiceLoaderFeatureExcludeServices=java.net.ContentHandlerFactory \ - -H:ServiceLoaderFeatureExcludeServices=java.nio.charset.spi.CharsetProvider + -H:ServiceLoaderFeatureExcludeServices=java.nio.charset.spi.CharsetProvider \ + -EBABASHKA_STATIC \ + -EBABASHKA_MUSL \ + -EBABASHKA_FEATURE_YAML \ + -EBABASHKA_FEATURE_XML \ + -EBABASHKA_FEATURE_CSV \ + -EBABASHKA_FEATURE_TRANSIT \ + -EBABASHKA_FEATURE_JAVA_TIME \ + -EBABASHKA_FEATURE_JAVA_NET_HTTP \ + -EBABASHKA_FEATURE_JAVA_NIO \ + -EBABASHKA_FEATURE_HTTPKIT_CLIENT \ + -EBABASHKA_FEATURE_HTTPKIT_SERVER \ + -EBABASHKA_FEATURE_CORE_MATCH \ + -EBABASHKA_FEATURE_HICCUP \ + -EBABASHKA_FEATURE_TEST_CHECK \ + -EBABASHKA_FEATURE_SELMER \ + -EBABASHKA_FEATURE_LOGGING \ + -EBABASHKA_FEATURE_PRIORITY_MAP \ + -EBABASHKA_FEATURE_JDBC \ + -EBABASHKA_FEATURE_SQLITE \ + -EBABASHKA_FEATURE_POSTGRESQL \ + -EBABASHKA_FEATURE_ORACLEDB \ + -EBABASHKA_FEATURE_HSQLDB \ + -EBABASHKA_FEATURE_DATASCRIPT \ + -EBABASHKA_FEATURE_LANTERNA \ + -EBABASHKA_FEATURE_SPEC_ALPHA \ + -EBABASHKA_FEATURE_RRB_VECTOR \ + -EBABASHKA_REQUIRE_SCAN diff --git a/resources/UrlClassLoaderSubstitutions.java b/resources/UrlClassLoaderSubstitutions.java new file mode 100644 index 00000000..53bb1a32 --- /dev/null +++ b/resources/UrlClassLoaderSubstitutions.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.AccessControlContext; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.WeakHashMap; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; + +@TargetClass(URLClassLoader.class) +@SuppressWarnings({"unused", "static-method"}) +final class Target_java_net_URLClassLoader { + @Alias// + @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.NewInstance, declClass = WeakHashMap.class)// + private WeakHashMap closeables; + + @Substitute + public InputStream getResourceAsStream(String name) throws IOException { + System.out.println("getResource"); + return null; + // return Resources.createInputStream(name); + } +} diff --git a/resources/src/babashka/clojure/spec/alpha.clj b/resources/src/babashka/clojure/spec/alpha.clj index 9b9772d3..b24f5688 100644 --- a/resources/src/babashka/clojure/spec/alpha.clj +++ b/resources/src/babashka/clojure/spec/alpha.clj @@ -322,10 +322,7 @@ "Returns a symbol from a symbol or var" [x] (if (var? x) - (let [m (meta x) - n (:name m) - ns (:ns m)] - (symbol (str ns) (str n))) + (symbol x) x)) (defn- unfn [expr] @@ -339,10 +336,7 @@ (defn- res [form] (cond (keyword? form) form - (symbol? form) (cond - (= 'fn form) 'clojure.core/fn ;; make tests pass, fn is not a macro in SCI - (= 'not form) 'clojure.core/not ;; make tests pass, not is not a macro in SCI - :else (c/or (-> form resolve ->sym) form)) + (symbol? form) (c/or (-> form resolve ->sym) form) (sequential? form) (walk/postwalk #(if (symbol? %) (res %) %) (unfn form)) :else form)) diff --git a/sci b/sci index 92a07126..bf6a0f1e 160000 --- a/sci +++ b/sci @@ -1 +1 @@ -Subproject commit 92a071269f1a4e3f4fda262b33b47ec827be3d4e +Subproject commit bf6a0f1e00313a902c62c59e440266612725b926 diff --git a/script/built_in.clj b/script/built_in.clj index 743b2b02..346acca3 100755 --- a/script/built_in.clj +++ b/script/built_in.clj @@ -9,7 +9,8 @@ (let [tmp-dir (fs/file tmp-dir)] (shell {:dir tmp-dir} "git clone https://github.com/babashka/spec.alpha") (let [spec-dir (fs/file tmp-dir "spec.alpha")] - (shell {:dir spec-dir} "git reset 1d9df099be4fbfd30b9b903642ad376373c16298 --hard") - (fs/copy-tree (fs/file spec-dir "src" "main" "clojure") (fs/file "resources" "src" "babashka"))))) + (shell {:dir spec-dir} "git reset 951b49b8c173244e66443b8188e3ff928a0a71e7 --hard") + (fs/copy-tree (fs/file spec-dir "src" "main" "clojure") (fs/file "resources" "src" "babashka") + {:replace-existing true})))) diff --git a/script/bump_graal_version.clj b/script/bump_graal_version.clj index 191c8e01..415d5896 100755 --- a/script/bump_graal_version.clj +++ b/script/bump_graal_version.clj @@ -11,7 +11,7 @@ ;; GraalVM Community Edition 19.3.2 based on OpenJDK 8u252 ;; GraalVM Community Edition 19.3.2 based on OpenJDK 11.0.7 ;; -;; Currently we use GraalVM java11-20.1.0 +;; Currently we use GraalVM java19-20.1.0 (ns bump-graal-version (:require [clojure.string :as str] @@ -31,8 +31,8 @@ "" "./bump_graal_version.clj -g 19.3.2 (the new version)" "or" - "./bump_graal_version.clj -g 19.3.2 --java java11" - "(for GraalVM java11-19.3.2)" + "./bump_graal_version.clj -g 19.3.2 --java java19" + "(for GraalVM java19-19.3.2)" ""] (str/join \newline)))) @@ -54,8 +54,8 @@ ;; OR ;; ;; We could have them as environment variables -(def current-graal-version "22.3.0") -(def current-java-version "java11") +(def current-graal-version "22.3.1") +(def current-java-version "java19") (def cl-options [["-g" "--graal VERSION" "graal version"] diff --git a/script/check_glibc.sh b/script/check_glibc.sh new file mode 100755 index 00000000..36a3bf4a --- /dev/null +++ b/script/check_glibc.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +function ver_lte() { + printf '%s\n%s' "$1" "$2" | sort -C -V +} + +max_glibc_version="2.31" +current_glibc_version=$(ldd --version | head -1 | awk '{print $4}' | cut -d "-" -f 1) + +function bail() { + echo "glibc greater than max version ${max_glibc_version}: ${current_glibc_version}" + exit 1 +} + +ver_lte "${current_glibc_version}" "${max_glibc_version}" || bail diff --git a/script/compile b/script/compile index 17b7dc76..04c3c1f7 100755 --- a/script/compile +++ b/script/compile @@ -26,17 +26,6 @@ fi export JAVA_HOME=$GRAALVM_HOME export PATH=$GRAALVM_HOME/bin:$PATH -rm -rf resources/*.class -# SVM_JAR=$(find -L "$GRAALVM_HOME" | grep svm.jar) -# "$GRAALVM_HOME/bin/javac" -cp "$SVM_JAR" resources/CutOffCoreServicesDependencies.java -# "$GRAALVM_HOME/bin/javac" -cp "$SVM_JAR" resources/CutOffSunAwtWwwContentAudioAiff.java -# "$GRAALVM_HOME/bin/javac" -cp "$SVM_JAR" resources/CutOffMisc.java -if [ -z "$BABASHKA_JAR" ]; then - lein with-profiles +reflection,+native-image "do" run - lein "do" clean, uberjar, metabom - BABASHKA_JAR=${BABASHKA_JAR:-"target/babashka-$BABASHKA_VERSION-standalone.jar"} -fi - # because script/test cleans target during ci before the jar can we saved cp target/metabom.jar . @@ -54,8 +43,12 @@ args=("-jar" "$BABASHKA_JAR" "--verbose" "--no-fallback" "--native-image-info" + "--install-exit-handlers" # --trace-class-initialization=jdk.internal.net.http.common.DebugLogger,jdk.internal.net.http.websocket.WebSocketImpl,jdk.internal.net.http.common.Utils - "$BABASHKA_XMX") + "$BABASHKA_XMX" + "--enable-preview" + "-march=compatibility" # necessary for compatibility with older machines, e.g. see https://github.com/borkdude/deps.clj/actions/runs/6337277754/job/17212028399 + "-O1") BABASHKA_STATIC=${BABASHKA_STATIC:-} BABASHKA_MUSL=${BABASHKA_MUSL:-} @@ -113,4 +106,4 @@ then export BABASHKA_FEATURE_PRIORITY_MAP="${BABASHKA_FEATURE_PRIORITY_MAP:-false}" fi -"$GRAALVM_HOME/bin/native-image" "${args[@]}" +"$GRAALVM_HOME/bin/native-image" "${args[@]}" "$@" diff --git a/script/compile.bat b/script/compile.bat index 56d3396c..51abbc72 100644 --- a/script/compile.bat +++ b/script/compile.bat @@ -29,7 +29,12 @@ call %GRAALVM_HOME%\bin\native-image.cmd ^ "-H:+ReportExceptionStackTraces" ^ "--verbose" ^ "--no-fallback" ^ - "%BABASHKA_XMX%" + "--enable-preview" ^ + "--install-exit-handlers" ^ + "-march=compatibility" ^ + "-O1" ^ + "%BABASHKA_XMX%" ^ + %* if %errorlevel% neq 0 exit /b %errorlevel% diff --git a/script/install-graalvm b/script/install-graalvm index 19d43c5a..dd27675a 100755 --- a/script/install-graalvm +++ b/script/install-graalvm @@ -4,34 +4,33 @@ set -euo pipefail INSTALL_DIR="${1:-$HOME}" -GRAALVM_VERSION="${GRAALVM_VERSION:-21.2.0}" +GRAALVM_VERSION="${GRAALVM_VERSION:-22}" -case "$BABASHKA_PLATFORM" in - macos) - GRAALVM_PLATFORM="darwin" - ;; - linux) - GRAALVM_PLATFORM="linux" - ;; -esac +GRAALVM_PLATFORM=$BABASHKA_PLATFORM case "${BABASHKA_ARCH:-}" in - aarch64) - GRAALVM_ARCH="aarch64" - ;; - *) - GRAALVM_ARCH="amd64" - ;; + aarch64) + GRAALVM_ARCH="aarch64" + ;; + *) + GRAALVM_ARCH="x64" + ;; esac -GRAALVM_FILENAME="graalvm-ce-java11-$GRAALVM_PLATFORM-$GRAALVM_ARCH-$GRAALVM_VERSION.tar.gz" +GRAALVM_DIR_NAME="graalvm-$GRAALVM_VERSION" +GRAALVM_FILENAME="graalvm-jdk-${GRAALVM_VERSION}_${GRAALVM_PLATFORM}-${GRAALVM_ARCH}_bin.tar.gz" +DOWNLOAD_URL="https://download.oracle.com/graalvm/22/archive/${GRAALVM_FILENAME}" pushd "$INSTALL_DIR" >/dev/null -if ! [ -d "graalvm-ce-java11-$GRAALVM_VERSION" ]; then - echo "Downloading GraalVM $GRAALVM_PLATFORM-$GRAALVM_ARCH-$GRAALVM_VERSION on '$PWD'..." - curl -O -sL "https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-$GRAALVM_VERSION/$GRAALVM_FILENAME" - tar xzf "$GRAALVM_FILENAME" +if ! [ -d "$GRAALVM_DIR_NAME" ]; then + echo "Downloading GraalVM $GRAALVM_PLATFORM-$GRAALVM_ARCH-$GRAALVM_VERSION on '$PWD'..." + echo "$DOWNLOAD_URL" + curl --fail -LO "$DOWNLOAD_URL" + ls -la + mkdir "$GRAALVM_DIR_NAME" + tar xzvf "$GRAALVM_FILENAME" -C "$GRAALVM_DIR_NAME" --strip-components 1 + ls -la "$GRAALVM_DIR_NAME" fi popd >/dev/null diff --git a/script/test b/script/test index 2d6317b9..ad459154 100755 --- a/script/test +++ b/script/test @@ -1,5 +1,7 @@ #!/usr/bin/env bash +set -eo pipefail + if [ "$GRAALVM_HOME" != "" ] then export JAVA_HOME=$GRAALVM_HOME @@ -8,7 +10,6 @@ fi java -version -set -eo pipefail unset BABASHKA_PRELOADS unset BABASHKA_CLASSPATH unset BABASHKA_PRELOADS_TEST @@ -16,6 +17,9 @@ unset BABASHKA_PRELOADS_TEST echo "running tests part 1" lein "do" clean, test "$@" +echo "running flaky tests" +lein "do" clean, test :flaky || true + export BABASHKA_PRELOADS='(defn __bb__foo [] "foo") (defn __bb__bar [] "bar")' export BABASHKA_PRELOADS_TEST=true echo "running tests part 2" @@ -37,3 +41,13 @@ lein test :only babashka.pod-test export BABASHKA_SOCKET_REPL_TEST=true lein test :only babashka.impl.socket-repl-test + +# test invoking script in subdir with bb.edn in parent dir +unset BABASHKA_PRELOADS +unset BABASHKA_CLASSPATH +pushd test-resources/bb_in_root_script_in_other_dir +if [[ $BABASHKA_TEST_ENV = "native" ]] +then + ../../bb dir/script.clj +fi +popd diff --git a/script/test.bat b/script/test.bat index d0fe976e..106caed2 100755 --- a/script/test.bat +++ b/script/test.bat @@ -16,7 +16,11 @@ set BABASHKA_POD_TEST= set BABASHKA_SOCKET_REPL_TEST= echo "running tests part 1" -call lein do clean, test :windows || exit /B 1 +call lein do clean, test %* || exit /B 1 + +echo "running flaky tests" +REM there's no "or exit" here because we don't want flaky tests to fail the script +call lein do clean, test :flaky set BABASHKA_PRELOADS=(defn __bb__foo [] "foo") (defn __bb__bar [] "bar") set BABASHKA_PRELOADS_TEST=true diff --git a/script/uberjar b/script/uberjar index 42025c74..525a2d71 100755 --- a/script/uberjar +++ b/script/uberjar @@ -175,8 +175,15 @@ else BABASHKA_LEIN_PROFILES+=",-feature/rrb-vector" fi +mkdir -p resources/META-INF/babashka cp deps.edn resources/META-INF/babashka/deps.edn +rm -rf resources/*.class +# SVM_JAR=$(find -L "$GRAALVM_HOME" | grep svm.jar) +# "$GRAALVM_HOME/bin/javac" -cp "$SVM_JAR" resources/UrlClassLoaderSubstitutions.java +# "$GRAALVM_HOME/bin/javac" -cp "$SVM_JAR" resources/CutOffSunAwtWwwContentAudioAiff.java +# "$GRAALVM_HOME/bin/javac" -cp "$SVM_JAR" resources/CutOffMisc.java + if [ -z "$BABASHKA_JAR" ]; then lein with-profiles "$BABASHKA_LEIN_PROFILES,+reflection,-uberjar" do run lein with-profiles "$BABASHKA_LEIN_PROFILES" do clean, uberjar, metabom diff --git a/src/aaaa_this_has_to_be_first/because_patches.clj b/src/aaaa_this_has_to_be_first/because_patches.clj index d0b5cda1..b8651a25 100644 --- a/src/aaaa_this_has_to_be_first/because_patches.clj +++ b/src/aaaa_this_has_to_be_first/because_patches.clj @@ -3,28 +3,46 @@ (:require [babashka.impl.patches.datafy] [babashka.impl.pprint])) -;; Enable this for scanning requiring-resolve usage: -;; --- -;; (def old-requiring-resolve requiring-resolve) - -;; (defmacro static-requiring-resolve [sym] -;; (prn :sym sym) -;; `(old-requiring-resolve ~sym)) - -;; (alter-var-root #'requiring-resolve (constantly @#'static-requiring-resolve)) -;; (doto #'requiring-resolve (.setMacro)) -;; --- - -;; ((requiring-resolve 'clojure.pprint/pprint) (range 20)) - -;; Enable this for detecting literal usages of require -;; --- -;; (def old-require require) - -;; (defmacro static-require [& syms] -;; (when (meta &form) -;; (prn :require &form )) -;; `(old-require ~@syms)) -;; (alter-var-root #'require (constantly @#'static-require)) -;; (doto #'require (.setMacro)) +;; Enable this for scanning requiring usage: +(def enable-require-scan + "(do + (def old-require require) + (def old-resolve resolve) + + (def our-requiring-resolve (fn [sym] + (let [ns (symbol (namespace sym))] + (old-require ns) + (old-resolve sym)))) + + (defn static-requiring-resolve [form _ _] + (prn :req-resolve form :args (rest form)) + `(let [res# (our-requiring-resolve ~@(rest form))] + res#)) + + (alter-var-root #'requiring-resolve (constantly @#'static-requiring-resolve)) + (doto #'requiring-resolve (.setMacro)) + + (defn static-require [& [&form _bindings & syms]] + (when (meta &form) + (prn :require &form (meta &form) *file*)) + `(old-require ~@syms)) + (alter-var-root #'require (constantly @#'static-require)) + (doto #'require (.setMacro)) + + (alter-var-root #'clojure.core/serialized-require (constantly (fn [& args] + (prn :serialized-req args))))) + + + + (defn static-resolve [& [&form _bindings & syms]] + (when (meta &form) + (prn :require &form (meta &form) *file*)) + `(old-resolve ~@syms)) + (alter-var-root #'resolve (constantly @#'static-resolve)) + (doto #'resolve (.setMacro)) +") + + +(when (System/getenv "BABASHKA_REQUIRE_SCAN") + (load-string enable-require-scan)) ;; --- diff --git a/src/babashka/deps.clj b/src/babashka/deps.clj index ea8daa59..817b81d2 100644 --- a/src/babashka/deps.clj +++ b/src/babashka/deps.clj @@ -22,7 +22,7 @@ Examples: - (-> (clojure '[-M -e (+ 1 2 3)] {:out :string}) deref :out) returns + (-> (clojure {:out :string} '-M '-e '(+ 1 2 3)]) deref :out) returns \"6\n\". (-> @(clojure) :exit) starts a clojure REPL, waits for it @@ -38,17 +38,13 @@ *out* @sci/out *err* @sci/err deps/*dir* (:dir opts) - deps/*env* (:env opts) - deps/*extra-env* (:extra-env opts) - deps/*process-fn* (fn - ([cmd] (pp/process* {:cmd cmd - :prev prev - :opts opts})) - ([cmd _] (pp/process* {:cmd cmd - :prev prev - :opts opts}))) - deps/*exit-fn* (fn - ([_]) - ([_exit-code msg] - (throw (Exception. msg))))] + deps/*aux-process-fn* (fn [{:keys [cmd out]}] + (pp/shell (assoc opts :out out :cmd cmd))) + deps/*clojure-process-fn* (fn [{:keys [cmd]}] + (pp/process* {:cmd cmd + :prev prev + :opts opts})) + deps/*exit-fn* (fn [{:keys [message]}] + (when message + (throw (Exception. message))))] (apply deps/-main cmd)))) diff --git a/src/babashka/dude.clj b/src/babashka/dude.clj new file mode 100644 index 00000000..6a984cf7 --- /dev/null +++ b/src/babashka/dude.clj @@ -0,0 +1,8 @@ +(ns babashka.dude + (:require [clojure-csv.core :as csv] + [clojure.java.io :as io] + [clojure.string :as string])) + +csv/x +io/x +string/x diff --git a/src/babashka/impl/classes.clj b/src/babashka/impl/classes.clj index 561da019..4040ad54 100644 --- a/src/babashka/impl/classes.clj +++ b/src/babashka/impl/classes.clj @@ -8,6 +8,18 @@ [sci.core :as sci] [sci.impl.types :as t])) +(set! *warn-on-reflection* true) + +(def has-of-virtual? + (some #(= "ofVirtual" (.getName ^java.lang.reflect.Method %)) + (.getMethods Thread))) + +(def has-domain-sockets? + (resolve 'java.net.UnixDomainSocketAddress)) + +(def has-graal-process-properties? + (resolve 'org.graalvm.nativeimage.ProcessProperties)) + (def base-custom-map `{clojure.lang.LineNumberingPushbackReader {:allPublicConstructors true :allPublicMethods true} @@ -47,7 +59,10 @@ {:name "sleep"} {:name "start"} {:name "toString"} - {:name "yield"}]} + {:name "yield"} + ~@(when has-of-virtual? [{:name "ofVirtual"} + {:name "startVirtualThread"} + {:name "isVirtual"}])]} java.net.URL {:allPublicConstructors true :allPublicFields true @@ -76,7 +91,8 @@ java.util.Arrays {:methods [{:name "copyOf"} {:name "copyOfRange"} - {:name "equals"}]} + {:name "equals"} + {:name "fill"}]} ;; this fixes clojure.lang.Reflector for Java 11 java.lang.reflect.AccessibleObject {:methods [{:name "canAccess"}]} @@ -111,17 +127,21 @@ clojure.lang.RT {:methods [{:name "aget"} {:name "aset"} - {:name "aclone"}]} + {:name "aclone"} + ;; we expose this via the Compiler/LOADER dynamic var + {:name "baseLoader"}]} clojure.lang.Compiler {:fields [{:name "specials"} {:name "CHAR_MAP"}]} clojure.lang.PersistentHashMap {:fields [{:name "EMPTY"}]} clojure.lang.APersistentVector - {:methods [{:name "indexOf"}]} + {:methods [{:name "indexOf"} + {:name "contains"}]} clojure.lang.LazySeq {:allPublicConstructors true, - :methods [{:name "indexOf"}]} + :methods [{:name "indexOf"} + {:name "contains"}]} clojure.lang.ILookup {:methods [{:name "valAt"}]} clojure.lang.IPersistentMap @@ -140,19 +160,39 @@ {:methods [{:name "hasNext"} {:name "next"}]} java.util.TimeZone - {:methods [{:name "getTimeZone"}]}}) + {:methods [{:name "getTimeZone"}]} + java.net.URLClassLoader + {:methods [{:name "close"} + {:name "findResource"} + {:name "findResources"} + {:name "getResourceAsStream"} + {:name "getURLs"}]} + java.lang.ClassLoader + {:methods [{:name "getResource"} + {:name "getResources"} + {:name "getResourceAsStream"} + {:name "getParent"}]} + clojure.lang.ARef + {:methods [{:name "getWatches"}]} + clojure.lang.MapEntry + {:allPublicConstructors true + :methods [{:name "create"}]}}) (def custom-map (cond-> - (merge base-custom-map - proxy/custom-reflect-map) + (merge base-custom-map + proxy/custom-reflect-map) features/hsqldb? (assoc `org.hsqldb.dbinfo.DatabaseInformationFull {:methods [{:name "" :parameterTypes ["org.hsqldb.Database"]}]} `java.util.ResourceBundle {:methods [{:name "getBundle" :parameterTypes ["java.lang.String","java.util.Locale", - "java.lang.ClassLoader"]}]}))) + "java.lang.ClassLoader"]}]}) + + has-graal-process-properties? + (assoc `org.graalvm.nativeimage.ProcessProperties + {:methods [{:name "exec"}]}))) (def java-net-http-classes "These classes must be initialized at run time since GraalVM 22.1" @@ -182,7 +222,14 @@ java.net.http.WebSocket$Builder java.net.http.WebSocket$Listener java.security.cert.X509Certificate + java.security.cert.CertificateFactory + javax.crypto.Cipher javax.crypto.Mac + javax.crypto.SecretKey + javax.crypto.SecretKeyFactory + javax.crypto.spec.GCMParameterSpec + javax.crypto.spec.IvParameterSpec + javax.crypto.spec.PBEKeySpec javax.crypto.spec.SecretKeySpec javax.net.ssl.HostnameVerifier ;; clj-http-lite javax.net.ssl.HttpsURLConnection ;; clj-http-lite @@ -194,6 +241,9 @@ javax.net.ssl.TrustManager javax.net.ssl.TrustManagerFactory javax.net.ssl.X509TrustManager + javax.net.ssl.X509ExtendedTrustManager + javax.net.ssl.SSLSocket + javax.net.ssl.SSLSocketFactory jdk.internal.net.http.HttpClientBuilderImpl jdk.internal.net.http.HttpClientFacade jdk.internal.net.http.HttpRequestBuilderImpl @@ -284,7 +334,9 @@ java.lang.StringBuilder java.lang.System java.lang.Throwable - ;; java.lang.UnsupportedOperationException + java.lang.ThreadLocal + java.lang.Thread$UncaughtExceptionHandler + java.lang.UnsupportedOperationException java.lang.ref.WeakReference java.lang.ref.ReferenceQueue java.lang.ref.Cleaner @@ -299,11 +351,15 @@ java.net.HttpURLConnection java.net.InetAddress java.net.InetSocketAddress + java.net.StandardProtocolFamily java.net.ServerSocket java.net.Socket java.net.SocketException + ~@(when has-domain-sockets? + '[java.net.UnixDomainSocketAddress]) java.net.UnknownHostException java.net.URI + java.net.URISyntaxException ;; java.net.URL, see custom map java.net.URLConnection java.net.URLEncoder @@ -319,6 +375,8 @@ java.nio.file.StandardOpenOption java.nio.channels.FileChannel java.nio.channels.FileChannel$MapMode + java.nio.channels.ServerSocketChannel + java.nio.channels.SocketChannel java.nio.charset.Charset java.nio.charset.CoderResult java.nio.charset.CharsetEncoder @@ -343,14 +401,17 @@ java.nio.file.attribute.FileTime java.nio.file.attribute.PosixFilePermission java.nio.file.attribute.PosixFilePermissions]) + java.security.spec.PKCS8EncodedKeySpec java.security.MessageDigest java.security.DigestInputStream java.security.Provider + java.security.KeyFactory java.security.KeyStore java.security.SecureRandom java.security.Security java.sql.Date java.text.ParseException + java.text.ParsePosition ;; adds about 200kb, same functionality provided by java.time: java.text.SimpleDateFormat ~@(when features/java-time? @@ -378,6 +439,7 @@ java.time.format.DateTimeFormatterBuilder java.time.format.DateTimeParseException java.time.format.DecimalStyle + java.time.format.FormatStyle java.time.format.ResolverStyle java.time.format.SignStyle java.time.temporal.ChronoField @@ -386,31 +448,44 @@ java.time.temporal.TemporalAdjusters java.time.temporal.TemporalAmount java.time.temporal.TemporalField + java.time.temporal.WeekFields ~(symbol "[Ljava.time.temporal.TemporalField;") java.time.format.TextStyle java.time.temporal.Temporal java.time.temporal.TemporalAccessor - java.time.temporal.TemporalAdjuster]) + java.time.temporal.TemporalAdjuster + java.time.temporal.TemporalQuery + ~(symbol "[Ljava.time.temporal.TemporalQuery;")]) java.util.concurrent.atomic.AtomicInteger java.util.concurrent.atomic.AtomicLong java.util.concurrent.atomic.AtomicReference + java.util.concurrent.Callable java.util.concurrent.CancellationException java.util.concurrent.CompletionException + java.util.concurrent.CountDownLatch java.util.concurrent.ExecutionException java.util.concurrent.Executor + java.util.concurrent.ExecutorService + java.util.concurrent.BlockingQueue + java.util.concurrent.ArrayBlockingQueue java.util.concurrent.LinkedBlockingQueue + java.util.concurrent.ScheduledFuture java.util.concurrent.ScheduledThreadPoolExecutor + java.util.concurrent.Semaphore + java.util.concurrent.ThreadFactory java.util.concurrent.ThreadPoolExecutor java.util.concurrent.ThreadPoolExecutor$AbortPolicy java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy java.util.concurrent.ThreadPoolExecutor$DiscardOldestPolicy java.util.concurrent.ThreadPoolExecutor$DiscardPolicy + java.util.concurrent.ExecutorService java.util.concurrent.ScheduledExecutorService java.util.concurrent.Future java.util.concurrent.FutureTask java.util.concurrent.CompletableFuture java.util.concurrent.Executors java.util.concurrent.TimeUnit + java.util.jar.Attributes java.util.jar.Attributes$Name java.util.jar.JarFile java.util.jar.JarEntry @@ -420,6 +495,7 @@ java.util.jar.Manifest java.util.stream.BaseStream java.util.stream.Stream + java.util.stream.IntStream java.util.Random java.util.regex.Matcher java.util.regex.Pattern @@ -432,6 +508,7 @@ java.util.Base64$Encoder java.util.Date java.util.HashMap + java.util.HashSet java.util.IdentityHashMap java.util.InputMismatchException java.util.List @@ -452,6 +529,8 @@ java.util.function.BiFunction java.util.function.Predicate java.util.function.Supplier + java.util.zip.CheckedInputStream + java.util.zip.CRC32 java.util.zip.Inflater java.util.zip.InflaterInputStream java.util.zip.Deflater @@ -485,7 +564,6 @@ ~@(when features/yaml? '[org.yaml.snakeyaml.error.YAMLException]) ~@(when features/hsqldb? '[org.hsqldb.jdbcDriver])] :constructors [clojure.lang.Delay - clojure.lang.MapEntry clojure.lang.LineNumberingPushbackReader java.io.EOFException] :methods [borkdude.graal.LockFix] ;; support for locking @@ -527,19 +605,23 @@ clojure.lang.IRef clojure.lang.ISeq clojure.lang.IPersistentVector + clojure.lang.ITransientSet clojure.lang.ITransientVector clojure.lang.Iterate clojure.lang.LispReader$Resolver + clojure.lang.LongRange clojure.lang.Named clojure.lang.Keyword clojure.lang.PersistentArrayMap clojure.lang.PersistentHashSet clojure.lang.PersistentList + clojure.lang.PersistentList$EmptyList clojure.lang.PersistentQueue clojure.lang.PersistentStructMap clojure.lang.PersistentTreeMap clojure.lang.PersistentTreeSet clojure.lang.PersistentVector + clojure.lang.Range clojure.lang.Ratio clojure.lang.ReaderConditional clojure.lang.Repeat @@ -557,7 +639,6 @@ java.lang.LinkageError java.lang.ThreadDeath java.lang.VirtualMachineError - java.net.URLClassLoader java.sql.Timestamp java.util.concurrent.TimeoutException java.util.Collection @@ -574,7 +655,27 @@ (:instance-checks classes)) m (apply hash-map (for [c classes - c [(list 'quote c) c]] + c [(list 'quote c) (cond-> `{:class ~c} + (= 'java.lang.Class c) + (assoc :static-methods + {(list 'quote 'forName) + `(fn + ([_# ^String class-name#] + (Class/forName class-name#)) + ([_# ^String class-name# initialize# ^java.lang.ClassLoader clazz-loader#] + (Class/forName class-name#)))}) + (= 'java.lang.Thread c) + (assoc :static-methods + {(list 'quote 'sleep) + `(fn + ([_# x#] + (if (instance? Number x#) + (let [x# (long x#)] + (Thread/sleep x#)) + (let [^java.time.Duration x# x#] + (Thread/sleep x#)))) + ([_# ^java.lang.Long millis# ^java.lang.Long nanos#] + (Thread/sleep millis# nanos#)))}))]] c)) m (assoc m :public-class (fn [v] @@ -607,6 +708,8 @@ java.nio.file.FileSystem (instance? java.nio.file.PathMatcher v) java.nio.file.PathMatcher + (instance? java.util.stream.IntStream v) + java.util.stream.IntStream (instance? java.util.stream.BaseStream v) java.util.stream.BaseStream (instance? java.nio.ByteBuffer v) @@ -619,6 +722,10 @@ java.nio.CharBuffer (instance? java.nio.channels.FileChannel v) java.nio.channels.FileChannel + (instance? java.nio.channels.ServerSocketChannel v) + java.nio.channels.ServerSocketChannel + (instance? java.nio.channels.SocketChannel v) + java.nio.channels.SocketChannel (instance? java.net.CookieStore v) java.net.CookieStore ;; this makes interop on reified classes work @@ -626,18 +733,38 @@ (instance? sci.impl.types.IReified v) (first (t/getInterfaces v)) ;; fix for #1061 - (instance? java.io.Closeable v) - java.io.Closeable + (instance? java.net.URLClassLoader v) + java.net.URLClassLoader + (instance? java.lang.ClassLoader v) + java.lang.ClassLoader (instance? java.nio.file.attribute.BasicFileAttributes v) java.nio.file.attribute.BasicFileAttributes (instance? java.util.concurrent.Future v) java.util.concurrent.Future (instance? java.util.concurrent.ScheduledExecutorService v) java.util.concurrent.ScheduledExecutorService + (instance? java.util.concurrent.ExecutorService v) + java.util.concurrent.ExecutorService (instance? java.util.Iterator v) java.util.Iterator + (instance? javax.crypto.SecretKey v) + javax.crypto.SecretKey + (instance? javax.net.ssl.SSLSocketFactory v) + javax.net.ssl.SSLSocketFactory + (instance? javax.net.ssl.SSLSocket v) + javax.net.ssl.SSLSocket + (instance? java.lang.Thread v) + java.lang.Thread + (instance? java.security.cert.X509Certificate v) + java.security.cert.X509Certificate + (instance? java.io.Console v) + java.io.Console + (instance? java.util.Set v) + java.util.Set + (instance? java.io.Closeable v) + java.io.Closeable ;; keep commas for merge friendliness - ,,,))) + ))) m (assoc m (list 'quote 'clojure.lang.Var) 'sci.lang.Var) m (assoc m (list 'quote 'clojure.lang.Namespace) 'sci.lang.Namespace)] m)) @@ -648,6 +775,14 @@ allowed to be initialized at build time." (gen-class-map)) +#_(let [class-name (str c)] + (cond-> (Class/forName class-name) + (= "java.lang.Class" class-name) + (->> (hash-map :static-methods {'forName (fn [class-name] + (prn :class-for) + (Class/forName class-name))} + :class)))) + (def class-map "A delay to delay initialization of java-net-http classes to run time, since GraalVM 22.1" (delay (persistent! (reduce (fn [acc c] @@ -664,6 +799,7 @@ BigInteger java.math.BigInteger Boolean java.lang.Boolean Byte java.lang.Byte + Callable java.util.concurrent.Callable Character java.lang.Character CharSequence java.lang.CharSequence Class java.lang.Class @@ -689,9 +825,10 @@ Number java.lang.Number NumberFormatException java.lang.NumberFormatException Object java.lang.Object + Runnable java.lang.Runnable Runtime java.lang.Runtime RuntimeException java.lang.RuntimeException - Process java.lang.Process + Process java.lang.Process ProcessBuilder java.lang.ProcessBuilder Short java.lang.Short StackTraceElement java.lang.StackTraceElement @@ -699,11 +836,13 @@ StringBuilder java.lang.StringBuilder System java.lang.System Thread java.lang.Thread + ThreadLocal java.lang.ThreadLocal + Thread$UncaughtExceptionHandler java.lang.Thread$UncaughtExceptionHandler Throwable java.lang.Throwable VirtualMachineError java.lang.VirtualMachineError ThreadDeath java.lang.ThreadDeath Void java.lang.Void - ;; UnsupportedOperationException java.lang.UnsupportedOperationException + UnsupportedOperationException java.lang.UnsupportedOperationException }) (defn reflection-file-entries [] @@ -746,13 +885,13 @@ "resources/META-INF/native-image/babashka/babashka/reflect-config.json") (json/generate-string all-entries {:pretty true})))) -(defn public-declared-method? [c m] +(defn public-declared-method? [^Class c ^java.lang.reflect.Method m] (and (= c (.getDeclaringClass m)) (not (.getAnnotation m Deprecated)))) -(defn public-declared-method-names [c] +(defn public-declared-method-names [^Class c] (->> (.getMethods c) - (keep (fn [m] + (keep (fn [^java.lang.reflect.Method m] (when (public-declared-method? c m) {:class c :name (.getName m)}))) @@ -760,8 +899,9 @@ (sort-by :name) (vec))) -(defn all-classes [] +(defn all-classes "Returns every java.lang.Class instance Babashka supports." + [] (->> (reflection-file-entries) (map :name) (map #(Class/forName %)))) @@ -779,6 +919,4 @@ (public-declared-method-names java.net.URL) (public-declared-method-names java.util.Properties) - (all-classes) - - ) + (all-classes)) diff --git a/src/babashka/impl/classpath.clj b/src/babashka/impl/classpath.clj index 11691390..6da8b59f 100644 --- a/src/babashka/impl/classpath.clj +++ b/src/babashka/impl/classpath.clj @@ -5,66 +5,51 @@ [clojure.java.io :as io] [clojure.string :as str] [sci.core :as sci]) - (:import [java.util.jar JarFile Manifest] + (:import [java.util.jar Manifest] (java.net URL))) (set! *warn-on-reflection* true) -(defprotocol IResourceResolver - (getResource [this paths opts]) - (getResources [this paths opts])) - -(deftype DirectoryResolver [path] - IResourceResolver - (getResource [_ resource-paths url?] - (some - (fn [resource-path] - (let [f (io/file path resource-path)] - (when (.exists f) - (if url? - ;; manual conversion, faster than going through .toURI - (java.net.URL. "file" nil (.getAbsolutePath f)) - {:file (.getAbsolutePath f) - :source (slurp f)})))) - resource-paths))) - -(defn path-from-jar - [^java.io.File jar-file resource-paths 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))) - -(deftype JarFileResolver [jar-file] - IResourceResolver - (getResource [_ resource-paths url?] - (path-from-jar jar-file resource-paths url?))) - -(defn part->entry [part] - (when-not (str/blank? part) - (if (str/ends-with? part ".jar") - (JarFileResolver. (io/file part)) - (DirectoryResolver. (io/file part))))) - -(deftype Loader [entries] - IResourceResolver - (getResource [_ resource-paths opts] - (some #(getResource % resource-paths opts) entries)) - (getResources [_ resource-paths opts] - (keep #(getResource % resource-paths opts) entries))) +(defn getResource [^babashka.impl.URLClassLoader class-loader resource-paths url?] + (some (fn [resource] + (when-let [^java.net.URL res (.findResource class-loader resource)] + (if url? + res + {:file (if (= "jar" (.getProtocol res)) + resource + (.getFile res)) + :source (slurp res)}))) + resource-paths)) (def path-sep (System/getProperty "path.separator")) -(defn loader [^String classpath] - (let [parts (.split classpath path-sep) - entries (keep part->entry parts)] - (Loader. entries))) +(defn ->url ^java.net.URL [^String s] + (.toURL (java.io.File. s))) + +(defn new-loader ^babashka.impl.URLClassLoader + ([paths] + (babashka.impl.URLClassLoader. (into-array java.net.URL (map ->url paths))))) + +(def ^babashka.impl.URLClassLoader the-url-loader (delay (new-loader []))) + +(defn add-classpath + "Adds extra-classpath, a string as for example returned by clojure + -Spath, to the current classpath." + [^String extra-classpath] + (let [paths (.split extra-classpath path-sep) + paths (map ->url paths) + loader @the-url-loader] + (run! (fn [path] + (._addURL ^babashka.impl.URLClassLoader loader path) + loader) + paths) + ;; (run! prn (.getURLs the-url-loader)) + (System/setProperty "java.class.path" + (let [system-cp (System/getProperty "java.class.path")] + (-> (cond-> system-cp + (not (str/blank? system-cp)) (str path-sep)) + (str extra-classpath))))) + nil) (defn resource-paths [namespace] (let [ns-str (name namespace) @@ -86,33 +71,20 @@ (.getValue "Main-Class") (demunge)))) -(def cp-state (atom nil)) - -(defn add-classpath - "Adds extra-classpath, a string as for example returned by clojure - -Spath, to the current classpath." - [extra-classpath] - (swap! cp-state - (fn [{:keys [:cp]}] - (let [new-cp - (if-not cp extra-classpath - (str cp path-sep extra-classpath))] - {:loader (loader new-cp) - :cp new-cp}))) - nil) - (defn split-classpath "Returns the classpath as a seq of strings, split by the platform specific path separator." - ([^String cp] (vec (.split cp path-sep)))) + ([^String cp] (vec (when cp (.split cp path-sep))))) (defn get-classpath "Returns the current classpath as set by --classpath, BABASHKA_CLASSPATH and add-classpath." [] - (:cp @cp-state)) + (let [cp (System/getProperty "java.class.path")] + (when-not (str/blank? cp) + cp))) (defn resource - (^URL [path] (when-let [st @cp-state] (resource (:loader st) path))) + (^URL [path] (resource @the-url-loader path)) (^URL [loader path] (if (str/starts-with? path "/") nil ;; non-relative paths always return nil (getResource loader [path] true)))) @@ -132,4 +104,3 @@ (def l (loader cp)) (source-for-namespace l 'babashka.impl.cheshire nil) (time (:file (source-for-namespace l 'cheshire.core nil)))) ;; 20ms -> 2.25ms - diff --git a/src/babashka/impl/cli.clj b/src/babashka/impl/cli.clj index 753e99a7..1ec08daf 100644 --- a/src/babashka/impl/cli.clj +++ b/src/babashka/impl/cli.clj @@ -17,18 +17,22 @@ (let [extra-opts '%s sym `%s the-var (requiring-resolve sym) + _ (when-not the-var + (throw (ex-info (str \"Could not resolve sym to a function: \" sym) {:babashka/exit 1}))) the-var-meta (meta the-var) ns (:ns (meta the-var)) ns-meta (meta ns) ct (babashka.tasks/current-task) + exec-args (babashka.cli/merge-opts (:exec-args (:org.babashka/cli ns-meta)) + (:exec-args (:org.babashka/cli the-var-meta)) + (:exec-args (:org.babashka/cli ct)) + (:exec-args ct) + (:exec-args extra-opts)) cli-opts (babashka.cli/merge-opts (:org.babashka/cli ns-meta) (:org.babashka/cli the-var-meta) (:org.babashka/cli ct) extra-opts) - task-exec-args (:exec-args ct) - cli-exec-args (:exec-args cli-opts) - exec-args {:exec-args (babashka.cli/merge-opts cli-exec-args task-exec-args)} - cli-opts (babashka.cli/merge-opts exec-args cli-opts) + cli-opts (assoc cli-opts :exec-args exec-args) opts (babashka.cli/parse-opts *command-line-args* cli-opts)] (the-var opts))" (random-uuid) diff --git a/src/babashka/impl/clojure/core.clj b/src/babashka/impl/clojure/core.clj index 56da8704..64438f47 100644 --- a/src/babashka/impl/clojure/core.clj +++ b/src/babashka/impl/clojure/core.clj @@ -15,15 +15,6 @@ (defn locking* [form bindings v f & args] (apply @#'locking/locking form bindings v f args)) -(defn time* - "Evaluates expr and prints the time it took. Returns the value of - expr." - [_ _ expr] - `(let [start# (. System (nanoTime)) - ret# ~expr] - (prn (str "Elapsed time: " (/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs")) - ret#)) - (defn core-dynamic-var ([sym] (core-dynamic-var sym nil)) ([sym init-val] (sci/new-dynamic-var sym init-val {:ns clojure-core-ns}))) @@ -34,6 +25,8 @@ (def compile-files (core-dynamic-var '*compile-files* false)) (def unchecked-math (core-dynamic-var '*unchecked-math* false)) (def math-context (core-dynamic-var '*math-context*)) +(def compile-path (core-dynamic-var '*compile-path* *compile-path*)) +(def compiler-options (core-dynamic-var '*compiler-options*)) (defn read+string "Added for compatibility. Must be used with @@ -158,7 +151,6 @@ 'shutdown-agents (copy-core-var shutdown-agents) 'slurp (copy-core-var slurp) 'spit (copy-core-var spit) - 'time (macrofy 'time time*) 'Throwable->map (copy-core-var Throwable->map) 'tap> (copy-core-var tap>) 'add-tap (copy-core-var add-tap) @@ -173,6 +165,8 @@ '*compile-files* compile-files '*unchecked-math* unchecked-math '*math-context* math-context + '*compiler-options* compiler-options + '*compile-path* compile-path 'with-precision (sci/copy-var with-precision clojure-core-ns) '-with-precision (sci/copy-var -with-precision clojure-core-ns) ;; STM @@ -199,5 +193,7 @@ 'print-method (sci/copy-var print-method clojure-core-ns) 'print-dup (sci/copy-var print-dup clojure-core-ns) 'PrintWriter-on (sci/copy-var PrintWriter-on clojure-core-ns) - '*compiler-options* (sci/new-dynamic-var '*compiler-options*)} + 'set-agent-send-executor! (sci/copy-var set-agent-send-executor! clojure-core-ns) + 'set-agent-send-off-executor! (sci/copy-var set-agent-send-off-executor! clojure-core-ns) + } ) diff --git a/src/babashka/impl/clojure/core/async.clj b/src/babashka/impl/clojure/core/async.clj index 391c4d9b..4d0d22a3 100644 --- a/src/babashka/impl/clojure/core/async.clj +++ b/src/babashka/impl/clojure/core/async.clj @@ -6,8 +6,14 @@ [sci.impl.copy-vars :refer [macrofy]] [sci.impl.vars :as vars])) +(set! *warn-on-reflection* true) + (def ^java.util.concurrent.Executor executor @#'async/thread-macro-executor) +(def ^java.util.concurrent.Executor virtual-executor + (try (eval '(java.util.concurrent.Executors/newVirtualThreadPerTaskExecutor)) + (catch Exception _ nil))) + (defn thread-call "Executes f in another thread, returning immediately to the calling thread. Returns a channel which will receive the result of calling @@ -26,10 +32,32 @@ (async/close! c)))))) c)) +(defn -vthread-call + "Executes f in another virtual thread, returning immediately to the calling + thread. Returns a channel which will receive the result of calling + f when completed, then close." + [f] + (let [c (async/chan 1)] + (let [binds (vars/get-thread-binding-frame)] + (.execute virtual-executor + (fn [] + (vars/reset-thread-binding-frame binds) + (try + (let [ret (f)] + (when-not (nil? ret) + (async/>!! c ret))) + (finally + (async/close! c)))))) + c)) + (defn thread [_ _ & body] `(~'clojure.core.async/thread-call (fn [] ~@body))) +(defn -vthread + [_ _ & body] + `(~'clojure.core.async/-vthread-call (fn [] ~@body))) + (defn alt!! "Like alt!, except as if by alts!!, will block until completed, and not intended for use in (go ...) blocks." @@ -38,10 +66,19 @@ (defn go-loop [_ _ bindings & body] - (list 'clojure.core.async/thread (list* 'loop bindings body))) + (list 'clojure.core.async/go (list* 'loop bindings body))) (def core-async-namespace (sci/create-ns 'clojure.core.async nil)) +(defn timeout [ms] + (if virtual-executor + (let [chan (async/chan nil)] + (.execute virtual-executor (fn [] + (Thread/sleep (long ms)) + (async/close! chan))) + chan) + (async/timeout ms))) + (def async-namespace {:obj core-async-namespace '! (copy-var async/>!! core-async-namespace {:name '>!}) 'alt! (macrofy 'alt! alt!! core-async-namespace) @@ -119,3 +159,4 @@ (def async-protocols-namespace {:obj async-protocols-ns 'ReadPort (copy-var protocols/ReadPort async-protocols-ns)}) +;; trigger CI diff --git a/src/babashka/impl/clojure/java/browse.clj b/src/babashka/impl/clojure/java/browse.clj index 37db104b..25718853 100644 --- a/src/babashka/impl/clojure/java/browse.clj +++ b/src/babashka/impl/clojure/java/browse.clj @@ -25,7 +25,7 @@ (case os :mac (sh "open" url) :linux (sh "xdg-open" url) - :windows (sh "cmd" "/C" "start" url))))) + :windows (sh "cmd" "/C" "start" (.replace url "&" "^&")))))) (def browse-namespace {'*open-url-script* open-url-script diff --git a/src/babashka/impl/clojure/reflect.clj b/src/babashka/impl/clojure/reflect.clj new file mode 100644 index 00000000..26e97244 --- /dev/null +++ b/src/babashka/impl/clojure/reflect.clj @@ -0,0 +1,7 @@ +(ns babashka.impl.clojure.reflect + (:require [clojure.reflect] + [sci.core :as sci])) + +(def rns (sci/create-ns 'clojure.reflect)) + +(def reflect-namespace {'reflect (sci/copy-var clojure.reflect/reflect rns)}) diff --git a/src/babashka/impl/clojure/test.clj b/src/babashka/impl/clojure/test.clj index 8903e911..05d01dba 100644 --- a/src/babashka/impl/clojure/test.clj +++ b/src/babashka/impl/clojure/test.clj @@ -316,8 +316,8 @@ Does nothing if *report-counters* is nil." {:added "1.1"} [name] - (when @report-counters - (swap! @report-counters update-in [name] (fnil inc 0)))) + (when-let [rc @report-counters] + (dosync (commute rc update-in [name] (fnil inc 0))))) ;;; TEST RESULT REPORTING @@ -660,14 +660,12 @@ ;;; DEFINING FIXTURES -(def ^:private ns->fixtures (atom {})) - (defn- add-ns-meta "Adds elements in coll to the current namespace metadata as the value of key." {:added "1.1"} [key coll] - (swap! ns->fixtures assoc-in [(sci-namespaces/sci-ns-name @sci/ns) key] coll)) + (alter-meta! @sci/ns assoc key coll)) (defmulti use-fixtures "Wrap test runs in a fixture function to perform setup and @@ -677,10 +675,10 @@ (fn [fixture-type & args] fixture-type)) (defmethod use-fixtures :each [fixture-type & args] - (add-ns-meta ::each-fixtures args)) + (add-ns-meta :clojure.test/each-fixtures args)) (defmethod use-fixtures :once [fixture-type & args] - (add-ns-meta ::once-fixtures args)) + (add-ns-meta :clojure.test/once-fixtures args)) (defn- default-fixture "The default, empty, fixture function. Just calls its argument." @@ -731,10 +729,8 @@ [vars] (doseq [[ns vars] (group-by (comp :ns meta) vars) :when ns] - (let [ns-name (sci-namespaces/sci-ns-name ns) - fixtures (get @ns->fixtures ns-name) - once-fixture-fn (join-fixtures (::once-fixtures fixtures)) - each-fixture-fn (join-fixtures (::each-fixtures fixtures))] + (let [once-fixture-fn (join-fixtures (:clojure.test/once-fixtures (meta ns))) + each-fixture-fn (join-fixtures (:clojure.test/each-fixtures (meta ns)))] (once-fixture-fn (fn [] (doseq [v vars] @@ -758,7 +754,7 @@ *report-counters*." {:added "1.1"} [ctx ns] - (sci/binding [report-counters (atom @initial-report-counters)] + (sci/binding [report-counters (ref @initial-report-counters)] (let [ns-obj (sci-namespaces/sci-the-ns ctx ns)] (do-report {:type :begin-test-ns, :ns ns-obj}) ;; If the namespace has a test-ns-hook function, call that: @@ -809,7 +805,7 @@ "Runs the tests for a single Var, with fixtures executed around the test, and summary output after." {:added "1.11"} [v] - (sci/binding [report-counters (atom @initial-report-counters)] + (sci/binding [report-counters (ref @initial-report-counters)] (let [ns-obj (-> v meta :ns) summary (do (do-report {:type :begin-test-ns diff --git a/src/babashka/impl/clojure/tools/reader.clj b/src/babashka/impl/clojure/tools/reader.clj index 29f67289..7e942e67 100644 --- a/src/babashka/impl/clojure/tools/reader.clj +++ b/src/babashka/impl/clojure/tools/reader.clj @@ -1,9 +1,11 @@ (ns babashka.impl.clojure.tools.reader - (:refer-clojure :exclude [read]) + (:refer-clojure :exclude [read read-string]) (:require + [clojure.tools.reader.reader-types :as rt] [edamame.core :as e] [sci.core :as sci] - [clojure.tools.reader.reader-types :as rt])) + [sci.ctx-store :as ctx] + [sci.impl.parser :as p])) (def rns (sci/create-ns 'clojure.tools.reader)) @@ -15,6 +17,15 @@ :location? seq? :end-location false})) +(def default-data-reader-fn (sci/new-dynamic-var '*default-data-reader-fn* nil {:ns rns})) +(def alias-map (sci/new-dynamic-var '*alias-map* nil {:ns rns})) + +(defn resolve-tag [sym] + ;; https://github.com/clojure/tools.reader/blob/ff18b1b872398a99e3e2941a0ed9abc0c2dec151/src/main/clojure/clojure/tools/reader.clj#L858 + (or (default-data-readers sym) + (when-let [f @default-data-reader-fn] + (f sym)))) + ;; Added for compatibility with tools.namespace (defn read "Reads the first object from an IPushbackReader or a java.io.PushbackReader. @@ -38,7 +49,17 @@ ([{eof :eof :as opts :or {eof :eofthrow}} reader] (let [opts (assoc default-opts :read-cond (:read-cond opts) - :features (:features opts)) + :features (:features opts) + :readers (fn [sym] + (resolve-tag sym)) + :auto-resolve (fn [alias] + (if (= :current alias) + (symbol (str @sci/ns)) + (or (when-let [alias-map @alias-map] + (@alias-map alias)) + (sci/eval-form (ctx/get-ctx) + (list 'get '(ns-aliases *ns*) + (list 'quote alias))))))) v (e/parse-next reader opts)] (if (identical? ::e/eof v) (if (identical? :eofthrow eof) @@ -53,4 +74,18 @@ sentinel) v)))) -(def reader-namespace {'read (sci/copy-var read rns)}) +(defn read-string + ([s] (read-string nil s)) + ([opts s] + (when (and s (not (identical? s ""))) + (read opts (rt/string-push-back-reader s))))) + +(defn resolve-symbol [sym] + (p/fully-qualify (ctx/get-ctx) sym)) + +(def reader-namespace + {'read (sci/copy-var read rns) + 'read-string (sci/copy-var read-string rns) + 'resolve-symbol (sci/copy-var resolve-symbol rns) + '*default-data-reader-fn* default-data-reader-fn + '*alias-map* alias-map}) diff --git a/src/babashka/impl/clojure/tools/reader_types.clj b/src/babashka/impl/clojure/tools/reader_types.clj index 15045405..92a6a1ca 100644 --- a/src/babashka/impl/clojure/tools/reader_types.clj +++ b/src/babashka/impl/clojure/tools/reader_types.clj @@ -15,4 +15,5 @@ 'read-char (sci/copy-var rt/read-char rtns) 'unread (sci/copy-var rt/unread rtns) 'source-logging-push-back-reader (sci/copy-var rt/source-logging-push-back-reader rtns) - 'source-logging-reader? (sci/copy-var rt/source-logging-reader? rtns)}) + 'source-logging-reader? (sci/copy-var rt/source-logging-reader? rtns) + 'string-push-back-reader (sci/copy-var rt/string-push-back-reader rtns)}) diff --git a/src/babashka/impl/deps.clj b/src/babashka/impl/deps.clj index c6cfb413..7d737376 100644 --- a/src/babashka/impl/deps.clj +++ b/src/babashka/impl/deps.clj @@ -3,6 +3,7 @@ [babashka.fs :as fs] [babashka.impl.classpath :as cp] [babashka.impl.common :refer [bb-edn]] + [babashka.process :as process] [borkdude.deps :as deps] [clojure.string :as str] [sci.core :as sci])) @@ -59,42 +60,61 @@ then used to resolve dependencies in babashka." ([deps-map] (add-deps deps-map nil)) ([deps-map {:keys [:aliases :env :extra-env :force]}] - (when-let [paths (:paths deps-map)] - (let [paths (if-let [deps-root (:deps-root @bb-edn)] - (let [deps-root (fs/absolutize deps-root) - paths (mapv #(str (fs/file deps-root %)) paths)] - paths) - paths)] - (cp/add-classpath (str/join cp/path-sep paths)))) - (let [need-deps? (or (seq (:deps deps-map)) - (and (:aliases deps-map) - aliases))] - (when need-deps? - (let [deps-map (dissoc deps-map - ;; paths are added manually above - ;; extra-paths are added as :paths in tasks - :paths :tasks :raw :file :deps-root - :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 - :classpath-overrides (cond-> - '{org.clojure/clojure "" - org.clojure/spec.alpha ""} - ;; only remove core specs when they are not mentioned in deps map - (not (str/includes? (str deps-map) "org.clojure/core.specs.alpha")) - (assoc 'org.clojure/core.specs.alpha ""))}) - args (list "-Srepro" ;; do not include deps.edn from user config - "-Spath" "-Sdeps" (str deps-map) - "-Sdeps-file" "") ;; we reset deps file so the local deps.edn isn't used - args (if force (cons "-Sforce" args) args) - args (concat args [(str "-A:" (str/join ":" (cons ":org.babashka/defaults" aliases)))]) - cp (with-out-str (binding [deps/*env* env - deps/*extra-env* extra-env] - (apply deps/-main args))) - cp (str/trim cp) - cp (str/replace cp (re-pattern (str cp/path-sep "+$")) "")] - (cp/add-classpath cp)))))))) + (let [deps-root (:deps-root @bb-edn)] + (when-let [paths (:paths deps-map)] + (let [paths (if deps-root + (let [deps-root (fs/absolutize deps-root) + paths (mapv #(str (fs/file deps-root %)) paths)] + paths) + paths)] + (cp/add-classpath (str/join cp/path-sep paths)))) + (let [need-deps? (or (seq (:deps deps-map)) + (and (:aliases deps-map) + aliases))] + (when need-deps? + (let [deps-map (dissoc deps-map + ;; paths are added manually above + ;; extra-paths are added as :paths in tasks + :paths :tasks :raw :file :deps-root + :min-bb-version) + ;; associate deps-root to avoid cache conflict between different + ;; bb.edns with relative local/roots by the same name NOTE: + ;; deps-root is nil when bb.edn isn't used, so clashes may still + ;; happen with dynamic add-deps, but at least we don't invoke + ;; clojure CLI's java process each time we call a script from a + ;; different directory. + deps-map (assoc deps-map :deps-root (str deps-root))] + (binding [*print-namespace-maps* false] + (let [deps-map (assoc-in deps-map [:aliases :org.babashka/defaults] + {:replace-paths [] ;; babashka sets paths manually + :classpath-overrides (cond-> + '{org.clojure/clojure "" + org.clojure/spec.alpha ""} + ;; only remove core specs when they are not mentioned in deps map + (not (str/includes? (str deps-map) "org.clojure/core.specs.alpha")) + (assoc 'org.clojure/core.specs.alpha ""))}) + args (list "-Srepro" ;; do not include deps.edn from user config + "-Spath" "-Sdeps" (str deps-map) + "-Sdeps-file" "__babashka_no_deps_file__.edn") ;; we reset deps file so the local deps.edn isn't used + args (if force (cons "-Sforce" args) args) + args (concat args [(str "-A:" (str/join ":" (cons ":org.babashka/defaults" aliases)))]) + bindings (cond-> + {#'deps/*aux-process-fn* (fn [{:keys [cmd out]}] + (process/shell + {:cmd cmd + :out out + :env env + :dir (when deps-root (str deps-root)) + :extra-env extra-env})) + #'deps/*exit-fn* (fn [{:keys [message]}] + (when message + (throw (Exception. message))))} + deps-root (assoc #'deps/*dir* (str deps-root))) + cp (with-out-str (with-bindings bindings + (apply deps/-main args))) + cp (str/trim cp) + cp (str/replace cp (re-pattern (str cp/path-sep "+$")) "")] + (cp/add-classpath cp))))))))) (def deps-namespace {'add-deps (sci/copy-var add-deps dns) diff --git a/src/babashka/impl/http_client.clj b/src/babashka/impl/http_client.clj new file mode 100644 index 00000000..c07ccfe7 --- /dev/null +++ b/src/babashka/impl/http_client.clj @@ -0,0 +1,15 @@ +(ns babashka.impl.http-client + (:require + [babashka.http-client] + [babashka.http-client.websocket] + [sci.core :as sci])) + +(def hns (sci/create-ns 'babashka.http-client)) +(def wns (sci/create-ns 'babashka.http-client.websocket)) + +(def http-client-namespace + (sci/copy-ns babashka.http-client hns)) + +(def http-client-websocket-namespace + (sci/copy-ns babashka.http-client.websocket wns)) + diff --git a/src/babashka/impl/nrepl_server.clj b/src/babashka/impl/nrepl_server.clj index 5b3569cb..ab3c0c28 100644 --- a/src/babashka/impl/nrepl_server.clj +++ b/src/babashka/impl/nrepl_server.clj @@ -1,11 +1,18 @@ (ns babashka.impl.nrepl-server {:no-doc true} (:require - [babashka.impl.clojure.core] + [babashka.impl.classpath :as cp] + [babashka.impl.clojure.core :as core-extras] [babashka.impl.common :as common] + [babashka.nrepl.impl.server :refer [process-msg]] [babashka.nrepl.server :as server] [sci.core :as sci])) +(defmethod process-msg :classpath [rf result m] + (rf result {:response {"status" ["done"] + "classpath" (cp/split-classpath (cp/get-classpath))} + :response-for (:msg m)})) + (defn start-server! ([] (start-server! nil)) @@ -13,7 +20,11 @@ (let [dev? (= "true" (System/getenv "BABASHKA_DEV")) opts (merge {:debug dev? :describe {"versions" {"babashka" common/version}} - :thread-bind [babashka.impl.clojure.core/warn-on-reflection]} + :thread-bind [core-extras/warn-on-reflection + core-extras/unchecked-math + core-extras/data-readers + sci/ns + sci/print-length]} opts)] (server/start-server! (common/ctx) opts)))) diff --git a/src/babashka/impl/print_deps.clj b/src/babashka/impl/print_deps.clj index 0e9d0288..593fd836 100644 --- a/src/babashka/impl/print_deps.clj +++ b/src/babashka/impl/print_deps.clj @@ -14,11 +14,11 @@ edn/read-string) deps (:deps deps) deps (assoc deps - 'babashka/fs {:mvn/version "0.2.12"} + 'babashka/fs {:mvn/version "0.4.19"} 'babashka/babashka.curl {:mvn/version "0.1.2"} 'babashka/babashka.core {:git/url "https://github.com/babashka/babashka.core" :git/sha "52a6037bd4b632bffffb04394fb4efd0cdab6b1e"} - 'babashka/process {:mvn/version "0.4.13"}) + 'babashka/process {:mvn/version "0.5.21"}) deps (dissoc deps 'borkdude/sci 'org.babashka/sci @@ -30,6 +30,7 @@ 'org.hsqldb/hsqldb) bb-edn-deps (:deps @common/bb-edn) deps (merge deps bb-edn-deps) + deps (into (sorted-map) deps) paths (:paths @common/bb-edn) deps {:deps deps} deps (cond-> deps diff --git a/src/babashka/impl/process.clj b/src/babashka/impl/process.clj index 33d77ea3..a931e23c 100644 --- a/src/babashka/impl/process.clj +++ b/src/babashka/impl/process.clj @@ -20,6 +20,18 @@ (binding [process/*defaults* @defaults] (apply process/pb args))) +(defn sh [& args] + (binding [process/*defaults* @defaults] + (apply process/sh args))) + +(defn shell [& args] + (binding [process/*defaults* @defaults] + (apply process/shell args))) + +(defn exec [& args] + (binding [process/*defaults* @defaults] + (apply process/exec args))) + (def process-namespace {'parse-args (copy-var process/parse-args tns) 'process* (copy-var process/process* tns) @@ -29,11 +41,11 @@ 'start (copy-var process/start tns) 'pipeline (copy-var process/pipeline tns) '$ (copy-var process/$ tns) - 'sh (copy-var process/sh tns) + 'sh (copy-var sh tns) 'tokenize (copy-var process/tokenize tns) '*defaults* defaults 'destroy (copy-var process/destroy tns) 'destroy-tree (copy-var process/destroy-tree tns) - 'exec (copy-var process/exec tns) - 'shell (copy-var process/shell tns) + 'exec (copy-var exec tns) + 'shell (copy-var shell tns) 'alive? (copy-var process/alive? tns)}) diff --git a/src/babashka/impl/proxy.clj b/src/babashka/impl/proxy.clj index f40b7ab1..7e4ad722 100644 --- a/src/babashka/impl/proxy.clj +++ b/src/babashka/impl/proxy.clj @@ -100,6 +100,36 @@ (write ([b] ((method-or-bust methods 'write) this b)) ([b off len] ((method-or-bust methods 'write) this b off len)))) + + ["java.io.OutputStream" #{}] + (proxy [java.io.OutputStream] [] + (close [] (when-let [m (get methods 'close)] + (m this))) + (flush [] (when-let [m (get methods 'flush)] + (m this))) + (write + ([b] + ((method-or-bust methods 'write) this b)) + ([b off len] + ((method-or-bust methods 'write) this b off len)))) + ["javax.net.ssl.X509ExtendedTrustManager" #{}] + (proxy [javax.net.ssl.X509ExtendedTrustManager] [] + (checkClientTrusted + ([x y] + ((method-or-bust methods 'checkClientTrusted) this x y)) + ([x y z] + ((method-or-bust methods 'checkClientTrusted) this x y z))) + (checkServerTrusted + ([x y] + ((method-or-bust methods 'checkServerTrusted) this x y)) + ([x y z] + ((method-or-bust methods 'checkServerTrusted) this x y z))) + (getAcceptedIssuers [] ((method-or-bust methods 'getAcceptedIssuers) this))) + + ["java.lang.ThreadLocal" #{}] + (proxy [java.lang.ThreadLocal] [] + (initialValue [] + ((method-or-bust methods 'initialValue) this))) , ;; keep this for merge friendliness ))) @@ -112,7 +142,9 @@ {:methods [{:name "connectFailed"} {:name "select"}]} (class-sym (class (proxy-fn {:class javax.net.ssl.HostnameVerifier}))) - {:methods [{:name "verify"}]}}) + {:methods [{:name "verify"}]} + (class-sym (class (proxy-fn {:class java.lang.ThreadLocal}))) + {:methods [{:name "get"}]}}) ;;; Scratch diff --git a/src/babashka/impl/repl.clj b/src/babashka/impl/repl.clj index 11dcf370..86fd58e6 100644 --- a/src/babashka/impl/repl.clj +++ b/src/babashka/impl/repl.clj @@ -1,6 +1,7 @@ (ns babashka.impl.repl {:no-doc true} (:require + [babashka.impl.clojure.core :as core-extras] [babashka.impl.clojure.main :as m] [clojure.java.io :as io] [clojure.string :as str] @@ -50,41 +51,46 @@ ([sci-ctx] (repl sci-ctx nil)) ([sci-ctx {:keys [:init :read :eval :need-prompt :prompt :flush :print :caught]}] (let [in @sci/in] - (m/repl - :init (or init - (fn [] - (sci/with-bindings {sci/out @sci/err} - (sio/println "Babashka" - (str "v" (str/trim (slurp (io/resource "BABASHKA_VERSION")))) - "REPL.") - (sio/println "Use :repl/quit or :repl/exit to quit the REPL.") - (sio/println "Clojure rocks, Bash reaches.") - (sio/println)) - (eval-form sci-ctx `(apply require (quote ~m/repl-requires))))) - :read (or read - (fn [_request-prompt request-exit] - (if (nil? (r/peek-char in)) - request-exit - (let [v (parser/parse-next sci-ctx in)] - (skip-if-eol in) - (if (or (identical? :repl/quit v) - (identical? :repl/exit v)) - request-exit - v))))) - :eval (or eval - (fn [expr] - (sci/with-bindings {sci/file "" - sci/*1 *1 - sci/*2 *2 - sci/*3 *3 - sci/*e *e} - (let [ret (eval-form sci-ctx expr)] - ret)))) - :need-prompt (or need-prompt (fn [] true)) - :prompt (or prompt #(sio/printf "%s=> " (utils/current-ns-name))) - :flush (or flush sio/flush) - :print (or print sio/prn) - :caught (or caught repl-caught))))) + (sci/binding [core-extras/warn-on-reflection @core-extras/warn-on-reflection + core-extras/unchecked-math @core-extras/unchecked-math + core-extras/data-readers @core-extras/data-readers + sci/ns @sci/ns + sci/print-length @sci/print-length] + (m/repl + :init (or init + (fn [] + (sci/with-bindings {sci/out @sci/err} + (sio/println "Babashka" + (str "v" (str/trim (slurp (io/resource "BABASHKA_VERSION")))) + "REPL.") + (sio/println "Use :repl/quit or :repl/exit to quit the REPL.") + (sio/println "Clojure rocks, Bash reaches.") + (sio/println)) + (eval-form sci-ctx `(apply require (quote ~m/repl-requires))))) + :read (or read + (fn [_request-prompt request-exit] + (if (nil? (r/peek-char in)) + request-exit + (let [v (parser/parse-next sci-ctx in)] + (skip-if-eol in) + (if (or (identical? :repl/quit v) + (identical? :repl/exit v)) + request-exit + v))))) + :eval (or eval + (fn [expr] + (sci/with-bindings {sci/file "" + sci/*1 *1 + sci/*2 *2 + sci/*3 *3 + sci/*e *e} + (let [ret (eval-form sci-ctx expr)] + ret)))) + :need-prompt (or need-prompt (fn [] true)) + :prompt (or prompt #(sio/printf "%s=> " (utils/current-ns-name))) + :flush (or flush sio/flush) + :print (or print sio/prn) + :caught (or caught repl-caught)))))) (defn start-repl! ([sci-ctx] (start-repl! sci-ctx nil)) diff --git a/src/babashka/impl/sci.clj b/src/babashka/impl/sci.clj new file mode 100644 index 00000000..b930a418 --- /dev/null +++ b/src/babashka/impl/sci.clj @@ -0,0 +1,52 @@ +(ns babashka.impl.sci + {:no-doc true} + (:require [sci.core :as sci] + [sci.ctx-store :as store])) + +(def sns (sci/create-ns 'sci.core nil)) + +(defmacro copy-ns + "Returns map of names to SCI vars as a result of copying public + Clojure vars from ns-sym (a symbol). Attaches sci-ns (result of + sci/create-ns) to meta. Copies :name, :macro :doc, :no-doc + and :argslists metadata. + + Options: + + - :exclude: a seqable of names to exclude from the + namespace. Defaults to none. + + - :copy-meta: a seqable of keywords to copy from the original var + meta. Use :all instead of a seqable to copy all. Defaults + to [:doc :arglists :macro]. + + - :exclude-when-meta: seqable of keywords; vars with meta matching + these keys are excluded. Defaults to [:no-doc :skip-wiki] + + The selection of vars is done at compile time which is mostly + important for ClojureScript to not pull in vars into the compiled + JS. Any additional vars can be added after the fact with sci/copy-var + manually." + ([ns-sym sci-ns] `(sci.core/copy-ns ~ns-sym ~sci-ns nil)) + ([ns-sym sci-ns opts] + ;; this branch is hit by macroexpanding in JVM Clojure, not in the CLJS compiler + (let [publics-map (sci/eval-form (store/get-ctx) (list 'ns-publics (list 'quote ns-sym))) + publics-map (#'sci/process-publics publics-map opts) + mf (#'sci/meta-fn (:copy-meta opts)) + publics-map (#'sci/exclude-when-meta + publics-map + meta + (fn [k] + (list 'quote k)) + (fn [var m] + {:name (list 'quote (:name m)) + :var var + :meta (list 'quote (mf m))}) + (or (:exclude-when-meta opts) + [:no-doc :skip-wiki]))] + `(sci.core/-copy-ns ~publics-map ~sci-ns)))) + +(def sci-core-namespace + (assoc (sci/copy-ns sci.core sns {:exclude [copy-ns]}) + 'copy-ns (sci/copy-var copy-ns sns) + '-copy-ns (sci/copy-var sci/-copy-ns sns))) diff --git a/src/babashka/impl/tasks.clj b/src/babashka/impl/tasks.clj index f2775b8f..3fe45785 100644 --- a/src/babashka/impl/tasks.clj +++ b/src/babashka/impl/tasks.clj @@ -6,7 +6,6 @@ [babashka.impl.process :as pp] [babashka.process :as p] [clojure.core.async :refer [ args + cmd (->> (cons cmd))) local-log-level (:log-level opts)] (sci/binding [log-level (or local-log-level @log-level)] (apply log-info (cons "clojure" cmd)) - (handle-non-zero (deps/clojure cmd (merge default-opts opts)) opts)))) + (handle-non-zero (apply deps/clojure (merge default-opts opts) cmd) opts)))) (defn -wait [res] (when res @@ -139,7 +127,7 @@ "Used internally for debugging" [& strs] (locking o - (apply prn strs))) + (apply prn strs))) (defn wait-tasks [deps] (if deps @@ -397,7 +385,7 @@ loc (zip/down loc)] (into [] (comp - (take-nth 2 ) + (take-nth 2) (take-while #(not (zip/end? %))) (filter zip/sexpr-able?) (map zip/sexpr) @@ -437,8 +425,11 @@ ([task] (run task nil)) ([task {:keys [:parallel] :or {parallel (:parallel (current-task))}}] - (let [[[expr]] (assemble-task task parallel)] - (sci/eval-string* (ctx) expr)))) + (let [[[expr] exit-code] (assemble-task task parallel)] + (if (or (nil? exit-code) (zero? exit-code)) + (sci/eval-string* (ctx) expr) + (throw (ex-info nil + {:babashka/exit exit-code})))))) (defn exec ([sym] diff --git a/src/babashka/main.clj b/src/babashka/main.clj index 6d117011..9626a9c0 100644 --- a/src/babashka/main.clj +++ b/src/babashka/main.clj @@ -19,13 +19,14 @@ [babashka.impl.clojure.java.shell :refer [shell-namespace]] [babashka.impl.clojure.main :as clojure-main :refer [demunge]] [babashka.impl.clojure.math :refer [math-namespace]] + [babashka.impl.clojure.reflect :refer [reflect-namespace]] [babashka.impl.clojure.stacktrace :refer [stacktrace-namespace]] [babashka.impl.clojure.tools.reader :refer [reader-namespace]] [babashka.impl.clojure.tools.reader-types :refer [edn-namespace reader-types-namespace]] [babashka.impl.clojure.zip :refer [zip-namespace]] [babashka.impl.common :as common] - [babashka.impl.core :as bbcore] + [babashka.impl.core :as bbcore] [babashka.impl.curl :refer [curl-namespace]] [babashka.impl.data :as data] [babashka.impl.datafy :refer [datafy-namespace]] @@ -34,6 +35,7 @@ [babashka.impl.error-handler :refer [error-handler]] [babashka.impl.features :as features] [babashka.impl.fs :refer [fs-namespace]] + [babashka.impl.http-client :refer [http-client-namespace http-client-websocket-namespace]] [babashka.impl.nrepl-server :refer [nrepl-server-namespace]] [babashka.impl.pods :as pods] [babashka.impl.pprint :refer [pprint-namespace]] @@ -44,6 +46,7 @@ [babashka.impl.reify2 :refer [reify-fn]] [babashka.impl.repl :as repl] [babashka.impl.rewrite-clj :as rewrite] + [babashka.impl.sci :refer [sci-core-namespace]] [babashka.impl.server :refer [clojure-core-server-namespace]] [babashka.impl.socket-repl :as socket-repl] [babashka.impl.tasks :as tasks :refer [tasks-namespace]] @@ -55,6 +58,7 @@ [clojure.edn :as edn] [clojure.java.io :as io] [clojure.string :as str] + [edamame.core :as edamame] [hf.depstar.uberjar :as uberjar] [sci.addons :as addons] [sci.core :as sci] @@ -62,6 +66,7 @@ [sci.impl.copy-vars :as sci-copy-vars] [sci.impl.io :as sio] [sci.impl.namespaces :as sci-namespaces] + [sci.impl.parser] [sci.impl.types :as sci-types] [sci.impl.unrestrict :refer [*unrestricted*]] [sci.impl.vars :as vars]) @@ -139,6 +144,7 @@ (println " Usage: bb [svm-opts] [global-opts] [eval opts] [cmdline args] or: bb [svm-opts] [global-opts] file [cmdline args] +or: bb [svm-opts] [global-opts] task [cmdline args] or: bb [svm-opts] [global-opts] subcommand [subcommand opts] [cmdline args] Substrate VM opts: @@ -151,10 +157,13 @@ Global opts: -cp, --classpath Classpath to use. Overrides bb.edn classpath. --debug Print debug information and internal stacktrace in case of exception. --init Load file after any preloads and prior to evaluation/subcommands. - --config Replacing bb.edn with file. Relative paths are resolved relative to file. + --config Replace bb.edn with file. Defaults to bb.edn adjacent to invoked file or bb.edn in current dir. Relative paths are resolved relative to bb.edn. --deps-root Treat dir as root of relative paths in config. + --prn Print result via clojure.core/prn -Sforce Force recalculation of the classpath (don't use the cache) -Sdeps Deps data to use as the last deps file to be merged + -f, --file Run file + --jar Run uberjar Help: @@ -166,7 +175,6 @@ Help: Evaluation: -e, --eval Evaluate an expression. - -f, --file Evaluate a file. -m, --main Call the -main function from a namespace or call a fully qualified var. -x, --exec Call the fully qualified var. Args are parsed by babashka CLI. @@ -232,8 +240,7 @@ When no eval opts or subcommand is provided, the implicit subcommand is repl.") (clojure.repl/doc %1$s) true)" arg))) [nil 0] - [nil 1])) - ,) + [nil 1]))) (defn print-run-help [] (println (str/trim " @@ -320,25 +327,25 @@ Use bb run --help to show this help output. (def aliases (cond-> - '{str clojure.string - set clojure.set - tools.cli clojure.tools.cli - edn clojure.edn - wait babashka.wait - signal babashka.signal - shell clojure.java.shell - io clojure.java.io - json cheshire.core - curl babashka.curl - fs babashka.fs - bencode bencode.core - deps babashka.deps - async clojure.core.async} - features/xml? (assoc 'xml 'clojure.data.xml) - features/yaml? (assoc 'yaml 'clj-yaml.core) - features/jdbc? (assoc 'jdbc 'next.jdbc) - features/csv? (assoc 'csv 'clojure.data.csv) - features/transit? (assoc 'transit 'cognitect.transit))) + '{str clojure.string + set clojure.set + tools.cli clojure.tools.cli + edn clojure.edn + wait babashka.wait + signal babashka.signal + shell clojure.java.shell + io clojure.java.io + json cheshire.core + curl babashka.curl + fs babashka.fs + bencode bencode.core + deps babashka.deps + async clojure.core.async} + features/xml? (assoc 'xml 'clojure.data.xml) + features/yaml? (assoc 'yaml 'clj-yaml.core) + features/jdbc? (assoc 'jdbc 'next.jdbc) + features/csv? (assoc 'csv 'clojure.data.csv) + features/transit? (assoc 'transit 'cognitect.transit))) ;;(def ^:private server-ns-obj (sci/create-ns 'clojure.core.server nil)) @@ -349,85 +356,85 @@ Use bb run --help to show this help output. (defn catvec [& xs] (into [] cat xs)) -(def sci-ns (sci/create-ns 'sci.core)) +(def main-var (sci/new-var 'main nil {:ns clojure-main-ns})) (require '[clojure.reflect] '[clojure.core.memoize :as memoize]) (def namespaces (cond-> - {'user {'*input* (reify - sci-types/Eval - (eval [_ _ctx _bindings] - (force @input-var)))} - 'clojure.tools.cli tools-cli-namespace - 'clojure.java.shell shell-namespace - 'babashka.core bbcore/core-namespace - 'babashka.nrepl.server nrepl-server-namespace - 'babashka.wait wait-namespace - 'babashka.signal signal-ns - 'clojure.java.io io-namespace - 'cheshire.core cheshire-core-namespace - 'clojure.data data/data-namespace - 'clojure.instant instant/instant-namespace - 'clojure.stacktrace stacktrace-namespace - 'clojure.zip zip-namespace - 'clojure.main {:obj clojure-main-ns - 'demunge (sci/copy-var demunge clojure-main-ns) - 'repl-requires (sci/copy-var clojure-main/repl-requires clojure-main-ns) - 'repl (sci/new-var 'repl - (fn [& opts] - (let [opts (apply hash-map opts)] - (repl/start-repl! (common/ctx) opts))) {:ns clojure-main-ns}) - 'with-bindings (sci/copy-var clojure-main/with-bindings clojure-main-ns)} - 'clojure.test t/clojure-test-namespace - 'clojure.math math-namespace - 'babashka.classpath classpath-namespace - 'babashka.classes classes-namespace - 'clojure.pprint pprint-namespace - 'babashka.curl curl-namespace - 'babashka.fs fs-namespace - 'babashka.pods pods/pods-namespace - 'bencode.core bencode-namespace - 'clojure.java.browse browse-namespace - 'clojure.datafy datafy-namespace - 'clojure.core.protocols protocols-namespace - 'babashka.process process-namespace - 'clojure.core.server clojure-core-server-namespace - 'babashka.deps deps-namespace - 'babashka.tasks tasks-namespace - 'clojure.tools.reader.edn edn-namespace - 'clojure.tools.reader.reader-types reader-types-namespace - 'clojure.tools.reader reader-namespace - 'clojure.core.async async-namespace - 'clojure.core.async.impl.protocols async-protocols-namespace - 'rewrite-clj.node rewrite/node-namespace - 'rewrite-clj.paredit rewrite/paredit-namespace - 'rewrite-clj.parser rewrite/parser-namespace - 'rewrite-clj.zip rewrite/zip-namespace - 'rewrite-clj.zip.subedit rewrite/subedit-namespace - 'clojure.core.rrb-vector (if features/rrb-vector? - @(resolve 'babashka.impl.rrb-vector/rrb-vector-namespace) - {'catvec (sci/copy-var catvec - (sci/create-ns 'clojure.core.rrb-vector))}) - 'edamame.core edamame-namespace - 'sci.core {'format-stacktrace (sci/copy-var sci/format-stacktrace sci-ns) - 'stacktrace (sci/copy-var sci/stacktrace sci-ns) - ;; 'eval-string (sci/copy-var sci/eval-string sci-ns) - ;; 'eval-string* (sci/copy-var sci/eval-string* sci-ns) - ;; 'init (sci/copy-var sci/init sci-ns) - ;; 'fork (sci/copy-var sci/fork sci-ns) - } - 'babashka.cli cli/cli-namespace - } - features/xml? (assoc 'clojure.data.xml @(resolve 'babashka.impl.xml/xml-namespace) - 'clojure.data.xml.event @(resolve 'babashka.impl.xml/xml-event-namespace) - 'clojure.data.xml.tree @(resolve 'babashka.impl.xml/xml-tree-namespace)) + {'user {'*input* (reify + sci-types/Eval + (eval [_ _ctx _bindings] + (force @input-var)))} + 'clojure.core core-extras + 'clojure.tools.cli tools-cli-namespace + 'clojure.java.shell shell-namespace + 'babashka.core bbcore/core-namespace + 'babashka.nrepl.server nrepl-server-namespace + 'babashka.wait wait-namespace + 'babashka.signal signal-ns + 'clojure.java.io io-namespace + 'cheshire.core cheshire-core-namespace + 'clojure.data data/data-namespace + 'clojure.instant instant/instant-namespace + 'clojure.stacktrace stacktrace-namespace + 'clojure.zip zip-namespace + 'clojure.main {:obj clojure-main-ns + 'demunge (sci/copy-var demunge clojure-main-ns) + 'repl-requires (sci/copy-var clojure-main/repl-requires clojure-main-ns) + 'repl (sci/new-var 'repl + (fn [& opts] + (let [opts (apply hash-map opts)] + (repl/start-repl! (common/ctx) opts))) {:ns clojure-main-ns}) + 'with-bindings (sci/copy-var clojure-main/with-bindings clojure-main-ns) + 'repl-caught (sci/copy-var repl/repl-caught clojure-main-ns) + 'main main-var} + 'clojure.test t/clojure-test-namespace + 'clojure.math math-namespace + 'babashka.classpath classpath-namespace + 'babashka.classes classes-namespace + 'clojure.pprint pprint-namespace + 'babashka.curl curl-namespace + 'babashka.fs fs-namespace + 'babashka.pods pods/pods-namespace + 'bencode.core bencode-namespace + 'clojure.java.browse browse-namespace + 'clojure.datafy datafy-namespace + 'clojure.core.protocols protocols-namespace + 'babashka.process process-namespace + 'clojure.core.server clojure-core-server-namespace + 'babashka.deps deps-namespace + 'babashka.tasks tasks-namespace + 'clojure.tools.reader.edn edn-namespace + 'clojure.tools.reader.reader-types reader-types-namespace + 'clojure.tools.reader reader-namespace + 'clojure.core.async async-namespace + 'clojure.core.async.impl.protocols async-protocols-namespace + 'clojure.reflect reflect-namespace + 'rewrite-clj.node rewrite/node-namespace + 'rewrite-clj.paredit rewrite/paredit-namespace + 'rewrite-clj.parser rewrite/parser-namespace + 'rewrite-clj.zip rewrite/zip-namespace + 'rewrite-clj.zip.subedit rewrite/subedit-namespace + 'clojure.core.rrb-vector (if features/rrb-vector? + @(resolve 'babashka.impl.rrb-vector/rrb-vector-namespace) + {'catvec (sci/copy-var catvec + (sci/create-ns 'clojure.core.rrb-vector))}) + 'edamame.core edamame-namespace + 'sci.core sci-core-namespace + 'babashka.cli cli/cli-namespace + 'babashka.http-client http-client-namespace + 'babashka.http-client.websocket http-client-websocket-namespace} + features/xml? (assoc 'clojure.data.xml @(resolve 'babashka.impl.xml/xml-namespace) + 'clojure.data.xml.event @(resolve 'babashka.impl.xml/xml-event-namespace) + 'clojure.data.xml.tree @(resolve 'babashka.impl.xml/xml-tree-namespace)) features/yaml? (assoc 'clj-yaml.core @(resolve 'babashka.impl.yaml/yaml-namespace) - 'flatland.ordered.map @(resolve 'babashka.impl.ordered/ordered-map-ns)) + 'flatland.ordered.map @(resolve 'babashka.impl.ordered/ordered-map-ns) + 'flatland.ordered.set @(resolve 'babashka.impl.ordered/ordered-set-ns)) features/jdbc? (assoc 'next.jdbc @(resolve 'babashka.impl.jdbc/njdbc-namespace) 'next.jdbc.sql @(resolve 'babashka.impl.jdbc/next-sql-namespace) 'next.jdbc.result-set @(resolve 'babashka.impl.jdbc/result-set-namespace)) - features/csv? (assoc 'clojure.data.csv @(resolve 'babashka.impl.csv/csv-namespace)) + features/csv? (assoc 'clojure.data.csv @(resolve 'babashka.impl.csv/csv-namespace)) features/transit? (assoc 'cognitect.transit @(resolve 'babashka.impl.transit/transit-namespace)) features/datascript? (assoc 'datascript.core @(resolve 'babashka.impl.datascript/datascript-namespace) 'datascript.db @(resolve 'babashka.impl.datascript/datascript-db-namespace)) @@ -455,8 +462,8 @@ Use bb run --help to show this help output. @(resolve 'babashka.impl.clojure.test.check/test-check-namespace) ;; it's better to load this from source by adding the clojure.test.check dependency #_#_'clojure.test.check.clojure-test - @(resolve 'babashka.impl.clojure.test.check/test-check-clojure-test-namespace)) - features/spec-alpha? (-> (assoc ;; spec + @(resolve 'babashka.impl.clojure.test.check/test-check-clojure-test-namespace)) + features/spec-alpha? (-> (assoc ;; spec 'clojure.spec.alpha @(resolve 'babashka.impl.spec/spec-namespace) 'clojure.spec.gen.alpha @(resolve 'babashka.impl.spec/gen-namespace) 'clojure.spec.test.alpha @(resolve 'babashka.impl.spec/test-namespace))) @@ -471,6 +478,7 @@ Use bb run --help to show this help output. 'selmer.validator @(resolve 'babashka.impl.selmer/selmer-validator-namespace)) features/logging? (assoc 'taoensso.timbre @(resolve 'babashka.impl.logging/timbre-namespace) + 'taoensso.timbre.appenders.core @(resolve 'babashka.impl.logging/timbre-appenders-namespace) 'clojure.tools.logging @(resolve 'babashka.impl.logging/tools-logging-namespace) 'clojure.tools.logging.impl @@ -487,7 +495,8 @@ Use bb run --help to show this help output. (def edn-readers (cond-> {} features/yaml? - (assoc 'ordered/map @(resolve 'flatland.ordered.map/ordered-map)) + (assoc 'ordered/map @(resolve 'flatland.ordered.map/ordered-map) + 'ordered/set @(resolve 'flatland.ordered.set/ordered-set)) features/xml? (assoc 'xml/ns @(resolve 'clojure.data.xml.name/uri-symbol) 'xml/element @(resolve 'clojure.data.xml.node/tagged-element)))) @@ -575,10 +584,10 @@ Use bb run --help to show this help output. :edn-in true)) ("-o") (recur (next options) (assoc opts-map - :shell-out true)) + :shell-out true :prn true)) ("-O") (recur (next options) (assoc opts-map - :edn-out true)) + :edn-out true :prn true)) ("-io") (recur (next options) (assoc opts-map :shell-in true @@ -609,16 +618,6 @@ Use bb run --help to show this help output. (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) @@ -645,16 +644,17 @@ Use bb run --help to show this help output. (assoc opts-map :nrepl (or opt "1667")))) ("--eval", "-e") - (let [options (next options)] + (let [options (next options) + opts-map (assoc opts-map :prn true)] (recur (next options) (update opts-map :expressions (fnil conj []) (first options)))) - ("--main", "-m",) + ("--main", "-m") (let [options (next options)] (assoc opts-map :main (first options) :command-line-args (if (= "--" (second options)) (nthrest options 2) (rest options)))) - ("--exec", "-x",) + ("--exec", "-x") (let [options (next options)] (assoc opts-map :exec (first options) :command-line-args (if (= "--" (second options)) @@ -681,6 +681,7 @@ Use bb run --help to show this help output. (case c (\( \{ \[ \* \@ \#) (-> opts-map + (assoc :prn true) (update :expressions (fnil conj []) (first options)) (assoc :command-line-args (next options))) (assoc opts-map @@ -716,36 +717,49 @@ Use bb run --help to show this help output. ("--deps-root") (recur (nnext options) (assoc opts-map :deps-root (second options))) + ("--prn") + (recur (next options) (assoc opts-map :prn true)) + ("-f" "--file") + (recur (nnext options) (assoc opts-map :file (second options))) + ("-jar" "--jar") + (recur (nnext options) (assoc opts-map :jar (second options))) [options opts-map]) [options opts-map]))) (defn parse-file-opt [options opts-map] - (let [opt (first options) - opts-key (if (str/ends-with? opt ".jar") - :jar :file)] - (assoc opts-map - opts-key opt - :command-line-args (next options)))) + (let [opt (first options)] + (if (and opt (and (fs/exists? opt) + (not (fs/directory? opt)))) + [nil (assoc opts-map + (if (str/ends-with? opt ".jar") + :jar :file) opt + :command-line-args (next options))] + [options opts-map]))) (defn parse-opts ([options] (parse-opts options nil)) ([options opts-map] (let [opt (first options) - tasks (into #{} (map str) (keys (:tasks @common/bb-edn)))] + task-map (:tasks @common/bb-edn) + tasks (into #{} (map str) (keys task-map))] + (when-let [commands (seq (filter (fn [task] + (and (command? task) + (not (:override-builtin (get task-map (symbol task)))))) + tasks))] + (binding [*out* *err*] + (println "[babashka] WARNING: task(s)" (str/join ", " (map #(format "'%s'" %) commands)) "override built-in command(s). Use :override-builtin true to disable warning."))) (if-not opt opts-map ;; FILE > TASK > SUBCOMMAND (cond - (.isFile (io/file opt)) - (if (or (:file opts-map) (:jar opts-map)) - opts-map ; we've already parsed the file opt - (parse-file-opt options opts-map)) - + (and (not (or (:file opts-map) + (:jar opts-map))) + (.isFile (io/file opt))) + (parse-file-opt options opts-map) (contains? tasks opt) (assoc opts-map :run opt :command-line-args (next options)) - (command? opt) (recur (cons (str "--" opt) (next options)) opts-map) @@ -775,13 +789,56 @@ Use bb run --help to show this help output. env-os-name-present? (not= env-os-name sys-os-name) env-os-arch-present? (not= env-os-arch sys-os-arch)))) +(defn file-write-allowed? + "For output file of uberscript/uberjar, allow writing of jar files + and files that are empty/don't exist." + [path] + (or (= "jar" (fs/extension path)) + (not (fs/exists? path)))) + +(def seen-urls (atom nil)) + +(defn read-data-readers [url] + (edamame/parse-string (slurp url) + {:read-cond :allow + :features #{:bb :clj} + :eof nil})) + +(defn readers-fn + "Lazy reading of data reader functions" + [ctx t] + (or (@core/data-readers t) + (default-data-readers t) + (when (simple-symbol? t) + (when-let [the-var (sci/resolve ctx t)] + (some-> the-var meta :sci.impl.record/map-constructor))) + (when-let [f @sci.impl.parser/default-data-reader-fn] + (fn [form] + (f t form))) + (let [;; urls is a vector for equality check + urls (vec (.getURLs ^java.net.URLClassLoader @cp/the-url-loader)) + parsed-resources (or (get @seen-urls urls) + (let [^java.net.URLClassLoader cl @cp/the-url-loader + resources (concat (enumeration-seq (.getResources cl "data_readers.clj")) + (enumeration-seq (.getResources cl "data_readers.cljc"))) + parsed-resources (apply merge (map read-data-readers resources)) + _ (swap! seen-urls assoc urls parsed-resources)] + parsed-resources))] + (when-let [var-sym (get parsed-resources t)] + (when-let [the-var (sci/resolve ctx var-sym)] + (sci/eval-form ctx (list 'clojure.core/var-set core/data-readers (list 'quote (assoc @core/data-readers t the-var)))) + the-var))))) + (defn exec [cli-opts] - (binding [*unrestricted* true] + (with-bindings {#'*unrestricted* true + clojure.lang.Compiler/LOADER @cp/the-url-loader} (sci/binding [core/warn-on-reflection @core/warn-on-reflection core/unchecked-math @core/unchecked-math core/data-readers @core/data-readers sci/ns @sci/ns - sci/print-length @sci/print-length] + sci/print-length @sci/print-length + ;; when adding vars here, also add them to repl.clj and nrepl_server.clj + ] (let [{version-opt :version :keys [:shell-in :edn-in :shell-out :edn-out :help :file :command-line-args @@ -794,6 +851,7 @@ Use bb run --help to show this help output. :print-deps :prepare] exec-fn :exec} cli-opts + print-result? (:prn cli-opts) _ (when debug (vreset! common/debug true)) _ (do ;; set properties (when main (System/setProperty "babashka.main" main)) @@ -828,15 +886,14 @@ Use bb run --help to show this help output. abs-path)) _ (when jar (cp/add-classpath jar)) - load-fn (fn [{:keys [:namespace :reload]}] - (let [{:keys [loader]} - @cp/cp-state] + load-fn (fn [{:keys [namespace reload ctx]}] + (let [loader @cp/the-url-loader] (or (when ;; ignore built-in namespaces when uberscripting, unless with :reload - (and uberscript - (not reload) - (or (contains? namespaces namespace) - (contains? sci-namespaces/namespaces namespace))) + (and uberscript + (not reload) + (or (contains? namespaces namespace) + (contains? sci-namespaces/namespaces namespace))) "") ;; pod namespaces go before namespaces from source, ;; unless reload is used @@ -873,19 +930,18 @@ Use bb run --help to show this help output. clojure.core.specs.alpha (binding [*out* *err*] (println "[babashka] WARNING: clojure.core.specs.alpha is removed from the classpath, unless you explicitly add the dependency.")) - nil)))) + (when-not (sci/find-ns ctx namespace) + (let [file (str/replace (namespace-munge namespace) "." "/")] + (throw (new java.io.FileNotFoundException (format "Could not locate %s.bb, %s.clj or %s.cljc on classpath." file file file))))))))) main (if (and jar (not main)) (when-let [res (cp/getResource - (cp/loader jar) + (cp/new-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-copy-vars/new-var 'load-file load-file*)))) + :namespaces (assoc-in namespaces ['clojure.core 'load-file] (sci-copy-vars/new-var 'load-file load-file*)) :env env :features #{:bb :clj} :classes @classes/class-map @@ -894,13 +950,14 @@ Use bb run --help to show this help output. :uberscript uberscript ;; :readers core/data-readers :reify-fn reify-fn - :proxy-fn proxy-fn} + :proxy-fn proxy-fn + :readers #(readers-fn (common/ctx) %)} opts (addons/future opts) sci-ctx (sci/init opts) _ (ctx-store/reset-ctx! sci-ctx) _ (when-let [pods (:pods @common/bb-edn)] (when-let [pod-metadata (pods/load-pods-metadata - pods {:download-only (download-only?)})] + pods {:download-only (download-only?)})] (vreset! pod-namespaces pod-metadata))) preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim)) [expressions exit-code] @@ -929,7 +986,7 @@ Use bb run --help to show this help output. :debug debug :preloads preloads :init init - :loader (:loader @cp/cp-state)})))) + :loader @cp/the-url-loader})))) expression (str/join " " expressions) ;; this might mess with the locations... exit-code ;; handle preloads @@ -943,7 +1000,7 @@ Use bb run --help to show this help output. :debug debug :preloads preloads :init init - :loader (:loader @cp/cp-state)}))))) + :loader @cp/the-url-loader}))))) nil)) exit-code ;; handle --init @@ -956,7 +1013,7 @@ Use bb run --help to show this help output. :debug debug :preloads preloads :init init - :loader (:loader @cp/cp-state)})))) + :loader @cp/the-url-loader})))) nil)) ;; socket REPL is start asynchronously. when no other args are ;; provided, a normal REPL will be started as well, which causes the @@ -999,8 +1056,7 @@ Use bb run --help to show this help output. (sci/eval-string* sci-ctx expression))] ;; return value printing (when (and (some? res) - (or (not run) - (:prn cli-opts))) + print-result?) (if-let [pr-f (cond shell-out println edn-out sio/prn)] (if (sequential? res) @@ -1016,7 +1072,7 @@ Use bb run --help to show this help output. (error-handler e {:expression expression :debug debug :preloads preloads - :loader (:loader @cp/cp-state)})))) + :loader @cp/the-url-loader})))) clojure [nil (if-let [proc (bdeps/clojure command-line-args)] (-> @proc :exit) 0)] @@ -1024,29 +1080,38 @@ Use bb run --help to show this help output. 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))) + (if (file-write-allowed? uberscript) + (do + (spit uberscript "") ;; reset file + (doseq [s (distinct @uberscript-sources)] + (spit uberscript s :append true)) + (spit uberscript preloads :append true) + (spit uberscript expression :append true)) + (throw (Exception. (str "Uberscript target file '" uberscript + "' exists and is not empty. Overwrite prohibited."))))) (when uberjar - (if-let [cp (cp/get-classpath)] - (let [uber-params {:dest uberjar - :jar :uber - :classpath cp - :main-class main - :verbose debug}] - (if-let [bb-edn-pods (:pods @common/bb-edn)] - (fs/with-temp-dir [bb-edn-dir {}] - (let [bb-edn-resource (fs/file bb-edn-dir "META-INF" "bb.edn")] - (fs/create-dirs (fs/parent bb-edn-resource)) - (->> {:pods bb-edn-pods} pr-str (spit bb-edn-resource)) - (let [cp-with-bb-edn (str bb-edn-dir cp/path-sep cp)] - (uberjar/run (assoc uber-params - :classpath cp-with-bb-edn))))) - (uberjar/run uber-params))) - (throw (Exception. "The uberjar task needs a classpath.")))) + (let [cp (cp/get-classpath)] + (cond + (not (file-write-allowed? uberjar)) + (throw (Exception. (str "Uberjar target file '" uberjar + "' exists and is not empty. Overwrite prohibited."))) + (not cp) + (throw (Exception. "The uberjar task needs a classpath.")) + :else + (let [uber-params {:dest uberjar + :jar :uber + :classpath cp + :main-class main + :verbose debug}] + (if-let [bb-edn-pods (:pods @common/bb-edn)] + (fs/with-temp-dir [bb-edn-dir {}] + (let [bb-edn-resource (fs/file bb-edn-dir "META-INF" "bb.edn")] + (fs/create-dirs (fs/parent bb-edn-resource)) + (->> {:pods bb-edn-pods} pr-str (spit bb-edn-resource)) + (let [cp-with-bb-edn (str bb-edn-dir cp/path-sep cp)] + (uberjar/run (assoc uber-params + :classpath cp-with-bb-edn))))) + (uberjar/run uber-params)))))) exit-code)))) (defn satisfies-min-version? [min-version] @@ -1058,8 +1123,8 @@ Use bb run --help to show this help output. (and (= minor-current minor-min) (>= patch-current patch-min))))))) -(defn load-bb-edn [string] - (try (edn/read-string {:default tagged-literal} string) +(defn read-bb-edn [string] + (try (edn/read-string {:default tagged-literal :eof nil} string) (catch java.lang.RuntimeException e (if (re-find #"No dispatch macro for: \"" (.getMessage e)) (throw (ex-info "Invalid regex literal found in EDN config, use re-pattern instead" {})) @@ -1067,40 +1132,85 @@ Use bb run --help to show this help output. (println "Error during loading bb.edn:")) (throw e)))))) +(defn binary-invoked-as-jar [] + (and (= "executable" (System/getProperty "org.graalvm.nativeimage.kind")) + (let [bin (-> (java.lang.ProcessHandle/current) + .info + .command + .get) + fn (fs/file-name bin)] + (if (= "bb" fn) + false + (if (and (fs/windows?) + (= "bb.exe" fn)) + false + (when (try (with-open [_ (java.util.zip.ZipFile. (fs/file bin))]) + true + (catch Exception _ false)) + bin)))))) + +(defn resolve-symbolic-link [f] + (if (and f (fs/exists? f)) + (str (fs/real-path f)) + f)) + (defn main [& args] - (let [[args global-opts] (parse-global-opts args) - {:keys [:jar] :as file-opt} (when (some-> args first io/file .isFile) - (parse-file-opt args global-opts)) - config (:config global-opts) - merge-deps (:merge-deps global-opts) - abs-path #(-> % io/file .getAbsolutePath) - bb-edn-file (cond - config (when (fs/exists? config) (abs-path config)) - jar (some-> jar cp/loader (cp/resource "META-INF/bb.edn") .toString) - :else (when (fs/exists? "bb.edn") (abs-path "bb.edn"))) - bb-edn (when (or bb-edn-file merge-deps) - (when bb-edn-file (System/setProperty "babashka.config" bb-edn-file)) - (let [raw-string (when bb-edn-file (slurp bb-edn-file)) - edn (when bb-edn-file (load-bb-edn raw-string)) + (let [bin-jar (binary-invoked-as-jar) + args (if bin-jar + (list* "--jar" bin-jar "--" args) + args) + [args opts] (parse-global-opts args) + [args {:keys [config merge-deps debug] :as opts}] + (if-not (or (:file opts) + (:jar opts)) + (parse-file-opt args opts) + [args opts]) + {:keys [jar file]} opts + abs-path resolve-symbolic-link + config (cond + config (if (fs/exists? config) (abs-path config) + (when debug + (binding [*out* *err*] + (println "[babashka] WARNING: config file does not exist:" config)) + nil)) + jar (let [jar (resolve-symbolic-link jar)] + (some-> [jar] cp/new-loader (cp/resource "META-INF/bb.edn") .toString)) + :else (if (and file (fs/exists? file)) + ;; file relative to bb.edn + (let [file (abs-path file) ;; follow symlink + rel-bb-edn (fs/file (fs/parent file) "bb.edn")] + (if (fs/exists? rel-bb-edn) + (abs-path rel-bb-edn) + ;; fall back to local bb.edn + (when (fs/exists? "bb.edn") + (abs-path "bb.edn")))) + ;; default to local bb.edn + (when (fs/exists? "bb.edn") + (abs-path "bb.edn")))) + bb-edn (when (or config merge-deps) + (when config (System/setProperty "babashka.config" config)) + (let [raw-string (when config (slurp config)) + edn (when config (read-bb-edn raw-string)) edn (if merge-deps - (deps/merge-deps [edn (load-bb-edn merge-deps)]) + (deps/merge-deps [edn (read-bb-edn merge-deps)]) edn) edn (assoc edn :raw raw-string - :file bb-edn-file) - edn (if-let [deps-root (or (:deps-root global-opts) + :file config) + edn (if-let [deps-root (or (:deps-root opts) (some-> config fs/parent))] (assoc edn :deps-root deps-root) edn)] (vreset! common/bb-edn edn))) - ;; _ (.println System/err (str bb-edn)) + opts (parse-opts args opts) min-bb-version (:min-bb-version bb-edn)] + (System/setProperty "java.class.path" "") (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 (merge global-opts file-opt))))) + (exec opts))) (def musl? "Captured at compile time, to know if we are running inside a @@ -1139,6 +1249,7 @@ Use bb run --help to show this help output. (let [exit-code (run args)] (System/exit exit-code)))) +(sci/alter-var-root main-var (constantly -main)) ;;;; Scratch (comment) diff --git a/test-resources/adjacent_bb/bb.edn b/test-resources/adjacent_bb/bb.edn new file mode 100644 index 00000000..72cb21ec --- /dev/null +++ b/test-resources/adjacent_bb/bb.edn @@ -0,0 +1,2 @@ +{:deps {medley/medley {:mvn/version "1.3.0"} + my-local/dep {:local/root "../local-dep"}}} diff --git a/test-resources/adjacent_bb/medley.bb b/test-resources/adjacent_bb/medley.bb new file mode 100755 index 00000000..e0f63c78 --- /dev/null +++ b/test-resources/adjacent_bb/medley.bb @@ -0,0 +1,10 @@ +#!/usr/bin/env bb + +(require '[local-dep]) + +(assert (= :foo local-dep/local-dep-var)) + +(ns medley + (:require [medley.core :as medley])) + +(prn (medley/index-by :id [{:id 1}])) diff --git a/test-resources/babashka/exec_test.clj b/test-resources/babashka/exec_test.clj index a18676bf..d68ab185 100644 --- a/test-resources/babashka/exec_test.clj +++ b/test-resources/babashka/exec_test.clj @@ -2,7 +2,9 @@ {:org.babashka/cli {:coerce {:foo []}}}) (defn exec-test - {:org.babashka/cli {:coerce {:bar :keyword}}} + {:org.babashka/cli + {:exec-args {:foo :foo} + :coerce {:bar :keyword}}} [m] (if (:meta m) (prn (meta m)) diff --git a/test-resources/babashka/src_for_classpath_test/data_readers.clj b/test-resources/babashka/src_for_classpath_test/data_readers.clj new file mode 100644 index 00000000..bd963623 --- /dev/null +++ b/test-resources/babashka/src_for_classpath_test/data_readers.clj @@ -0,0 +1 @@ +{r/reverse reader/reversev} diff --git a/test-resources/babashka/src_for_classpath_test/data_readers.cljc b/test-resources/babashka/src_for_classpath_test/data_readers.cljc new file mode 100644 index 00000000..26a070c1 --- /dev/null +++ b/test-resources/babashka/src_for_classpath_test/data_readers.cljc @@ -0,0 +1 @@ +{r/distinct reader/distinctv} diff --git a/test-resources/babashka/src_for_classpath_test/reader.clj b/test-resources/babashka/src_for_classpath_test/reader.clj new file mode 100644 index 00000000..8e4b0c26 --- /dev/null +++ b/test-resources/babashka/src_for_classpath_test/reader.clj @@ -0,0 +1,7 @@ +(ns reader) + +(defn reversev [data] + (vec (reverse data))) + +(defn distinctv [data] + (vec (distinct data))) diff --git a/test-resources/babashka/uberjar/src/my/main_main.clj b/test-resources/babashka/uberjar/src/my/main_main.clj index 238d2b52..fb7dca4e 100644 --- a/test-resources/babashka/uberjar/src/my/main_main.clj +++ b/test-resources/babashka/uberjar/src/my/main_main.clj @@ -1,6 +1,6 @@ (ns my.main-main (:require [my.impl :as impl]) - (:require [my.impl2 :as impl2])) + (:require [my.impl2])) (defn -main [& args] - (impl/impl-fn args)) + (prn (impl/impl-fn args))) diff --git a/test-resources/bb_in_root_script_in_other_dir/bb.edn b/test-resources/bb_in_root_script_in_other_dir/bb.edn new file mode 100644 index 00000000..573466cb --- /dev/null +++ b/test-resources/bb_in_root_script_in_other_dir/bb.edn @@ -0,0 +1,2 @@ +{:deps {medley/medley {:mvn/version "1.3.0"}}} + diff --git a/test-resources/bb_in_root_script_in_other_dir/dir/script.clj b/test-resources/bb_in_root_script_in_other_dir/dir/script.clj new file mode 100755 index 00000000..c28aa58d --- /dev/null +++ b/test-resources/bb_in_root_script_in_other_dir/dir/script.clj @@ -0,0 +1,6 @@ +#!/usr/bin/env bb + +(ns script + (:require [medley.core :as medley])) + +(prn (medley/index-by :id [{:id 1}])) diff --git a/test-resources/certificate.crt b/test-resources/certificate.crt new file mode 100644 index 00000000..ee544493 --- /dev/null +++ b/test-resources/certificate.crt @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF0zCCBLugAwIBAgIQByqysLNGGzf4RXfWDxaxzTANBgkqhkiG9w0BAQsFADA8 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g +UlNBIDIwNDggTTAyMB4XDTIzMDIyMTAwMDAwMFoXDTIzMTAxNDIzNTk1OVowFjEU +MBIGA1UEAxMLY2xvanVyZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6JGRt221bAElnBPzSVOebsjPofEDQdLTfAr52LCQLCo/4x7cHsPTi9x4+ +W3Pl7Fq0yrgimBhJHT34z7UeFqHEMnjsGxt7dLY0XJ87iikd8pz+v9xqXW8rrGaT +ykGx/85JFOGHJy+ZCparNYgUYN68IkaLe1QLb5w0GBcQa1U9JwpLqQVajrPDgD9Z +YRBJqgaFzJuRVsXo28rxHHFdNlP6PF2scMSFrAZEaex0bLXDxw/bHChzjypPODuO +ElsqxqI48Gsotqxe+iyP+Tu3B4GCHv2NKEFBYKiE+9UmNqQfXdVFvZFT+V2r4R9B +SMv3hpDNjmDcgpCVxmCI1sUttfQvAgMBAAGjggL1MIIC8TAfBgNVHSMEGDAWgBTA +MVLNWlDDgnx0cc7L6Zz5euuC4jAdBgNVHQ4EFgQUqkWGgRcK24T3gV3nNWwZ1EwQ +MPUwJwYDVR0RBCAwHoILY2xvanVyZS5vcmeCD3d3dy5jbG9qdXJlLm9yZzAOBgNV +HQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMDsGA1Ud +HwQ0MDIwMKAuoCyGKmh0dHA6Ly9jcmwucjJtMDIuYW1hem9udHJ1c3QuY29tL3Iy +bTAyLmNybDATBgNVHSAEDDAKMAgGBmeBDAECATB1BggrBgEFBQcBAQRpMGcwLQYI +KwYBBQUHMAGGIWh0dHA6Ly9vY3NwLnIybTAyLmFtYXpvbnRydXN0LmNvbTA2Bggr +BgEFBQcwAoYqaHR0cDovL2NydC5yMm0wMi5hbWF6b250cnVzdC5jb20vcjJtMDIu +Y2VyMAwGA1UdEwEB/wQCMAAwggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB1AOg+ +0No+9QY1MudXKLyJa8kD08vREWvs62nhd31tBr1uAAABhnTETgYAAAQDAEYwRAIg +IqyTlcuIQp6edqePX8mbZL42EgXfdTuFcBsoFdK9R5UCIEfKD8zISR0q08R2Zk6J +xnHAwBNsMXbebXu0ez3CjeDDAHcAs3N3B+GEUPhjhtYFqdwRCUp5LbFnDAuH3PAD +Dnk2pZoAAAGGdMROQQAABAMASDBGAiEAhVldSvw96LP/Qb48ZnHC0LqtsV77t/zd +7BCUNJY5bsACIQCENQ19xq5Yu7fWEB+qQQhjHlHD5yrocxqbdmMvdx9uggB2ALc+ ++yTfnE26dfI5xbpY9Gxd/ELPep81xJ4dCYEl7bSZAAABhnTETg4AAAQDAEcwRQIh +APsLYOmzOz3qoDCqBULup/twLLmLLu5pK7R/zP1lyPP4AiBZ7PQzi4K2pD8Rfcv1 +6Xm1oQwud8jmp1uQbnTCKHUPLjANBgkqhkiG9w0BAQsFAAOCAQEAdACtcuC2kFe0 +sQU1m6nnjXdXpds25Xa+Rfbls2vyTqrmatFhSleOhzbkTOnWv/tNunHikttp3+DR +YOAbhJoUe4RVfuu1cQzL3kdc1q4MEmGgEl6V+jDO8657Ck1ld1ViGnqxKtncbV8b +k7hUOurO2saQhlzgylKyFL02Re+kXgw4x/U1n20MvvzHU6QCpU6KcAUeQfub3orQ +gEjmMxjOsnI38ZY8NX7guwhRiyFex3NOZ3avZxG6p8S5amXj8H6M8RBeQ07FVO3H +wj/WczPsGo6D8RG0nU4MiGCerMiY1oF9navINFTZptWiy/gVhR85XwQrKu+Pt+AM +47JAFeruig== +-----END CERTIFICATE----- diff --git a/test-resources/domain_sockets.bb b/test-resources/domain_sockets.bb new file mode 100644 index 00000000..2d57eac5 --- /dev/null +++ b/test-resources/domain_sockets.bb @@ -0,0 +1,41 @@ +(import java.net.UnixDomainSocketAddress + java.net.StandardProtocolFamily + [java.nio.channels ServerSocketChannel SocketChannel]) + +(require '[clojure.java.io :as io] + '[babashka.fs]) + +(def sockaddr (UnixDomainSocketAddress/of + (-> (doto (fs/file (fs/temp-dir) "server.socket") + (.deleteOnExit)) + str))) + +;; server +(def server + (future + (let [ch (ServerSocketChannel/open StandardProtocolFamily/UNIX)] + (.bind ch sockaddr) + (.accept ch)))) + +(Thread/sleep 100) + + ;; client +(let [ch (SocketChannel/open StandardProtocolFamily/UNIX) + ch (loop [retry 0] + (let [v (try (.connect ch sockaddr) + (catch Exception e e))] + (if (instance? Exception v) + (if (< retry 10) + (do (Thread/sleep 100) + (recur (inc retry))) + (throw v)) + v)))] + #_(prn :ch ch) + #_(.close ch)) + +@server + +(when-not (System/getProperty "babashka.version") + (shutdown-agents)) + +:success diff --git a/test-resources/lib_tests/babashka/run_all_libtests.clj b/test-resources/lib_tests/babashka/run_all_libtests.clj index 092263f8..b9b70876 100644 --- a/test-resources/lib_tests/babashka/run_all_libtests.clj +++ b/test-resources/lib_tests/babashka/run_all_libtests.clj @@ -3,6 +3,7 @@ [babashka.classpath :as cp :refer [add-classpath]] [babashka.core :refer [windows?]] [babashka.fs :as fs] + [babashka.process :refer [sh]] [clojure.edn :as edn] [clojure.java.io :as io] [clojure.spec.test.alpha :as st] @@ -53,18 +54,30 @@ (swap! status (fn [status] (merge-with + status (dissoc m :type)))))))))))) +(defn current-branch [] + (or (System/getenv "APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH") + (System/getenv "APPVEYOR_REPO_BRANCH") + (System/getenv "CIRCLE_BRANCH") + (System/getenv "GITHUB_REF_NAME") + (System/getenv "CIRRUS_BRANCH") + (-> (sh "git" "rev-parse" "--abbrev-ref" "HEAD") + :out + str/trim))) + ;; Standard test-runner for libtests (let [lib-tests (edn/read-string (slurp (io/resource "bb-tested-libs.edn"))) test-nss (atom [])] (doseq [[libname {tns :test-namespaces skip-windows :skip-windows :keys [test-paths - git-sha]}] lib-tests] + git-sha flaky]}] lib-tests] (let [git-dir (format ".gitlibs/libs/%s/%s" libname git-sha) git-dir (fs/file (fs/home) git-dir)] (doseq [p test-paths] (add-classpath (str (fs/file git-dir p))))) (when-not (and skip-windows (windows?)) - (swap! test-nss into tns))) + (if (and flaky (#{"main" "master"} (current-branch))) + (println "Skipping" tns "for main branch because it's marked flaky") + (swap! test-nss into tns)))) (apply test-namespaces @test-nss)) ;; Non-standard tests - These are tests with unusual setup around test-namespaces @@ -80,13 +93,15 @@ (test-doric-cyclic-dep-problem)) ;;;; babashka.process -(when-not (windows?) - ;; test built-in babashka.process - (test-namespaces 'babashka.process-test) +;; test built-in babashka.process +(test-namespaces 'babashka.process-test) +(when (= "native" (System/getenv "BABASHKA_TEST_ENV")) ;; test babashka.process from source + #_{:clj-kondo/ignore [:duplicate-require]} (require '[babashka.process] :reload) - (test-namespaces 'babashka.process-test)) + (System/setProperty "babashka.process.test.reload" "true") + (test-namespaces 'babashka.process-test 'babashka.process-exec-test)) ;;;; final exit code diff --git a/test-resources/lib_tests/bb-tested-libs.edn b/test-resources/lib_tests/bb-tested-libs.edn index d549dfa2..f7ba6189 100644 --- a/test-resources/lib_tests/bb-tested-libs.edn +++ b/test-resources/lib_tests/bb-tested-libs.edn @@ -46,8 +46,8 @@ com.github.seancorfield/honeysql {:git-sha "6e4e1f6928450788353c181f32474d930d6afe84", :git-url "https://github.com/seancorfield/honeysql", :test-namespaces (honey.sql-test honey.sql.helpers-test honey.sql.postgres-test), :branch "develop"} honeysql/honeysql {:git-sha "1137dd12350afdc30ad4976c3718279581390b36", :git-url "https://github.com/seancorfield/honeysql", :test-namespaces (honeysql.format-test honeysql.core-test), :branch "v1"} ; skip tests on Windows because of the :compressed thing - babashka/babashka.curl {:git-url "https://github.com/babashka/babashka.curl", :test-namespaces [babashka.curl-test], :skip-windows true, :manually-added true} - http-kit/http-kit {:git-url "https://github.com/http-kit/http-kit", :test-namespaces [httpkit.client-test], :manually-added true} + babashka/babashka.curl {:git-url "https://github.com/babashka/babashka.curl", :test-namespaces [babashka.curl-test], :skip-windows true, :manually-added true :flaky true} + http-kit/http-kit {:git-url "https://github.com/http-kit/http-kit", :test-namespaces [httpkit.client-test], :manually-added true :flaky true} org.clojure/core.match {:git-url "https://github.com/clojure/core.match", :test-namespaces [core-match.core-tests], :manually-added true} hiccup/hiccup {:git-url "http://github.com/weavejester/hiccup", :test-namespaces [hiccup.core-test hiccup2.core-test], :manually-added true} org.clojure/test.check {:git-url "https://github.com/clojure/test.check", :test-namespaces [test-check.smoke-test], :manually-added true} @@ -57,15 +57,15 @@ crispin/crispin {:git-url "https://github.com/dunaj-project/crispin", :test-namespaces [crispin.core-test], :manually-added true} clj-commons/multigrep {:git-url "https://github.com/clj-commons/multigrep", :test-namespaces [multigrep.core-test], :manually-added true} org.clj-commons/digest {:git-url "https://github.com/clj-commons/clj-digest", :test-namespaces [clj-commons.digest-test], :manually-added true} - hato/hato {:git-url "https://github.com/gnarroway/hato", :test-namespaces [hato.client-test], :manually-added true} - java-http-clj/java-http-clj {:git-url "http://www.github.com/schmee/java-http-clj", :test-namespaces [java-http-clj.smoke-test], :manually-added true} + hato/hato {:git-url "https://github.com/gnarroway/hato", :test-namespaces [hato.client-test], :manually-added true :flaky true} + java-http-clj/java-http-clj {:git-url "http://www.github.com/schmee/java-http-clj", :test-namespaces [java-http-clj.smoke-test], :manually-added true :flaky true} rewrite-clj/rewrite-clj {:git-url "https://github.com/clj-commons/rewrite-clj", :test-namespaces [rewrite-clj.parser-test rewrite-clj.node-test rewrite-clj.zip-test rewrite-clj.paredit-test rewrite-clj.zip.subedit-test rewrite-clj.node.coercer-test], :manually-added true} ;; TODO: env tests don't work because envoy lib isn't compatible with bb ;; TODO: failing tests in the following namespaces: vault.client.mock-test, vault.secrets.kvv1-test vault.secrets.kvv2-test amperity/vault-clj {:git-url "https://github.com/amperity/vault-clj", :test-namespaces [vault.lease-test vault.client.http-test], :manually-added true} orchestra/orchestra {:git-url "https://github.com/jeaye/orchestra", :test-namespaces (orchestra.make-fns orchestra.many-fns orchestra.expound-test orchestra.core-test orchestra.reload-test), :test-directories ("test/cljc" "test/clj"), :git-sha "81e5181f7b42e5e2763a2b37db17954f3be0314e"} ;; BB-TEST-PATCH: Deleted tasks.clj - org.clj-commons/clj-http-lite {:git-url "https://github.com/clj-commons/clj-http-lite", :test-namespaces (clj-http.lite.test-runner clj-http.lite.client-test), :test-directories ("bb"), :git-sha "6b53000df55ac05c4ff8e5047a5323fc08a52e8b"} + org.clj-commons/clj-http-lite {:git-url "https://github.com/clj-commons/clj-http-lite", :test-namespaces (clj-http.lite.test-runner clj-http.lite.client-test), :test-directories ("bb"), :git-sha "6b53000df55ac05c4ff8e5047a5323fc08a52e8b" :flaky true} cprop/cprop {:git-url "https://github.com/tolitius/cprop", :test-namespaces [cprop.smoke-test], :manually-added true} org.clojure/data.zip {:git-url "https://github.com/clojure/data.zip", :test-namespaces [clojure.data.zip-test], :manually-added true} borkdude/deps {:git-url "https://github.com/borkdude/deps.clj", :test-namespaces [borkdude.deps.smoke-test], :manually-added true} @@ -94,7 +94,8 @@ progrock/progrock {:git-url "https://github.com/weavejester/progrock", :test-namespaces (progrock.core-test), :git-sha "9c277a3244c52bfde19c21add327d6e20b94fdf5"} ;; Don't run portal.jvm-test as it depends on headless chrome djblue/portal {:git-url "https://github.com/djblue/portal", :test-namespaces (portal.test-runner portal.runtime.cson-test portal.runtime.fs-test portal.e2e portal.bench), :git-sha "64e4624bcf3bee2dd47e3d8e47982c709738eb11"} - integrant/integrant {:git-url "https://github.com/weavejester/integrant", :test-namespaces (integrant.test.foo integrant.test.quz integrant.test.bar integrant.test.baz integrant.core-test), :git-sha "32a46f5dca8a6b563a6dddf88bec887be3201b08"} + integrant/integrant {:git-url "https://github.com/weavejester/integrant", :test-namespaces (integrant.test.foo integrant.test.quz integrant.test.bar integrant.test.baz integrant.core-test), :git-sha "a9fd7c02bd7201f36344b47142badc3c3ef22f88" + :test-paths ["test"]} com.wsscode/cljc-misc {:git-url "https://github.com/wilkerlucio/cljc-misc", :test-namespaces (com.wsscode.misc.uuid-test com.wsscode.misc.macros-test com.wsscode.misc.math-test com.wsscode.misc.coll-test com.wsscode.misc.refs-test), :git-sha "dc8e31a200f9cacf86af10b63e40fcb448c259f4"} edn-query-language/eql {:git-url "https://github.com/edn-query-language/eql", :test-namespaces (edn-query-language.core-test), :git-sha "0d4f9745d98c3d20b81bb4bdce3e8e15db7fd094"} meta-merge/meta-merge {:git-url "https://github.com/weavejester/meta-merge", :test-namespaces (meta-merge.core-test), :git-sha "c968c38baccd4219fe0ba592d89af37ea8e426bf"} @@ -182,4 +183,12 @@ org.clojure/tools.namespace {:git-sha "daf82a10e70182aea4c0716a48f3922163441b32", :git-url "https://github.com/clojure/tools.namespace", :test-namespaces [clojure.tools.namespace.test-helpers clojure.tools.namespace.dependency-test clojure.tools.namespace.find-test clojure.tools.namespace.move-test clojure.tools.namespace.parse-test], - :test-paths ["src/test/clojure"]}} + :test-paths ["src/test/clojure"]} + net.cgrand/xforms {:git-url "https://github.com/cgrand/xforms", + :git-sha "550dbc150a79c6ecc148d8a7e260e10bc36321c6", + :test-namespaces [net.cgrand.xforms-test], + :test-paths ["test"]} + prismatic/plumbing {:git-url "https://github.com/plumatic/plumbing", + :git-sha "424bc704f2db422de34269c139a5494314b3a43b", + :test-namespaces [plumbing.core-test], + :test-paths ["test"]}} diff --git a/test-resources/lib_tests/hato/client_test.clj b/test-resources/lib_tests/hato/client_test.clj index d17d7b55..7c663710 100644 --- a/test-resources/lib_tests/hato/client_test.clj +++ b/test-resources/lib_tests/hato/client_test.clj @@ -331,7 +331,7 @@ (deftest ^:integration test-http2 (testing "can make an http2 request" - (let [r (get "https://nghttp2.org/httpbin/get" {:as :json})] + (let [r (get "https://httpbin.org/get" {:as :json})] (is (= :http-2 (:version r)))))) (deftest custom-middleware diff --git a/test-resources/lib_tests/integrant/core_test.cljc b/test-resources/lib_tests/integrant/core_test.cljc deleted file mode 100644 index ceeb60f7..00000000 --- a/test-resources/lib_tests/integrant/core_test.cljc +++ /dev/null @@ -1,549 +0,0 @@ -(ns integrant.core-test - (:require [clojure.spec.alpha :as s] - #?(:clj [clojure.test :refer :all] - :cljs [cljs.test :refer-macros [are deftest is testing]]) - [integrant.core :as ig] - [weavejester.dependency :as dep])) - -(def log (atom [])) - -(defmethod ig/prep-key ::p [_ v] - (merge {:a (ig/ref ::a)} v)) - -(defmethod ig/init-key :default [k v] - (swap! log conj [:init k v]) - [v]) - -(defmethod ig/init-key ::x [k v] - (swap! log conj [:init k v]) - :x) - -(defmethod ig/init-key ::error-init [_ _] - (throw (ex-info "Testing" {:reason ::test}))) - -(defmethod ig/init-key ::k [_ v] v) - -(defmethod ig/init-key ::n [_ v] (inc v)) -(defmethod ig/pre-init-spec ::n [_] nat-int?) - -(defmethod ig/init-key ::r [_ v] {:v v}) -(defmethod ig/resolve-key ::r [_ {:keys [v]}] v) -(defmethod ig/resume-key ::r [k v _ _] (ig/init-key k v)) - -(defmethod ig/halt-key! :default [k v] - (swap! log conj [:halt k v])) - -(defmethod ig/halt-key! ::error-halt [_ _] - (throw (ex-info "Testing" {:reason ::test}))) - -(defmethod ig/resume-key :default [k cfg cfg' sys] - (swap! log conj [:resume k cfg cfg' sys]) - [cfg]) - -(defmethod ig/resume-key ::x [k cfg cfg' sys] - (swap! log conj [:resume k cfg cfg' sys]) - :rx) - -(defmethod ig/suspend-key! :default [k v] - (swap! log conj [:suspend k v])) - -(derive ::p ::pp) -(derive ::pp ::ppp) - -(derive ::ap ::a) -(derive ::ap ::p) - -(deftest ref-test - (is (ig/ref? (ig/ref ::foo))) - (is (ig/ref? (ig/ref [::foo ::bar]))) - (is (ig/reflike? (ig/ref ::foo))) - (is (ig/reflike? (ig/ref [::foo ::bar])))) - -(deftest refset-test - (is (ig/refset? (ig/refset ::foo))) - (is (ig/refset? (ig/refset [::foo ::bar]))) - (is (ig/reflike? (ig/refset ::foo))) - (is (ig/reflike? (ig/refset [::foo ::bar])))) - -(deftest composite-keyword-test - (let [k (ig/composite-keyword [::a ::b])] - (is (isa? k ::a)) - (is (isa? k ::b)) - (is (identical? k (ig/composite-keyword [::a ::b]))) - (is (not= k (ig/composite-keyword [::a ::c]))))) - -(deftest valid-config-key-test - (is (ig/valid-config-key? ::a)) - (is (not (ig/valid-config-key? :a)))) - -(deftest expand-test - (is (= (ig/expand {::a (ig/ref ::b), ::b 1}) - {::a 1, ::b 1})) - (is (= (ig/expand {::a (ig/ref ::b), ::b (ig/ref ::c), ::c 2}) - {::a 2, ::b 2, ::c 2})) - (is (= (ig/expand {::a (ig/ref ::pp), ::p 1}) - {::a 1, ::p 1})) - (is (= (ig/expand {::a (ig/refset ::ppp), ::p 1, ::pp 2}) - {::a #{1 2}, ::p 1, ::pp 2})) - (is (= (ig/expand {::a (ig/refset ::ppp)}) - {::a #{}}))) - -#?(:clj - (deftest read-string-test - (is (= (ig/read-string "{:foo/a #ig/ref :foo/b, :foo/b 1}") - {:foo/a (ig/ref :foo/b), :foo/b 1})) - (is (= (ig/read-string "{:foo/a #ig/refset :foo/b, :foo/b 1}") - {:foo/a (ig/refset :foo/b), :foo/b 1})) - (is (= (ig/read-string {:readers {'test/var find-var}} - "{:foo/a #test/var clojure.core/+}") - {:foo/a #'+})))) - -;; BB-TEST-PATCH: No *loaded-libs* in bb -#?(:bb :TODO :clj - (defn- remove-lib [lib] - (remove-ns lib) - (dosync (alter @#'clojure.core/*loaded-libs* disj lib)))) - -(derive :integrant.test-child/foo :integrant.test/foo) - -;; BB-TEST-PATCH: No *loaded-libs* in bb -#?(:bb :TODO - :clj - (deftest load-namespaces-test - (testing "all namespaces" - (remove-lib 'integrant.test.foo) - (remove-lib 'integrant.test.bar) - (remove-lib 'integrant.test.baz) - (remove-lib 'integrant.test.quz) - (is (= (set (ig/load-namespaces {:integrant.test/foo 1 - :integrant.test.bar/wuz 2 - [:integrant.test/baz :integrant.test/x] 3 - [:integrant.test/y :integrant.test/quz] 4})) - '#{integrant.test.foo - integrant.test.bar - integrant.test.baz - integrant.test.quz})) - (is (some? (find-ns 'integrant.test.foo))) - (is (some? (find-ns 'integrant.test.bar))) - (is (some? (find-ns 'integrant.test.baz))) - (is (some? (find-ns 'integrant.test.quz))) - (is (= (some-> 'integrant.test.foo/message find-var var-get) "foo")) - (is (= (some-> 'integrant.test.bar/message find-var var-get) "bar")) - (is (= (some-> 'integrant.test.baz/message find-var var-get) "baz")) - (is (= (some-> 'integrant.test.quz/message find-var var-get) "quz"))) - - (testing "some namespaces" - (remove-lib 'integrant.test.foo) - (remove-lib 'integrant.test.bar) - (remove-lib 'integrant.test.baz) - (remove-lib 'integrant.test.quz) - (is (= (set (ig/load-namespaces - {:integrant.test/foo 1 - :integrant.test/bar (ig/ref :integrant.test/foo) - :integrant.test/baz 3} - [:integrant.test/bar])) - '#{integrant.test.foo - integrant.test.bar})) - (is (some? (find-ns 'integrant.test.foo))) - (is (some? (find-ns 'integrant.test.bar))) - (is (nil? (find-ns 'integrant.test.baz)))) - - (testing "load namespaces of ancestors" - (remove-lib 'integrant.test.foo) - (is (= (set (ig/load-namespaces - {:integrant.test-child/foo 1})) - '#{integrant.test.foo})) - (is (some? (find-ns 'integrant.test.foo)))))) - - -(deftest dependency-graph-test - (let [m {::a (ig/ref ::p), ::b (ig/refset ::ppp) ::p 1, ::pp 2}] - (testing "graph with refsets" - (let [g (ig/dependency-graph m)] - (is (dep/depends? g ::a ::p)) - (is (dep/depends? g ::b ::p)) - (is (dep/depends? g ::b ::pp)))) - - (testing "graph without refsets" - (let [g (ig/dependency-graph m {:include-refsets? false})] - (is (dep/depends? g ::a ::p)) - (is (not (dep/depends? g ::b ::p))) - (is (not (dep/depends? g ::b ::pp))))))) - -(deftest key-comparator-test - (let [graph (ig/dependency-graph {::a (ig/ref ::ppp) ::p 1, ::b 2})] - (is (= (sort (ig/key-comparator graph) [::b ::a ::p]) - [::p ::a ::b])))) - -(deftest derived-from?-test - (are [a b] (ig/derived-from? a b) - ::p ::p - ::p ::pp - ::p ::ppp - ::ap [::a ::p] - ::ap [::a ::pp] - [::a ::p] [::a ::pp] - [::a ::b ::p] [::a ::ppp])) - -(deftest find-derived-1-test - (testing "missing key" - (is (nil? (ig/find-derived-1 {} ::p)))) - - (testing "derived key" - (is (= (ig/find-derived-1 {::a "x" ::p "y"} ::pp) - [::p "y"]))) - - (testing "ambiguous key" - (is (thrown-with-msg? - #?(:clj clojure.lang.ExceptionInfo :cljs cljs.core.ExceptionInfo) - (re-pattern (str "Ambiguous key: " ::pp "\\. " - "Found multiple candidates: " ::p ", " ::pp)) - (ig/find-derived-1 {::a "x" ::p "y", ::pp "z"} ::pp)))) - - (testing "composite key" - (is (= (ig/find-derived-1 {::a "x" [::b ::x] "y"} ::x) - [[::b ::x] "y"])))) - -(deftest find-derived-test - (testing "missing key" - (is (nil? (ig/find-derived {} ::p)))) - - (testing "derived key" - (is (= (ig/find-derived {::a "x" ::p "y" ::pp "z"} ::pp) - [[::p "y"] [::pp "z"]]))) - - (testing "ambiguous key" - (is (= (ig/find-derived {::a "x" ::p "y" ::pp "z"} ::ppp) - [[::p "y"] [::pp "z"]]))) - - (testing "composite key" - (is (= (ig/find-derived {::a "x" [::b ::x] "y", [::b ::y] "z"} ::b) - [[[::b ::x] "y"] [[::b ::y] "z"]])))) - -(deftest prep-test - (testing "default" - (is (= (ig/prep {::q {:b 2}, ::a 1}) - {::q {:b 2}, ::a 1}))) - - (testing "custom prep-key" - (is (= (ig/prep {::p {:b 2}, ::a 1}) - {::p {:a (ig/ref ::a), :b 2}, ::a 1}))) - - (testing "prep then init" - (is (= (ig/init (ig/prep {::p {:b 2}, ::a 1})) - {::p [{:a [1], :b 2}], ::a [1]})))) - -(deftest init-test - (testing "without keys" - (reset! log []) - (let [m (ig/init {::a (ig/ref ::b), ::b 1})] - (is (= m {::a [[1]], ::b [1]})) - (is (= @log [[:init ::b 1] - [:init ::a [1]]])))) - - (testing "with keys" - (reset! log []) - (let [m (ig/init {::a (ig/ref ::b), ::b 1, ::c 2} [::a])] - (is (= m {::a [[1]], ::b [1]})) - (is (= @log [[:init ::b 1] - [:init ::a [1]]])))) - - (testing "with inherited keys" - (reset! log []) - (let [m (ig/init {::p (ig/ref ::a), ::a 1} [::pp])] - (is (= m {::p [[1]], ::a [1]})) - (is (= @log [[:init ::a 1] - [:init ::p [1]]])))) - - (testing "with composite keys" - (reset! log []) - (let [m (ig/init {::a (ig/ref ::b), [::x ::b] 1})] - (is (= m {::a [:x], [::x ::b] :x})) - (is (= @log [[:init [::x ::b] 1] - [:init ::a :x]])))) - - (testing "with composite refs" - (reset! log []) - (let [m (ig/init {::a (ig/ref [::b ::c]), [::b ::c ::e] 1, [::b ::d] 2})] - (is (= m {::a [[1]], [::b ::c ::e] [1], [::b ::d] [2]})) - (is (or (= @log [[:init [::b ::c ::e] 1] - [:init ::a [1]] - [:init [::b ::d] 2]]) - (= @log [[:init [::b ::d] 2] - [:init [::b ::c ::e] 1] - [:init ::a [1]]]))))) - - (testing "with failing composite refs" - (reset! log []) - (is (thrown-with-msg? - #?(:clj clojure.lang.ExceptionInfo :cljs cljs.core.ExceptionInfo) - #"^Invalid composite key: \[:integrant.core-test/a :b\]. Every keyword must be namespaced.$" - (ig/init {[::a :b] :anything})))) - - (testing "with custom resolve-key" - (let [m (ig/init {::a (ig/ref ::r), ::r 1})] - (is (= m {::a [1], ::r {:v 1}})))) - - (testing "with refsets" - (reset! log []) - (let [m (ig/init {::a (ig/refset ::ppp), ::p 1, ::pp 2})] - (is (= m {::a [#{[1] [2]}], ::p [1], ::pp [2]})) - (is (= @log [[:init ::p 1] - [:init ::pp 2] - [:init ::a #{[1] [2]}]])))) - - (testing "with refsets and keys" - (reset! log []) - (let [m {::a (ig/refset ::ppp), ::p 1, ::pp 2}] - (is (= (ig/init m [::a]) {::a [#{}]})) - (is (= (ig/init m [::a ::p]) {::a [#{[1]}] ::p [1]})) - (is (= (ig/init m [::a ::pp]) {::a [#{[1] [2]}] ::p [1] ::pp [2]})))) - - (testing "large config" - (is (= (ig/init {:a/a1 {} :a/a2 {:_ (ig/ref :a/a1)} - :a/a3 {} :a/a4 {} :a/a5 {} - :a/a6 {} :a/a7 {} :a/a8 {} - :a/a9 {} :a/a10 {}}) - {:a/a1 [{}] :a/a2 [{:_ [{}]}] - :a/a3 [{}] :a/a4 [{}] :a/a5 [{}] - :a/a6 [{}] :a/a7 [{}] :a/a8 [{}] - :a/a9 [{}] :a/a10 [{}]}))) - - (testing "with passing specs" - (let [m (ig/init {::n (ig/ref ::k), ::k 1})] - (is (= m {::n 2, ::k 1})))) - - (testing "with failing specs" - (is (thrown-with-msg? - #?(:clj clojure.lang.ExceptionInfo :cljs cljs.core.ExceptionInfo) - (re-pattern (str "Spec failed on key " ::n " when building system")) - (ig/init {::n (ig/ref ::k), ::k 1.1})))) - - (testing "with failing composite specs" - (is (thrown-with-msg? - #?(:clj clojure.lang.ExceptionInfo :cljs cljs.core.ExceptionInfo) - (re-pattern (str "Spec failed on key \\[" ::n " " ::nnn "\\] when building system")) - (ig/init {[::n ::nnn] 1.1}))))) - -(deftest halt-test - (testing "without keys" - (reset! log []) - (let [m (ig/init {::a (ig/ref ::b), ::b 1})] - (ig/halt! m) - (is (= @log [[:init ::b 1] - [:init ::a [1]] - [:halt ::a [[1]]] - [:halt ::b [1]]])))) - - (testing "with keys" - (reset! log []) - (let [m (ig/init {::a (ig/ref ::b), ::b (ig/ref ::c), ::c 1})] - (ig/halt! m [::a]) - (is (= @log [[:init ::c 1] - [:init ::b [1]] - [:init ::a [[1]]] - [:halt ::a [[[1]]]]])) - (reset! log []) - (ig/halt! m [::c]) - (is (= @log [[:halt ::a [[[1]]]] - [:halt ::b [[1]]] - [:halt ::c [1]]])))) - - (testing "with partial system" - (reset! log []) - (let [m (ig/init {::a 1, ::b (ig/ref ::a)} [::a])] - (ig/halt! m) - (is (= @log [[:init ::a 1] - [:halt ::a [1]]])))) - - (testing "with inherited keys" - (reset! log []) - (let [m (ig/init {::a (ig/ref ::p), ::p 1} [::a])] - (ig/halt! m [::pp]) - (is (= @log [[:init ::p 1] - [:init ::a [1]] - [:halt ::a [[1]]] - [:halt ::p [1]]])))) - - (testing "with composite keys" - (reset! log []) - (let [m (ig/init {::a (ig/ref ::b), [::x ::b] 1})] - (ig/halt! m) - (is (= @log [[:init [::x ::b] 1] - [:init ::a :x] - [:halt ::a [:x]] - [:halt [::x ::b] :x]]))))) - -(deftest suspend-resume-test - (testing "same configuration" - (reset! log []) - (let [c {::a (ig/ref ::b), ::b 1} - m (ig/init c) - _ (ig/suspend! m) - m' (ig/resume c m)] - (is (= @log [[:init ::b 1] - [:init ::a [1]] - [:suspend ::a [[1]]] - [:suspend ::b [1]] - [:resume ::b 1 1 [1]] - [:resume ::a [1] [1] [[1]]]])))) - - (testing "missing keys" - (reset! log []) - (let [c {::a (ig/ref ::b), ::b 1} - m (ig/init c) - _ (ig/suspend! m) - m' (ig/resume (dissoc c ::a) m)] - (is (= @log [[:init ::b 1] - [:init ::a [1]] - [:suspend ::a [[1]]] - [:suspend ::b [1]] - [:halt ::a [[1]]] - [:resume ::b 1 1 [1]]])))) - - (testing "missing refs" - (reset! log []) - (let [c {::a {:b (ig/ref ::b)}, ::b 1} - m (ig/init c) - _ (ig/suspend! m) - m' (ig/resume {::a []} m)] - (is (= @log [[:init ::b 1] - [:init ::a {:b [1]}] - [:suspend ::a [{:b [1]}]] - [:suspend ::b [1]] - [:halt ::b [1]] - [:resume ::a [] {:b [1]} [{:b [1]}]]])))) - - (testing "with custom resolve-key" - (let [c {::a (ig/ref ::r), ::r 1} - m (ig/init c) - _ (ig/suspend! m) - m' (ig/resume c m)] - (is (= m m')))) - - (testing "composite keys" - (reset! log []) - (let [c {::a (ig/ref ::x), [::b ::x] 1} - m (ig/init c) - _ (ig/suspend! m) - m' (ig/resume c m)] - (is (= @log [[:init [::b ::x] 1] - [:init ::a :x] - [:suspend ::a [:x]] - [:suspend [::b ::x] :x] - [:resume [::b ::x] 1 1 :x] - [:resume ::a :rx :x [:x]]])))) - - (testing "resume key with dependencies" - (reset! log []) - (let [c {::a {:b (ig/ref ::b)}, ::b 1} - m (ig/init c [::a]) - _ (ig/suspend! m) - m' (ig/resume c m [::a])] - (is (= @log - [[:init ::b 1] - [:init ::a {:b [1]}] - [:suspend ::a [{:b [1]}]] - [:suspend ::b [1]] - [:resume ::b 1 1 [1]] - [:resume ::a {:b [1]} {:b [1]} [{:b [1]}]]]))))) - -(deftest invalid-configs-test - (testing "ambiguous refs" - (is (thrown-with-msg? - #?(:clj clojure.lang.ExceptionInfo :cljs cljs.core.ExceptionInfo) - (re-pattern (str "Ambiguous key: " ::ppp "\\. " - "Found multiple candidates: " - "(" ::p ", " ::pp "|" ::pp ", " ::p ")")) - (ig/init {::a (ig/ref ::ppp), ::p 1, ::pp 2})))) - - (testing "missing refs" - (is (thrown-with-msg? - #?(:clj clojure.lang.ExceptionInfo :cljs cljs.core.ExceptionInfo) - (re-pattern (str "Missing definitions for refs: " ::b)) - (ig/init {::a (ig/ref ::b)})))) - - (testing "missing refs with explicit keys" - (is (= (ig/init {::a (ig/ref ::ppp), ::p 1, ::pp 2} [::p ::pp]) - {::p [1], ::pp [2]}))) - - (testing "missing refs with explicit keys" - (is (= (ig/init {::a 1, ::b (ig/ref ::c)} [::a]) - {::a [1]})))) - -(defn build-log [config] - (let [log (atom [])] - [(ig/build config (keys config) (fn [k v] (last (swap! log conj [:build k v])))) - @log])) - -(deftest build-test - (is (= [{::a [:build ::a [:build ::b 1]] - ::b [:build ::b 1]} - [[:build ::b 1] - [:build ::a [:build ::b 1]]]] - (build-log {::a (ig/ref ::b) - ::b 1})))) - -(defn test-log [f m] - (let [log (atom [])] - [(f m (keys m) (fn [k v] (last (swap! log conj [:test k v])))) - @log])) - -(deftest run-test* ;; BB-TEST-PATCH: renamed due to conflict with clojure.test - (let [config {::a (ig/ref ::b), ::b 1} - [system _] (build-log config)] - (is (= [nil - [[:test ::b [:build ::b 1]] - [:test ::a [:build ::a [:build ::b 1]]]]] - (test-log ig/run! system))) - (is (= [nil - [[:test ::a [:build ::a [:build ::b 1]]] - [:test ::b [:build ::b 1]]]] - (test-log ig/reverse-run! system))))) - -(deftest fold-test - (let [config {::a (ig/ref ::ppp), ::b (ig/ref ::pp), ::p 1, ::c 2} - system (ig/init config)] - (is (= (ig/fold system #(conj %1 [%2 %3]) []) - [[::p [1]] [::a [[1]]] [::b [[1]]] [::c [2]]])))) - -(deftest wrapped-exception-test - (testing "exception when building" - (let [ex (try (ig/init {::a 1, ::error-init (ig/ref ::a)}) nil - (catch #?(:clj Throwable :cljs :default) t t))] - (is (some? ex)) - (is (= (#?(:clj .getMessage :cljs ex-message) ex) - (str "Error on key " ::error-init " when building system"))) - (is (= (ex-data ex) - {:reason ::ig/build-threw-exception - :system {::a [1]} - :function ig/init-key - :key ::error-init - :value [1]})) - (let [cause (#?(:clj .getCause :cljs ex-cause) ex)] - (is (some? cause)) - (is (= (#?(:clj .getMessage :cljs ex-message) cause) "Testing")) - (is (= (ex-data cause) {:reason ::test}))))) - - (testing "exception when running" - (let [system (ig/init {::a 1 - ::error-halt (ig/ref ::a) - ::b (ig/ref ::error-halt) - ::c (ig/ref ::b)}) - ex (try (ig/halt! system) - (catch #?(:clj Throwable :cljs :default) t t))] - (is (some? ex)) - (is (= (#?(:clj .getMessage :cljs ex-message) ex) - (str "Error on key " ::error-halt " when running system"))) - (is (= (ex-data ex) - {:reason ::ig/run-threw-exception - :system {::a [1], ::error-halt [[1]], ::b [[[1]]], ::c [[[[1]]]]} - :completed-keys '(::c ::b) - :remaining-keys '(::a) - :function ig/halt-key! - :key ::error-halt - :value [[1]]})) - (let [cause (#?(:clj .getCause :cljs ex-cause) ex)] - (is (some? cause)) - (is (= (#?(:clj .getMessage :cljs ex-message) cause) "Testing")) - (is (= (ex-data cause) {:reason ::test})))))) diff --git a/test-resources/lib_tests/integrant/test/bar.clj b/test-resources/lib_tests/integrant/test/bar.clj deleted file mode 100644 index e24dec6a..00000000 --- a/test-resources/lib_tests/integrant/test/bar.clj +++ /dev/null @@ -1,3 +0,0 @@ -(ns integrant.test.bar) - -(def message "bar") diff --git a/test-resources/lib_tests/integrant/test/baz.clj b/test-resources/lib_tests/integrant/test/baz.clj deleted file mode 100644 index bf565ab7..00000000 --- a/test-resources/lib_tests/integrant/test/baz.clj +++ /dev/null @@ -1,3 +0,0 @@ -(ns integrant.test.baz) - -(def message "baz") diff --git a/test-resources/lib_tests/integrant/test/foo.clj b/test-resources/lib_tests/integrant/test/foo.clj deleted file mode 100644 index 3d9ab7d5..00000000 --- a/test-resources/lib_tests/integrant/test/foo.clj +++ /dev/null @@ -1,3 +0,0 @@ -(ns integrant.test.foo) - -(def message "foo") diff --git a/test-resources/lib_tests/integrant/test/quz.clj b/test-resources/lib_tests/integrant/test/quz.clj deleted file mode 100644 index 7ebd2a8b..00000000 --- a/test-resources/lib_tests/integrant/test/quz.clj +++ /dev/null @@ -1,3 +0,0 @@ -(ns integrant.test.quz) - -(def message "quz") diff --git a/test-resources/lib_tests/integrant/test_runner.cljs b/test-resources/lib_tests/integrant/test_runner.cljs deleted file mode 100644 index 8e014182..00000000 --- a/test-resources/lib_tests/integrant/test_runner.cljs +++ /dev/null @@ -1,5 +0,0 @@ -(ns integrant.test-runner - (:require [doo.runner :refer-macros [doo-tests]] - [integrant.core-test])) - -(doo-tests 'integrant.core-test) diff --git a/test-resources/lib_tests/rewrite_clj/node/coercer_test.cljc b/test-resources/lib_tests/rewrite_clj/node/coercer_test.cljc index f0e858e3..fbb9ff8d 100644 --- a/test-resources/lib_tests/rewrite_clj/node/coercer_test.cljc +++ b/test-resources/lib_tests/rewrite_clj/node/coercer_test.cljc @@ -1,67 +1,77 @@ (ns rewrite-clj.node.coercer-test (:require [clojure.test :refer [deftest testing is are]] [rewrite-clj.node :as node] + [rewrite-clj.node.protocols :as protocols] + [rewrite-clj.node.regex :as regex] [rewrite-clj.parser :as p])) (deftest t-sexpr->node->sexpr-roundtrip (testing "simple cases roundtrip" - (are [?sexpr expected-tag ] + (are [?sexpr expected-tag expected-type] (let [n (node/coerce ?sexpr)] (is (node/node? n)) (is (= ?sexpr (node/sexpr n))) (is (string? (node/string n))) (is (= expected-tag (node/tag n)) "tag") + #_(is (= expected-type (protocols/node-type n)) "node-type") (is (not (meta n))) - (is (= (type ?sexpr) (type (node/sexpr n))))) + (if (seq? ?sexpr) + (is (seq? (node/sexpr n))) + (is (= (type (node/sexpr n)) (type ?sexpr) )))) ;; numbers ;; note that we do have an integer-node, but rewrite-clj never parses to it ;; so we never coerce to it either - 3 :token ;;:token - 3N :token ;;:token - 3.14 :token ;;:token - 3.14M :token ;;:token - 3e14 :token ;;:token + 3 :token :token + 3N :token :token + 3.14 :token :token + 3.14M :token :token + 3e14 :token :token ;; ratios are not valid in cljs - #?@(:clj [3/4 :token ;;:token - ] - ) + #?@(:clj [3/4 :token :token]) ;; symbol/keyword/string/... - 'symbol :token ;;:symbol - 'namespace/symbol :token ;;:symbol - :keyword :token ;;:keyword - :1.5.1 :token ;;:keyword - ::keyword :token ;;:keyword - ::1.5.1 :token ;;:keyword - :namespace/keyword :token ;;:keyword + 'symbol :token :symbol + 'namespace/symbol :token :symbol + :keyword :token :keyword + :1.5.1 :token :keyword + ::keyword :token :keyword + ::1.5.1 :token :keyword + :namespace/keyword :token :keyword - "" :token ;;:string - "hello, over there!" :token ;;:string - "multi\nline" :multi-line ;;:string - " " :token ;;:string - "\n" :multi-line ;;:string - "\n\n" :multi-line ;;:string - "," :token ;;:string + "" :token :string + "hello, over there!" :token :string + "multi\nline" :token :string + " " :token :string + "\n" :token :string + "\n\n" :token :string + "," :token :string + "inner\"quote" :token :string + "\\s+" :token :string ;; seqs - [] :vector ;;:seq - [1 2 3] :vector ;;:seq - () :list ;;:seq - '() :list ;;:seq - (list 1 2 3) :list ;;:seq - #{} :set ;;:seq - #{1 2 3} :set ;;:seq + [] :vector :seq + [1 2 3] :vector :seq + () :list :seq + '() :list :seq + (list 1 2 3) :list :seq + #{} :set :seq + #{1 2 3} :set :seq + (cons 1 [2 3]) :list :seq + (lazy-seq [1 2 3]) :list :seq ;; date - #inst "2014-11-26T00:05:23" :token ;; :token - )) - (testing "multi-line string newline variants are normalized" + #inst "2014-11-26T00:05:23" :token :token)) + (testing "multi-line string newline variants are preserved" (let [s "hey\nyou\rover\r\nthere" n (node/coerce s)] - (is (= "hey\nyou\nover\nthere" (node/sexpr n)))))) + (is (= s (node/sexpr n))))) + (testing "coerce string roundtrip" + (is (= "\"hey \\\" man\"" (-> "hey \" man" node/coerce node/string)))) + (testing "coerce string equals parsed string" + (is (= (p/parse-string "\"hello\"") (node/coerce "hello"))))) (deftest t-quoted-list-reader-location-metadata-elided @@ -81,7 +91,7 @@ (let [n (node/coerce ?sexpr)] (is (node/node? n)) (is (= :map (node/tag n))) - ;; (is (= :seq protocols/node-type n)) + #_(is (= :seq (protocols/node-type n))) (is (string? (node/string n))) (is (= ?sexpr (node/sexpr n))) ;; we do not restore to original map (hash-map or array-map), @@ -94,27 +104,16 @@ (array-map) (array-map :d 4 :e 5))) -(deftest t-namespaced-maps-coerce-to-maps - (are [?sexpr] - (let [n (node/coerce ?sexpr)] - (is (node/node? n)) - (is (= :map (node/tag n))) - ;; (is (= :seq (protocols/node-type n))) - (is (string? (node/string n))) - (is (= ?sexpr (node/sexpr n))) - (is (map? (node/sexpr n)))) - #:prefix {:a 1 :b 2} - #::{:c 3 :d 4} - #::p{:e 5 :f 6})) + (deftest t-sexpr->node->sexpr-roundtrip-for-regex (are [?in] (let [n (node/coerce ?in)] (is (node/node? n)) (is (= :regex (node/tag n))) - ;; (is (= :regex (protocols/node-type n))) + #_(is (= :regex (protocols/node-type n))) (is (string? (node/string n))) - #_(is (= (list 're-pattern (regex/pattern-string-for-regex ?in)) + (is (= (list 're-pattern (regex/pattern-string-for-regex ?in)) (node/sexpr n)))) #"abc" #"a\nb\nc" @@ -127,7 +126,7 @@ (let [n (node/coerce #'identity)] (is (node/node? n)) (is (= :var (node/tag n))) - #_(is (= :reader (protocols/node-type n))) + (is (= :reader (protocols/node-type n))) (is (= '(var #?(:clj clojure.core/identity :cljs cljs.core/identity)) (node/sexpr n))))) (deftest t-nil @@ -153,31 +152,30 @@ (deftest t-nodes-coerce-to-themselves (testing "parsed nodes" ;; lean on the parser to create node structures - (are [?s ?tag #_?type] + (are [?s ?tag ?type] (let [n (p/parse-string ?s)] (is (= n (node/coerce n))) (is (= ?tag (node/tag n))) #_(is (= ?type (protocols/node-type n)))) - ";; comment" :comment ;;:comment - "#! comment" :comment ;;:comment - "#(+ 1 %)" :fn ;;:fn - ":my-kw" :token ;;:keyword - "^:m1 [1 2 3]" :meta ;;:meta - "#:p1{:a 1 :b 2}" :namespaced-map ;;:namespaced-map - "'a" :quote ;;:quote - "#'var" :var ;;:reader - "#=eval" :eval ;;:reader - "@deref" :deref ;;:deref - "#mymacro 44" :reader-macro ;;:reader-macro - "#\"regex\"" :regex ;;:regex - "[1 2 3]" :vector ;;:seq - "42" :token ;;:token - "sym" :token ;;:symbol - "#_ 99" :uneval ;;:uneval - " " :whitespace ;;:whitespace - "," :comma ;;:comma - "\n" :newline ;;:newline - )) + ";; comment" :comment :comment + "#! comment" :comment :comment + "#(+ 1 %)" :fn :fn + ":my-kw" :token :keyword + "^:m1 [1 2 3]" :meta :meta + "#:p1{:a 1 :b 2}" :namespaced-map :namespaced-map + "'a" :quote :quote + "#'var" :var :reader + "#=eval" :eval :reader + "@deref" :deref :deref + "#mymacro 44" :reader-macro :reader-macro + "#\"regex\"" :regex :regex + "[1 2 3]" :vector :seq + "42" :token :token + "sym" :token :symbol + "#_ 99" :uneval :uneval + " " :whitespace :whitespace + "," :comma :comma + "\n" :newline :newline)) (testing "parsed forms nodes" (let [n (p/parse-string-all "(def a 1)")] (is (= n (node/coerce n))) diff --git a/test-resources/lib_tests/selmer/core_test.clj b/test-resources/lib_tests/selmer/core_test.clj index 0e200953..72b55012 100644 --- a/test-resources/lib_tests/selmer/core_test.clj +++ b/test-resources/lib_tests/selmer/core_test.clj @@ -8,7 +8,8 @@ [selmer.filters :as f] [selmer.parser :as p :refer [render render-file render-template parse parse-input known-variables - << resolve-var-from-kw env-map]] + << resolve-var-from-kw env-map + resolve-arg]] [selmer.tags :as tags] [clojure.set :as set]) (:import (java.io StringReader ByteArrayInputStream) @@ -1294,3 +1295,32 @@ (is (= "false" (let [y false] (<< "{{y}}"))) "<< picks up local values even if they are false")) + +(deftest resolve-arg-test + (is (= "John" + (resolve-arg "{{variable}}" {:variable "John"})) + "When arg is a variable, returns it substituted by its value.") + (is (= "Hello John!" + (resolve-arg "Hello {{variable}}!" {:variable "John"})) + "When arg contains a variable, return it with the variable substituted by its value.") + (is (= "JOHN" + (resolve-arg "{{variable|upper}}" {:variable "John"})) + "When arg is a filter, returns it where the filter was applied to its value.") + (is (= "Hello JOHN!" + (resolve-arg "Hello {{variable|upper}}!" {:variable "John"})) + "When arg contains a filter, returns it where the filter was applied to its value.") + (is (= "Mr John" + (resolve-arg "{% if variable = \"John\" %}Mr {{variable}}{% endif %}" {:variable "John"})) + "When arg is a tag, returns it where the tag was rendered to its value.") + (is (= "Hello Mr John!" + (resolve-arg "Hello {% if variable = \"John\" %}Mr {{variable}}{% endif %}!" {:variable "John"})) + "When arg contains a tag, returns it where the tag was rendered to its value.") + (is (= "Hello John!" + (resolve-arg "\"Hello John!\"" {})) + "When arg is a double quoted literal string, returns it without double quoting.") + (is (= "Hello John!" + (resolve-arg "Hello John!" {})) + "When arg is a literal string, returns it as is.") + (is (= "29.99" + (resolve-arg "29.99" {})) + "When arg is a literal number, returns it as is.")) diff --git a/test-resources/local-dep/deps.edn b/test-resources/local-dep/deps.edn new file mode 100644 index 00000000..ccd9a316 --- /dev/null +++ b/test-resources/local-dep/deps.edn @@ -0,0 +1 @@ +{:paths ["src"]} diff --git a/test-resources/local-dep/src/local_dep.clj b/test-resources/local-dep/src/local_dep.clj new file mode 100644 index 00000000..c12396c0 --- /dev/null +++ b/test-resources/local-dep/src/local_dep.clj @@ -0,0 +1,3 @@ +(ns local-dep) + +(def local-dep-var :foo) diff --git a/test-resources/script_with_overlapping_opts.clj b/test-resources/script_with_overlapping_opts.clj new file mode 100644 index 00000000..5b71e3dc --- /dev/null +++ b/test-resources/script_with_overlapping_opts.clj @@ -0,0 +1 @@ +(prn *command-line-args*) diff --git a/test-resources/symlink-adjacent-bb b/test-resources/symlink-adjacent-bb new file mode 120000 index 00000000..40b5d4bf --- /dev/null +++ b/test-resources/symlink-adjacent-bb @@ -0,0 +1 @@ +adjacent_bb/medley.bb \ No newline at end of file diff --git a/test/babashka/async_test.clj b/test/babashka/async_test.clj index 7fd1050c..770ac9aa 100644 --- a/test/babashka/async_test.clj +++ b/test/babashka/async_test.clj @@ -42,3 +42,11 @@ [(async/go (async/ (list 'shell {:out out - :err out + (test-utils/with-config {:tasks {'foo (list '-> (list 'shell {:out out + :err out :continue '(fn [proc] (contains? proc :exit))} ls-cmd "foobar") @@ -94,8 +94,8 @@ (is (pos? (bb "run" "--prn" "foo"))))) (testing "shell test with :error" (test-utils/with-config - {:tasks {'foo (list '-> (list 'shell {:out out - :err out + {:tasks {'foo (list '-> (list 'shell {:out out + :err out :error-fn '(constantly 1337)} ls-cmd "foobar"))}} (is (= 1337 (bb "run" "--prn" "foo")))) @@ -118,23 +118,23 @@ (fs/delete out) (testing "depends" (test-utils/with-config {:tasks {'quux (list 'spit out "quux\n") - 'baz (list 'spit out "baz\n" :append true) - 'bar {:depends ['baz] - :task (list 'spit out "bar\n" :append true)} - 'foo {:depends ['quux 'bar 'baz] - :task (list 'spit out "foo\n" :append true)}}} + 'baz (list 'spit out "baz\n" :append true) + 'bar {:depends ['baz] + :task (list 'spit out "bar\n" :append true)} + 'foo {:depends ['quux 'bar 'baz] + :task (list 'spit out "foo\n" :append true)}}} (bb "foo") (is (= "quux\nbaz\nbar\nfoo\n" (slurp out))))) (fs/delete out) ;; This is why we don't support :when for now #_(testing "depends with :when" (test-utils/with-config {:tasks {'quux (list 'spit out "quux\n") - 'baz (list 'spit out "baz\n" :append true) - 'bar {:when false - :depends ['baz] - :task (list 'spit out "bar\n" :append true)} - 'foo {:depends ['quux 'bar] - :task (list 'spit out "foo\n" :append true)}}} + 'baz (list 'spit out "baz\n" :append true) + 'bar {:when false + :depends ['baz] + :task (list 'spit out "bar\n" :append true)} + 'foo {:depends ['quux 'bar] + :task (list 'spit out "foo\n" :append true)}}} (bb "foo") (is (= "quux\nbaz\nbar\nfoo\n" (slurp out)))))) (testing "fully qualified symbol execution" @@ -143,34 +143,34 @@ (is (= :foo (bb "run" "--prn" "foo")))) (test-utils/with-config {:paths ["test-resources/task_scripts"] :tasks '{:requires ([tasks :as t]) - foo t/foo}} + foo t/foo}} (is (= :foo (bb "run" "--prn" "foo")))) (test-utils/with-config {:paths ["test-resources/task_scripts"] :tasks '{foo {:requires ([tasks :as t]) - :task t/foo}}} + :task t/foo}}} (is (= :foo (bb "run" "--prn" "foo"))))) (testing "extra-paths" (test-utils/with-config {:paths ["test-resources/task_scripts"] :tasks '{:requires ([tasks :as t]) - foo {:extra-paths ["test-resources/task_test_scripts"] - :requires ([task-test :as tt]) - :task tt/task-test-fn}}} + foo {:extra-paths ["test-resources/task_test_scripts"] + :requires ([task-test :as tt]) + :task tt/task-test-fn}}} (is (= :task-test-fn (bb "run" "--prn" "foo"))))) (testing "extra-deps" (test-utils/with-config {:tasks '{foo {:extra-deps {medley/medley {:mvn/version "1.3.0"}} - :requires ([medley.core :as m]) - :task (m/index-by :id [{:id 1} {:id 2}])}}} + :requires ([medley.core :as m]) + :task (m/index-by :id [{:id 1} {:id 2}])}}} (is (= {1 {:id 1}, 2 {:id 2}} (bb "run" "--prn" "foo"))))) (testing "enter / leave" - (test-utils/with-config '{:tasks {:init (do (def enter-ctx (atom [])) - (def leave-ctx (atom []))) + (test-utils/with-config '{:tasks {:init (do (def enter-ctx (atom [])) + (def leave-ctx (atom []))) :enter (swap! enter-ctx conj (:name (current-task))) :leave (swap! leave-ctx conj (:name (current-task))) - foo {:depends [bar] - :task [@enter-ctx @leave-ctx]} - bar {:depends [baz]} - baz {:enter nil - :leave nil}}} + foo {:depends [bar] + :task [@enter-ctx @leave-ctx]} + bar {:depends [baz]} + baz {:enter nil + :leave nil}}} (is (= '[[bar foo] [bar]] (bb "run" "--prn" "foo"))))) (testing "run" (test-utils/with-config '{:tasks {a (+ 1 2 3) @@ -184,28 +184,28 @@ (testing "unresolved dependency" (test-utils/with-config '{:tasks {a (+ 1 2 3) b {:depends [x] - :task (+ a 4 5 6)}}} + :task (+ a 4 5 6)}}} (is (thrown-with-msg? Exception #"No such task: x" (bb "run" "b"))))) (testing "cyclic task" (test-utils/with-config '{:tasks {b {:depends [b] - :task (+ a 4 5 6)}}} + :task (+ a 4 5 6)}}} (is (thrown-with-msg? Exception #"Cyclic task: b" (bb "run" "b")))) (test-utils/with-config '{:tasks {c {:depends [b]} b {:depends [c] - :task (+ a 4 5 6)}}} + :task (+ a 4 5 6)}}} (is (thrown-with-msg? Exception #"Cyclic task: b" (bb "run" "b"))))) (testing "friendly regex literal error handling" (test-utils/with-config - "{:tasks {something (clojure.string/split \"1-2\" #\"-\")}}" - (is (thrown-with-msg? - Exception #"Invalid regex literal" - (bb "run" "something"))))) + "{:tasks {something (clojure.string/split \"1-2\" #\"-\")}}" + (is (thrown-with-msg? + Exception #"Invalid regex literal" + (bb "run" "something"))))) (testing "doc" (test-utils/with-config '{:tasks {b {:doc "Beautiful docstring"}}} (let [s (test-utils/bb nil "doc" "b")] @@ -216,21 +216,21 @@ (is (= "b" s))))) (testing "parallel test" (test-utils/with-config (edn/read-string (slurp "test-resources/coffee-tasks.edn")) - (let [tree [:made-coffee [[:ground-beans [:measured-beans]] [:heated-water [:poured-water]] :filter :mug]] - t0 (System/currentTimeMillis) - s (bb "run" "--prn" "coffeep") - t1 (System/currentTimeMillis) + (let [tree [:made-coffee [[:ground-beans [:measured-beans]] [:heated-water [:poured-water]] :filter :mug]] + t0 (System/currentTimeMillis) + s (bb "run" "--prn" "coffeep") + t1 (System/currentTimeMillis) delta-sequential (- t1 t0)] (is (= tree s)) (test-utils/with-config (edn/read-string (slurp "test-resources/coffee-tasks.edn")) - (let [t0 (System/currentTimeMillis) - s (bb "run" "--parallel" "--prn" "coffeep") - t1 (System/currentTimeMillis) + (let [t0 (System/currentTimeMillis) + s (bb "run" "--parallel" "--prn" "coffeep") + t1 (System/currentTimeMillis) delta-parallel (- t1 t0)] (is (= tree s)) - (when (>= (doto (-> (Runtime/getRuntime) (.availableProcessors)) - (prn)) - 2) + (when (>= (doto (-> (Runtime/getRuntime) (.availableProcessors)) + (prn)) + 2) (is (< delta-parallel delta-sequential))))))) (testing "exception" (test-utils/with-config '{:tasks {a (Thread/sleep 10000) @@ -241,40 +241,40 @@ (bb "run" "--parallel" "c"))))) (testing "edge case" (test-utils/with-config '{:tasks - {a (run '-a {:parallel true}) - -a {:depends [a:a a:b c] - :task (prn [a:a a:b c])} + {a (run '-a {:parallel true}) + -a {:depends [a:a a:b c] + :task (prn [a:a a:b c])} a:a {:depends [c] - :task (+ 1 2 3)} + :task (+ 1 2 3)} a:b {:depends [c] - :task (do (Thread/sleep 10) - (+ 1 2 3))} - c (do (Thread/sleep 10) :c)}} + :task (do (Thread/sleep 10) + (+ 1 2 3))} + c (do (Thread/sleep 10) :c)}} (is (= [6 6 :c] (bb "run" "--prn" "a")))))) (testing "dynamic vars" (test-utils/with-config '{:tasks {:init (def ^:dynamic *foo* true) - a (do - (def ^:dynamic *bar* false) - (binding [*foo* false - *bar* true] - [*foo* *bar*]))}} + a (do + (def ^:dynamic *bar* false) + (binding [*foo* false + *bar* true] + [*foo* *bar*]))}} (is (= [false true] (bb "run" "--prn" "a"))))) (testing "stable namespace name" (test-utils/with-config '{:tasks - {:init (do (def ^:dynamic *jdk*) - (def ^:dynamic *server*)) - server [*jdk* *server*] - run-all (for [jdk [8 11 15] + {:init (do (def ^:dynamic *jdk*) + (def ^:dynamic *server*)) + server [*jdk* *server*] + run-all (for [jdk [8 11 15] server [:foo :bar]] - (binding [*jdk* jdk + (binding [*jdk* jdk *server* server] (babashka.tasks/run 'server)))}} (is (= '([8 :foo] [8 :bar] [11 :foo] [11 :bar] [15 :foo] [15 :bar]) (bb "run" "--prn" "run-all"))))) ;; TODO: disabled because of " Volume in drive C has no label.\r\n Volume Serial Number is 1CB8-D4AA\r\n\r\n Directory of C:\\projects\\babashka\r\n\r\n" on Appveyor. See https://ci.appveyor.com/project/borkdude/babashka/builds/40003094. (testing "shell test with :continue" - (let [ls-cmd (if main/windows? "cmd /c dir" "ls")] + (let [ls-cmd (if main/windows? "cmd /c dir" "ls")] (test-utils/with-config {:tasks {'foo (list 'do (list 'shell {:continue true} (str ls-cmd " foobar")) @@ -303,6 +303,27 @@ (let [s (bb "run" "--prn" "a")] (is (str/includes? s "paths"))))))) +(deftest tasks:clojure-test + (testing "tokenization when called from tasks" + (test-utils/with-config + (pr-str '{:tasks {foo (-> (clojure {:out :string} "-J-Dfoo=\"{:port 5555 :accept clojure.core.server/repl}\" -M -e \"(clojure.edn/read-string (System/getProperty (name :foo)))\"") :out clojure.edn/read-string prn)}}) + (is (= '{:port 5555, :accept clojure.core.server/repl} + (bb "run" "foo"))))) + (testing "can be called without args" + (test-utils/with-config + (pr-str '{:tasks {foo (-> (clojure {:in "(+ 1 2 3)" :out :string}) :out prn)}}) + (is (str/includes? (bb "run" "foo") "6"))) + ;; can't properly test this, but `(clojure)` should work with zero args + #_(test-utils/with-config + (pr-str '{:tasks {foo (-> (clojure) :out prn)}}) + (is (str/includes? (test-utils/bb "(+ 1 2 3)" "run" "foo") "6")))) + (testing "call to run in missing dir gives 'cannot run program' message" + (test-utils/with-config + (pr-str '{:tasks {foo (clojure {:dir "../missingdir"} "-M" "-r")}}) + ;; check rough text of error message, specific message about missing directory is OS-dependent + (is (thrown-with-msg? Exception #"Cannot run program .* \(in directory \"\.\.[/\\]missingdir\"\)" + (bb "run" "foo")))))) + (deftest list-tasks-test (test-utils/with-config {} (let [res (test-utils/bb nil "tasks")] @@ -319,7 +340,7 @@ (test-utils/with-config '{:tasks {abc 1 xyz 2}} (let [res (test-utils/bb nil "tasks")] (is (= "The following tasks are available:\n\nabc\nxyz\n" res)))) - (test-utils/with-config '{:tasks {abc 1 xyz {:doc "some text" :tasks 5} + (test-utils/with-config '{:tasks {abc 1 xyz {:doc "some text" :tasks 5} -xyz 3 qrs {:private true}}} (let [res (test-utils/bb nil "tasks")] (is (= "The following tasks are available:\n\nabc\nxyz some text\n" res)))) @@ -366,7 +387,7 @@ even more stuff here\" (try (spit "uberjar" "#!/usr/bin/env bb\n(+ 1 2 3)") (vreset! common/bb-edn '{:tasks {uberjar (+ 1 2 3)}}) - (is (= "uberjar" (:file (main/parse-opts ["uberjar"])))) + (is (= {:file "uberjar", :command-line-args '("--version")} (second (main/parse-opts ["uberjar" "--version"])))) (finally (fs/delete "uberjar")))))) (deftest min-bb-version-test @@ -375,7 +396,7 @@ even more stuff here\" (spit config '{:min-bb-version "300.0.0"}) (let [sw (java.io.StringWriter.)] (binding [*err* sw] - (main/main "--config" config "-e" "nil")) + (main/main "--config" config "-e" "nil")) (is (str/includes? (str sw) "WARNING: this project requires babashka 300.0.0 or newer, but you have: ")))))) @@ -388,7 +409,7 @@ even more stuff here\" entries (cp/split-classpath out) entry (first entries)] (is (= 1 (count entries))) - (is (= (fs/parent config) (fs/parent entry))) + (is (= (fs/real-path (fs/parent config)) (fs/real-path (fs/parent entry)))) (is (str/ends-with? entry "src")))))) (deftest without-deps-test @@ -455,9 +476,9 @@ even more stuff here\" (when (= "amd64" (System/getProperty "os.arch")) ; TODO: Build bootleg for aarch64 too or use a different pod (test-utils/with-config (pr-str '{:paths ["test-resources"] - :pods {retrogradeorbit/bootleg {:version "0.1.9"}}}) + :pods {retrogradeorbit/bootleg {:version "0.1.9"}}}) (is (= "\"

Test

\"\n" - (test-utils/bb nil "-m" "pod-tests.bootleg")))))) + (test-utils/bb nil "--prn" "-m" "pod-tests.bootleg")))))) (deftest ^:skip-windows local-pod-test (test-utils/with-config @@ -476,3 +497,47 @@ even more stuff here\" "{:deps {}}" (is (= {1 {:a 1}} (bb "-Sdeps" "{:deps {medley/medley {:mvn/version \"1.4.0\"}}}" "-e" "(require 'medley.core) (medley.core/index-by :a [{:a 1}])"))))) + +(deftest deps-root-test + (fs/with-temp-dir [dir {}] + (let [f (fs/file dir "bb.edn") + config (str f)] + (spit config + '{:paths ["src"] + :tasks {cp (prn (babashka.classpath/get-classpath))}}) + (testing "custom deps-root path" + (let [out (bb "--config" config "--deps-root" (str dir) "cp") + entries (cp/split-classpath out)] + (is (= 1 (count entries))) + (is (= (fs/file dir "src") (fs/file (first entries)))))) + (testing "default deps-root path is same as bb.edn" + (let [out (bb "--config" config "cp") + entries (cp/split-classpath out)] + (is (= (fs/real-path(fs/parent f)) (fs/real-path (fs/parent (first entries))))))) + (spit config + '{:paths ["src"] + :deps {local/dep {:local/root "local-dep"}} + :tasks {cp (prn (babashka.classpath/get-classpath))}}) + (testing "relative paths in deps should be relative to bb.edn" + (let [root (fs/create-dir (fs/file dir "local-dep")) + _ (spit (str (fs/file root "deps.edn")) {}) + out (bb "--config" config "cp") + entries (cp/split-classpath out)] + (is (= (fs/real-path (fs/parent f)) (fs/real-path (fs/parent (first entries)))))))))) + +(deftest adjacent-bb-edn-test + (is (= {1 {:id 1}} (bb "test-resources/adjacent_bb/medley.bb"))) + (is (= {1 {:id 1}} (bb "-f" "test-resources/adjacent_bb/medley.bb"))) + (testing "symlink" + (is (= {1 {:id 1}} (bb (str (fs/file "test-resources" "symlink-adjacent-bb"))))))) + +(deftest non-existing-tasks-in-run-gives-exit-code-1 + (is (thrown? Exception (bb "-Sdeps" "{:tasks {foo {:task (run (quote bar))}}}" "foo")))) + +(deftest empty-bb-edn-test + (is (= 6 (bb "-Sdeps" "" "-e" "(+ 1 2 3)")))) + +(deftest warning-on-override-task + (when-not test-utils/native? + (binding [*out* *err*] + (is (str/includes? (with-out-str (bb "-Sdeps" "{:tasks {run {:task 1}}}" "run")) "'run' override"))))) diff --git a/test/babashka/classpath_test.clj b/test/babashka/classpath_test.clj index ae04e945..dbdaaa3a 100644 --- a/test/babashka/classpath_test.clj +++ b/test/babashka/classpath_test.clj @@ -13,12 +13,12 @@ (deftest classpath-test (is (= :my-script/bb - (bb nil "--classpath" "test-resources/babashka/src_for_classpath_test" + (bb nil "--prn" "--classpath" "test-resources/babashka/src_for_classpath_test" "(require '[my-script :as ms]) (ms/foo)"))) (is (= "hello from foo\n" - (tu/bb nil "--classpath" "test-resources/babashka/src_for_classpath_test/foo.jar" + (tu/bb nil "--prn" "--classpath" "test-resources/babashka/src_for_classpath_test/foo.jar" "(require '[foo :as f]) (f/foo)"))) - (is (thrown-with-msg? Exception #"not find" + (is (thrown-with-msg? Exception #"not locate" (tu/bb nil "(require '[foo :as f])")))) @@ -42,10 +42,10 @@ (deftest main-test (is (= "(\"1\" \"2\" \"3\" \"4\")\n" - (tu/bb nil "--classpath" "test-resources/babashka/src_for_classpath_test" "-m" "my.main" "1" "2" "3" "4"))) + (tu/bb nil "--prn" "--classpath" "test-resources/babashka/src_for_classpath_test" "-m" "my.main" "1" "2" "3" "4"))) (testing "system property" (is (= "\"my.main2\"" - (str/trim (tu/bb nil "--classpath" "test-resources/babashka/src_for_classpath_test" "-m" "my.main2")))))) + (str/trim (tu/bb nil "--prn" "--classpath" "test-resources/babashka/src_for_classpath_test" "-m" "my.main2")))))) (deftest error-while-loading-test (is (true? @@ -70,3 +70,20 @@ (str/trim (tu/bb nil "--classpath" "test-resources/babashka/src_for_classpath_test/foo.jar" "(pos? (count (slurp (io/resource \"foo.clj\")))) ")))))) + +(deftest classloader-test + (let [url + (tu/bb nil "--classpath" "test-resources/babashka/src_for_classpath_test/foo.jar" + "(first (map str (.getURLs (clojure.lang.RT/baseLoader))))")] + (is (str/includes? url "file:")) + (is (str/includes? url "foo.jar"))) + (let [results (tu/bb nil "--classpath" "test-resources/babashka/src_for_classpath_test/foo.jar" + "(map some? [(.getResource (clojure.lang.RT/baseLoader) \"foo.clj\") + (.getResourceAsStream (clojure.lang.RT/baseLoader) \"foo.clj\") + (.getResources (clojure.lang.RT/baseLoader) \"foo.clj\")])")] + (is (= [true true true] (edn/read-string results))))) + +(deftest reader-tag-test + (is (= [[3 2 1] [1 2 3]] + (bb nil "--classpath" "test-resources/babashka/src_for_classpath_test" + "(require 'reader) [#r/reverse [1 2 3] #r/distinct [1 1 2 3]]")))) diff --git a/test/babashka/crypto_test.clj b/test/babashka/crypto_test.clj index 48503f69..22141991 100644 --- a/test/babashka/crypto_test.clj +++ b/test/babashka/crypto_test.clj @@ -34,3 +34,19 @@ (String. (.encode (java.util.Base64/getEncoder) (hmac-sha-256 (.getBytes key-s) data)) "utf-8")))))))) + +(deftest secretkey-test + (is (= 32 (bb '(do (import 'javax.crypto.SecretKeyFactory) + (import 'javax.crypto.spec.PBEKeySpec) + + (defn gen-secret-key + "Generate secret key based on a given token string. + Returns bytes array 256-bit length." + [^String secret-token] + (let [salt (.getBytes "abcde") + factory (SecretKeyFactory/getInstance "PBKDF2WithHmacSHA256") + spec (PBEKeySpec. (.toCharArray secret-token) salt 10000 256) + secret (.generateSecret factory spec)] + (count (.getEncoded secret)))) + + (gen-secret-key "foo")))))) diff --git a/test/babashka/deps_test.clj b/test/babashka/deps_test.clj index e130e680..56980e98 100644 --- a/test/babashka/deps_test.clj +++ b/test/babashka/deps_test.clj @@ -7,10 +7,11 @@ [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)))) + (let [edn-str (apply test-utils/bb nil (map str args))] + (edn/read-string + {:readers *data-readers* + :eof nil} + edn-str))) (deftest dependency-test (is (= #{:a :c :b} (bb " @@ -105,21 +106,21 @@ true (is (fs/exists? libs-dir2)))))) (deftest ^:windows-only win-clojure-test - (testing "GITLIBS can set location of .gitlibs dir" - (let [tmp-dir (fs/create-temp-dir) - libs-dir (fs/file tmp-dir ".gitlibs") - libs-dir2 (fs/file tmp-dir ".gitlibs2") + (testing "GITLIBS can set location of .gitlibs dir" + (let [tmp-dir (fs/create-temp-dir) + libs-dir (fs/file tmp-dir ".gitlibs") + libs-dir2 (fs/file tmp-dir ".gitlibs2") ; nested quotes need different escaping for Windows based on jvm/native test - escape-quote (if test-utils/native? "\\\\\"" "\\\"") - deps-map (str/join escape-quote [" \"{:deps {babashka/process {:git/url " - "https://github.com/babashka/process" " :sha " - "4c6699d06b49773d3e5c5b4c11d3334fb78cc996" "}}}\""]) - template (str "(do (babashka.deps/clojure [\"-Sforce\" \"-Spath\" \"-Sdeps\"" deps-map "] + escape-quote (if test-utils/native? "\\\\\"" "\\\"") + deps-map (str/join escape-quote [" \"{:deps {babashka/process {:git/url " + "https://github.com/babashka/process" " :sha " + "4c6699d06b49773d3e5c5b4c11d3334fb78cc996" "}}}\""]) + template (str "(do (babashka.deps/clojure [\"-Sforce\" \"-Spath\" \"-Sdeps\"" deps-map "] {:out :string :env-key {\"PATH\" (System/getenv \"PATH\") \"GITLIBS\" :gitlibs}}) nil)")] - (bb (-> template (str/replace ":gitlibs" (pr-str (str libs-dir))) + (bb (-> template (str/replace ":gitlibs" (pr-str (str libs-dir))) (str/replace ":env-key" ":env"))) - (bb (-> template (str/replace ":gitlibs" (pr-str (str libs-dir2))) + (bb (-> template (str/replace ":gitlibs" (pr-str (str libs-dir2))) (str/replace ":env-key" ":extra-env"))) - (is (fs/exists? libs-dir)) - (is (fs/exists? libs-dir2))))) + (is (fs/exists? libs-dir)) + (is (fs/exists? libs-dir2))))) diff --git a/test/babashka/error_test.clj b/test/babashka/error_test.clj index e82a7175..e79967b3 100644 --- a/test/babashka/error_test.clj +++ b/test/babashka/error_test.clj @@ -7,13 +7,19 @@ [clojure.test :as t :refer [deftest is testing]] [matcher-combinators.test])) +(defn process-difference [line] + (-> line str/trimr + ;; take into account JDK14+ and native image differences + (str/replace "class clojure.lang" "clojure.lang") + (str/replace #" \(.*\)$" ""))) + (defmacro multiline-equals [s1 s2] `(let [lines-s1# (str/split-lines ~s1) lines-s2# (str/split-lines ~s2) max-lines# (max (count lines-s1#) (count lines-s2#)) lines-s1# (take max-lines# lines-s1#) lines-s2# (take max-lines# lines-s2#)] - (is (~'match? (map str/trimr lines-s1#) (map str/trimr lines-s2#))) + (is (~'match? (map process-difference lines-s1#) (map process-difference lines-s2#))) #_(run! (fn [i] (let [l1 (get lines-s1 i) l2 (get lines-s2 i)] @@ -117,58 +123,63 @@ user - :1:1"))))) (deftest error-while-macroexpanding-test - (let [output (try (tu/bb nil "-e" "(defmacro foo [x] (subs nil 1) `(do ~x ~x)) (foo 1)") + (let [output (try (tu/bb nil "-e" "(defmacro foo [x] (assoc :foo 1 2) `(do ~x ~x)) (foo 1)") (catch Exception e (ex-message e)))] (multiline-equals output "----- Error -------------------------------------------------------------------- -Type: java.lang.NullPointerException +Type: java.lang.ClassCastException +Message: clojure.lang.Keyword cannot be cast to clojure.lang.Associative Location: :1:19 Phase: macroexpand ----- Context ------------------------------------------------------------------ -1: (defmacro foo [x] (subs nil 1) `(do ~x ~x)) (foo 1) - ^--- +1: (defmacro foo [x] (assoc :foo 1 2) `(do ~x ~x)) (foo 1) + ^--- clojure.lang.Keyword cannot be cast to clojure.lang.Associative ----- Stack trace -------------------------------------------------------------- -clojure.core/subs - -user/foo - :1:19 -user/foo - :1:1 -user - :1:45"))) +clojure.core/assoc--5481 - +clojure.core/assoc - +user/foo - :1:19 +user/foo - :1:1 +user - :1:49"))) (deftest error-in-macroexpansion-test - (let [output (try (tu/bb nil "-e" "(defmacro foo [x] `(subs nil ~x)) (foo 1)") + (let [output (try (tu/bb nil "-e" "(defmacro foo [x] `(assoc :foo ~x 2)) (foo 1)") (catch Exception e (ex-message e)))] (multiline-equals output "----- Error -------------------------------------------------------------------- -Type: java.lang.NullPointerException -Location: :1:35 +Type: java.lang.ClassCastException +Message: clojure.lang.Keyword cannot be cast to clojure.lang.Associative +Location: :1:39 ----- Context ------------------------------------------------------------------ -1: (defmacro foo [x] `(subs nil ~x)) (foo 1) - ^--- +1: (defmacro foo [x] `(assoc :foo ~x 2)) (foo 1) + ^--- clojure.lang.Keyword cannot be cast to clojure.lang.Associative ----- Stack trace -------------------------------------------------------------- -clojure.core/subs - -user - :1:35 -")) +clojure.core/assoc--5481 - +clojure.core/assoc - +user - :1:39")) (testing "calling a var inside macroexpansion" - (let [output (try (tu/bb nil "-e" "(defn quux [] (subs nil 1)) (defmacro foo [x & xs] `(do (quux) ~x)) (defn bar [] (foo 1)) (bar)") + (let [output (try (tu/bb nil "-e" "(defn quux [] (assoc :foo 1 2)) (defmacro foo [x & xs] `(do (quux) ~x)) (defn bar [] (foo 1)) (bar)") (catch Exception e (ex-message e)))] (multiline-equals output "----- Error -------------------------------------------------------------------- -Type: java.lang.NullPointerException +Type: java.lang.ClassCastException +Message: clojure.lang.Keyword cannot be cast to clojure.lang.Associative Location: :1:15 ----- Context ------------------------------------------------------------------ -1: (defn quux [] (subs nil 1)) (defmacro foo [x & xs] `(do (quux) ~x)) (defn bar [] (foo 1)) (bar) - ^--- +1: (defn quux [] (assoc :foo 1 2)) (defmacro foo [x & xs] `(do (quux) ~x)) (defn bar [] (foo 1)) (bar) + ^--- clojure.lang.Keyword cannot be cast to clojure.lang.Associative ----- Stack trace -------------------------------------------------------------- -clojure.core/subs - -user/quux - :1:15 -user/quux - :1:1 -user/bar - :1:69 -user - :1:91")))) +clojure.core/assoc--5481 - +clojure.core/assoc - +user/quux - :1:15 +user/quux - :1:1 +user/bar - :1:73 +user - :1:95")))) (deftest print-exception-data-test (testing "output of uncaught ExceptionInfo" @@ -232,29 +243,31 @@ clojure.lang.ExceptionInfo: Divide by zero"))) "{:type :sci/error, :line 1, :column 12, :message \"Divide by zero\","))))) (deftest macro-test - (let [output (try (tu/bb nil "--debug" "(defmacro foo [x] (subs nil 1) `(do ~x ~x)) (foo 1)") + (let [output (try (tu/bb nil "--debug" "(defmacro foo [x] (assoc :foo 1 2) `(do ~x ~x)) (foo 1)") (is false) (catch Exception e (ex-message e))) output (tu/normalize output) - actual-lines (str/join "\n" (take 17 (str/split-lines output)))] + actual-lines (str/join "\n" (take 19 (str/split-lines output)))] (multiline-equals actual-lines "----- Error -------------------------------------------------------------------- -Type: java.lang.NullPointerException +Type: java.lang.ClassCastException +Message: clojure.lang.Keyword cannot be cast to clojure.lang.Associative Location: :1:19 Phase: macroexpand ----- Context ------------------------------------------------------------------ -1: (defmacro foo [x] (subs nil 1) `(do ~x ~x)) (foo 1) - ^--- +1: (defmacro foo [x] (assoc :foo 1 2) `(do ~x ~x)) (foo 1) + ^--- clojure.lang.Keyword cannot be cast to clojure.lang.Associative ----- Stack trace -------------------------------------------------------------- -clojure.core/subs - -user/foo - :1:19 -user/foo - :1:1 -user - :1:45 +clojure.core/assoc--5481 - +clojure.core/assoc - +user/foo - :1:19 +user/foo - :1:1 +user - :1:49 ----- Exception ---------------------------------------------------------------- -clojure.lang.ExceptionInfo: null"))) +clojure.lang.ExceptionInfo: clojure.lang.Keyword cannot be cast to clojure.lang.Associative"))) (deftest native-stacktrace-test (let [output (try (tu/bb nil "(merge 1 2 3)") diff --git a/test/babashka/exec_test.clj b/test/babashka/exec_test.clj index 4a990520..69d13659 100644 --- a/test/babashka/exec_test.clj +++ b/test/babashka/exec_test.clj @@ -11,9 +11,11 @@ (deftest exec-test (is (= {:foo 1} (edn/read-string (bb "-x" "prn" "--foo" "1")))) (is (thrown? Exception (bb "-x" "json/generate-string" "--foo" "1"))) + (is (thrown-with-msg? Exception #"Could not resolve sym to a function: clojure.core/generate-string" + (bb "-x" "clojure.core/generate-string" "--foo" "1"))) (is (= {:foo 1} (cheshire/parse-string (edn/read-string - (bb "-x" "cheshire.core/generate-string" "--foo" "1")) true)))) + (bb "--prn" "-x" "cheshire.core/generate-string" "--foo" "1")) true)))) (deftest tasks-exec-test (u/with-config @@ -37,8 +39,13 @@ (testing "task exec args" (u/with-config "{:deps {} - :tasks {foo {:exec-args {:foo :bar} - :task (exec 'babashka.exec-test/exec-test)}}}" + :tasks {foo {:task (exec 'babashka.exec-test/exec-test)}}}" + (is (= {:foo :foo, :bar :yeah} + (edn/read-string (bb "-cp" "test-resources" "run" "foo" "--bar" "yeah"))))) + (u/with-config + "{:deps {} + :tasks {foo {:exec-args {:foo :bar} + :task (exec 'babashka.exec-test/exec-test)}}}" (is (= {:foo :bar, :bar :yeah} (edn/read-string (bb "-cp" "test-resources" "run" "foo" "--bar" "yeah")))))) (testing "meta" diff --git a/test/babashka/file_var_test.clj b/test/babashka/file_var_test.clj index e601523a..bc5d058b 100644 --- a/test/babashka/file_var_test.clj +++ b/test/babashka/file_var_test.clj @@ -10,7 +10,7 @@ (deftest file-var-test (let [[f1 f2 f3 f4] - (str/split (bb nil "--classpath" "test/babashka/scripts" + (str/split (bb nil "--prn" "--classpath" "test/babashka/scripts" "test/babashka/scripts/file_var.bb") #"\n")] (is (str/ends-with? f1 "file_var_classpath.bb")) @@ -19,6 +19,6 @@ (is (str/ends-with? f4 "file_var.bb"))) (testing "file var uses absolute path" (is (str/includes? - (bb nil (io/file "test" ".." "test" - "babashka" "scripts" "simple_file_var.bb")) + (bb nil "--prn" (io/file "test" ".." "test" + "babashka" "scripts" "simple_file_var.bb")) "..")))) diff --git a/test/babashka/http_connection_test.clj b/test/babashka/http_connection_test.clj index 25a5338e..b3247c1d 100644 --- a/test/babashka/http_connection_test.clj +++ b/test/babashka/http_connection_test.clj @@ -7,7 +7,7 @@ (defn bb [& args] (apply tu/bb nil (map str args))) -(deftest open-connection-test +(deftest ^:flaky open-connection-test (is (try (= "\"1\"" (str/trim (bb "-e" " (require '[cheshire.core :as json]) (let [conn ^java.net.HttpURLConnection (.openConnection (java.net.URL. \"https://postman-echo.com/get?foo=1\"))] diff --git a/test/babashka/impl/clojure/java/browse_test.clj b/test/babashka/impl/clojure/java/browse_test.clj new file mode 100644 index 00000000..83ee3fb1 --- /dev/null +++ b/test/babashka/impl/clojure/java/browse_test.clj @@ -0,0 +1,25 @@ +(ns babashka.impl.clojure.java.browse-test + #_(:require + [babashka.test-utils :refer [bb]] + [cheshire.core :as json] + [clojure.test :refer [deftest is]] + [org.httpkit.server :as http])) + +(def ^:dynamic *http-port* 1234) + +#_(deftest browse-url-test + (let [p (promise) + stop-server (http/run-server (fn [{:keys [query-string]}] + (let [params (apply hash-map (mapcat #(.split % "=") (.split query-string "&")))] + (deliver p params) + {:status 200 + :content-type "application/json" + :body (json/encode params)})) + {:port *http-port*})] + (try + (bb nil + (str "(clojure.java.browse/browse-url \"http://localhost:" *http-port* "?arg1=v1&arg2=v2\")")) + (is (= {"arg1" "v1" + "arg2" "v2"} + (deref p 5000 ::timeout))) + (finally (stop-server :timeout 1000))))) diff --git a/test/babashka/impl/nrepl_server_test.clj b/test/babashka/impl/nrepl_server_test.clj index 04946085..108cc123 100644 --- a/test/babashka/impl/nrepl_server_test.clj +++ b/test/babashka/impl/nrepl_server_test.clj @@ -1,13 +1,18 @@ (ns babashka.impl.nrepl-server-test (:require + [babashka.fs :as fs] + [babashka.impl.nrepl-server :refer [start-server!]] [babashka.main :as main] - [babashka.nrepl.server :refer [start-server! stop-server! parse-opt]] + [babashka.nrepl.server :refer [parse-opt stop-server!]] [babashka.test-utils :as tu] [babashka.wait :as wait] [bencode.core :as bencode] [clojure.test :as t :refer [deftest is testing]] - [sci.impl.opts :refer [init]]) - (:import [java.lang ProcessBuilder$Redirect])) + [sci.core :as sci] + [sci.ctx-store :as ctx-store] + [babashka.impl.classpath :as cp]) + (:import + [java.lang ProcessBuilder$Redirect])) (def debug? false) @@ -28,6 +33,9 @@ res) res (if-let [status (:sessions res)] (assoc res :sessions (mapv bytes->str status)) + res) + res (if-let [cp (:classpath res)] + (assoc res :classpath (mapv bytes->str cp)) res)] res)) @@ -59,9 +67,11 @@ (let [msg (read-reply in session @id) id (:id msg) versions (:versions msg) - babashka-version (bytes->str (get versions "babashka"))] + babashka-version (bytes->str (get versions "babashka")) + ops (:ops msg)] (is (= 1 id)) - (is (= main/version babashka-version)))) + (is (= main/version babashka-version)) + (is (contains? ops "classpath")))) (testing "eval" (bencode/write-bencode os {"op" "eval" "code" "(+ 1 2 3)" "session" session "id" (new-id!)}) (let [msg (read-reply in session @id) @@ -87,13 +97,13 @@ (is (= ":foo0" (:value (read-reply in session @id))))) ;; TODO: I don't remember why we created a new ns #_(testing "providing an ns value of a non-existing namespace creates the namespace" - (bencode/write-bencode os {"op" "eval" - "code" "(ns-name *ns*)" - "session" session - "id" (new-id!) - "ns" "unicorn"}) - (let [reply (read-reply in session @id)] - (is (= "unicorn" (:value reply)))))) + (bencode/write-bencode os {"op" "eval" + "code" "(ns-name *ns*)" + "session" session + "id" (new-id!) + "ns" "unicorn"}) + (let [reply (read-reply in session @id)] + (is (= "unicorn" (:value reply)))))) (testing "multiple top level expressions results in two value replies" (bencode/write-bencode os {"op" "eval" "code" "(+ 1 2 3) (+ 1 2 3)" @@ -181,37 +191,53 @@ "session" session "id" (new-id!)}) (dotimes [_ 3] (let [reply (read-reply in session @id)] - (is (= "Hello\n" (tu/normalize (:out reply)))))))))) + (is (= "Hello\n" (tu/normalize (:out reply))))))) + (testing "dynamic var can be set!, test unchecked-math" + (bencode/write-bencode os {"op" "eval" "code" "(set! *unchecked-math* true)" + "session" session "id" (new-id!)}) + (let [reply (read-reply in session @id)] + (is (= "true" (:value reply))))) + (testing "classpath op" + (bencode/write-bencode os {"op" "eval" "code" "(babashka.classpath/add-classpath \"test-resources/babashka/src_for_classpath_test\")" + "session" session "id" (new-id!)}) + (read-reply in session @id) + (bencode/write-bencode os {"op" "classpath" + "session" session "id" (new-id!)}) + (let [reply (read-reply in session @id) + cp (:classpath reply)] + (is (every? string? cp)) + (is (pos? (count cp))) + ;; dev-resources doesn't exist + (is (pos? (count (filter fs/exists? cp))))))))) (deftest ^:skip-windows nrepl-server-test (let [proc-state (atom nil) - server-state (atom nil)] - (try - (if tu/jvm? - (let [nrepl-opts (parse-opt "0.0.0.0:1668") - nrepl-opts (assoc nrepl-opts - :describe {"versions" {"babashka" main/version}}) - server (start-server! - (init {:namespaces main/namespaces - :features #{:bb}}) - nrepl-opts)] - (reset! server-state server)) - (let [pb (ProcessBuilder. ["./bb" "nrepl-server" "0.0.0.0:1668"]) - _ (.redirectError pb ProcessBuilder$Redirect/INHERIT) - ;; _ (.redirectOutput pb ProcessBuilder$Redirect/INHERIT) - ;; env (.environment pb) - ;; _ (.put env "BABASHKA_DEV" "true") - proc (.start pb)] - (reset! proc-state proc))) - (babashka.wait/wait-for-port "localhost" 1668) - (nrepl-test) - (finally + server-state (atom nil) + ctx (sci/init {:namespaces main/namespaces + :features #{:bb}})] + (sci.ctx-store/with-ctx ctx + (try (if tu/jvm? - (stop-server! @server-state) - (when-let [proc @proc-state] - (.destroy ^Process proc))))))) + (let [nrepl-opts (parse-opt "0.0.0.0:1668") + nrepl-opts (assoc nrepl-opts + :describe {"versions" {"babashka" main/version}}) + server (start-server! nrepl-opts)] + (reset! server-state server)) + (let [pb (ProcessBuilder. ["./bb" "nrepl-server" "0.0.0.0:1668"]) + _ (.redirectError pb ProcessBuilder$Redirect/INHERIT) + ;; _ (.redirectOutput pb ProcessBuilder$Redirect/INHERIT) + ;; env (.environment pb) + ;; _ (.put env "BABASHKA_DEV" "true") + proc (.start pb)] + (reset! proc-state proc))) + (babashka.wait/wait-for-port "localhost" 1668) + (nrepl-test) + (finally + (if tu/jvm? + (stop-server! @server-state) + (when-let [proc @proc-state] + (.destroy ^Process proc)))))))) ;;;; Scratch -(comment - ) +(comment) diff --git a/test/babashka/impl/socket_repl_test.clj b/test/babashka/impl/socket_repl_test.clj index 5699b456..ba94da9d 100644 --- a/test/babashka/impl/socket_repl_test.clj +++ b/test/babashka/impl/socket_repl_test.clj @@ -1,8 +1,8 @@ (ns babashka.impl.socket-repl-test (:require - [babashka.impl.common :as common] [babashka.impl.server :refer [clojure-core-server-namespace]] [babashka.impl.socket-repl :refer [start-repl! stop-repl!]] + [babashka.main :as main] [babashka.process :as p] [babashka.test-utils :as tu] [babashka.wait :as w] @@ -15,115 +15,122 @@ (set! *warn-on-reflection* true) -(defn socket-command [expr expected] - (with-open [socket (java.net.Socket. "127.0.0.1" 1666) - reader (io/reader socket) - sw (java.io.StringWriter.) - writer (io/writer socket)] - (binding [*out* writer] - (println (str expr "\n"))) - (loop [] - (when-let [l (try (.readLine ^java.io.BufferedReader reader) - (catch java.net.SocketException _ nil))] - ;; (prn :l l) - (binding [*out* sw] - (println l)) - (let [s (str sw)] - ;; (prn :s s :expected expected (str/includes? s expected)) - (if (if (fn? expected) - (expected s) - (str/includes? s expected)) - (is true) - (recur))))) - (binding [*out* writer] - (println ":repl/quit\n")) - :success)) +(defn socket-command-on-port [^long port] + (fn [expr expected & [log?]] + (with-open [socket (java.net.Socket. "127.0.0.1" port) + reader (io/reader socket) + sw (java.io.StringWriter.) + writer (io/writer socket)] + (binding [*out* writer] + (println (str expr "\n"))) + (loop [] + (when-let [l (try (.readLine ^java.io.BufferedReader reader) + (catch java.net.SocketException _ nil))] + (when log? + (println "===" l)) + (binding [*out* sw] + (println l)) + (let [s (str sw)] + ;; (prn :s s :expected expected (str/includes? s expected)) + (if (if (fn? expected) + (expected s) + (str/includes? s expected)) + (is true) + (recur))))) + (binding [*out* writer] + (println ":repl/quit\n")) + :success))) (def server-process (volatile! nil)) (def exec? (System/getenv "BABASHKA_SOCKET_REPL_TEST")) -(deftest socket-repl-test +(deftest ^:flaky socket-repl-test (when exec? - (try - (if tu/jvm? - (let [ctx (init {:namespaces {'clojure.core.server clojure-core-server-namespace} - :features #{:bb}})] - (ctx-store/reset-ctx! ctx) - (start-repl! "0.0.0.0:1666" ctx)) - (do (vreset! server-process - (p/process ["./bb" "socket-repl" "localhost:1666"])) - (w/wait-for-port "localhost" 1666))) - (Thread/sleep 50) - (is (socket-command "(+ 1 2 3)" "user=> 6")) - (testing "&env" - (socket-command "(defmacro bindings [] (mapv #(list 'quote %) (keys &env)))" "bindings") - (socket-command "(defn bar [x y z] (bindings))" "bar") - (is (socket-command "(bar 1 2 3)" "[x y z]"))) - (testing "reader conditionals" - (is (socket-command "#?(:bb 1337 :clj 8888)" "1337"))) - (testing "*1, *2, *3, *e" - (is (socket-command "1\n*1" "1"))) - (testing "*ns*" - (is (socket-command "(ns foo.bar) (ns-name *ns*)" "foo.bar"))) - (finally + (let [socket-command (socket-command-on-port 1666)] + (try (if tu/jvm? - (do (stop-repl!) - (ctx-store/reset-ctx! nil) - (Thread/sleep 100)) - (p/destroy-tree @server-process)))))) + (let [ctx (init {:namespaces main/namespaces + :features #{:bb}})] + (ctx-store/reset-ctx! ctx) + (start-repl! "0.0.0.0:1666" ctx)) + (do (vreset! server-process + (p/process ["./bb" "socket-repl" "localhost:1666"])) + (w/wait-for-port "localhost" 1666))) + (Thread/sleep 50) + (is (socket-command "(+ 1 2 3)" "user=> 6")) + (testing "&env" + (socket-command "(defmacro bindings [] (mapv #(list 'quote %) (keys &env)))" "bindings") + (socket-command "(defn bar [x y z] (bindings))" "bar") + (is (socket-command "(bar 1 2 3)" "[x y z]"))) + (testing "reader conditionals" + (is (socket-command "#?(:bb 1337 :clj 8888)" "1337"))) + (testing "*1, *2, *3, *e" + (is (socket-command "1\n*1" "1"))) + (testing "*ns*" + (is (socket-command "(ns foo.bar) (ns-name *ns*)" "foo.bar"))) + (testing "set dyn vars" + (is (socket-command "[(set! *warn-on-reflection* true) (set! *unchecked-math* true)]" "[true true]"))) + (finally + (if tu/jvm? + (do (stop-repl!) + (ctx-store/reset-ctx! nil) + (Thread/sleep 100)) + (p/destroy-tree @server-process))))))) -(deftest socket-repl-opts-test +(deftest ^:flaky socket-repl-opts-test (when exec? - (try - (if tu/jvm? - (let [ctx (init {:bindings {'*command-line-args* - ["a" "b" "c"]} - :env (atom {}) - :namespaces {'clojure.core.server clojure-core-server-namespace} - :features #{:bb}})] - (ctx-store/reset-ctx! ctx) - (start-repl! "{:address \"localhost\" :accept clojure.core.server/repl :port 1666}" - ctx)) - (do (vreset! server-process - (p/process ["./bb" "--socket-repl" "{:address \"localhost\" :accept clojure.core.server/repl :port 1666}"])) - (w/wait-for-port "localhost" 1666))) - (Thread/sleep 50) - (is (socket-command "(+ 1 2 3)" "user=> 6")) - (finally - (if tu/jvm? - (do (stop-repl!) - (ctx-store/reset-ctx! nil) - (Thread/sleep 100)) - (p/destroy-tree @server-process)))))) + (let [socket-command (socket-command-on-port 1667)] + (try + (if tu/jvm? + (let [ctx (init {:bindings {'*command-line-args* + ["a" "b" "c"]} + :env (atom {}) + :namespaces {'clojure.core.server clojure-core-server-namespace} + :features #{:bb}})] + (ctx-store/reset-ctx! ctx) + (start-repl! "{:address \"localhost\" :accept clojure.core.server/repl :port 1667}" + ctx)) + (do (vreset! server-process + (p/process ["./bb" "--socket-repl" "{:address \"localhost\" :accept clojure.core.server/repl :port 1667}"])) + (w/wait-for-port "localhost" 1667))) + (Thread/sleep 50) + (is (socket-command "(+ 1 2 3)" "user=> 6")) + (finally + (if tu/jvm? + (do (stop-repl!) + (ctx-store/reset-ctx! nil) + (Thread/sleep 100)) + (p/destroy-tree @server-process))))))) -(deftest socket-prepl-test +(deftest ^:flaky socket-prepl-test (when exec? - (try - (if tu/jvm? - (let [ctx (init {:bindings {'*command-line-args* - ["a" "b" "c"]} - :env (atom {}) - :namespaces {'clojure.core.server clojure-core-server-namespace} - :features #{:bb}})] - (ctx-store/reset-ctx! ctx) - (start-repl! "{:address \"localhost\" :accept clojure.core.server/io-prepl :port 1666}" - ctx)) - (do (vreset! server-process - (p/process ["./bb" "--socket-repl" "{:address \"localhost\" :accept clojure.core.server/io-prepl :port 1666}"])) - (w/wait-for-port "localhost" 1666))) - (Thread/sleep 50) - (is (socket-command "(+ 1 2 3)" (fn [s] - (let [m (edn/read-string s)] - (and (= "6" (:val m)) - (= "user" (:ns m)) - (= "(+ 1 2 3)" (:form m))))))) - (finally - (if tu/jvm? - (do (stop-repl!) - (ctx-store/reset-ctx! nil) - (Thread/sleep 100)) - (p/destroy-tree @server-process)))))) + (let [socket-command (socket-command-on-port 1668)] + (try + (if tu/jvm? + (let [ctx (init {:bindings {'*command-line-args* + ["a" "b" "c"]} + :env (atom {}) + :namespaces {'clojure.core.server clojure-core-server-namespace} + :features #{:bb}})] + (ctx-store/reset-ctx! ctx) + (start-repl! "{:address \"localhost\" :accept clojure.core.server/io-prepl :port 1668}" + ctx)) + (do (vreset! server-process + (p/process ["./bb" "--socket-repl" "{:address \"localhost\" :accept clojure.core.server/io-prepl :port 1668}"])) + (w/wait-for-port "localhost" 1668))) + (Thread/sleep 50) + (is (socket-command "(+ 1 2 3)" (fn [s] + (let [m (edn/read-string s)] + (and (= "6" (:val m)) + (= "user" (:ns m)) + (= "(+ 1 2 3)" (:form m))))))) + (finally + (if tu/jvm? + (do (stop-repl!) + (ctx-store/reset-ctx! nil) + (Thread/sleep 100)) + (p/destroy-tree @server-process))))))) ;;;; Scratch @@ -138,5 +145,5 @@ '*command-line-args* ["a" "b" "c"]} :env (atom {})}) - (socket-command "(+ 1 2 3)" "6") + ((socket-command-on-port 1666) "(+ 1 2 3)" "6") ) diff --git a/test/babashka/interop_test.clj b/test/babashka/interop_test.clj new file mode 100644 index 00000000..0f537d86 --- /dev/null +++ b/test/babashka/interop_test.clj @@ -0,0 +1,57 @@ +(ns babashka.interop-test + (:require + [babashka.test-utils :as test-utils] + [clojure.edn :as edn] + [clojure.test :as test :refer [deftest is testing]])) + +(defn bb [input & args] + (test-utils/normalize + (edn/read-string + {:readers *data-readers* + :eof nil} + (apply test-utils/bb (when (some? input) (str input)) (map str args))))) + +(deftest vthreads-test + (testing "can invoke methods on java.lang.VirtualThread" + (is (= "" (bb nil "(set-agent-send-off-executor! (java.util.concurrent.Executors/newVirtualThreadPerTaskExecutor)) @(future (.getName (Thread/currentThread)))")))) + (is (= [false true] + (bb nil (pr-str '(do + (def t (Thread. (fn []))) + (def vt (Thread/startVirtualThread (fn []))) + [(.isVirtual t) (.isVirtual vt)])))))) + +(deftest domain-sockets-test + (is (= :success (bb nil (slurp "test-resources/domain_sockets.bb"))))) + +(deftest map-entry-create-test + (is (true? (bb nil "(= (first {1 2}) + (clojure.lang.MapEntry. 1 2) + (clojure.lang.MapEntry/create 1 2))")))) + +(deftest X509Certificate-test + (is (true? (bb nil "(import java.security.cert.X509Certificate) +(import java.security.cert.CertificateFactory) +(require '[clojure.java.io :as io]) +(defn x509-certificate + ^X509Certificate + [f] + (let [input (io/input-stream f) + factory (CertificateFactory/getInstance \"X.509\")] + (.generateCertificate factory input))) +(def cert (x509-certificate (io/file \"test-resources/certificate.crt\"))) +(some? (.getSubjectX500Principal cert)) +")))) + +(deftest IntStream-test + (is (pos? (bb nil "(.count (.codePoints \"woof🐕\"))")))) + +(deftest Thread-sleep-test + (is (bb nil "(Thread/sleep (/ 1 200)) + (Thread/sleep (/ 1 200) (/ 1 200)) + (Thread/sleep (java.time.Duration/ofMillis 1)) + true"))) + +(deftest SSL-test + (is (= :user/success + (bb nil "(try (.createSocket (javax.net.ssl.SSLSocketFactory/getDefault) \"localhost\" 4444) (catch java.net.ConnectException e ::success))"))) + (is (bb nil " (.startHandshake (.createSocket (javax.net.ssl.SSLSocketFactory/getDefault) \"clojure.org\" 443)) ::success"))) diff --git a/test/babashka/java_net_http_test.clj b/test/babashka/java_net_http_test.clj index bed2b2c9..dfa8b94f 100644 --- a/test/babashka/java_net_http_test.clj +++ b/test/babashka/java_net_http_test.clj @@ -62,7 +62,7 @@ ;; HttpClient options -(deftest authenticator-test +(deftest ^:flaky authenticator-test (is (= [401 200] (bb '(do @@ -89,7 +89,7 @@ auth-res (.send auth-client req handler)] [(.statusCode no-auth-res) (.statusCode auth-res)])))))) -(deftest cookie-test +(deftest ^:flaky cookie-test (is (= [] (bb '(do (ns net (:import [java.net CookieManager])) @@ -121,7 +121,7 @@ first (.getDomain)))))))) -(deftest connect-timeout-test +(deftest ^:flaky connect-timeout-test (is (str/includes? (bb '(do @@ -150,7 +150,7 @@ ;; can be either java.net.http.HttpConnectTimeoutException or java.net.http.HttpTimeoutException "TimeoutException"))) -(deftest executor-test +(deftest ^:flaky executor-test (is (= 200 (bb '(do @@ -172,7 +172,7 @@ res (.send client req (HttpResponse$BodyHandlers/discarding))] (.statusCode res))))))) -(deftest proxy-test +(deftest ^:flaky proxy-test (is (= true (bb '(do @@ -210,7 +210,7 @@ res (.send client req (HttpResponse$BodyHandlers/discarding))] (.statusCode res))))))) -(deftest redirect-test +(deftest ^:flaky redirect-test (let [redirect-prog (fn [redirect-kind] (str/replace (str '(do @@ -244,7 +244,7 @@ (println "Testing redirect never") (is (= 302 (bb (redirect-prog :never)))))) -(deftest ssl-context-test +(deftest ^:flaky ssl-context-test (let [result (bb '(do @@ -336,7 +336,7 @@ ;; HttpRequest -(deftest body-publishers-test +(deftest ^:flaky body-publishers-test (is (= true (bb '(do @@ -428,7 +428,7 @@ :data))))))))) -(deftest request-timeout-test +(deftest ^:flaky request-timeout-test (is (str/includes? (bb '(do @@ -455,7 +455,7 @@ name)))))) "TimeoutException"))) -(deftest body-handlers-test +(deftest ^:flaky body-handlers-test (is (= true (bb '(do diff --git a/test/babashka/java_time_test.clj b/test/babashka/java_time_test.clj index 90d8f704..a2b3ed9a 100644 --- a/test/babashka/java_time_test.clj +++ b/test/babashka/java_time_test.clj @@ -2,6 +2,7 @@ (:require [babashka.test-utils :as test-utils] [clojure.edn :as edn] + [clojure.string :as str] [clojure.test :as test :refer [deftest is]])) (defn bb [expr] @@ -17,6 +18,12 @@ (bb '(.format (java.time.LocalDateTime/parse "2019-12-18T16:01:41.485") (java.time.format.DateTimeFormatter/ofPattern "dd-MM-yyyy HH:mm:ss"))))) + + (let [out (bb '(.format (java.time.LocalDateTime/parse "2019-12-18T16:01:41.485") + (java.time.format.DateTimeFormatter/ofLocalizedDateTime java.time.format.FormatStyle/SHORT)))] + (is (and (str/includes? out "12") (str/includes? out "18")))) + + (is (number? (bb " (let [x (java.time.LocalDateTime/parse \"2019-12-18T16:01:41.485\") y (java.time.LocalDateTime/now)] diff --git a/test/babashka/logging_test.clj b/test/babashka/logging_test.clj index 7adb5e9f..18df2046 100644 --- a/test/babashka/logging_test.clj +++ b/test/babashka/logging_test.clj @@ -44,7 +44,7 @@ (deftest logging-test (let [res (tu/bb nil (pr-str program))] - (is (= 17 (count (re-seq #"\[dude:.\]" res)))) + (is (= 8 (count (re-seq #"\[dude:.\]" res)))) (is (= 6 (count (re-seq #"DEBUG" res)))) (is (= 11 (count (re-seq #"INFO" res))))) (testing "println appender works with with-out-str" @@ -110,3 +110,11 @@ (is (= 3 (count (re-seq #"\"test warn\"" res))))) (testing "lists are printed readably" (is (= 2 (count (re-seq #"\(\\a \\b\)" res))))))) + +(deftest timbre-log!-test + (is (str/includes? (tu/bb nil + (pr-str '(do (require '[taoensso.timbre :as timbre]) + (defn log-wrapper [& args] + (timbre/log! :info :p args)) + (log-wrapper "hallo")))) + "hallo"))) diff --git a/test/babashka/main_test.clj b/test/babashka/main_test.clj index 28102ba8..b6e62947 100644 --- a/test/babashka/main_test.clj +++ b/test/babashka/main_test.clj @@ -1,6 +1,7 @@ (ns babashka.main-test {:clj-kondo/config '{:linters {:unresolved-symbol {:exclude [working?]}}}} (:require + [babashka.fs :as fs] [babashka.main :as main] [babashka.test-utils :as test-utils] [clojure.edn :as edn] @@ -9,6 +10,7 @@ [clojure.string :as str] [clojure.test :as test :refer [deftest is testing]] [flatland.ordered.map :refer [ordered-map]] + [flatland.ordered.set :refer [ordered-set]] [sci.core :as sci])) (defn bb [input & args] @@ -31,11 +33,11 @@ (parse-opts ["--nrepl-server" "-cp" "src"]))) (is (= {:nrepl "1667", :classpath "src"} (parse-opts ["-cp" "src" "nrepl-server"]))) - (is (= {:socket-repl "1666", :expressions ["123"]} + (is (= {:prn true :socket-repl "1666", :expressions ["123"]} (parse-opts ["--socket-repl" "-e" "123"]))) - (is (= {:socket-repl "1666", :expressions ["123"]} + (is (= {:prn true :socket-repl "1666", :expressions ["123"]} (parse-opts ["--socket-repl" "1666" "-e" "123"]))) - (is (= {:nrepl "1666", :expressions ["123"]} + (is (= {:prn true :nrepl "1666", :expressions ["123"]} (parse-opts ["--nrepl-server" "1666" "-e" "123"]))) (is (= {:classpath "src" :uberjar "foo.jar"} @@ -50,7 +52,7 @@ (is (= 123 (bb nil "-e" "(println 123)"))) (is (= 123 (bb nil "--eval" "(println 123)"))) (testing "distinguish automatically between expression or file name" - (is (= {:result 8080} (bb nil "test/babashka/scripts/tools.cli.bb"))) + (is (= {:result 8080} (bb nil "--prn" "test/babashka/scripts/tools.cli.bb"))) (is (thrown-with-msg? Exception #"does not exist" (bb nil "foo.clj"))) (is (thrown-with-msg? Exception #"does not exist" (bb nil "-help")))) (is (= "1 2 3" (bb nil "-e" "(require '[clojure.string :as str1])" "-e" "(str1/join \" \" [1 2 3])"))) @@ -64,8 +66,16 @@ (is (= {:force? true :list-tasks true :command-line-args nil} (parse-opts ["--force" "tasks"]))) (is (= {:force? true :run "sometask" :command-line-args nil} (parse-opts ["--force" "run" "sometask"]))) (is (= {:force? true :repl true} (parse-opts ["--force" "repl"]))) - (is (= {:force? true :clojure true :command-line-args '("-M" "-r")} - (parse-opts ["--force" "clojure" "-M" "-r"])))) + (is (= {:force? true :clojure true :command-line-args '("-M" "-r")} + (parse-opts ["--force" "clojure" "-M" "-r"]))) + (testing "file opts parsing does not mess with :command-line-args" + (is (= {:prn true, :expressions ["(prn :foo)"]} + (-> (let [[_ opts] (main/parse-file-opt ["-e" "(prn :foo)"] {})] + (main/parse-opts ["-e" "(prn :foo)"] opts))))) + (is (= {:file "foo", :command-line-args ["README.md"]} + (main/parse-opts ["README.md"] {:file "foo"}))) + (is (= ["--version"] (bb nil (fs/file "test-resources" "script_with_overlapping_opts.clj") "--version"))) + (is (= ["version"] (bb nil (fs/file "test-resources" "script_with_overlapping_opts.clj") "version"))))) (deftest version-test (is (= [1 0 0] (main/parse-version "1.0.0-SNAPSHOT"))) @@ -138,7 +148,7 @@ (is (= "hello\n" (test-utils/bb nil "(println \"hello\")")))) (deftest System-test - (let [res (bb nil "-f" "test/babashka/scripts/System.bb")] + (let [res (bb nil "--prn" "-f" "test/babashka/scripts/System.bb")] (is (= "bar" (second res))) (doseq [s res] (is (not-empty s))))) @@ -147,7 +157,7 @@ (is (thrown-with-msg? Exception #"File does not exist: non-existing" (bb nil "-f" "non-existing")))) -(deftest ssl-test +(deftest ^:flaky ssl-test (let [resp (bb nil "(slurp \"https://www.google.com\")")] (is (re-find #"doctype html" resp)))) @@ -202,31 +212,31 @@ (deftest init-test (testing "init with a file" - (is (= "foo" (bb nil "--init" "test-resources/babashka/init_test.clj" - "-f" "test-resources/babashka/init_caller.clj")))) + (is (= "foo" (bb nil "--prn" "--init" "test-resources/babashka/init_test.clj" + "-f" "test-resources/babashka/init_caller.clj")))) (testing "init with eval(s)" (is (= "foo" (bb nil "--init" "test-resources/babashka/init_test.clj" - "-e" "(init-test/do-a-thing)")))) + "-e" "(init-test/do-a-thing)")))) (testing "init with main from init'ed ns" - (is (= "Hello from init!" (bb nil "--init" "test-resources/babashka/init_test.clj" - "-m" "init-test")))) + (is (= "Hello from init!" (bb nil "--prn" "--init" "test-resources/babashka/init_test.clj" + "-m" "init-test")))) (testing "init with main from another namespace" (test-utils/with-config '{:paths ["test-resources/babashka/src_for_classpath_test"]} - (is (= "foo" (bb nil "--init" "test-resources/babashka/init_test.clj" - "-m" "call-init-main"))))) + (is (= "foo" (bb nil "--prn" "--init" "test-resources/babashka/init_test.clj" + "-m" "call-init-main"))))) (testing "init with a qualified function passed to --main" (test-utils/with-config '{:paths ["test-resources/babashka/src_for_classpath_test"]} - (is (= "foobar" (bb nil "--init" "test-resources/babashka/init_test.clj" - "-m" "call-init-main/foobar"))))) + (is (= "foobar" (bb nil "--prn" "--init" "test-resources/babashka/init_test.clj" + "-m" "call-init-main/foobar"))))) (testing "init with a subcommand after it" (let [actual-output (test-utils/bb "(println (init-test/do-a-thing))" - "--init" "test-resources/babashka/init_test.clj" "repl")] + "--init" "test-resources/babashka/init_test.clj" "repl")] (is (str/includes? actual-output "foo\n"))) - (test-utils/with-config '{:tasks {thing (println (init-test/do-a-thing))}} ; make a task available + (test-utils/with-config '{:tasks {thing (println (init-test/do-a-thing))}} ; make a task available (let [actual-output (test-utils/bb nil "--init" "test-resources/babashka/init_test.clj" "tasks")] (is (every? #(str/includes? actual-output %) ["following tasks are available" "thing"]))))) (testing "init with a task name after it" - (test-utils/with-config '{:tasks {thing (println (init-test/do-a-thing))}} ; make a task available + (test-utils/with-config '{:tasks {thing (println (init-test/do-a-thing))}} ; make a task available (is (= "foo\n" (test-utils/bb nil "--init" "test-resources/babashka/init_test.clj" "thing")))))) (deftest preloads-test @@ -258,8 +268,8 @@ (deftest ^:windows-only win-pipe-test (when (and test-utils/native? main/windows?) (let [out (:out (sh "cmd" "/c" ".\\bb -O \"(repeat 50 \\\"dude\\\")\" |" - ".\\bb --stream \"(str *input* \\\"rino\\\")\" |" - ".\\bb -I \"(take 3 *input*)\"")) + ".\\bb --stream \"(str *input* \\\"rino\\\")\" |" + ".\\bb -I \"(take 3 *input*)\"")) out (edn/read-string out)] (is (= '("duderino" "duderino" "duderino") out))))) @@ -303,7 +313,7 @@ (deftest create-temp-file-test (is (= true - (bb nil "(let [tfile (File/createTempFile \"ctf\" \"tmp\")] + (bb nil "(let [tfile (File/createTempFile \"ctf\" \"tmp\")] (.deleteOnExit tfile) ; for cleanup (.exists tfile))")))) @@ -314,7 +324,7 @@ (is (= :timed-out (bb nil "(wait/wait-for-port \"127.0.0.1\" 1777 {:default :timed-out :timeout 50})")))) (let [edn (bb nil (io/file "test" "babashka" "scripts" "socket_server.bb"))] (is (= "127.0.0.1" (:host edn))) - (is (= 1777 (:port edn))) + (is (= 1777 (:port edn))) (is (number? (:took edn))))) (deftest ^:skip-windows wait-for-path-test @@ -341,7 +351,7 @@ temp-dir-path)))))) (deftest tools-cli-test - (is (= {:result 8080} (bb nil "test/babashka/scripts/tools.cli.bb")))) + (is (= {:result 8080} (bb nil "--prn" "test/babashka/scripts/tools.cli.bb")))) (deftest try-catch-test (is (zero? (bb nil "(try (/ 1 0) (catch ArithmeticException _ 0))"))) @@ -365,8 +375,8 @@ (deftest csv-test (is (= '(["Adult" "87727"] ["Elderly" "43914"] ["Child" "33411"] ["Adolescent" "29849"] - ["Infant" "15238"] ["Newborn" "10050"] ["In Utero" "1198"]) - (bb nil (.getPath (io/file "test" "babashka" "scripts" "csv.bb")))))) + ["Infant" "15238"] ["Newborn" "10050"] ["In Utero" "1198"]) + (bb nil "--prn" (.getPath (io/file "test" "babashka" "scripts" "csv.bb")))))) (deftest assert-test ;; assert was first implemented in bb but moved to sci later (is (thrown-with-msg? Exception #"should-be-true" @@ -389,14 +399,14 @@ (deftest binding-test (is (= (if main/windows? 7 6) - (bb nil "(def w (java.io.StringWriter.)) + (bb nil "(def w (java.io.StringWriter.)) (binding [clojure.core/*out* w] (println \"hello\")) (count (str w))")))) (deftest with-out-str-test (is (= (if main/windows? 7 6) - (bb nil "(count (with-out-str (println \"hello\")))")))) + (bb nil "(count (with-out-str (println \"hello\")))")))) (deftest with-in-str-test (is (= 5 (bb nil "(count (with-in-str \"hello\" (read-line)))")))) @@ -419,7 +429,7 @@ (java.nio.file.Files/copy p p' (into-array [java.nio.file.StandardCopyOption/REPLACE_EXISTING]))))))" temp-path)) (is (.exists f2)) - (let [v (bb nil "-f" (.getPath (io/file "test-resources" "babashka" "glob.clj")))] + (let [v (bb nil "--prn" "-f" (.getPath (io/file "test-resources" "babashka" "glob.clj")))] (is (vector? v)) (is (.exists (io/file (first v))))) (is (= :success (bb nil "(with-open [str (java.nio.file.Files/newDirectoryStream (.toPath (clojure.java.io/file \".\")))] :success)"))) @@ -432,7 +442,7 @@ (deftest future-print-test (testing "the root binding of sci/*out*" - (is (= "hello" (bb nil "@(future (prn \"hello\"))"))))) + (is (= "hello" (bb nil "@(future (prn \"hello\"))"))))) (deftest Math-test (is (== 8.0 (bb nil "(Math/pow 2 3)")))) @@ -477,16 +487,52 @@ (deftest clojure-data-xml-test (is (= "12" (bb nil "(let [xml (xml/parse-str \"12\")] (xml/emit-str xml))"))) - (is (= "0.0.87-SNAPSHOT" (bb nil "examples/pom_version_get.clj" (.getPath (io/file "test-resources" "pom.xml"))))) + (is (= "0.0.87-SNAPSHOT" (bb nil "--prn" "examples/pom_version_get.clj" (.getPath (io/file "test-resources" "pom.xml"))))) (is (= ":xmlns.DAV%3A/propfind" (bb nil "(clojure.data.xml/alias-uri :D \"DAV:\") (str ::D/propfind)")))) (deftest uberscript-test (let [tmp-file (java.io.File/createTempFile "uberscript" ".clj")] (.deleteOnExit tmp-file) + (.delete tmp-file) ; prevent overwrite failure (is (empty? (bb nil "--uberscript" (test-utils/escape-file-paths (.getPath tmp-file)) "-e" "(System/exit 1)"))) (is (= "(System/exit 1)" (slurp tmp-file))))) +(deftest uberscript-overwrite-test + (testing "trying to make uberscript overwrite a non-jar file fails" + (let [tmp-file (java.io.File/createTempFile "uberscript_overwrite" ".clj")] + (.deleteOnExit tmp-file) + (is (thrown-with-msg? Exception #"Overwrite prohibited." + (test-utils/bb nil "--uberscript" (test-utils/escape-file-paths (.getPath tmp-file)) "-e" "(println 123)")))))) + +(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" + (test-utils/bb nil "uberjar" path "-m" "my.main-main"))))))) + +(deftest target-file-overwrite-test + (test-utils/with-config {:paths ["test-resources/babashka/uberjar/src"]} + (testing "trying to make uberjar overwrite a non-empty jar file is allowed" + (let [tmp-file (java.io.File/createTempFile "uberjar_overwrite" ".jar") + path (.getPath tmp-file)] + (.deleteOnExit tmp-file) + (spit path "this isn't empty") + (test-utils/bb nil "--uberjar" (test-utils/escape-file-paths path) "-m" "my.main-main") + ; execute uberjar to confirm that the file is overwritten + (is (= "(\"42\")\n" (test-utils/bb nil "--prn" "--jar" (test-utils/escape-file-paths path) "42"))))) + (testing "trying to make uberjar overwrite a non-jar file is not allowed" + (let [tmp-file (java.io.File/createTempFile "oops_all_source" ".clj") + path (.getPath tmp-file)] + (.deleteOnExit tmp-file) + (is (thrown-with-msg? Exception #"Overwrite prohibited." + (test-utils/bb nil "--uberjar" (test-utils/escape-file-paths path) "-m" "my.main-main"))))))) + (deftest unrestricted-access (testing "babashka is allowed to mess with built-in vars" (is (= {} (bb nil " @@ -587,7 +633,8 @@ (is (= 2 (bb nil "(set! *data-readers* {'t/tag inc}) #t/tag 1")))) (deftest ordered-test - (is (= (ordered-map :a 1 :b 2) (bb nil "(flatland.ordered.map/ordered-map :a 1 :b 2)")))) + (is (= (ordered-map :a 1 :b 2) (bb nil "(flatland.ordered.map/ordered-map :a 1 :b 2)"))) + (is (= (ordered-set :a 1 :b 2) (bb nil "(flatland.ordered.set/ordered-set :a 1 :b 2)")))) (deftest data-diff-test (is (= [[nil 1] [nil 2] [1 nil 2]] (bb nil "(require '[clojure.data :as d]) (d/diff [1 1 2] [1 2 2])")))) @@ -617,13 +664,13 @@ (deftest file-property-test (is (= "true\nfalse\n" - (test-utils/bb nil (.getPath (io/file "test-resources" "babashka" "file_property1.clj"))))) + (test-utils/bb nil "--prn" (.getPath (io/file "test-resources" "babashka" "file_property1.clj"))))) (is (= "true\n" - (test-utils/bb nil (.getPath (io/file "test-resources" "babashka" "file_property2.clj"))))) + (test-utils/bb nil "--prn" (.getPath (io/file "test-resources" "babashka" "file_property2.clj"))))) (is (apply = - (bb nil (.getPath (io/file "test" "babashka" "scripts" "simple_file_var.bb"))))) - (let [res (bb nil (.getPath (io/file "test" ".." "test" "babashka" - "scripts" "simple_file_var.bb")))] + (bb nil "--prn" (.getPath (io/file "test" "babashka" "scripts" "simple_file_var.bb"))))) + (let [res (bb nil "--prn" (.getPath (io/file "test" ".." "test" "babashka" + "scripts" "simple_file_var.bb")))] (is (apply = res)) (is (str/includes? (first res) "..")))) @@ -744,8 +791,8 @@ true"))) (deftest ^:windows-only win-process-handler-info-test (when (and test-utils/native? main/windows?) (is (str/ends-with? - (bb nil "-e" "(.get (.command (.info (java.lang.ProcessHandle/current))))") - "bb.exe")))) + (bb nil "-e" "(.get (.command (.info (java.lang.ProcessHandle/current))))") + "bb.exe")))) (deftest interop-concurrency-test (is (= ["true" 3] (last (bb nil "-e" @@ -773,26 +820,26 @@ true"))) (is (<= 8 (bb nil '(count (apropos "first"))))) (is (= [1 2 3] (bb "[1 2 3]" "(pprint *input*)"))) (let [first-doc (test-utils/bb nil "(doc first)")] - (is (every? #(str/includes? first-doc %) ["---" "clojure.core/first" "first item"]))))) + (is (every? #(str/includes? first-doc %) ["---" "clojure.core/first" "first item"]))))) (deftest edn-input-test (testing "clojure's default readers" (is (= '(#inst "2021-08-24T00:56:02.014-00:00") - (bb "#inst \"2021-08-24T00:56:02.014-00:00\"" "-I" "(println *input*)"))) + (bb "#inst \"2021-08-24T00:56:02.014-00:00\"" "-I" "(println *input*)"))) (is (= '(#uuid "00000000-0000-0000-0000-000000000000") - (bb "#uuid \"00000000-0000-0000-0000-000000000000\"" "-I" "(println *input*)")))) + (bb "#uuid \"00000000-0000-0000-0000-000000000000\"" "-I" "(println *input*)")))) (testing "use tagged-literal as default data reader fn..." (testing "when using the -I option" (is (= "(#made-up-tag 42)\n" - (test-utils/normalize (test-utils/bb "#made-up-tag 42" "-I" "(println *input*)")))) + (test-utils/normalize (test-utils/bb "#made-up-tag 42" "-I" "(println *input*)")))) (is (= "(#abc 123 #cde 789)\n" - (test-utils/normalize (test-utils/bb "{:a #abc 123}{:a #cde 789}" "-I" "(map :a *input*)"))))) + (test-utils/normalize (test-utils/bb "{:a #abc 123}{:a #cde 789}" "-I" "(map :a *input*)"))))) (testing "when using --stream and -I" (is (= "#abc 123\n#cde 789\n" - (test-utils/normalize (test-utils/bb "{:a #abc 123}{:a #cde 789}" "--stream" "-I" "-e" "(println (:a *input*))"))))) + (test-utils/normalize (test-utils/bb "{:a #abc 123}{:a #cde 789}" "--stream" "-I" "-e" "(println (:a *input*))"))))) (testing "when using --stream (-I is sort of implied if no -i)" (is (= "#abc 123\n#cde 789\n" - (test-utils/normalize (test-utils/bb "{:a #abc 123}{:a #cde 789}" "--stream" "-e" "(println (:a *input*))"))))) + (test-utils/normalize (test-utils/bb "{:a #abc 123}{:a #cde 789}" "--stream" "-e" "(println (:a *input*))"))))) (testing "when reading one EDN form from stdin (no --stream or -I or -i)" (is (= "#abc 123\n" (test-utils/normalize (test-utils/bb "{:a #abc 123}{:a #cde 789}" "-e" "(println (:a *input*))"))))))) @@ -843,6 +890,17 @@ true"))) (deftest index-of-test (is (= 1 (bb nil "(.indexOf (map inc [1 2 3]) 3)")))) +(deftest get-watches-test + (is (true? (bb nil "(map? (.getWatches (doto (atom nil) (add-watch :foo (fn [k r o n])))))")))) + +(deftest tools-reader-test + (is (= :user/foo (bb nil "(require '[clojure.tools.reader :as r]) (r/read-string \"::foo\")"))) + (is (= :clojure.tools.reader/foo (bb nil "(require '[clojure.tools.reader :as r]) (r/read-string \"::r/foo\")"))) + (is (= [1 2 3] (bb nil " +(require '[clojure.tools.reader :as r]) +(binding [r/*default-data-reader-fn* (fn [sym] (fn [val] [1 2 3]))] +(r/read-string \"#dude []\"))")))) + ;;;; Scratch (comment diff --git a/test/babashka/namespace_test.clj b/test/babashka/namespace_test.clj index 7d8b1f5b..1e825b24 100644 --- a/test/babashka/namespace_test.clj +++ b/test/babashka/namespace_test.clj @@ -23,3 +23,9 @@ {:containing-ns ns-ns-name :ns-on-var var-ns-name :var-name var-symbol}))"))))) + +(deftest reload-existing-ns-test + (is (bb nil "(require '[clojure.java.io] :reload) true"))) + +(deftest file-not-found-exception-non-existing-ns-test + (is (= :user/dude (bb nil "(try (require '[foo-dude.bar]) (catch java.io.FileNotFoundException _ ::dude))")))) diff --git a/test/babashka/print_deps_test.clj b/test/babashka/print_deps_test.clj index 4d06b0d5..7471875e 100644 --- a/test/babashka/print_deps_test.clj +++ b/test/babashka/print_deps_test.clj @@ -2,16 +2,28 @@ (:require [babashka.deps :as deps] [babashka.fs :as fs] [babashka.test-utils :refer [bb]] + [borkdude.rewrite-edn :as r] [clojure.string :as str] [clojure.test :refer [deftest is testing]] + [rewrite-clj.node :as n] [sci.core :as sci])) + (deftest print-deps-test - (let [deps (bb nil "print-deps" "--format" "deps") - tmp-dir (fs/create-temp-dir)] + (let [deps (bb nil "print-deps" "--format" "deps")] (testing "printed deps map can be read by Clojure" - (spit (fs/file tmp-dir "deps.edn") deps) - (let [cp (sci/with-out-str - (deps/clojure ["-Spath"] {:dir (str tmp-dir)}))] - (is (str/includes? cp "babashka.curl"))) - (fs/delete-tree tmp-dir)))) + (let [tmp-dir (fs/create-temp-dir)] + (spit (fs/file tmp-dir "deps.edn") deps) + (let [cp (sci/with-out-str + (deps/clojure ["-Spath"] {:dir (str tmp-dir)}))] + (is (str/includes? cp "babashka.curl"))) + (fs/delete-tree tmp-dir))) + + (testing "keys in dep map are sorted" + (let [values-sorted? (fn [xs] (= xs (sort xs))) + deps-edn-str-deps-keys (fn [s] + (->> (r/get (r/parse-string s) :deps) + n/child-sexprs + (partition 2) + (map first)))] + (is (values-sorted? (deps-edn-str-deps-keys deps))))))) diff --git a/test/babashka/sci_test.clj b/test/babashka/sci_test.clj new file mode 100644 index 00000000..477fe493 --- /dev/null +++ b/test/babashka/sci_test.clj @@ -0,0 +1,14 @@ +(ns babashka.sci-test + (:require + [babashka.test-utils :as tu] + [clojure.edn :as edn] + [clojure.test :as t :refer [deftest is]])) + +(deftest sci-test + (is (= 1 (edn/read-string + (tu/bb nil "-e" " +(ns foo) +(require '[sci.core :as sci]) +(def x 1) +(def ctx (sci/init {:namespaces {'foo (sci/copy-ns foo (sci/create-ns 'foo))}})) +(sci/eval-string* ctx \"foo/x\")"))))) diff --git a/test/babashka/test_test.clj b/test/babashka/test_test.clj index b0ad6da5..0f4c6cdc 100644 --- a/test/babashka/test_test.clj +++ b/test/babashka/test_test.clj @@ -37,8 +37,12 @@ (deftest fixtures-test (let [output (bb " (require '[clojure.test :as t]) -(defn once [f] (prn :once-before) (f) (prn :once-after)) -(defn each [f] (prn :each-before) (f) (prn :each-after)) +(defn once [f] (prn :once-before) (f) +(prn :once-after) +(prn (some? (::t/once-fixtures (meta *ns*))))) + +(defn each [f] (prn :each-before) (f) (prn :each-after) +(prn (some? (::t/each-fixtures (meta *ns*))))) (t/use-fixtures :once once) (t/use-fixtures :each each) (t/deftest foo) @@ -48,9 +52,12 @@ :once-before :each-before :each-after +true :each-before :each-after -:once-after"))))) +true +:once-after +true"))))) (deftest with-test (let [output (bb " diff --git a/test/babashka/test_utils.clj b/test/babashka/test_utils.clj index 9a856a96..b2b440eb 100644 --- a/test/babashka/test_utils.clj +++ b/test/babashka/test_utils.clj @@ -5,7 +5,6 @@ [babashka.impl.common :as common] [babashka.main :as main] [babashka.process :as p] - [clojure.edn :as edn] [clojure.string :as str] [clojure.test :as test :refer [*report-counters*]] [clojure.tools.reader.reader-types :as r] @@ -45,7 +44,7 @@ (System/exit 1))))) (defn bb-jvm [input-or-opts & args] - (reset! cp/cp-state nil) + (alter-var-root #'cp/the-url-loader (constantly (delay (cp/new-loader [])))) (reset! main/env {}) (vreset! common/bb-edn nil) (System/clearProperty "babashka.config") diff --git a/test/babashka/uberjar_test.clj b/test/babashka/uberjar_test.clj index 4476f45a..c8ad8c21 100644 --- a/test/babashka/uberjar_test.clj +++ b/test/babashka/uberjar_test.clj @@ -2,8 +2,10 @@ (:require [babashka.fs :as fs] [babashka.main :as main] + [babashka.process :refer [shell]] [babashka.test-utils :as tu] [clojure.edn :as edn] + [clojure.java.io :as io] [clojure.string :as str] [clojure.test :as t :refer [deftest is testing]]) (:import (java.io File InputStreamReader PushbackReader) @@ -77,15 +79,16 @@ InputStreamReader. PushbackReader. edn/read)] (is (= #{:pods} (-> bb-edn keys set))) (is (= (:pods config) (:pods bb-edn)))) - (is (str/includes? (tu/bb nil "--jar" path) "3"))))))) + (is (str/includes? (tu/bb nil "--prn" "--jar" 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"))))))) +(deftest uberjar-as-binary-test + (when tu/native? + (let [tmp-file (java.io.File/createTempFile "uber" ".jar") + path (.getPath tmp-file) + bin-file (if (fs/windows?) "my-binary.exe" "my-binary")] + (.deleteOnExit tmp-file) + (.deleteOnExit (io/file bin-file)) + (tu/bb nil "--classpath" "test-resources/babashka/uberjar/src" "uberjar" path "-m" "my.main-main") + (shell {:out bin-file} "cat" "./bb" path) + (.setExecutable (io/file bin-file) true) + (is (str/includes? (:out (shell {:out :string} (str (io/file "." bin-file)) "1 2 3 4")) "1 2 3 4"))))) diff --git a/test/babashka/uberscript_test.clj b/test/babashka/uberscript_test.clj index 9a111b18..66376038 100644 --- a/test/babashka/uberscript_test.clj +++ b/test/babashka/uberscript_test.clj @@ -1,35 +1,37 @@ (ns babashka.uberscript-test (:require [babashka.test-utils :as tu] - [clojure.test :as t :refer [deftest is]] - [clojure.string :as str])) + [clojure.string :as str] + [clojure.test :as t :refer [deftest is]])) + +(defn deleted-temp-file [] + (doto (java.io.File/createTempFile "uberscript" ".clj") + .deleteOnExit + .delete)) ; delete file to prevent overwrite failure (deftest basic-test - (let [tmp-file (java.io.File/createTempFile "uberscript" ".clj")] - (.deleteOnExit tmp-file) - (is (empty? (tu/bb nil "--classpath" "test-resources/babashka/src_for_classpath_test" "uberscript" (.getPath tmp-file) "-m" "my.main"))) + (let [tmp-file (deleted-temp-file)] + (is (empty? (tu/bb nil "--prn" "--classpath" "test-resources/babashka/src_for_classpath_test" "uberscript" (.getPath tmp-file) "-m" "my.main"))) (is (= "(\"1\" \"2\" \"3\" \"4\")\n" - (tu/bb nil "--file" (.getPath tmp-file) "1" "2" "3" "4"))))) + (tu/bb nil "--prn" "--file" (.getPath tmp-file) "1" "2" "3" "4"))))) (when-not (= "aarch64" (System/getenv "BABASHKA_ARCH")) (deftest advanced-test - (let [tmp-file (java.io.File/createTempFile "uberscript" ".clj")] - (.deleteOnExit tmp-file) + (let [tmp-file (deleted-temp-file)] ;; we test: ;; order of namespaces ;; reader error for ::a/foo is swallowed ;; pod namespaces can be loaded without a problem ;; resulting program can be executed - (is (empty? (tu/bb nil "--classpath" "test-resources/babashka/uberscript/src" "uberscript" (.getPath tmp-file) "-m" "my.main"))) + (is (empty? (tu/bb nil "--prn" "--classpath" "test-resources/babashka/uberscript/src" "uberscript" (.getPath tmp-file) "-m" "my.main"))) (is (= ":clojure.string/foo\ntrue\n(\"1\" \"2\" \"3\" \"4\")\n" - (tu/bb nil "--file" (.getPath tmp-file) "1" "2" "3" "4")))))) + (tu/bb nil "--prn" "--file" (.getPath tmp-file) "1" "2" "3" "4")))))) (deftest pods-test - (let [tmp-file (java.io.File/createTempFile "uberscript" ".clj")] - (.deleteOnExit tmp-file) + (let [tmp-file (deleted-temp-file)] (tu/with-config (pr-str '{:paths ["test-resources/babashka/uberscript/src"] :pods {org.babashka/go-sqlite3 {:version "0.1.0"}}}) - (is (empty? (tu/bb nil "uberscript" (.getPath tmp-file) "-m" "my.main-pod"))) + (is (empty? (tu/bb nil "--prn" "uberscript" (.getPath tmp-file) "-m" "my.main-pod"))) (is (= 1 (count (re-seq #"load-pod 'org.babashka/go-sqlite3" (str/join (str/split-lines (slurp tmp-file)))))))) - (is (str/includes? (tu/bb nil "--file" (.getPath tmp-file)) "3")))) + (is (str/includes? (tu/bb nil "--prn" "--file" (.getPath tmp-file)) "3")))) diff --git a/test/babashka/xml_test.clj b/test/babashka/xml_test.clj index b3fde386..7624688d 100644 --- a/test/babashka/xml_test.clj +++ b/test/babashka/xml_test.clj @@ -1,7 +1,7 @@ (ns babashka.xml-test (:require [babashka.test-utils :as test-utils] [clojure.string :as str] - [clojure.test :refer [deftest is testing]])) + [clojure.test :refer [deftest is]])) (def simple-xml-str "data") @@ -15,3 +15,22 @@ (deftest xml-data-readers-test (is (str/includes? (test-utils/bb nil round-trip-prog) simple-xml-str))) + +(deftest virtual-threads-bug-test + (is (str/starts-with? (test-utils/bb nil "(require '[clojure.core.async] + '[clojure.data.xml]) + +(def go-blocks (atom [])) + +(dotimes [_ 100] + (swap! go-blocks conj (clojure.core.async/go (clojure.data.xml/parse + (java.io.ByteArrayInputStream. + (.getBytes \"\" \"UTF-8\")) + :namespace-aware false + :skip-whitespace true)))) + +(doseq [block @go-blocks] + (clojure.core.async/