Skip to content

Commit

Permalink
Add ability to skip dynapath-based functionality
Browse files Browse the repository at this point in the history
Closes #112
Related #103
Related #105
  • Loading branch information
vemv authored and bbatsov committed Apr 13, 2021
1 parent 8d488a2 commit d4fa4b8
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 96 deletions.
60 changes: 11 additions & 49 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ executors:
docker:
- image: circleci/clojure:openjdk-11-lein-2.9.1
<<: *defaults
openjdk15:
openjdk16:
docker:
- image: circleci/clojure:openjdk-15-lein-2.9.5-buster
- image: circleci/clojure:openjdk-16-lein-2.9.5-buster
<<: *defaults

# Runs a given set of steps, with some standard pre- and post-
Expand Down Expand Up @@ -114,9 +114,13 @@ jobs:
clojure_version:
description: Version of Clojure to test against
type: string
test_profiles:
description: Maps directly to the TEST_PROFILES var in Makefile
type: string
executor: << parameters.jdk_version >>
environment:
VERSION: << parameters.clojure_version >>
TEST_PROFILES: << parameters.test_profiles >>
steps:
- checkout
- with_cache:
Expand Down Expand Up @@ -146,53 +150,11 @@ workflows:
ci-test-matrix:
jobs:
- test_code:
name: Java 8, Clojure 1.8
clojure_version: "1.8"
jdk_version: openjdk8
- test_code:
name: Java 8, Clojure 1.9
clojure_version: "1.9"
jdk_version: openjdk8
- test_code:
name: Java 8, Clojure 1.10
clojure_version: "1.10"
jdk_version: openjdk8
- test_code:
name: Java 8, Clojure master
clojure_version: "master"
jdk_version: openjdk8
- test_code:
name: Java 11, Clojure 1.8
clojure_version: "1.8"
jdk_version: openjdk11
- test_code:
name: Java 11, Clojure 1.9
clojure_version: "1.9"
jdk_version: openjdk11
- test_code:
name: Java 11, Clojure 1.10
clojure_version: "1.10"
jdk_version: openjdk11
- test_code:
name: Java 11, Clojure master
clojure_version: "master"
jdk_version: openjdk11
- test_code:
name: Java 15, Clojure 1.8
clojure_version: "1.8"
jdk_version: openjdk15
- test_code:
name: Java 15, Clojure 1.9
clojure_version: "1.9"
jdk_version: openjdk15
- test_code:
name: Java 15, Clojure 1.10
clojure_version: "1.10"
jdk_version: openjdk15
- test_code:
name: Java 15, Clojure master
clojure_version: "master"
jdk_version: openjdk15
matrix:
parameters:
jdk_version: [openjdk8, openjdk11, openjdk16]
clojure_version: ["1.8", "1.9", "1.10", "master"]
test_profiles: ["+test", "+test,+no-dynapath"]
- util_job:
name: Code Linting, JDK8 (Eastwood only)
jdk_version: openjdk8
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/checkouts
/classes
/gh-pages
/unzipped-jdk-source
/target
autodoc.sh
pom.xml
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

### Changes

* [#113](https://github.com/clojure-emacs/orchard/issues/113) Add ability to skip functionality that works by altering the classpath
* You an opt in to this choice by setting `"-Dorchard.use-dynapath=false"`.
* The _class info cache_ is now initialized silently by default. This results in less confusing output.
* You can now `@orchard.java/cache-initializer` for deterministically waiting for this cache workload to complete.
* You can control its verbosity by setting `"-Dorchard.initialize-cache.silent=false"` (or `[...]=true`).
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

VERSION ?= 1.10

TEST_PROFILES := +test
TEST_PROFILES ?= +test

resources/clojuredocs/export.edn:
curl -o $@ https://github.com/clojure-emacs/clojuredocs-export-edn/raw/master/exports/export.compact.edn
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,13 @@ functionality that's provided.
So far, Orchard follows these options, which can be specified as Java system properties
(which means that end users can choose to set them globally without fiddling with tooling internals):

* `"-Dorchard.use-dynapath=false"` (default: true)
* if `false`, all features that currently depend on dynapath (and therefore alter the classpath) will be disabled.
* This is a way to avoid a number of known issues: [#103](https://github.com/clojure-emacs/orchard/issues/103), [#105](https://github.com/clojure-emacs/orchard/issues/105), [#112](https://github.com/clojure-emacs/orchard/pull/112).
* Note that if this option is `false`, Orchard clients will have to figure out themselves a way to e.g. fetch Java sources.
* It is foreseen that soon enough this will be reliably offered as a Lein plugin.
* `"-Dorchard.initialize-cache.silent=true"` (default: `true`)
* if false, the _class info cache_ initialization may print warnings (possibly spurious ones).
* if `false`, the _class info cache_ initialization may print warnings (possibly spurious ones).xx

## History

Expand Down
73 changes: 69 additions & 4 deletions project.clj
Original file line number Diff line number Diff line change
@@ -1,3 +1,61 @@
;;;; The following code allows to add the JDK sources without `dynapath` being present.

(require '[clojure.java.io :as io])

(import '[java.util.zip ZipInputStream]
'[java.io FileOutputStream])

(defmacro while-let [[sym expr] & body]
`(loop [~sym ~expr]
(when ~sym
~@body
(recur ~expr))))

(defn jdk-find [f]
(let [home (io/file (System/getProperty "java.home"))
parent (.getParentFile home)
paths [(io/file home f)
(io/file home "lib" f)
(io/file parent f)
(io/file parent "lib" f)]]
(->> paths (filter #(.canRead ^java.io.File %)) first str)))

(def jdk-sources
(let [java-path->zip-path (fn [path]
(some-> (io/resource path)
^java.net.JarURLConnection (. openConnection)
(. getJarFileURL)
io/as-file
str))]
(or (java-path->zip-path "java.base/java/lang/Object.java") ; JDK9+
(java-path->zip-path "java/lang/Object.java") ; JDK8-
(jdk-find "src.zip"))))

(defn uncompress [path target]
(let [zis (-> target io/input-stream ZipInputStream.)]
(while-let [entry (-> zis .getNextEntry)]
(let [size (-> entry .getSize)
bytes (byte-array 1024)
dest (->> entry .getName (io/file path))
dir (-> entry .getName (clojure.string/split #"/") butlast)
_ (->> (clojure.string/join "/" dir) (java.io.File. path) .mkdirs)
output (FileOutputStream. dest)]
(loop [len (-> zis (.read bytes))]
(when (pos? len)
(-> output (.write bytes 0 len))
(recur (-> zis (.read bytes)))))
(-> output .close)))))

(defn unzipped-jdk-source []
(let [choice jdk-sources]
(when-not (-> "unzipped-jdk-source" io/file .exists)
(-> "unzipped-jdk-source" io/file .mkdirs)
;; For some reason simply adding a .zip to the classpath doesn't work, so one has to uncompress the contents:
(uncompress "./unzipped-jdk-source/" choice))
"unzipped-jdk-source"))

(def jdk8? (->> "java.version" System/getProperty (re-find #"^1.8.")))

(defproject cider/orchard "0.6.5"
:description "A fertile ground for Clojure tooling"
:url "https://github.com/clojure-emacs/orchard"
Expand All @@ -23,6 +81,8 @@
:password :env/clojars_password
:sign-releases false}]]

:jvm-opts ["-Dorchard.use-dynapath=true"]

:profiles {
;; Clojure versions matrix
:provided {:dependencies [[org.clojure/clojure "1.10.1"]
Expand All @@ -38,10 +98,17 @@
:dependencies [[org.clojure/clojure "1.11.0-master-SNAPSHOT"]
[org.clojure/clojure "1.11.0-master-SNAPSHOT" :classifier "sources"]]}

:test {:resource-paths ["test-resources"]
:test {:dependencies [[org.clojure/java.classpath "1.0.0"]]
:resource-paths ["test-resources"]
;; Initialize the cache verbosely, as usual, so that possible issues can be more easily diagnosed:
:jvm-opts ["-Dorchard.initialize-cache.silent=false"]}

:no-dynapath {:jvm-opts ["-Dorchard.use-dynapath=false"]
:resource-paths [~(unzipped-jdk-source)]
:plugins ~(if jdk8?
'[[lein-jdk-tools "0.1.1"]]
[])}

;; Development tools
:dev {:dependencies [[pjstadig/humane-test-output "0.10.0"]]
:resource-paths ["test-resources"]
Expand All @@ -63,8 +130,6 @@
{:dependencies [[clj-kondo "2021.03.31"]]}]

:eastwood {:plugins [[jonase/eastwood "0.4.0"]]
:eastwood {:exclude-namespaces [~(if (-> "java.version"
System/getProperty
(.contains "1.8."))
:eastwood {:exclude-namespaces [~(if jdk8?
'orchard.java.parser
'orchard.java.legacy-parser)]}}})
31 changes: 20 additions & 11 deletions src/orchard/java.clj
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,24 @@
(io/file parent "lib" f)]]
(->> paths (filter #(.canRead ^File %)) first io/as-url)))

(def add-java-sources-via-dynapath?
"Should orchard use the dynapath library to use \"fetch Java sources\" functionality?
Note that using dynapath currently implies some bugs, so you might want to disable this option."
(contains? #{"true" "1"} (System/getProperty "orchard.use-dynapath" "true")))

(def jdk-sources
"The JDK sources path. If found on the existing classpath, this is the
corresponding classpath entry. Otherwise, the JDK directory is searched for
the file `src.zip`."
(let [base-url (fn [path]
(some-> (io/resource path)
^JarURLConnection (. openConnection)
(. getJarFileURL)))]
(or (base-url "java.base/java/lang/Object.java") ; JDK9+
(base-url "java/lang/Object.java") ; JDK8-
(jdk-find "src.zip"))))
(when add-java-sources-via-dynapath?
(let [base-url (fn [path]
(some-> (io/resource path)
^JarURLConnection (. openConnection)
(. getJarFileURL)))]
(or (base-url "java.base/java/lang/Object.java") ; JDK9+
(base-url "java/lang/Object.java") ; JDK8-
(jdk-find "src.zip")))))

(def jdk-tools
"The `tools.jar` path, for JDK8 and earlier. If found on the existing
Expand All @@ -78,15 +85,17 @@
(or (some-> (io/resource "com/sun/javadoc/Doc.class")
^JarURLConnection (. openConnection)
(. getJarFileURL))
(some-> (jdk-find "tools.jar") cp/add-classpath!))))
(and add-java-sources-via-dynapath?
(some-> (jdk-find "tools.jar") cp/add-classpath!)))))

(defn ensure-jdk-sources
"If `jdk-sources` is present, check that this entry is on the context
classpath and if not, add it."
[]
(let [classpath (set (cp/classpath))]
(when (and jdk-sources (not (classpath jdk-sources)))
(cp/add-classpath! jdk-sources))))
(when add-java-sources-via-dynapath?
(let [classpath (set (cp/classpath))]
(when (and jdk-sources (not (classpath jdk-sources)))
(cp/add-classpath! jdk-sources)))))

;;; ## Javadoc URLs
;;
Expand Down
57 changes: 30 additions & 27 deletions test/orchard/java/classpath_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[clojure.set :as set]
[clojure.string :as str]
[clojure.test :refer [deftest is testing]]
[orchard.java]
[orchard.java.classpath :as cp]
[orchard.misc :as misc])
(:import
Expand Down Expand Up @@ -48,31 +49,33 @@
(set (cp/system-classpath))
(set (cp/classpath)))))))

(deftest classpath-resources-test
(testing "Iterating classpath resources"
(testing "returns non-empty lists"
(is (every? seq (map cp/classpath-seq (cp/classpath)))))
(testing "returns relative paths"
(is (every? #(not (.isAbsolute (File. %)))
(mapcat cp/classpath-seq (cp/classpath)))))))
(when orchard.java/add-java-sources-via-dynapath?
(deftest classpath-resources-test
(testing "Iterating classpath resources"
(testing "returns non-empty lists"
(is (every? seq (map cp/classpath-seq (cp/classpath)))))
(testing "returns relative paths"
(is (every? #(not (.isAbsolute (File. %)))
(mapcat cp/classpath-seq (cp/classpath))))))))

(deftest classloader-test
(testing "Classloader hierarchy contains current classloader"
(is (contains? (set (cp/classloaders)) (cp/context-classloader))))
(testing "Classpath modification"
(let [orig-classloaders (cp/classloaders)
orig-classpath (cp/classpath)
url (-> (System/getProperty "java.io.tmpdir")
(io/file "test.txt")
(io/as-url))]
(cp/add-classpath! url)
(testing "adds the URL"
(is (contains? (set (cp/classpath)) url)))
(testing "preserves prior classpath URLs"
(is (set/subset?
(set orig-classpath)
(set (cp/classpath)))))
(testing "preserves the classloader hierarchy"
(is (set/subset?
(set orig-classloaders)
(set (cp/classloaders))))))))
(when orchard.java/add-java-sources-via-dynapath?
(deftest classloader-test
(testing "Classloader hierarchy contains current classloader"
(is (contains? (set (cp/classloaders)) (cp/context-classloader))))
(testing "Classpath modification"
(let [orig-classloaders (cp/classloaders)
orig-classpath (cp/classpath)
url (-> (System/getProperty "java.io.tmpdir")
(io/file "test.txt")
(io/as-url))]
(cp/add-classpath! url)
(testing "adds the URL"
(is (contains? (set (cp/classpath)) url)))
(testing "preserves prior classpath URLs"
(is (set/subset?
(set orig-classpath)
(set (cp/classpath)))))
(testing "preserves the classloader hierarchy"
(is (set/subset?
(set orig-classloaders)
(set (cp/classloaders)))))))))
13 changes: 13 additions & 0 deletions test/orchard/java/classpath_test/third_party_compat_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
(ns orchard.java.classpath-test.third-party-compat-test
(:require
[orchard.java]
[clojure.java.classpath]
[clojure.test :refer [deftest is]]))

;; make this namespace's tests deterministic:
@orchard.java/cache-initializer

(when-not orchard.java/add-java-sources-via-dynapath?
(deftest works
(is (seq (clojure.java.classpath/classpath-directories))
"The presence of `clojure.java` does not affect third-party libraries")))
10 changes: 7 additions & 3 deletions test/orchard/java_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@

(def jdk-parser? (or (>= misc/java-api-version 9) jdk-tools))

(when-not jdk-parser? (println "No JDK parser available!"))
(when-not jdk-sources (println "No JDK sources available!"))
(assert jdk-parser? "No JDK parser available!")
(assert (if orchard.java/add-java-sources-via-dynapath?
jdk-sources
true)
"No JDK sources available!")

(javadoc/add-remote-javadoc "com.amazonaws." "http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/")
(javadoc/add-remote-javadoc "org.apache.kafka." "https://kafka.apache.org/090/javadoc/")
Expand All @@ -25,7 +28,8 @@
(dp/all-classpath-urls)
(set))]
(testing "of defined vars"
(is (= jdk-sources jdk-sources-path))
(when orchard.java/add-java-sources-via-dynapath?
(is (= jdk-sources jdk-sources-path)))
(is (= jdk-tools jdk-tools-path)))
(testing "of dynamically added classpath entries"
(is (= jdk-sources (classpath-urls jdk-sources)))
Expand Down

0 comments on commit d4fa4b8

Please sign in to comment.