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 (parse-mvn-version 185 version)))) 186 187;;; deprecated, unused: we now get the version directly from the JAR manifest 188(defun mvn-version-from-mvn-executable () 189 "Return the Maven version used by the Aether connector located by 190 FIND-MVN as a list of (MAJOR MINOR PATHLEVEL) integers. 191 192Signals a simple-error with additional information if this attempt fails." 193 (handler-case 194 (let* ((mvn 195 (truename (find-mvn))) 196 (pattern (#"compile" 197 'regex.Pattern 198 "^Apache Maven ([0-9]+\\.[0-9]+\\.[0-9]+)"))) 199 (multiple-value-bind (output error) 200 (uiop:run-program 201 (format nil "~a --version" mvn) 202 :output :string :error :string) 203 (let ((matcher (#"matcher" pattern output))) 204 (when (#"find" matcher) 205 (return-from mvn-version-from-mvn-executable 206 (parse-mvn-version (#"group" matcher 1))))) 207 (when output 208 (signal "No parseable Maven version found in ~a" output)) 209 (signal "Invocation of Maven returned the error ~{~& ~A~}" error))) 210 (t (e) 211 (error "Failed to determine Maven version: ~A." e)))) 212 213(defun parse-mvn-version (version-string) 214 (let* ((pattern (#"compile" 215 'regex.Pattern 216 "([0-9]+)\\.([0-9]+)\\.([0-9]+)")) 217 (matcher (#"matcher" pattern version-string))) 218 (if (#"find" matcher) 219 (mapcar #'parse-integer 220 `(,(#"group" matcher 1) 221 ,(#"group" matcher 2) 222 ,(#"group" matcher 3))) 223 (error "Failed to parse a MAJOR.MINOR.PATCHLEVEL version from '~a'" version-string)))) 224 225 226(defun mvn-home () 227 "If the Maven executable can be invoked, introspect the value 228 reported as Maven home." 229 (handler-case 230 (multiple-value-bind (output error-output status) 231 (uiop:run-program 232 (format nil "~a --version" (truename (find-mvn))) 233 :output :string 234 :error-output :string) 235 (unless (zerop status) 236 (error "Failed to invoke Maven executable to introspect library locations: ~a." error-output)) 237 (let ((pattern (#"compile" 238 'regex.Pattern 239 "Maven home: (.+)$"))) 240 (with-input-from-string (s output) 241 (do ((line (read-line s nil :eof) 242 (read-line s nil :eof))) 243 ((or (not line) (eq line :eof)) nil) 244 (let ((matcher (#"matcher" pattern line))) 245 (when (#"find" matcher) 246 (return-from mvn-home 247 (uiop/pathname:ensure-directory-pathname (#"group" matcher 1))))))))) 248 (subprocess-error (e) 249 (error "Failed to invoke Maven executable to introspect library locations: ~a." e)))) 250 251(defun ensure-mvn-version () 252 "Return t if Maven version is 3.0.3 or greater." 253 (let* ((version (mvn-version)) 254 (major (first version)) 255 (minor (second version)) 256 (patch (third version))) 257 (values 258 (or 259 (and (>= major 3) 260 (>= minor 1)) 261 (and (>= major 3) 262 (>= minor 0) 263 (>= patch 3))) 264 (list major minor patch)))) 265 266(define-condition no-aether-maven-libs (error) 267 ((locations :initarg :locations 268 :initform nil 269 :reader locations)) 270 (:report (lambda (condition stream) 271 (format stream "No Maven Aether libraries found locally in '~a'." 272 (locations condition))))) 273 274(defparameter *init-p* nil 275 "Whether we have successfully located the necessary Maven libraries") 276 277(defun init (&optional &key (force nil)) 278 "Run the initialization strategy to bootstrap a Maven dependency node 279 280Set *MVN-LIBS-DIRECTORY* to an explicit value before running this 281function in order to bypass the dynamic introspection of the location 282of the mvn executable with an explicit value." 283 (when force 284 (setf *session* nil 285 *repository-system* nil)) 286 (unless (or force *mvn-libs-directory*) 287 (setf *mvn-libs-directory* (find-mvn-libs))) 288 (unless (and *mvn-libs-directory* 289 (probe-file *mvn-libs-directory*)) 290 ;; FIXME Remove warning; put message in restart 291 (warn "Please obtain and install maven-3.0.3 or later locally from <http://maven.apache.org/download.html>, then set ABCL-ASDF:*MVN-LIBS-DIRECTORY* to the directory containing maven-core-3.*.jar et. al.") 292 (error (make-condition 'abcl-asdf::no-aether-maven-libs 293 :locations (list *mvn-libs-directory*)))) 294 (unless (ensure-mvn-version) 295 (error "We need maven-3.0.3 or later.")) 296 (add-directory-jars-to-class-path *mvn-libs-directory* nil) 297 (setf *init-p* t)) 298 299;;; The AETHER-DIRECTORY parameter is conceptually a little broken: 300;;; because we can't "unload" jar files, we can't easily switch 301;;; between Maven implementation at runtime. Maybe this would be 302;;; possible with some sort of classloader chaining, but such effort 303;;; is not currently deemed as worthwhile. Instead, to change Aether 304;;; libraries, you'll have to restart ABCL. 305(defmacro with-aether ((&optional aether-directory) &body body) 306 "Ensure that the code in BODY is executed with the Maven Aether libraries on the classpath" 307 (if aether-directory 308 `(let ((*mvn-libs-directory* ,aether-directory)) 309 (init :force t) 310 ,@body) 311 `(progn (unless *init-p* 312 (init)) 313 ,@body))) 314 315(defun find-http-wagon () 316 "Find an implementation of the object that provides access to http and https resources. 317 318Supposedly configurable with the java.net.protocols (c.f. reference 319maso2000 in the Manual.)" 320 (handler-case 321 ;; maven-3.0.4 322 (java:jnew "org.apache.maven.wagon.providers.http.HttpWagon") 323 (error () 324 ;; maven-3.0.3 reported as not working with all needed functionality 325 (java:jnew "org.apache.maven.wagon.providers.http.LightweightHttpWagon")))) 326 327(defun make-wagon-provider () 328 "Returns an implementation of the org.sonatype.aether.connector.wagon.WagonProvider contract 329 330The implementation is specified as Lisp closures. Currently, it only 331specializes the lookup() method if passed an 'http' or an 'https' role 332hint." 333 (unless *init-p* (init)) 334 (java:jinterface-implementation 335 (#"getName" 336 (or 337 (ignore-errors ;; Maven 3.2.5+ 338 (jss:find-java-class 'aether.transport.wagon.WagonProvider)) 339 (ignore-errors ;; Maven 3.1.0+ 340 (jss:find-java-class 'aether.connector.wagon.WagonProvider)) 341 (ignore-errors ;; Maven 3.0.x 342 (jss:find-java-class 'org.sonatype.aether.connector.wagon.WagonProvider)))) 343 "lookup" 344 (lambda (role-hint) 345 (cond 346 ((find role-hint '("http" "https") :test #'string-equal) 347 (find-http-wagon)) 348 (t 349 (progn 350 (format cl:*load-verbose* 351 "~&; abcl-asdf; WagonProvider stub passed '~A' as a hint it couldn't satisfy.~%" 352 role-hint) 353 java:+null+)))) 354 "release" 355 (lambda (wagon) 356 (declare (ignore wagon))))) 357 358(defun find-service-locator () 359 (or 360 (ignore-errors 361 ;; maven-3.0.4 362 (jss:new "org.apache.maven.repository.internal.MavenServiceLocator")) 363 (ignore-errors 364 ;; maven-3.1.0 using org.eclipse.aether... 365 (jss:new "aether.impl.DefaultServiceLocator")) 366 (ignore-errors 367 (jss:new "org.apache.maven.repository.internal.DefaultServiceLocator")) 368 (ignore-errors 369 ;; maven-3.1.0 370 (#"newServiceLocator" 'org.apache.maven.repository.internal.MavenRepositorySystemUtils)))) 371 372 373(defun make-repository-system () 374 (unless *init-p* (init)) 375 (let ((locator 376 (find-service-locator)) 377 (wagon-provider-class 378 (or 379 (ignore-errors 380 (java:jclass "org.sonatype.aether.connector.wagon.WagonProvider")) 381 (ignore-errors ;; Maven-3.3.x 382 (jss:find-java-class 'connector.transport.TransporterFactory)) 383 (ignore-errors ;; Maven-3.2.5 384 (jss:find-java-class 'org.eclipse.aether.transport.wagon.WagonProvider)) 385 (ignore-errors ;; Maven-3.1.x 386 (jss:find-java-class 'aether.connector.wagon.WagonProvider)))) 387 (wagon-repository-connector-factory-class 388 (or 389 (ignore-errors 390 (jss:find-java-class 'org.sonatype.aether.connector.wagon.WagonRepositoryConnectorFactory)) 391 (ignore-errors 392 (jss:find-java-class 'org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory)) 393 (ignore-errors 394 (java:jclass "org.sonatype.aether.connector.wagon.WagonRepositoryConnectorFactory")))) 395 (repository-connector-factory-class 396 (or 397 (ignore-errors 398 (jss:find-java-class 'aether.spi.connector.RepositoryConnectorFactory)) 399 (ignore-errors 400 (jss:find-java-class 'org.eclipse.aether.spi.connector.RepositoryConnectorFactory)) 401 (ignore-errors 402 (java:jclass "org.sonatype.aether.spi.connector.RepositoryConnectorFactory")))) 403 (repository-system-class 404 (or 405 (ignore-errors 406 (java:jclass "org.sonatype.aether.RepositorySystem")) 407 (ignore-errors 408 (jss:find-java-class 'org.eclipse.aether.RepositorySystem)) 409 (ignore-errors 410 (jss:find-java-class 'aether.RepositorySystem))))) 411 (if (equal wagon-provider-class (ignore-errors (jss:find-java-class 'TransporterFactory))) 412 ;;; Maven-3.3.3 413 (let ((wagon-transporter-factory (jss:new 'WagonTransporterFactory))) 414 (#"setWagonProvider" wagon-transporter-factory (make-wagon-provider)) 415 (#"setServices" locator 416 wagon-provider-class 417 (java:jarray-from-list (list wagon-transporter-factory)))) 418 (#"setServices" locator 419 wagon-provider-class 420 (java:jarray-from-list 421 (list (make-wagon-provider))))) 422 (#"addService" locator 423 repository-connector-factory-class 424 wagon-repository-connector-factory-class) 425 (values (#"getService" locator 426 repository-system-class) 427 locator))) 428 429(defun make-session (repository-system) 430 "Construct a new aether.RepositorySystemSession from the specified REPOSITORY-SYSTEM." 431 (with-aether () 432 (let ((session 433 (or 434 (ignore-errors 435 (java:jnew 436 (jss:find-java-class "MavenRepositorySystemSession"))) 437 (ignore-errors 438 (#"newSession" 439 'org.apache.maven.repository.internal.MavenRepositorySystemUtils)))) 440 (local-repository 441 (make-local-repository))) 442 (#"setLocalRepositoryManager" 443 session 444 (make-local-repository-manager repository-system local-repository session))))) 445 446 447(defun make-local-repository-manager (repository-system local-repository session) 448 (or 449 (ignore-errors 450 (#"newLocalRepositoryManager" 451 repository-system local-repository)) 452 (ignore-errors ;; maven-3.1.0 453 (#"newLocalRepositoryManager" 454 repository-system session local-repository)))) 455 456(defun make-local-repository () 457 (java:jnew 458 (or 459 (ignore-errors 460 (jss:find-java-class "org.sonatype.aether.repository.LocalRepository")) 461 (ignore-errors 462 (jss:find-java-class "org.eclipse.aether.repository.LocalRepository"))) 463 (namestring (merge-pathnames ".m2/repository/" 464 (user-homedir-pathname))))) 465 466(defparameter *maven-http-proxy* nil 467 "A string containing the URI of an http proxy for Maven to use.") 468 469(defun make-proxy () 470 "Return an aether.repository.Proxy instance initialized from *MAVEN-HTTP-PROXY*." 471 (unless *maven-http-proxy* 472 (warn "No proxy specified in *MAVEN-HTTP-PROXY*") 473 (return-from make-proxy nil)) 474 (let* ((p (pathname *maven-http-proxy*)) 475 (scheme (ext:url-pathname-scheme p)) 476 (authority (ext:url-pathname-authority p)) 477 (host (if (search ":" authority) 478 (subseq authority 0 (search ":" authority)) 479 authority)) 480 (port (when (search ":" authority) 481 (parse-integer (subseq authority (1+ (search ":" authority)))))) 482 ;; TODO allow specification of authentication 483 (authentication java:+null+)) 484 (or 485 (ignore-errors 486 (jss:new 'org.eclipse.aether.repository.Proxy 487 scheme host port authentication)) 488 (ignore-errors 489 (jss:new 'org.sonatype.aether.repository.Proxy 490 scheme host port authentication))))) 491 492(defparameter *repository-system* nil 493 "The aether.RepositorySystem used by the Maeven Aether connector.") 494(defun ensure-repository-system (&key (force nil)) 495 (when (or force (not *repository-system*)) 496 (setf *repository-system* (make-repository-system))) 497 *repository-system*) 498 499(defparameter *session* nil 500 "Reference to the Maven RepositorySystemSession") 501(defun ensure-session (&key (force nil)) 502 "Ensure that the RepositorySystemSession has been created. 503 504If *MAVEN-HTTP-PROXY* is non-nil, parse its value as the http proxy." 505 (when (or force (not *session*)) 506 (ensure-repository-system :force force) 507 (setf *session* (make-session *repository-system*)) 508 (#"setRepositoryListener" *session* (make-repository-listener)) 509 (when *maven-http-proxy* 510 (let ((proxy (make-proxy))) 511 (#"add" (#"getProxySelector" *session*) 512 proxy 513 ;; A string specifying non proxy hosts, or null 514 java:+null+)))) 515 *session*) 516 517(defun make-artifact (artifact-string) 518 "Return an instance of aether.artifact.DefaultArtifact initialized from ARTIFACT-STRING" 519 (or 520 (ignore-errors 521 (jss:new "org.sonatype.aether.util.artifact.DefaultArtifact" artifact-string)) 522 (ignore-errors 523 (jss:new 'aether.artifact.DefaultArtifact artifact-string)))) 524 525(defun make-artifact-request () 526 "Construct a new aether.resolution.ArtifactRequest." 527 (or 528 (ignore-errors 529 (java:jnew (jss:find-java-class 'aether.resolution.ArtifactRequest))) 530 (ignore-errors 531 (java:jnew "org.sonatype.aether.resolution.ArtifactRequest")))) 532 533;;; TODO change this to work on artifact strings like log4j:log4j:jar:1.2.16 534(defun resolve-artifact (group-id artifact-id &key (version "LATEST" versionp)) 535 "Resolve artifact to location on the local filesystem. 536 537Declared dependencies are not attempted to be located. 538 539If unspecified, the string \"LATEST\" will be used for the VERSION. 540 541Returns the Maven specific string for the artifact " 542 (unless versionp 543 (warn "Using LATEST for unspecified version.")) 544 (unless *init-p* (init)) 545 (let* ((artifact-string 546 (format nil "~A:~A:~A" group-id artifact-id version)) 547 (artifact 548 (make-artifact artifact-string)) 549 (artifact-request 550 (make-artifact-request))) 551 (#"setArtifact" artifact-request artifact) 552 (#"addRepository" artifact-request (ensure-remote-repository)) 553 (#"toString" (#"getFile" 554 (#"getArtifact" (#"resolveArtifact" (ensure-repository-system) 555 (ensure-session) artifact-request)))))) 556 557(defun make-remote-repository (id type url) 558 (or 559 (ignore-errors 560 (jss:new 'org.sonatype.aether.repository.RemoteRepository id type url)) 561 (ignore-errors 562 (#"build" (jss:new "org.eclipse.aether.repository.RemoteRepository$Builder" id type url))))) 563 564(defvar *default-repository* 565 "https://repo1.maven.org/maven2/" 566 "URI of default remote Maven repository") 567 568(defun add-repository (repository) 569 (ensure-remote-repository :repository repository)) 570 571(defparameter *maven-remote-repository* nil 572 "Reference to remote repository used by the Maven Aether 573 embedder.") 574 575(defun ensure-remote-repository (&key 576 (force nil) 577 (repository *default-repository* repository-p)) 578 (unless *init-p* (init)) 579 (when (or force 580 repository-p 581 (not *maven-remote-repository*)) 582 (let ((r (make-remote-repository "central" "default" repository))) 583 (when *maven-http-proxy* 584 (#"setProxy" r (make-proxy))) 585 (setf *maven-remote-repository* r))) 586 *maven-remote-repository*) 587 588(defun resolve-dependencies (group-id artifact-id 589 &key 590 (version "LATEST" versionp) 591 (repository *maven-remote-repository* repository-p) 592 (repositories NIL repositories-p)) 593 "Dynamically resolve Maven dependencies for item with GROUP-ID and ARTIFACT-ID 594optionally with a VERSION and a REPOSITORY. 595 596All recursive dependencies will be visited before resolution is successful. 597 598If unspecified, the string \"LATEST\" will be used for the VERSION. 599 600Returns a string containing the necessary jvm classpath entries packed 601in Java CLASSPATH representation." 602 (unless *init-p* (init)) 603 (unless versionp 604 (warn "Using LATEST for unspecified version.")) 605 (let* ((coords 606 (format nil "~A:~A:~A" group-id artifact-id (if versionp version "LATEST"))) 607 (artifact 608 (make-artifact coords)) 609 (dependency 610 (make-dependency artifact)) 611 (collect-request 612 (or 613 (ignore-errors 614 (java:jnew (jss:find-java-class "org.sonatype.aether.collection.CollectRequest"))) 615 (ignore-errors 616 (java:jnew (jss:find-java-class "org.eclipse.aether.collection.CollectRequest")))))) 617 (#"setRoot" collect-request dependency) 618 (setf repositories-p (or repository-p repositories-p)) 619 ;; Don't call addRepository if we explicitly specify a NIL repository 620 (cond 621 ((not repositories-p) 622 (#"addRepository" collect-request (ensure-remote-repository))) 623 (repository 624 (if (stringp repository) 625 (push repository repositories) 626 (#"addRepository" collect-request repository)))) 627 (dolist (repository repositories) 628 (#"addRepository" collect-request 629 (let ((r (make-remote-repository "central" "default" repository))) 630 (when *maven-http-proxy* 631 (#"setProxy" r (make-proxy))) 632 r))) 633 (let* ((collect-result (#"collectDependencies" (ensure-repository-system) 634 (ensure-session) collect-request)) 635 (node 636 (#"getRoot" collect-result)) 637 (dependency-request 638 (or 639 (ignore-errors 640 ;;; pre Maven-3.3.x 641 (java:jnew (jss:find-java-class "DependencyRequest") 642 node java:+null+)) 643 (ignore-errors 644 (jss:new 'DependencyRequest)))) 645 (nlg 646 (java:jnew (jss:find-java-class "PreorderNodeListGenerator")))) 647 (#"setRoot" dependency-request node) 648 (#"resolveDependencies" (ensure-repository-system) (ensure-session) dependency-request) 649 (#"accept" node nlg) 650 (#"getClassPath" nlg)))) 651 652(defun make-dependency (artifact) 653 (or 654 (ignore-errors 655 (java:jnew (jss:find-java-class 'org.sonatype.aether.graph.Dependency) 656 artifact 657 (java:jfield 658 (jss:find-java-class "org.sonatype.aether.util.artifact.JavaScopes") 659 "COMPILE"))) 660 (ignore-errors 661 (java:jnew (jss:find-java-class 'org.eclipse.aether.graph.Dependency) 662 artifact 663 (java:jfield 664 (jss:find-java-class "org.eclipse.aether.util.artifact.JavaScopes") 665 "COMPILE"))))) 666 667 668(defun make-repository-listener () 669 (flet ((log (e) 670 (format cl:*load-verbose* "~&; abcl-asdf; ~A~%" (#"toString" e)))) 671 (java:jinterface-implementation 672 (#"getName" (jss:find-java-class 'aether.RepositoryListener)) 673 "artifactDeployed" 674 #'log 675 "artifactDeploying" 676 #'log 677 "artifactDescriptorInvalid" 678 #'log 679 "artifactDescriptorMissing" 680 #'log 681 "artifactDownloaded" 682 #'log 683 "artifactDownloading" 684 #'log 685 "artifactInstalled" 686 #'log 687 "artifactInstalling" 688 #'log 689 "artifactResolved" 690 #'log 691 "artifactResolving" 692 #'log 693 "metadataDeployed" 694 #'log 695 "metadataDeploying" 696 #'log 697 "metadataDownloaded" 698 #'log 699 "metadataDownloading" 700 #'log 701 "metadataInstalled" 702 #'log 703 "metadataInstalling" 704 #'log 705 "metadataInvalid" 706 #'log 707 "metadataResolved" 708 #'log 709 "metadataResolving" 710 #'log))) 711 712(defmethod resolve ((string string)) 713 "Resolve a colon separated GROUP-ID:ARTIFACT-ID[:VERSION] reference to a Maven artifact. 714 715Examples of artifact references: \"log4j:log4j:1.2.14\" for 716'log4j-1.2.14.jar'. Resolving \"log4j:log4j\" would return the latest 717version of the artifact known to the distributed Maven pom.xml graph. 718 719Returns a string containing the necessary classpath entries for this 720artifact and all of its transitive dependencies." 721 (let ((result (split-string string ":"))) 722 (cond 723 ((= (length result) 3) 724 (resolve-dependencies 725 (first result) (second result) :version (third result))) 726 ((string= string "com.sun.jna:jna") 727 (warn "Replacing request for no longer available com.sun.jna:jna with net.java.dev.jna:jna") 728 (resolve-dependencies "net.java.dev.jna" "jna" :version "LATEST")) 729 ((= (length result) 2) 730 (resolve-dependencies 731 (first result) (second result))) 732 (t 733 (destructuring-bind (group-id artifact-id &optional version repository) 734 (abcl-build:split-string string "/") 735 (setf result 736 (apply #'resolve-dependencies group-id artifact-id 737 (append (when version 738 `(:version ,version)) 739 (when repository 740 `(:repository ,repository)))))))))) 741 742 743;;; Currently the last file listed in ASDF 744(provide 'abcl-asdf) 745 746 747