source: trunk/abcl/contrib/abcl-asdf/maven.lisp

Last change on this file was 15657, checked in by Mark Evenson, 21 months ago

abcl-asdf: provide restart for installing Maven

Not as useful as one might think, as ASDF apparently swallows all
conditions.

File size: 28.7 KB
Line 
1;;;; Use the Aether system packaged as jar files in a locally
2;;;; installed Maven3 distribution to download and install JVM
3;;;; artifact dependencies.
4
5
6#|
7
8# Implementation
9
10Not necessarily multi-threaded safe, and unclear how much work that
11would be, as it is unknown how the Maven implementation behaves.
12
13## Installing Maven
14http://maven.apache.org/download.html
15
16## Current Javadoc for Maven Aether connector
17http://sonatype.github.com/sonatype-aether/apidocs/overview-summary.html
18
19## Incomplete, seemingly often wrong
20https://docs.sonatype.org/display/AETHER/Home
21
22Note that this is not an implementation of Maven per se, but the use
23of the Maven Aether connector infrastructure.  Among other things, this means
24that the Maven specific "~/.m2/settings.xml" file is NOT parsed for settings.
25
26|#
27
28#|
29
30We aim to be compatible with the "current" version of Maven back to
31maven-3.0.4.  The necessary internals of Maven are messy, and not very
32well abstracted, especially in the earlier releases.  In maintaining
33this code over the past decade, it has been the case that entire APIs
34will disappear during what are advertised as "patchlevel" upgrades of
35Maven.
36
37
38|#
39
40;;; N.b. evaluated *after* we load the ABCL specific modifications of
41;;;      ASDF in abcl-asdf.lisp
42
43(in-package :abcl-asdf)
44
45(require :abcl-contrib)
46(require :jss)
47
48#|
49Test:
50(abcl-asdf:resolve "org.slf4j:slf4j-api:1.6.1")
51
52(abcl-asdf:resolve "org.apache.maven:maven-aether-provider:3.0.4")
53
54(abcl-asdf:resolve "com.google.gwt:gwt-user")
55
56|#
57
58(defparameter *mavens* 
59  (if (find :windows *features*)
60      '("mvn" "mvn.bat" "mvn.cmd" "mvn3.bat")
61      '("mvn" "mvn3"
62        ;; MacPorts
63        "/opt/local/bin/mvn" "/opt/local/bin/mvn3"))
64  "Locations to search for the Maven executable.")
65
66(defun find-mvn () 
67  "Attempt to find a suitable Maven ('mvn') executable on the hosting operating system.
68
69Returns the path of the Maven executable or nil if none are found.
70
71Returns the version of Maven found as the second value.
72
73Emits warnings if not able to find a suitable executable."
74
75  (let ((m2-home (ext:getenv "M2_HOME"))
76        (m2 (ext:getenv "M2"))
77        (mvn-executable (if (find :unix *features*)
78                            "mvn"
79                            "mvn.bat")))
80    (when (and m2-home (probe-file m2-home))
81      (let* ((m2-home (truename m2-home))
82             (mvn-path (merge-pathnames 
83                        (format nil "bin/~A" mvn-executable)
84                        m2-home))
85             (mvn (truename mvn-path)))
86        (if mvn
87            (values (return-from find-mvn mvn)
88                    (ensure-mvn-version))
89            (warn "M2_HOME was set to '~A' in the process environment but '~A' doesn't exist." 
90                  m2-home mvn-path))))
91    (when (and m2 (probe-file m2))
92      (let* ((m2 (truename m2))
93             (mvn-path (merge-pathnames mvn-executable m2))
94             (mvn (truename mvn-path)))
95        (if mvn
96            (values (return-from find-mvn mvn)
97                    (ensure-mvn-version))
98            (warn "M2 was set to '~A' in the process environment but '~A' doesn't exist." 
99                  m2 mvn-path))))
100    (let ((which-cmd 
101            (if (find :unix *features*)
102                "which" 
103                ;; Starting with Windows Server 2003
104                "where.exe")))
105      (dolist (mvn-path *mavens*)
106  (let ((mvn 
107         (handler-case
108       (truename 
109        (string-trim
110         '(#\space #\newline #\return #\tab)
111         (uiop:run-program
112          (format nil "~a ~a" which-cmd mvn-path)
113          :output :string)))
114     (t (e) 
115       (format cl:*load-verbose*
116         "~&; abcl-asdf; Failed to find Maven executable '~a' in PATH because~%~a" 
117         mvn-path e)))))
118    (when mvn
119      (return-from find-mvn mvn)))))
120  (warn "Unable to locate Maven executable to find Maven Aether adaptors.")))
121
122(defun find-mvn-libs ()
123  (unless (find-mvn)
124    (warn "Failed to find Maven executable to determine Aether library location.  Continuing anyways."))
125  (some 
126   (lambda (d)
127     (when (and
128            (pathnamep d)
129            (directory (merge-pathnames "maven-core*.jar" d)))
130       (truename d)))
131   (list (ignore-errors
132           (make-pathname :defaults (merge-pathnames "../lib/" (find-mvn))
133                          :name nil :type nil))
134         (ignore-errors
135           (make-pathname :defaults (merge-pathnames "lib/" (mvn-home))
136                          :name nil :type nil))
137         ;; library location for homebrew maven package on OS X
138         (ignore-errors
139           (make-pathname :defaults (merge-pathnames "../libexec/lib/" (find-mvn))
140                          :name nil :type nil))
141         #p"/usr/local/share/java/maven3/lib/" ;; FreeBSD ports
142         #p"/usr/local/maven/lib/"))) ;; OpenBSD location suggested by Timo MyyrÀ
143
144(defparameter *mvn-libs-directory*
145  nil
146  "Location of 'maven-core-3.<m>.<p>.jar', 'maven-embedder-3.<m>.<p>.jar' etc.")
147
148(defun normalize-mvn-libs ()
149  "Ensure that any *mvn-libs-directory* is a both directory and a pathname"
150  (unless *mvn-libs-directory*
151    (return-from normalize-mvn-libs nil))
152  (when (not (pathnamep *mvn-libs-directory*))
153    (setf *mvn-libs-directory* (pathname *mvn-libs-directory*)))
154  (when (not (#"endsWith" (namestring *mvn-libs-directory*) "/"))
155    (setf *mvn-libs-directory*
156          (pathname (concatenate 'string *mvn-libs-directory* "/"))))
157  *mvn-libs-directory*)
158
159(defun mvn-version ()
160  "Return the version of Maven libaries in use"
161  (unless (normalize-mvn-libs)
162    (error "Need to specify a value of *mvn-libs-directory*"))
163  (let* ((pattern
164          "maven-core*.jar")
165         (maven-core-jars
166          (directory (merge-pathnames pattern
167                                      *mvn-libs-directory*)))
168         (maven-core-jar 
169          (cond
170            ((= (length maven-core-jars) 0)
171             (error "No file matching '~a' found in '~a'." pattern *mvn-libs-directory*))
172            ((> (length maven-core-jars) 1)
173             (warn "More than one file matching '~a' found in '~a'."
174                   pattern *mvn-libs-directory*)
175             (first maven-core-jars))
176            (t
177             (first maven-core-jars)))))
178    (let* ((manifest
179            (#"getManifest" (jss:new 'java.util.jar.JarFile (namestring maven-core-jar))))
180           (attributes
181            (#"getMainAttributes" manifest))
182           (version
183            (#"getValue" attributes "Implementation-Version")))
184      (if version
185          (parse-mvn-version
186           version)
187          (mvn-version-from-mvn-executable)))))
188
189;;; deprecated, unused:  we now get the version directly from the JAR manifest
190(defun mvn-version-from-mvn-executable ()
191  "Return the Maven version used by the Aether connector located by
192  FIND-MVN as a list of (MAJOR MINOR PATHLEVEL) integers.
193
194Signals a simple-error with additional information if this attempt fails."
195  (handler-case
196      (let* ((mvn
197              (truename (find-mvn)))
198             (pattern (#"compile"
199                       'regex.Pattern
200                       "^Apache Maven ([0-9]+\\.[0-9]+\\.[0-9]+)")))
201        (multiple-value-bind (output error)
202            (uiop:run-program
203             (format nil "~a --version" mvn)
204             :output :string :error :string)
205          (let ((matcher (#"matcher" pattern output)))
206            (when (#"find" matcher)
207              (return-from mvn-version-from-mvn-executable
208                (parse-mvn-version (#"group" matcher 1)))))
209          (when output
210            (signal "No parseable Maven version found in ~a" output))
211          (signal "Invocation of Maven returned the error ~{~&  ~A~}" error)))
212    (t (e) 
213      (error "Failed to determine Maven version: ~A." e))))
214
215(defun parse-mvn-version (version-string)
216  (let* ((pattern (#"compile"
217                   'regex.Pattern
218                   "([0-9]+)\\.([0-9]+)\\.([0-9]+)"))
219         (matcher (#"matcher" pattern version-string)))
220    (if (#"find" matcher)
221        (mapcar #'parse-integer 
222                `(,(#"group" matcher 1) 
223                   ,(#"group" matcher 2)
224                   ,(#"group" matcher 3)))
225        (error "Failed to parse a MAJOR.MINOR.PATCHLEVEL version from '~a'" version-string))))
226
227 
228(defun mvn-home ()
229  "If the Maven executable can be invoked, introspect the value
230  reported as Maven home."
231  (handler-case 
232      (multiple-value-bind (output error-output status)
233          (uiop:run-program
234           (format nil "~a --version" (truename (find-mvn)))
235           :output :string
236           :error-output :string)
237        (unless (zerop status)
238          (error "Failed to invoke Maven executable to introspect library locations: ~a." error-output))
239        (let ((pattern (#"compile"
240                        'regex.Pattern
241                        "Maven home: (.+)$")))
242          (with-input-from-string (s output)
243            (do ((line (read-line s nil :eof) 
244                       (read-line s nil :eof)))
245                ((or (not line) (eq line :eof)) nil)
246              (let ((matcher (#"matcher" pattern line)))
247                (when (#"find" matcher)
248                  (return-from mvn-home
249                    (uiop/pathname:ensure-directory-pathname (#"group" matcher 1)))))))))
250    (subprocess-error (e)
251          (error "Failed to invoke Maven executable to introspect library locations: ~a." e))))
252
253(defun ensure-mvn-version ()
254  "Return t if Maven version is 3.0.3 or greater."
255  (let* ((version (mvn-version))
256         (major (first version))
257         (minor (second version))
258         (patch (third version)))
259    (values
260     (or 
261      (and (>= major 3)
262           (>= minor 1))
263      (and (>= major 3)
264           (>= minor 0)
265           (>= patch 3)))
266     (list major minor patch))))
267
268(define-condition no-aether-maven-libs (error)
269  ((locations :initarg :locations
270              :initform nil
271              :reader locations))
272  (:report (lambda (condition stream)
273             (format stream "No Maven Aether libraries found locally in '~a'."
274                     (locations condition)))))
275             
276(defparameter *init-p* nil
277  "Whether we have successfully located the necessary Maven libraries")
278
279(defun init (&optional &key (force nil))
280  (restart-case
281      (if force
282          (%init :force t)
283          (%init))
284    (no-aether-maven-libs ()
285      :report "Install and use Maven libraries under local XDG hierarchy"
286      (make-local-maven))))
287
288(defun %init (&optional &key (force nil))
289  "Run the initialization strategy to bootstrap a Maven dependency node
290
291Set *MVN-LIBS-DIRECTORY* to an explicit value before running this
292function in order to bypass the dynamic introspection of the location
293of the mvn executable with an explicit value."
294  (when force
295    (setf *session* nil
296          *repository-system* nil))
297  (unless (or force *mvn-libs-directory*)
298    (setf *mvn-libs-directory* (find-mvn-libs)))
299  (unless (and *mvn-libs-directory*
300               (probe-file *mvn-libs-directory*))
301    (error (make-condition 'abcl-asdf::no-aether-maven-libs
302                           :locations (list *mvn-libs-directory*))))
303  (unless (ensure-mvn-version)
304    (error "We need maven-3.0.3 or later."))
305  (add-directory-jars-to-class-path *mvn-libs-directory* nil)
306  (setf *init-p* t))
307
308;;; The AETHER-DIRECTORY parameter is conceptually a little broken:
309;;; because we can't "unload" jar files, we can't easily switch
310;;; between Maven implementation at runtime.  Maybe this would be
311;;; possible with some sort of classloader chaining, but such effort
312;;; is not currently deemed as worthwhile.  Instead, to change Aether
313;;; libraries, you'll have to restart ABCL.
314(defmacro with-aether ((&optional aether-directory) &body body)
315  "Ensure that the code in BODY is executed with the Maven Aether libraries on the classpath"
316  (if aether-directory
317      `(let ((*mvn-libs-directory* ,aether-directory))
318         (init :force t)
319         ,@body)
320      `(progn (unless *init-p*
321                (init))
322              ,@body)))
323
324(defun find-http-wagon ()
325  "Find an implementation of the object that provides access to http and https resources.
326
327Supposedly configurable with the java.net.protocols (c.f. reference
328maso2000 in the Manual.)"
329  (handler-case 
330      ;; maven-3.0.4
331      (java:jnew "org.apache.maven.wagon.providers.http.HttpWagon") 
332    (error () 
333      ;; maven-3.0.3 reported as not working with all needed functionality
334      (java:jnew  "org.apache.maven.wagon.providers.http.LightweightHttpWagon"))))
335
336(defun make-wagon-provider ()
337  "Returns an implementation of the org.sonatype.aether.connector.wagon.WagonProvider contract
338
339The implementation is specified as Lisp closures.  Currently, it only
340specializes the lookup() method if passed an 'http' or an 'https' role
341hint."
342  (unless *init-p* (init))
343  (java:jinterface-implementation 
344   (#"getName" 
345    (or
346     (ignore-errors  ;; Maven 3.2.5+
347       (jss:find-java-class 'aether.transport.wagon.WagonProvider))
348     (ignore-errors  ;; Maven 3.1.0+
349       (jss:find-java-class 'aether.connector.wagon.WagonProvider))
350     (ignore-errors  ;; Maven 3.0.x
351       (jss:find-java-class 'org.sonatype.aether.connector.wagon.WagonProvider))))
352   "lookup"
353   (lambda (role-hint)
354     (cond 
355       ((find role-hint '("http" "https") :test #'string-equal)
356        (find-http-wagon))
357       (t
358        (progn 
359          (format cl:*load-verbose*
360                  "~&; abcl-asdf; WagonProvider stub passed '~A' as a hint it couldn't satisfy.~%"
361                  role-hint)
362          java:+null+))))
363   "release"
364   (lambda (wagon)
365     (declare (ignore wagon)))))
366
367(defun find-service-locator ()
368  (or
369   (ignore-errors
370     ;; maven-3.0.4
371     (jss:new "org.apache.maven.repository.internal.MavenServiceLocator")) 
372   (ignore-errors
373     ;; maven-3.1.0 using org.eclipse.aether...
374     (jss:new "aether.impl.DefaultServiceLocator"))
375   (ignore-errors
376     (jss:new "org.apache.maven.repository.internal.DefaultServiceLocator"))
377   (ignore-errors
378     ;; maven-3.1.0
379     (#"newServiceLocator" 'org.apache.maven.repository.internal.MavenRepositorySystemUtils))))
380
381
382(defun make-repository-system ()
383  (unless *init-p* (init))
384  (let ((locator 
385         (find-service-locator))
386        (wagon-provider-class 
387         (or
388          (ignore-errors 
389            (java:jclass "org.sonatype.aether.connector.wagon.WagonProvider"))
390          (ignore-errors ;; Maven-3.3.x
391            (jss:find-java-class 'connector.transport.TransporterFactory))
392          (ignore-errors ;; Maven-3.2.5
393            (jss:find-java-class 'org.eclipse.aether.transport.wagon.WagonProvider))
394          (ignore-errors  ;; Maven-3.1.x
395            (jss:find-java-class 'aether.connector.wagon.WagonProvider))))
396        (wagon-repository-connector-factory-class
397         (or
398          (ignore-errors 
399            (jss:find-java-class 'org.sonatype.aether.connector.wagon.WagonRepositoryConnectorFactory))
400          (ignore-errors 
401            (jss:find-java-class 'org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory))
402          (ignore-errors 
403            (java:jclass "org.sonatype.aether.connector.wagon.WagonRepositoryConnectorFactory"))))
404        (repository-connector-factory-class 
405         (or
406          (ignore-errors
407            (jss:find-java-class 'aether.spi.connector.RepositoryConnectorFactory))
408          (ignore-errors
409            (jss:find-java-class 'org.eclipse.aether.spi.connector.RepositoryConnectorFactory))
410          (ignore-errors 
411            (java:jclass "org.sonatype.aether.spi.connector.RepositoryConnectorFactory"))))
412        (repository-system-class
413         (or
414          (ignore-errors
415            (java:jclass "org.sonatype.aether.RepositorySystem"))
416          (ignore-errors 
417            (jss:find-java-class 'org.eclipse.aether.RepositorySystem))
418          (ignore-errors 
419            (jss:find-java-class 'aether.RepositorySystem)))))
420    (if (equal wagon-provider-class (ignore-errors (jss:find-java-class 'TransporterFactory)))
421        ;;; Maven-3.3.3
422        (let ((wagon-transporter-factory (jss:new 'WagonTransporterFactory)))
423          (#"setWagonProvider" wagon-transporter-factory (make-wagon-provider))
424          (#"setServices" locator
425                          wagon-provider-class
426                          (java:jarray-from-list (list wagon-transporter-factory))))
427        (#"setServices" locator
428                        wagon-provider-class
429                        (java:jarray-from-list
430                         (list (make-wagon-provider)))))
431    (#"addService" locator
432                   repository-connector-factory-class
433                   wagon-repository-connector-factory-class)
434    (values (#"getService" locator
435                           repository-system-class)
436            locator)))
437
438(defun make-session (repository-system)
439  "Construct a new aether.RepositorySystemSession from the specified REPOSITORY-SYSTEM."
440  (with-aether ()
441    (let ((session
442           (or 
443            (ignore-errors
444              (java:jnew
445               (jss:find-java-class "MavenRepositorySystemSession")))
446            (ignore-errors
447              (#"newSession"
448               'org.apache.maven.repository.internal.MavenRepositorySystemUtils))))
449          (local-repository
450           (make-local-repository)))
451      (#"setLocalRepositoryManager"
452       session
453       (make-local-repository-manager repository-system local-repository session)))))
454
455
456(defun make-local-repository-manager (repository-system local-repository session)
457  (or 
458   (ignore-errors 
459     (#"newLocalRepositoryManager" 
460      repository-system local-repository))
461   (ignore-errors      ;; maven-3.1.0
462     (#"newLocalRepositoryManager" 
463      repository-system session local-repository))))
464
465(defun make-local-repository ()
466  (java:jnew
467   (or
468    (ignore-errors
469      (jss:find-java-class "org.sonatype.aether.repository.LocalRepository"))
470    (ignore-errors
471      (jss:find-java-class "org.eclipse.aether.repository.LocalRepository")))
472   (namestring (merge-pathnames ".m2/repository/"
473                                (user-homedir-pathname)))))
474
475(defparameter *maven-http-proxy* nil
476  "A string containing the URI of an http proxy for Maven to use.")
477
478(defun make-proxy ()
479  "Return an aether.repository.Proxy instance initialized from *MAVEN-HTTP-PROXY*."
480  (unless *maven-http-proxy*
481    (warn "No proxy specified in *MAVEN-HTTP-PROXY*")
482    (return-from make-proxy nil))
483  (let* ((p (pathname *maven-http-proxy*))
484         (scheme (ext:url-pathname-scheme p))
485         (authority (ext:url-pathname-authority p))
486         (host (if (search ":" authority)
487                   (subseq authority 0 (search ":" authority))
488                   authority))
489         (port (when (search ":" authority)
490                 (parse-integer (subseq authority (1+ (search ":" authority))))))
491         ;; TODO allow specification of authentication
492         (authentication java:+null+))
493    (or 
494     (ignore-errors
495       (jss:new 'org.eclipse.aether.repository.Proxy
496                scheme host port authentication))
497     (ignore-errors
498       (jss:new 'org.sonatype.aether.repository.Proxy
499                scheme host port authentication)))))
500
501(defparameter *repository-system*  nil
502  "The aether.RepositorySystem used by the Maeven Aether connector.")
503(defun ensure-repository-system (&key (force nil))
504  (when (or force (not *repository-system*))
505    (setf *repository-system* (make-repository-system)))
506  *repository-system*)
507
508(defparameter *session* nil
509  "Reference to the Maven RepositorySystemSession")
510(defun ensure-session (&key (force nil))
511  "Ensure that the RepositorySystemSession has been created.
512
513If *MAVEN-HTTP-PROXY* is non-nil, parse its value as the http proxy."
514  (when (or force (not *session*))
515    (ensure-repository-system :force force)
516    (setf *session* (make-session *repository-system*))
517    (#"setRepositoryListener" *session* (make-repository-listener))
518    (when *maven-http-proxy*
519      (let ((proxy (make-proxy)))
520        (#"add" (#"getProxySelector" *session*)
521                proxy 
522                ;; A string specifying non proxy hosts, or null
523                java:+null+))))
524  *session*)
525
526(defun make-artifact (artifact-string)
527  "Return an instance of aether.artifact.DefaultArtifact initialized from ARTIFACT-STRING" 
528  (or
529   (ignore-errors
530     (jss:new "org.sonatype.aether.util.artifact.DefaultArtifact" artifact-string))
531   (ignore-errors
532     (jss:new 'aether.artifact.DefaultArtifact artifact-string))))
533
534(defun make-artifact-request () 
535  "Construct a new aether.resolution.ArtifactRequest."
536  (or 
537   (ignore-errors
538     (java:jnew (jss:find-java-class 'aether.resolution.ArtifactRequest)))
539   (ignore-errors
540     (java:jnew "org.sonatype.aether.resolution.ArtifactRequest"))))
541
542;;; TODO change this to work on artifact strings like log4j:log4j:jar:1.2.16
543(defun resolve-artifact (group-id artifact-id &key (version "LATEST" versionp))
544  "Resolve artifact to location on the local filesystem.
545
546Declared dependencies are not attempted to be located.
547
548If unspecified, the string \"LATEST\" will be used for the VERSION.
549
550Returns the Maven specific string for the artifact "
551  (unless versionp
552    (warn "Using LATEST for unspecified version."))
553  (unless *init-p* (init))
554  (let* ((artifact-string 
555          (format nil "~A:~A:~A" group-id artifact-id version))
556         (artifact 
557          (make-artifact artifact-string))
558         (artifact-request 
559          (make-artifact-request)))
560    (#"setArtifact" artifact-request artifact)
561    (#"addRepository" artifact-request (ensure-remote-repository))
562    (#"toString" (#"getFile" 
563                  (#"getArtifact" (#"resolveArtifact" (ensure-repository-system) 
564                                                      (ensure-session) artifact-request))))))
565
566(defun make-remote-repository (id type url) 
567  (or
568   (ignore-errors
569     (jss:new 'org.sonatype.aether.repository.RemoteRepository id type url))
570   (ignore-errors 
571     (#"build" (jss:new "org.eclipse.aether.repository.RemoteRepository$Builder" id type url)))))
572
573(defvar *default-repository* 
574  "https://repo1.maven.org/maven2/"
575  "URI of default remote Maven repository")
576
577(defun add-repository (repository)
578  (ensure-remote-repository :repository repository))
579
580(defparameter *maven-remote-repository*  nil
581  "Reference to remote repository used by the Maven Aether
582  embedder.")
583
584(defun ensure-remote-repository (&key 
585                                   (force nil)
586                                   (repository *default-repository* repository-p))
587  (unless *init-p* (init))
588  (when (or force 
589            repository-p 
590            (not *maven-remote-repository*))
591    (let ((r (make-remote-repository "central" "default" repository)))
592      (when *maven-http-proxy*
593        (#"setProxy" r (make-proxy)))
594      (setf *maven-remote-repository* r)))
595  *maven-remote-repository*)
596
597(defun resolve-dependencies (group-id artifact-id 
598                             &key
599                               (version "LATEST" versionp)
600                               (repository *maven-remote-repository* repository-p)
601                               (repositories NIL repositories-p))
602  "Dynamically resolve Maven dependencies for item with GROUP-ID and ARTIFACT-ID
603optionally with a VERSION and a REPOSITORY. 
604
605All recursive dependencies will be visited before resolution is successful.
606
607If unspecified, the string \"LATEST\" will be used for the VERSION.
608
609Returns a string containing the necessary jvm classpath entries packed
610in Java CLASSPATH representation."
611  (unless *init-p* (init))
612  (unless versionp
613    (warn "Using LATEST for unspecified version."))
614  (let* ((coords 
615          (format nil "~A:~A:~A" group-id artifact-id (if versionp version "LATEST")))
616         (artifact 
617          (make-artifact coords))
618         (dependency
619          (make-dependency artifact))
620         (collect-request
621          (or
622           (ignore-errors
623             (java:jnew (jss:find-java-class "org.sonatype.aether.collection.CollectRequest")))
624           (ignore-errors
625             (java:jnew (jss:find-java-class "org.eclipse.aether.collection.CollectRequest"))))))
626    (#"setRoot" collect-request dependency)
627    (setf repositories-p (or repository-p repositories-p))
628    ;; Don't call addRepository if we explicitly specify a NIL repository
629    (cond
630      ((not repositories-p)
631       (#"addRepository" collect-request (ensure-remote-repository)))
632      (repository
633       (if (stringp repository)
634           (push repository repositories)
635           (#"addRepository" collect-request repository))))
636    (dolist (repository repositories)
637      (#"addRepository" collect-request
638                        (let ((r (make-remote-repository "central" "default" repository)))
639                          (when *maven-http-proxy*
640                            (#"setProxy" r (make-proxy)))
641                          r)))
642    (let* ((collect-result (#"collectDependencies" (ensure-repository-system)
643                                                   (ensure-session) collect-request))
644           (node 
645            (#"getRoot" collect-result))
646           (dependency-request
647            (or
648             (ignore-errors
649            ;;; pre Maven-3.3.x
650               (java:jnew (jss:find-java-class "DependencyRequest")
651                          node java:+null+))
652             (ignore-errors
653               (jss:new 'DependencyRequest))))
654           (nlg 
655            (java:jnew (jss:find-java-class "PreorderNodeListGenerator"))))
656      (#"setRoot" dependency-request node)
657      (#"resolveDependencies" (ensure-repository-system) (ensure-session) dependency-request)
658      (#"accept" node nlg)
659      (#"getClassPath" nlg))))
660
661(defun make-dependency (artifact)
662  (or
663   (ignore-errors
664     (java:jnew (jss:find-java-class 'org.sonatype.aether.graph.Dependency)
665                artifact
666                (java:jfield
667                 (jss:find-java-class "org.sonatype.aether.util.artifact.JavaScopes")
668                 "COMPILE")))
669   (ignore-errors
670     (java:jnew (jss:find-java-class 'org.eclipse.aether.graph.Dependency)
671                artifact
672                (java:jfield
673                 (jss:find-java-class "org.eclipse.aether.util.artifact.JavaScopes")
674                 "COMPILE")))))
675
676
677(defun make-repository-listener ()
678  (flet ((log (e) 
679           (format cl:*load-verbose* "~&; abcl-asdf; ~A~%" (#"toString" e))))
680    (java:jinterface-implementation 
681     (#"getName" (jss:find-java-class 'aether.RepositoryListener))
682     "artifactDeployed" 
683     #'log
684     "artifactDeploying" 
685     #'log
686     "artifactDescriptorInvalid" 
687     #'log
688     "artifactDescriptorMissing" 
689     #'log
690     "artifactDownloaded" 
691     #'log
692     "artifactDownloading" 
693     #'log
694     "artifactInstalled" 
695     #'log
696     "artifactInstalling" 
697     #'log
698     "artifactResolved" 
699     #'log
700     "artifactResolving" 
701     #'log
702     "metadataDeployed" 
703     #'log
704     "metadataDeploying" 
705     #'log
706     "metadataDownloaded" 
707     #'log
708     "metadataDownloading" 
709     #'log
710     "metadataInstalled"
711     #'log
712     "metadataInstalling" 
713     #'log
714     "metadataInvalid" 
715     #'log
716     "metadataResolved" 
717     #'log
718     "metadataResolving"
719     #'log)))
720
721(defmethod resolve ((string string))
722  "Resolve a colon separated GROUP-ID:ARTIFACT-ID[:VERSION] reference to a Maven artifact.
723
724Examples of artifact references: \"log4j:log4j:1.2.14\" for
725'log4j-1.2.14.jar'.  Resolving \"log4j:log4j\" would return the latest
726version of the artifact known to the distributed Maven pom.xml graph.
727
728Returns a string containing the necessary classpath entries for this
729artifact and all of its transitive dependencies."
730  (let ((result (split-string string ":")))
731    (cond 
732      ((= (length result) 3)
733       (resolve-dependencies 
734        (first result) (second result) :version (third result)))
735      ((string= string "com.sun.jna:jna")
736       (warn "Replacing request for no longer available com.sun.jna:jna with net.java.dev.jna:jna")
737       (resolve-dependencies "net.java.dev.jna" "jna" :version "LATEST"))
738      ((= (length result) 2)
739       (resolve-dependencies
740        (first result) (second result)))
741      (t 
742       (destructuring-bind (group-id artifact-id &optional version repository)
743           (abcl-build:split-string string "/")
744         (setf result 
745               (apply #'resolve-dependencies group-id artifact-id
746                      (append (when version
747                                `(:version ,version))
748                              (when repository
749                                `(:repository ,repository))))))))))
750
751(defun make-local-maven ()
752  "Use ABCL-BUILD to install a local Maven, configuring it for subsequent use"
753  ;; TODO: use this in an interactive restart if Maven can't be found
754    (setf abcl-asdf:*mvn-libs-directory*
755     (multiple-value-bind (mvn entries)
756         (abcl-build:mvn/install)
757       (let* ((root
758                (first (last entries)))
759              (lib
760                (merge-pathnames  "./lib/" root )))
761         (abcl-asdf:with-aether (lib)
762           (values
763            (and
764             ;; for good measure
765             (when (asdf:clear-system :jna)
766               (asdf:make :jna))
767             lib)
768            (abcl-asdf:ensure-mvn-version)
769            (abcl-build:ensure-maven)))))))
770
771;;; Currently the last file listed in ASDF
772(provide 'abcl-asdf)
773
774
Note: See TracBrowser for help on using the repository browser.