Merge branch 'master' into clojure.data.xml

This commit is contained in:
Michiel Borkent 2020-01-08 21:25:12 +01:00
commit 90f10061b3
61 changed files with 1527 additions and 1107 deletions

3
.carve_ignore Normal file
View file

@ -0,0 +1,3 @@
babashka.impl.clojure.stacktrace/root-cause
babashka.impl.classes/generate-reflection-file
babashka.main/-main

View file

@ -11,7 +11,7 @@ jobs:
working_directory: ~/repo
environment:
LEIN_ROOT: "true"
# GRAALVM_HOME: /home/circleci/graalvm-ce-java8-19.3.0
BABASHKA_PLATFORM: linux # could be used in jar name
steps:
- checkout
- run:
@ -34,18 +34,6 @@ jobs:
name: Install lsof
command: |
sudo apt-get install lsof
# - run:
# name: Download GraalVM
# command: |
# cd ~
# if ! [ -d graalvm-ce-java8-19.3.0 ]; then
# curl -O -sL https://github.com/oracle/graal/releases/download/vm-19.2.0/graalvm-ce-linux-amd64-19.2.0.tar.gz
# tar xzf graalvm-ce-linux-amd64-19.2.0.tar.gz
# fi
# - run:
# name: Install GraalVM SSL libs
# command: |
# .circleci/script/graalvm_ssl
- run:
name: Run JVM tests
command: |
@ -58,6 +46,16 @@ jobs:
name: Run as lein command
command: |
.circleci/script/lein
- run:
name: Create uberjar
command: |
mkdir -p /tmp/release
lein do clean, uberjar
VERSION=$(cat resources/BABASHKA_VERSION)
cp target/babashka-$VERSION-standalone.jar /tmp/release/babashka-$VERSION-standalone.jar
- store_artifacts:
path: /tmp/release
destination: release
- save_cache:
paths:
- ~/.m2
@ -117,6 +115,7 @@ jobs:
name: Run tests
command: |
script/test
script/run_lib_tests
# - run:
# name: Performance report
# command: |
@ -133,6 +132,10 @@ jobs:
- store_artifacts:
path: /tmp/release
destination: release
- run:
name: Publish artifact link to Slack
command: |
./bb .circleci/script/publish_artifact.clj
mac:
macos:
xcode: "9.0"
@ -180,6 +183,7 @@ jobs:
name: Run tests
command: |
script/test
script/run_lib_tests
# - run:
# name: Performance report
# command: |
@ -196,6 +200,10 @@ jobs:
- store_artifacts:
path: /tmp/release
destination: release
- run:
name: Publish artifact link to Slack
command: |
./bb .circleci/script/publish_artifact.clj
deploy:
docker:
- image: circleci/clojure:lein-2.8.1

View file

@ -0,0 +1,19 @@
(require '[clojure.java.shell :refer [sh]]
'[cheshire.core :refer [generate-string]])
(def channel "#babashka_circleci_builds")
#_(def channel "#_test")
(def text (format "[%s - %s@%s]: https://%s-201467090-gh.circle-artifacts.com/0/release/babashka-0.0.61-SNAPSHOT-%s-amd64.zip"
(System/getenv "BABASHKA_PLATFORM")
(System/getenv "CIRCLE_BRANCH")
(System/getenv "CIRCLE_SHA1")
(System/getenv "CIRCLE_BUILD_NUM")
(System/getenv "BABASHKA_PLATFORM")))
(def slack-hook-url (System/getenv "SLACK_HOOK_URL"))
(when slack-hook-url
(let [json (generate-string {:username "borkdude"
:channel channel
:text text})]
(sh "curl" "-X" "POST" "-H" "Content-Type: application/json" "-d" json slack-hook-url)))

View file

@ -9,7 +9,7 @@ assignees: ''
**version**
[ Please specify which version of babashka you're using. You can find this with `babashka --version`. The documentation on the master branch may be ahead of the most released version. You can check the docs for your version by going to cljdoc. ]
[ Please specify which version of babashka you're using. You can find this with `babashka --version`. The documentation on the master branch may be ahead of the most released version. ]
**platform**

2
.gitignore vendored
View file

@ -15,4 +15,4 @@ pom.xml.asc
!java/src/babashka/impl/LockFix.class
!test-resources/babashka/src_for_classpath_test/foo.jar
.cpcache
deps.edn
reflection.json

11
CHANGES.md Normal file
View file

@ -0,0 +1,11 @@
# Changes
## Breaking changes
## v0.0.44 - 0.0.45
- #173: Rename `*in*` to `*input*` (in the `user` namespace). The reason for
this is that itt shadowed `clojure.core/*in*` when used unqualified.
## v0.0.43
- #160: Add support for `java.lang.ProcessBuilder`. See docs. This replaces the
`conch` namespace.

325
README.md
View file

@ -1,18 +1,24 @@
# babashka
<img src="logo/babashka.svg" width="425px">
[![CircleCI](https://circleci.com/gh/borkdude/babashka/tree/master.svg?style=shield)](https://circleci.com/gh/borkdude/babashka/tree/master)
[![Clojars Project](https://img.shields.io/clojars/v/borkdude/babashka.svg)](https://clojars.org/borkdude/babashka)
[![cljdoc badge](https://cljdoc.org/badge/borkdude/babashka)](https://cljdoc.org/d/borkdude/babashka/CURRENT)
[![project chat](https://img.shields.io/badge/slack-join_chat-brightgreen.svg)](https://app.slack.com/client/T03RZGPFR/CLX41ASCS)
<!-- [![cljdoc badge](https://cljdoc.org/badge/borkdude/babashka)](https://cljdoc.org/d/borkdude/babashka/CURRENT) -->
A Clojure [babushka](https://en.wikipedia.org/wiki/Headscarf) for the grey areas of Bash.
<blockquote class="twitter-tweet" data-lang="en">
<p lang="en" dir="ltr">Life's too short to remember how to write Bash code. I feel liberated.</p>
&mdash;
<a href="https://github.com/laheadle">@laheadle</a> on Clojurians Slack
</blockquote>
## Quickstart
``` shellsession
$ bash <(curl -s https://raw.githubusercontent.com/borkdude/babashka/master/install)
$ ls | bb --time -i '(filter #(-> % io/file .isDirectory) *in*)'
$ ls | bb --time -i '(filter #(-> % io/file .isDirectory) *input*)'
("doc" "resources" "sci" "script" "src" "target" "test")
bb took 4ms.
```
@ -41,45 +47,42 @@ Non-goals:
* Provide a mixed Clojure/bash DSL (see portability).
* Replace existing shells. Babashka is a tool you can use inside existing shells like bash and it is designed to play well with them. It does not aim to replace them.
Reasons why babashka may not be the right fit for your use case:
- It uses [sci](https://github.com/borkdude/sci) for interpreting Clojure. Sci
implements only a subset of Clojure and is not as performant as compiled code.
- External libraries are not available (although you may use `load-file` for
loading external scripts).
Babashka uses [sci](https://github.com/borkdude/sci) for interpreting Clojure. Sci
implements a subset of Clojure and is not as performant as compiled code. If your script is taking more than a few seconds, Clojure on the JVM may be a better fit.
Read more about the differences with Clojure [here](#differences-with-clojure).
## Status
Experimental. Breaking changes are expected to happen at this phase.
Experimental. Breaking changes are expected to happen at this phase. Keep an eye
on [CHANGES.md](CHANGES.md) for a list of breaking changes.
## Examples
``` shellsession
$ ls | bb -i '*in*'
["LICENSE" "README.md" "bb" "doc" "pom.xml" "project.clj" "reflection.json" "resources" "script" "src" "target" "test"]
$ ls | bb -i '*input*'
["LICENSE" "README.md" "bb" "doc" "pom.xml" "project.clj" "resources" "script" "src" "target" "test"]
$ ls | bb -i '(count *in*)'
$ ls | bb -i '(count *input*)'
12
$ bb '(vec (dedupe *in*))' <<< '[1 1 1 1 2]'
$ bb '(vec (dedupe *input*))' <<< '[1 1 1 1 2]'
[1 2]
$ bb '(filterv :foo *in*)' <<< '[{:foo 1} {:bar 2}]'
$ bb '(filterv :foo *input*)' <<< '[{:foo 1} {:bar 2}]'
[{:foo 1}]
$ bb '(#(+ %1 %2 %3) 1 2 *in*)' <<< 3
$ bb '(#(+ %1 %2 %3) 1 2 *input*)' <<< 3
6
$ ls | bb -i '(filterv #(re-find #"reflection" %) *in*)'
["reflection.json"]
$ ls | bb -i '(filterv #(re-find #"README" %) *input*)'
["README.md"]
$ bb '(run! #(shell/sh "touch" (str "/tmp/test/" %)) (range 100))'
$ ls /tmp/test | bb -i '*in*'
$ ls /tmp/test | bb -i '*input*'
["0" "1" "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" "2" "20" "21" ...]
$ bb -O '(repeat "dude")' | bb --stream '(str *in* "rino")' | bb -I '(take 3 *in*)'
$ bb -O '(repeat "dude")' | bb --stream '(str *input* "rino")' | bb -I '(take 3 *input*)'
("duderino" "duderino" "duderino")
```
@ -128,20 +131,24 @@ You may also download a binary from [Github](https://github.com/borkdude/babashk
``` shellsession
Usage: bb [ -i | -I ] [ -o | -O ] [ --stream ] [--verbose]
[ ( --classpath | -cp ) <cp> ] [ ( --main | -m ) <main-namespace> ]
( -e <expression> | -f <file> | --repl | --socket-repl [<host>:]<port> )
[ ( --classpath | -cp ) <cp> ] [ --uberscript <file> ]
[ ( --main | -m ) <main-namespace> | -e <expression> | -f <file> |
--repl | --socket-repl [<host>:]<port> ]
[ arg* ]
Options:
--help, -h or -? Print this help text.
--version Print the current version of babashka.
-i Bind *in* to a lazy seq of lines from stdin.
-I Bind *in* to a lazy seq of EDN values from stdin.
-i Bind *input* to a lazy seq of lines from stdin.
-I Bind *input* to a lazy seq of EDN values from stdin.
-o Write lines to stdout.
-O Write EDN values to stdout.
--verbose Print entire stacktrace in case of exception.
--stream Stream over lines or EDN values from stdin. Combined with -i or -I *in* becomes a single value per iteration.
--stream Stream over lines or EDN values from stdin. Combined with -i or -I *input* becomes a single value per iteration.
--uberscript <file> Collect preloads, -e, -f and -m and all required namespaces from the classpath into a single executable file.
-e, --eval <expr> Evaluate an expression.
-f, --file <path> Evaluate a file.
-cp, --classpath Classpath to use.
@ -165,40 +172,22 @@ enumerated explicitly.
- `clojure.set` aliased as `set`
- `clojure.edn` aliased as `edn`:
- `read-string`
- `clojure.java.shell` aliases as `shell`:
- `sh`
- `clojure.java.shell` aliases as `shell`
- `clojure.java.io` aliased as `io`:
- `as-relative-path`, `copy`, `delete-file`, `file`
- `as-relative-path`, `as-url`, `copy`, `delete-file`, `file`, `input-stream`,
`make-parents`, `output-stream`, `reader`, `resource`, `writer`
- `clojure.main`: `repl`
- [`clojure.core.async`](https://clojure.github.io/core.async/) aliased as
`async`. The `alt` and `go` macros are not available but `alts!!` does work as
it is a function.
- [`me.raynes.conch.low-level`](https://github.com/clj-commons/conch#low-level-usage)
aliased as `conch`
- `clojure.stacktrace`
- [`clojure.tools.cli`](https://github.com/clojure/tools.cli) aliased as `tools.cli`
- [`clojure.data.csv`](https://github.com/clojure/data.csv) aliased as `csv`
- [`cheshire.core`](https://github.com/dakrone/cheshire) aliased as `json`
The following Java classes are available:
A selection of java classes are available, see `babashka/impl/classes.clj`.
- `ArithmeticException`
- `AssertionError`
- `Boolean`
- `Class`
- `Double`
- `Exception`
- `clojure.lang.ExceptionInfo`
- `Integer`
- `java.io.File`
- `java.nio.Files`
- `java.util.regex.Pattern`
- `String`
- `System`
- `Thread`
More classes can be added by request. See `reflection.json` and the `:classes`
option in `main.clj`.
Babashka supports `import` : `(import clojure.lang.ExceptionInfo)`.
Babashka supports `import`: `(import clojure.lang.ExceptionInfo)`.
Babashka supports a subset of the `ns` form where you may use `:require` and `:import`:
@ -211,11 +200,48 @@ Babashka supports a subset of the `ns` form where you may use `:require` and `:i
For the unsupported parts of the ns form, you may use [reader
conditionals](#reader-conditionals) to maintain compatibility with JVM Clojure.
Special vars:
### Input and output flags
- `*in*`: contains the input read from stdin. EDN by default, multiple lines of
text with the `-i` option, or multiple EDN values with the `-I` option.
- `*command-line-args*`: contain the command line args
In one-liners the `*input*` value may come in handy. It contains the input read from stdin as EDN by default. If you want to read in text, use the `-i` flag, which binds `*input*` to a lazy seq of lines of text. If you want to read multiple EDN values, use the `-I` flag. The `-o` option prints the result as lines of text. The `-O` option prints the result as lines of EDN values.
The following table illustrates the combination of options for commands of the form
echo "{{Input}}" | bb {{Input flags}} {{Output flags}} "*input*"
| Input | Input flags | Output flag | `*input*` | Output |
|----------------|-------------|-------------|---------------|----------|
| `{:a 1}` <br> `{:a 2}` | | | `{:a 1}` | `{:a 1}` |
| hello <br> bye | `-i` | | `("hello" "bye")` | `("hello" "bye")` |
| hello <br> bye | `-i` | `-o` | `("hello" "bye")` | hello <br> bye |
| `{:a 1}` <br> `{:a 2}` | `-I` | | `({:a 1} {:a 2})` | `({:a 1} {:a 2})` |
| `{:a 1}` <br> `{:a 2}` | `-I` | `-O` | `({:a 1} {:a 2})` | `{:a 1}` <br> `{:a 2}` |
When combined with the `--stream` option, the expression is executed for each value in the input:
``` clojure
$ echo '{:a 1} {:a 2}' | bb --stream '*input*'
{:a 1}
{:a 2}
```
### Current file path
The var `*file*` contains the full path of the file that is currently being
executed:
``` shellsession
$ cat example.clj
(prn *file*)
$ bb example.clj
"/Users/borkdude/example.clj"
```
### Command-line arguments
Command-line arguments can be retrieved using `*command-line-args*`.
### Additional functions
Additionally, babashka adds the following functions:
@ -329,7 +355,7 @@ export BABASHKA_PRELOADS
Note that you can concatenate multiple expressions. Now you can use these functions in babashka:
``` shellsession
$ bb '(-> (foo *in*) bar)' <<< 1
$ bb '(-> (foo *input*) bar)' <<< 1
6
```
@ -339,7 +365,7 @@ You can also preload an entire file using `load-file`:
export BABASHKA_PRELOADS='(load-file "my_awesome_prelude.clj")'
```
Note that `*in*` is not available in preloads.
Note that `*input*` is not available in preloads.
## Classpath
@ -363,8 +389,8 @@ $ cat deps.edn
{:deps
{my_gist_script
{:git/url "https://gist.github.com/borkdude/263b150607f3ce03630e114611a4ef42"
:sha "cfc761d06dfb30bb77166b45d439fe8fe54a31b8"}}}
:sha "cfc761d06dfb30bb77166b45d439fe8fe54a31b8"}}
:aliases {:my-script {:main-opts ["-m" "my-gist-script"]}}}
$ CLASSPATH=$(clojure -Spath)
$ bb --classpath "$CLASSPATH" --main my-gist-script
@ -381,6 +407,57 @@ $ bb "(my-gist-script/-main)"
Hello from gist script!
```
### Deps.clj
The [`deps.clj`](https://github.com/borkdude/deps.clj/) script can be used to work with `deps.edn`-based projects:
``` shell
$ deps.clj -A:my-script -Scommand "bb -cp {{classpath}} {{main-opts}}"
Hello from gist script!
```
Create these aliases for brevity:
``` shell
$ alias bbk='deps.clj -Scommand "bb -cp {{classpath}} {{main-opts}}"'
$ alias babashka='rlwrap deps.clj -Scommand "bb -cp {{classpath}} {{main-opts}}"'
$ bbk -A:my-script
Hello from gist script!
$ babashka
Babashka v0.0.58 REPL.
Use :repl/quit or :repl/exit to quit the REPL.
Clojure rocks, Bash reaches.
user=> (require '[my-gist-script :as mgs])
nil
user=> (mgs/-main)
Hello from gist script!
nil
```
## Uberscript
The `--uberscript` option collects the expressions in
`BABASHKA_PRELOADS`, the command line expression or file, the main entrypoint
and all required namespaces from the classpath into a single file. This can be
convenient for debugging and deployment.
Given the `deps.edn` from above:
``` clojure
$ deps.clj -A:my-script -Scommand "bb -cp {{classpath}} {{main-opts}} --uberscript my-script.clj"
$ cat my-script.clj
(ns my-gist-script)
(defn -main [& args]
(println "Hello from gist script!"))
(ns user (:require [my-gist-script]))
(apply my-gist-script/-main *command-line-args*)
$ bb my-script.clj
Hello from gist script!
```
## Parsing command line arguments
Babashka ships with `clojure.tools.cli`:
@ -446,22 +523,27 @@ A socket REPL client for Emacs is
## Spawning and killing a process
You may use the `conch` namespace for this. It maps to
[`me.raynes.conch.low-level`](https://github.com/clj-commons/conch#low-level-usage).
Use the `java.lang.ProcessBuilder` class.
Example:
``` clojure
$ bb '
(def ws (conch/proc "python" "-m" "SimpleHTTPServer" "1777"))
(net/wait-for-it "localhost" 1777) (conch/destroy ws)'
user=> (def ws (-> (ProcessBuilder. ["python" "-m" "SimpleHTTPServer" "1777"]) (.start)))
#'user/ws
user=> (wait/wait-for-port "localhost" 1777)
{:host "localhost", :port 1777, :took 2}
user=> (.destroy ws)
nil
```
Also see this [example](examples/process_builder.clj).
## Async
Apart from `future` for creating threads and the `conch` namespace for creating
processes, you may use the `async` namespace, which maps to `clojure.core.async`, for asynchronous scripting. The following
example shows how to get first available value from two different processes:
Apart from `future` and `pmap` for creating threads, you may use the `async`
namespace, which maps to `clojure.core.async`, for asynchronous scripting. The
following example shows how to get first available value from two different
processes:
``` clojure
bb '
@ -487,9 +569,6 @@ same. Multi-threading is supported (`pmap`, `future`).
Differences with Clojure:
- No first class vars. Note that you can define and redefine global values with
`def` / `defn`, but there is no `var` indirection.
- A subset of Java classes are supported.
- Only the `clojure.core`, `clojure.set`, `clojure.string` and `clojure.walk`
@ -500,6 +579,62 @@ Differences with Clojure:
- No support for unboxed types.
## External resources
### Tools and libraries
The following libraries are known to work with Babashka:
#### [deps.clj](https://github.com/borkdude/deps.clj)
A port of the [clojure](https://github.com/clojure/brew-install/) bash script to
Clojure / babashka.
#### [spartan.test](https://github.com/borkdude/spartan.test/)
A minimal test framework compatible with babashka.
#### [medley](https://github.com/borkdude/medley/)
A fork of [medley](https://github.com/weavejester/medley) made compatible with
babashka. Requires `bb` >= v0.0.58.
#### [clj-http-lite](https://github.com/borkdude/clj-http-lite)
This fork does not depend on any other libraries. Example:
``` shell
$ export BABASHKA_CLASSPATH="$(clojure -Sdeps '{:deps {clj-http-lite {:git/url "https://github.com/borkdude/clj-http-lite" :sha "f44ebe45446f0f44f2b73761d102af3da6d0a13e"}}}' -Spath)"
$ bb "(require '[clj-http.lite.client :as client]) (:status (client/get \"https://www.clojure.org\"))"
200
```
#### [limit-break](https://github.com/technomancy/limit-break)
A debug REPL library. Example:
``` shell
$ export BABASHKA_CLASSPATH="$(clojure -Sdeps '{:deps {limit-break {:git/url "https://github.com/technomancy/limit-break" :sha "050fcfa0ea29fe3340927533a6fa6fffe23bfc2f" :deps/manifest :deps}}}' -Spath)"
$ bb "(require '[limit.break :as lb]) (let [x 1] (lb/break))"
Babashka v0.0.49 REPL.
Use :repl/quit or :repl/exit to quit the REPL.
Clojure rocks, Bash reaches.
break> x
1
```
### Blogs
- [Clojure Start Time in 2019](https://stuartsierra.com/2019/12/21/clojure-start-time-in-2019) by Stuart Sierra
- [Advent of Random
Hacks](https://lambdaisland.com/blog/2019-12-19-advent-of-parens-19-advent-of-random-hacks)
by Arne Brasseur
- [Clojure in the Shell](https://lambdaisland.com/blog/2019-12-05-advent-of-parens-5-clojure-in-the-shell) by Arne Brasseur
- [Clojure Tool](https://purelyfunctional.tv/issues/purelyfunctional-tv-newsletter-351-clojure-tool-babashka/) by Eric Normand
## Developing Babashka
To work on Babashka itself make sure Git submodules are checked out.
@ -520,6 +655,19 @@ You need [Leiningen](https://leiningen.org/), and for building binaries you need
`lein repl` will get you a standard REPL/nREPL connection. To work on tests use `lein with-profiles +test repl`.
### Adding classes
Add necessary classes to `babashka/impl/classes.clj`. For every addition, write
a unit test, so it's clear why it is added and removing it will break the
tests. Try to reduce the size of the binary by only adding the necessary parts
of a class in `:instance-check`, `:constructors`, `:methods`, `:fields` or
`:custom`.
The `reflection.json` file that is needed for GraalVM compilation is generated
with:
lein with-profiles +reflection run
### Test
Test on the JVM (for development):
@ -553,7 +701,7 @@ welcome!
### Delete a list of files returned by a Unix command
```
find . | grep conflict | bb -i '(doseq [f *in*] (.delete (io/file f)))'
find . | grep conflict | bb -i '(doseq [f *input*] (.delete (io/file f)))'
```
### Calculate aggregate size of directory
@ -587,7 +735,7 @@ $ cat /tmp/test.txt
3 Babashka
4 Goodbye
$ < /tmp/test.txt bb -io '(shuffle *in*)'
$ < /tmp/test.txt bb -io '(shuffle *input*)'
3 Babashka
2 Clojure
4 Goodbye
@ -601,7 +749,7 @@ For converting JSON to EDN, see [jet](https://github.com/borkdude/jet).
``` shellsession
$ curl -s https://api.github.com/repos/borkdude/babashka/tags |
jet --from json --keywordize --to edn |
bb '(-> *in* first :name (subs 1))'
bb '(-> *input* first :name (subs 1))'
"0.0.4"
```
@ -610,16 +758,18 @@ bb '(-> *in* first :name (subs 1))'
``` shellsession
$ curl -s https://api.github.com/repos/borkdude/babashka/releases |
jet --from json --keywordize |
bb '(-> *in* first :assets)' |
bb '(some #(re-find #".*linux.*" (:browser_download_url %)) *in*)'
bb '(-> *input* first :assets)' |
bb '(some #(re-find #".*linux.*" (:browser_download_url %)) *input*)'
"https://github.com/borkdude/babashka/releases/download/v0.0.4/babashka-0.0.4-linux-amd64.zip"
```
### View download statistics from Clojars
Contributed by [@plexus](https://github.com/plexus).
``` shellsession
$ curl https://clojars.org/stats/all.edn |
bb -o '(for [[[group art] counts] *in*] (str (reduce + (vals counts)) " " group "/" art))' |
bb -o '(for [[[group art] counts] *input*] (str (reduce + (vals counts)) " " group "/" art))' |
sort -rn |
less
14113842 clojure-complete/clojure-complete
@ -665,6 +815,29 @@ clj-http/clj-http can be upgraded from 3.4.0 to 3.10.0
cheshire/cheshire can be upgraded from 5.8.1 to 5.9.0
```
### Convert project.clj to deps.edn
Contributed by [@plexus](https://github.com/plexus).
``` shellsession
$ cat project.clj |
sed -e 's/#=//g' -e 's/~@//g' -e 's/~//g' |
bb '(let [{:keys [dependencies source-paths resource-paths]} (apply hash-map (drop 3 *input*))]
{:paths (into source-paths resource-paths)
:deps (into {} (for [[d v] dependencies] [d {:mvn/version v}]))}) ' |
jet --pretty > deps.edn
```
### Print current time in California
See [examples/pst.clj](https://github.com/borkdude/babashka/blob/master/examples/pst.clj)
### Tiny http server
See [examples/http_server.clj](https://github.com/borkdude/babashka/blob/master/examples/http_server.clj)
Original by [@souenzzo](https://gist.github.com/souenzzo/a959a4c5b8c0c90df76fe33bb7dfe201)
## Thanks
- [adgoji](https://www.adgoji.com/) for financial support
@ -677,5 +850,3 @@ Distributed under the EPL License. See LICENSE.
This project contains code from:
- Clojure, which is licensed under the same EPL License.
- [conch](https://github.com/clj-commons/conch), which is licensed under the
same EPL License.

12
deps.edn Normal file
View file

@ -0,0 +1,12 @@
{:paths ["src" "sci/src" "resources" "sci/resources"],
:deps {org.clojure/clojure {:mvn/version "1.10.1"},
org.clojure/tools.reader {:mvn/version "1.3.2"},
borkdude/edamame {:mvn/version "0.0.10-alpha.4"},
borkdude/graal.locking {:mvn/version "0.0.2"},
borkdude/sci.impl.reflector {:mvn/version "0.0.1"}
org.clojure/core.async {:mvn/version "0.4.500"},
org.clojure/tools.cli {:mvn/version "0.4.2"},
org.clojure/data.csv {:mvn/version "0.1.4"},
cheshire {:mvn/version "5.9.0"}}
:aliases {:main
{:main-opts ["-m" "babashka.main"]}}}

51
examples/http_server.clj Executable file
View file

@ -0,0 +1,51 @@
#!/usr/bin/env bb
(import (java.net ServerSocket))
(require '[clojure.string :as string]
'[clojure.java.io :as io])
(with-open [server-socket (new ServerSocket 8080)
client-socket (.accept server-socket)]
(loop []
(let [out (io/writer (.getOutputStream client-socket))
in (io/reader (.getInputStream client-socket))
[req-line & headers] (loop [headers []]
(let [line (.readLine in)]
(if (string/blank? line)
headers
(recur (conj headers line)))))
[_ _ path _] (re-find #"([^\s]+)\s([^\s]+)\s([^\s]+)" req-line)
f (io/file (format "./%s" path))
status (if (.exists f)
200
404)
html (fn html-fn [tag & body]
(let [attrs? (map? (first body))
attrs-str (str (when attrs?
(format " %s" (string/join " " (for [[k v] (first body)]
(format "%s=%s" (name k) (name v)))))))]
(format "<%s%s>%s</%s>"
(name tag)
attrs-str
(string/join (if attrs? (rest body) body))
(name tag))))
body (cond
(not (.exists f)) ""
(.isFile f) (slurp f)
(.isDirectory f) (format "<!DOCTYPE html>\n%s"
(html :html
(html :head
(html :title path))
(html :body
(html :h1 path)
(html :tt
(apply html :pre
(for [i (.list f)]
(html :div (html :a {:href (format "\"%s\"" i)} i)))))))))]
(prn path)
(.write out (format "HTTP/1.1 %s OK\r\nContent-Length: %s\r\n\r\n%s"
status
(count body)
body))
(.flush out))
(recur)))

19
examples/process_builder.clj Executable file
View file

@ -0,0 +1,19 @@
#!/usr/bin/env bb
(require '[clojure.java.io :as io])
(import '[java.lang ProcessBuilder$Redirect])
(defn grep [input pattern]
(let [proc (-> (ProcessBuilder. ["grep" pattern])
(.redirectOutput ProcessBuilder$Redirect/INHERIT)
(.redirectError ProcessBuilder$Redirect/INHERIT)
(.start))
proc-input (.getOutputStream proc)]
(with-open [w (io/writer proc-input)]
(binding [*out* w]
(print input)
(flush)))
(.waitFor proc)
nil))
(grep "hello\nbye\n" "bye")

7
examples/pst.clj Executable file
View file

@ -0,0 +1,7 @@
#!/usr/bin/env bb
(def now (java.time.ZonedDateTime/now))
(def LA-timezone (java.time.ZoneId/of "America/Los_Angeles"))
(def LA-time (.withZoneSameInstant now LA-timezone))
(def pattern (java.time.format.DateTimeFormatter/ofPattern "HH:mm"))
(println (.format LA-time pattern))

BIN
logo/babashka.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

30
logo/babashka.svg Normal file
View file

@ -0,0 +1,30 @@
<svg width="882" height="648" viewBox="0 0 2944 2162" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1742.91 1219.89C1857.93 1138.38 1933 1004.21 1933 852.5C1933 603.972 1761 280.5 1483 139C1207.5 288 1033 603.972 1033 852.5C1033 1004.21 1108.07 1138.38 1223.09 1219.89C1218.98 1220.44 1214.95 1221.14 1211 1222C1123.61 1241.07 1017 1385.05 1017 1385.05H1281C1327.97 1385.05 1372.63 1367.41 1407.96 1347.62C1425.12 1370.32 1452.34 1385 1483 1385C1513.66 1385 1540.89 1370.32 1558.04 1347.62C1593.37 1367.41 1638.03 1385.05 1685 1385.05H1949C1949 1385.05 1842.39 1241.07 1755 1222C1751.05 1221.14 1747.02 1220.44 1742.91 1219.89Z" fill="url(#paint0_linear)"/>
<circle cx="1482.5" cy="878.5" r="311.5" fill="white"/>
<path d="M1212 745.5C1396.5 758.5 1505.5 713.5 1581 602C1581 602 1435.5 553 1347 602C1258.8 650.834 1212 745.5 1212 745.5Z" fill="#B4B4B4"/>
<path d="M1728 718C1654 710 1588 686 1556.5 595.5C1556.5 595.5 1621.93 610.435 1688 670.614C1756.62 733.114 1728 718 1728 718Z" fill="#B4B4B4"/>
<circle cx="1482.5" cy="878.5" r="311.5" stroke="#0A0000" stroke-width="40"/>
<path d="M1544 745H1421L1451 775V865C1455.5 835.5 1511.5 834.5 1514.5 865L1522 775L1544 745Z" fill="black"/>
<path d="M1098.12 785C1098.12 762.909 1116.03 745 1138.12 745H1423.02C1447.2 745 1465.85 766.292 1462.67 790.264L1437.91 976.764C1435.27 996.646 1418.31 1011.5 1398.26 1011.5H1138.12C1116.03 1011.5 1098.12 993.591 1098.12 971.5V785Z" fill="black"/>
<path d="M1216.1 967.55V951.05H1210.55C1206.95 951.05 1204.55 948.5 1203.35 944.75L1180.55 876.05C1179.05 871.7 1177.25 868.55 1174.85 866.15C1170.35 861.5 1164.5 860 1157.9 860H1150.7V877.25H1155.5C1159.55 877.25 1162.7 878.75 1164.05 883.55L1167.2 894.65L1139 967.55H1159.55L1176.5 919.55L1185.5 948.5C1188.95 959.75 1194.8 967.55 1208 967.55H1216.1Z" fill="white"/>
<path d="M1248.8 951.05V967.55H1304.8V951.05H1248.8Z" fill="white"/>
<path d="M1503.01 790.267C1499.83 766.294 1518.48 745 1542.66 745H1827C1849.09 745 1867 762.909 1867 785V971.096C1867 993.187 1849.09 1011.1 1827 1011.1H1567.38C1547.33 1011.1 1530.37 996.244 1527.73 976.362L1503.01 790.267Z" fill="black"/>
<path d="M1649.3 968V951.5H1643.75C1640.15 951.5 1637.75 948.95 1636.55 945.2L1613.75 876.5C1612.25 872.15 1610.45 869 1608.05 866.6C1603.55 861.95 1597.7 860.45 1591.1 860.45H1583.9V877.7H1588.7C1592.75 877.7 1595.9 879.2 1597.25 884L1600.4 895.1L1572.2 968H1592.75L1609.7 920L1618.7 948.95C1622.15 960.2 1628 968 1641.2 968H1649.3Z" fill="white"/>
<path d="M1682 951.5V968H1738V951.5H1682Z" fill="white"/>
<path d="M245.923 1711.64C254.47 1700.19 264.812 1691.21 276.949 1684.72C289.085 1678.05 301.991 1674.72 315.667 1674.72C349.684 1674.72 374.897 1687.37 391.308 1712.67C407.889 1737.79 416.18 1773.26 416.18 1819.08C416.18 1847.45 411.906 1872.67 403.359 1894.72C394.983 1916.6 382.59 1933.78 366.18 1946.26C349.94 1958.56 330.282 1964.72 307.205 1964.72C293.53 1964.72 280.795 1961.81 269 1956C257.376 1950.36 247.633 1942.15 239.769 1931.38L236.692 1956H164.897V1575.49L245.923 1567.03V1711.64ZM285.923 1905.74C317.034 1905.74 332.59 1877.2 332.59 1820.1C332.59 1798.22 330.88 1781.04 327.462 1768.56C324.043 1755.91 319.171 1747.11 312.846 1742.15C306.692 1737.03 299.085 1734.46 290.026 1734.46C273.274 1734.46 258.573 1744.97 245.923 1766V1880.87C251.222 1889.25 257.034 1895.49 263.359 1899.59C269.855 1903.69 277.376 1905.74 285.923 1905.74Z" fill="black"/>
<path d="M701.745 1877.54C701.745 1887.62 703.113 1895.06 705.848 1899.85C708.583 1904.46 713.198 1907.97 719.694 1910.36L703.027 1963.69C686.446 1962.15 672.686 1958.56 661.745 1952.92C650.976 1947.11 642.515 1938.74 636.361 1927.79C626.275 1939.93 613.198 1949.16 597.13 1955.49C581.233 1961.64 564.395 1964.72 546.617 1964.72C528.156 1964.72 511.916 1961.13 497.899 1953.95C484.053 1946.6 473.369 1936.51 465.848 1923.69C458.327 1910.7 454.566 1895.83 454.566 1879.08C454.566 1859.59 459.78 1842.92 470.207 1829.08C480.634 1815.23 495.763 1804.72 515.592 1797.54C535.421 1790.19 559.267 1786.51 587.13 1786.51H623.797V1776.26C623.797 1748.39 605.848 1734.46 569.951 1734.46C560.891 1734.46 549.438 1735.74 535.592 1738.31C521.916 1740.87 508.498 1744.38 495.335 1748.82L476.617 1694.97C493.711 1688.48 511.745 1683.52 530.72 1680.1C549.694 1676.51 567.045 1674.72 582.771 1674.72C622.771 1674.72 652.6 1682.84 672.258 1699.08C691.916 1715.32 701.745 1739.25 701.745 1770.87V1877.54ZM574.566 1906.77C583.797 1906.77 593.113 1904.12 602.515 1898.82C611.916 1893.35 619.01 1886 623.797 1876.77V1832.67H603.797C581.574 1832.67 565.079 1836.09 554.31 1842.92C543.54 1849.76 538.156 1859.93 538.156 1873.44C538.156 1884.03 541.318 1892.24 547.643 1898.05C554.139 1903.86 563.113 1906.77 574.566 1906.77Z" fill="black"/>
<path d="M984.204 1711.64C992.751 1700.19 1003.09 1691.21 1015.23 1684.72C1027.37 1678.05 1040.27 1674.72 1053.95 1674.72C1087.97 1674.72 1113.18 1687.37 1129.59 1712.67C1146.17 1737.79 1154.46 1773.26 1154.46 1819.08C1154.46 1847.45 1150.19 1872.67 1141.64 1894.72C1133.26 1916.6 1120.87 1933.78 1104.46 1946.26C1088.22 1958.56 1068.56 1964.72 1045.49 1964.72C1031.81 1964.72 1019.08 1961.81 1007.28 1956C995.657 1950.36 985.914 1942.15 978.05 1931.38L974.974 1956H903.179V1575.49L984.204 1567.03V1711.64ZM1024.2 1905.74C1055.32 1905.74 1070.87 1877.2 1070.87 1820.1C1070.87 1798.22 1069.16 1781.04 1065.74 1768.56C1062.32 1755.91 1057.45 1747.11 1051.13 1742.15C1044.97 1737.03 1037.37 1734.46 1028.31 1734.46C1011.55 1734.46 996.854 1744.97 984.204 1766V1880.87C989.503 1889.25 995.315 1895.49 1001.64 1899.59C1008.14 1903.69 1015.66 1905.74 1024.2 1905.74Z" fill="black"/>
<path d="M1440.03 1877.54C1440.03 1887.62 1441.39 1895.06 1444.13 1899.85C1446.86 1904.46 1451.48 1907.97 1457.98 1910.36L1441.31 1963.69C1424.73 1962.15 1410.97 1958.56 1400.03 1952.92C1389.26 1947.11 1380.8 1938.74 1374.64 1927.79C1364.56 1939.93 1351.48 1949.16 1335.41 1955.49C1319.51 1961.64 1302.68 1964.72 1284.9 1964.72C1266.44 1964.72 1250.2 1961.13 1236.18 1953.95C1222.33 1946.6 1211.65 1936.51 1204.13 1923.69C1196.61 1910.7 1192.85 1895.83 1192.85 1879.08C1192.85 1859.59 1198.06 1842.92 1208.49 1829.08C1218.92 1815.23 1234.04 1804.72 1253.87 1797.54C1273.7 1790.19 1297.55 1786.51 1325.41 1786.51H1362.08V1776.26C1362.08 1748.39 1344.13 1734.46 1308.23 1734.46C1299.17 1734.46 1287.72 1735.74 1273.87 1738.31C1260.2 1740.87 1246.78 1744.38 1233.62 1748.82L1214.9 1694.97C1231.99 1688.48 1250.03 1683.52 1269 1680.1C1287.98 1676.51 1305.33 1674.72 1321.05 1674.72C1361.05 1674.72 1390.88 1682.84 1410.54 1699.08C1430.2 1715.32 1440.03 1739.25 1440.03 1770.87V1877.54ZM1312.85 1906.77C1322.08 1906.77 1331.39 1904.12 1340.8 1898.82C1350.2 1893.35 1357.29 1886 1362.08 1876.77V1832.67H1342.08C1319.86 1832.67 1303.36 1836.09 1292.59 1842.92C1281.82 1849.76 1276.44 1859.93 1276.44 1873.44C1276.44 1884.03 1279.6 1892.24 1285.92 1898.05C1292.42 1903.86 1301.39 1906.77 1312.85 1906.77Z" fill="black"/>
<path d="M1627.64 1906.77C1641.49 1906.77 1652.43 1904.63 1660.46 1900.36C1668.67 1895.91 1672.77 1889.5 1672.77 1881.13C1672.77 1875.15 1671.15 1870.27 1667.9 1866.51C1664.65 1862.58 1658.58 1858.91 1649.7 1855.49C1640.81 1851.9 1626.96 1847.54 1608.16 1842.41C1580.81 1835.06 1559.87 1824.97 1545.34 1812.15C1530.98 1799.33 1523.8 1781.56 1523.8 1758.82C1523.8 1742.41 1528.33 1727.88 1537.39 1715.23C1546.62 1702.58 1560.12 1692.67 1577.9 1685.49C1595.68 1678.31 1617.05 1674.72 1642 1674.72C1685.25 1674.72 1722 1685.74 1752.26 1707.79L1719.95 1755.49C1708.16 1748.31 1695.76 1742.67 1682.77 1738.56C1669.95 1734.46 1657.13 1732.41 1644.31 1732.41C1631.15 1732.41 1621.32 1734.21 1614.82 1737.79C1608.5 1741.21 1605.34 1746.43 1605.34 1753.44C1605.34 1758.22 1606.96 1762.24 1610.21 1765.49C1613.63 1768.74 1619.7 1772.07 1628.41 1775.49C1637.3 1778.91 1651.06 1783.26 1669.7 1788.56C1689.35 1794.03 1705.34 1800.1 1717.64 1806.77C1729.95 1813.26 1739.52 1821.98 1746.36 1832.92C1753.37 1843.69 1756.87 1857.62 1756.87 1874.72C1756.87 1893.86 1751.06 1910.27 1739.44 1923.95C1727.99 1937.45 1712.52 1947.62 1693.03 1954.46C1673.54 1961.3 1651.92 1964.72 1628.16 1964.72C1602.69 1964.72 1579.78 1961.13 1559.44 1953.95C1539.1 1946.6 1521.58 1936.51 1506.87 1923.69L1547.64 1878.05C1558.93 1886.94 1571.32 1893.95 1584.82 1899.08C1598.33 1904.21 1612.6 1906.77 1627.64 1906.77Z" fill="black"/>
<path d="M1907.06 1710.62C1929.79 1686.68 1956.46 1674.72 1987.06 1674.72C2011.16 1674.72 2029.62 1681.81 2042.44 1696C2055.43 1710.02 2061.93 1730.02 2061.93 1756V1956H1980.9V1778.56C1980.9 1767.45 1980.05 1758.74 1978.34 1752.41C1976.8 1746.09 1974.15 1741.56 1970.39 1738.82C1966.63 1735.91 1961.41 1734.46 1954.75 1734.46C1946.54 1734.46 1938.59 1737.28 1930.9 1742.92C1923.21 1748.56 1915.26 1756.77 1907.06 1767.54V1956H1826.03V1575.49L1907.06 1567.54V1710.62Z" fill="black"/>
<path d="M2337.72 1956H2256.69V1575.49L2337.72 1567.03V1956ZM2425.67 1804.21L2522.59 1956H2430.28L2340.8 1806.77L2430.28 1683.69H2515.16L2425.67 1804.21Z" fill="black"/>
<path d="M2793.54 1877.54C2793.54 1887.62 2794.91 1895.06 2797.64 1899.85C2800.38 1904.46 2805 1907.97 2811.49 1910.36L2794.82 1963.69C2778.24 1962.15 2764.48 1958.56 2753.54 1952.92C2742.77 1947.11 2734.31 1938.74 2728.16 1927.79C2718.07 1939.93 2705 1949.16 2688.93 1955.49C2673.03 1961.64 2656.19 1964.72 2638.41 1964.72C2619.95 1964.72 2603.71 1961.13 2589.7 1953.95C2575.85 1946.6 2565.17 1936.51 2557.64 1923.69C2550.12 1910.7 2546.36 1895.83 2546.36 1879.08C2546.36 1859.59 2551.58 1842.92 2562 1829.08C2572.43 1815.23 2587.56 1804.72 2607.39 1797.54C2627.22 1790.19 2651.06 1786.51 2678.93 1786.51H2715.59V1776.26C2715.59 1748.39 2697.64 1734.46 2661.75 1734.46C2652.69 1734.46 2641.23 1735.74 2627.39 1738.31C2613.71 1740.87 2600.29 1744.38 2587.13 1748.82L2568.41 1694.97C2585.51 1688.48 2603.54 1683.52 2622.52 1680.1C2641.49 1676.51 2658.84 1674.72 2674.57 1674.72C2714.57 1674.72 2744.4 1682.84 2764.06 1699.08C2783.71 1715.32 2793.54 1739.25 2793.54 1770.87V1877.54ZM2666.36 1906.77C2675.59 1906.77 2684.91 1904.12 2694.31 1898.82C2703.71 1893.35 2710.81 1886 2715.59 1876.77V1832.67H2695.59C2673.37 1832.67 2656.88 1836.09 2646.11 1842.92C2635.34 1849.76 2629.95 1859.93 2629.95 1873.44C2629.95 1884.03 2633.11 1892.24 2639.44 1898.05C2645.94 1903.86 2654.91 1906.77 2666.36 1906.77Z" fill="black"/>
<path d="M862 1819C862 1848.27 838.271 1872 809 1872C779.729 1872 756 1848.27 756 1819C756 1789.73 779.729 1766 809 1766C838.271 1766 862 1789.73 862 1819Z" fill="black"/>
<path d="M2215.5 1819C2215.5 1848.27 2191.77 1872 2162.5 1872C2133.23 1872 2109.5 1848.27 2109.5 1819C2109.5 1789.73 2133.23 1766 2162.5 1766C2191.77 1766 2215.5 1789.73 2215.5 1819Z" fill="black"/>
<defs>
<linearGradient id="paint0_linear" x1="1499" y1="139" x2="1499" y2="1385.52" gradientUnits="userSpaceOnUse">
<stop stop-color="#E41F26"/>
<stop offset="1" stop-color="#B70000"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

BIN
logo/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

18
logo/icon.svg Normal file
View file

@ -0,0 +1,18 @@
<svg width="128" height="128" viewBox="0 0 1472 1472" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M995.911 1193.89C1110.93 1112.38 1186 978.206 1186 826.5C1186 577.972 1014 254.5 736 113C460.5 262 286 577.972 286 826.5C286 978.206 361.07 1112.38 476.089 1193.89C471.983 1194.44 467.949 1195.14 464 1196C376.611 1215.07 270 1359.05 270 1359.05H534C580.972 1359.05 625.631 1341.41 660.956 1321.62C678.115 1344.32 705.344 1359 736 1359C766.656 1359 793.885 1344.32 811.044 1321.62C846.369 1341.41 891.028 1359.05 938 1359.05H1202C1202 1359.05 1095.39 1215.07 1008 1196C1004.05 1195.14 1000.02 1194.44 995.911 1193.89Z" fill="url(#paint0_linear)"/>
<circle cx="735.5" cy="852.5" r="311.5" fill="white"/>
<path d="M443 750.417C662 765.866 791.382 712.388 881 579.881C881 579.881 708.293 521.649 603.244 579.881C498.55 637.916 443 750.417 443 750.417Z" fill="#B4B4B4"/>
<path d="M1039.99 748.282C914.862 734.745 803.264 694.135 750 541C750 541 860.633 566.272 972.355 668.101C1088.39 773.857 1039.99 748.282 1039.99 748.282Z" fill="#B4B4B4"/>
<circle cx="735.5" cy="852.5" r="311.5" stroke="#0A0000" stroke-width="40"/>
<path d="M797 719H674L704 749V839C708.5 809.5 764.5 808.5 767.5 839L775 749L797 719Z" fill="black"/>
<path d="M351.117 759C351.117 736.909 369.026 719 391.117 719H676.018C700.2 719 718.852 740.292 715.67 764.264L690.91 950.764C688.27 970.646 671.314 985.5 651.258 985.5H391.117C369.026 985.5 351.117 967.591 351.117 945.5V759Z" fill="black"/>
<path d="M756.012 764.267C752.828 740.294 771.481 719 795.664 719H1080C1102.09 719 1120 736.909 1120 759V945.096C1120 967.187 1102.09 985.096 1080 985.096H820.381C800.325 985.096 783.37 970.244 780.729 950.362L756.012 764.267Z" fill="black"/>
<path d="M1024 952V921.47H1013.71C1007.03 921.47 1002.58 916.752 1000.35 909.813L958.064 782.697C955.282 774.649 951.944 768.82 947.492 764.379C939.146 755.775 928.296 753 916.054 753H902.7V784.918H911.603C919.115 784.918 924.957 787.693 927.461 796.575L933.303 817.113L881 952H919.115L950.553 863.185L967.245 916.752C973.644 937.568 984.494 952 1008.98 952H1024Z" fill="white"/>
<path d="M590 952V921.47H579.706C573.029 921.47 568.578 916.752 566.352 909.813L524.064 782.697C521.282 774.649 517.944 768.82 513.492 764.379C505.146 755.775 494.296 753 482.054 753H468.7V784.918H477.603C485.115 784.918 490.957 787.693 493.461 796.575L499.303 817.113L447 952H485.115L516.553 863.185L533.245 916.752C539.644 937.568 550.494 952 574.977 952H590Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear" x1="752" y1="113" x2="752" y2="1359.52" gradientUnits="userSpaceOnUse">
<stop stop-color="#E41F26"/>
<stop offset="1" stop-color="#B70000"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -11,7 +11,9 @@
:resource-paths ["resources" "sci/resources"]
:dependencies [[org.clojure/clojure "1.10.1"]
[org.clojure/tools.reader "1.3.2"]
[borkdude/edamame "0.0.10-alpha.2"]
[borkdude/edamame "0.0.10-alpha.4"]
[borkdude/graal.locking "0.0.2"]
[borkdude/sci.impl.reflector "0.0.1"]
[org.clojure/core.async "0.4.500"]
[org.clojure/tools.cli "0.4.2"]
[org.clojure/data.csv "0.1.4"]
@ -22,7 +24,8 @@
:jvm-opts ["-Dclojure.compiler.direct-linking=true"
"-Dclojure.spec.skip-macros=true"]
:main babashka.main
:aot :all}}
:aot :all}
:reflection {:main babashka.impl.classes/generate-reflection-file}}
:aliases {"bb" ["run" "-m" "babashka.main"]}
:deploy-repositories [["clojars" {:url "https://clojars.org/repo"
:username :env/clojars_user

View file

@ -1,189 +0,0 @@
[
{
"name":"java.lang.ArithmeticException",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.lang.AssertionError",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.lang.Boolean",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.io.BufferedWriter",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.io.BufferedReader",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name": "java.lang.Class",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true
},
{
"name":"java.lang.Double",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.lang.Exception",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name": "clojure.lang.ExceptionInfo",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.lang.Integer",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.io.File",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"clojure.lang.LineNumberingPushbackReader",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.util.concurrent.LinkedBlockingQueue",
"allPublicMethods":true
},
{
"name":"java.util.regex.Pattern",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.lang.Process",
"allPublicMethods":true
},
{
"name":"java.lang.ProcessBuilder",
"allPublicConstructors":true
},
{
"name":"java.lang.String",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.io.StringReader",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.io.StringWriter",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.lang.System",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.lang.Thread",
"methods": [{"name":"run"},{"name":"toString"},{"name":"isInterrupted"},{"name":"currentThread"},{"name":"getName"},{"name":"join"},{"name":"getThreadGroup"},{"name":"getStackTrace"},{"name":"holdsLock"},{"name":"checkAccess"},{"name":"dumpStack"},{"name":"yield"},{"name":"setPriority"},{"name":"setDaemon"},{"name":"start"},{"name":"sleep"},{"name":"interrupt"},{"name":"interrupted"},{"name":"isAlive"},{"name":"getPriority"},{"name":"setName"},{"name":"activeCount"},{"name":"enumerate"},{"name":"isDaemon"},{"name":"getContextClassLoader"},{"name":"setContextClassLoader"},{"name":"getAllStackTraces"},{"name":"getId"},{"name":"getState"},{"name":"setDefaultUncaughtExceptionHandler"},{"name":"getDefaultUncaughtExceptionHandler"},{"name":"getUncaughtExceptionHandler"},{"name":"setUncaughtExceptionHandler"}],
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.lang.UNIXProcess",
"allPublicMethods":true
},
{
"name":"java.nio.file.attribute.FileAttribute",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.nio.file.attribute.PosixFilePermission",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.nio.file.attribute.PosixFilePermissions",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.nio.file.Path",
"allPublicMethods":true
},
{
"name":"java.nio.file.CopyOption",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.nio.file.FileAlreadyExistsException",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.nio.file.Files",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.nio.file.NoSuchFileException",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"java.nio.file.StandardCopyOption",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{
"name":"sun.nio.fs.UnixPath",
"allPublicMethods":true,
"allPublicFields": true,
"allPublicConstructors": true
},
{"name":"com.sun.xml.internal.stream.XMLInputFactoryImpl",
"methods":[{"name":"<init>","parameterTypes":[] }]},
{"name":"com.sun.xml.internal.stream.XMLOutputFactoryImpl",
"methods":[{"name":"<init>","parameterTypes":[] }]}
]

View file

@ -1 +1 @@
0.0.42
0.0.60

View file

@ -1 +1 @@
0.0.43-SNAPSHOT
0.0.61-SNAPSHOT

2
sci

@ -1 +1 @@
Subproject commit 07d28ee572e90a629e01b10aa5b98cb33ccdc1e5
Subproject commit 57b584ba0a6a1f74a887d350463a700976dd37d8

60
script/bump_version.clj Executable file
View file

@ -0,0 +1,60 @@
#!/usr/bin/env bb
(ns bump-version
(:require [clojure.java.io :as io]
[clojure.string :as str]))
(import '[java.lang ProcessBuilder$Redirect])
(defn shell-command
"Executes shell command. Exits script when the shell-command has a non-zero exit code, propagating it.
Accepts the following options:
`:input`: instead of reading from stdin, read from this string.
`:to-string?`: instead of writing to stdoud, write to a string and
return it."
([args] (shell-command args nil))
([args {:keys [:input :to-string?]}]
(let [args (mapv str args)
pb (cond-> (-> (ProcessBuilder. ^java.util.List args)
(.redirectError ProcessBuilder$Redirect/INHERIT))
(not to-string?) (.redirectOutput ProcessBuilder$Redirect/INHERIT)
(not input) (.redirectInput ProcessBuilder$Redirect/INHERIT))
proc (.start pb)]
(when input
(with-open [w (io/writer (.getOutputStream proc))]
(binding [*out* w]
(print input)
(flush))))
(let [string-out
(when to-string?
(let [sw (java.io.StringWriter.)]
(with-open [w (io/reader (.getInputStream proc))]
(io/copy w sw))
(str sw)))
exit-code (.waitFor proc)]
(when-not (zero? exit-code)
(System/exit exit-code))
string-out))))
(def version-file (io/file "resources" "BABASHKA_VERSION"))
(def released-version-file (io/file "resources" "BABASHKA_RELEASED_VERSION"))
(case (first *command-line-args*)
"release" (let [version-string (str/trim (slurp version-file))
[major minor patch] (str/split version-string #"\.")
patch (str/replace patch "-SNAPSHOT" "")
new-version (str/join "." [major minor patch])]
(spit version-file new-version)
(shell-command ["git" "commit" "-a" "-m" (str "v" new-version)])
(shell-command ["git" "diff" "HEAD^" "HEAD"]))
"post-release" (do
(io/copy version-file released-version-file)
(let [version-string (str/trim (slurp version-file))
[major minor patch] (str/split version-string #"\.")
patch (Integer. patch)
patch (str (inc patch) "-SNAPSHOT")
new-version (str/join "." [major minor patch])]
(spit version-file new-version)
(shell-command ["git" "commit" "-a" "-m" "Version bump"])
(shell-command ["git" "diff" "HEAD^" "HEAD"])))
(println "Expected: release | post-release."))

View file

@ -23,7 +23,9 @@ BABASHKA_VERSION=$(cat resources/BABASHKA_VERSION)
# mkdir -p src/sci
# cp -R /tmp/sci/src/* src
lein with-profiles +reflection do run
lein do clean, uberjar
$NATIVE_IMAGE \
-jar target/babashka-$BABASHKA_VERSION-standalone.jar \
-H:Name=bb \

View file

@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -eo pipefail
export BABASHKA_CLASSPATH=$(clojure -Sdeps '{:deps {clj-http-lite {:git/url "https://github.com/borkdude/clj-http-lite" :sha "f44ebe45446f0f44f2b73761d102af3da6d0a13e"}}}' -Spath)
./bb -e "
(require '[clj-http.lite.client :as client])
(prn (:status (client/get \"https://www.clojure.org\")))
(prn (:status (client/get \"https://postman-echo.com/get?foo1=bar1&foo2=bar2\")))
(prn (:status (client/post \"https://postman-echo.com/post\")))
(prn (:status (client/put \"https://postman-echo.com/put\")))
"

9
script/lib_tests/deps_clj_test Executable file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -eo pipefail
curl -sL https://raw.githubusercontent.com/borkdude/deps.clj/master/deps.clj -o deps_test.clj
chmod +x deps_test.clj
./bb deps_test.clj -Sdescribe
rm deps_test.clj

View file

@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -eo pipefail
export BABASHKA_CLASSPATH=$(clojure -Sdeps '{:deps {spartan.spec {:git/url "https://github.com/borkdude/spartan.spec" :sha "16f7eec4b6589c77c96c9fcf989f78fffcee7c4c"}}}' -Spath)
./bb -e "
(require '[spartan.spec :as s])
(s/explain (s/cat :i int? :s string?) [1 :foo])
(s/conform (s/cat :i int? :s string?) [1 \"foo\"])
"

7
script/run_lib_tests Executable file
View file

@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -eo pipefail
script/lib_tests/clj_http_lite_test
script/lib_tests/deps_clj_test
script/lib_tests/spartan_spec_test

View file

@ -1,219 +0,0 @@
#!/usr/bin/env bash
set -e
function join { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }
# Extract opts
print_classpath=false
describe=false
verbose=false
force=false
repro=false
tree=false
pom=false
resolve_tags=false
help=false
resolve_aliases=()
classpath_aliases=()
main_aliases=()
all_aliases=()
while [ $# -gt 0 ]
do
case "$1" in
-J*)
shift
;;
-R*)
resolve_aliases+=("${1:2}")
shift
;;
-C*)
classpath_aliases+=("${1:2}")
shift
;;
-O*)
shift
;;
-M*)
main_aliases+=("${1:2}")
shift
;;
-A*)
all_aliases+=("${1:2}")
shift
;;
-Sdeps)
shift
deps_data="${1}"
shift
;;
-Scp)
shift
force_cp="${1}"
shift
;;
-Spath)
print_classpath=true
shift
;;
-Sverbose)
verbose=true
shift
;;
-Sdescribe)
describe=true
shift
;;
-Sforce)
force=true
shift
;;
-Srepro)
repro=true
shift
;;
-Stree)
tree=true
shift
;;
-Spom)
pom=true
shift
;;
-Sresolve-tags)
resolve_tags=true
shift
;;
-S*)
echo "Invalid option: $1"
exit 1
;;
-h|--help|"-?")
if [[ ${#main_aliases[@]} -gt 0 ]] || [[ ${#all_aliases[@]} -gt 0 ]]; then
break
else
help=true
shift
fi
;;
*)
break
;;
esac
done
# Find clojure executable
set +e
CLOJURE_CMD=$(type -p clojure)
set -e
if [[ ! -n "$CLOJURE_CMD" ]]; then
>&2 echo "Couldn't find 'clojure'."
>&2 echo "You can launch Babashka directly using 'bb'."
>&2 echo "To use 'bbk', please ensure 'clojure' is installed and on"
>&2 echo "your path. See https://clojure.org/guides/getting_started"
exit 1
fi
if "$help"; then
cat <<-END
Usage: bbk [dep-opt*] [bb-opt*] [arg*]
The bbk script is a runner for Babashka which ultimately constructs and
invokes a command-line of the form:
bb --classpath classpath [bb-opt*] [*args]
The dep-opts are used to build the classpath using the clojure tool:
-Ralias... Concatenated resolve-deps aliases, ex: -R:bench:1.9
-Calias... Concatenated make-classpath aliases, ex: -C:dev
-Malias... Concatenated main option aliases, ex: -M:test
-Aalias... Concatenated aliases of any kind, ex: -A:dev:mem
-Sdeps EDN Deps data to use as the final deps file
-Spath Compute classpath and echo to stdout only
-Scp CP Do NOT compute or cache classpath, use this one instead
-Srepro Ignore the ~/.clojure/deps.edn config file
-Sforce Force recomputation of the classpath (don't use the cache)
-Spom Generate (or update existing) pom.xml with deps and paths
-Stree Print dependency tree
-Sresolve-tags Resolve git coordinate tags to shas and update deps.edn
-Sverbose Print important path info to console
-Sdescribe Print environment and command parsing info as data
Additionally, for compatibility with clojure, -Jopt and -Oalias... dep-opts
are accepted but ignored.
Babashka options:
END
bb -h | tail -n +9
exit 0
fi
# Execute resolve-tags command
if "$resolve_tags"; then
"$CLOJURE_CMD" -Sresolve-tags
exit
fi
clojure_args=()
if [[ -n "$deps_data" ]]; then
clojure_args+=("-Sdeps" "$deps_data")
fi
if [[ ${#resolve_aliases[@]} -gt 0 ]]; then
clojure_args+=("-R$(join '' ${resolve_aliases[@]})")
fi
if [[ ${#classpath_aliases[@]} -gt 0 ]]; then
clojure_args+=("-C$(join '' ${classpath_aliases[@]})")
fi
if [[ ${#main_aliases[@]} -gt 0 ]]; then
clojure_args+=("-M$(join '' ${main_aliases[@]})")
fi
if [[ ${#all_aliases[@]} -gt 0 ]]; then
clojure_args+=("-A$(join '' ${all_aliases[@]})")
fi
if "$repro"; then
clojure_args+=("-Srepro")
fi
if "$force"; then
clojure_args+=("-Sforce")
fi
if "$pom"; then
if "$verbose"; then
clojure_args+=("-Sverbose")
fi
"$CLOJURE_CMD" "${clojure_args[@]}" -Spom
elif "$describe"; then
if "$verbose"; then
clojure_args+=("-Sverbose")
fi
"$CLOJURE_CMD" "${clojure_args[@]}" -Sdescribe
elif "$tree"; then
if "$verbose"; then
clojure_args+=("-Sverbose")
fi
"$CLOJURE_CMD" "${clojure_args[@]}" -Stree
else
set -f
if [[ -n "$force_cp" ]]; then
cp="$force_cp"
else
if "$verbose"; then
"$CLOJURE_CMD" "${clojure_args[@]}" -Sverbose -e nil
fi
cp=`"$CLOJURE_CMD" "${clojure_args[@]}" -Spath`
fi
if "$print_classpath"; then
echo $cp
else
if [[ ${#main_aliases[@]} -gt 0 ]] || [[ ${#all_aliases[@]} -gt 0 ]]; then
# Attempt to extract the main cache filename by parsing the output of -Sverbose
cp_file=`"$CLOJURE_CMD" "${clojure_args[@]}" -Sverbose -Spath | grep cp_file | cut -d = -f 2 | sed 's/^ *//g'`
main_file="${cp_file%.cp}.main"
fi
if [[ -e "$main_file" ]]; then
main_cache_opts=($(cat "$main_file"))
fi
exec bb --classpath "$cp" "${main_cache_opts[@]}" "$@"
fi
fi

View file

@ -1,15 +0,0 @@
(ns babashka.impl.Boolean
{:no-doc true}
(:refer-clojure :exclude [list]))
(set! *warn-on-reflection* true)
(defn parseBoolean [^String x]
(Boolean/parseBoolean x))
(def boolean-bindings
{'Boolean/parseBoolean parseBoolean})
(comment
)

View file

@ -1,15 +0,0 @@
(ns babashka.impl.Double
{:no-doc true}
(:refer-clojure :exclude [list]))
(set! *warn-on-reflection* true)
(defn parseDouble [^String x]
(Double/parseDouble x))
(def double-bindings
{'Double/parseDouble parseDouble})
(comment
)

View file

@ -11,4 +11,8 @@
'generate-smile json/generate-smile
'decode json/decode
'parse-string json/parse-string
'parse-smile json/parse-smile
'parse-stream json/parse-stream
'parsed-seq json/parsed-seq
'parsed-smile-seq json/parsed-smile-seq
'decode-smile json/decode-smile})

View file

@ -0,0 +1,232 @@
(ns babashka.impl.classes
{:no-doc true}
(:require
[cheshire.core :as json]
#_[clojure.string :as str]))
;; (def os-name (str/lower-case (System/getProperty "os.name")))
;; (def os (cond (str/includes? os-name "mac") :mac
;; (or (str/includes? os-name "nix")
;; (str/includes? os-name "nux")) :linux
;; (str/includes? os-name "win") :windows))
;; (def unix-like? (or (identical? os :linux)
;; (identical? os :mac)))
(def classes
'{:all [java.io.BufferedReader
java.io.BufferedWriter
java.io.ByteArrayInputStream
java.io.ByteArrayOutputStream
java.io.File
java.io.InputStream
java.io.IOException
java.io.OutputStream
java.io.StringReader
java.io.StringWriter
java.lang.ArithmeticException
java.lang.AssertionError
java.lang.Boolean
java.lang.Class
java.lang.Double
java.lang.Exception
java.lang.Integer
java.lang.Math
java.util.concurrent.LinkedBlockingQueue
java.lang.Object
java.lang.String
java.lang.System
java.lang.Throwable
java.lang.Process
java.lang.ProcessBuilder
java.lang.ProcessBuilder$Redirect
java.net.URI
java.net.HttpURLConnection
java.net.ServerSocket
java.net.Socket
java.net.UnknownHostException
java.net.URLEncoder
java.net.URLDecoder
java.nio.file.CopyOption
java.nio.file.FileAlreadyExistsException
java.nio.file.Files
java.nio.file.LinkOption
java.nio.file.NoSuchFileException
java.nio.file.Path
java.nio.file.Paths
java.nio.file.StandardCopyOption
java.nio.file.attribute.FileAttribute
java.nio.file.attribute.FileTime
java.nio.file.attribute.PosixFilePermission
java.nio.file.attribute.PosixFilePermissions
java.time.format.DateTimeFormatter
java.time.Clock
java.time.DateTimeException
java.time.DayOfWeek
java.time.Duration
java.time.Instant
java.time.LocalDate
java.time.LocalDateTime
java.time.LocalTime
java.time.Month
java.time.MonthDay
java.time.OffsetDateTime
java.time.OffsetTime
java.time.Period
java.time.Year
java.time.YearMonth
java.time.ZonedDateTime
java.time.ZoneId
java.time.ZoneOffset
java.time.temporal.TemporalAccessor
java.util.regex.Pattern
java.util.Base64
java.util.Base64$Decoder
java.util.Base64$Encoder
java.util.Date
java.util.UUID
java.util.concurrent.TimeUnit
java.util.zip.InflaterInputStream
java.util.zip.DeflaterInputStream
java.util.zip.GZIPInputStream
java.util.zip.GZIPOutputStream]
:constructors [clojure.lang.Delay
clojure.lang.MapEntry
clojure.lang.LineNumberingPushbackReader]
:methods [borkdude.graal.LockFix ;; support for locking
]
:fields [clojure.lang.PersistentQueue]
:instance-checks [clojure.lang.ExceptionInfo
clojure.lang.IObj
clojure.lang.IEditableCollection]
:custom {clojure.lang.LineNumberingPushbackReader {:allPublicConstructors true
:allPublicMethods true}
java.lang.Thread
{:allPublicConstructors true
;; generated with `public-declared-method-names`, see in
;; `comment` below
:methods [{:name "activeCount"}
{:name "checkAccess"}
{:name "currentThread"}
{:name "dumpStack"}
{:name "enumerate"}
{:name "getAllStackTraces"}
{:name "getContextClassLoader"}
{:name "getDefaultUncaughtExceptionHandler"}
{:name "getId"}
{:name "getName"}
{:name "getPriority"}
{:name "getStackTrace"}
{:name "getState"}
{:name "getThreadGroup"}
{:name "getUncaughtExceptionHandler"}
{:name "holdsLock"}
{:name "interrupt"}
{:name "interrupted"}
{:name "isAlive"}
{:name "isDaemon"}
{:name "isInterrupted"}
{:name "join"}
{:name "run"}
{:name "setContextClassLoader"}
{:name "setDaemon"}
{:name "setDefaultUncaughtExceptionHandler"}
{:name "setName"}
{:name "setPriority"}
{:name "setUncaughtExceptionHandler"}
{:name "sleep"}
{:name "start"}
{:name "toString"}
{:name "yield"}]}
java.net.URL
{:allPublicConstructors true
:allPublicFields true
;; generated with `public-declared-method-names`, see in
;; `comment` below
:methods [{:name "equals"}
{:name "getAuthority"}
{:name "getContent"}
{:name "getDefaultPort"}
{:name "getFile"}
{:name "getHost"}
{:name "getPath"}
{:name "getPort"}
{:name "getProtocol"}
{:name "getQuery"}
{:name "getRef"}
{:name "getUserInfo"}
{:name "hashCode"}
{:name "openConnection"}
{:name "openStream"}
{:name "sameFile"}
;; not supported: {:name "setURLStreamHandlerFactory"}
{:name "toExternalForm"}
{:name "toString"}
{:name "toURI"}]}}})
(defmacro gen-class-map []
(let [classes (concat (:all classes)
(keys (:custom classes))
(:constructors classes)
(:methods classes)
(:fields classes)
(:instance-checks classes))
m (apply hash-map
(for [c classes
c [(list 'quote c) c]]
c))]
(assoc m :public-class
(fn [v]
(cond (instance? java.nio.file.Path v)
java.nio.file.Path
(instance? java.lang.Process v)
java.lang.Process)))))
(def class-map (gen-class-map))
(defn generate-reflection-file
"Generate reflection.json file"
[& args]
(let [entries (vec (for [c (sort (:all classes))
:let [class-name (str c)]]
{:name class-name
:allPublicMethods true
:allPublicFields true
:allPublicConstructors true}))
constructors (vec (for [c (sort (:constructors classes))
:let [class-name (str c)]]
{:name class-name
:allPublicConstructors true}))
methods (vec (for [c (sort (:methods classes))
:let [class-name (str c)]]
{:name class-name
:allPublicMethods true}))
fields (vec (for [c (sort (:fields classes))
:let [class-name (str c)]]
{:name class-name
:allPublicFields true}))
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)]
(spit (or
(first args)
"reflection.json") (json/generate-string all-entries {:pretty true}))))
(comment
(defn public-declared-method? [c m]
(and (= c (.getDeclaringClass m))
(not (.getAnnotation m Deprecated))))
(defn public-declared-method-names [c]
(->> (.getMethods c)
(keep (fn [m]
(when (public-declared-method? c m)
{:name (.getName m)})) )
(distinct)
(sort-by :name)
(vec)))
(public-declared-method-names java.lang.UNIXProcess)
(public-declared-method-names java.net.URL)
)

View file

@ -7,29 +7,37 @@
(set! *warn-on-reflection* true)
(defprotocol IResourceResolver
(getResource [this path]))
(getResource [this path opts]))
(deftype DirectoryResolver [path]
IResourceResolver
(getResource [this resource-path]
(getResource [this resource-path {:keys [:url?]}]
(let [f (io/file path resource-path)]
(when (.exists f)
(slurp f)))))
(if url?
(java.net.URL. (str "file:"
(.getCanonicalPath f)))
{:file (.getCanonicalPath f)
:source (slurp f)})))))
(defn path-from-jar
[^java.io.File jar-file path]
[^java.io.File jar-file path {:keys [:url?]}]
(with-open [jar (JarFile. jar-file)]
(let [entries (enumeration-seq (.entries jar))
entry (some (fn [^JarFile$JarFileEntry x]
(let [nm (.getName x)]
(when (and (not (.isDirectory x)) (= path nm))
(slurp (.getInputStream jar x))))) entries)]
(if url?
(java.net.URL.
(str "jar:file:" (.getCanonicalPath jar-file) "!/" path))
{:file path
:source (slurp (.getInputStream jar x))})))) entries)]
entry)))
(deftype JarFileResolver [path]
IResourceResolver
(getResource [this resource-path]
(path-from-jar path resource-path)))
(getResource [this resource-path opts]
(path-from-jar path resource-path opts)))
(defn part->entry [part]
(if (str/ends-with? part ".jar")
@ -38,25 +46,25 @@
(deftype Loader [entries]
IResourceResolver
(getResource [this resource-path]
(some #(getResource % resource-path) entries)))
(getResource [this resource-path opts]
(some #(getResource % resource-path opts) entries)))
(defn loader [^String classpath]
(let [parts (.split classpath (System/getProperty "path.separator"))
entries (map part->entry parts)]
(Loader. entries)))
(defn source-for-namespace [loader namespace]
(defn source-for-namespace [loader namespace opts]
(let [ns-str (name namespace)
^String ns-str (munge ns-str)
path (.replace ns-str "." (System/getProperty "file.separator"))
paths (map #(str path %) [".bb" ".clj" ".cljc"])]
(some #(getResource loader %) paths)))
(some #(getResource loader % opts) paths)))
;;;; Scratch
(comment
(def l (loader "src:/Users/borkdude/.m2/repository/cheshire/cheshire/5.9.0/cheshire-5.9.0.jar"))
(source-for-namespace l 'babashka.impl.cheshire)
(source-for-namespace l 'cheshire.core)
(source-for-namespace l 'babashka.impl.cheshire nil)
(:file (source-for-namespace l 'cheshire.core nil))
)

View file

@ -1,14 +1,22 @@
(ns babashka.impl.clojure.core
{:no-doc true}
(:refer-clojure :exclude [future]))
(:refer-clojure :exclude [future])
(:require [borkdude.graal.locking :as locking]))
(defn locking* [form bindings v f & args]
(apply @#'locking/locking form bindings v f args))
(def core-extras
{'file-seq file-seq
'agent agent
'instance? instance? ;; TODO: move to sci
'send send
'send-off send-off
'promise promise
'deliver deliver
'locking (with-meta locking* {:sci/macro true})
'shutdown-agents shutdown-agents
'slurp slurp
'spit spit})
'spit spit
'Throwable->map Throwable->map
'compare-and-set! compare-and-set!})

View file

@ -14,10 +14,11 @@
:no-doc true}
babashka.impl.clojure.core.server
(:refer-clojure :exclude [locking])
(:require [sci.core :as sci])
(:import
[clojure.lang LineNumberingPushbackReader]
[java.net InetAddress Socket ServerSocket SocketException]
[java.io Reader Writer PrintWriter BufferedWriter BufferedReader InputStreamReader OutputStreamWriter]))
[java.io BufferedWriter InputStreamReader OutputStreamWriter]))
(set! *warn-on-reflection* true)
@ -41,9 +42,9 @@
args - to pass to accept-fn"
[^Socket conn client-id in out err accept args]
(try
(binding [*in* in
*out* out
*err* err]
(sci/with-bindings {sci/in in
sci/out out
sci/err err}
(swap! server assoc-in [:sessions client-id] {})
(apply accept args))
(catch SocketException _disconnect)

View file

@ -1,8 +1,10 @@
(ns babashka.impl.clojure.java.io
{:no-doc true}
(:require [clojure.java.io :as io]))
(def io-namespace
{'as-relative-path io/as-relative-path
'as-url io/as-url
'copy io/copy
'delete-file io/delete-file
'file io/file

View file

@ -0,0 +1,42 @@
(ns babashka.impl.clojure.java.shell
{:no-doc true}
(:require [clojure.java.shell :as shell]
[sci.core :as sci]))
(def sh-dir (sci/new-dynamic-var '*sh-dir* nil))
(def sh-env (sci/new-dynamic-var '*sh-env* nil))
(defn with-sh-dir*
[_ _ dir & forms]
`(binding [clojure.java.shell/*sh-dir* ~dir]
~@forms))
(defn with-sh-env*
[_ _ env & forms]
`(binding [clojure.java.shell/*sh-env* ~env]
~@forms))
(defn parse-args
[args]
(let [default-encoding "UTF-8" ;; see sh doc string
default-opts {:out-enc default-encoding
:in-enc default-encoding
:dir @sh-dir
:env @sh-env}
[cmd opts] (split-with string? args)
opts (merge default-opts (apply hash-map opts))
opts-seq (apply concat opts)]
(concat cmd opts-seq)))
(defn sh [& args]
(let [args (parse-args args)]
(apply shell/sh args)))
(def shell-namespace
{'*sh-dir* sh-dir
'*sh-env* sh-env
'with-sh-dir (with-meta with-sh-dir*
{:sci/macro true})
'with-sh-env (with-meta with-sh-env*
{:sci/macro true})
'sh sh})

View file

@ -15,9 +15,13 @@
:author "Stephen C. Gilardi and Rich Hickey"
:no-doc true}
babashka.impl.clojure.main
(:refer-clojure :exclude [with-bindings])
(:import (java.io StringReader)
(clojure.lang LineNumberingPushbackReader LispReader$ReaderException)))
(:refer-clojure :exclude [with-bindings]))
(defn demunge
"Given a string representation of a fn class,
as in a stack trace element, returns a readable version."
[fn-name]
(clojure.lang.Compiler/demunge fn-name))
(defmacro with-bindings
"Executes body in the context of thread-local bindings for several vars
@ -44,18 +48,6 @@
*e nil]
~@body))
(defn repl-prompt
"Default :prompt hook for repl"
[]
(print "bb=> " ))
(defn repl-caught
"Default :caught hook for repl"
[e]
(binding [*out* *err*]
(println (.getMessage ^Exception e))
(flush)))
(defn repl
"Generic, reusable, read-eval-print loop. By default, reads from *in*,
writes to *out*, and prints exception summaries to *err*. If you use the
@ -92,14 +84,7 @@
read, eval, or print throws an exception or error
default: repl-caught"
[& options]
(let [{:keys [init need-prompt prompt flush read eval print caught]
:or {need-prompt (if (instance? LineNumberingPushbackReader *in*)
#(.atLineStart ^LineNumberingPushbackReader *in*)
#(identity true))
prompt repl-prompt
flush flush
print prn
caught repl-caught}}
(let [{:keys [init need-prompt prompt flush read eval print caught]}
(apply hash-map options)
request-prompt (Object.)
request-exit (Object.)

View file

@ -80,9 +80,9 @@
(print "Caused by: " )
(recur cause n))))
(defn e
"REPL utility. Prints a brief stack trace for the root cause of the
most recent exception."
{:added "1.1"}
[]
(print-stack-trace (root-cause *e) 8))
(def stacktrace-namespace
{'root-cause root-cause
'print-trace-element print-trace-element
'print-throwable print-throwable
'print-stack-trace print-stack-trace
'print-cause-trace print-cause-trace})

View file

@ -1,18 +0,0 @@
(ns babashka.impl.conch
{:no-doc true}
(:require
[babashka.impl.me.raynes.conch.low-level :as ll]))
(def conch-namespace
{;; low level API
'proc ll/proc
'destroy ll/destroy
'exit-code ll/exit-code
'flush ll/flush
'done ll/done
'stream-to ll/stream-to
'feed-from ll/feed-from
'stream-to-string ll/stream-to-string
'stream-to-out ll/stream-to-out
'feed-from-string ll/feed-from-string
'read-line ll/read-line})

View file

@ -1,7 +1 @@
(ns babashka.impl.exceptions)
(def exception-bindings
{'ArithmeticException ArithmeticException
'java.lang.ArithmeticException ArithmeticException
'java.lang.AssertionError AssertionError
'AssertionError AssertionError})

View file

@ -1,126 +0,0 @@
;; From https://github.com/clj-commons/conch
(ns babashka.impl.me.raynes.conch.low-level
"A simple but flexible library for shelling out from Clojure."
{:no-doc true}
(:refer-clojure :exclude [flush read-line])
(:require [clojure.java.io :as io])
(:import [java.util.concurrent TimeUnit TimeoutException]
[java.io InputStream OutputStream]))
(set! *warn-on-reflection* true)
(defn proc
"Spin off another process. Returns the process's input stream,
output stream, and err stream as a map of :in, :out, and :err keys
If passed the optional :dir and/or :env keyword options, the dir
and enviroment will be set to what you specify. If you pass
:verbose and it is true, commands will be printed. If it is set to
:very, environment variables passed, dir, and the command will be
printed. If passed the :clear-env keyword option, then the process
will not inherit its environment from its parent process."
[& args]
(let [[cmd args] (split-with (complement keyword?) args)
args (apply hash-map args)
builder (ProcessBuilder. ^"[Ljava.lang.String;" (into-array String cmd))
env (.environment builder)]
(when (:clear-env args)
(.clear env))
(doseq [[k v] (:env args)]
(.put env k v))
(when-let [dir (:dir args)]
(.directory builder (io/file dir)))
(when (:verbose args) (apply println cmd))
(when (= :very (:verbose args))
(when-let [env (:env args)] (prn env))
(when-let [dir (:dir args)] (prn dir)))
(when (:redirect-err args)
(.redirectErrorStream builder true))
(let [process (.start builder)]
{:out (.getInputStream process)
:in (.getOutputStream process)
:err (.getErrorStream process)
:process process})))
(defn destroy
"Destroy a process."
[process]
(.destroy ^Process (:process process)))
;; .waitFor returns the exit code. This makes this function useful for
;; both getting an exit code and stopping the thread until a process
;; terminates.
(defn exit-code
"Waits for the process to terminate (blocking the thread) and returns
the exit code. If timeout is passed, it is assumed to be milliseconds
to wait for the process to exit. If it does not exit in time, it is
killed (with or without fire)."
([process] (.waitFor ^Process (:process process)))
([process timeout]
(try
(let [^java.util.concurrent.Future fut
(future (.waitFor ^Process (:process process)))]
(.get fut timeout TimeUnit/MILLISECONDS))
(catch Exception e
(if (or (instance? TimeoutException e)
(instance? TimeoutException (.getCause e)))
(do (destroy process)
:timeout)
(throw e))))))
(defn flush
"Flush the output stream of a process."
[process]
(let [^OutputStream in (:in process)]
(.flush in)))
(defn done
"Close the process's output stream (sending EOF)."
[proc]
(let [^OutputStream in (:in proc)]
(.close in)))
(defn stream-to
"Stream :out or :err from a process to an ouput stream.
Options passed are fed to clojure.java.io/copy. They are :encoding to
set the encoding and :buffer-size to set the size of the buffer.
:encoding defaults to UTF-8 and :buffer-size to 1024."
[process from to & args]
(apply io/copy (process from) to args))
(defn feed-from
"Feed to a process's input stream with optional. Options passed are
fed to clojure.java.io/copy. They are :encoding to set the encoding
and :buffer-size to set the size of the buffer. :encoding defaults to
UTF-8 and :buffer-size to 1024. If :flush is specified and is false,
the process will be flushed after writing."
[process from & {flush? :flush :or {flush? true} :as all}]
(apply io/copy from (:in process) all)
(when flush? (flush process)))
(defn stream-to-string
"Streams the output of the process to a string and returns it."
[process from & args]
(with-open [writer (java.io.StringWriter.)]
(apply stream-to process from writer args)
(str writer)))
;; The writer that Clojure wraps System/out in for *out* seems to buffer
;; things instead of writing them immediately. This wont work if you
;; really want to stream stuff, so we'll just skip it and throw our data
;; directly at System/out.
(defn stream-to-out
"Streams the output of the process to System/out"
[process from & args]
(apply stream-to process from (System/out) args))
(defn feed-from-string
"Feed the process some data from a string."
[process s & args]
(apply feed-from process (java.io.StringReader. s) args))
(defn read-line
"Read a line from a process' :out or :err."
[process from]
(binding [*in* (io/reader (from process))]
(clojure.core/read-line)))

View file

@ -5,23 +5,35 @@
[clojure.java.io :as io]
[clojure.string :as str]
[clojure.tools.reader.reader-types :as r]
[sci.core :as sci]
[sci.impl.interpreter :refer [eval-form]]
[sci.impl.opts :refer [init]]
[sci.impl.parser :as parser]))
[sci.impl.parser :as parser]
[sci.core :as sci]
[sci.impl.io :as sio]))
(defn repl-caught
"Default :caught hook for repl"
[e]
(sci/with-bindings {sci/out @sci/err}
(sio/println (.getMessage ^Exception e))
(sio/flush)))
(defn repl
"REPL with predefined hooks for attachable socket server."
[sci-ctx]
(let [in (r/indexing-push-back-reader (r/push-back-reader *in*))]
([sci-ctx] (repl sci-ctx nil))
([sci-ctx {:keys [:init :read :eval :need-prompt :prompt :flush :print :caught]}]
(let [in (r/indexing-push-back-reader (r/push-back-reader @sci/in))]
(m/repl
:init #(do (println "Babashka"
:init (or init
#(do (sio/println "Babashka"
(str "v" (str/trim (slurp (io/resource "BABASHKA_VERSION"))))
"REPL.")
(println "Use :repl/quit or :repl/exit to quit the REPL.")
(println "Clojure rocks, Bash reaches.")
(println))
:read (fn [_request-prompt request-exit]
(sio/println "Use :repl/quit or :repl/exit to quit the REPL.")
(sio/println "Clojure rocks, Bash reaches.")
(sio/println)))
:read (or read
(fn [_request-prompt request-exit]
;; (prn "PEEK" @sci/in (r/peek-char @sci/in))
;; (prn "PEEK" @sci/in (r/peek-char @sci/in)) this works fine
(if (r/peek-char in) ;; if this is nil, we reached EOF
(let [v (parser/parse-next sci-ctx in)]
(if (or (identical? :repl/quit v)
@ -29,12 +41,10 @@
(identical? :edamame.impl.parser/eof v))
request-exit
v))
request-exit))
:eval (fn [expr]
(let [ret (sci/with-bindings {sci/in *in*
sci/out *out*
sci/err *err*}
(eval-form (update sci-ctx
request-exit)))
:eval (or eval
(fn [expr]
(let [ret (eval-form (update sci-ctx
:env
(fn [env]
(swap! env update-in [:namespaces 'clojure.core]
@ -44,11 +54,23 @@
'*3 *3
'*e *e)
env))
expr))]
ret))
:need-prompt (fn [] true)
:prompt #(printf "%s=> " (-> sci-ctx :env deref :current-ns)))))
expr)]
ret)))
:need-prompt (or need-prompt (fn [] true))
:prompt (or prompt #(sio/printf "%s=> " (-> sci-ctx :env deref :current-ns)))
:flush (or flush sio/flush)
:print (or print sio/prn)
:caught (or caught repl-caught)))))
(defn start-repl! [ctx]
(let [sci-ctx (init ctx)]
(repl sci-ctx)))
(defn start-repl!
([sci-ctx] (start-repl! sci-ctx nil))
([sci-ctx opts]
(repl sci-ctx opts)))
;;;; Scratch
(comment
(def in (-> (java.io.StringReader. "(+ 1 2 3)") clojure.lang.LineNumberingPushbackReader.))
(r/peek-char in)
(r/read-char in)
)

View file

@ -3,19 +3,17 @@
(:require
[babashka.impl.clojure.core.server :as server]
[babashka.impl.repl :as repl]
[clojure.string :as str]
[sci.impl.opts :refer [init]]))
[clojure.string :as str]))
(set! *warn-on-reflection* true)
(defn start-repl! [host+port sci-opts]
(defn start-repl! [host+port sci-ctx]
(let [parts (str/split host+port #":")
[host port] (if (= 1 (count parts))
[nil (Integer. ^String (first parts))]
[(first parts) (Integer. ^String (second parts))])
host+port (if-not host (str "localhost:" port)
host+port)
sci-ctx (init sci-opts)
socket (server/start-server
{:address host
:port port
@ -29,7 +27,6 @@
(server/stop-server))
(comment
(def sock (start-repl! "0.0.0.0:1666" {:env (atom {})}))
@#'server/servers
(stop-repl!)
)

View file

@ -1,9 +0,0 @@
(ns babashka.impl.utils
{:no-doc true}
(:require [sci.core :as sci]))
(defn eval-string [expr ctx]
(sci/with-bindings {sci/out *out*
sci/in *in*
sci/err *err*}
(sci/eval-string expr ctx)))

View file

@ -3,26 +3,34 @@
(:require
[babashka.impl.async :refer [async-namespace]]
[babashka.impl.cheshire :refer [cheshire-core-namespace]]
[babashka.impl.classes :as classes]
[babashka.impl.classpath :as cp]
[babashka.impl.clojure.core :refer [core-extras]]
[babashka.impl.clojure.java.io :refer [io-namespace]]
[babashka.impl.clojure.stacktrace :refer [print-stack-trace]]
[babashka.impl.conch :refer [conch-namespace]]
[babashka.impl.clojure.java.shell :refer [shell-namespace]]
[babashka.impl.clojure.main :refer [demunge]]
[babashka.impl.clojure.stacktrace :refer [stacktrace-namespace print-stack-trace]]
[babashka.impl.csv :as csv]
[babashka.impl.xml :as xml]
[babashka.impl.pipe-signal-handler :refer [handle-pipe! pipe-signal-received?]]
[babashka.impl.repl :as repl]
[babashka.impl.socket-repl :as socket-repl]
[babashka.impl.tools.cli :refer [tools-cli-namespace]]
[babashka.impl.utils :refer [eval-string]]
[babashka.impl.classpath :as cp]
[babashka.impl.xml :as xml]
[babashka.wait :as wait]
[clojure.edn :as edn]
[clojure.java.io :as io]
[clojure.java.shell :as shell]
[clojure.string :as str]
[sci.addons :as addons])
[sci.addons :as addons]
[sci.core :as sci]
[sci.impl.interpreter :refer [eval-string*]]
[sci.impl.opts :as sci-opts]
[sci.impl.vars :as vars])
(:gen-class))
(sci/alter-var-root sci/in (constantly *in*))
(sci/alter-var-root sci/out (constantly *out*))
(sci/alter-var-root sci/err (constantly *err*))
(set! *warn-on-reflection* true)
;; To detect problems when generating the image, run:
;; echo '1' | java -agentlib:native-image-agent=config-output-dir=/tmp -jar target/babashka-xxx-standalone.jar '...'
@ -31,65 +39,71 @@
(defn parse-opts [options]
(let [opts (loop [options options
opts-map {}]
(if-let [opt (first options)]
(if options
(let [opt (first options)]
(case opt
("--version") {:version true}
("--help" "-h" "-?") {:help? true}
("--verbose")(recur (rest options)
("--verbose")(recur (next options)
(assoc opts-map
:verbose? true))
("--stream") (recur (rest options)
("--stream") (recur (next options)
(assoc opts-map
:stream? true))
("--time") (recur (rest options)
("--time") (recur (next options)
(assoc opts-map
:time? true))
("-i") (recur (rest options)
("-i") (recur (next options)
(assoc opts-map
:shell-in true))
("-I") (recur (rest options)
("-I") (recur (next options)
(assoc opts-map
:edn-in true))
("-o") (recur (rest options)
("-o") (recur (next options)
(assoc opts-map
:shell-out true))
("-O") (recur (rest options)
("-O") (recur (next options)
(assoc opts-map
:edn-out true))
("-io") (recur (rest options)
("-io") (recur (next options)
(assoc opts-map
:shell-in true
:shell-out true))
("-IO") (recur (rest options)
("-IO") (recur (next options)
(assoc opts-map
:edn-in true
:edn-out true))
("--classpath", "-cp")
(let [options (next options)]
(recur (next options)
(assoc opts-map :classpath (first options))))
("--uberscript")
(let [options (next options)]
(recur (next options)
(assoc opts-map
:uberscript (first options))))
("-f" "--file")
(let [options (rest options)]
(recur (rest options)
(let [options (next options)]
(recur (next options)
(assoc opts-map
:file (first options))))
("--repl")
(let [options (rest options)]
(recur (rest options)
(let [options (next options)]
(recur (next options)
(assoc opts-map
:repl true)))
("--socket-repl")
(let [options (rest options)]
(recur (rest options)
(let [options (next options)]
(recur (next options)
(assoc opts-map
:socket-repl (first options))))
("--eval", "-e")
(let [options (rest options)]
(recur (rest options)
(let [options (next options)]
(recur (next options)
(assoc opts-map :expression (first options))))
("--classpath", "-cp")
(let [options (rest options)]
(recur (rest options)
(assoc opts-map :classpath (first options))))
("--main", "-m")
(let [options (rest options)]
(recur (rest options)
(let [options (next options)]
(recur (next options)
(assoc opts-map :main (first options))))
(if (some opts-map [:file :socket-repl :expression :main])
(assoc opts-map
@ -98,10 +112,10 @@
(.exists (io/file opt)))
(assoc opts-map
:file opt
:command-line-args (rest options))
:command-line-args (next options))
(assoc opts-map
:expression opt
:command-line-args (rest options)))))
:command-line-args (next options))))))
opts-map))]
opts))
@ -122,8 +136,9 @@
(println (str "babashka v"(str/trim (slurp (io/resource "BABASHKA_VERSION"))))))
(def usage-string "Usage: bb [ -i | -I ] [ -o | -O ] [ --stream ] [--verbose]
[ ( --classpath | -cp ) <cp> ] [ ( --main | -m ) <main-namespace> ]
( -e <expression> | -f <file> | --repl | --socket-repl [<host>:]<port> )
[ ( --classpath | -cp ) <cp> ] [ --uberscript <file> ]
[ ( --main | -m ) <main-namespace> | -e <expression> | -f <file> |
--repl | --socket-repl [<host>:]<port> ]
[ arg* ]")
(defn print-usage []
(println usage-string))
@ -138,12 +153,15 @@
(println "
--help, -h or -? Print this help text.
--version Print the current version of babashka.
-i Bind *in* to a lazy seq of lines from stdin.
-I Bind *in* to a lazy seq of EDN values from stdin.
-i Bind *input* to a lazy seq of lines from stdin.
-I Bind *input* to a lazy seq of EDN values from stdin.
-o Write lines to stdout.
-O Write EDN values to stdout.
--verbose Print entire stacktrace in case of exception.
--stream Stream over lines or EDN values from stdin. Combined with -i or -I *in* becomes a single value per iteration.
--stream Stream over lines or EDN values from stdin. Combined with -i or -I *input* becomes a single value per iteration.
--uberscript <file> Collect preloads, -e, -f and -m and all required namespaces from the classpath into a single executable file.
-e, --eval <expr> Evaluate an expression.
-f, --file <path> Evaluate a file.
-cp, --classpath Classpath to use.
@ -167,28 +185,21 @@ Everything after that is bound to *command-line-args*."))
(edn/read {;;:readers *data-readers*
:eof ::EOF} *in*))
(defn load-file* [ctx file]
(let [s (slurp file)]
(eval-string s ctx)))
(def reflection-var (sci/new-dynamic-var '*warn-on-reflection* false))
(defn eval* [ctx form]
(eval-string (pr-str form) ctx))
(defn load-file* [sci-ctx f]
(let [f (io/file f)
s (slurp f)]
(sci/with-bindings {vars/file-var (.getCanonicalPath f)}
(eval-string* sci-ctx s))))
(defn start-repl! [ctx read-next]
(let [ctx (update ctx :bindings assoc
(with-meta '*in*
{:sci/deref! true})
(read-next))]
(repl/start-repl! ctx)))
(defn eval* [sci-ctx form]
(eval-string* sci-ctx (pr-str form)))
(defn start-socket-repl! [address ctx read-next]
(let [ctx (update ctx :bindings assoc
(with-meta '*in*
{:sci/deref! true})
(read-next))]
(defn start-socket-repl! [address ctx]
(socket-repl/start-repl! address ctx)
;; hang until SIGINT
@(promise)))
@(promise))
(defn exit [n]
(throw (ex-info "" {:bb/exit-code n})))
@ -197,6 +208,49 @@ Everything after that is bound to *command-line-args*."))
;; (sci/set-var-root! sci/*out* *out*)
;; (sci/set-var-root! sci/*err* *err*)
(def aliases
'{tools.cli 'clojure.tools.cli
edn clojure.edn
wait babashka.wait
sig babashka.signal
shell clojure.java.shell
io clojure.java.io
async clojure.core.async
csv clojure.data.csv
json cheshire.core})
(def namespaces
{'clojure.tools.cli tools-cli-namespace
'clojure.edn {'read edn/read
'read-string edn/read-string}
'clojure.java.shell shell-namespace
'babashka.wait {'wait-for-port wait/wait-for-port
'wait-for-path wait/wait-for-path}
'babashka.signal {'pipe-signal-received? pipe-signal-received?}
'clojure.java.io io-namespace
'clojure.core.async async-namespace
'clojure.data.csv csv/csv-namespace
'cheshire.core cheshire-core-namespace
'clojure.stacktrace stacktrace-namespace
'clojure.main {'demunge demunge}
'clojure.repl {'demunge demunge}
'clojure.data.xml xml/xml-namespace})
(def bindings
{'java.lang.System/exit exit ;; override exit, so we have more control
'System/exit exit})
(defn error-handler* [^Exception e verbose?]
(binding [*out* *err*]
(let [d (ex-data e)
exit-code (:bb/exit-code d)]
(if exit-code [nil exit-code]
(do (if verbose?
(print-stack-trace e)
(println (.getMessage e)))
(flush)
[nil 1])))))
(defn main
[& args]
(handle-pipe!)
@ -208,7 +262,7 @@ Everything after that is bound to *command-line-args*."))
:expression :stream? :time?
:repl :socket-repl
:verbose? :classpath
:main] :as _opts}
:main :uberscript] :as _opts}
(parse-opts args)
read-next (fn [*in*]
(if (pipe-signal-received?)
@ -222,6 +276,7 @@ Everything after that is bound to *command-line-args*."))
(edn-seq *in*)
:else
(edn/read *in*))))))
uberscript-sources (atom ())
env (atom {})
classpath (or classpath
(System/getenv "BABASHKA_CLASSPATH"))
@ -229,84 +284,72 @@ Everything after that is bound to *command-line-args*."))
(cp/loader classpath))
load-fn (when classpath
(fn [{:keys [:namespace]}]
(cp/source-for-namespace loader namespace)))
ctx {:aliases '{tools.cli 'clojure.tools.cli
edn clojure.edn
wait babashka.wait
sig babashka.signal
shell clojure.java.shell
io clojure.java.io
conch me.raynes.conch.low-level
async clojure.core.async
csv clojure.data.csv
json cheshire.core}
:namespaces {'clojure.core (assoc core-extras
'*command-line-args* command-line-args)
'clojure.tools.cli tools-cli-namespace
'clojure.edn {'read-string edn/read-string}
'clojure.java.shell {'sh shell/sh}
'babashka.wait {'wait-for-port wait/wait-for-port
'wait-for-path wait/wait-for-path}
'babashka.signal {'pipe-signal-received? pipe-signal-received?}
'clojure.java.io io-namespace
'me.raynes.conch.low-level conch-namespace
'clojure.core.async async-namespace
'clojure.data.csv csv/csv-namespace
'cheshire.core cheshire-core-namespace
'clojure.data.xml xml/xml-namespace}
:bindings {'java.lang.System/exit exit ;; override exit, so we have more control
'System/exit exit}
(let [res (cp/source-for-namespace loader namespace nil)]
(when uberscript (swap! uberscript-sources conj (:source res)))
res)))
_ (when file (vars/bindRoot vars/file-var (.getCanonicalPath (io/file file))))
ctx {:aliases aliases
:namespaces (-> namespaces
(assoc 'clojure.core
(assoc core-extras
'*command-line-args*
(sci/new-dynamic-var '*command-line-args* command-line-args)
'*file* vars/file-var
'*warn-on-reflection* reflection-var))
(assoc-in ['clojure.java.io 'resource]
#(when classpath (cp/getResource loader % {:url? true}))))
:bindings bindings
:env env
:features #{:bb}
:classes {'java.lang.ArithmeticException ArithmeticException
'java.lang.AssertionError AssertionError
'java.lang.Boolean Boolean
'java.io.BufferedWriter java.io.BufferedWriter
'java.io.BufferedReader java.io.BufferedReader
'java.lang.Class Class
'java.lang.Double Double
'java.lang.Exception Exception
'clojure.lang.ExceptionInfo clojure.lang.ExceptionInfo
'java.lang.Integer Integer
'java.io.File java.io.File
'clojure.lang.LineNumberingPushbackReader clojure.lang.LineNumberingPushbackReader
'java.util.regex.Pattern java.util.regex.Pattern
'java.lang.String String
'java.io.StringReader java.io.StringReader
'java.io.StringWriter java.io.StringWriter
'java.lang.System System
'java.lang.Thread Thread
'sun.nio.fs.UnixPath sun.nio.fs.UnixPath
'java.nio.file.attribute.FileAttribute java.nio.file.attribute.FileAttribute
'java.nio.file.attribute.PosixFilePermission java.nio.file.attribute.PosixFilePermission
'java.nio.file.attribute.PosixFilePermissions java.nio.file.attribute.PosixFilePermissions
'java.nio.file.CopyOption java.nio.file.CopyOption
'java.nio.file.FileAlreadyExistsException java.nio.file.FileAlreadyExistsException
'java.nio.file.Files java.nio.file.Files
'java.nio.file.NoSuchFileException java.nio.file.NoSuchFileException
'java.nio.file.StandardCopyOption java.nio.file.StandardCopyOption
}
:classes classes/class-map
:imports '{ArithmeticException java.lang.ArithmeticException
AssertionError java.lang.AssertionError
Boolean java.lang.Boolean
Class java.lang.Class
Double java.lang.Double
Exception java.lang.Exception
IllegalArgumentException java.lang.IllegalArgumentException
Integer java.lang.Integer
File java.io.File
Math java.lang.Math
Object java.lang.Object
ProcessBuilder java.lang.ProcessBuilder
String java.lang.String
System java.lang.System
Thread java.lang.Thread}
:load-fn load-fn}
ctx (update ctx :bindings assoc 'eval #(eval* ctx %)
'load-file #(load-file* ctx %))
:load-fn load-fn
:dry-run uberscript}
ctx (addons/future ctx)
_preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim) (eval-string ctx))
expression (if main
(format "(ns user (:require [%1$s])) (apply %1$s/-main *command-line-args*)"
main)
expression)
sci-ctx (sci-opts/init ctx)
_ (swap! (:env sci-ctx)
(fn [env]
(update-in env [:namespaces 'clojure.core] assoc
'eval #(eval* sci-ctx %)
'load-file #(load-file* sci-ctx %))))
_ (swap! (:env sci-ctx)
(fn [env]
(assoc-in env [:namespaces 'clojure.main 'repl]
(fn [& opts]
(let [opts (apply hash-map opts)]
(repl/start-repl! sci-ctx opts))))))
preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim))
[expression exit-code]
(cond expression [expression nil]
main [(format "(ns user (:require [%1$s])) (apply %1$s/-main *command-line-args*)"
main) nil]
file (try [(read-file file) nil]
(catch Exception e
(error-handler* e verbose?))))
exit-code
;; handle preloads
(if exit-code exit-code
(do (when preloads (try (eval-string* sci-ctx preloads)
(catch Throwable e
(error-handler* e verbose?))))
nil))
exit-code
(or exit-code
(sci/with-bindings {reflection-var false}
(or
#_(binding [*out* *err*]
(prn ">>" _opts))
@ -315,19 +358,19 @@ Everything after that is bound to *command-line-args*."))
[(print-version) 0]
help?
[(print-help) 0]
repl [(start-repl! ctx #(read-next *in*)) 0]
socket-repl [(start-socket-repl! socket-repl ctx #(read-next *in*)) 0]
:else
repl [(repl/start-repl! sci-ctx) 0]
socket-repl [(start-socket-repl! socket-repl sci-ctx) 0]
(not (str/blank? expression))
(try
(let [expr (if file (read-file file) expression)]
(if expr
(loop [in (read-next *in*)]
(let [ctx (update-in ctx [:namespaces 'user] assoc (with-meta '*in*
(let [_ (swap! env update-in [:namespaces 'user]
assoc (with-meta '*input*
(when-not stream?
{:sci/deref! true})) in)]
{:sci.impl/deref! true}))
(sci/new-dynamic-var '*input* in))]
(if (identical? ::EOF in)
[nil 0] ;; done streaming
(let [res [(let [res (eval-string expr ctx)]
(let [res [(let [res (eval-string* sci-ctx expression)]
(when (some? res)
(if-let [pr-f (cond shell-out println
edn-out prn)]
@ -340,22 +383,23 @@ Everything after that is bound to *command-line-args*."))
(if stream?
(recur (read-next *in*))
res)))))
[(start-repl! ctx #(read-next *in*)) 0]))
(catch Throwable e
(binding [*out* *err*]
(let [d (ex-data e)
exit-code (:bb/exit-code d)]
(if exit-code [nil exit-code]
(do (if verbose?
(print-stack-trace e)
(println (.getMessage e)))
(flush)
[nil 1]))))))))
1)
(error-handler* e verbose?)))
uberscript [nil 0]
:else [(repl/start-repl! sci-ctx) 0]))
1)))
t1 (System/currentTimeMillis)]
(flush)
(when uberscript
uberscript
(let [uberscript-out uberscript]
(spit uberscript-out "") ;; reset file
(doseq [s @uberscript-sources]
(spit uberscript-out s :append true))
(spit uberscript-out preloads :append true)
(spit uberscript-out expression :append true)))
(when time? (binding [*out* *err*]
(println "bb took" (str (- t1 t0) "ms."))))
(flush)
exit-code))
(defn -main

View file

@ -17,8 +17,8 @@
opts)
t0 (System/currentTimeMillis)]
(loop []
(let [v (try (Socket. host port)
(- (System/currentTimeMillis) t0)
(let [v (try (with-open [_ (Socket. host port)]
(- (System/currentTimeMillis) t0))
(catch ConnectException _e
(let [took (- (System/currentTimeMillis) t0)]
(if (and timeout (>= took timeout))

View file

@ -0,0 +1,4 @@
(ns ns-with-error)
(def x 0)
(def y (/ 1 0))

View file

@ -2,7 +2,8 @@
(:require
[babashka.test-utils :as tu]
[clojure.edn :as edn]
[clojure.test :as t :refer [deftest is]]))
[clojure.test :as t :refer [deftest is]]
[clojure.java.io :as io]))
(defn bb [input & args]
(edn/read-string (apply tu/bb (when (some? input) (str input)) (map str args))))
@ -25,3 +26,26 @@
(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"))))
(deftest uberscript-test
(let [tmp-file (java.io.File/createTempFile "uberscript" ".clj")]
(.deleteOnExit tmp-file)
(tu/bb nil "--classpath" "test-resources/babashka/src_for_classpath_test" "-m" "my.main" "--uberscript" (.getPath tmp-file))
(is (= "(\"1\" \"2\" \"3\" \"4\")\n"
(tu/bb nil "--file" (.getPath tmp-file) "1" "2" "3" "4")))))
(deftest error-while-loading-test
(is (true?
(bb nil "--classpath" "test-resources/babashka/src_for_classpath_test"
"
(try
(require '[ns-with-error])
(catch Exception nil))
(nil? (resolve 'ns-with-error/x))"))))
(deftest resource-test
(let [tmp-file (java.io.File/createTempFile "icon" ".png")]
(.deleteOnExit tmp-file)
(bb nil "--classpath" "logo" "-e" (format "(io/copy (io/input-stream (io/resource \"icon.png\")) (io/file \"%s\"))" (.getPath tmp-file)))
(is (= (.length (io/file "logo" "icon.png"))
(.length tmp-file)))))

View file

@ -0,0 +1,17 @@
(ns babashka.file-var-test
(:require
[babashka.test-utils :as tu]
[clojure.test :as t :refer [deftest is]]
[clojure.string :as str]))
(defn bb [input & args]
(apply tu/bb (when (some? input) (str input)) (map str args)))
(deftest file-var-test
(let [[f1 f2 f3]
(str/split (bb nil "--classpath" "test/babashka/scripts"
"test/babashka/scripts/file_var.bb")
#"\n")]
(is (str/ends-with? f1 "file_var_classpath.bb"))
(is (str/ends-with? f2 "loaded_by_file_var.bb"))
(is (str/ends-with? f3 "file_var.bb"))))

View file

@ -0,0 +1,21 @@
(ns babashka.http-connection-test
(:require
[babashka.test-utils :as tu]
[clojure.test :as t :refer [deftest is]]
[clojure.string :as str]))
(defn bb [& args]
(apply tu/bb nil (map str args)))
(deftest open-connection-test
(is (= "\"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\"))]
(.setConnectTimeout conn 1000)
(.setRequestProperty conn \"Content-Type\" \"application/json\") ;; nonsensical, but to test if this method exists
(.connect conn)
(let [is (.getInputStream conn)
err (.getErrorStream conn)
response (json/decode (slurp is) true)]
(-> response :args :foo)))
")))))

View file

@ -0,0 +1,17 @@
(ns babashka.impl.clojure.java.shell-test
(:require [clojure.test :as t :refer [deftest is testing]]
[babashka.test-utils :as test-utils]
[clojure.string :as str]))
(deftest with-sh-env-test
(is (= "\"BAR\""
(str/trim (test-utils/bb nil "
(-> (shell/with-sh-env {:FOO \"BAR\"}
(shell/sh \"bash\" \"-c\" \"echo $FOO\"))
:out
str/trim)"))))
(is (str/includes? (str/trim (test-utils/bb nil "
(-> (shell/with-sh-dir \"logo\"
(shell/sh \"ls\"))
:out)"))
"icon.svg")))

View file

@ -2,32 +2,38 @@
(:require
[babashka.impl.repl :refer [start-repl!]]
[clojure.string :as str]
[clojure.test :as t :refer [deftest is]]))
[clojure.test :as t :refer [deftest is]]
[sci.impl.opts :refer [init]]
[sci.core :as sci]
[sci.impl.vars :as vars]))
(set! *warn-on-reflection* true)
;; (vars/bindRoot sci/in *in*)
;; (vars/bindRoot sci/out *out*)
(vars/bindRoot sci/err *err*)
(defn repl! []
(start-repl! {:bindings {(with-meta '*in*
{:sci/deref! true})
(delay [1 2 3])
'*command-line-args*
(start-repl! (init {:bindings {'*command-line-args*
["a" "b" "c"]}
:env (atom {})}))
:env (atom {})})))
(defn assert-repl [expr expected]
(is (str/includes? (with-out-str
(with-in-str (str expr "\n:repl/quit")
(is (str/includes? (sci/with-out-str
(sci/with-in-str (str expr "\n:repl/quit")
(repl!))) expected)))
(deftest repl-test
(assert-repl "1" "1")
(assert-repl "[1 2 3]" "[1 2 3]")
(assert-repl "()" "()")
(assert-repl "(+ 1 2 3)" "6")
(assert-repl "(defn foo [] (+ 1 2 3)) (foo)" "6")
(assert-repl "(defn foo [] (+ 1 2 3)) (foo)" "6")
(assert-repl "1\n(inc *1)" "2")
(assert-repl "1\n(dec *1)(+ *2 *2)" "2")
(assert-repl "1\n(dec *1)(+ *2 *2)" "2")
(assert-repl "*command-line-args*" "[\"a\" \"b\" \"c\"]")
(assert-repl "*in*" "[1 2 3]"))
(assert-repl "*command-line-args*" "[\"a\" \"b\" \"c\"]"))
;;;; Scratch

View file

@ -5,7 +5,8 @@
[clojure.java.shell :refer [sh]]
[clojure.string :as str]
[clojure.test :as t :refer [deftest is testing]]
[clojure.java.io :as io]))
[clojure.java.io :as io]
[sci.impl.opts :refer [init]]))
(set! *warn-on-reflection* true)
@ -31,13 +32,10 @@
(deftest socket-repl-test
(try
(if tu/jvm?
(start-repl! "0.0.0.0:1666" {:bindings {(with-meta '*in*
{:sci/deref! true})
(delay [1 2 3])
'*command-line-args*
(start-repl! "0.0.0.0:1666" (init {:bindings {'*command-line-args*
["a" "b" "c"]}
:env (atom {})
:features #{:bb}})
:features #{:bb}}))
(future
(sh "bash" "-c"
"echo '[1 2 3]' | ./bb --socket-repl 0.0.0.0:1666 a b c")))
@ -47,8 +45,6 @@
(sh "bash" "-c"
"lsof -t -i:1666"))))))
(is (socket-command "(+ 1 2 3)" "user=> 6"))
(testing "*in*"
(is (socket-command "*in*" "[1 2 3]")))
(testing "*command-line-args*"
(is (socket-command '*command-line-args* "\"a\" \"b\" \"c\"")))
(testing "&env"
@ -72,7 +68,7 @@
(dotimes [_ 1000]
(t/run-tests))
(stop-repl!)
(start-repl! "0.0.0.0:1666" {:bindings {(with-meta '*in*
(start-repl! "0.0.0.0:1666" {:bindings {(with-meta '*input*
{:sci/deref! true})
(delay [1 2 3])
'*command-line-args*

View file

@ -0,0 +1,19 @@
(ns babashka.java-time-test
(:require
[babashka.test-utils :as test-utils]
[clojure.edn :as edn]
[clojure.test :as test :refer [deftest is]]))
(defn bb [expr]
(edn/read-string (apply test-utils/bb nil [(str expr)])))
(deftest java-time-test
(is (= "2019-12-18" (bb '(str (java.time.LocalDate/of 2019 12 18)))))
(is (= "2019-12-01" (bb '(str
(-> (java.time.LocalDate/of 2019 12 18)
(.minusDays 17))))))
(is (= "MONDAY" (bb '(str java.time.DayOfWeek/MONDAY))))
(is (= "18-12-2019 16:01:41"
(bb '(.format
(java.time.LocalDateTime/parse "2019-12-18T16:01:41.485")
(java.time.format.DateTimeFormatter/ofPattern "dd-MM-yyyy HH:mm:ss"))))))

View file

@ -3,10 +3,10 @@
[babashka.main :as main]
[babashka.test-utils :as test-utils]
[clojure.edn :as edn]
[clojure.java.io :as io]
[clojure.java.shell :refer [sh]]
[clojure.string :as str]
[clojure.test :as test :refer [deftest is testing]]
[clojure.java.io :as io]
[sci.core :as sci]))
(defn bb [input & args]
@ -21,58 +21,58 @@
(testing "distinguish automatically between expression or file name"
(is (= {:expression "(println 123)"
:command-line-args []}
:command-line-args nil}
(main/parse-opts ["(println 123)"])))
(is (= {:file "src/babashka/main.clj"
:command-line-args []}
:command-line-args nil}
(main/parse-opts ["src/babashka/main.clj"])))
(is (= {:expression "does-not-exist"
:command-line-args []}
:command-line-args nil}
(main/parse-opts ["does-not-exist"])))))
(deftest main-test
(testing "-io behaves as identity"
(= "foo\nbar\n" (test-utils/bb "foo\nbar\n" "-io" "*in*")))
(= "foo\nbar\n" (test-utils/bb "foo\nbar\n" "-io" "*input*")))
(testing "if and when"
(is (= 1 (bb 0 '(if (zero? *in*) 1 2))))
(is (= 2 (bb 1 '(if (zero? *in*) 1 2))))
(is (= 1 (bb 0 '(when (zero? *in*) 1))))
(is (nil? (bb 1 '(when (zero? *in*) 1)))))
(is (= 1 (bb 0 '(if (zero? *input*) 1 2))))
(is (= 2 (bb 1 '(if (zero? *input*) 1 2))))
(is (= 1 (bb 0 '(when (zero? *input*) 1))))
(is (nil? (bb 1 '(when (zero? *input*) 1)))))
(testing "and and or"
(is (= false (bb 0 '(and false true *in*))))
(is (= 0 (bb 0 '(and true true *in*))))
(is (= 1 (bb 1 '(or false false *in*))))
(is (= false (bb false '(or false false *in*))))
(is (= 3 (bb false '(or false false *in* 3)))))
(is (= false (bb 0 '(and false true *input*))))
(is (= 0 (bb 0 '(and true true *input*))))
(is (= 1 (bb 1 '(or false false *input*))))
(is (= false (bb false '(or false false *input*))))
(is (= 3 (bb false '(or false false *input* 3)))))
(testing "fn"
(is (= 2 (bb 1 "(#(+ 1 %) *in*)")))
(is (= 2 (bb 1 "(#(+ 1 %) *input*)")))
(is (= [1 2 3] (bb 1 "(map #(+ 1 %) [0 1 2])")))
(is (= 1 (bb 1 "(#(when (odd? *in*) *in*))"))))
(is (= 1 (bb 1 "(#(when (odd? *input*) *input*))"))))
(testing "map"
(is (= [1 2 3] (bb 1 '(map inc [0 1 2])))))
(testing "keep"
(is (= [false true false] (bb 1 '(keep odd? [0 1 2])))))
(testing "->"
(is (= 4 (bb 1 '(-> *in* inc inc (inc))))))
(is (= 4 (bb 1 '(-> *input* inc inc (inc))))))
(testing "->>"
(is (= 10 (edn/read-string (test-utils/bb "foo\n\baar\baaaaz" "-i" "(->> *in* (map count) (apply max))")))))
(is (= 10 (edn/read-string (test-utils/bb "foo\n\baar\baaaaz" "-i" "(->> *input* (map count) (apply max))")))))
(testing "literals"
(is (= {:a 4
:b {:a 2}
:c [1 1]
:d #{1 2}}
(bb 1 '{:a (+ 1 2 *in*)
:b {:a (inc *in*)}
:c [*in* *in*]
:d #{*in* (inc *in*)}}))))
(bb 1 '{:a (+ 1 2 *input*)
:b {:a (inc *input*)}
:c [*input* *input*]
:d #{*input* (inc *input*)}}))))
(testing "shuffle the contents of a file"
(let [in "foo\n Clojure is nice. \nbar\n If you're nice to clojure. "
in-lines (set (str/split in #"\n"))
out (test-utils/bb in
"-io"
(str '(shuffle *in*)))
(str '(shuffle *input*)))
out-lines (set (str/split out #"\n"))]
(is (= in-lines out-lines))))
(testing "find occurrences in file by line number"
@ -80,14 +80,14 @@
(->
(bb "foo\n Clojure is nice. \nbar\n If you're nice to clojure. "
"-i"
"(map-indexed #(-> [%1 %2]) *in*)")
(bb "(keep #(when (re-find #\"(?i)clojure\" (second %)) (first %)) *in*)"))))))
"(map-indexed #(-> [%1 %2]) *input*)")
(bb "(keep #(when (re-find #\"(?i)clojure\" (second %)) (first %)) *input*)"))))))
(deftest println-test
(is (= "hello\n" (test-utils/bb nil "(println \"hello\")"))))
(deftest input-test
(testing "bb doesn't wait for input if *in* isn't used"
(testing "bb doesn't wait for input if *input* isn't used"
(is (= "2\n" (with-out-str (main/main "(inc 1)"))))))
(deftest System-test
@ -114,12 +114,12 @@
(is (re-find #"doctype html" resp))))
(deftest stream-test
(is (= "2\n3\n4\n" (test-utils/bb "1 2 3" "--stream" "(inc *in*)")))
(is (= "2\n3\n4\n" (test-utils/bb "{:x 2} {:x 3} {:x 4}" "--stream" "(:x *in*)")))
(is (= "2\n3\n4\n" (test-utils/bb "1 2 3" "--stream" "(inc *input*)")))
(is (= "2\n3\n4\n" (test-utils/bb "{:x 2} {:x 3} {:x 4}" "--stream" "(:x *input*)")))
(let [x "foo\n\bar\n"]
(is (= x (test-utils/bb x "--stream" "-io" "*in*"))))
(is (= x (test-utils/bb x "--stream" "-io" "*input*"))))
(let [x "f\n\b\n"]
(is (= x (test-utils/bb x "--stream" "-io" "(subs *in* 0 1)")))))
(is (= x (test-utils/bb x "--stream" "-io" "(subs *input* 0 1)")))))
(deftest load-file-test
(let [tmp (java.io.File/createTempFile "script" ".clj")]
@ -145,29 +145,35 @@
(deftest pipe-test
(when test-utils/native?
(let [out (:out (sh "bash" "-c" "./bb -o '(range)' |
./bb --stream '(* *in* *in*)' |
./bb --stream '(* *input* *input*)' |
head -n10"))
out (str/split-lines out)
out (map edn/read-string out)]
(is (= (take 10 (map #(* % %) (range))) out))))
(when test-utils/native?
(let [out (:out (sh "bash" "-c" "./bb -O '(repeat \"dude\")' |
./bb --stream '(str *in* \"rino\")' |
./bb -I '(take 3 *in*)'"))
./bb --stream '(str *input* \"rino\")' |
./bb -I '(take 3 *input*)'"))
out (edn/read-string out)]
(is (= '("duderino" "duderino" "duderino") out)))))
(deftest lazy-text-in-test
(when test-utils/native?
(let [out (:out (sh "bash" "-c" "yes | ./bb -i '(take 2 *in*)'"))
(let [out (:out (sh "bash" "-c" "yes | ./bb -i '(take 2 *input*)'"))
out (edn/read-string out)]
(is (= '("y" "y") out)))))
(deftest future-test
(is (= 6 (bb nil "@(future (+ 1 2 3))"))))
(deftest conch-test
(is (str/includes? (bb nil "(->> (conch/proc \"ls\") (conch/stream-to-string :out))")
(deftest process-builder-test
(is (str/includes? (bb nil "
(def ls (-> (ProcessBuilder. [\"ls\"]) (.start)))
(def input (.getOutputStream ls))
(.write (io/writer input) \"hello\") ;; dummy test just to see if this works
(def output (.getInputStream ls))
(assert (int? (.waitFor ls)))
(slurp output)")
"LICENSE")))
(deftest create-temp-file-test
@ -181,11 +187,14 @@
temp-dir-path))))))
(deftest wait-for-port-test
(is (= :timed-out
(bb nil "(def web-server (conch/proc \"python\" \"-m\" \"SimpleHTTPServer\" \"7171\"))
(wait/wait-for-port \"127.0.0.1\" 7171)
(conch/destroy web-server)
(wait/wait-for-port \"localhost\" 7172 {:default :timed-out :timeout 50})"))))
(let [server (test-utils/start-server! 1777)]
(is (= 1777 (:port (bb nil "(wait/wait-for-port \"127.0.0.1\" 1777)"))))
(test-utils/stop-server! server)
(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 (number? (:took edn)))))
(deftest wait-for-path-test
(let [temp-dir-path (System/getProperty "java.io.tmpdir")]
@ -287,6 +296,48 @@
(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)"))))
(deftest Base64-test
(is (= "babashka"
(bb nil "(String. (.decode (java.util.Base64/getDecoder) (.encode (java.util.Base64/getEncoder) (.getBytes \"babashka\"))))"))))
(deftest Thread-test
(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*)"))))
(deftest file-in-error-msg-test
(is (thrown-with-msg? Exception #"error.bb"
(bb nil (.getPath (io/file "test" "babashka" "scripts" "error.bb"))))))
(deftest compatibility-test
(is (true? (bb nil "(set! *warn-on-reflection* true)"))))
(deftest clojure-main-repl-test
(is (= "\"> foo!\\nnil\\n> \"\n" (test-utils/bb nil "
(defn foo [] (println \"foo!\"))
(with-out-str
(with-in-str \"(foo)\"
(clojure.main/repl :init (fn []) :prompt (fn [] (print \"> \")))))"))))
(deftest command-line-args-test
(is (true? (bb nil "(nil? *command-line-args*)")))
(is (= ["1" "2" "3"] (bb nil "*command-line-args*" "1" "2" "3"))))
(deftest need-constructors-test
(testing "the clojure.lang.Delay constructor works"
(is (= 1 (bb nil "@(delay 1)"))))
(testing "the clojure.lang.MapEntry constructor works"
(is (true? (bb nil "(= (first {1 2}) (clojure.lang.MapEntry. 1 2))")))))
;;;; Scratch
(comment
(dotimes [_ 10] (wait-for-port-test))
)

View file

@ -0,0 +1 @@
(/ 1 0)

View file

@ -0,0 +1,6 @@
(ns file-var
(:require [clojure.java.io :as io]))
(require '[file-var-classpath])
(load-file (io/file "test" "babashka" "scripts" "loaded_by_file_var.bb"))
(println *file*)

View file

@ -0,0 +1 @@
(println *file*)

View file

@ -0,0 +1,3 @@
(ns loaded-by-file-var)
(println *file*)

View file

@ -0,0 +1,22 @@
(require '[babashka.wait :as wait])
(defn socket-loop [^java.net.ServerSocket server]
(with-open [listener server]
(loop []
(with-open [socket (.accept listener)]
(let [input-stream (.getInputStream socket)]
(print (slurp input-stream))
(flush)))
(recur))))
(defn start-server! [port]
(let [server (java.net.ServerSocket. port)]
(future (socket-loop server))
server))
(defn stop-server! [^java.net.ServerSocket server]
(.close server))
(let [server (start-server! 1777)]
(prn (wait/wait-for-port "127.0.0.1" 1777))
(stop-server! server))

View file

@ -2,7 +2,8 @@
(:require
[babashka.main :as main]
[me.raynes.conch :refer [let-programs] :as sh]
[sci.core :as sci]))
[sci.core :as sci]
[sci.impl.vars :as vars]))
(set! *warn-on-reflection* true)
@ -14,6 +15,10 @@
bindings-map (cond-> {sci/out os
sci/err es}
is (assoc sci/in is))]
(try
(when input (vars/bindRoot sci/in is))
(vars/bindRoot sci/out os)
(vars/bindRoot sci/err es)
(sci/with-bindings bindings-map
(let [res (binding [*out* os
*err* es]
@ -24,7 +29,11 @@
(str os)
(throw (ex-info (str es)
{:stdout (str os)
:stderr (str es)})))))))
:stderr (str es)})))))
(finally
(when input (vars/bindRoot sci/in *in*))
(vars/bindRoot sci/out *out*)
(vars/bindRoot sci/err *err*)))))
(defn bb-native [input & args]
(let-programs [bb "./bb"]
@ -49,3 +58,20 @@
(if jvm?
(println "==== Testing JVM version")
(println "==== Testing native version"))
(defn socket-loop [^java.net.ServerSocket server]
(with-open [listener server]
(loop []
(with-open [socket (.accept listener)]
(let [input-stream (.getInputStream socket)]
(print (slurp input-stream))
(flush)))
(recur))))
(defn start-server! [port]
(let [server (java.net.ServerSocket. port)]
(future (socket-loop server))
server))
(defn stop-server! [^java.net.ServerSocket server]
(.close server))