blob: edd212b6e2521101926e04ec71f09cb892cd4c05 [file] [log] [blame]
Stuart McCullochf3173222012-06-07 21:57:32 +00001package aQute.bnd.maven;
2
3import java.io.*;
4import java.net.*;
5import java.util.*;
6import java.util.Map.Entry;
7import java.util.concurrent.*;
8import java.util.jar.*;
9import java.util.regex.*;
10
11import aQute.bnd.maven.support.*;
12import aQute.bnd.maven.support.Pom.Scope;
13import aQute.bnd.settings.*;
14import aQute.lib.collections.*;
15import aQute.lib.io.*;
16import aQute.lib.osgi.*;
17import aQute.lib.osgi.Descriptors.PackageRef;
18import aQute.libg.command.*;
19import aQute.libg.header.*;
20
21public class MavenCommand extends Processor {
22 final Settings settings = new Settings();
23 File temp;
24
Stuart McCulloch4482c702012-06-15 13:27:53 +000025 public MavenCommand() {}
Stuart McCullochf3173222012-06-07 21:57:32 +000026
27 public MavenCommand(Processor p) {
28 super(p);
29 }
30
31 /**
32 * maven deploy [-url repo] [-passphrase passphrase] [-homedir homedir]
33 * [-keyname keyname] bundle ...
34 *
35 * @param args
36 * @param i
37 * @throws Exception
38 */
39 public void run(String args[], int i) throws Exception {
40 temp = new File("maven-bundle");
41
42 if (i >= args.length) {
43 help();
44 return;
45 }
46
47 while (i < args.length && args[i].startsWith("-")) {
48 String option = args[i];
49 trace("option " + option);
50 if (option.equals("-temp"))
51 temp = getFile(args[++i]);
52 else {
53 help();
54 error("Invalid option " + option);
55 }
56 i++;
57 }
58
59 String cmd = args[i++];
60
61 trace("temp dir " + temp);
62 IO.delete(temp);
63 temp.mkdirs();
64 if (!temp.isDirectory())
65 throw new IOException("Cannot create temp directory");
66
67 if (cmd.equals("settings"))
68 settings();
69 else if (cmd.equals("help"))
70 help();
71 else if (cmd.equals("bundle"))
72 bundle(args, i);
73 else if (cmd.equals("view"))
74 view(args, i);
75 else
76 error("No such command %s, type help", cmd);
77 }
78
79 private void help() {
80 System.err.println("Usage:%n");
81 System.err
82 .println(" maven %n" //
83 + " [-temp <dir>] use as temp directory%n" //
84 + " settings show maven settings%n" //
85 + " bundle turn a bundle into a maven bundle%n" //
86 + " [-properties <file>] provide properties, properties starting with javadoc are options for javadoc, like javadoc-tag=...%n"
87 + " [-javadoc <file|url>] where to find the javadoc (zip/dir), otherwise generated%n" //
88 + " [-source <file|url>] where to find the source (zip/dir), otherwise from OSGI-OPT/src%n" //
89 + " [-scm <url>] required scm in pom, otherwise from Bundle-SCM%n" //
90 + " [-url <url>] required project url in pom%n" //
91 + " [-bsn bsn] overrides bsn%n" //
92 + " [-version <version>] overrides version%n" //
93 + " [-developer <email>] developer email%n" //
94 + " [-nodelete] do not delete temp files%n" //
95 + " [-passphrase <gpgp passphrase>] signer password%n"//
96 + " <file|url> ");
97 }
98
99 /**
100 * Show the maven settings
101 *
102 * @throws FileNotFoundException
103 * @throws Exception
104 */
105 private void settings() throws FileNotFoundException, Exception {
106 File userHome = new File(System.getProperty("user.home"));
107 File m2 = new File(userHome, ".m2");
108 if (!m2.isDirectory()) {
109 error("There is no m2 directory at %s", userHome);
110 return;
111 }
112 File settings = new File(m2, "settings.xml");
113 if (!settings.isFile()) {
114 error("There is no settings file at '%s'", settings.getAbsolutePath());
115 return;
116 }
117 LineCollection lc = new LineCollection(IO.reader(settings));
118 while (lc.hasNext()) {
119 System.err.println(lc.next());
120 }
121 }
122
123 /**
124 * Create a maven bundle.
125 *
126 * @param args
127 * @param i
128 * @throws Exception
129 */
130 private void bundle(String args[], int i) throws Exception {
131 List<String> developers = new ArrayList<String>();
132 Properties properties = new Properties();
133
134 String scm = null;
135 String passphrase = null;
136 String javadoc = null;
137 String source = null;
138 String output = "bundle.jar";
139 String url = null;
140 String artifact = null;
141 String group = null;
142 String version = null;
143 boolean nodelete = false;
144
145 while (i < args.length && args[i].startsWith("-")) {
146 String option = args[i++];
147 trace("bundle option %s", option);
148 if (option.equals("-scm"))
149 scm = args[i++];
150 else if (option.equals("-group"))
151 group = args[i++];
152 else if (option.equals("-artifact"))
153 artifact = args[i++];
154 else if (option.equals("-version"))
155 version = args[i++];
156 else if (option.equals("-developer"))
157 developers.add(args[i++]);
158 else if (option.equals("-passphrase")) {
159 passphrase = args[i++];
160 } else if (option.equals("-url")) {
161 url = args[i++];
162 } else if (option.equals("-javadoc"))
163 javadoc = args[i++];
164 else if (option.equals("-source"))
165 source = args[i++];
166 else if (option.equals("-output"))
167 output = args[i++];
168 else if (option.equals("-nodelete"))
Stuart McCulloch4482c702012-06-15 13:27:53 +0000169 nodelete = true;
Stuart McCullochf3173222012-06-07 21:57:32 +0000170 else if (option.startsWith("-properties")) {
171 InputStream in = null;
172 try {
173 in = new FileInputStream(args[i++]);
174 properties.load(in);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000175 }
176 catch (Exception e) {}
177 finally {
Stuart McCullochf3173222012-06-07 21:57:32 +0000178 if (in != null) {
179 in.close();
180 }
181 }
182 }
183 }
184
185 if (developers.isEmpty()) {
186 String email = settings.globalGet(Settings.EMAIL, null);
187 if (email == null)
188 error("No developer email set, you can set global default email with: bnd global email Peter.Kriens@aQute.biz");
189 else
190 developers.add(email);
191 }
192
193 if (i == args.length) {
194 error("too few arguments, no bundle specified");
195 return;
196 }
197
198 if (i != args.length - 1) {
199 error("too many arguments, only one bundle allowed");
200 return;
201 }
202
203 String input = args[i++];
204
205 Jar binaryJar = getJarFromFileOrURL(input);
206 trace("got %s", binaryJar);
207 if (binaryJar == null) {
208 error("JAR does not exist: %s", input);
209 return;
210 }
211
212 File original = getFile(temp, "original");
213 original.mkdirs();
214 binaryJar.expand(original);
215 binaryJar.calcChecksums(null);
216
217 Manifest manifest = binaryJar.getManifest();
218 trace("got manifest");
219
220 PomFromManifest pom = new PomFromManifest(manifest);
221
222 if (scm != null)
223 pom.setSCM(scm);
224 if (url != null)
225 pom.setURL(url);
226 if (artifact != null)
227 pom.setArtifact(artifact);
228 if (artifact != null)
229 pom.setGroup(group);
230 if (version != null)
231 pom.setVersion(version);
232 trace(url);
233 for (String d : developers)
234 pom.addDeveloper(d);
235
Stuart McCulloch4482c702012-06-15 13:27:53 +0000236 Set<String> exports = OSGiHeader.parseHeader(manifest.getMainAttributes().getValue(Constants.EXPORT_PACKAGE))
237 .keySet();
Stuart McCullochf3173222012-06-07 21:57:32 +0000238
239 Jar sourceJar;
240 if (source == null) {
241 trace("Splitting source code");
242 sourceJar = new Jar("source");
Stuart McCulloch4482c702012-06-15 13:27:53 +0000243 for (Map.Entry<String,Resource> entry : binaryJar.getResources().entrySet()) {
Stuart McCullochf3173222012-06-07 21:57:32 +0000244 if (entry.getKey().startsWith("OSGI-OPT/src")) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000245 sourceJar.putResource(entry.getKey().substring("OSGI-OPT/src/".length()), entry.getValue());
Stuart McCullochf3173222012-06-07 21:57:32 +0000246 }
247 }
248 copyInfo(binaryJar, sourceJar, "source");
249 } else {
250 sourceJar = getJarFromFileOrURL(source);
251 }
252 sourceJar.calcChecksums(null);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000253
Stuart McCullochf3173222012-06-07 21:57:32 +0000254 Jar javadocJar;
255 if (javadoc == null) {
256 trace("creating javadoc because -javadoc not used");
257 javadocJar = javadoc(getFile(original, "OSGI-OPT/src"), exports, manifest, properties);
258 if (javadocJar == null) {
259 error("Cannot find source code in OSGI-OPT/src to generate Javadoc");
260 return;
261 }
262 copyInfo(binaryJar, javadocJar, "javadoc");
263 } else {
264 trace("Loading javadoc externally %s", javadoc);
265 javadocJar = getJarFromFileOrURL(javadoc);
266 }
267 javadocJar.calcChecksums(null);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000268
Stuart McCullochf3173222012-06-07 21:57:32 +0000269 addClose(binaryJar);
270 addClose(sourceJar);
271 addClose(javadocJar);
272
273 trace("creating bundle dir");
274 File bundle = new File(temp, "bundle");
275 bundle.mkdirs();
276
277 String prefix = pom.getArtifactId() + "-" + pom.getVersion();
278 File binaryFile = new File(bundle, prefix + ".jar");
279 File sourceFile = new File(bundle, prefix + "-sources.jar");
280 File javadocFile = new File(bundle, prefix + "-javadoc.jar");
281 File pomFile = new File(bundle, "pom.xml").getAbsoluteFile();
Stuart McCulloch4482c702012-06-15 13:27:53 +0000282 trace("creating output files %s, %s,%s, and %s", binaryFile, sourceFile, javadocFile, pomFile);
Stuart McCullochf3173222012-06-07 21:57:32 +0000283
284 IO.copy(pom.openInputStream(), pomFile);
285 trace("copied pom");
286
287 trace("writing binary %s", binaryFile);
288 binaryJar.write(binaryFile);
289
290 trace("writing source %s", sourceFile);
291 sourceJar.write(sourceFile);
292
293 trace("writing javadoc %s", javadocFile);
294 javadocJar.write(javadocFile);
295
296 sign(binaryFile, passphrase);
297 sign(sourceFile, passphrase);
298 sign(javadocFile, passphrase);
299 sign(pomFile, passphrase);
300
301 trace("create bundle");
302 Jar bundleJar = new Jar(bundle);
303 addClose(bundleJar);
304 File outputFile = getFile(output);
305 bundleJar.write(outputFile);
306 trace("created bundle %s", outputFile);
307
308 binaryJar.close();
309 sourceJar.close();
310 javadocJar.close();
311 bundleJar.close();
312 if (!nodelete)
313 IO.delete(temp);
314 }
315
316 private void copyInfo(Jar source, Jar dest, String type) throws Exception {
317 source.ensureManifest();
318 dest.ensureManifest();
Stuart McCulloch4482c702012-06-15 13:27:53 +0000319 copyInfoResource(source, dest, "LICENSE");
320 copyInfoResource(source, dest, "LICENSE.html");
321 copyInfoResource(source, dest, "about.html");
322
Stuart McCullochf3173222012-06-07 21:57:32 +0000323 Manifest sm = source.getManifest();
324 Manifest dm = dest.getManifest();
Stuart McCulloch4482c702012-06-15 13:27:53 +0000325 copyInfoHeader(sm, dm, "Bundle-Description", "");
326 copyInfoHeader(sm, dm, "Bundle-Vendor", "");
327 copyInfoHeader(sm, dm, "Bundle-Copyright", "");
328 copyInfoHeader(sm, dm, "Bundle-DocURL", "");
329 copyInfoHeader(sm, dm, "Bundle-License", "");
330 copyInfoHeader(sm, dm, "Bundle-Name", " " + type);
331 copyInfoHeader(sm, dm, "Bundle-SymbolicName", "." + type);
332 copyInfoHeader(sm, dm, "Bundle-Version", "");
Stuart McCullochf3173222012-06-07 21:57:32 +0000333 }
334
335 private void copyInfoHeader(Manifest sm, Manifest dm, String key, String value) {
336 String v = sm.getMainAttributes().getValue(key);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000337 if (v == null) {
Stuart McCullochf3173222012-06-07 21:57:32 +0000338 trace("no source for " + key);
339 return;
340 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000341
342 if (dm.getMainAttributes().getValue(key) != null) {
343 trace("already have " + key);
Stuart McCullochf3173222012-06-07 21:57:32 +0000344 return;
345 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000346
Stuart McCullochf3173222012-06-07 21:57:32 +0000347 dm.getMainAttributes().putValue(key, v + value);
348 }
349
350 private void copyInfoResource(Jar source, Jar dest, String type) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000351 if (source.getResources().containsKey(type) && !dest.getResources().containsKey(type))
Stuart McCullochf3173222012-06-07 21:57:32 +0000352 dest.putResource(type, source.getResource(type));
353 }
354
355 /**
356 * @return
357 * @throws IOException
358 * @throws MalformedURLException
359 */
360 protected Jar getJarFromFileOrURL(String spec) throws IOException, MalformedURLException {
361 Jar jar;
362 File jarFile = getFile(spec);
363 if (jarFile.exists()) {
364 jar = new Jar(jarFile);
365 } else {
366 URL url = new URL(spec);
367 InputStream in = url.openStream();
368 try {
369 jar = new Jar(url.getFile(), in);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000370 }
371 finally {
Stuart McCullochf3173222012-06-07 21:57:32 +0000372 in.close();
373 }
374 }
375 addClose(jar);
376 return jar;
377 }
378
379 private void sign(File file, String passphrase) throws Exception {
380 trace("signing %s", file);
381 File asc = new File(file.getParentFile(), file.getName() + ".asc");
382 asc.delete();
383
384 Command command = new Command();
385 command.setTrace();
386
387 command.add(getProperty("gpgp", "gpg"));
388 if (passphrase != null)
389 command.add("--passphrase", passphrase);
390 command.add("-ab", "--sign"); // not the -b!!
391 command.add(file.getAbsolutePath());
392 System.err.println(command);
393 StringBuilder stdout = new StringBuilder();
394 StringBuilder stderr = new StringBuilder();
395 int result = command.execute(stdout, stderr);
396 if (result != 0) {
397 error("gpg signing %s failed because %s", file, "" + stdout + stderr);
398 }
399 }
400
Stuart McCulloch4482c702012-06-15 13:27:53 +0000401 private Jar javadoc(File source, Set<String> exports, Manifest m, Properties p) throws Exception {
Stuart McCullochf3173222012-06-07 21:57:32 +0000402 File tmp = new File(temp, "javadoc");
403 tmp.mkdirs();
404
405 Command command = new Command();
406 command.add(getProperty("javadoc", "javadoc"));
407 command.add("-quiet");
408 command.add("-protected");
409 // command.add("-classpath");
410 // command.add(binary.getAbsolutePath());
411 command.add("-d");
412 command.add(tmp.getAbsolutePath());
413 command.add("-charset");
414 command.add("UTF-8");
415 command.add("-sourcepath");
416 command.add(source.getAbsolutePath());
417
418 Attributes attr = m.getMainAttributes();
419 Properties pp = new Properties(p);
420 set(pp, "-doctitle", description(attr));
421 set(pp, "-header", description(attr));
422 set(pp, "-windowtitle", name(attr));
423 set(pp, "-bottom", copyright(attr));
424 set(pp, "-footer", license(attr));
425
426 command.add("-tag");
427 command.add("Immutable:t:Immutable");
428 command.add("-tag");
429 command.add("ThreadSafe:t:ThreadSafe");
430 command.add("-tag");
431 command.add("NotThreadSafe:t:NotThreadSafe");
432 command.add("-tag");
433 command.add("GuardedBy:mf:Guarded By:");
434 command.add("-tag");
435 command.add("security:m:Required Permissions");
436 command.add("-tag");
437 command.add("noimplement:t:Consumers of this API must not implement this interface");
438
Stuart McCulloch4482c702012-06-15 13:27:53 +0000439 for (Enumeration< ? > e = pp.propertyNames(); e.hasMoreElements();) {
Stuart McCullochf3173222012-06-07 21:57:32 +0000440 String key = (String) e.nextElement();
441 String value = pp.getProperty(key);
442
443 if (key.startsWith("javadoc")) {
444 key = key.substring("javadoc".length());
445 removeDuplicateMarker(key);
446 command.add(key);
447 command.add(value);
448 }
449 }
450 for (String packageName : exports) {
451 command.add(packageName);
452 }
453
454 StringBuilder out = new StringBuilder();
455 StringBuilder err = new StringBuilder();
456
457 System.err.println(command);
458
459 int result = command.execute(out, err);
460 if (result != 0) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000461 warning("Error during execution of javadoc command: %s\n******************\n%s", out, err);
Stuart McCullochf3173222012-06-07 21:57:32 +0000462 }
463 Jar jar = new Jar(tmp);
464 addClose(jar);
465 return jar;
466 }
467
468 /**
469 * Generate a license string
470 *
471 * @param attr
472 * @return
473 */
474 private String license(Attributes attr) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000475 Parameters map = Processor.parseHeader(attr.getValue(Constants.BUNDLE_LICENSE), null);
Stuart McCullochf3173222012-06-07 21:57:32 +0000476 if (map.isEmpty())
477 return null;
478
479 StringBuilder sb = new StringBuilder();
480 String sep = "Licensed under ";
Stuart McCulloch4482c702012-06-15 13:27:53 +0000481 for (Entry<String,Attrs> entry : map.entrySet()) {
Stuart McCullochf3173222012-06-07 21:57:32 +0000482 sb.append(sep);
483 String key = entry.getKey();
484 String link = entry.getValue().get("link");
485 String description = entry.getValue().get("description");
486
487 if (description == null)
488 description = key;
489
490 if (link != null) {
491 sb.append("<a href='");
492 sb.append(link);
493 sb.append("'>");
494 }
495 sb.append(description);
496 if (link != null) {
497 sb.append("</a>");
498 }
499 sep = ",<br/>";
500 }
501
502 return sb.toString();
503 }
504
505 /**
506 * Generate the copyright statement.
507 *
508 * @param attr
509 * @return
510 */
511 private String copyright(Attributes attr) {
512 return attr.getValue(Constants.BUNDLE_COPYRIGHT);
513 }
514
515 private String name(Attributes attr) {
516 String name = attr.getValue(Constants.BUNDLE_NAME);
517 if (name == null)
518 name = attr.getValue(Constants.BUNDLE_SYMBOLICNAME);
519 return name;
520 }
521
522 private String description(Attributes attr) {
523 String descr = attr.getValue(Constants.BUNDLE_DESCRIPTION);
524 if (descr == null)
525 descr = attr.getValue(Constants.BUNDLE_NAME);
526 if (descr == null)
527 descr = attr.getValue(Constants.BUNDLE_SYMBOLICNAME);
528 return descr;
529 }
530
531 private void set(Properties pp, String option, String defaultValue) {
532 String key = "javadoc" + option;
533 String existingValue = pp.getProperty(key);
534 if (existingValue != null)
535 return;
536
537 pp.setProperty(key, defaultValue);
538 }
539
Stuart McCullochf3173222012-06-07 21:57:32 +0000540 /**
541 * View - Show the dependency details of an artifact
542 */
Stuart McCullochf3173222012-06-07 21:57:32 +0000543
Stuart McCulloch4482c702012-06-15 13:27:53 +0000544 static Executor executor = Executors.newCachedThreadPool();
545 static Pattern GROUP_ARTIFACT_VERSION = Pattern.compile("([^+]+)\\+([^+]+)\\+([^+]+)");
546
547 void view(String args[], int i) throws Exception {
Stuart McCullochf3173222012-06-07 21:57:32 +0000548 Maven maven = new Maven(executor);
549 OutputStream out = System.err;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000550
551 List<URI> urls = new ArrayList<URI>();
552
553 while (i < args.length && args[i].startsWith("-")) {
554 if ("-r".equals(args[i])) {
Stuart McCullochf3173222012-06-07 21:57:32 +0000555 URI uri = new URI(args[++i]);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000556 urls.add(uri);
Stuart McCullochf3173222012-06-07 21:57:32 +0000557 System.err.println("URI for repo " + uri);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000558 } else if ("-o".equals(args[i])) {
559 out = new FileOutputStream(args[++i]);
560 } else
561 throw new IllegalArgumentException("Unknown option: " + args[i]);
562
Stuart McCullochf3173222012-06-07 21:57:32 +0000563 i++;
564 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000565
Stuart McCullochf3173222012-06-07 21:57:32 +0000566 URI[] urls2 = urls.toArray(new URI[urls.size()]);
567 PrintWriter pw = IO.writer(out);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000568
569 while (i < args.length) {
Stuart McCullochf3173222012-06-07 21:57:32 +0000570 String ref = args[i++];
571 pw.println("Ref " + ref);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000572
Stuart McCullochf3173222012-06-07 21:57:32 +0000573 Matcher matcher = GROUP_ARTIFACT_VERSION.matcher(ref);
574 if (matcher.matches()) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000575
Stuart McCullochf3173222012-06-07 21:57:32 +0000576 String group = matcher.group(1);
577 String artifact = matcher.group(2);
578 String version = matcher.group(3);
579 CachedPom pom = maven.getPom(group, artifact, version, urls2);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000580
Stuart McCullochf3173222012-06-07 21:57:32 +0000581 Builder a = new Builder();
582 a.setProperty("Private-Package", "*");
583 Set<Pom> dependencies = pom.getDependencies(Scope.compile, urls2);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000584 for (Pom dep : dependencies) {
585 System.err.printf("%20s %-20s %10s%n", dep.getGroupId(), dep.getArtifactId(), dep.getVersion());
Stuart McCullochf3173222012-06-07 21:57:32 +0000586 a.addClasspath(dep.getArtifact());
587 }
588 pw.println(a.getClasspath());
589 a.build();
590
Stuart McCulloch4482c702012-06-15 13:27:53 +0000591 TreeSet<PackageRef> sorted = new TreeSet<PackageRef>(a.getImports().keySet());
592 for (PackageRef p : sorted) {
593 pw.printf("%-40s\n", p);
Stuart McCullochf3173222012-06-07 21:57:32 +0000594 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000595 // for ( Map.Entry<String, Set<String>> entry :
596 // a.getUses().entrySet()) {
597 // String from = entry.getKey();
598 // for ( String uses : entry.getValue()) {
599 // System.err.printf("%40s %s\n", from, uses);
600 // from = "";
601 // }
602 // }
Stuart McCullochf3173222012-06-07 21:57:32 +0000603 a.close();
604 } else
605 System.err.println("Wrong, must look like group+artifact+version, is " + ref);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000606
Stuart McCullochf3173222012-06-07 21:57:32 +0000607 }
608 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000609
Stuart McCullochf3173222012-06-07 21:57:32 +0000610}