Merge branch 'master' into ansi
This commit is contained in:
commit
d36b1272ec
29 changed files with 792 additions and 269 deletions
|
|
@ -3,14 +3,16 @@
|
|||
rm -rf /tmp/release
|
||||
mkdir -p /tmp/release
|
||||
cp bb /tmp/release
|
||||
# cp src-bash/bbk /tmp/release
|
||||
|
||||
VERSION=$(cat resources/BABASHKA_VERSION)
|
||||
|
||||
cd /tmp/release
|
||||
|
||||
## release binary as zip archive
|
||||
|
||||
zip "babashka-$VERSION-$BABASHKA_PLATFORM-amd64.zip" bb
|
||||
zip "babashka-$VERSION-$BABASHKA_PLATFORM-amd64.zip" bb # bbk
|
||||
|
||||
## cleanup
|
||||
|
||||
rm bb
|
||||
rm bb # bbk
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -13,3 +13,6 @@ pom.xml.asc
|
|||
/bb
|
||||
.clj-kondo/.cache
|
||||
!java/src/babashka/impl/LockFix.class
|
||||
!test-resources/babashka/src_for_classpath_test/foo.jar
|
||||
.cpcache
|
||||
deps.edn
|
||||
|
|
|
|||
162
README.md
162
README.md
|
|
@ -3,6 +3,8 @@
|
|||
[](https://circleci.com/gh/borkdude/babashka/tree/master)
|
||||
[](https://clojars.org/borkdude/babashka)
|
||||
[](https://cljdoc.org/d/borkdude/babashka/CURRENT)
|
||||
[](https://app.slack.com/client/T03RZGPFR/CLX41ASCS)
|
||||
|
||||
|
||||
A Clojure [babushka](https://en.wikipedia.org/wiki/Headscarf) for the grey areas of Bash.
|
||||
|
||||
|
|
@ -125,22 +127,28 @@ You may also download a binary from [Github](https://github.com/borkdude/babashk
|
|||
## Usage
|
||||
|
||||
``` shellsession
|
||||
Usage: bb [ -i | -I ] [ -o | -O ] [ --stream ] ( -e <expression> | -f <file> | --socket-repl [<host>:]<port> )
|
||||
Usage: bb [ -i | -I ] [ -o | -O ] [ --stream ] [--verbose]
|
||||
[ ( --classpath | -cp ) <cp> ] [ ( --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.
|
||||
-o: write lines to stdout.
|
||||
-O: write EDN values to stdout.
|
||||
--stream: stream over lines or EDN values from stdin. Combined with -i or -I *in* becomes a single value per iteration.
|
||||
-e, --eval <expression>: evaluate an expression
|
||||
-f, --file <path>: evaluate a file
|
||||
--socket-repl: start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666).
|
||||
--time: print execution time before exiting.
|
||||
--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.
|
||||
-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.
|
||||
-e, --eval <expr> Evaluate an expression.
|
||||
-f, --file <path> Evaluate a file.
|
||||
-cp, --classpath Classpath to use.
|
||||
-m, --main <ns> Call the -main function from namespace with args.
|
||||
--repl Start REPL
|
||||
--socket-repl Start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666).
|
||||
--time Print execution time before exiting.
|
||||
|
||||
If neither -e, -f, or --socket-repl are specified, then the first argument that is not parsed as a option is treated as a file if it exists, or as an expression otherwise.
|
||||
Everything after that is bound to *command-line-args*.
|
||||
|
|
@ -149,8 +157,9 @@ Everything after that is bound to *command-line-args*.
|
|||
The `clojure.core` functions are accessible without a namespace alias.
|
||||
|
||||
The following namespaces are required by default and available through the
|
||||
pre-defined aliases. You may use `require` + `:as` and/or `:refer` on these
|
||||
namespaces. If not all vars are available, they are enumerated explicitly.
|
||||
pre-defined aliases in the `user` namespace. You may use `require` + `:as`
|
||||
and/or `:refer` on these namespaces. If not all vars are available, they are
|
||||
enumerated explicitly.
|
||||
|
||||
- `clojure.string` aliased as `str`
|
||||
- `clojure.set` aliased as `set`
|
||||
|
|
@ -167,6 +176,7 @@ namespaces. If not all vars are available, they are enumerated explicitly.
|
|||
aliased as `conch`
|
||||
- [`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:
|
||||
|
||||
|
|
@ -305,6 +315,72 @@ $ cat script.clj
|
|||
("hello" "1" "2" "3")
|
||||
```
|
||||
|
||||
## Preloads
|
||||
|
||||
The environment variable `BABASHKA_PRELOADS` allows to define code that will be
|
||||
available in all subsequent usages of babashka.
|
||||
|
||||
``` shellsession
|
||||
BABASHKA_PRELOADS='(defn foo [x] (+ x 2))'
|
||||
BABASHKA_PRELOADS=$BABASHKA_PRELOADS' (defn bar [x] (* x 2))'
|
||||
export BABASHKA_PRELOADS
|
||||
```
|
||||
|
||||
Note that you can concatenate multiple expressions. Now you can use these functions in babashka:
|
||||
|
||||
``` shellsession
|
||||
$ bb '(-> (foo *in*) bar)' <<< 1
|
||||
6
|
||||
```
|
||||
|
||||
You can also preload an entire file using `load-file`:
|
||||
|
||||
``` shellsession
|
||||
export BABASHKA_PRELOADS='(load-file "my_awesome_prelude.clj")'
|
||||
```
|
||||
|
||||
Note that `*in*` is not available in preloads.
|
||||
|
||||
## Classpath
|
||||
|
||||
Babashka accepts a `--classpath` option that will be used to search for
|
||||
namespaces and load them:
|
||||
|
||||
``` clojure
|
||||
$ cat src/my/namespace.clj
|
||||
(ns my.namespace)
|
||||
(defn -main [& _args]
|
||||
(println "Hello from my namespace!"))
|
||||
|
||||
$ bb --classpath src --main my.namespace
|
||||
Hello from my namespace!
|
||||
```
|
||||
|
||||
Note that you can use the `clojure` tool to produce classpaths and download dependencies:
|
||||
|
||||
``` shellsession
|
||||
$ cat deps.edn
|
||||
{:deps
|
||||
{my_gist_script
|
||||
{:git/url "https://gist.github.com/borkdude/263b150607f3ce03630e114611a4ef42"
|
||||
:sha "cfc761d06dfb30bb77166b45d439fe8fe54a31b8"}}}
|
||||
|
||||
|
||||
$ CLASSPATH=$(clojure -Spath)
|
||||
$ bb --classpath "$CLASSPATH" --main my-gist-script
|
||||
Hello from gist script!
|
||||
```
|
||||
|
||||
If there is no `--classpath` argument, the `BABASHKA_CLASSPATH` environment
|
||||
variable will be used:
|
||||
|
||||
``` shellsession
|
||||
$ export BABASHKA_CLASSPATH=$(clojure -Spath)
|
||||
$ export BABASHKA_PRELOADS="(require '[my-gist-script])"
|
||||
$ bb "(my-gist-script/-main)"
|
||||
Hello from gist script!
|
||||
```
|
||||
|
||||
## Parsing command line arguments
|
||||
|
||||
Babashka ships with `clojure.tools.cli`:
|
||||
|
|
@ -342,32 +418,6 @@ $ ./bb example.clj
|
|||
babashka doesn't support in-ns yet!
|
||||
```
|
||||
|
||||
## Preloads
|
||||
|
||||
The environment variable `BABASHKA_PRELOADS` allows to define code that will be
|
||||
available in all subsequent usages of babashka.
|
||||
|
||||
``` shellsession
|
||||
BABASHKA_PRELOADS='(defn foo [x] (+ x 2))'
|
||||
BABASHKA_PRELOADS=$BABASHKA_PRELOADS' (defn bar [x] (* x 2))'
|
||||
export BABASHKA_PRELOADS
|
||||
```
|
||||
|
||||
Note that you can concatenate multiple expressions. Now you can use these functions in babashka:
|
||||
|
||||
``` shellsession
|
||||
$ bb '(-> (foo *in*) bar)' <<< 1
|
||||
6
|
||||
```
|
||||
|
||||
You can also preload an entire file using `load-file`:
|
||||
|
||||
``` shellsession
|
||||
export BABASHKA_PRELOADS='(load-file "my_awesome_prelude.clj")'
|
||||
```
|
||||
|
||||
Note that `*in*` is not available in preloads.
|
||||
|
||||
## Socket REPL
|
||||
|
||||
Start the socket REPL like this:
|
||||
|
|
@ -442,16 +492,8 @@ Differences with Clojure:
|
|||
|
||||
- A subset of Java classes are supported.
|
||||
|
||||
- Only the `clojure.core`, `clojure.set` and `clojure.string` namespaces are
|
||||
available from Clojure.
|
||||
|
||||
- There is no classpath and no support for loading code from Maven/Clojars
|
||||
dependencies. However, you can use `load-file` to load external code from
|
||||
disk.
|
||||
|
||||
- `require` does not load files; it only provides a way to create different
|
||||
aliases for included namespaces, which makes it easier to make scripts
|
||||
portable between the JVM and babashka.
|
||||
- Only the `clojure.core`, `clojure.set`, `clojure.string` and `clojure.walk`
|
||||
namespaces are available from Clojure.
|
||||
|
||||
- Interpretation comes with overhead. Therefore tight loops are likely slower
|
||||
than in Clojure on the JVM.
|
||||
|
|
@ -588,7 +630,7 @@ less
|
|||
|
||||
### Portable tree command
|
||||
|
||||
See [examples/tree.clj](https://github.com/borkdude/babashka/blob/8afb87142e0e4da8b6f912cfd7daf9c30b805ab3/examples/tree.clj).
|
||||
See [examples/tree.clj](https://github.com/borkdude/babashka/blob/master/examples/tree.clj).
|
||||
|
||||
``` shellsession
|
||||
$ clojure -Sdeps '{:deps {org.clojure/tools.cli {:mvn/version "0.4.2"}}}' examples/tree.clj src
|
||||
|
|
@ -605,6 +647,22 @@ src
|
|||
├── impl
|
||||
│ ├── tools
|
||||
│ │ └── cli.clj
|
||||
...
|
||||
```
|
||||
|
||||
### List outdated maven dependencies
|
||||
|
||||
See [examples/outdated.clj](https://github.com/borkdude/babashka/blob/master/examples/outdated.clj).
|
||||
Inspired by an idea from [@seancorfield](https://github.com/seancorfield).
|
||||
|
||||
``` shellsession
|
||||
$ cat /tmp/deps.edn
|
||||
{:deps {cheshire {:mvn/version "5.8.1"}
|
||||
clj-http {:mvn/version "3.4.0"}}}
|
||||
|
||||
$ examples/outdated.clj /tmp/deps.edn
|
||||
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
|
||||
```
|
||||
|
||||
## Thanks
|
||||
|
|
|
|||
34
examples/outdated.clj
Executable file
34
examples/outdated.clj
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env bb
|
||||
|
||||
(require '[clojure.edn :as edn])
|
||||
(require '[clojure.java.shell :refer [sh]])
|
||||
(require '[clojure.string :as str])
|
||||
|
||||
(def deps (-> (slurp (or (first *command-line-args*)
|
||||
"deps.edn"))
|
||||
edn/read-string
|
||||
:deps))
|
||||
(def with-release (zipmap (keys deps)
|
||||
(map #(assoc % :mvn/version "RELEASE")
|
||||
(vals deps))))
|
||||
|
||||
(defn deps->versions [deps]
|
||||
(let [res (sh "clojure" "-Sdeps" (str {:deps deps}) "-Stree")
|
||||
tree (:out res)
|
||||
lines (str/split tree #"\n")
|
||||
top-level (remove #(str/starts-with? % " ") lines)
|
||||
deps (map #(str/split % #" ") top-level)]
|
||||
(reduce (fn [acc [dep version]]
|
||||
(assoc acc dep version))
|
||||
{}
|
||||
deps)))
|
||||
|
||||
(def version-map (deps->versions deps))
|
||||
(def new-version-map (deps->versions with-release))
|
||||
|
||||
(doseq [[dep version] version-map
|
||||
:let [new-version (get new-version-map dep)]
|
||||
:when (not= version new-version)]
|
||||
(println dep "can be upgraded from" version "to" new-version))
|
||||
|
||||
;; Inspired by an idea from @seancorfield on Clojurians Slack
|
||||
|
|
@ -11,10 +11,11 @@
|
|||
:resource-paths ["resources" "sci/resources"]
|
||||
:dependencies [[org.clojure/clojure "1.10.1"]
|
||||
[org.clojure/tools.reader "1.3.2"]
|
||||
[borkdude/edamame "0.0.9-alpha.2"]
|
||||
[borkdude/edamame "0.0.10-alpha.2"]
|
||||
[org.clojure/core.async "0.4.500"]
|
||||
[org.clojure/tools.cli "0.4.2"]
|
||||
[org.clojure/data.csv "0.1.4"]
|
||||
[cheshire "5.9.0"]
|
||||
[io.aviso/pretty "0.1.37"]]
|
||||
:profiles {:test {:dependencies [[clj-commons/conch "0.9.2"]]}
|
||||
:uberjar {:global-vars {*assert* false}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@
|
|||
},
|
||||
{
|
||||
"name":"java.io.BufferedReader",
|
||||
"allPublicMethods":true
|
||||
"allPublicMethods":true,
|
||||
"allPublicFields": true,
|
||||
"allPublicConstructors": true
|
||||
},
|
||||
{
|
||||
"name": "java.lang.Class",
|
||||
|
|
@ -122,6 +124,24 @@
|
|||
"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
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
0.0.37
|
||||
0.0.42
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
0.0.38-SNAPSHOT
|
||||
0.0.43-SNAPSHOT
|
||||
|
|
|
|||
2
sci
2
sci
|
|
@ -1 +1 @@
|
|||
Subproject commit 9ab0b1d181605e4c78eb8a90a75982df5c822514
|
||||
Subproject commit 07d28ee572e90a629e01b10aa5b98cb33ccdc1e5
|
||||
13
script/test
13
script/test
|
|
@ -1,6 +1,15 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -eo pipefail
|
||||
export BABASHKA_PRELOADS='(defn __bb__foo [] "foo") (defn __bb__bar [] "bar")'
|
||||
BABASHKA_PRELOADS=""
|
||||
BABASHKA_CLASSPATH=""
|
||||
lein test "$@"
|
||||
|
||||
lein test
|
||||
BABASHKA_PRELOADS='(defn __bb__foo [] "foo") (defn __bb__bar [] "bar")'
|
||||
BABASHKA_PRELOADS_TEST=true
|
||||
lein test :only babashka.main-test/preloads-test
|
||||
|
||||
BABASHKA_PRELOADS="(require '[env-ns])"
|
||||
BABASHKA_CLASSPATH_TEST=true
|
||||
BABASHKA_CLASSPATH="test-resources/babashka/src_for_classpath_test/env"
|
||||
lein test :only babashka.classpath-test/classpath-env-test
|
||||
|
|
|
|||
219
src-bash/bbk
Executable file
219
src-bash/bbk
Executable file
|
|
@ -0,0 +1,219 @@
|
|||
#!/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
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
(defn thread
|
||||
[_ _ & body]
|
||||
`(~'async/thread-call (fn [] ~@body)))
|
||||
`(~'clojure.core.async/thread-call (fn [] ~@body)))
|
||||
|
||||
(def async-namespace
|
||||
{'<!! async/<!!
|
||||
|
|
|
|||
14
src/babashka/impl/cheshire.clj
Normal file
14
src/babashka/impl/cheshire.clj
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
(ns babashka.impl.cheshire
|
||||
{:no-doc true}
|
||||
(:require [cheshire.core :as json]))
|
||||
|
||||
(def cheshire-core-namespace
|
||||
{'encode json/encode
|
||||
'generate-string json/generate-string
|
||||
'encode-stream json/encode-stream
|
||||
'generate-stream json/generate-stream
|
||||
'encode-smile json/encode-smile
|
||||
'generate-smile json/generate-smile
|
||||
'decode json/decode
|
||||
'parse-string json/parse-string
|
||||
'decode-smile json/decode-smile})
|
||||
62
src/babashka/impl/classpath.clj
Normal file
62
src/babashka/impl/classpath.clj
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
(ns babashka.impl.classpath
|
||||
{:no-doc true}
|
||||
(:require [clojure.java.io :as io]
|
||||
[clojure.string :as str])
|
||||
(:import [java.util.jar JarFile JarFile$JarFileEntry]))
|
||||
|
||||
(set! *warn-on-reflection* true)
|
||||
|
||||
(defprotocol IResourceResolver
|
||||
(getResource [this path]))
|
||||
|
||||
(deftype DirectoryResolver [path]
|
||||
IResourceResolver
|
||||
(getResource [this resource-path]
|
||||
(let [f (io/file path resource-path)]
|
||||
(when (.exists f)
|
||||
(slurp f)))))
|
||||
|
||||
(defn path-from-jar
|
||||
[^java.io.File jar-file path]
|
||||
(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)]
|
||||
entry)))
|
||||
|
||||
(deftype JarFileResolver [path]
|
||||
IResourceResolver
|
||||
(getResource [this resource-path]
|
||||
(path-from-jar path resource-path)))
|
||||
|
||||
(defn part->entry [part]
|
||||
(if (str/ends-with? part ".jar")
|
||||
(JarFileResolver. (io/file part))
|
||||
(DirectoryResolver. (io/file part))))
|
||||
|
||||
(deftype Loader [entries]
|
||||
IResourceResolver
|
||||
(getResource [this resource-path]
|
||||
(some #(getResource % resource-path) 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]
|
||||
(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)))
|
||||
|
||||
;;;; 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)
|
||||
)
|
||||
|
|
@ -2,65 +2,8 @@
|
|||
{:no-doc true}
|
||||
(:refer-clojure :exclude [future]))
|
||||
|
||||
(defn future
|
||||
[_ _ & body]
|
||||
`(~'future-call (fn [] ~@body)))
|
||||
|
||||
(defn __close!__ [^java.io.Closeable x]
|
||||
(.close x))
|
||||
|
||||
(defn with-open*
|
||||
[_ _ bindings & body]
|
||||
(cond
|
||||
(= (count bindings) 0) `(do ~@body)
|
||||
(symbol? (bindings 0)) `(let ~(subvec bindings 0 2)
|
||||
(try
|
||||
(with-open ~(subvec bindings 2) ~@body)
|
||||
(finally
|
||||
(~'__close!__ ~(bindings 0)))))
|
||||
:else (throw (IllegalArgumentException.
|
||||
"with-open only allows Symbols in bindings"))))
|
||||
|
||||
(defn binding*
|
||||
"This macro only works with symbols that evaluate to vars themselves. See `*in*` and `*out*` below."
|
||||
[_ _ bindings & body]
|
||||
`(do
|
||||
(let []
|
||||
(push-thread-bindings (hash-map ~@bindings))
|
||||
(try
|
||||
~@body
|
||||
(finally
|
||||
(pop-thread-bindings))))))
|
||||
|
||||
;; this works now!
|
||||
"(def w (java.io.StringWriter.)) (push-thread-bindings {clojure.core/*out* w}) (try (println \"hello\") (finally (pop-thread-bindings))) (prn \">\" (str w))"
|
||||
|
||||
;; this also works now! "(def w (java.io.StringWriter.)) (binding [clojure.core/*out* w] (println \"hello\")) (str w)"
|
||||
|
||||
(defn with-out-str*
|
||||
[_ _ & body]
|
||||
`(let [s# (java.io.StringWriter.)]
|
||||
(binding [*out* s#]
|
||||
~@body
|
||||
(str s#))))
|
||||
|
||||
(defn with-in-str*
|
||||
[_ _ s & body]
|
||||
`(with-open [s# (-> (java.io.StringReader. ~s) clojure.lang.LineNumberingPushbackReader.)]
|
||||
(binding [*in* s#]
|
||||
~@body)))
|
||||
|
||||
(def core-extras
|
||||
{'*in* #'*in*
|
||||
'*out* #'*out*
|
||||
'binding (with-meta binding* {:sci/macro true})
|
||||
'file-seq file-seq
|
||||
'future-call future-call
|
||||
'future (with-meta future {:sci/macro true})
|
||||
'future-cancel future-cancel
|
||||
'future-cancelled? future-cancelled?
|
||||
'future-done? future-done?
|
||||
'future? future?
|
||||
{'file-seq file-seq
|
||||
'agent agent
|
||||
'send send
|
||||
'send-off send-off
|
||||
|
|
@ -68,18 +11,4 @@
|
|||
'deliver deliver
|
||||
'shutdown-agents shutdown-agents
|
||||
'slurp slurp
|
||||
'spit spit
|
||||
'pmap pmap
|
||||
'pr pr
|
||||
'prn prn
|
||||
'print print
|
||||
'println println
|
||||
'println-str println-str
|
||||
'pop-thread-bindings pop-thread-bindings
|
||||
'push-thread-bindings push-thread-bindings
|
||||
'flush flush
|
||||
'read-line read-line
|
||||
'__close!__ __close!__
|
||||
'with-open (with-meta with-open* {:sci/macro true})
|
||||
'with-out-str (with-meta with-out-str* {:sci/macro true})
|
||||
'with-in-str (with-meta with-in-str* {:sci/macro true})})
|
||||
'spit spit})
|
||||
|
|
|
|||
54
src/babashka/impl/repl.clj
Normal file
54
src/babashka/impl/repl.clj
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
(ns babashka.impl.repl
|
||||
{:no-doc true}
|
||||
(:require
|
||||
[babashka.impl.clojure.main :as m]
|
||||
[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]))
|
||||
|
||||
(defn repl
|
||||
"REPL with predefined hooks for attachable socket server."
|
||||
[sci-ctx]
|
||||
(let [in (r/indexing-push-back-reader (r/push-back-reader *in*))]
|
||||
(m/repl
|
||||
:init #(do (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]
|
||||
(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)
|
||||
(identical? :repl/exit v)
|
||||
(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
|
||||
:env
|
||||
(fn [env]
|
||||
(swap! env update-in [:namespaces 'clojure.core]
|
||||
assoc
|
||||
'*1 *1
|
||||
'*2 *2
|
||||
'*3 *3
|
||||
'*e *e)
|
||||
env))
|
||||
expr))]
|
||||
ret))
|
||||
:need-prompt (fn [] true)
|
||||
:prompt #(printf "%s=> " (-> sci-ctx :env deref :current-ns)))))
|
||||
|
||||
(defn start-repl! [ctx]
|
||||
(let [sci-ctx (init ctx)]
|
||||
(repl sci-ctx)))
|
||||
|
|
@ -2,51 +2,12 @@
|
|||
{:no-doc true}
|
||||
(:require
|
||||
[babashka.impl.clojure.core.server :as server]
|
||||
[babashka.impl.clojure.main :as m]
|
||||
[clojure.java.io :as io]
|
||||
[babashka.impl.repl :as repl]
|
||||
[clojure.string :as str]
|
||||
[clojure.tools.reader.reader-types :as r]
|
||||
[sci.impl.interpreter :refer [opts->ctx eval-form]]
|
||||
[sci.impl.parser :as parser]))
|
||||
[sci.impl.opts :refer [init]]))
|
||||
|
||||
(set! *warn-on-reflection* true)
|
||||
|
||||
(defn repl
|
||||
"REPL with predefined hooks for attachable socket server."
|
||||
[sci-ctx]
|
||||
(let [in (r/indexing-push-back-reader (r/push-back-reader *in*))]
|
||||
(m/repl
|
||||
:init #(do (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]
|
||||
(if (r/peek-char in) ;; if this is nil, we reached EOF
|
||||
(let [v (parser/parse-next in #{:bb} {:current (-> sci-ctx :env deref :current-ns)})]
|
||||
(if (or (identical? :repl/quit v)
|
||||
(identical? :repl/exit v)
|
||||
(identical? :edamame.impl.parser/eof v))
|
||||
request-exit
|
||||
v))
|
||||
request-exit))
|
||||
:eval (fn [expr]
|
||||
(let [ret (eval-form (update sci-ctx
|
||||
:env
|
||||
(fn [env]
|
||||
(swap! env update-in [:namespaces 'clojure.core]
|
||||
assoc
|
||||
'*1 *1
|
||||
'*2 *2
|
||||
'*3 *3
|
||||
'*e *e)
|
||||
env))
|
||||
expr)]
|
||||
ret))
|
||||
:need-prompt (fn [] true)
|
||||
:prompt #(printf "%s=> " (-> sci-ctx :env deref :current-ns)))))
|
||||
|
||||
(defn start-repl! [host+port sci-opts]
|
||||
(let [parts (str/split host+port #":")
|
||||
[host port] (if (= 1 (count parts))
|
||||
|
|
@ -54,12 +15,12 @@
|
|||
[(first parts) (Integer. ^String (second parts))])
|
||||
host+port (if-not host (str "localhost:" port)
|
||||
host+port)
|
||||
sci-ctx (opts->ctx sci-opts)
|
||||
sci-ctx (init sci-opts)
|
||||
socket (server/start-server
|
||||
{:address host
|
||||
:port port
|
||||
:name "bb"
|
||||
:accept babashka.impl.socket-repl/repl
|
||||
:accept babashka.impl.repl/repl
|
||||
:args [sci-ctx]})]
|
||||
(println "Babashka socket REPL started at" host+port)
|
||||
socket))
|
||||
|
|
|
|||
9
src/babashka/impl/utils.clj
Normal file
9
src/babashka/impl/utils.clj
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
(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)))
|
||||
|
|
@ -2,21 +2,25 @@
|
|||
{:no-doc true}
|
||||
(:require
|
||||
[babashka.impl.async :refer [async-namespace]]
|
||||
[babashka.impl.cheshire :refer [cheshire-core-namespace]]
|
||||
[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.csv :as csv]
|
||||
[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]]
|
||||
[io.aviso.ansi :as ansi]
|
||||
[babashka.impl.utils :refer [eval-string]]
|
||||
[babashka.wait :as wait]
|
||||
[clojure.edn :as edn]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.java.shell :as shell]
|
||||
[clojure.string :as str]
|
||||
[sci.core :as sci])
|
||||
[io.aviso.ansi :as ansi]
|
||||
[sci.addons :as addons])
|
||||
(:gen-class))
|
||||
|
||||
(set! *warn-on-reflection* true)
|
||||
|
|
@ -65,6 +69,11 @@
|
|||
(recur (rest options)
|
||||
(assoc opts-map
|
||||
:file (first options))))
|
||||
("--repl")
|
||||
(let [options (rest options)]
|
||||
(recur (rest options)
|
||||
(assoc opts-map
|
||||
:repl true)))
|
||||
("--socket-repl")
|
||||
(let [options (rest options)]
|
||||
(recur (rest options)
|
||||
|
|
@ -74,7 +83,15 @@
|
|||
(let [options (rest options)]
|
||||
(recur (rest options)
|
||||
(assoc opts-map :expression (first options))))
|
||||
(if (some opts-map [:file :socket-repl :expression])
|
||||
("--classpath", "-cp")
|
||||
(let [options (rest options)]
|
||||
(recur (rest options)
|
||||
(assoc opts-map :classpath (first options))))
|
||||
("--main", "-m")
|
||||
(let [options (rest options)]
|
||||
(recur (rest options)
|
||||
(assoc opts-map :main (first options))))
|
||||
(if (some opts-map [:file :socket-repl :expression :main])
|
||||
(assoc opts-map
|
||||
:command-line-args options)
|
||||
(if (and (not= \( (first (str/trim opt)))
|
||||
|
|
@ -104,31 +121,36 @@
|
|||
(defn print-version []
|
||||
(println (str "babashka v"(str/trim (slurp (io/resource "BABASHKA_VERSION"))))))
|
||||
|
||||
(def usage-string "Usage: bb [ -i | -I ] [ -o | -O ] [--verbose] [ --stream ] ( -e <expression> | -f <file> | --socket-repl [<host>:]<port> )")
|
||||
(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> )
|
||||
[ arg* ]")
|
||||
(defn print-usage []
|
||||
(println usage-string))
|
||||
|
||||
(defn print-help []
|
||||
(println (str "babashka v" (str/trim (slurp (io/resource "BABASHKA_VERSION")))))
|
||||
(println (str "Babashka v" (str/trim (slurp (io/resource "BABASHKA_VERSION")))))
|
||||
;; (println (str "sci v" (str/trim (slurp (io/resource "SCI_VERSION")))))
|
||||
(println)
|
||||
(print-usage)
|
||||
(println)
|
||||
(println "Options:")
|
||||
(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.
|
||||
-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.
|
||||
-e, --eval <expression>: evaluate an expression
|
||||
-f, --file <path>: evaluate a file
|
||||
--socket-repl: start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666).
|
||||
--time: print execution time before exiting.
|
||||
--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.
|
||||
-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.
|
||||
-e, --eval <expr> Evaluate an expression.
|
||||
-f, --file <path> Evaluate a file.
|
||||
-cp, --classpath Classpath to use.
|
||||
-m, --main <ns> Call the -main function from namespace with args.
|
||||
--repl Start REPL
|
||||
--socket-repl Start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666).
|
||||
--time Print execution time before exiting.
|
||||
|
||||
If neither -e, -f, or --socket-repl are specified, then the first argument that is not parsed as a option is treated as a file if it exists, or as an expression otherwise.
|
||||
Everything after that is bound to *command-line-args*."))
|
||||
|
|
@ -147,10 +169,17 @@ Everything after that is bound to *command-line-args*."))
|
|||
|
||||
(defn load-file* [ctx file]
|
||||
(let [s (slurp file)]
|
||||
(sci/eval-string s ctx)))
|
||||
(eval-string s ctx)))
|
||||
|
||||
(defn eval* [ctx form]
|
||||
(sci/eval-string (pr-str form) ctx))
|
||||
(eval-string (pr-str form) ctx))
|
||||
|
||||
(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 start-socket-repl! [address ctx read-next]
|
||||
(let [ctx (update ctx :bindings assoc
|
||||
|
|
@ -164,6 +193,10 @@ Everything after that is bound to *command-line-args*."))
|
|||
(defn exit [n]
|
||||
(throw (ex-info "" {:bb/exit-code n})))
|
||||
|
||||
;; (sci/set-var-root! sci/*in* *in*)
|
||||
;; (sci/set-var-root! sci/*out* *out*)
|
||||
;; (sci/set-var-root! sci/*err* *err*)
|
||||
|
||||
(defn main
|
||||
[& args]
|
||||
(handle-pipe!)
|
||||
|
|
@ -172,7 +205,10 @@ Everything after that is bound to *command-line-args*."))
|
|||
(let [t0 (System/currentTimeMillis)
|
||||
{:keys [:version :shell-in :edn-in :shell-out :edn-out
|
||||
:help? :file :command-line-args
|
||||
:expression :stream? :time? :socket-repl :verbose?] :as _opts}
|
||||
:expression :stream? :time?
|
||||
:repl :socket-repl
|
||||
:verbose? :classpath
|
||||
:main] :as _opts}
|
||||
(parse-opts args)
|
||||
read-next (fn [*in*]
|
||||
(if (pipe-signal-received?)
|
||||
|
|
@ -187,6 +223,13 @@ Everything after that is bound to *command-line-args*."))
|
|||
:else
|
||||
(edn/read *in*))))))
|
||||
env (atom {})
|
||||
classpath (or classpath
|
||||
(System/getenv "BABASHKA_CLASSPATH"))
|
||||
loader (when classpath
|
||||
(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
|
||||
|
|
@ -195,7 +238,8 @@ Everything after that is bound to *command-line-args*."))
|
|||
io clojure.java.io
|
||||
conch me.raynes.conch.low-level
|
||||
async clojure.core.async
|
||||
csv clojure.data.csv}
|
||||
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
|
||||
|
|
@ -208,6 +252,7 @@ Everything after that is bound to *command-line-args*."))
|
|||
'me.raynes.conch.low-level conch-namespace
|
||||
'clojure.core.async async-namespace
|
||||
'clojure.data.csv csv/csv-namespace
|
||||
'cheshire.core cheshire-core-namespace
|
||||
'io.aviso.ansi {'blue ansi/blue
|
||||
'red ansi/red}}
|
||||
:bindings {'java.lang.System/exit exit ;; override exit, so we have more control
|
||||
|
|
@ -218,6 +263,7 @@ Everything after that is bound to *command-line-args*."))
|
|||
'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
|
||||
|
|
@ -232,6 +278,9 @@ Everything after that is bound to *command-line-args*."))
|
|||
'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
|
||||
|
|
@ -248,10 +297,16 @@ Everything after that is bound to *command-line-args*."))
|
|||
File java.io.File
|
||||
String java.lang.String
|
||||
System java.lang.System
|
||||
Thread java.lang.Thread}}
|
||||
Thread java.lang.Thread}
|
||||
:load-fn load-fn}
|
||||
ctx (update ctx :bindings assoc 'eval #(eval* ctx %)
|
||||
'load-file #(load-file* ctx %))
|
||||
_preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim) (sci/eval-string ctx))
|
||||
'load-file #(load-file* ctx %))
|
||||
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)
|
||||
exit-code
|
||||
(or
|
||||
#_(binding [*out* *err*]
|
||||
|
|
@ -261,6 +316,7 @@ 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
|
||||
(try
|
||||
|
|
@ -272,7 +328,7 @@ Everything after that is bound to *command-line-args*."))
|
|||
{:sci/deref! true})) in)]
|
||||
(if (identical? ::EOF in)
|
||||
[nil 0] ;; done streaming
|
||||
(let [res [(let [res (sci/eval-string expr ctx)]
|
||||
(let [res [(let [res (eval-string expr ctx)]
|
||||
(when (some? res)
|
||||
(if-let [pr-f (cond shell-out println
|
||||
edn-out prn)]
|
||||
|
|
@ -285,7 +341,7 @@ Everything after that is bound to *command-line-args*."))
|
|||
(if stream?
|
||||
(recur (read-next *in*))
|
||||
res)))))
|
||||
[(print-help) 1]))
|
||||
[(start-repl! ctx #(read-next *in*)) 0]))
|
||||
(catch Throwable e
|
||||
(binding [*out* *err*]
|
||||
(let [d (ex-data e)
|
||||
|
|
|
|||
4
test-resources/babashka/src_for_classpath_test/env/env_ns.clj
vendored
Normal file
4
test-resources/babashka/src_for_classpath_test/env/env_ns.clj
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
(ns env-ns)
|
||||
|
||||
(defn foo []
|
||||
"env!")
|
||||
BIN
test-resources/babashka/src_for_classpath_test/foo.jar
Normal file
BIN
test-resources/babashka/src_for_classpath_test/foo.jar
Normal file
Binary file not shown.
|
|
@ -0,0 +1,5 @@
|
|||
(ns my.impl)
|
||||
|
||||
(defn impl-fn
|
||||
"identity"
|
||||
[x] x)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
(ns my.main
|
||||
(:require [my.impl :as impl]))
|
||||
|
||||
(defn -main [& args]
|
||||
(impl/impl-fn args))
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
(ns my-script)
|
||||
|
||||
(defn foo []
|
||||
::bb)
|
||||
27
test/babashka/classpath_test.clj
Normal file
27
test/babashka/classpath_test.clj
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
(ns babashka.classpath-test
|
||||
(:require
|
||||
[babashka.test-utils :as tu]
|
||||
[clojure.edn :as edn]
|
||||
[clojure.test :as t :refer [deftest is]]))
|
||||
|
||||
(defn bb [input & args]
|
||||
(edn/read-string (apply tu/bb (when (some? input) (str input)) (map str args))))
|
||||
|
||||
(deftest classpath-test
|
||||
(is (= :my-script/bb
|
||||
(bb nil "--classpath" "test-resources/babashka/src_for_classpath_test"
|
||||
"(require '[my-script :as ms]) (ms/foo)")))
|
||||
(is (= "hello from foo\n"
|
||||
(tu/bb nil "--classpath" "test-resources/babashka/src_for_classpath_test/foo.jar"
|
||||
"(require '[foo :as f]) (f/foo)"))))
|
||||
|
||||
(deftest classpath-env-test
|
||||
;; for this test you have to set `BABASHKA_CLASSPATH` to test-resources/babashka/src_for_classpath_test/env
|
||||
;; and `BABASHKA_PRELOADS` to "(require '[env-ns])"
|
||||
(when (System/getenv "BABASHKA_CLASSPATH_TEST")
|
||||
(println (System/getenv "BABASHKA_CLASSPATH"))
|
||||
(is (= "env!" (bb nil "(env-ns/foo)")))))
|
||||
|
||||
(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"))))
|
||||
35
test/babashka/impl/repl_test.clj
Normal file
35
test/babashka/impl/repl_test.clj
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
(ns babashka.impl.repl-test
|
||||
(:require
|
||||
[babashka.impl.repl :refer [start-repl!]]
|
||||
[clojure.string :as str]
|
||||
[clojure.test :as t :refer [deftest is]]))
|
||||
|
||||
(set! *warn-on-reflection* true)
|
||||
|
||||
(defn repl! []
|
||||
(start-repl! {:bindings {(with-meta '*in*
|
||||
{:sci/deref! true})
|
||||
(delay [1 2 3])
|
||||
'*command-line-args*
|
||||
["a" "b" "c"]}
|
||||
:env (atom {})}))
|
||||
|
||||
(defn assert-repl [expr expected]
|
||||
(is (str/includes? (with-out-str
|
||||
(with-in-str (str expr "\n:repl/quit")
|
||||
(repl!))) expected)))
|
||||
|
||||
(deftest repl-test
|
||||
(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]"))
|
||||
|
||||
;;;; Scratch
|
||||
|
||||
(comment
|
||||
)
|
||||
|
|
@ -4,19 +4,29 @@
|
|||
[babashka.test-utils :as tu]
|
||||
[clojure.java.shell :refer [sh]]
|
||||
[clojure.string :as str]
|
||||
[clojure.test :as t :refer [deftest is testing]]))
|
||||
[clojure.test :as t :refer [deftest is testing]]
|
||||
[clojure.java.io :as io]))
|
||||
|
||||
(def mac?
|
||||
(str/includes?
|
||||
(str/lower-case (System/getProperty "os.name"))
|
||||
"mac"))
|
||||
(set! *warn-on-reflection* true)
|
||||
|
||||
(defn socket-command [expr]
|
||||
(let [expr (format "echo \"%s\n:repl/exit\" | nc 127.0.0.1 1666"
|
||||
(pr-str expr))
|
||||
ret (sh "bash" "-c"
|
||||
expr)]
|
||||
(:out ret)))
|
||||
(defn socket-command [expr expected]
|
||||
(with-open [socket (java.net.Socket. "127.0.0.1" 1666)
|
||||
reader (io/reader socket)
|
||||
sw (java.io.StringWriter.)
|
||||
writer (io/writer socket)]
|
||||
(binding [*out* writer]
|
||||
(println (str expr))
|
||||
(println ":repl/exit\n"))
|
||||
(loop []
|
||||
(when-let [l (.readLine ^java.io.BufferedReader reader)]
|
||||
(binding [*out* sw]
|
||||
(println l))
|
||||
(recur)))
|
||||
(let [s (str sw)]
|
||||
(is (str/includes? s expected)
|
||||
(format "\"%s\" does not contain \"%s\""
|
||||
s expected))
|
||||
s)))
|
||||
|
||||
(deftest socket-repl-test
|
||||
(try
|
||||
|
|
@ -26,7 +36,8 @@
|
|||
(delay [1 2 3])
|
||||
'*command-line-args*
|
||||
["a" "b" "c"]}
|
||||
:env (atom {})})
|
||||
:env (atom {})
|
||||
:features #{:bb}})
|
||||
(future
|
||||
(sh "bash" "-c"
|
||||
"echo '[1 2 3]' | ./bb --socket-repl 0.0.0.0:1666 a b c")))
|
||||
|
|
@ -35,36 +46,19 @@
|
|||
(while (not (zero? (:exit
|
||||
(sh "bash" "-c"
|
||||
"lsof -t -i:1666"))))))
|
||||
(is (str/includes? (socket-command '(+ 1 2 3))
|
||||
"user=> 6"))
|
||||
(testing "ctrl-d exits normally, doesn't print nil"
|
||||
(is (str/ends-with? (:out (sh "bash" "-c"
|
||||
(if mac? ;; mac doesn't support -q
|
||||
"echo \"(inc 1336)\" | nc 127.0.0.1 1666"
|
||||
"echo \"(inc 1336)\" | nc -q 1 127.0.0.1 1666")))
|
||||
"1337\nuser=> ")))
|
||||
(is (socket-command "(+ 1 2 3)" "user=> 6"))
|
||||
(testing "*in*"
|
||||
(is (str/includes? (socket-command "*in*")
|
||||
"[1 2 3]")))
|
||||
(is (socket-command "*in*" "[1 2 3]")))
|
||||
(testing "*command-line-args*"
|
||||
(is (str/includes? (socket-command '*command-line-args*)
|
||||
"\"a\" \"b\" \"c\"")))
|
||||
(is (socket-command '*command-line-args* "\"a\" \"b\" \"c\"")))
|
||||
(testing "&env"
|
||||
(socket-command '(defmacro bindings [] (mapv #(list 'quote %) (keys &env))))
|
||||
(socket-command '(defn bar [x y z] (bindings)))
|
||||
(is (str/includes? (socket-command '(bar 1 2 3))
|
||||
"[x y z]")))
|
||||
(socket-command "(defmacro bindings [] (mapv #(list 'quote %) (keys &env)))" "bindings")
|
||||
(socket-command "(defn bar [x y z] (bindings))" "bar")
|
||||
(is (socket-command "(bar 1 2 3)" "[x y z]")))
|
||||
(testing "reader conditionals"
|
||||
(is (str/includes? (let [ret (sh "bash" "-c"
|
||||
(format "echo \"%s\n:repl/exit\" | nc 127.0.0.1 1666"
|
||||
"#?(:bb 1337 :clj 8888)"))]
|
||||
(:out ret))
|
||||
"1337")))
|
||||
(is (socket-command "#?(:bb 1337 :clj 8888)" "1337")))
|
||||
(testing "*1, *2, *3, *e"
|
||||
(is (= 2 (count (re-seq #"1\n" (let [ret (sh "bash" "-c"
|
||||
(format "echo \"%s\n*1\n:repl/exit\" | nc 127.0.0.1 1666"
|
||||
"1"))]
|
||||
(:out ret)))))))
|
||||
(is (socket-command "1\n*1" "1")))
|
||||
(finally
|
||||
(if tu/jvm?
|
||||
(stop-repl!)
|
||||
|
|
@ -75,4 +69,14 @@
|
|||
|
||||
(comment
|
||||
(socket-repl-test)
|
||||
(dotimes [_ 1000]
|
||||
(t/run-tests))
|
||||
(stop-repl!)
|
||||
(start-repl! "0.0.0.0:1666" {:bindings {(with-meta '*in*
|
||||
{:sci/deref! true})
|
||||
(delay [1 2 3])
|
||||
'*command-line-args*
|
||||
["a" "b" "c"]}
|
||||
:env (atom {})})
|
||||
(socket-command "(+ 1 2 3)" "6")
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@
|
|||
[clojure.java.shell :refer [sh]]
|
||||
[clojure.string :as str]
|
||||
[clojure.test :as test :refer [deftest is testing]]
|
||||
[clojure.java.io :as io]))
|
||||
[clojure.java.io :as io]
|
||||
[sci.core :as sci]))
|
||||
|
||||
(defn bb [input & args]
|
||||
(edn/read-string (apply test-utils/bb (str input) (map str args))))
|
||||
(edn/read-string (apply test-utils/bb (when (some? input) (str input)) (map str args))))
|
||||
|
||||
(deftest parse-opts-test
|
||||
(is (= {:expression "(println 123)"}
|
||||
|
|
@ -82,6 +83,9 @@
|
|||
"(map-indexed #(-> [%1 %2]) *in*)")
|
||||
(bb "(keep #(when (re-find #\"(?i)clojure\" (second %)) (first %)) *in*)"))))))
|
||||
|
||||
(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"
|
||||
(is (= "2\n" (with-out-str (main/main "(inc 1)"))))))
|
||||
|
|
@ -93,29 +97,20 @@
|
|||
(is (not-empty s))))
|
||||
(let [out (java.io.StringWriter.)
|
||||
err (java.io.StringWriter.)
|
||||
exit-code (binding [*out* out *err* err]
|
||||
(main/main "--time" "(println \"Hello world!\") (System/exit 42)"))]
|
||||
exit-code (sci/with-bindings {sci/out out
|
||||
sci/err err}
|
||||
(binding [*out* out *err* err]
|
||||
(main/main "--time" "(println \"Hello world!\") (System/exit 42)")))]
|
||||
(is (= (str out) "Hello world!\n"))
|
||||
(is (re-find #"took" (str err)))
|
||||
(is (= 42 exit-code))))
|
||||
|
||||
(deftest malformed-command-line-args-test
|
||||
(is (thrown-with-msg? Exception #"File does not exist: non-existing\n"
|
||||
(bb nil "-f" "non-existing")))
|
||||
(testing "no arguments prints help"
|
||||
(is (str/includes?
|
||||
(try (test-utils/bb nil)
|
||||
(catch clojure.lang.ExceptionInfo e
|
||||
(:stdout (ex-data e))))
|
||||
"Usage:"))))
|
||||
(bb nil "-f" "non-existing"))))
|
||||
|
||||
(deftest ssl-test
|
||||
(let [graalvm-home (System/getenv "GRAALVM_HOME")
|
||||
lib-path (format "%1$s/jre/lib:%1$s/jre/lib/amd64" graalvm-home)
|
||||
;; _ (prn "lib-path" lib-path)
|
||||
resp (bb nil (format "(System/setProperty \"java.library.path\" \"%s\")
|
||||
(slurp \"https://www.google.com\")"
|
||||
lib-path))]
|
||||
(let [resp (bb nil "(slurp \"https://www.google.com\")")]
|
||||
(is (re-find #"doctype html" resp))))
|
||||
|
||||
(deftest stream-test
|
||||
|
|
@ -140,7 +135,8 @@
|
|||
(deftest preloads-test
|
||||
;; THIS TEST REQUIRES:
|
||||
;; export BABASHKA_PRELOADS='(defn __bb__foo [] "foo") (defn __bb__bar [] "bar")'
|
||||
(is (= "foobar" (bb nil "(str (__bb__foo) (__bb__bar))"))))
|
||||
(when (System/getenv "BABASHKA_PRELOADS_TEST")
|
||||
(is (= "foobar" (bb nil "(str (__bb__foo) (__bb__bar))")))))
|
||||
|
||||
(deftest io-test
|
||||
(is (true? (bb nil "(.exists (io/file \"README.md\"))")))
|
||||
|
|
@ -288,3 +284,9 @@
|
|||
(java.nio.file.Files/copy p p' (into-array [java.nio.file.StandardCopyOption/REPLACE_EXISTING]))))))"
|
||||
temp-path))
|
||||
(is (.exists f2))))
|
||||
|
||||
(deftest future-print-test
|
||||
(testing "the root binding of sci/*out*"
|
||||
(is (= "hello" (bb nil "@(future (prn \"hello\"))"))))
|
||||
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,19 +1,25 @@
|
|||
(ns babashka.test-utils
|
||||
(:require
|
||||
[babashka.main :as main]
|
||||
[me.raynes.conch :refer [let-programs] :as sh]))
|
||||
[me.raynes.conch :refer [let-programs] :as sh]
|
||||
[sci.core :as sci]))
|
||||
|
||||
(set! *warn-on-reflection* true)
|
||||
|
||||
(defn bb-jvm [input & args]
|
||||
(let [es (java.io.StringWriter.)
|
||||
os (java.io.StringWriter.)]
|
||||
(binding [*err* es
|
||||
*out* os]
|
||||
(let [res (if input
|
||||
(with-in-str input
|
||||
(apply main/main args))
|
||||
(apply main/main args))]
|
||||
(let [os (java.io.StringWriter.)
|
||||
es (java.io.StringWriter.)
|
||||
is (when input
|
||||
(java.io.StringReader. input))
|
||||
bindings-map (cond-> {sci/out os
|
||||
sci/err es}
|
||||
is (assoc sci/in is))]
|
||||
(sci/with-bindings bindings-map
|
||||
(let [res (binding [*out* os
|
||||
*err* es]
|
||||
(if input
|
||||
(with-in-str input (apply main/main args))
|
||||
(apply main/main args)))]
|
||||
(if (zero? res)
|
||||
(str os)
|
||||
(throw (ex-info (str es)
|
||||
|
|
|
|||
Loading…
Reference in a new issue