From 4417bb69a665a6787e3c05b0ab94a3620510fb62 Mon Sep 17 00:00:00 2001 From: ikappaki <34983288+ikappaki@users.noreply.github.com> Date: Tue, 25 Oct 2022 21:32:15 +0100 Subject: [PATCH] Fix #66: Respect CLJ_JVM_OPTS env var options when downloading clojure-tools (#73) Co-authored-by: ikappaki --- .circleci/config.yml | 14 ++- .github/workflows/test.yml | 21 +++- README.md | 18 ++- deps.edn | 2 +- src/borkdude/deps.clj | 237 ++++++++++++++++++++++++++++-------- test/borkdude/deps_test.clj | 135 +++++++++++++++++++- 6 files changed, 356 insertions(+), 71 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4b58860..02ef574 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -61,11 +61,19 @@ jobs: - v1-dependencies-{{ checksum "deps.edn" }} # fallback to using the latest cache if no exact match is found - v1-dependencies- + # - run: + # name: Install babashka + # command: | + # bash <(curl -s https://raw.githubusercontent.com/borkdude/babashka/master/install) --dir ~ + # sudo mv ~/bb /usr/local/bin/bb + - run: - name: Install babashka + name: Install babashka REVIEW command: | - bash <(curl -s https://raw.githubusercontent.com/borkdude/babashka/master/install) --dir ~ - sudo mv ~/bb /usr/local/bin/bb + curl -sLO https://raw.githubusercontent.com/babashka/babashka/master/install + chmod +x ./install + sudo ./install --version 1.0.165-SNAPSHOT + - run: name: Run babashka tests command: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c55bd70..aa99644 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,10 +30,29 @@ jobs: - name: Install Clojure uses: DeLaGuardo/setup-clojure@9.5 with: - bb: '0.10.163' + # bb: '0.10.163' cli: '1.10.3.1013' lein: '2.9.10' + - name: Install Babashka REVIEW + if: "!startsWith (matrix.os, 'win')" + run: | + curl -sLO https://raw.githubusercontent.com/babashka/babashka/master/install + chmod +x ./install + ./install --version 1.0.165-SNAPSHOT + + - name: Install Babashka REVIEW (win) + if: "startsWith (matrix.os, 'win')" + shell: cmd + run: | + set LOCAL_BIN_DIR=%USERPROFILE%\.local\bin + + mkdir %LOCAL_BIN_DIR% + + curl.exe -fsSL https://github.com/babashka/babashka-dev-builds/releases/download/v1.0.165-SNAPSHOT/babashka-1.0.165-SNAPSHOT-windows-amd64.zip -o bb-snap.zip || exit /b + unzip bb-snap.zip -d %LOCAL_BIN_DIR% || exit /b + echo %LOCAL_BIN_DIR%>> %GITHUB_PATH% + - name: Cache clojure dependencies uses: actions/cache@v3 with: diff --git a/README.md b/README.md index af66aae..dcdaa30 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,11 @@ Windows binary from [Github releases](https://github.com/borkdude/deps.clj/releases) and place it on your path. +As of 1.11.1.1165, the scripts passes the values of the `CLJ_JVM_OPTS` +or `JAVA_OPTIONS` to `java` when downloading dependencies or executing +all other commands respectively (useful for setting up network +connectivity behind firewalls). + ## Why Originally this project was created as a proof of concept to see if the @@ -176,9 +181,10 @@ This project will look in `$HOME/.deps.clj//ClojureTools` for `clojure-tools-.jar`, `exec.jar` and `example-deps.edn`. If it cannot it find those files there, it will try to download them from [this](https://download.clojure.org/install/clojure-tools-1.10.1.697.zip) -location. You can override the location of these jars with the -`DEPS_CLJ_TOOLS_DIR` environment variable. If the download fails for some reason, -you can try to download the zip yourself and unzip it at the expected location. +location invoking `java` using the value of the `CLJ_JVM_OPTS` environment variable as options. +You can override the location of these jars with the `DEPS_CLJ_TOOLS_DIR` environment variable. +If the download fails for some reason, you can try to download the zip yourself at the location +suggested by the failure message. If you have an already installed version of clojure using e.g. brew, you can set `DEPS_CLJ_TOOLS_DIR` to that directory: @@ -296,7 +302,7 @@ $ lein run -m borkdude.deps -Spath To run jvm tests: ``` -$ bb test +$ bb jvm-test ``` To run with babashka after making changes to `src/borkdude/deps.clj`, you should run: @@ -328,7 +334,7 @@ The script also assumes that you have Run the compile script with: ``` -$ script/compile +$ bb compile ``` If everything worked out, there will be a `deps` binary in the root of the @@ -337,7 +343,7 @@ project. To run executable tests: ``` -$ script/exe_test +$ bb exe-test ``` ## License diff --git a/deps.edn b/deps.edn index 7a7fb04..058f63c 100644 --- a/deps.edn +++ b/deps.edn @@ -4,7 +4,7 @@ {:extra-paths ["test"] :extra-deps {io.github.cognitect-labs/test-runner {:git/tag "v0.5.0" :git/sha "b3fd0d2"} - babashka/fs {:mvn/version "0.1.0"} + babashka/fs {:mvn/version "0.1.11"} babashka/process {:mvn/version "0.0.2"}} :main-opts ["-m" "cognitect.test-runner"] :exec-fn cognitect.test-runner.api/test} diff --git a/src/borkdude/deps.clj b/src/borkdude/deps.clj index 67822b5..b7cf4eb 100755 --- a/src/borkdude/deps.clj +++ b/src/borkdude/deps.clj @@ -3,7 +3,7 @@ [clojure.java.io :as io] [clojure.string :as str]) (:import [java.lang ProcessBuilder$Redirect] - [java.net URL HttpURLConnection] + [java.net URL URLConnection HttpURLConnection] [java.nio.file Files FileSystems Path CopyOption]) (:gen-class)) @@ -225,43 +225,169 @@ For more info, see: (*getenv-fn* "userprofile") (System/getProperty "user.home"))) -(defn download [source dest] - (warn "Downloading" (str source) "to" (str (.getParent (io/file dest)))) - (let [source (URL. source) - dest (io/file dest) - conn ^HttpURLConnection (.openConnection ^URL source)] - (.setInstanceFollowRedirects conn true) - (.connect conn) - (with-open [is (.getInputStream conn)] - (io/copy is dest)))) +(def java-exe (if windows? "java.exe" "java")) + +(defn- get-java-cmd + "Returns the path to java executable to invoke sub commands with." + [] + (or (*getenv-fn* "JAVA_CMD") + (let [java-cmd (which java-exe)] + (if (str/blank? java-cmd) + (let [java-home (*getenv-fn* "JAVA_HOME")] + (if-not (str/blank? java-home) + (let [f (io/file java-home "bin" java-exe)] + (if (and (.exists f) + (.canExecute f)) + (.getCanonicalPath f) + (throw (Exception. "Couldn't find 'java'. Please set JAVA_HOME.")))) + (throw (Exception. "Couldn't find 'java'. Please set JAVA_HOME.")))) + java-cmd)))) -(def clojure-tools-jar (delay (format "clojure-tools-%s.jar" @version))) +(defn clojure-tools-download-direct + "Downloads from SOURCE url to DEST file returning true on success." + [source dest] + (try + (let [source (URL. source) + dest (io/file dest) + conn ^URLConnection (.openConnection ^URL source)] + (when (instance? java.net.HttpURLConnection conn) + (.setInstanceFollowRedirects #^java.net.HttpURLConnection conn true)) + (.connect conn) + (with-open [is (.getInputStream conn)] + (io/copy is dest)) + true) + (catch Exception e + (warn ::direct-download (.getMessage e)) + false))) + +(def ^:private clojure-tools-info* + "A delay'd map with information about the clojure tools archive, where + to download it from, which files to extract and where to. + + The map contains: + + :ct-base-dir The relative top dir name to extract the archive files to. + + :ct-error-exit-code The process exit code to return if the archive + cannot be downloaded. + + :ct-aux-files-names Other important files in the archive. + + :ct-jar-name The main clojure tools jar file in the archive. + + :ct-url-str The url to download the archive from. + + :ct-zip-name The file name to store the archive as." + (delay (let [version @version] + {:ct-base-dir "ClojureTools" + :ct-error-exit-code 99 + :ct-aux-files-names ["exec.jar" "example-deps.edn" "tools.edn"] + :ct-jar-name (format "clojure-tools-%s.jar" version) + :ct-url-str (format "https://download.clojure.org/install/clojure-tools-%s.zip" version) + :ct-zip-name "tools.zip"}))) (defn unzip [zip-file destination-dir] - (let [zip-file (io/file zip-file) + (let [{:keys [ct-base-dir ct-aux-files-names ct-jar-name]} @clojure-tools-info* + zip-file (io/file zip-file) _ (.mkdirs (io/file destination-dir)) - ^ClassLoader x nil - fs (FileSystems/newFileSystem (.toPath zip-file) x)] - (doseq [f [@clojure-tools-jar - "exec.jar" - "example-deps.edn" - "tools.edn"]] - (let [file-in-zip (.getPath fs "ClojureTools" (into-array String [f]))] - (Files/copy file-in-zip (.toPath (io/file destination-dir f)) - ^{:tag "[Ljava.nio.file.CopyOption;"} - (into-array CopyOption - [java.nio.file.StandardCopyOption/REPLACE_EXISTING])))))) + ^ClassLoader x nil] + (with-open [fs (FileSystems/newFileSystem (.toPath zip-file) x)] + (doseq [f (into [ct-jar-name] ct-aux-files-names)] + (let [file-in-zip (.getPath fs ct-base-dir (into-array String [f]))] + (Files/copy file-in-zip (.toPath (io/file destination-dir f)) + ^{:tag "[Ljava.nio.file.CopyOption;"} + (into-array CopyOption + [java.nio.file.StandardCopyOption/REPLACE_EXISTING]))))))) + +(defn- clojure-tools-java-downloader-spit + "Spits out and returns the path to `ClojureToolsDownloader.java` file + in DEST-DIR'ectory that when invoked (presumambly by the `java` + executable directly) with a source URL and destination file path in + args[0] and args[1] respectively, will download the source to + destination. No arguments validation is performed and returns exit + code 1 on failure." + [dest-dir] + (let [dest-file (.getCanonicalPath (io/file dest-dir "ClojureToolsDownloader.java"))] + (spit dest-file + (str " +/** Auto-generated by " *file* ". **/ +package borkdude.deps; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URLConnection; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.channels.Channels;import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +public class ClojureToolsDownloader { + public static void main (String[] args) { + try { + URL url = new URL(args[0]); +// System.err.println (\":0 \" +args [0]+ \" :1 \"+args [1]); + URLConnection conn = url.openConnection(); + if (conn instanceof HttpURLConnection) + {((HttpURLConnection) conn).setInstanceFollowRedirects(true);} + ReadableByteChannel readableByteChannel = Channels.newChannel(conn.getInputStream()); + FileOutputStream fileOutputStream = new FileOutputStream(args[1]); + FileChannel fileChannel = fileOutputStream.getChannel(); + fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE); + System.exit(0); + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); }}}")) + dest-file)) + +(defn- clojure-tools-download-java + "Downloads URL file to DEST-ZIP-FILE by invoking `java` with JVM-OPTS + on a `.java` program file, and returns true on success. Requires + Java 11+ (JEP 330)." + [url dest-zip-file jvm-opts] + (let [dest-dir (.getCanonicalPath (io/file dest-zip-file "..")) + dlr-path (clojure-tools-java-downloader-spit dest-dir) + java-cmd (get-java-cmd) + success?* (atom true)] + (binding [*exit-fn* (fn + ([exit-code] (when-not (= exit-code 0) (reset! success?* false))) + ([exit-code msg] (when-not (= exit-code 0) + (warn msg) + (reset! success?* false))))] + (shell-command (vec (concat [java-cmd] + jvm-opts + [dlr-path url (str dest-zip-file)]))) + (io/delete-file dlr-path true) + @success?*))) (defn clojure-tools-jar-download - "Downloads clojure tools jar into deps-clj-config-dir." - [deps-clj-config-dir] - (let [dir (io/file deps-clj-config-dir) - zip (io/file deps-clj-config-dir "tools.zip")] - (.mkdirs dir) - (download (format "https://download.clojure.org/install/clojure-tools-%s.zip" @version) - zip) - (unzip zip (.getPath dir)) - (.delete zip)) + "Downloads clojure tools archive in OUT-DIR, if not already there, + and extracts in-place the clojure tools jar file and other important + files. + + The download is attempted directly from this process, unless + JAVA-ARGS-WITH-CLJ-JVM-OPTS is set, in which case a java subprocess + is created to download the archive passing in its value as command + line options. + + It calls `*exit-fn*` if it cannot download the archive, with + instructions how to manually download it." + [out-dir java-args-with-clj-jvm-opts & {:keys [debug?] :as _opts}] + (let [{:keys [ct-error-exit-code ct-url-str ct-zip-name]} @clojure-tools-info* + dir (io/file out-dir) + zip-file (io/file out-dir ct-zip-name)] + (when-not (.exists zip-file) + (warn "Downloading" ct-url-str "to" (str zip-file)) + (.mkdirs dir) + (or (when java-args-with-clj-jvm-opts + (when debug? (warn "Attempting download using java subprocess... (requires Java11+")) + (clojure-tools-download-java ct-url-str (str zip-file) java-args-with-clj-jvm-opts)) + (do (when debug? (warn "Attempting direct download...")) + (clojure-tools-download-direct ct-url-str zip-file)) + (*exit-fn* ct-error-exit-code (str "Error: Cannot download Clojure tools." + " Please download manually from " ct-url-str + " to " (str (io/file dir ct-zip-name)))) + {:url ct-url-str :dest-dir (str dir)})) + (warn "Unziping" (str zip-file) "...") + (unzip zip-file (.getPath dir)) + (.delete zip-file)) (warn "Successfully installed clojure tools!")) (def ^:private authenticated-proxy-re #".+:.+@(.+):(\d+).*") @@ -428,26 +554,27 @@ For more info, see: (.relativize (as-path dir) (as-path f)) f)) -(def java-exe (if windows? "java.exe" "java")) +(defn -main + "See `help-text`. -(defn- get-java-cmd - "Returns the path to java executable to invoke commands on." - [] - (or (*getenv-fn* "JAVA_CMD") - (let [java-cmd (which java-exe)] - (if (str/blank? java-cmd) - (let [java-home (*getenv-fn* "JAVA_HOME")] - (if-not (str/blank? java-home) - (let [f (io/file java-home "bin" java-exe)] - (if (and (.exists f) - (.canExecute f)) - (.getCanonicalPath f) - (throw (Exception. "Couldn't find 'java'. Please set JAVA_HOME.")))) - (throw (Exception. "Couldn't find 'java'. Please set JAVA_HOME.")))) - java-cmd)))) + In addition + + - the values of the `CLJ_JVM_OPTS` and `JAVA_OPTIONS` environment + variables are passed to the java subprocess as command line options + when downloading dependencies and running any other commands + respectively. -(defn -main [& command-line-args] + - if the clojure tools jar cannot be located and the clojure tools + archive is not found, an attempt is made to download the archive + from the official site and extract its contents locally. The archive + is downloaded from this process directly, unless the `CLJ_JVM_OPTS` + env variable is set and a succesful attempt is made to download the + archive by invoking a java subprocess passing the env variable value + as command line options." + [& command-line-args] (let [opts (parse-args command-line-args) + {:keys [ct-base-dir ct-jar-name]} @clojure-tools-info* + debug? (*getenv-fn* "DEPS_CLJ_DEBUG") java-cmd (get-java-cmd) env-tools-dir (or ;; legacy name @@ -457,7 +584,7 @@ For more info, see: (.getPath (io/file (home-dir) ".deps.clj" @version - "ClojureTools"))) + ct-base-dir))) install-dir tools-dir libexec-dir (if env-tools-dir (let [f (io/file env-tools-dir "libexec")] @@ -465,16 +592,19 @@ For more info, see: (.getPath f) env-tools-dir)) tools-dir) - tools-jar (io/file libexec-dir - (format "clojure-tools-%s.jar" @version)) + tools-jar (io/file libexec-dir ct-jar-name) exec-jar (io/file libexec-dir "exec.jar") proxy-settings (jvm-proxy-settings) ;; side effecting, sets java proxy properties for download + clj-jvm-opts (some-> (*getenv-fn* "CLJ_JVM_OPTS") (str/split #" ")) tools-cp (or (when (.exists tools-jar) (.getPath tools-jar)) (binding [*out* *err*] (warn "Clojure tools not yet in expected location:" (str tools-jar)) - (clojure-tools-jar-download tools-dir) + (let [java-clj-jvm-opts (when clj-jvm-opts (vec (concat clj-jvm-opts + proxy-settings + ["-Xms256m"])))] + (clojure-tools-jar-download libexec-dir java-clj-jvm-opts :debug? debug?)) tools-jar)) mode (:mode opts) exec? (= :exec mode) @@ -484,7 +614,6 @@ For more info, see: deps-edn (or (:deps-file opts) (.getPath (io/file *dir* "deps.edn"))) - clj-jvm-opts (some-> (*getenv-fn* "CLJ_JVM_OPTS") (str/split #" ")) clj-main-cmd (vec (concat [java-cmd] clj-jvm-opts diff --git a/test/borkdude/deps_test.clj b/test/borkdude/deps_test.clj index 9f97e1e..ea1e226 100644 --- a/test/borkdude/deps_test.clj +++ b/test/borkdude/deps_test.clj @@ -6,7 +6,9 @@ [clojure.edn :as edn] [clojure.java.io :as io] [clojure.string :as str] - [clojure.test :as t :refer [deftest is testing]])) + [clojure.test :as t :refer [deftest is testing]]) + + (:import [java.util.zip ZipEntry ZipOutputStream])) ;; Print out information about the java executable that will be used ;; by deps.clj, useful for user validation. @@ -133,16 +135,15 @@ (deftest tools-dir-env-test (fs/delete-tree "tools-dir") (try - (let [[out err exit] + (let [[out err] (-> (process (invoke-deps-cmd "-Sdescribe") {:out :string :err :string :extra-env {"DEPS_CLJ_TOOLS_VERSION" "1.10.3.899" "DEPS_CLJ_TOOLS_DIR" "tools-dir"}}) - deref - ((juxt :out :err :exit)))] - (when-not (zero? exit) - (println err)) + check + ((juxt :out :err)))] + (println err) (is (= "1.10.3.899" (:version (edn/read-string out)))) (is (str/includes? err "Clojure tools not yet in expected location:")) (is (fs/exists? (fs/file "tools-dir" "clojure-tools-1.10.3.899.jar"))) @@ -210,6 +211,128 @@ ~@body (or (deref ret*# 500 false) (ex-info "No shell-command invoked in body." {:body ~body-str}))))))) +(defn java-major-version-get + "Returns the major version number of the java executable used to run + the java commands at run time. For implementation simplicity the + major version before java 11 is returned as 1." + [] + (-> (process [(#'deps/get-java-cmd) "-version"] {:err :string}) + check + :err + (->> (re-find #"version \"(\d+)")) + second + Integer/parseInt)) + +(defn clojure-tools-dummy-zip-file-create + "Creates a dummy clojure tools zip file in OUT-DIR and returns its + path." + [out-dir] + (let [{:keys [ct-base-dir ct-aux-files-names ct-jar-name]} @@#'deps/clojure-tools-info* + file (io/file out-dir "borkdude-deps-test-dummy-tools.zip")] + (with-open [os (io/output-stream file) + zip (ZipOutputStream. os)] + (doseq [entry (into [ct-jar-name] ct-aux-files-names)] + (doto zip + (.putNextEntry (ZipEntry. (str ct-base-dir "/" entry))) + (.write (.getBytes (str "dummy-" entry))) + (.closeEntry))) + file))) + +(deftest clojure-tools-download + ;; Test clojure tools download methods + ;; + ;; - via java subprocess, when CLJ_JVM_OPTS (requires java11+). + ;; - direct download, when the above is not ran or fails. + ;; - manual download, simulating a user following manual instructions. + (let [java-version (java-major-version-get) + {:keys [ct-error-exit-code ct-jar-name ct-url-str ct-zip-name]} @@#'deps/clojure-tools-info*] + (testing "java downloader" + (fs/with-temp-dir + [temp-dir {}] + (let [tools-zip (clojure-tools-dummy-zip-file-create (str temp-dir)) + url (io/as-url tools-zip) + dest-zip-file (fs/file temp-dir ct-zip-name)] + (if (< java-version 11) + ;; requires java11+, fails otherwise + (do (is (= false (#'deps/clojure-tools-download-java url dest-zip-file []))) + (is (not (fs/exists? dest-zip-file)))) + + (do (is (= true (#'deps/clojure-tools-download-java url dest-zip-file []))) + (is (fs/exists? dest-zip-file))))))) + + (when (>= java-version 11) + (testing "java downloader called from -main when CLJ_JVM_OPTS is set" + (fs/with-temp-dir + [temp-dir {}] + (let [dest-jar-file (fs/file temp-dir ct-jar-name)] + (with-redefs [deps/clojure-tools-download-direct + (fn [& _] (throw (Exception. "Direct should not be called.")))] + (let [xx-pclf "-XX:+PrintCommandLineFlags" + xx-gc-threads "-XX:ConcGCThreads=1" + sh-args (get-shell-command-args + {"DEPS_CLJ_TOOLS_DIR" (str temp-dir) + "CLJ_JVM_OPTS" (str/join " " [xx-pclf xx-gc-threads])} + (deps/-main "--version"))] + (is (some #{xx-pclf} sh-args)) + ;; second and third args + (is (= [xx-pclf xx-gc-threads] (->> (rest sh-args) (take 2))))) + (is (fs/exists? dest-jar-file))))))) + + (testing "direct downloader" + (fs/with-temp-dir + [temp-dir {}] + (let [url-str ct-url-str + dest-zip-file (fs/file temp-dir ct-zip-name)] + (is (= true (deps/clojure-tools-download-direct url-str dest-zip-file))) + (is (fs/exists? dest-zip-file))))) + + (testing "direct downloader called from -main (CLJ_JVM_OPTS not set)" + (fs/with-temp-dir + [temp-dir {}] + (let [dest-jar-file (fs/file temp-dir ct-jar-name)] + (with-redefs [deps/clojure-tools-download-java + (fn [& _] (throw (Exception. "Java subprocess should not be called.")))] + (binding [deps/*getenv-fn* #(or (get {"DEPS_CLJ_TOOLS_DIR" (str temp-dir) + "CLJ_JVM_OPTS" nil} %) + (System/getenv %))] + + (deps-main-throw "--version") + (is (fs/exists? dest-jar-file))))))) + + (testing "manual user installation" + (fs/with-temp-dir + [temp-dir {}] + (let [tools-zip-file (clojure-tools-dummy-zip-file-create (str temp-dir)) + dest-zip-file (fs/file temp-dir ct-zip-name) + dest-jar-file (fs/file temp-dir ct-jar-name)] + (fs/copy tools-zip-file dest-zip-file) ;; user copies downloaded file + (with-redefs [deps/clojure-tools-download-java + (fn [& _] (throw (Exception. "Java should not be called."))) + deps/clojure-tools-download-direct + (fn [& _] (throw (Exception. "Direct should not be called.")))] + (binding [deps/*getenv-fn* #(or (get {"DEPS_CLJ_TOOLS_DIR" (str temp-dir)} %) + (System/getenv %))] + + (deps-main-throw "--version") + (is (fs/exists? dest-jar-file))))))) + + (testing "prompt for manual user install" + (fs/with-temp-dir + [temp-dir {}] + (with-redefs [deps/clojure-tools-download-java + (fn [& _] false) + deps/clojure-tools-download-direct + (fn [& _] false)] + (binding [deps/*getenv-fn* #(or (get {"DEPS_CLJ_TOOLS_DIR" (str temp-dir)} %) + (System/getenv %))] + + (let [exit-data* (atom {})] + (try (deps-main-throw "--version") + (catch Exception e + (reset! exit-data* (ex-data e)))) + (let [exit-data @exit-data*] + (is (= ct-error-exit-code (:exit-code exit-data)) exit-data))))))))) + (deftest clj-jvm-opts+java-opts ;; The `CLJ_JVM_OPTS` env var should only apply to -P and -Spom. ;; The `JAVA_OPTS` env varshould only apply to everything else.