source: trunk/abcl/contrib/abcl-asdf/maven-embedder.lisp @ 13807

Last change on this file since 13807 was 13807, checked in by Mark Evenson, 11 years ago

Further fix for ABCL-ASDF load time failure.

Start separating out the public api by exporting and documenting symbols.

File size: 8.2 KB
Line 
1;;; Use the Aether system in a default maven distribution to download
2;;; and install dependencies.
3;;;
4;;; https://docs.sonatype.org/display/AETHER/Home
5;;;
6
7(in-package :abcl-asdf)
8
9(require :abcl-contrib)
10(require :jss)
11
12#|
13Test:
14(resolve-dependencies "org.slf4j" "slf4j-api" "1.6.1")
15
16(resolve-dependencies "org.apache.maven" "maven-aether-provider" "3.0.4")
17|#
18
19(defvar *mavens* '("/opt/local/bin/mvn3" "mvn3" "mvn" "mvn.bat")
20  "Locations to search for the Maven executable.")
21
22(defun find-mvn () 
23  "Attempt to find a suitable Maven ('mvn') executable on the hosting operating system."
24  (dolist (mvn-path *mavens*)
25    (let ((mvn 
26           (handler-case 
27               (truename (read-line (sys::process-output 
28                                     (sys::run-program "which" `(,mvn-path)))))
29             (end-of-file () nil))))
30      (when mvn
31        (return-from find-mvn mvn)))))
32
33(defun find-mvn-libs ()
34  (let ((mvn (find-mvn)))
35    (unless mvn
36      (warn "Failed to find Maven3 libraries.")
37      (return-from find-mvn-libs))
38    (truename (make-pathname 
39               :defaults (merge-pathnames "../lib/" mvn)
40               :name nil :type nil))))
41
42(defparameter *mvn-libs-directory*
43  nil
44  "Location of 'maven-core-3.<m>.<p>.jar', 'maven-embedder-3.<m>.<p>.jar' etc.")
45
46(defun mvn-version ()
47  (let* ((line
48         (read-line (sys::process-output 
49                     (sys::run-program 
50                      (namestring (find-mvn)) '("-version")))))
51         (pattern (#"compile" 
52                   'regex.Pattern
53                   "Apache Maven ([0-9]+)\\.([0-9]+)\\.([0-9]+)"))
54         (matcher (#"matcher" pattern line))
55         (found (#"find" matcher)))
56    (unless found 
57      (return-from mvn-version nil))
58    (mapcar #'parse-integer
59            `(,(#"group" matcher 1)
60              ,(#"group" matcher 2)
61              ,(#"group" matcher 3)))))
62
63(defun ensure-mvn-version ()
64  "Return t if Maven version is 3.0.3 or greater."
65  (let* ((version (mvn-version))
66         (major (first version))
67         (minor (second version))
68         (patch (third version)))
69    (or 
70     (and (>= major 3)
71          (>= minor 1))
72     (and (>= major 3)
73          (>= major 0)
74          (>= patch 3)))))
75
76(defparameter *init* nil)
77
78(defun init ()
79  "Run the initialization strategy to bootstrap a Maven dependency node."
80  (unless *mvn-libs-directory*
81    (setf *mvn-libs-directory* (find-mvn-libs)))
82  (unless (probe-file *mvn-libs-directory*)
83    (error "You must download maven-3.0.3 or later from http://maven.apache.org/download.html, then set ABCL-ASDF:*MVN-DIRECTORY* appropiately."))
84  (unless (ensure-mvn-version)
85    (error "We need maven-3.0.3 or later."))
86  (add-directory-jars-to-class-path *mvn-libs-directory* nil)
87  (setf *init* t))
88
89(defparameter *http-wagon-implementations*
90  `("org.apache.maven.wagon.providers.http.HttpWagon" ;; introduced as default with maven-3.0.4
91    "org.apache.maven.wagon.providers.http.LightweightHttpWagon")
92  "A list of possible candidate implementations that provide access to http and https resources.
93
94Supposedly configurable with the java.net.protocols (c.f. reference maso2000 in the Manual.")
95
96(defun make-wagon-provider ()
97  "Returns an implementation of the org.sonatype.aether.connector.wagon.WagonProvider contract.
98
99The implementation is specified as Lisp closures.  Currently, it only
100specializes the lookup() method if passed an 'http' role hint."
101  (unless *init* (init))
102  (java:jinterface-implementation 
103   "org.sonatype.aether.connector.wagon.WagonProvider"
104   "lookup"
105   (lambda (role-hint)
106     (if (string-equal "http" role-hint)
107         (some (lambda (provider) (java:jnew provider)) *http-wagon-implementations*)
108       java:+null+))
109   "release"
110   (lambda (wagon)
111     (declare (ignore wagon)))))
112
113(defun repository-system ()
114  (unless *init* (init))
115  (let ((locator 
116         (java:jnew "org.apache.maven.repository.internal.DefaultServiceLocator"))
117        (repository-connector-factory-class 
118         (java:jclass "org.sonatype.aether.spi.connector.RepositoryConnectorFactory"))
119        (wagon-repository-connector-factory-class
120         (java:jclass "org.sonatype.aether.connector.wagon.WagonRepositoryConnectorFactory"))
121        (wagon-provider-class 
122         (java:jclass "org.sonatype.aether.connector.wagon.WagonProvider"))
123        (repository-system-class
124         (java:jclass "org.sonatype.aether.RepositorySystem")))
125    (#"addService" locator
126                   repository-connector-factory-class 
127                   wagon-repository-connector-factory-class)
128    (#"setServices" locator
129                    wagon-provider-class
130                    (java:jnew-array-from-list 
131                     "org.sonatype.aether.connector.wagon.WagonProvider"
132                     (list 
133                      (make-wagon-provider))))
134    (#"getService" locator
135                   repository-system-class)))
136       
137(defun new-session (repository-system)
138  (let ((session 
139         (java:jnew (jss:find-java-class "MavenRepositorySystemSession")))
140        (local-repository 
141         (java:jnew (jss:find-java-class "LocalRepository")
142                  (namestring (merge-pathnames ".m2/repository/"
143                                               (user-homedir-pathname))))))
144    (#"setLocalRepositoryManager" 
145     session
146     (#"newLocalRepositoryManager" repository-system local-repository))))
147
148(defun resolve-artifact (group-id artifact-id &optional (version "LATEST" versionp))
149  "Directly resolve Maven dependencies for item with GROUP-ID and ARTIFACT-ID at VERSION, ignoring dependencies.
150
151Declared dependencies are not attempted to be located.
152
153If unspecified, the string \"LATEST\" will be used for the VERSION.
154
155Returns a string containing the necessary jvm classpath entries packed
156in Java CLASSPATH representation."
157
158  (unless versionp
159    (warn "Using LATEST for unspecified version."))
160  (let* ((system 
161          (repository-system))
162         (session 
163          (new-session system))
164         (repository 
165          (jss:new "org.sonatype.aether.repository.RemoteRepository"
166                   "central" "default" "http://repo1.maven.org/maven2/"))
167         (artifact-string (format nil "~A:~A:~A"
168                                  group-id artifact-id version))
169         (artifact 
170          (jss:new "org.sonatype.aether.util.artifact.DefaultArtifact" artifact-string))
171         (artifact-request 
172          (java:jnew "org.sonatype.aether.resolution.ArtifactRequest")))
173    (#"setArtifact" artifact-request artifact)
174    (#"addRepository" artifact-request repository)
175    (#"resolveArtifact" system session artifact-request)))
176
177(defun resolve-dependencies (group-id artifact-id &optional (version "LATEST" versionp))
178  "Dynamically resolve Maven dependencies for item with GROUP-ID and ARTIFACT-ID at VERSION.
179
180All recursive dependencies will be visited before resolution is successful.
181
182If unspecified, the string \"LATEST\" will be used for the VERSION.
183
184Returns a string containing the necessary jvm classpath entries packed
185in Java CLASSPATH representation."
186  (unless *init* (init))
187  (unless versionp
188    (warn "Using LATEST for unspecified version."))
189  (let* ((system 
190          (repository-system))
191         (session 
192          (new-session system))
193         (artifact
194          (java:jnew (jss:find-java-class "aether.util.artifact.DefaultArtifact")
195                     (format nil "~A:~A:~A"
196                             group-id artifact-id version)))
197         (dependency 
198          (java:jnew (jss:find-java-class "aether.graph.Dependency")
199                     artifact "compile"))
200         (central
201          (java:jnew (jss:find-java-class "RemoteRepository")
202                     "central" "default" "http://repo1.maven.org/maven2/"))
203         (collect-request (java:jnew (jss:find-java-class "CollectRequest"))))
204    (#"setRoot" collect-request dependency)
205    (#"addRepository" collect-request central)
206    (let* ((node 
207            (#"getRoot" (#"collectDependencies" system session collect-request)))
208           (dependency-request 
209            (java:jnew (jss:find-java-class "DependencyRequest")
210                       node java:+null+))
211           (nlg 
212            (java:jnew (jss:find-java-class "PreorderNodeListGenerator"))))
213      (#"resolveDependencies" system session dependency-request)
214      (#"accept" node nlg)
215      (#"getClassPath" nlg))))
216
217
218
219
220         
221
Note: See TracBrowser for help on using the repository browser.