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