diff --git a/.circleci/config.yml b/.circleci/config.yml index ad653a42..53356bc5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -76,7 +76,7 @@ jobs: working_directory: ~/repo environment: LEIN_ROOT: "true" - GRAALVM_HOME: /home/circleci/graalvm-ce-java11-21.0.0 + GRAALVM_HOME: /home/circleci/graalvm-ce-java11-21.1.0 BABASHKA_PLATFORM: linux # used in release script BABASHKA_TEST_ENV: native BABASHKA_XMX: "-J-Xmx6500m" @@ -110,9 +110,9 @@ jobs: name: Download GraalVM command: | cd ~ - if ! [ -d graalvm-ce-java11-21.0.0 ]; then - curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0/graalvm-ce-java11-linux-amd64-21.0.0.tar.gz - tar xzf graalvm-ce-java11-linux-amd64-21.0.0.tar.gz + if ! [ -d graalvm-ce-java11-21.1.0 ]; then + curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-linux-amd64-21.1.0.tar.gz + tar xzf graalvm-ce-java11-linux-amd64-21.1.0.tar.gz fi - run: name: Build binary @@ -136,7 +136,7 @@ jobs: - save_cache: paths: - ~/.m2 - - ~/graalvm-ce-java11-21.0.0 + - ~/graalvm-ce-java11-21.1.0 key: linux-{{ checksum "project.clj" }}-{{ checksum ".circleci/config.yml" }} - store_artifacts: path: /tmp/release @@ -151,7 +151,7 @@ jobs: working_directory: ~/repo environment: LEIN_ROOT: "true" - GRAALVM_HOME: /home/circleci/graalvm-ce-java11-21.0.0 + GRAALVM_HOME: /home/circleci/graalvm-ce-java11-21.1.0 BABASHKA_PLATFORM: linux # used in release script BABASHKA_TEST_ENV: native BABASHKA_STATIC: "true" @@ -198,9 +198,9 @@ jobs: name: Download GraalVM command: | cd ~ - if ! [ -d graalvm-ce-java11-21.0.0 ]; then - curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0/graalvm-ce-java11-linux-amd64-21.0.0.tar.gz - tar xzf graalvm-ce-java11-linux-amd64-21.0.0.tar.gz + if ! [ -d graalvm-ce-java11-21.1.0 ]; then + curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-linux-amd64-21.1.0.tar.gz + tar xzf graalvm-ce-java11-linux-amd64-21.1.0.tar.gz fi - run: name: Build binary @@ -224,7 +224,7 @@ jobs: - save_cache: paths: - ~/.m2 - - ~/graalvm-ce-java11-21.0.0 + - ~/graalvm-ce-java11-21.1.0 key: linux-{{ checksum "project.clj" }}-{{ checksum ".circleci/config.yml" }} - store_artifacts: path: /tmp/release @@ -241,7 +241,7 @@ jobs: working_directory: ~/repo environment: LEIN_ROOT: "true" - GRAALVM_HOME: /home/circleci/graalvm-ce-java11-21.0.0 + GRAALVM_HOME: /home/circleci/graalvm-ce-java11-21.1.0 BABASHKA_PLATFORM: linux # used in release script BABASHKA_ARCH: aarch64 BABASHKA_TEST_ENV: native @@ -287,9 +287,9 @@ jobs: name: Download GraalVM command: | cd ~ - if ! [ -d graalvm-ce-java11-21.0.0 ]; then - curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0/graalvm-ce-java11-linux-aarch64-21.0.0.tar.gz - tar xzf graalvm-ce-java11-linux-aarch64-21.0.0.tar.gz + if ! [ -d graalvm-ce-java11-21.1.0 ]; then + curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-linux-aarch64-21.1.0.tar.gz + tar xzf graalvm-ce-java11-linux-aarch64-21.1.0.tar.gz fi - run: name: Build binary @@ -313,7 +313,7 @@ jobs: - save_cache: paths: - ~/.m2 - - ~/graalvm-ce-java11-21.0.0 + - ~/graalvm-ce-java11-21.1.0 key: linux-aarch64-{{ checksum "project.clj" }}-{{ checksum ".circleci/config.yml" }} - store_artifacts: path: /tmp/release @@ -330,7 +330,7 @@ jobs: working_directory: ~/repo environment: LEIN_ROOT: "true" - GRAALVM_HOME: /home/circleci/graalvm-ce-java11-21.0.0 + GRAALVM_HOME: /home/circleci/graalvm-ce-java11-21.1.0 BABASHKA_PLATFORM: linux # used in release script BABASHKA_ARCH: aarch64 BABASHKA_TEST_ENV: native @@ -378,9 +378,9 @@ jobs: name: Download GraalVM command: | cd ~ - if ! [ -d graalvm-ce-java11-21.0.0 ]; then - curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0/graalvm-ce-java11-linux-aarch64-21.0.0.tar.gz - tar xzf graalvm-ce-java11-linux-aarch64-21.0.0.tar.gz + if ! [ -d graalvm-ce-java11-21.1.0 ]; then + curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-linux-aarch64-21.1.0.tar.gz + tar xzf graalvm-ce-java11-linux-aarch64-21.1.0.tar.gz fi - run: name: Build binary @@ -404,7 +404,7 @@ jobs: - save_cache: paths: - ~/.m2 - - ~/graalvm-ce-java11-21.0.0 + - ~/graalvm-ce-java11-21.1.0 key: linux-aarch64-{{ checksum "project.clj" }}-{{ checksum ".circleci/config.yml" }} - store_artifacts: path: /tmp/release @@ -418,7 +418,7 @@ jobs: xcode: "12.0.0" environment: MACOSX_DEPLOYMENT_TARGET: 10.13 # 10.12 is EOL - GRAALVM_HOME: /Users/distiller/graalvm-ce-java11-21.0.0/Contents/Home + GRAALVM_HOME: /Users/distiller/graalvm-ce-java11-21.1.0/Contents/Home BABASHKA_PLATFORM: macos # used in release script BABASHKA_TEST_ENV: native BABASHKA_XMX: "-J-Xmx6500m" @@ -446,9 +446,9 @@ jobs: command: | cd ~ ls -la - if ! [ -d graalvm-ce-java11-21.0.0 ]; then - curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0/graalvm-ce-java11-darwin-amd64-21.0.0.tar.gz - tar xzf graalvm-ce-java11-darwin-amd64-21.0.0.tar.gz + if ! [ -d graalvm-ce-java11-21.1.0 ]; then + curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-darwin-amd64-21.1.0.tar.gz + tar xzf graalvm-ce-java11-darwin-amd64-21.1.0.tar.gz fi - run: name: Build binary @@ -470,7 +470,7 @@ jobs: - save_cache: paths: - ~/.m2 - - ~/graalvm-ce-java11-21.0.0 + - ~/graalvm-ce-java11-21.1.0 key: mac-{{ checksum "project.clj" }}-{{ checksum ".circleci/config.yml" }} - store_artifacts: path: /tmp/release diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 52cef161..c93c1ac4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,17 +45,17 @@ jobs: uses: actions/cache@v1 id: cache-graalvm with: - path: ~/graalvm-ce-java11-21.0.0 - key: ${{ runner.os }}-graalvm-21.0.0 + path: ~/graalvm-ce-java11-21.1.0 + key: ${{ runner.os }}-graalvm-21.1.0 restore-keys: | - ${{ runner.os }}-graalvm-21.0.0 + ${{ runner.os }}-graalvm-21.1.0 - name: Download GraalVM run: | cd ~ - if ! [ -d graalvm-ce-java11-21.0.0 ]; then - curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0/graalvm-ce-java11-linux-amd64-21.0.0.tar.gz - tar xzf graalvm-ce-java11-linux-amd64-21.0.0.tar.gz + if ! [ -d graalvm-ce-java11-21.1.0 ]; then + curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-linux-amd64-21.1.0.tar.gz + tar xzf graalvm-ce-java11-linux-amd64-21.1.0.tar.gz fi - name: Fetch deps @@ -65,18 +65,18 @@ jobs: - name: Run tests run: | - export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.0.0" + export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.1.0" script/test - name: Test libraries run: | - export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.0.0" + export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.1.0" sudo script/install-clojure script/run_lib_tests - name: Build uberjar run: | - export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.0.0" + export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.1.0" script/uberjar - name: Babashka version @@ -133,17 +133,17 @@ jobs: uses: actions/cache@v1 id: cache-graalvm with: - path: ~/graalvm-ce-java11-21.0.0 - key: ${{ runner.os }}-graalvm-21.0.0 + path: ~/graalvm-ce-java11-21.1.0 + key: ${{ runner.os }}-graalvm-21.1.0 restore-keys: | - ${{ runner.os }}-graalvm-21.0.0 + ${{ runner.os }}-graalvm-21.1.0 - name: Download GraalVM run: | cd ~ - if ! [ -d graalvm-ce-java11-21.0.0 ]; then - curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0/graalvm-ce-java11-linux-amd64-21.0.0.tar.gz - tar xzf graalvm-ce-java11-linux-amd64-21.0.0.tar.gz + if ! [ -d graalvm-ce-java11-21.1.0 ]; then + curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-linux-amd64-21.1.0.tar.gz + tar xzf graalvm-ce-java11-linux-amd64-21.1.0.tar.gz fi - name: Babashka version @@ -156,13 +156,13 @@ jobs: run: | export BABASHKA_JAR=babashka-${{ steps.babashka-version.outputs.version }}-standalone.jar export BABASHKA_XMX="-J-Xmx6g" - export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.0.0" + export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.1.0" cp babashka-${{ steps.babashka-version.outputs.version }}-reflection.json reflection.json script/compile - name: Test binary run: | - export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.0.0" + export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.1.0" BABASHKA_TEST_ENV=native script/test - name: Install clojure @@ -171,7 +171,7 @@ jobs: - name: Test libraries run: | - export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.0.0" + export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.1.0" BABASHKA_TEST_ENV=native script/run_lib_tests - name: Upload artifact @@ -214,17 +214,17 @@ jobs: uses: actions/cache@v1 id: cache-graalvm with: - path: ~/graalvm-ce-java11-21.0.0 - key: ${{ runner.os }}-graalvm-21.0.0 + path: ~/graalvm-ce-java11-21.1.0 + key: ${{ runner.os }}-graalvm-21.1.0 restore-keys: | - ${{ runner.os }}-graalvm-21.0.0 + ${{ runner.os }}-graalvm-21.1.0 - name: Download GraalVM run: | cd ~ - if ! [ -d graalvm-ce-java11-21.0.0 ]; then - curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0/graalvm-ce-java11-linux-amd64-21.0.0.tar.gz - tar xzf graalvm-ce-java11-linux-amd64-21.0.0.tar.gz + if ! [ -d graalvm-ce-java11-21.1.0 ]; then + curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-linux-amd64-21.1.0.tar.gz + tar xzf graalvm-ce-java11-linux-amd64-21.1.0.tar.gz fi - name: Babashka version @@ -237,7 +237,7 @@ jobs: run: | export BABASHKA_JAR=babashka-${{ steps.babashka-version.outputs.version }}-standalone.jar export BABASHKA_XMX="-J-Xmx6g" - export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.0.0" + export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.1.0" export BABASHKA_STATIC=true cp babashka-${{ steps.babashka-version.outputs.version }}-reflection.json reflection.json script/compile @@ -245,7 +245,7 @@ jobs: - name: Test binary run: | ./bb '(+ 1 2 3)' - export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.0.0" + export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.1.0" BABASHKA_TEST_ENV=native script/test - name: Install clojure @@ -254,7 +254,7 @@ jobs: - name: Test libraries run: | - export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.0.0" + export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.1.0" BABASHKA_TEST_ENV=native script/run_lib_tests - name: Upload artifact @@ -288,17 +288,17 @@ jobs: uses: actions/cache@v1 id: cache-graalvm with: - path: ~/graalvm-ce-java11-21.0.0 - key: ${{ runner.os }}-graalvm-21.0.0 + path: ~/graalvm-ce-java11-21.1.0 + key: ${{ runner.os }}-graalvm-21.1.0 restore-keys: | - ${{ runner.os }}-graalvm-21.0.0 + ${{ runner.os }}-graalvm-21.1.0 - name: Download GraalVM run: | cd ~ - if ! [ -d graalvm-ce-java11-21.0.0 ]; then - curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0/graalvm-ce-java11-darwin-amd64-21.0.0.tar.gz - tar xzf graalvm-ce-java11-darwin-amd64-21.0.0.tar.gz + if ! [ -d graalvm-ce-java11-21.1.0 ]; then + curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-darwin-amd64-21.1.0.tar.gz + tar xzf graalvm-ce-java11-darwin-amd64-21.1.0.tar.gz fi - name: Babashka version @@ -311,19 +311,19 @@ jobs: run: | export BABASHKA_JAR=babashka-${{ steps.babashka-version.outputs.version }}-standalone.jar export BABASHKA_XMX="-J-Xmx6g" - export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.0.0/Contents/Home" + export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.1.0/Contents/Home" cp babashka-${{ steps.babashka-version.outputs.version }}-reflection.json reflection.json script/compile - name: Test binary run: | - export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.0.0/Contents/Home" + export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.1.0/Contents/Home" sudo script/install-leiningen BABASHKA_TEST_ENV=native script/test - name: Test libraries run: | - export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.0.0/Contents/Home" + export GRAALVM_HOME="$HOME/graalvm-ce-java11-21.1.0/Contents/Home" sudo script/install-clojure BABASHKA_TEST_ENV=native script/run_lib_tests diff --git a/CHANGELOG.md b/CHANGELOG.md index 37945b19..0a5bcf6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,44 @@ For a list of breaking changes, check [here](#breaking-changes). +## 0.4.6 + +- Upgrade to GraalVM 21.1, fixes [#884](https://github.com/babashka/babashka/issues/884) + +## 0.4.5 + +Babashka proper: + +- Add `java.net.InetSocketAddress` +- Add support for slingshot [#675](https://github.com/babashka/babashka/issues/675) +- Add STM facilities (`dosync`, `ref`, etc.) +- Fix `.wait`, `.notify` interop on arbitrary classes + +Deps.clj (used for resolving deps and `clojure` invocations): + +- Fix JVM option parsing [#46](https://github.com/borkdude/deps.clj/issues/46) + +Sci: various minor performance improvements. + +## 0.4.4 + +Babashka proper: + +- Print ex-data in error report [#730](https://github.com/babashka/babashka/issues/730) ([@GreshamDanielStephens](https://github.com/GreshamDanielStephens), [@rng-dynamics](https://github.com/rng-dynamics)) +- Tasks: support dynamic vars [#865](https://github.com/babashka/babashka/issues/865) +- Tasks: use stable namespace when using `run` [#865](https://github.com/babashka/babashka/issues/865) +- Add `java.lang.ProcessHandle$Info` [#872](https://github.com/babashka/babashka/issues/872) +- Add `java.util.Optional` [#872](https://github.com/babashka/babashka/issues/872) +- Add `java.lang.StackTraceElement` (to gain compatibility with libraries such as [omniconf](https://github.com/grammarly/omniconf)) + +Babashka.nrepl: + +- Error reporting improvement [#40](https://github.com/babashka/babashka.nrepl/issues/865) + +Sci: + +- Support trailing metadata in `defn` + ## 0.4.3 - Add `cognitect.transit/tagged-value`, needed for sql pods diff --git a/Dockerfile b/Dockerfile index ba680a5a..a56bbafc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,13 +4,13 @@ ENV DEBIAN_FRONTEND=noninteractive RUN apt update RUN apt install --no-install-recommends -yy curl unzip build-essential zlib1g-dev sudo WORKDIR "/opt" -RUN curl -sLO https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0/graalvm-ce-java11-linux-amd64-21.0.0.tar.gz -RUN tar -xzf graalvm-ce-java11-linux-amd64-21.0.0.tar.gz +RUN curl -sLO https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-linux-amd64-21.1.0.tar.gz +RUN tar -xzf graalvm-ce-java11-linux-amd64-21.1.0.tar.gz ARG BABASHKA_XMX="-J-Xmx4500m" -ENV GRAALVM_HOME="/opt/graalvm-ce-java11-21.0.0" -ENV JAVA_HOME="/opt/graalvm-ce-java11-21.0.0/bin" +ENV GRAALVM_HOME="/opt/graalvm-ce-java11-21.1.0" +ENV JAVA_HOME="/opt/graalvm-ce-java11-21.1.0/bin" ENV PATH="$JAVA_HOME:$PATH" ENV BABASHKA_XMX=$BABASHKA_XMX diff --git a/appveyor.yml b/appveyor.yml index 3fb7e31e..b5e535ed 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,8 @@ image: Visual Studio 2017 clone_folder: C:\projects\babashka environment: - GRAALVM_HOME: C:\projects\babashka\graalvm\graalvm-ce-java11-21.0.0 + GRAALVM_HOME: C:\projects\babashka\graalvm\graalvm-ce-java11-21.1.0 + JAVA_HOME: C:\projects\babashka\graalvm\graalvm-ce-java11-21.1.0 BABASHKA_XMX: "-J-Xmx5g" cache: @@ -30,24 +31,26 @@ clone_script: - cmd: git submodule update --init --recursive build_script: +- cmd: >- + powershell -Command "if (Test-Path('graalvm')) { return } else { (New-Object Net.WebClient).DownloadFile('https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-windows-amd64-21.1.0.zip', 'graalvm.zip') }" + + powershell -Command "if (Test-Path('graalvm')) { return } else { Expand-Archive graalvm.zip graalvm }" + - cmd: >- powershell -Command "(New-Object Net.WebClient).DownloadFile('https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein.bat', 'lein.bat')" call lein self-install -# set CLJ_KONDO_TEST_ENV=jvm +- cmd: >- + set BABASHKA_TEST_ENV=jvm -# call script/test.bat + call script/test.bat # see https://github.com/quarkusio/quarkus/pull/7663 - cmd: >- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" - powershell -Command "if (Test-Path('graalvm')) { return } else { (New-Object Net.WebClient).DownloadFile('https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0/graalvm-ce-java11-windows-amd64-21.0.0.zip', 'graalvm.zip') }" - - powershell -Command "if (Test-Path('graalvm')) { return } else { Expand-Archive graalvm.zip graalvm }" - call script/uberjar.bat call script/compile.bat @@ -62,12 +65,9 @@ build_script: bb release-artifact %zip% -# - cmd: >- -# lein clean + set BABASHKA_TEST_ENV=native -# set CLJ_KONDO_TEST_ENV=native - -# call script/test.bat + call script/test.bat artifacts: - path: babashka-*-windows-amd64.zip diff --git a/babashka.nrepl b/babashka.nrepl index d9b63bba..bd1ce0cb 160000 --- a/babashka.nrepl +++ b/babashka.nrepl @@ -1 +1 @@ -Subproject commit d9b63bbadce04799ef791c02a7066dd3197aa1b9 +Subproject commit bd1ce0cbc0861350a4a36e9c31ef79fa0c7cff84 diff --git a/deps.clj b/deps.clj index f59e735a..520b6b05 160000 --- a/deps.clj +++ b/deps.clj @@ -1 +1 @@ -Subproject commit f59e735a585d73e9f800a7e824e2afa550092caf +Subproject commit 520b6b053b7bdfe46990ab82220a2d13f79f9772 diff --git a/deps.edn b/deps.edn index 6d3bca04..ec064874 100644 --- a/deps.edn +++ b/deps.edn @@ -78,7 +78,8 @@ gaka/gaka {:mvn/version "0.3.0"} failjure/failjure {:mvn/version "2.1.1"} io.helins/binf {:mvn/version "1.1.0-beta0"} - rm-hull/jasentaa {:mvn/version "0.2.5"}} + rm-hull/jasentaa {:mvn/version "0.2.5"} + slingshot/slingshot {:mvn/version "0.12.2"}} :classpath-overrides {org.clojure/clojure nil org.clojure/spec.alpha nil org.clojure/core.specs.alpha nil}} diff --git a/doc/build.md b/doc/build.md index 78a2da8f..c27082d5 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-21.0.0*. +- Download [GraalVM](https://www.graalvm.org/downloads/). Currently we use *java11-21.1.0*. - 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-21.0.0/Contents/Home + export GRAALVM_HOME=~/Downloads/graalvm-ce-java11-21.1.0/Contents/Home ``` On linux: ``` shell - export GRAALVM_HOME=~/Downloads/graalvm-ce-java11-21.0.0 + export GRAALVM_HOME=~/Downloads/graalvm-ce-java11-21.1.0 ``` 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-21.0.0 + set GRAALVM_HOME=%USERPROFILE%\Downloads\graalvm-ce-java11-21.1.0 ``` If you are not running from the x64 Native Tools Command Prompt, you will need to set additional environment variables using: ``` diff --git a/doc/dev.md b/doc/dev.md index 1c5f3c6b..d9e4e15c 100644 --- a/doc/dev.md +++ b/doc/dev.md @@ -1,6 +1,6 @@ # Developing Babashka -You need [lein](https://leiningen.org/) for running JVM tests and/or producing uberjars. For building binaries you need GraalVM. Currently we use java11-21.0.0. +You need [lein](https://leiningen.org/) for running JVM tests and/or producing uberjars. For building binaries you need GraalVM. Currently we use java11-21.1.0. ## Clone repository @@ -114,6 +114,8 @@ Some of these design decisions were formed in [these discussions](https://github Keep notes here about how adding libraries and classes to Babashka affects the binary size. We're registering the size of the macOS binary (as built on CircleCI). +2021/06/13 Upgrading from GraalvM 21.0 to 21.1 added roughly 3mb. Issue [here](https://github.com/oracle/graal/issues/3280#issuecomment-846402115). + 2020/10/30 Without httpkit client+server: 68113436. With: 69503316 = 1390kb added. 2020/05/01 Removed `next.jdbc` and postgres JDBC driver: 48304980 diff --git a/doc/projects.md b/doc/projects.md index 4194eef8..41737362 100644 --- a/doc/projects.md +++ b/doc/projects.md @@ -39,6 +39,7 @@ The following libraries and projects are known to work with babashka. - [binf](#binf) - [rewrite-edn](#rewrite-edn) - [expound](#expound) + - [omniconf](#omniconf) - [Pods](#pods) - [Projects](#projects-1) - [babashka-test-action](#babashka-test-action) @@ -60,6 +61,7 @@ The following libraries and projects are known to work with babashka. - [interdep](#interdep) - [sha-words](#sha-words) - [adam-james-v/scripts](#adam-james-vscripts) + - [oidc-client](#oidc-client) Also keep an eye on the [news](news.md) page for new projects, gists and other developments around babashka. @@ -544,6 +546,44 @@ Example: (expound/expound ::a [1 2]) ``` +### [omniconf](https://github.com/grammarly/omniconf) + +script.clj: +``` clojure +#!/usr/bin/env bb + +(ns script + (:require [babashka.deps :as deps])) + +(deps/add-deps + '{:deps {com.grammarly/omniconf {:mvn/version "0.4.3"}}}) + +(require '[omniconf.core :as cfg]) +(cfg/define {:foo {}}) +(cfg/populate-from-env) +(cfg/get :foo) +``` + +``` text +FOO=1 script.clj +Populating Omniconf from env: 1 value(s) +"1" +``` + +### [slingshot](https://github.com/scgilardi/slingshot) + +Enhanced try and throw for Clojure leveraging Clojure's capabilities. + +``` clojure +$ export BABASHKA_CLASSPATH=$(clojure -Spath -Sdeps '{:deps {slingshot/slingshot {:mvn/version "0.12.2"}}}') +$ bb -e "(require '[slingshot.slingshot :as s]) (s/try+ (s/throw+ {:type ::foo}) (catch [:type ::foo] [] 1))" +1 +``` + +NOTE: slingshot's tests pass with babashka except one: catching a record types +by name. This is due to a difference in how records are implemented in +babashka. This may be fixed later if this turns out to be really useful. + ## Pods [Babashka pods](https://github.com/babashka/babashka.pods) are programs that can @@ -649,3 +689,9 @@ A clojure program to turn a sha hash into list of nouns in a predictable jar. ### [adam-james-v/scripts](https://github.com/adam-james-v/scripts) A collection of useful scripts. Mainly written with Clojure/babashka + +### [oidc-client](https://gist.github.com/holyjak/ad4e1e9b863f8ed57ef0cb6ac6b30494) + +Tired of being forced to use the browser every time you need to refresh an OIDC token to authenticate with a backend service? Finally there is a CLI tool for that - the babashka and Docker powered oidc_client.clj. + +Upon first invocation it opens up a browser for the OIDC provider login, thereafter it caches the refresh token and uses it as long as it remains valid. diff --git a/examples/README.md b/examples/README.md index 7211218c..37553d7a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -300,6 +300,8 @@ $ examples/which.clj rg A script to retrieve the version from a `pom.xml` file. See [pom_version_get.clj](pom_version_get.clj). Written by [@wilkerlucio](https://github.com/wilkerlucio). +See [pom_version_get_xml_zip.clj](pom_version_get_xml_zip.clj) for how to do the same using zippers. + Also see [pom_version_set.clj](pom_version_set.clj) to set the pom version. ## Whatsapp frequencies diff --git a/examples/pom_version_get_xml_zip.clj b/examples/pom_version_get_xml_zip.clj new file mode 100644 index 00000000..9caa1a9e --- /dev/null +++ b/examples/pom_version_get_xml_zip.clj @@ -0,0 +1,15 @@ +(require '[babashka.deps :as deps]) + +(deps/add-deps '{:deps {org.clojure/data.zip {:mvn/version "RELEASE"}}}) + +(require '[clojure.data.xml :as xml] + '[clojure.data.zip.xml :as xmlz] + '[clojure.zip :as zip]) + +(def xml "1.0.0") + +(-> xml + xml/parse-str + zip/xml-zip + (xmlz/xml1-> :pom :version zip/down zip/node)) +;; => 1.0.0 diff --git a/fs b/fs index 05d39293..1beb91cd 160000 --- a/fs +++ b/fs @@ -1 +1 @@ -Subproject commit 05d392933a4e6fe0e0c3fa002c1bdeeccc995801 +Subproject commit 1beb91cd5f352d1e3a914c94e7ec25a2644b07e5 diff --git a/install b/install index 14520a2f..e21e5a11 100755 --- a/install +++ b/install @@ -7,8 +7,7 @@ checksum="" static_binary="false" default_install_dir="/usr/local/bin" install_dir="$default_install_dir" -default_download_dir="/tmp" -download_dir="$default_download_dir" +download_dir="" print_help() { echo "Installs latest (or specific) version of babashka. Installation directory defaults to /usr/local/bin." @@ -18,7 +17,7 @@ print_help() { echo -e echo "Defaults:" echo " * Installation directory: ${default_install_dir}" - echo " * Download directory: ${default_download_dir}" + echo " * Download directory: temporary" if [[ -z "$checksum" ]]; then echo " * Checksum: no" else @@ -29,10 +28,6 @@ print_help() { exit 1 } -if [[ $# -eq 1 ]]; then - install_dir=${1:-} -fi - while [[ $# -gt 0 ]] do key="$1" @@ -68,6 +63,11 @@ do esac done +if [[ -z "$download_dir" ]]; then + download_dir="$(mktemp -d)" + trap 'rm -rf "$download_dir"' EXIT +fi + if [[ "$checksum" != "" ]] && [[ "$version" == "" ]]; then >&2 echo "Options --checksum and --version should be provided together!" exit 1 @@ -78,14 +78,14 @@ if [[ "$version" == "" ]]; then fi case "$(uname -s)" in - Linux*) platform=linux;; - Darwin*) platform=macos;; + Linux*) platform=linux;; + Darwin*) platform=macos;; esac case "$(uname -m)" in - aarch64) arch=aarch64;; + aarch64) arch=aarch64;; + *) arch=amd64;; esac -arch=${arch:-amd64} # Ugly ugly conversion of version to a comparable number IFS='.' read -ra VER <<< "$version" @@ -99,40 +99,42 @@ else util="$(which tar) -zxf" fi -if [[ "$static_binary" == "true" ]]; then - if [[ "$platform" != "linux" ]]; then - >&2 echo "Static binaries are only available in Linux platform!" - exit 1 - fi - filename="babashka-$version-$platform-$arch-static."$ext -else - filename="babashka-$version-$platform-$arch."$ext -fi +case "$platform-$static_binary" in + linux-true) filename="babashka-$version-$platform-$arch-static."$ext + ;; + *-true) >&2 echo "Static binaries are only available in Linux platform! Using the non-static one..." + filename="babashka-$version-$platform-$arch."$ext + ;; + *) filename="babashka-$version-$platform-$arch."$ext + ;; +esac + download_url="https://github.com/babashka/babashka/releases/download/v$version/$filename" -mkdir -p "$download_dir" -cd "$download_dir" -echo -e "Downloading $download_url to $download_dir" +# Running this part in a subshell so when it finishes we go back to the previous directory +mkdir -p "$download_dir" && ( + cd "$download_dir" + echo -e "Downloading $download_url to $download_dir" -rm -rf "$filename" -rm -rf "bb" -curl -o "$filename" -sL "$download_url" -if [[ -n "$checksum" ]]; then - if ! echo "$checksum $filename" | sha256sum --check --status; then - >&2 echo "Failed checksum on $filename" - >&2 echo "Got: $(sha256sum "$filename" | cut -d' ' -f1)" - >&2 echo "Expected: $checksum" - exit 1 + curl -o "$filename" -sL "$download_url" + if [[ -n "$checksum" ]]; then + if ! echo "$checksum *$filename" | shasum -a 256 --check --status; then + >&2 echo "Failed checksum on $filename" + >&2 echo "Got: $(shasum -a 256 "$filename" | cut -d' ' -f1)" + >&2 echo "Expected: $checksum" + exit 1 + fi fi -fi -$util "$filename" -rm "$filename" + $util "$filename" + rm -f "$filename" +) if [[ "$download_dir" != "$install_dir" ]] then mkdir -p "$install_dir" if [ -f "$install_dir/bb" ]; then echo "Moving $install_dir/bb to $install_dir/bb.old" + mv -f "$install_dir/bb" "$install_dir/bb.old" fi mv -f "$download_dir/bb" "$install_dir/bb" fi diff --git a/process b/process index 3559a706..4c6699d0 160000 --- a/process +++ b/process @@ -1 +1 @@ -Subproject commit 3559a70686e435504b1bed320d2e513c5bf2bb15 +Subproject commit 4c6699d06b49773d3e5c5b4c11d3334fb78cc996 diff --git a/project.clj b/project.clj index a3ac4c96..1d23e566 100644 --- a/project.clj +++ b/project.clj @@ -13,6 +13,7 @@ ;; for debugging Reflector.java code: ;; :java-source-paths ["sci/reflector/src-java"] :resource-paths ["resources" "sci/resources"] + :test-selectors {:windows :windows} :dependencies [[org.clojure/clojure "1.11.0-alpha1"] [borkdude/edamame "0.0.11"] [borkdude/graal.locking "0.0.2"] @@ -83,7 +84,7 @@ "-Dclojure.spec.skip-macros=true" "-Dborkdude.dynaload.aot=true"] :main babashka.main - :aot :all} + :aot [babashka.main]} :reflection {:main babashka.impl.classes/generate-reflection-file}} :aliases {"bb" ["with-profile" "test" "run" "-m" "babashka.main"]} :deploy-repositories [["clojars" {:url "https://clojars.org/repo" diff --git a/resources/BABASHKA_RELEASED_VERSION b/resources/BABASHKA_RELEASED_VERSION index 70d5b25f..c0a1ac19 100644 --- a/resources/BABASHKA_RELEASED_VERSION +++ b/resources/BABASHKA_RELEASED_VERSION @@ -1 +1 @@ -0.4.3 \ No newline at end of file +0.4.6 \ No newline at end of file diff --git a/resources/BABASHKA_VERSION b/resources/BABASHKA_VERSION index 86e5b12e..5b151cef 100644 --- a/resources/BABASHKA_VERSION +++ b/resources/BABASHKA_VERSION @@ -1 +1 @@ -0.4.4-SNAPSHOT \ No newline at end of file +0.4.7-SNAPSHOT \ No newline at end of file diff --git a/sci b/sci index 4de7c780..79376504 160000 --- a/sci +++ b/sci @@ -1 +1 @@ -Subproject commit 4de7c78024bfdb5c52e273be372144d46228939a +Subproject commit 7937650453b7ba9eb9ee43ea30b333b5950dc21e diff --git a/script/bump_graal_version.clj b/script/bump_graal_version.clj index e448d42f..786d415a 100755 --- a/script/bump_graal_version.clj +++ b/script/bump_graal_version.clj @@ -51,9 +51,9 @@ ;; OR ;; ;; We could have them as environment variables -(def current-graal-version "20.2.0") +(def current-graal-version "21.0.0") (def current-java-version "java11") -(def valid-graal-bumps ["19.3.2" "20.1.0" "20.2.0" "20.3.0" "21.0.0"]) +(def valid-graal-bumps ["19.3.2" "20.1.0" "20.2.0" "20.3.0" "21.0.0" "21.1.0"]) (def valid-java-bumps ["java8" "java11"]) (def cl-options diff --git a/script/setup-musl b/script/setup-musl index bc5d90ed..118c98ec 100755 --- a/script/setup-musl +++ b/script/setup-musl @@ -47,16 +47,10 @@ arch=${BABASHKA_ARCH:-"x86_64"} echo "ARCH: $arch" cd "zlib-${ZLIB_VERSION}" -CC=musl-gcc ./configure --static --prefix=/usr/lib/$arch-linux-musl/ +CC=musl-gcc ./configure --static --prefix="/usr/local" make CC=musl-gcc -sudo make install -export CC=gcc +make install cd .. -# depending on GCC version, we will have different directories here. -# for example, for GCC 6.3.0 we will have: -# - /usr/lib/gcc/x86_64-linux-gnu/6 -# - /usr/lib/gcc/x86_64-linux-gnu/6.3.0 -for dest_dir in /usr/lib/gcc/$arch-linux-gnu/*; do - sudo cp -f /usr/lib/$arch-linux-musl/lib/libz.a "$dest_dir" -done +# Install libz.a in the correct place so ldd can find it +install -Dm644 "/usr/local/lib/libz.a" "/usr/lib/$arch-linux-musl/libz.a" diff --git a/script/test.bat b/script/test.bat new file mode 100755 index 00000000..ba69e4ec --- /dev/null +++ b/script/test.bat @@ -0,0 +1,11 @@ +if "%GRAALVM_HOME%"=="" ( +echo Please set GRAALVM_HOME +exit /b +) + +echo "BABASHKA_TEST_ENV: %BABASHKA_TEST_ENV%" + +set JAVA_HOME=%GRAALVM_HOME% +set PATH=%GRAALVM_HOME%\bin;%PATH% + +call lein do clean, test :windows diff --git a/script/uberjar b/script/uberjar index 8a2bb9a6..7974b935 100755 --- a/script/uberjar +++ b/script/uberjar @@ -15,7 +15,6 @@ then # Remove all the default features, unless explicitly set to true: export BABASHKA_FEATURE_XML="${BABASHKA_FEATURE_XML:-false}" export BABASHKA_FEATURE_YAML="${BABASHKA_FEATURE_YAML:-false}" - export BABASHKA_FEATURE_CORE_ASYNC="${BABASHKA_FEATURE_CORE_ASYNC:-false}" export BABASHKA_FEATURE_CSV="${BABASHKA_FEATURE_CSV:-false}" export BABAHSKA_FEATURE_TRANSIT="${BABAHSKA_FEATURE_TRANSIT:-false}" export BABASHKA_FEATURE_JAVA_TIME="${BABASHKA_FEATURE_JAVA_TIME:-false}" diff --git a/src/babashka/impl/classes.clj b/src/babashka/impl/classes.clj index a148b8f9..fae96345 100644 --- a/src/babashka/impl/classes.clj +++ b/src/babashka/impl/classes.clj @@ -71,7 +71,8 @@ {:name "toString"} {:name "toURI"}]} java.util.Arrays - {:methods [{:name "copyOf"}]} + {:methods [{:name "copyOf"} + {:name "copyOfRange"}]} ;; this fixes clojure.lang.Reflector for Java 11 java.lang.reflect.AccessibleObject {:methods [{:name "canAccess"}]}} @@ -132,11 +133,13 @@ java.lang.Object java.lang.Process java.lang.ProcessHandle + java.lang.ProcessHandle$Info java.lang.ProcessBuilder java.lang.ProcessBuilder$Redirect java.lang.Runtime java.lang.RuntimeException java.lang.Short + java.lang.StackTraceElement java.lang.String java.lang.StringBuilder java.lang.System @@ -150,6 +153,7 @@ java.net.DatagramPacket java.net.HttpURLConnection java.net.InetAddress + java.net.InetSocketAddress java.net.ServerSocket java.net.Socket java.net.SocketException @@ -232,6 +236,7 @@ java.time.temporal.Temporal java.time.temporal.TemporalAccessor java.time.temporal.TemporalAdjuster]) + java.util.concurrent.ExecutionException java.util.concurrent.LinkedBlockingQueue java.util.jar.JarFile java.util.jar.JarEntry @@ -247,6 +252,7 @@ java.util.Locale java.util.Map java.util.MissingResourceException + java.util.Optional java.util.Properties java.util.Set java.util.UUID @@ -344,6 +350,8 @@ java.lang.Process (instance? java.lang.ProcessHandle v) java.lang.ProcessHandle + (instance? java.lang.ProcessHandle$Info v) + java.lang.ProcessHandle$Info ;; added for calling .put on .environment from ProcessBuilder (instance? java.util.Map v) java.util.Map @@ -398,10 +406,14 @@ :let [class-name (str c)]] {:name class-name :allPublicFields true})) + instance-checks (vec (for [c (sort (:instance-checks classes)) + :let [class-name (str c)]] + ;; don't include any methods + {:name class-name})) custom-entries (for [[c v] (:custom classes) :let [class-name (str c)]] (assoc v :name class-name)) - all-entries (concat entries constructors methods fields custom-entries)] + all-entries (concat entries constructors methods fields instance-checks custom-entries)] all-entries)) (defn generate-reflection-file diff --git a/src/babashka/impl/clojure/core.clj b/src/babashka/impl/clojure/core.clj index a04cb381..a7183ad6 100644 --- a/src/babashka/impl/clojure/core.clj +++ b/src/babashka/impl/clojure/core.clj @@ -1,7 +1,7 @@ (ns babashka.impl.clojure.core {:no-doc true} (:refer-clojure :exclude [future read+string clojure-version with-precision - send-via send send-off]) + send-via send send-off sync]) (:require [babashka.impl.common :as common] [borkdude.graal.locking :as locking] [clojure.core :as c] @@ -121,6 +121,22 @@ ;;;; End agents +;;;; STM + +(defn -run-in-transaction [f] + (clojure.lang.LockingTransaction/runInTransaction f)) + +(defmacro sync + "transaction-flags => TBD, pass nil for now + Runs the exprs (in an implicit do) in a transaction that encompasses + exprs and any nested calls. Starts a transaction if none is already + running on this thread. Any uncaught exception will abort the + transaction and flow out of sync. The exprs may be run more than + once, but any effects on Refs will be atomic." + {:added "1.0"} + [_flags-ignored-for-now & body] + `(clojure.core/-run-in-transaction (fn [] ~@body))) + (def core-extras {;; agents 'agent (copy-core-var agent) @@ -161,6 +177,14 @@ '*math-context* math-context 'with-precision (sci/copy-var with-precision clojure-core-ns) '-with-precision (sci/copy-var -with-precision clojure-core-ns) + ;; STM + 'alter (sci/copy-var alter clojure-core-ns) + 'commute (sci/copy-var commute clojure-core-ns) + 'dosync (sci/copy-var dosync clojure-core-ns) + '-run-in-transaction (sci/copy-var -run-in-transaction clojure-core-ns) + 'sync (sci/copy-var sync clojure-core-ns) + 'ref (sci/copy-var ref clojure-core-ns) + 'ref-set (sci/copy-var ref-set clojure-core-ns) ;;'*clojure-version* clojure-version-var ;;'clojure-version (sci/copy-var clojure-version clojure-core-ns) } diff --git a/src/babashka/impl/error_handler.clj b/src/babashka/impl/error_handler.clj index d0ad4750..e941c8ef 100644 --- a/src/babashka/impl/error_handler.clj +++ b/src/babashka/impl/error_handler.clj @@ -100,6 +100,9 @@ (.. e getClass getName))) (when-let [m (.getMessage e)] (println (str "Message: " m))) + (when-let [d (ex-data (.getCause e))] + (print (str "Data: ")) + (prn d)) (let [{:keys [:file :line :column]} d] (when line (println (str "Location: " diff --git a/src/babashka/impl/tasks.clj b/src/babashka/impl/tasks.clj index a21a18c8..2ffbf5cc 100644 --- a/src/babashka/impl/tasks.clj +++ b/src/babashka/impl/tasks.clj @@ -20,7 +20,7 @@ (def log-level (sci/new-dynamic-var '*-log-level* default-log-level {:ns sci-ns})) ;; (def task-name (sci/new-dynamic-var '*-task-name* nil {:ns sci-ns})) (def task (sci/new-dynamic-var '*task* nil {:ns sci-ns})) -(def current-task (sci/new-dynamic-var 'current-task (fn [] @task) {:ns sci-ns})) +(def current-task (sci/new-var 'current-task (fn [] @task) {:ns sci-ns})) (def state (sci/new-var 'state (atom {}) {:ns sci-ns})) (defn log-info [& strs] @@ -31,15 +31,6 @@ (binding [*out* *err*] (println (format "[bb %s]" (:name @task)) (str/join " " strs)))))) -#_(defn log-error [& strs] - (let [log-level @log-level] - (when (or - ;; log error also in case of info level - (identical? :info log-level) - (identical? :error log-level)) - (binding [*out* *err*] - (println (format "[bb %s]" (:name @task)) (str/join " " strs)))))) - (defn- handle-non-zero [proc opts] (when proc (when-let [proc (deref proc)] @@ -167,17 +158,17 @@ (format " (let [chans (filter babashka.tasks/-chan? %s)] (loop [cs chans] - (let [[v p] (clojure.core.async/alts!! cs) - [task-name v] v - cs (filterv #(not= p %%) cs) - ;; _ (.println System/err (str \"n: \" task-name \" v: \" v)) - ;; check for existence of v, as the channel may already have been consumed once - _ (when v (intern *ns* (symbol task-name) v))] - (when (instance? Throwable v) - (throw (ex-info (ex-message v) - {:babashka/exit 1 - :data (ex-data v)}))) - (when (seq cs) + (when (seq cs) + (let [[v p] (clojure.core.async/alts!! cs) + [task-name v] v + cs (filterv #(not= p %%) cs) + ;; _ (.println System/err (str \"n: \" task-name \" v: \" v)) + ;; check for existence of v, as the channel may already have been consumed once + _ (when v (intern *ns* (symbol task-name) v))] + (when (instance? Throwable v) + (throw (ex-info (ex-message v) + {:babashka/exit 1 + :data (ex-data v)}))) (recur cs)))))" deps) "") #_(format "(def %s (babashka.tasks/-wait %s))" dep dep)) @@ -234,6 +225,8 @@ prog (wrap-def task-map prog parallel? last?)] prog))))) +(def rand-ns (delay (symbol (str "user-" (java.util.UUID/randomUUID))))) + (defn format-task [init extra-paths extra-deps requires prog] (format " %s ;; extra-paths @@ -264,11 +257,11 @@ (if (seq extra-deps) (format "(babashka.deps/add-deps '%s)" (pr-str {:deps extra-deps})) "") - (gensym "user") + @rand-ns (if (seq requires) (format "(:require %s)" (str/join " " requires)) "") - (str init) + (pr-str init) prog)) (defn target-order @@ -303,75 +296,70 @@ enter (:enter tasks) leave (:leave tasks) task (get tasks task-name)] - (if task - (let [m? (map? task) - requires (get tasks :requires) - init (get tasks :init) - prog (if-let [depends (when m? (:depends task))] - (let [[targets error] - (try [(target-order tasks task-name)] - (catch clojure.lang.ExceptionInfo e - [nil (ex-message e)])) - #_#_dependees (tasks->dependees targets tasks) - task-map (cond-> {} - enter (assoc :enter enter) - leave (assoc :leave leave) - parallel? (assoc :parallel parallel?))] - (if error - [(binding [*out* *err*] - (println error)) 1] - (loop [prog "" - targets (seq targets) - done [] - extra-paths [] - extra-deps nil - requires requires] - (let [t (first targets) - targets (next targets) - #_#_ depends-on-t (get dependees t) - task-map (cond-> - (assoc task-map - :name t - #_#_:started done) - #_#_targets (assoc :pending (vec targets)) - #_#_depends-on-t (assoc :dependents depends-on-t))] - (if targets - (if-let [task (get tasks t)] - (recur (str prog "\n" (assemble-task-1 task-map task parallel?)) - targets - (conj done t) - (concat extra-paths (:extra-paths task)) - (merge extra-deps (:extra-deps task)) - (concat requires (:requires task))) - [(binding [*out* *err*] - (println "No such task:" t)) 1]) - (if-let [task (get tasks t)] - (let [prog (str prog "\n" - #_(wait-tasks depends) #_(apply str (map deref-task depends)) - "\n" - (assemble-task-1 task-map task parallel? true)) - extra-paths (concat extra-paths (:extra-paths task)) - extra-deps (merge extra-deps (:extra-deps task)) - requires (concat requires (:requires task))] - [[(format-task init extra-paths extra-deps requires prog)] nil]) - [(binding [*out* *err*] - (println "No such task:" t)) 1])))))) - [[(format-task - init - (:extra-paths task) - (:extra-deps task) - (concat requires (:requires task)) - (assemble-task-1 (cond-> {:name task-name} - enter (assoc :enter enter) - leave (assoc :leave leave) - parallel? (assoc :parallel parallel?)) - task parallel? true))] nil])] - (when @debug - (binding [*out* *err*] - (println (ffirst prog)))) - prog) - [(binding [*out* *err*] - (println "No such task:" task-name)) 1]))) + (binding [*print-meta* true] + (if task + (let [m? (map? task) + requires (get tasks :requires) + init (get tasks :init) + prog (if (when m? (:depends task)) + (let [[targets error] + (try [(target-order tasks task-name)] + (catch clojure.lang.ExceptionInfo e + [nil (ex-message e)])) + task-map (cond-> {} + enter (assoc :enter enter) + leave (assoc :leave leave) + parallel? (assoc :parallel parallel?))] + (if error + [(binding [*out* *err*] + (println error)) 1] + (loop [prog "" + targets (seq targets) + done [] + extra-paths [] + extra-deps nil + requires requires] + (let [t (first targets) + targets (next targets) + task-map (assoc task-map + :name t)] + (if targets + (if-let [task (get tasks t)] + (recur (str prog "\n" (assemble-task-1 task-map task parallel?)) + targets + (conj done t) + (concat extra-paths (:extra-paths task)) + (merge extra-deps (:extra-deps task)) + (concat requires (:requires task))) + [(binding [*out* *err*] + (println "No such task:" t)) 1]) + (if-let [task (get tasks t)] + (let [prog (str prog "\n" + #_(wait-tasks depends) #_(apply str (map deref-task depends)) + "\n" + (assemble-task-1 task-map task parallel? true)) + extra-paths (concat extra-paths (:extra-paths task)) + extra-deps (merge extra-deps (:extra-deps task)) + requires (concat requires (:requires task))] + [[(format-task init extra-paths extra-deps requires prog)] nil]) + [(binding [*out* *err*] + (println "No such task:" t)) 1])))))) + [[(format-task + init + (:extra-paths task) + (:extra-deps task) + (concat requires (:requires task)) + (assemble-task-1 (cond-> {:name task-name} + enter (assoc :enter enter) + leave (assoc :leave leave) + parallel? (assoc :parallel parallel?)) + task parallel? true))] nil])] + (when @debug + (binding [*out* *err*] + (println (ffirst prog)))) + prog) + [(binding [*out* *err*] + (println "No such task:" task-name)) 1])))) (defn doc-from-task [sci-ctx tasks task] (or (:doc task) diff --git a/src/babashka/main.clj b/src/babashka/main.clj index 8ec4f1df..3ee0551d 100644 --- a/src/babashka/main.clj +++ b/src/babashka/main.clj @@ -45,6 +45,7 @@ [sci.core :as sci] [sci.impl.namespaces :as sci-namespaces] [sci.impl.unrestrict :refer [*unrestricted*]] + [sci.impl.utils :refer [ctx-fn]] [sci.impl.vars :as vars]) (:gen-class)) @@ -309,9 +310,15 @@ Use bb run --help to show this help output. 'start-server (fn [& args] (apply server/start-server @common/ctx args))}) +(def input-var (sci/new-dynamic-var '*input*)) + (def namespaces (cond-> - {'clojure.tools.cli tools-cli-namespace + {'user {'*input* (ctx-fn + (fn [_ctx] + (force @input-var)) + nil)} + 'clojure.tools.cli tools-cli-namespace 'clojure.java.shell shell-namespace 'babashka.wait {'wait-for-port wait/wait-for-port 'wait-for-path wait/wait-for-path} @@ -428,13 +435,13 @@ Use bb run --help to show this help output. Process java.lang.Process ProcessBuilder java.lang.ProcessBuilder Short java.lang.Short + StackTraceElement java.lang.StackTraceElement String java.lang.String StringBuilder java.lang.StringBuilder System java.lang.System Thread java.lang.Thread Throwable java.lang.Throwable}) -(def input-var (sci/new-dynamic-var '*input* nil)) (def edn-readers (cond-> {} features/yaml? (assoc 'ordered/map @(resolve 'flatland.ordered.map/ordered-map)))) @@ -717,10 +724,7 @@ Use bb run --help to show this help output. :namespaces (-> namespaces (assoc 'clojure.core (assoc core-extras - 'load-file load-file*)) - (assoc-in ['user (with-meta '*input* - (when-not stream? - {:sci.impl/deref! true}))] input-var)) + 'load-file load-file*))) :env env :features #{:bb :clj} :classes classes/class-map diff --git a/test-resources/lib_tests/babashka/run_all_libtests.clj b/test-resources/lib_tests/babashka/run_all_libtests.clj index cc4e3c70..e7dd18d3 100644 --- a/test-resources/lib_tests/babashka/run_all_libtests.clj +++ b/test-resources/lib_tests/babashka/run_all_libtests.clj @@ -212,6 +212,12 @@ 'honey.sql.helpers-test 'honey.sql.postgres-test) +(test-namespaces 'slingshot.slingshot-test + 'slingshot.support-test + ;; TODO: + ;; 'slingshot.test-test + ) + ;;;; final exit code (let [{:keys [:test :fail :error] :as m} @status] diff --git a/test-resources/lib_tests/slingshot/slingshot_test.clj b/test-resources/lib_tests/slingshot/slingshot_test.clj new file mode 100644 index 00000000..09938332 --- /dev/null +++ b/test-resources/lib_tests/slingshot/slingshot_test.clj @@ -0,0 +1,516 @@ +(ns slingshot.slingshot-test + (:require [clojure.test :refer :all] + [slingshot.slingshot :refer :all] + [clojure.string :as str]) + (:import java.util.concurrent.ExecutionException)) + +(defrecord exception-record [error-code duration-ms message]) +(defrecord x-failure [message]) + +(def a-sphere ^{:type ::sphere} {:radius 3}) + +(def h1 (derive (make-hierarchy) ::square ::shape)) +(def a-square ^{:type ::square} {:size 4}) + +(def exception-1 (Exception. "exceptional")) +(def exception-record-1 (exception-record. 6 1000 "pdf failure")) + +(defn mult-func [x y] + (let [a 7 b 11] + (if (= x 3) + (* a b x y) + (throw+ (x-failure. "x isn't 3... really??"))))) + +(defmacro mega-try [body] + `(try+ + ~body + + ;; by class derived from Throwable + (catch IllegalArgumentException e# + [:class-iae e#]) + (catch Exception e# + [:class-exception e#]) + + ;; by java class generically + (catch String e# + [:class-string e#]) + + ;; by clojure record type + (catch exception-record e# + [:class-exception-record e#]) + + ;; by key-value + (catch [:a-key 4] e# + [:key-yields-value e#]) + + ;; by multiple-key-value + (catch [:key1 4 :key2 5] e# + [:keys-yield-values e#]) + + ;; by key present + (catch (and (set? ~'%) (contains? ~'% :a-key)) e# + [:key-is-present e#]) + + ;; by clojure type, with optional hierarchy + (catch (isa? (type ~'%) ::sphere) e# + [:type-sphere (type e#) e#]) + (catch (isa? h1 (type ~'%) ::shape) e# + [:type-shape-in-h1 (type e#) e#]) + + ;; by predicate + (catch nil? e# + [:pred-nil e#]) + (catch keyword? e# + [:pred-keyword e#]) + (catch symbol? e# + [:pred-symbol e#]) + (catch map? e# + [:pred-map e# (meta e#)]))) + +(deftest test-try+ + (testing "catch by class derived from Throwable" + (testing "treat throwables exactly as throw does, interop with try/throw" + (is (= [:class-exception exception-1] + (mega-try (throw+ exception-1)) + (mega-try (throw exception-1)) + (try (throw+ exception-1) + (catch Exception e [:class-exception e])) + (try (throw exception-1) + (catch Exception e [:class-exception e]))))) + (testing "IllegalArgumentException thrown by clojure/core" + (is (= :class-iae (first (mega-try (str/replace "foo" 1 1))))))) + + (testing "catch by java class generically" + (is (= [:class-string "fail"] (mega-try (throw+ "fail"))))) + + #_(testing "catch by clojure record type" + (is (= [:class-exception-record exception-record-1] + (mega-try (throw+ exception-record-1))))) + + (testing "catch by key is present" + (is (= [:key-is-present #{:a-key}] (mega-try (throw+ #{:a-key}))))) + + (testing "catch by keys and values" + (is (= [:key-yields-value {:a-key 4}] (mega-try (throw+ {:a-key 4})))) + (is (= [:keys-yield-values {:key1 4 :key2 5}] + (mega-try (throw+ {:key1 4 :key2 5}))))) + + (testing "catch by clojure type with optional hierarchy" + (is (= [:type-sphere ::sphere a-sphere] (mega-try (throw+ a-sphere)))) + (is (= [:type-shape-in-h1 ::square a-square] (mega-try (throw+ a-square))))) + + (testing "catch by predicate" + (is (= [:pred-nil nil] (mega-try (throw+ nil)))) + (is (= [:pred-keyword :awesome] (mega-try (throw+ :awesome)))) + (is (= [:pred-symbol 'yuletide] (mega-try (throw+ 'yuletide)))) + (is (= [:pred-map {:error-code 4} nil] (mega-try (throw+ {:error-code 4})))) + (testing "preservation of metadata" + (is (= [:pred-map {:error-code 4} {:severity 4}] + (mega-try (throw+ ^{:severity 4} {:error-code 4}))))))) + +(deftest test-clauses + (let [bumps (atom 0) + bump (fn [] (swap! bumps inc))] + (is (nil? (try+))) + (is (nil? (try+ (catch integer? i (inc i))))) + (is (nil? (try+ (finally (bump))))) + (is (nil? (try+ (catch integer? i (inc i)) (finally (bump))))) + (is (nil? (try+ (catch integer? i (inc i)) (catch map? m m) + (finally (bump))))) + + (is (= 3 (try+ 3))) + (is (= 3 (try+ 3 (catch integer? i 4)))) + (is (= 3 (try+ 3 (finally (bump))))) + (is (= 3 (try+ 3 (catch integer? i 4) (finally (bump))))) + (is (= 4 (try+ (throw+ 3) (catch integer? i (inc i)) (finally (bump))))) + (is (= 4 (try+ (throw+ 3) (catch integer? i (inc i)) (catch map? m m) + (finally (bump))))) + (is (= 4 (try+ (throw+ {:sel 4}) (catch integer? i (inc i)) + (catch map? m (:sel m)) (finally (bump))))) + + (is (= 4 (try+ 3 4))) + (is (= 4 (try+ 3 4 (catch integer? i 4)))) + (is (= 4 (try+ 3 4 (finally (bump))))) + (is (= 4 (try+ 3 4 (catch integer? i 4) (finally (bump))))) + (is (= 5 (try+ (throw+ 4) 4 (catch integer? i (inc i)) (finally (bump))))) + (is (= 11 @bumps)))) + +(defn ax [] (throw+ 1)) +(defn bx [] (try+ (ax) (catch integer? p (throw+ 2)))) +(defn cx [] (try+ (bx) (catch integer? q (throw+ 3)))) +(defn dx [] (try+ (cx) (catch integer? r (throw+ 4)))) +(defn ex [] (try+ (dx) (catch integer? s (throw+ 5)))) +(defn fx [] (try+ (ex) (catch integer? t (throw+ 6)))) +(defn gx [] (try+ (fx) (catch integer? u (throw+ 7)))) +(defn hx [] (try+ (gx) (catch integer? v (throw+ 8)))) +(defn ix [] (try+ (hx) (catch integer? w &throw-context))) + +(defn next-context [x] + (-> x :cause get-throw-context)) + +(deftest test-throw-context + (let [context (ix) + context1 (next-context context) + context2 (next-context context1)] + + (is (= #{:object :message :cause :stack-trace :wrapper :throwable} + (set (keys context)) + (set (keys context1)) + (set (keys context2)))) + (is (= 8 (-> context :object))) + (is (= 7 (-> context1 :object))) + (is (= 6 (-> context2 :object))))) + +(defn e [] + (try+ + (throw (Exception. "uncaught")) + (catch integer? i i))) + +(defn f [] + (try+ + (throw+ 3.2) + (catch integer? i i))) + + +(defn g [] + (try+ + (throw+ 3.2 "wasn't caught") + (catch integer? i i))) + +(deftest test-uncaught + (is (thrown-with-msg? Exception #"^uncaught$" (e))) + (is (thrown-with-msg? Exception #"^throw\+: .*" (f))) + (is (thrown-with-msg? Exception #"wasn't caught" (g)))) + +(defn h [] + (try+ + (try+ + (throw+ 0) + (catch zero? e + (throw+))) + (catch zero? e + :zero))) + +(deftest test-rethrow + (is (= :zero (h)))) + +(defn i [] + (try + (try+ + (doall (map (fn [x] (throw+ (str x))) [1])) + (catch string? x + x)) + (catch Throwable x))) + +(defn j [] + (try+ + (let [fut (future (throw+ "whoops"))] + @fut) + (catch string? e + e))) + +(deftest test-issue-5 + (is (= "1" (i))) + (is (= "whoops" (j)))) + +(deftest test-unmacroed-pct + (is (= :was-eee (try+ (throw+ "eee") + (catch (= % "eee") _ :was-eee) + (catch string? _ :no!))))) + +(deftest test-x-ray-vision + (let [[val wrapper] (try+ + (try + (try + (try + (throw+ "x-ray!") + (catch Throwable x + (throw (RuntimeException. x)))) + (catch Throwable x + (throw (ExecutionException. x)))) + (catch Throwable x + (throw (RuntimeException. x)))) + (catch string? x + [x (:throwable &throw-context)]))] + (is (= "x-ray!" val)) + (is (= "x-ray!" (get-thrown-object wrapper))))) + +(deftest test-catching-wrapper + (let [e (Exception.)] + (try + (try+ + (throw e) + (catch Exception _ + (throw+ :a "msg: %s" %))) + (is false) + (catch Exception s + (is (= "msg: :a" (.getMessage s))) + (is (= e (.getCause s))))))) + +(deftest test-eval-object-once + (let [bumps (atom 0) + bump (fn [] (swap! bumps inc))] + (try+ + (throw+ (bump) "this is it: %s %s %s" % % %) + (catch Object _)) + (is (= @bumps 1)))) + +(deftest test-get-throw-context + (let [object (Object.) + exception1 (Exception.) + exception2 (Exception. "ex1" exception1) + t1 (try + (throw+ object) + (catch Throwable t t)) + t2 (try + (throw+ exception2) + (catch Throwable t t)) + t3 (try + (throw exception2) + (catch Throwable t t))] + (is (= #{:object :message :cause :stack-trace :wrapper + :throwable} + (-> t1 get-throw-context keys set))) + (is (= #{:object :message :cause :stack-trace :throwable} + (-> t2 get-throw-context keys set))) + (is (= #{:object :message :cause :stack-trace :throwable} + (-> t3 get-throw-context keys set))) + + (is (identical? object (:object (get-throw-context t1)))) + (is (identical? exception2 (:object (get-throw-context t2)))) + (is (identical? exception2 (:object (get-throw-context t3)))) + + (is (identical? exception1 (:cause (get-throw-context t2)))) + (is (identical? exception1 (:cause (get-throw-context t3)))) + (is (= "ex1" (:message (get-throw-context t2)))) + (is (= "ex1" (:message (get-throw-context t3)))))) + +(deftest test-get-thrown-object + (let [object (Object.) + exception (Exception.) + t1 (try + (throw+ object) + (catch Throwable t t)) + t2 (try + (throw+ exception) + (catch Throwable t t)) + t3 (try + (throw exception) + (catch Throwable t t))] + (is (identical? object (get-thrown-object t1))) + (is (identical? exception (get-thrown-object t2))) + (is (identical? exception (get-thrown-object t3))))) + +(deftest test-wrapper-and-throwable + (let [context (try+ + (try + (throw+ :afp "wrapper-0") + (catch Exception e + (throw (RuntimeException. "wrapper-1" e)))) + (catch Object _ + &throw-context))] + (is (= "wrapper-0" (.getMessage ^Throwable (:wrapper context)))) + (is (= "wrapper-1" (.getMessage ^Throwable (:throwable context)))))) + +(deftest test-inline-predicate + (is (= :not-caught (try+ + (throw+ {:foo true}) + (catch #(-> % :foo (= false)) data + :caught) + (catch Object _ + :not-caught))))) + +(defn gen-body + [rec-sym throw?] + (let [body `(swap! ~rec-sym #(conj % :body))] + (if throw? + (list 'do body `(throw+ (Exception.))) + body))) + +(defn gen-catch-clause + [rec-sym] + `(catch Exception e# (swap! ~rec-sym #(conj % :catch)))) + +(defn gen-else-clause + [rec-sym broken?] + (let [else-body `(swap! ~rec-sym #(conj % :else))] + (if broken? + (list 'else (list 'do else-body `(throw+ (Exception.)))) + (list 'else else-body)))) + +(defn gen-finally-clause + [rec-sym] + `(finally (swap! ~rec-sym #(conj % :finally)))) + +(defn gen-try-else-form + "Generate variations of (try ... (else ...) ...) forms, which (when eval'd) + will return a vector describing the sequence in which things were evaluated, + e.g. [:body :catch :finally]" + [throw? catch? finally? broken-else?] + (let [rec-sym (gensym "rec") + body (gen-body rec-sym throw?) + catch-clause (if catch? (gen-catch-clause rec-sym)) + else-clause (gen-else-clause rec-sym broken-else?) + finally-clause (if finally? (gen-finally-clause rec-sym))] + `(let [~rec-sym (atom [])] + (try+ + ~(remove nil? `(try+ + ~body + ~catch-clause + ~else-clause + ~finally-clause)) + (catch Object e# + ;; if the inner try+ threw, report it as a :bang! in the return vec + (swap! ~rec-sym #(conj % :bang!)))) + @~rec-sym))) + +(deftest test-else + (doseq [throw? [true false] + catch? [true false] + broken-else? [true false] + finally? [true false]] + (testing (str "test-else: throw? " throw? " catch? " catch? + " broken-else? " broken-else? " finally? " finally?) + (let [try-else-form (gen-try-else-form throw? catch? finally? broken-else?) + actual (eval try-else-form) + expected (vec (remove nil? + [:body + (if (and throw? catch?) :catch) + (if (not throw?) :else) + (if finally? :finally) + ;; expect an escaped exception when either: + ;; a) the else clause runs, and throws + ;; b) the body throws, and is not caught + (if (or (and (not throw?) broken-else?) + (and throw? (not catch?))) :bang!)]))] + (is (= actual expected)))))) + +(deftest test-reflection + (try+ + nil + (catch Exception e + (.getMessage e)))) + +(deftest test-ex-info-compatibility + (let [data {:type :fail :reason :not-found} + message "oops" + wrapper (ex-info message data) + rte1 (RuntimeException. "one" wrapper) + rte2 (RuntimeException. "two" rte1) + direct (try+ + (throw wrapper) + (catch [:type :fail] e + &throw-context) + (catch Object _ + :whoops)) + cause-chain (try+ + (throw rte2) + (catch [:type :fail] e + &throw-context) + (catch Object _ + :whoops))] + (is (= (:object direct) data)) + (is (= (:object cause-chain) data)) + (is (= (:message direct) message)) + (is (= (:message cause-chain) message)) + (is (= (:wrapper direct) wrapper)) + (is (= (:wrapper cause-chain) wrapper)) + (is (= (:throwable direct) wrapper)) + (is (= (:throwable cause-chain) rte2)))) + +;; helpers for test-optional-cause + +(defmacro caught-result [& body] + `(try+ + ~@body + (catch Object ~'o + [(:cause ~'&throw-context) + (:message ~'&throw-context)]))) + +(defmacro caught-result-from-catch [cause & body] + `(caught-result + (try+ + (throw+ ~cause) + (catch Object ~'o + ~@body)))) + +(deftest test-optional-cause + (let [imp (Exception. "I did it implicitly.") + exp (Exception. "I did it explicitly.") + def-msg "throw+: 1" + msg "message two %s" + fmt "aha! %s" + fmt-msg "aha! 1" + fmt2 "%s leading to %s" + fmt2-msg "1 leading to [1 1]" + + ;; throw from outside catch, no implicit cause + + result1 (caught-result (throw+ 1)) + result2 (caught-result (throw+ 1 msg)) + result3 (caught-result (throw+ 1 fmt %)) + result4 (caught-result (throw+ 1 fmt2 % [% %])) + + result5 (caught-result (throw+ 1 nil)) + result6 (caught-result (throw+ 1 nil msg)) + result7 (caught-result (throw+ 1 nil fmt %)) + result8 (caught-result (throw+ 1 nil fmt2 % [% %])) + + result9 (caught-result (throw+ 1 exp)) + result10 (caught-result (throw+ 1 exp msg)) + result11 (caught-result (throw+ 1 exp fmt %)) + result12 (caught-result (throw+ 1 exp fmt2 % [% %])) + + ;; throw from inside catch, implicit cause available + + result13 (caught-result-from-catch imp (throw+)) + + result14 (caught-result-from-catch imp (throw+ 1)) + result15 (caught-result-from-catch imp (throw+ 1 msg)) + result16 (caught-result-from-catch imp (throw+ 1 fmt %)) + result17 (caught-result-from-catch imp (throw+ 1 fmt2 % [% %])) + + result18 (caught-result-from-catch imp (throw+ 1 nil)) + result19 (caught-result-from-catch imp (throw+ 1 nil msg)) + result20 (caught-result-from-catch imp (throw+ 1 nil fmt %)) + result21 (caught-result-from-catch imp (throw+ 1 nil fmt2 % [% %])) + + result22 (caught-result-from-catch imp (throw+ 1 exp)) + result23 (caught-result-from-catch imp (throw+ 1 exp msg)) + result24 (caught-result-from-catch imp (throw+ 1 exp fmt %)) + result25 (caught-result-from-catch imp (throw+ 1 exp fmt2 % [% %]))] + + (testing "outside catch" + (testing "implicit cause" + (is (= result1 [nil def-msg])) + (is (= result2 [nil msg])) + (is (= result3 [nil fmt-msg])) + (is (= result4 [nil fmt2-msg]))) + (testing "erased cause" + (is (= result5 [nil def-msg])) + (is (= result6 [nil msg])) + (is (= result7 [nil fmt-msg])) + (is (= result8 [nil fmt2-msg]))) + (testing "explicit cause" + (is (= result9 [exp def-msg])) + (is (= result10 [exp msg])) + (is (= result11 [exp fmt-msg])) + (is (= result12 [exp fmt2-msg])))) + (testing "inside catch" + (testing "rethrow" + (is (= result13 [nil "I did it implicitly."]))) + (testing "implicit cause" + (is (= result14 [imp def-msg])) + (is (= result15 [imp msg])) + (is (= result16 [imp fmt-msg])) + (is (= result17 [imp fmt2-msg]))) + (testing "erased cause" + (is (= result18 [nil def-msg])) + (is (= result19 [nil msg])) + (is (= result20 [nil fmt-msg])) + (is (= result21 [nil fmt2-msg]))) + (testing "explicit cause" + (is (= result22 [exp def-msg])) + (is (= result23 [exp msg])) + (is (= result24 [exp fmt-msg])) + (is (= result25 [exp fmt2-msg])))))) diff --git a/test-resources/lib_tests/slingshot/support_test.clj b/test-resources/lib_tests/slingshot/support_test.clj new file mode 100644 index 00000000..12d2c142 --- /dev/null +++ b/test-resources/lib_tests/slingshot/support_test.clj @@ -0,0 +1,111 @@ +(ns slingshot.support-test + (:require [clojure.test :refer :all] + [slingshot.slingshot :refer [throw+ try+]] + [slingshot.support :refer :all]) + (:import (java.util.concurrent ExecutionException))) + +(deftest test-parse-try+ + (let [f parse-try+] + (is (= [nil nil nil nil] (f ()))) + + (is (= ['(1) nil nil nil] (f '(1)))) + (is (= [nil '((catch 1)) nil nil] (f '((catch 1))))) + (is (= [nil nil '(else 1) nil] (f '((else 1))))) + (is (= [nil nil nil '(finally 1)] (f '((finally 1))))) + + (is (= ['(1) '((catch 1)) nil nil] (f '(1 (catch 1))))) + (is (= ['(1) nil '(else 1) nil] (f '(1 (else 1))))) + (is (= ['(1) nil nil '(finally 1)] (f '(1 (finally 1))))) + + (is (= ['(1) '((catch 1)) nil '(finally 1)] + (f '(1 (catch 1) (finally 1))))) + (is (= ['(1) '((catch 1) (catch 2)) nil '(finally 1)] + (f '(1 (catch 1) (catch 2) (finally 1))))) + (is (= ['(1) '((catch 1)) '(else 1) nil] + (f '(1 (catch 1) (else 1))))) + (is (= ['(1) '((catch 1) (catch 2)) '(else 1) nil] + (f '(1 (catch 1) (catch 2) (else 1))))) + + (is (= [nil nil '(else 1) '(finally 1)] + (f '((else 1) (finally 1))))) + (is (= ['(1) nil '(else 1) '(finally 1)] + (f '(1 (else 1) (finally 1))))) + (is (= [nil '((catch 1)) '(else 1) nil] + (f '((catch 1) (else 1))))) + (is (= ['(1) '((catch 1)) '(else 1) nil] + (f '(1 (catch 1) (else 1))))) + + (is (thrown? IllegalArgumentException (f '((catch 1) (1))))) + (is (thrown? IllegalArgumentException (f '((finally 1) (1))))) + (is (thrown? IllegalArgumentException (f '((finally 1) (catch 1))))) + (is (thrown? IllegalArgumentException (f '((finally 1) (finally 2))))) + (is (thrown? IllegalArgumentException (f '((else 1) (1))))) + (is (thrown? IllegalArgumentException (f '((else 1) (catch 1))))) + (is (thrown? IllegalArgumentException (f '((else 1) (else 2))))))) + +(defn stack-trace-fn [] + (stack-trace)) + +#_(deftest test-stack-trace + (let [{:keys [methodName className]} (-> (stack-trace-fn) first bean)] + (is (= methodName "invoke")) + (is (re-find #"stack_trace_fn" className)))) + +(deftest test-resolve-local + (let [a 4] + (is (= 4 (resolve-local a))) + (is (nil? (resolve-local b))))) + +(deftest test-wrap + (let [tmessage "test-wrap-1" + tobject 4 + tcause (Exception.) + tstack-trace (stack-trace) + tdata {:object tobject} + tcontext (assoc tdata + :message tmessage + :cause tcause + :stack-trace tstack-trace) + tthrowable (wrap tcontext) + {:keys [message cause data stackTrace]} (bean tthrowable)] + (is (ex-data tthrowable)) + (is (= [message cause (seq stackTrace) data] + [tmessage tcause (seq tstack-trace) tdata])))) + +(def test-hooked (atom nil)) + +(deftest test-throw-hook + (binding [*throw-hook* #(reset! test-hooked %)] + (throw+ "throw-hook-string") + (is (= (set (keys @test-hooked)) + (set [:object :message :cause :stack-trace]))) + (is (= "throw-hook-string" (:object @test-hooked)))) + (binding [*throw-hook* (fn [x] 42)] + (is (= (throw+ "something") 42)))) + +(def catch-hooked (atom nil)) + +(defn catch-hook-return [object] + (fn [x] (assoc x :catch-hook-return object))) + +(defn catch-hook-throw [object] + (fn [x] (assoc x :catch-hook-throw object))) + +(deftest test-catch-hook + (binding [*catch-hook* #(reset! catch-hooked %)] + (try+ (throw+ "catch-hook-string") (catch string? x x)) + (is (= (set (keys @catch-hooked)) + (set [:object :message :cause :stack-trace :wrapper :throwable]))) + (is (= "catch-hook-string" (:object @catch-hooked)))) + (binding [*catch-hook* (catch-hook-return 42)] + (is (= 42 (try+ (throw+ "boo") (catch string? x x))))) + (binding [*catch-hook* (catch-hook-throw (IllegalArgumentException. "bleh"))] + (is (thrown-with-msg? IllegalArgumentException #"bleh" + (try+ (throw+ "boo") (catch string? x x))))) + (is (= "soup!" + (try+ + (binding [*catch-hook* (catch-hook-throw "soup!")] + (try+ + (throw+ "boo") + (catch string? x x))) + (catch string? x x))))) diff --git a/test-resources/lib_tests/slingshot/test_test.clj b/test-resources/lib_tests/slingshot/test_test.clj new file mode 100644 index 00000000..b156c289 --- /dev/null +++ b/test-resources/lib_tests/slingshot/test_test.clj @@ -0,0 +1,8 @@ +(ns slingshot.test-test + (:require [clojure.test :refer :all] + [slingshot.slingshot :refer [throw+]] + [slingshot.test])) + +(deftest test-slingshot-test-macros + (is (thrown+? string? (throw+ "test"))) + (is (thrown+-with-msg? string? #"th" (throw+ "test" "hi there")))) diff --git a/test/babashka/bb_edn_test.clj b/test/babashka/bb_edn_test.clj index ae73f7fa..6c43a6ea 100644 --- a/test/babashka/bb_edn_test.clj +++ b/test/babashka/bb_edn_test.clj @@ -236,7 +236,28 @@ :task (do (Thread/sleep 10) (+ 1 2 3))} c (do (Thread/sleep 10) :c)}} - (is (= [6 6 :c] (bb "run" "--prn" "a"))))))) + (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*]))}} + (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] + server [:foo :bar]] + (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")))))) (deftest list-tasks-test (test-utils/with-config {} diff --git a/test/babashka/error_test.clj b/test/babashka/error_test.clj index 857ca787..25914e35 100644 --- a/test/babashka/error_test.clj +++ b/test/babashka/error_test.clj @@ -169,3 +169,33 @@ user/quux - :1:15 user/quux - :1:1 user/bar - :1:69 user - :1:91")))) + +(deftest print-exception-data-test + (testing "output of uncaught ExceptionInfo" + (let [output (try (tu/bb nil "(let [d {:zero 0 :one 1}] (throw (ex-info \"some msg\" d)))") + (catch Exception e (ex-message e)))] + (multiline-equals output + "----- Error -------------------------------------------------------------------- +Type: clojure.lang.ExceptionInfo +Message: some msg +Data: {:zero 0, :one 1} +Location: :1:27 + +----- Context ------------------------------------------------------------------ +1: (let [d {:zero 0 :one 1}] (throw (ex-info \"some msg\" d))) + ^--- some msg + +----- Locals ------------------------------------------------------------------- +d: {:zero 0, :one 1}"))) + (testing "output of ordinary Exception" + (let [output (try (tu/bb nil "(throw (Exception. \"some msg\"))") + (catch Exception e (ex-message e)))] + (multiline-equals output + "----- Error -------------------------------------------------------------------- +Type: java.lang.Exception +Message: some msg +Location: :1:1 + +----- Context ------------------------------------------------------------------ +1: (throw (Exception. \"some msg\")) + ^--- some msg")))) diff --git a/test/babashka/impl/nrepl_server_test.clj b/test/babashka/impl/nrepl_server_test.clj index 08dddee0..822fb802 100644 --- a/test/babashka/impl/nrepl_server_test.clj +++ b/test/babashka/impl/nrepl_server_test.clj @@ -180,7 +180,7 @@ "session" session "id" (new-id!)}) (dotimes [_ 3] (let [reply (read-reply in session @id)] - (is (= "Hello\n" (:out reply))))))))) + (is (= "Hello\n" (tu/normalize (:out reply)))))))))) (deftest nrepl-server-test (let [proc-state (atom nil) diff --git a/test/babashka/impl/repl_test.clj b/test/babashka/impl/repl_test.clj index d9cf7b70..a1bd730b 100644 --- a/test/babashka/impl/repl_test.clj +++ b/test/babashka/impl/repl_test.clj @@ -2,6 +2,7 @@ (:require [babashka.impl.pprint :refer [pprint-namespace]] [babashka.impl.repl :refer [start-repl!]] + [babashka.test-utils :as tu] [clojure.string :as str] [clojure.test :as t :refer [deftest is]] [sci.core :as sci] @@ -20,18 +21,20 @@ :namespaces {'clojure.pprint pprint-namespace}}))) (defn assert-repl [expr expected] - (is (str/includes? (sci/with-out-str - (sci/with-in-str (str expr "\n:repl/quit") - (repl!))) expected))) + (is (str/includes? (tu/normalize + (sci/with-out-str + (sci/with-in-str (str expr "\n:repl/quit") + (repl!)))) expected))) (defn assert-repl-error [expr expected] (is (str/includes? - (let [sw (java.io.StringWriter.)] - (sci/binding [sci/out (java.io.StringWriter.) - sci/err sw] - (sci/with-in-str (str expr "\n:repl/quit") - (repl!))) - (str sw)) expected))) + (tu/normalize + (let [sw (java.io.StringWriter.)] + (sci/binding [sci/out (java.io.StringWriter.) + sci/err sw] + (sci/with-in-str (str expr "\n:repl/quit") + (repl!))) + (str sw))) expected))) (deftest repl-test (assert-repl "1" "1") diff --git a/test/babashka/main_test.clj b/test/babashka/main_test.clj index 4fb329b3..a56bfc87 100644 --- a/test/babashka/main_test.clj +++ b/test/babashka/main_test.clj @@ -17,7 +17,7 @@ :eof nil} (apply test-utils/bb (when (some? input) (str input)) (map str args)))) -(deftest parse-opts-test +(deftest ^:windows parse-opts-test (is (= "1667" (:nrepl (main/parse-opts ["--nrepl-server"])))) (is (= "1666" @@ -52,17 +52,17 @@ (is (= '("-e" "1") (bb nil "-e" "*command-line-args*" "--" "-e" "1"))) (let [v (bb nil "--describe")] (is (:babashka/version v)) - (is (:feature/xml v))) - ) + (is (:feature/xml v)))) -(deftest version-test + +(deftest ^:windows version-test (is (= [1 0 0] (main/parse-version "1.0.0-SNAPSHOT"))) (is (main/satisfies-min-version? "0.1.0")) (is (main/satisfies-min-version? "0.1.0-SNAPSHOT")) (is (not (main/satisfies-min-version? "300.0.0"))) (is (not (main/satisfies-min-version? "300.0.0-SNAPSHOT")))) -(deftest print-error-test +(deftest ^:windows print-error-test (is (thrown-with-msg? Exception #"java.lang.NullPointerException" (bb nil "(subs nil 0 0)")))) @@ -131,8 +131,8 @@ (doseq [s res] (is (not-empty s))))) -(deftest malformed-command-line-args-test - (is (thrown-with-msg? Exception #"File does not exist: non-existing\n" +(deftest ^:windows malformed-command-line-args-test + (is (thrown-with-msg? Exception #"File does not exist: non-existing" (bb nil "-f" "non-existing")))) (deftest ssl-test @@ -178,10 +178,11 @@ name))))) (testing "print source from file on classpath" (is (= "(defn foo [x y]\n (+ x y))\n" - (bb nil - "-cp" dir - "-e" (format "(require '[clojure.repl :refer [source]] '[%s])" name) - "-e" (format "(with-out-str (source %s/foo))" name))))))) + (test-utils/normalize + (bb nil + "-cp" dir + "-e" (format "(require '[clojure.repl :refer [source]] '[%s])" name) + "-e" (format "(with-out-str (source %s/foo))" name)))))))) (deftest eval-test (is (= "120\n" (test-utils/bb nil "(eval '(do (defn foo [x y] (+ x y)) @@ -388,8 +389,7 @@ (is (= "hello" (bb nil "(doto (java.lang.Thread. (fn [] (prn \"hello\"))) (.start) (.join)) nil")))) (deftest dynvar-test - (is (= 1 (bb nil "(binding [*command-line-args* 1] *command-line-args*)"))) - (is (= 1 (bb nil "(binding [*input* 1] *input*)")))) + (is (= 1 (bb nil "(binding [*command-line-args* 1] *command-line-args*)")))) (deftest file-in-error-msg-test (is (thrown-with-msg? Exception #"error.bb" @@ -468,8 +468,8 @@ ;; TODO: refactor into individual unit tests ;; One for downloading a small file and one for unzipping. #_(is (try (= 6 (bb nil (io/file "test" "babashka" "scripts" "download_and_extract_zip.bb"))) - (catch Exception e - (is (str/includes? (str e) "timed out")))))) + (catch Exception e + (is (str/includes? (str e) "timed out")))))) (deftest get-message-on-exception-info-test (is "foo" (bb nil "(try (throw (ex-info \"foo\" {})) (catch Exception e (.getMessage e)))"))) @@ -639,6 +639,23 @@ true"))) (is (str/blank? (with-out-str (main/main "doc" "non-existing")))) (is (= 1 (main/main "doc" "non-existing"))))) +(deftest process-handler-info-test + (when test-utils/native? + (is (= ["-e" "(vec (.get (.arguments (.info (java.lang.ProcessHandle/current)))))"] + (bb nil "-e" "(vec (.get (.arguments (.info (java.lang.ProcessHandle/current)))))"))) + (is (str/ends-with? + (bb nil "-e" "(.get (.command (.info (java.lang.ProcessHandle/current))))") + "bb")))) + +(deftest interop-concurrency-test + (is (= ["true" 3] (last (bb nil "-e" + " +(def f (fn [_] + [(String/valueOf true) + (.length \"foo\")])) + +(vec (pmap f (map str (range 10000))))"))))) + ;;;; Scratch (comment diff --git a/test/babashka/test_utils.clj b/test/babashka/test_utils.clj index c3f18752..79cd85c2 100644 --- a/test/babashka/test_utils.clj +++ b/test/babashka/test_utils.clj @@ -6,12 +6,19 @@ [babashka.main :as main] [babashka.process :as p] [clojure.edn :as edn] + [clojure.string :as str] [clojure.test :as test :refer [*report-counters*]] [sci.core :as sci] [sci.impl.vars :as vars])) (set! *warn-on-reflection* true) + +(defn normalize [s] + (if main/windows? + (str/replace s "\r\n" "\n") + s)) + (def ^:dynamic *bb-edn-path* nil) (defmethod clojure.test/report :begin-test-var [m] @@ -55,7 +62,7 @@ (with-in-str input-or-opts (apply main/main args)) (apply main/main args)))] (if (zero? res) - (str os) + (normalize (str os)) (do (println (str os)) (throw (ex-info (str es)