| 1 | ;;;; The ABCL specific overrides in ASDF. |
|---|
| 2 | ;;;; |
|---|
| 3 | ;;;; Done separate from asdf.lisp for stability. |
|---|
| 4 | (require :asdf) |
|---|
| 5 | (in-package :asdf) |
|---|
| 6 | |
|---|
| 7 | (defclass iri (component) |
|---|
| 8 | ((schema :initform nil) |
|---|
| 9 | (authority :initform nil) |
|---|
| 10 | (path :initform nil) |
|---|
| 11 | (query :initform nil) |
|---|
| 12 | (fragment :initform nil))) |
|---|
| 13 | |
|---|
| 14 | (defclass mvn (iri) |
|---|
| 15 | ((group-id :initarg :group-id :initform nil) |
|---|
| 16 | (artifact-id :initarg :artifact-id :initform nil) |
|---|
| 17 | (repository :initform "http://repo1.maven.org/maven2/") ;;; XXX unimplmented |
|---|
| 18 | (classname :initarg :classname :initform nil) |
|---|
| 19 | (alternate-uri :initarg :alternate-uri :initform nil) |
|---|
| 20 | ;; inherited from ASDF:COMPONENT ??? what are the CL semantics on overriding -- ME 2012-04-01 |
|---|
| 21 | #+nil (version :initform nil))) |
|---|
| 22 | |
|---|
| 23 | #+nil |
|---|
| 24 | (defmethod find-component ((component iri) path) |
|---|
| 25 | component) |
|---|
| 26 | |
|---|
| 27 | |
|---|
| 28 | ;;; We intercept compilation to ensure that load-op will succeed |
|---|
| 29 | (defmethod perform ((op compile-op) (c mvn)) |
|---|
| 30 | (abcl-asdf:resolve |
|---|
| 31 | (ensure-parsed-mvn c))) |
|---|
| 32 | |
|---|
| 33 | (defmethod perform ((operation load-op) (c mvn)) |
|---|
| 34 | (let ((resolved-path |
|---|
| 35 | (abcl-asdf:resolve (ensure-parsed-mvn c)))) |
|---|
| 36 | (when (stringp resolved-path) |
|---|
| 37 | (java:add-to-classpath (abcl-asdf:as-classpath resolved-path))))) |
|---|
| 38 | |
|---|
| 39 | ;;; A Maven URI has the form "mvn:group-id/artifact-id/version" |
|---|
| 40 | ;;; |
|---|
| 41 | ;;; Sometimes people write "group-id:artifact-id:version" to refer to |
|---|
| 42 | ;;; Maven artifacts. One can use ABCL-ASDF:RESOLVE directly for |
|---|
| 43 | ;;; serialized references to artifacts of this form. |
|---|
| 44 | ;;; |
|---|
| 45 | ;;; Currently we "stuff" the group-id/artifact-id into the 'name' and |
|---|
| 46 | ;;; use the component 'version' for the version. Parts of ASDF |
|---|
| 47 | ;;; *reallY* want ASDF:VERSION to be a triple of intergers, and never |
|---|
| 48 | ;;; anything more, so that is part of the motivation behind this effort. |
|---|
| 49 | (defparameter *mvn-repositories* nil |
|---|
| 50 | "A list of all Maven repositories encountered in the lifetime of this instance of the implementation.") |
|---|
| 51 | |
|---|
| 52 | #+nil |
|---|
| 53 | (defmethod slot-missing ((class mvn) object slot-name operation &optional new-value) |
|---|
| 54 | (setf (slot-value object slot-name) |
|---|
| 55 | (if new-value |
|---|
| 56 | new-value |
|---|
| 57 | nil))) |
|---|
| 58 | |
|---|
| 59 | (defun ensure-parsed-mvn (component) |
|---|
| 60 | (with-slots (name group-id artifact-id |
|---|
| 61 | version schema path repository) |
|---|
| 62 | component |
|---|
| 63 | (when (null asdf::artifact-id) |
|---|
| 64 | (let ((parsed (abcl-asdf::split-string name "/")) |
|---|
| 65 | (asdf-version-p (slot-boundp component 'version)) |
|---|
| 66 | (default-version "LATEST")) |
|---|
| 67 | (cond ((= (length parsed) 3) |
|---|
| 68 | (setf |
|---|
| 69 | group-id (first parsed) |
|---|
| 70 | artifact-id (second parsed) |
|---|
| 71 | version (third parsed))) |
|---|
| 72 | ((= (length parsed) 2) |
|---|
| 73 | (setf |
|---|
| 74 | group-id (first parsed) |
|---|
| 75 | artifact-id (second parsed) |
|---|
| 76 | version (if asdf-version-p |
|---|
| 77 | version |
|---|
| 78 | default-version))) |
|---|
| 79 | (t |
|---|
| 80 | (error "Failed to construct a mvn reference from name '~A' and version '~A'" |
|---|
| 81 | name version))) |
|---|
| 82 | (setf schema "mvn") |
|---|
| 83 | (pushnew repository *mvn-repositories*) |
|---|
| 84 | ;;; Always normalized path "on the way out" to contain group-id/artifact-id/version |
|---|
| 85 | (setf path (format nil "~A/~A/~A" group-id artifact-id version)))) |
|---|
| 86 | component)) |
|---|
| 87 | |
|---|
| 88 | (export `(mvn iri ensure-parsed-mvn |
|---|
| 89 | group-id artifact-id version) 'asdf) |
|---|
| 90 | |
|---|
| 91 | (defmethod source-file-type ((component iri) (system system)) |
|---|
| 92 | nil) |
|---|
| 93 | |
|---|
| 94 | (defmethod component-relative-pathname ((component iri)) |
|---|
| 95 | nil) |
|---|
| 96 | |
|---|
| 97 | (in-package #:abcl-asdf) |
|---|
| 98 | |
|---|
| 99 | (defgeneric resolve (something) |
|---|
| 100 | (:documentation "Returns a string in JVM CLASSPATH format as entries delimited by classpath separator string.")) |
|---|
| 101 | |
|---|
| 102 | (defmethod resolve ((mvn-component asdf::mvn)) |
|---|
| 103 | "Resolve all runtime dependencies of MVN-COMPONENT. |
|---|
| 104 | |
|---|
| 105 | Returns either a string in jvm classpath format as entries delimited |
|---|
| 106 | by classpath separator string or T. If the value T is returned, it |
|---|
| 107 | denotes that current JVM already has already loaded a given class. Can possibly be a |
|---|
| 108 | single entry denoting a remote binary artifact." |
|---|
| 109 | (let ((name (slot-value mvn-component 'asdf::name)) |
|---|
| 110 | (group-id (slot-value mvn-component 'asdf::group-id)) |
|---|
| 111 | (artifact-id (slot-value mvn-component 'asdf::artifact-id)) |
|---|
| 112 | (classname (slot-value mvn-component 'asdf::classname)) |
|---|
| 113 | (alternate-uri (slot-value mvn-component 'asdf::alternate-uri)) |
|---|
| 114 | (version (if (slot-value mvn-component 'asdf::version) |
|---|
| 115 | (slot-value mvn-component 'asdf::version) |
|---|
| 116 | "LATEST"))) |
|---|
| 117 | (handler-case |
|---|
| 118 | (when (and classname |
|---|
| 119 | (jss:find-java-class classname)) |
|---|
| 120 | (warn "Not loading ~A from the network because ~A is present in classpath." |
|---|
| 121 | name classname) |
|---|
| 122 | (return-from resolve t)) |
|---|
| 123 | (java:java-exception (e) |
|---|
| 124 | (unless (java:jinstance-of-p (java:java-exception-cause e) |
|---|
| 125 | "java.lang.ClassNotFoundException") |
|---|
| 126 | (error "Unexpected Java exception~&~A.~&" e)))) |
|---|
| 127 | (if (find-mvn) |
|---|
| 128 | (resolve-dependencies group-id artifact-id version) |
|---|
| 129 | (if alternate-uri |
|---|
| 130 | (values (namestring alternate-uri) alternate-uri) |
|---|
| 131 | (t |
|---|
| 132 | (error "Failed to resolve MVN component name ~A." name)))))) |
|---|
| 133 | |
|---|
| 134 | (defun as-classpath (classpath) |
|---|
| 135 | "Break apart the JVM CLASSPATH string into a list of its consituents." |
|---|
| 136 | (split-string classpath |
|---|
| 137 | (java:jfield "java.io.File" "pathSeparator"))) |
|---|
| 138 | |
|---|
| 139 | (defun split-string (string split-char) |
|---|
| 140 | (loop :for i = 0 :then (1+ j) |
|---|
| 141 | :as j = (position split-char string :test #'string-equal :start i) |
|---|
| 142 | :collect (subseq string i j) |
|---|
| 143 | :while j)) |
|---|