-
-
Notifications
You must be signed in to change notification settings - Fork 53
/
Copy pathclasspath.clj
137 lines (121 loc) · 4.41 KB
/
classpath.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
(ns orchard.java.classpath
"Classpath access and modification utilities.
Provides an alternative to the java.classpath contrib library.
The library is Boot-aware, meaning it takes into account the
classpath manipulation magic, performed by the Boot build tool."
(:require
[clojure.java.io :as io]
[clojure.string :as str]
[dynapath.util :as dp]
[orchard.misc :as misc])
(:import
(java.io File)
(java.net URI URL)
(java.nio.file Paths)
(java.util.jar JarFile JarEntry)))
;;; Classloaders
(defn context-classloader
"Returns the current classloader for the current thread."
[]
(.getContextClassLoader (Thread/currentThread)))
(defn classloaders
"Returns the classloader hierarchy."
([^ClassLoader loader]
(->> loader
(iterate #(.getParent ^ClassLoader %))
(take-while identity)))
([]
(classloaders (context-classloader))))
(defn modifiable-classloader
"Returns the highest classloader in the hierarchy that satisfies
`dynapath.util/addable-classpath?`, or nil if none do."
([^ClassLoader loader]
(last (filter dp/addable-classpath?
(classloaders loader))))
([]
(modifiable-classloader (context-classloader))))
(defn set-classloader!
"Sets the current classloader for the current thread."
[^ClassLoader loader]
(let [thread (Thread/currentThread)]
(.setContextClassLoader thread loader)
loader))
;;; Classpaths
(defn system-classpath
"Returns the URLs defined by the 'java.class.path' system property."
[]
(map (comp io/as-url io/as-file)
(.split (System/getProperty "java.class.path")
(System/getProperty "path.separator"))))
(defn classpath
"Returns the URLs on the classpath."
([^ClassLoader loader]
(->> (classloaders loader)
(mapcat dp/classpath-urls)
(concat (system-classpath))
(distinct)))
([]
(classpath (context-classloader))))
(defn add-classpath!
"Adds the URL to the classpath and returns it if successful, or nil otherwise,
ensuring that a modifiable classloader is available."
[^URL url]
(let [loader (or (modifiable-classloader)
(set-classloader!
(modifiable-classloader @Compiler/LOADER)))]
(when (dp/add-classpath-url loader url)
url)))
;;; Classpath resources
(defn classpath-seq
"Returns a sequence of all descendant non-directory files or archive entries
as relative paths."
[^URL url]
(let [f (io/as-file url)]
(if (misc/archive? url)
(->> (enumeration-seq (.entries (JarFile. f)))
(filter #(not (.isDirectory ^JarEntry %)))
(map #(.getName ^JarEntry %)))
(->> (file-seq f)
(filter #(not (.isDirectory ^File %)))
(map #(.getPath (.relativize (.toURI url) (.toURI ^File %))))))))
;;; Boot support - previously part of cider-nrepl
;;
;; The Boot build tool stores files in a temporary directory, so
;; we have to do a bit of work to figure out where the real resources are.
(defn boot-classloader
"Creates a class-loader that knows original source files paths in Boot project."
[]
(let [class-path (System/getProperty "fake.class.path")
dir-separator (System/getProperty "file.separator")
paths (str/split class-path (re-pattern (System/getProperty "path.separator")))
urls (map
(fn [path]
(let [url (if (re-find #".jar$" path)
(str "file:" path)
(str "file:" path dir-separator))]
(new java.net.URL url)))
paths)]
;; TODO: Figure out how to add the JDK sources here
(new java.net.URLClassLoader (into-array java.net.URL urls))))
(defn boot-aware-classloader
[]
(if (misc/boot-project?)
(boot-classloader)
(context-classloader)))
(defn classpath-file-relative-path
"Boot stores files in a temporary directory & ClojureScript stores
the :file metadata location absolutely instead of relatively to the
classpath. This means when doing jump to source in Boot &
ClojureScript, you end up at the temp file. This code attempts to
find the classpath-relative location of the file, so that it can be
opened correctly."
[s]
(let [path (Paths/get s (into-array String []))
path-count (.getNameCount path)]
(or (first
(sequence
(comp (map #(.subpath path % path-count))
(map str)
(filter io/resource))
(range path-count)))
s)))