source: trunk/abcl/src/org/armedbear/lisp/abcl-contrib.lisp @ 15329

Last change on this file since 15329 was 15329, checked in by Mark Evenson, 3 years ago

meta: add a TLS network request as fallback for the system

File size: 8.9 KB
Line 
1;;;; Mechanisms for finding loadable artifacts from the environment,
2;;;; which are then used to locate the Common Lisp systems included as
3;;;; `abcl-contrib`.
4(require :asdf)
5
6(in-package :system)
7
8(defun boot-classloader ()
9  (let ((boot-class (java:jclass "org.armedbear.lisp.Main"))
10        (get-classloader (java:jmethod "java.lang.Class" "getClassLoader")))
11    (java:jcall get-classloader boot-class)))
12
13;;; java[678] packages the JVM system artifacts as jar files
14;;; java11 uses the module system
15(defun system-artifacts-are-jars-p ()
16  (java:jinstance-of-p (boot-classloader) "java.net.URLClassLoader"))
17
18(defun system-jar-p (p)
19  (or (named-jar-p "abcl" p)
20      (named-jar-p "abcl-aio" p)))
21
22(defun contrib-jar-p (p)
23  (or 
24   (named-jar-p "abcl-contrib" p)
25   (named-jar-p "abcl-aio" p)))
26
27(defun named-jar-p (name p)
28  (and (pathnamep p)
29       (equal (pathname-type p) "jar")
30       (or
31        (java:jstatic "matches"
32                      "java.util.regex.Pattern"
33                      (concatenate 'string name "(-[0-9]\\.[0-9]\\.[0-9]([+~-].+)?)?")
34                      (pathname-name p))
35        (java:jstatic "matches"
36                      "java.util.regex.Pattern"
37                      (concatenate 'string name "(-[0-9]\\.[0-9]\\.[0-9]\\.[0-9]([+~-]+)?)?")
38                      (pathname-name p)))
39       p))
40
41(defun find-system ()
42  "Find the location of the Armed Bear system implementation
43
44Used to determine relative pathname to find 'abcl-contrib.jar'."
45  (or
46   (ignore-errors
47     (find-system-jar))
48   (ignore-errors
49     (when (system-artifacts-are-jars-p)
50       (some
51        (lambda (u)
52          (probe-file (make-pathname
53                       :defaults (java:jcall "toString" u)
54                       :name "abcl")))
55        (java:jcall "getURLs" (boot-classloader)))))
56   ;; Need to test locating the system boot jar over the network, and
57   ;; it would minimally need to check version information.
58   (ignore-errors
59     #p"https://abcl.org/releases/1.7.0/abcl.jar")))
60
61(defun flatten (list)
62  (labels ((rflatten (list accumluator)
63     (dolist (element list)
64       (if (listp element)
65     (setf accumluator (rflatten element accumluator))
66     (push element accumluator)))
67     accumluator))
68    (let (result)
69      (reverse (rflatten list result)))))
70
71(defun java.class.path ()
72  "Return a list of the directories as pathnames referenced in the JVM classpath."
73  (let* ((separator (java:jstatic "getProperty" "java.lang.System" "path.separator"))
74   (paths (coerce (java:jcall "split"
75         (java:jstatic "getProperty" "java.lang.System"
76           "java.class.path")
77         separator)
78                        'list))
79         (p (coerce paths 'list)))
80    (flet ((directory-of (p) (make-pathname :defaults p :name nil :type nil)))
81      (values
82       (mapcar #'directory-of p)
83       p))))
84
85(defun enumerate-resource-directories ()
86  (flet ((directory-of (p)
87           (make-pathname :defaults p
88                          :name nil
89                          :type nil)))
90    (let ((result (java.class.path)))
91      (dolist (entry (flatten (java:dump-classpath)))
92        (cond
93          ((java:jinstance-of-p entry "java.net.URLClassLoader") ;; java1.[678]
94     (dolist (url (coerce (java:jcall "getURLs" entry)
95              'list))
96             (let ((p (directory-of (pathname (java:jcall "toString" url)))))
97         (when (probe-file p)
98           (pushnew p result :test 'equal)))))
99        ((pathnamep entry)
100         (pushnew (directory-of entry) result :test 'equal))
101        ((and (stringp entry)
102        (probe-file (pathname (directory-of entry))))
103         (pushnew (pathname (directory-of entry)) result :test 'equal))
104        (t
105         (format *standard-output*
106                 "~&Skipping enumeration of resource '~a' with type '~a'.~%"
107                 entry (type-of entry)))))
108      result)))
109
110(defun find-jar (predicate)
111  (dolist (d (enumerate-resource-directories))
112    (let ((entries (directory (make-pathname :defaults d
113               :name "*"
114               :type "jar"))))
115      (let ((jar (some predicate entries)))
116  (when jar
117    (return-from find-jar jar))))))
118
119(defun find-system-jar ()
120  "Return the pathname of the system jar, one of `abcl.jar` or
121`abcl-m.n.p.jar` or `abcl-m.n.p[.~-]something.jar`."
122  (find-jar #'system-jar-p))
123
124(defun find-contrib-jar ()
125  "Return the pathname of the contrib jar, one of `abcl-contrib.jar` or
126`abcl-contrib-m.n.p.jar` or `abcl-contrib-m.n.p[.~-]something.jar`."
127  (find-jar #'contrib-jar-p))
128
129(defvar *abcl-contrib* nil
130  "Pathname of the abcl-contrib artifact.
131
132Initialized via SYSTEM:FIND-CONTRIB.")
133
134;;; FIXME: stop using the obsolete ASDF:*CENTRAL-REGISTRY*
135(defun add-contrib (abcl-contrib-jar
136                    &key (verbose cl:*load-verbose*))
137  "Introspects the ABCL-CONTRIB-JAR path for sub-directories which
138  contain asdf definitions, adding those found to asdf."
139  (let ((jar-path (if (ext:pathname-jar-p abcl-contrib-jar)
140                      abcl-contrib-jar
141                      (make-pathname :device (list abcl-contrib-jar)))))
142    (dolist (asdf-file
143             (directory (merge-pathnames "*/*.asd" jar-path)))
144      (let ((asdf-directory (make-pathname :defaults asdf-file :name nil :type nil)))
145        (unless (find asdf-directory asdf:*central-registry* :test #'equal)
146          (push asdf-directory asdf:*central-registry*)
147          (format verbose "~&; Added ~A to ASDF.~%" asdf-directory))))))
148
149(defun find-and-add-contrib (&key (verbose cl:*load-verbose*))
150  "Attempt to find the ABCL contrib jar and add its contents to ASDF.
151returns the pathname of the contrib if it can be found."
152   (if *abcl-contrib*
153       (format verbose "~&; Finding contribs utilizing previously initialized value of SYS:*ABCL-CONTRIB* '~A'.~%"
154               *abcl-contrib*)
155       (progn
156         (let ((contrib (find-contrib)))
157           (when contrib
158             (format verbose "~&; Using probed value of SYS:*ABCL-CONTRIB* '~A'.~%"
159                     contrib)
160             (setf *abcl-contrib* contrib)))))
161   (when *abcl-contrib*  ;; For bootstrap compile there will be no contrib
162     (add-contrib *abcl-contrib*)))
163
164(defun find-name-for-implementation-title (file id)
165  "For a jar FILE containing a manifest, return the name of the
166  section which annotates 'Implementation-Title' whose string value is
167  ID."
168  (declare (type pathname file))
169  (let* ((jar (java:jnew "java.util.jar.JarFile" (namestring file)))
170         (manifest (java:jcall "getManifest" jar))
171         (entries (java:jcall "toArray"
172                              (java:jcall "entrySet"
173                                          (java:jcall "getEntries" manifest)))))
174    (dolist (entry 
175              (loop :for entry :across entries
176                 :collecting entry))
177      (let ((title (java:jcall "getValue"
178                               (java:jcall "getValue" entry)
179                               "Implementation-Title")))
180        (when (string-equal title id)
181          (return-from find-name-for-implementation-title
182            (java:jcall "getKey" entry))))
183    nil)))
184
185(defun find-contrib ()
186  "Introspect runtime classpaths to return a pathname containing
187  subdirectories containing ASDF definitions."
188
189  (or
190   ;; We identify the location of the directory within a jar file
191   ;; containing abcl-contrib ASDF definitions by looking for a section
192   ;; which contains the Implementation-Title "org.abcl-contrib".  The
193   ;; name of that section then identifies the relative pathname to the
194   ;; top-most directory in the Jar
195   ;;
196   ;; e.g. for an entry of the form
197   ;;
198   ;;     Name: contrib
199   ;;     Implementation-Title: org.abcl-contrib
200   ;;
201   ;; the directory 'contrib' would be searched for ASDF definitions.
202   (ignore-errors
203        (let* ((system-jar
204                (find-system-jar))
205               (relative-pathname 
206                (find-name-for-implementation-title system-jar "org.abcl-contrib")))
207          (when (and system-jar relative-pathname)
208            (merge-pathnames (pathname (concatenate 'string
209                                                   relative-pathname "/"))
210                            (make-pathname
211                             :device (list system-jar))))))
212   (ignore-errors
213     (find-contrib-jar))
214   (ignore-errors
215     (let ((system-jar (find-system-jar)))
216       (when system-jar
217         (probe-file (make-pathname
218                      :defaults system-jar
219                      :name (concatenate 'string
220                                         "abcl-contrib"
221                                         (subseq (pathname-name system-jar) 4)))))))
222   (when (java:jinstance-of-p (boot-classloader) "java.net.URLClassLoader")
223     (some
224      (lambda (u)
225        (probe-file (make-pathname
226                     :defaults (java:jcall "toString" u)
227                     :name "abcl-contrib")))
228      (java:jcall "getURLs" (boot-classloader))))))
229
230(export '(find-system
231          find-contrib
232          system-artifacts-are-jars-p
233          java.class.path
234          *abcl-contrib*)
235        :system)
236
237(when (find-and-add-contrib :verbose cl:*load-verbose*)
238  (provide :abcl-contrib))
Note: See TracBrowser for help on using the repository browser.