commit 6dee50b0face0b890a7f628a63b21d5d706a48ee Author: Michiel Borkent Date: Fri Aug 9 14:51:42 2019 +0200 initial commit diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..5e8d8737 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,207 @@ +# Clojure CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-clojure/ for more details +# +version: 2.1 +jobs: + jvm: + docker: + # specify the version you desire here + - image: circleci/clojure:lein-2.8.1 + working_directory: ~/repo + environment: + LEIN_ROOT: "true" + steps: + - checkout + - restore_cache: + keys: + - v1-dependencies-{{ checksum "project.clj" }} + # fallback to using the latest cache if no exact match is found + - v1-dependencies- + - run: + name: Install Clojure + command: | + wget -nc https://download.clojure.org/install/linux-install-1.10.1.447.sh + chmod +x linux-install-1.10.1.447.sh + sudo ./linux-install-1.10.1.447.sh + - run: + name: Run JVM tests + command: | + script/test + # - run: + # name: Run as tools.deps dependency + # command: | + # .circleci/script/tools.deps + - run: + name: Run as lein command + command: | + .circleci/script/lein + - save_cache: + paths: + - ~/.m2 + key: v1-dependencies-{{ checksum "project.clj" }} + linux: + docker: + - image: circleci/clojure:lein-2.8.1 + working_directory: ~/repo + environment: + LEIN_ROOT: "true" + GRAALVM_HOME: /home/circleci/graalvm-ce-19.1.1 + BABASHKA_PLATFORM: linux # used in release script + BABASHKA_TEST_ENV: native + steps: + - checkout + - restore_cache: + keys: + - linux-{{ checksum "project.clj" }}-{{ checksum ".circleci/config.yml" }} + - run: + name: Install Clojure + command: | + wget https://download.clojure.org/install/linux-install-1.10.1.447.sh + chmod +x linux-install-1.10.1.447.sh + sudo ./linux-install-1.10.1.447.sh + - run: + name: Install native dev tools + command: | + sudo apt-get update + sudo apt-get -y install gcc zlib1g-dev + - run: + name: Download GraalVM + command: | + cd ~ + if ! [ -d graalvm-ce-19.1.1 ]; then + curl -O -sL https://github.com/oracle/graal/releases/download/vm-19.1.1/graalvm-ce-linux-amd64-19.1.1.tar.gz + tar xzf graalvm-ce-linux-amd64-19.1.1.tar.gz + fi + - run: + name: Build binary + command: | + script/compile + no_output_timeout: 30m + - run: + name: Run tests + command: | + script/test + # - run: + # name: Performance report + # command: | + # .circleci/script/performance + - run: + name: Release + command: | + .circleci/script/release + - save_cache: + paths: + - ~/.m2 + - ~/graalvm-ce-19.1.1 + key: linux-{{ checksum "project.clj" }}-{{ checksum ".circleci/config.yml" }} + - store_artifacts: + path: /tmp/release + destination: release + mac: + macos: + xcode: "9.0" + environment: + GRAALVM_HOME: /Users/distiller/graalvm-ce-19.1.1/Contents/Home + BABASHKA_PLATFORM: macos # used in release script + BABASHKA_TEST_ENV: native + steps: + - checkout + - restore_cache: + keys: + - mac-{{ checksum "project.clj" }}-{{ checksum ".circleci/config.yml" }} + - run: + name: Install Clojure + command: | + .circleci/script/install-clojure /usr/local + - run: + name: Install Leiningen + command: | + .circleci/script/install-leiningen + + - run: + name: Download GraalVM + command: | + cd ~ + ls -la + if ! [ -d graalvm-ce-19.1.1 ]; then + curl -O -sL https://github.com/oracle/graal/releases/download/vm-19.1.1/graalvm-ce-darwin-amd64-19.1.1.tar.gz + tar xzf graalvm-ce-darwin-amd64-19.1.1.tar.gz + fi + - run: + name: Build binary + command: | + script/compile + no_output_timeout: 30m + - run: + name: Run tests + command: | + script/test + # - run: + # name: Performance report + # command: | + # .circleci/script/performance + - run: + name: Release + command: | + .circleci/script/release + - save_cache: + paths: + - ~/.m2 + - ~/graalvm-ce-19.1.1 + key: mac-{{ checksum "project.clj" }}-{{ checksum ".circleci/config.yml" }} + - store_artifacts: + path: /tmp/release + destination: release + deploy: + docker: + - image: circleci/clojure:lein-2.8.1 + working_directory: ~/repo + environment: + LEIN_ROOT: "true" + steps: + - checkout + - restore_cache: + keys: + - v1-dependencies-{{ checksum "project.clj" }} + # fallback to using the latest cache if no exact match is found + - v1-dependencies- + - run: .circleci/script/deploy + - save_cache: + paths: + - ~/.m2 + key: v1-dependencies-{{ checksum "project.clj" }} + # docker: + # docker: + # - image: circleci/buildpack-deps:stretch + # steps: + # - checkout + # - setup_remote_docker: + # docker_layer_caching: true + # - run: + # name: Build Docker image + # command: .circleci/script/docker + +workflows: + version: 2 + ci: + jobs: + - jvm + - linux + - mac + - deploy: + filters: + branches: + only: master + requires: + - jvm + - linux + - mac + # - docker: + # filters: + # branches: + # only: master + # requires: + # - jvm + # - linux + # - mac diff --git a/.circleci/script/deploy b/.circleci/script/deploy new file mode 100755 index 00000000..252f0433 --- /dev/null +++ b/.circleci/script/deploy @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +if [ -z "$CIRCLE_PULL_REQUEST" ] && [ "$CIRCLE_BRANCH" = "master" ] +then + lein deploy clojars +fi + +exit 0; diff --git a/.circleci/script/docker b/.circleci/script/docker new file mode 100755 index 00000000..69384b31 --- /dev/null +++ b/.circleci/script/docker @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +image_name="borkdude/clj-kondo" +image_tag=$(cat resources/CLJ_KONDO_VERSION) +latest_tag="latest" + +if [[ $image_tag =~ SNAPSHOT$ ]]; then + echo "This is a snapshot version" + snapshot="true" +else + echo "This is a non-snapshot version" + snapshot="false" +fi + +if [ -z "$CIRCLE_PULL_REQUEST" ] && [ "$CIRCLE_BRANCH" = "master" ]; then + echo "Building Docker image $image_name:$image_tag" + echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USER" --password-stdin + docker build -t "$image_name" . + docker tag "$image_name:$latest_tag" "$image_name:$image_tag" + # we only update latest when it's not a SNAPSHOT version + if [ "false" = "$snapshot" ]; then + echo "Pushing image $image_name:$latest_tag" + docker push "$image_name:$latest_tag" + fi + # we update the version tag, even if it's a SNAPSHOT version + echo "Pushing image $image_name:$image_tag" + docker push "$image_name:$image_tag" +else + echo "Not publishing Docker image" +fi + +exit 0; diff --git a/.circleci/script/install-clojure b/.circleci/script/install-clojure new file mode 100755 index 00000000..b4cb4a55 --- /dev/null +++ b/.circleci/script/install-clojure @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +install_dir=${1:-/tmp/clojure} +mkdir -p "$install_dir" +cd /tmp +curl -O -sL https://download.clojure.org/install/clojure-tools-1.10.1.447.tar.gz +tar xzf clojure-tools-1.10.1.447.tar.gz +cd clojure-tools +clojure_lib_dir="$install_dir/lib/clojure" +mkdir -p "$clojure_lib_dir/libexec" +cp ./*.jar "$clojure_lib_dir/libexec" +cp deps.edn "$clojure_lib_dir" +cp example-deps.edn "$clojure_lib_dir" + +sed -i -e 's@PREFIX@'"$clojure_lib_dir"'@g' clojure +mkdir -p "$install_dir/bin" +cp clojure "$install_dir/bin" +cp clj "$install_dir/bin" + +cd /tmp +rm -rf clojure-tools-1.10.1.447.tar.gz +rm -rf clojure-tools +echo "Installed clojure to $install_dir/bin" diff --git a/.circleci/script/install-leiningen b/.circleci/script/install-leiningen new file mode 100755 index 00000000..ff5f8235 --- /dev/null +++ b/.circleci/script/install-leiningen @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +curl https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein > lein +sudo mkdir -p /usr/local/bin/ +sudo mv lein /usr/local/bin/lein +sudo chmod a+x /usr/local/bin/lein diff --git a/.circleci/script/lein b/.circleci/script/lein new file mode 100755 index 00000000..37f446e6 --- /dev/null +++ b/.circleci/script/lein @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +echo '{:a 1}' | lein bb '(:a *in*)' diff --git a/.circleci/script/performance b/.circleci/script/performance new file mode 100755 index 00000000..f7dbbc20 --- /dev/null +++ b/.circleci/script/performance @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +err=0 +function _trap_error() { + local exit_code="$1" + if [ "$exit_code" -ne 2 ] && [ "$exit_code" -ne 3 ]; then + echo "EXIT CODE :( $exit_code" + (( err |= "$exit_code" )) + fi +} + +trap '_trap_error $?' ERR +trap 'exit $err' SIGINT SIGTERM + + +rm -rf performance.txt +echo -e "==== Build initial cache" | tee -a performance.txt +cp="$(clojure -R:cljs -Spath)" +read -r -d '' config <<'EOF' || true +{:linters + {:not-a-function + {:skip-args [clojure.pprint/defdirectives + cljs.pprint/defdirectives + clojure.data.json/codepoint-case]}}} +EOF + +(time ./clj-kondo --lint "$cp" --cache --config "$config") 2>&1 | tee -a performance.txt + +echo -e "\n==== Lint a single file (emulate in-editor usage)" | tee -a performance.txt +(time ./clj-kondo --lint src/clj_kondo/impl/core.clj --cache) 2>&1 | tee -a performance.txt + +count=$(find . -name "*.clj*" -type f | wc -l | tr -d ' ') +echo -e "\n==== Launch clj-kondo for each file in project ($count)" | tee -a performance.txt +(time find src -name "*.clj*" -type f -exec ./clj-kondo --lint {} --cache \; ) 2>&1 | tee -a performance.txt + +exit "$err" diff --git a/.circleci/script/release b/.circleci/script/release new file mode 100755 index 00000000..fa085248 --- /dev/null +++ b/.circleci/script/release @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +rm -rf /tmp/release +mkdir -p /tmp/release +cp bb /tmp/release +VERSION=$(cat resources/BABASHKA_VERSION) + +cd /tmp/release + +## release binary as zip archive + +zip "babashka-$VERSION-$BABASHKA_PLATFORM-amd64.zip" bb + +## cleanup + +rm bb diff --git a/.circleci/script/tools.deps b/.circleci/script/tools.deps new file mode 100755 index 00000000..e26840ca --- /dev/null +++ b/.circleci/script/tools.deps @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +err=0 +function _trap_error() { + local exit_code="$1" + if [ "$exit_code" -ne 2 ] && [ "$exit_code" -ne 3 ]; then + (( err |= "$exit_code" )) + fi +} + +trap '_trap_error $?' ERR +trap 'exit $err' SIGINT SIGTERM + + +# Run as local root dependency +rm -rf /tmp/proj +mkdir -p /tmp/proj +cd /tmp/proj +clojure -Sdeps '{:deps {clj-kondo {:local/root "/home/circleci/repo"}}}' \ + -m clj-kondo.main --lint /home/circleci/repo/src /home/circleci/repo/test + +# Run as git dependency +rm -rf /tmp/proj +mkdir -p /tmp/proj +cd /tmp/proj + +github_user=${CIRCLE_PR_USERNAME:-borkdude} +clojure -Sdeps "{:deps {clj-kondo {:git/url \"https://github.com/$github_user/clj-kondo\" :sha \"$CIRCLE_SHA1\"}}}" \ + -m clj-kondo.main --lint /home/circleci/repo/src /home/circleci/repo/test + +exit "$err" diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn new file mode 100644 index 00000000..8bd021f1 --- /dev/null +++ b/.clj-kondo/config.edn @@ -0,0 +1 @@ +{:lint-as {me.raynes.conch/let-programs clojure.core/let}} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..18814153 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +/target +/classes +/checkouts +profiles.clj +pom.xml +pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port +.hgignore +.hg/ +/bb +.clj-kondo/.cache diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..11ecb795 --- /dev/null +++ b/LICENSE @@ -0,0 +1,198 @@ +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM +CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation + distributed under this Agreement, and +b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + + where such changes and/or additions to the Program originate from and are + distributed by that particular Contributor. A Contribution 'originates' from + a Contributor if it was added to the Program by such Contributor itself or + anyone acting on such Contributor's behalf. Contributions do not include + additions to the Program which: (i) are separate modules of software + distributed in conjunction with the Program under their own license + agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are +necessarily infringed by the use or sale of its Contribution alone or when +combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + +2. GRANT OF RIGHTS + a) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free copyright license to + reproduce, prepare derivative works of, publicly display, publicly perform, + distribute and sublicense the Contribution of such Contributor, if any, and + such derivative works, in source code and object code form. + b) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free patent license under + Licensed Patents to make, use, sell, offer to sell, import and otherwise + transfer the Contribution of such Contributor, if any, in source code and + object code form. This patent license shall apply to the combination of the + Contribution and the Program if, at the time the Contribution is added by + the Contributor, such addition of the Contribution causes such combination + to be covered by the Licensed Patents. The patent license shall not apply + to any other combinations which include the Contribution. No hardware per + se is licensed hereunder. + c) Recipient understands that although each Contributor grants the licenses to + its Contributions set forth herein, no assurances are provided by any + Contributor that the Program does not infringe the patent or other + intellectual property rights of any other entity. Each Contributor + disclaims any liability to Recipient for claims brought by any other entity + based on infringement of intellectual property rights or otherwise. As a + condition to exercising the rights and licenses granted hereunder, each + Recipient hereby assumes sole responsibility to secure any other + intellectual property rights needed, if any. For example, if a third party + patent license is required to allow Recipient to distribute the Program, it + is Recipient's responsibility to acquire that license before distributing + the Program. + d) Each Contributor represents that to its knowledge it has sufficient + copyright rights in its Contribution, if any, to grant the copyright + license set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its +own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + b) its license agreement: + i) effectively disclaims on behalf of all Contributors all warranties and + conditions, express and implied, including warranties or conditions of + title and non-infringement, and implied warranties or conditions of + merchantability and fitness for a particular purpose; + ii) effectively excludes on behalf of all Contributors all liability for + damages, including direct, indirect, special, incidental and + consequential damages, such as lost profits; + iii) states that any provisions which differ from this Agreement are offered + by that Contributor alone and not by any other party; and + iv) states that source code for the Program is available from such + Contributor, and informs licensees how to obtain it in a reasonable + manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + b) a copy of this Agreement must be included with each copy of the Program. + Contributors may not remove or alter any copyright notices contained within + the Program. + +Each Contributor must identify itself as the originator of its Contribution, if +any, in a manner that reasonably allows subsequent Recipients to identify the +originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with +respect to end users, business partners and the like. While this license is +intended to facilitate the commercial use of the Program, the Contributor who +includes the Program in a commercial product offering should do so in a manner +which does not create potential liability for other Contributors. Therefore, if +a Contributor includes the Program in a commercial product offering, such +Contributor ("Commercial Contributor") hereby agrees to defend and indemnify +every other Contributor ("Indemnified Contributor") against any losses, damages +and costs (collectively "Losses") arising from claims, lawsuits and other legal +actions brought by a third party against the Indemnified Contributor to the +extent caused by the acts or omissions of such Commercial Contributor in +connection with its distribution of the Program in a commercial product +offering. The obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In order +to qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial Contributor to +control, and cooperate with the Commercial Contributor in, the defense and any +related settlement negotiations. The Indemnified Contributor may participate in +any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product +offering, Product X. That Contributor is then a Commercial Contributor. If that +Commercial Contributor then makes performance claims, or offers warranties +related to Product X, those performance claims and warranties are such +Commercial Contributor's responsibility alone. Under this section, the +Commercial Contributor would have to defend claims against the other +Contributors related to those performance claims and warranties, and if a court +requires any other Contributor to pay any damages as a result, the Commercial +Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each +Recipient is solely responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its exercise of +rights under this Agreement , including but not limited to the risks and costs +of program errors, compliance with applicable laws, damage to or loss of data, +programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY +CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST +PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS +GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable +law, it shall not affect the validity or enforceability of the remainder of the +terms of this Agreement, and without further action by the parties hereto, such +provision shall be reformed to the minimum extent necessary to make such +provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Program itself +(excluding combinations of the Program with other software or hardware) +infringes such Recipient's patent(s), then such Recipient's rights granted under +Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to +comply with any of the material terms or conditions of this Agreement and does +not cure such failure in a reasonable period of time after becoming aware of +such noncompliance. If all Recipient's rights under this Agreement terminate, +Recipient agrees to cease use and distribution of the Program as soon as +reasonably practicable. However, Recipient's obligations under this Agreement +and any licenses granted by Recipient relating to the Program shall continue and +survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in +order to avoid inconsistency the Agreement is copyrighted and may only be +modified in the following manner. The Agreement Steward reserves the right to +publish new versions (including revisions) of this Agreement from time to time. +No one other than the Agreement Steward has the right to modify this Agreement. +The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation +may assign the responsibility to serve as the Agreement Steward to a suitable +separate entity. Each new version of the Agreement will be given a +distinguishing version number. The Program (including Contributions) may always +be distributed subject to the version of the Agreement under which it was +received. In addition, after a new version of the Agreement is published, +Contributor may elect to distribute the Program (including its Contributions) +under the new version. Except as expressly stated in Sections 2(a) and 2(b) +above, Recipient receives no rights or licenses to the intellectual property of +any Contributor under this Agreement, whether expressly, by implication, +estoppel or otherwise. All rights in the Program not expressly granted under +this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to this +Agreement will bring a legal action under this Agreement more than one year +after the cause of action arose. Each party waives its rights to a jury trial in +any resulting litigation. diff --git a/README.md b/README.md new file mode 100644 index 00000000..56910987 --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +# babashka + +[![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) + +An extremely limited version of Clojure in Clojure for shell-scripting. + +Properties: + +- pure (no side effects) +- interprets only one form +- reads from stdin and writes to stdout + +## Status + +Experimental, mostly for fun. + +## Usage + +`bb` supports the following options: + + - `--version`: if present, prints current version of `bb` and exits. + +By default, the first argument to `bb` is the form to be executed. There is one +special variable, `*in*`. + +Examples: + +``` shellsession +$ echo 1 | bb '(inc *in*)' +2 + +$ echo '[1 1 1 1 2]' | bb '(vec (dedupe *in*))' +[1 2] + +$ echo '[1 1 1 1 2]' | bb '(inc (first *in*))' +2 + +$ echo '[{:foo 1} {:bar 2}]' | bb '(filter :foo *in*)' +({:foo 1}) + +$ echo '"babashka"' | bb '(re-find (re-pattern "b.b.*") *in*)' +"babashka" +``` + +## Test + +Test the JVM version: + + script/test + +Test the native version: + + BABASHKA_TEST_ENV=native script/test + +## Build + +You will need leiningen and GraalVM. + + script/compile + +## License + +Copyright © 2019 Michiel Borkent + +Distributed under the EPL License, same as Clojure. See LICENSE. diff --git a/doc/cljdoc.edn b/doc/cljdoc.edn new file mode 100644 index 00000000..c8bbab47 --- /dev/null +++ b/doc/cljdoc.edn @@ -0,0 +1,2 @@ +{:cljdoc.doc/tree + [["Readme" {:file "README.md"}]]} diff --git a/project.clj b/project.clj new file mode 100644 index 00000000..122be8d2 --- /dev/null +++ b/project.clj @@ -0,0 +1,27 @@ +(defproject borkdude/babashka + #=(clojure.string/trim + #=(slurp "resources/BABASHKA_VERSION")) + :description "babashka" + :url "https://github.com/borkdude/babashka" + :scm {:name "git" + :url "https://github.com/borkdude/babashka"} + :license {:name "Eclipse Public License 1.0" + :url "http://opensource.org/licenses/eclipse-1.0.php"} + :source-paths ["src"] + :dependencies [[org.clojure/clojure "1.9.0"] + [com.cognitect/transit-clj "0.8.313"] + [cheshire "5.8.1"] + [fipp "0.6.18"]] + :profiles {:clojure-1.9.0 {:dependencies [[org.clojure/clojure "1.9.0"]]} + :clojure-1.10.1 {:dependencies [[org.clojure/clojure "1.10.1"]]} + :test {:dependencies [[clj-commons/conch "0.9.2"]]} + :uberjar {:global-vars {*assert* false} + :jvm-opts ["-Dclojure.compiler.direct-linking=true" + "-Dclojure.spec.skip-macros=true"] + :main babashka.main + :aot :all}} + :aliases {"bb" ["run" "-m" "babashka.main"]} + :deploy-repositories [["clojars" {:url "https://clojars.org/repo" + :username :env/clojars_user + :password :env/clojars_pass + :sign-releases false}]]) diff --git a/reflection.json b/reflection.json new file mode 100644 index 00000000..81942d35 --- /dev/null +++ b/reflection.json @@ -0,0 +1,9 @@ +[ + { + "name": "java.lang.Class", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allPublicMethods": true + } +] diff --git a/resources/BABASHKA_VERSION b/resources/BABASHKA_VERSION new file mode 100644 index 00000000..cfefe08c --- /dev/null +++ b/resources/BABASHKA_VERSION @@ -0,0 +1 @@ +0.0.1-SNAPSHOT diff --git a/script/compile b/script/compile new file mode 100755 index 00000000..80ada177 --- /dev/null +++ b/script/compile @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +if [ -z "$GRAALVM_HOME" ]; then + echo "Please set GRAALVM_HOME" + exit 1 +fi + +"$GRAALVM_HOME/bin/gu" install native-image + +BABASHKA_VERSION=$(cat resources/BABASHKA_VERSION) + +lein with-profiles +clojure-1.10.1 do clean, uberjar +$GRAALVM_HOME/bin/native-image \ + -jar target/babashka-$BABASHKA_VERSION-standalone.jar \ + -H:Name=bb \ + -H:+ReportExceptionStackTraces \ + -J-Dclojure.spec.skip-macros=true \ + -J-Dclojure.compiler.direct-linking=true \ + "-H:IncludeResources=BABASHKA_VERSION" \ + -H:ReflectionConfigurationFiles=reflection.json \ + --initialize-at-build-time \ + -H:Log=registerResource: \ + --verbose \ + --no-fallback \ + --no-server \ + "-J-Xmx3g" + +lein clean diff --git a/script/test b/script/test new file mode 100755 index 00000000..54364d3c --- /dev/null +++ b/script/test @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -eo pipefail + +if [ "$JET_TEST_ENV" = "native" ]; then + lein test +else + echo "Testing with Clojure 1.9.0" + lein with-profiles +clojure-1.9.0 test + + echo "Testing with Clojure 1.10.1" + lein with-profiles +clojure-1.10.1 test +fi diff --git a/src/babashka/interpreter.clj b/src/babashka/interpreter.clj new file mode 100644 index 00000000..5e61f821 --- /dev/null +++ b/src/babashka/interpreter.clj @@ -0,0 +1,70 @@ +(ns babashka.interpreter + {:no-doc true} + (:refer-clojure :exclude [comparator])) + +(defn safe-nth [x n] + (try (nth x n) + (catch Exception _e + nil))) + +(def syms '(= < <= >= + +' - * / + aget alength assoc assoc-in + bit-set bit-shift-left bit-shift-right bit-xor boolean boolean? booleans boolean-array butlast + char char? conj cons contains? count + dec dec' decimal? dedupe dissoc distinct disj drop + eduction every? + get + first float? floats fnil + identity inc int-array iterate + juxt + filter find + last line-seq + keep keep-indexed keys + map map-indexed mapcat merge merge-with munge + newline not= num + neg? nth nthrest + peek pos? + re-seq re-find re-pattern rest reverse + safe-nth set? sequential? some? + take take-last take-nth tree-seq type + unchecked-inc-int unchecked-long unchecked-negate unchecked-remainder-int + unchecked-subtract-int unsigned-bit-shift-right unchecked-float + vals vec vector? + rand-int rand-nth range reduce reduced? remove + set seq seq? shuffle simple-symbol? sort sort-by subs)) + +;; TODO: +#_(def all-syms + '#{when-first while if-not when-let inc' cat StackTraceElement->vec flush take-while vary-meta <= alter -' if-some conj! repeatedly zipmap reset-vals! alter-var-root biginteger remove * re-pattern min pop! chunk-append prn-str with-precision format reversible? shutdown-agents conj bound? transduce lazy-seq *print-length* *file* compare-and-set! *use-context-classloader* await1 let ref-set pop-thread-bindings interleave printf map? -> defstruct *err* assert-same-protocol get doto identity into areduce long double volatile? definline nfirst meta find-protocol-impl bit-and-not *default-data-reader-fn* var? method-sig unchecked-add-int unquote-splicing hash-ordered-coll future reset-meta! Vec cycle fn seque empty? short definterface add-tap filterv hash quot ns-aliases read unchecked-double key longs not= string? uri? aset-double unchecked-multiply-int chunk-rest pcalls *allow-unresolved-vars* remove-all-methods ns-resolve as-> aset-boolean trampoline double? when-not *1 vec *print-meta* when int map-entry? ns-refers rand second vector-of hash-combine > replace int? associative? unchecked-int set-error-handler! inst-ms* keyword? force bound-fn* namespace-munge group-by prn extend unchecked-multiply some->> default-data-readers ->VecSeq even? unchecked-dec Inst tagged-literal? double-array in-ns create-ns re-matcher defn ref bigint extends? promise aset-char rseq ex-cause construct-proxy agent-errors *compile-files* ex-message *math-context* float pr-str concat aset-short set-agent-send-off-executor! ns symbol to-array-2d mod amap pop use VecNode unquote declare dissoc! reductions aset-byte indexed? ref-history-count - assoc! hash-set reduce-kv or cast reset! name ffirst sorted-set counted? byte-array IVecImpl tagged-literal println extend-type macroexpand-1 assoc-in char-name-string bit-test defmethod requiring-resolve EMPTY-NODE time memoize alter-meta! future? zero? simple-keyword? require unchecked-dec-int persistent! nnext add-watch not-every? class? rem agent-error some future-cancelled? memfn neg-int? struct-map drop *data-readers* nth sorted? nil? extend-protocol split-at *e load-reader random-sample cond-> dotimes select-keys bit-and bounded-count update list* reify update-in prefer-method aset-int *clojure-version* ensure-reduced *' instance? with-open mix-collection-hash re-find run! val defonce unchecked-add loaded-libs ->Vec bytes? not with-meta unreduced the-ns record? type identical? unchecked-divide-int ns-name max-key *unchecked-math* defn- *out* file-seq agent ns-map set-validator! ident? defprotocol swap! vals unchecked-subtract tap> *warn-on-reflection* sorted-set-by sync qualified-ident? assert *compile-path* true? release-pending-sends print empty remove-method *in* print-ctor letfn volatile! / read-line reader-conditional? bit-or clear-agent-errors vector proxy-super >= drop-last not-empty distinct partition loop add-classpath bit-flip long-array descendants merge accessor integer? mapv partition-all partition-by numerator object-array with-out-str condp derive load-string special-symbol? ancestors subseq error-handler gensym cond ratio? delay? intern print-simple flatten doubles halt-when with-in-str remove-watch ex-info ifn? some-> nat-int? proxy-name ns-interns all-ns find-protocol-method subvec for binding partial chunked-seq? find-keyword replicate min-key reduced char-escape-string re-matches array-map unchecked-byte with-local-vars ns-imports send-off defmacro every-pred keys rationalize load-file distinct? pos-int? extenders unchecked-short methods odd? ->ArrayChunk float-array *3 alias frequencies read-string proxy rsubseq inc get-method with-redefs uuid? bit-clear filter locking list + split-with aset ->VecNode keyword *ns* destructure *assert* defmulti chars str next hash-map if-let underive ref-max-history Throwable->map false? *print-readably* ints class some-fn case *flush-on-newline* to-array bigdec list? simple-ident? bit-not io! xml-seq VecSeq byte max == *agent* lazy-cat comment parents count supers *fn-loader* ArrayChunk sorted-map-by apply interpose deref assoc rational? transient clojure-version chunk-cons comparator sorted-map send drop-while proxy-call-with-super realized? char-array resolve compare complement *compiler-options* *print-dup* defrecord with-redefs-fn sequence constantly get-proxy-class make-array shorts completing update-proxy unchecked-negate-int hash-unordered-coll repeat unchecked-inc nthnext and create-struct get-validator number? await-for chunk-next print-str not-any? into-array qualified-symbol? init-proxy chunk-buffer seqable? symbol? when-some unchecked-char ->> future-cancel var-get commute coll? get-in fnext denominator bytes gen-and-load-class refer-clojure}) + +(declare var-lookup) + +(defmacro define-lookup [] + `(defn ~'var-lookup [sym#] + (case sym# + ~@(for [s# syms + s# [s# s#]] + s#) + nil))) + +(define-lookup) + +(defn interpret + [expr in] + (cond + (= '*in* expr) in + (list? expr) + (if-let [f (first expr)] + (if-let [v (or (var-lookup f))] + (apply v (map #(interpret % in) (rest expr))) + (if (or (symbol? f) (keyword? f)) + (get in f) + nil)) + expr) + :else + expr)) + +;;;; Scratch + +(comment + ) diff --git a/src/babashka/main.clj b/src/babashka/main.clj new file mode 100644 index 00000000..5324b919 --- /dev/null +++ b/src/babashka/main.clj @@ -0,0 +1,41 @@ +(ns babashka.main + {:no-doc true} + (:require + [clojure.edn :as edn] + [clojure.java.io :as io] + [clojure.string :as str :refer [starts-with?]] + [babashka.interpreter :as i]) + (:gen-class)) + +(set! *warn-on-reflection* true) + +(defn- parse-opts [options] + (let [opts (loop [options options + opts-map {} + current-opt nil] + (if-let [opt (first options)] + (if (starts-with? opt "--") + (recur (rest options) + (assoc opts-map opt []) + opt) + (recur (rest options) + (update opts-map current-opt conj opt) + current-opt)) + opts-map)) + version (boolean (get opts "--version"))] + {:version version})) + +(defn -main + [& args] + (let [{:keys [:version]} (parse-opts args)] + (cond version + (println (str/trim (slurp (io/resource "BABASHKA_VERSION")))) + :else + (let [expr (edn/read-string (first args)) + in (slurp *in*) + edn (edn/read-string in)] + (prn (i/interpret expr edn)))))) + +;;;; Scratch + +(comment) diff --git a/test/babashka/main_test.clj b/test/babashka/main_test.clj new file mode 100644 index 00000000..2a800985 --- /dev/null +++ b/test/babashka/main_test.clj @@ -0,0 +1,6 @@ +(ns babashka.main-test + (:require + [clojure.test :as test :refer [deftest is testing]] + [babashka.test-utils :refer [bb]])) + +(deftest main-test) diff --git a/test/babashka/test_utils.clj b/test/babashka/test_utils.clj new file mode 100644 index 00000000..5502be9d --- /dev/null +++ b/test/babashka/test_utils.clj @@ -0,0 +1,27 @@ +(ns babashka.test-utils + (:require + [babashka.main :as main] + [me.raynes.conch :refer [let-programs] :as sh])) + +(set! *warn-on-reflection* true) + +(defn bb-jvm [input & args] + (with-out-str + (with-in-str input + (apply main/-main args)))) + +(defn bb-native [input & args] + (let-programs [bb "./bb"] + (binding [sh/*throw* false] + (apply bb (conj (vec args) + {:in input}))))) + +(def bb + (case (System/getenv "BB_TEST_ENV") + "jvm" #'bb-jvm + "native" #'bb-native + #'bb-jvm)) + +(if (= bb #'bb-jvm) + (println "==== Testing JVM version") + (println "==== Testing native version"))