| package aQute.bnd.maven; |
| |
| import java.io.*; |
| import java.net.*; |
| import java.util.*; |
| import java.util.Map.Entry; |
| import java.util.concurrent.*; |
| import java.util.jar.*; |
| import java.util.regex.*; |
| |
| import aQute.bnd.header.*; |
| import aQute.bnd.maven.support.*; |
| import aQute.bnd.maven.support.Pom.Scope; |
| import aQute.bnd.osgi.*; |
| import aQute.bnd.osgi.Descriptors.PackageRef; |
| import aQute.bnd.settings.*; |
| import aQute.lib.collections.*; |
| import aQute.lib.io.*; |
| import aQute.libg.command.*; |
| |
| public class MavenCommand extends Processor { |
| final Settings settings = new Settings(); |
| File temp; |
| |
| public MavenCommand() {} |
| |
| public MavenCommand(Processor p) { |
| super(p); |
| } |
| |
| /** |
| * maven deploy [-url repo] [-passphrase passphrase] [-homedir homedir] |
| * [-keyname keyname] bundle ... |
| * |
| * @param args |
| * @param i |
| * @throws Exception |
| */ |
| public void run(String args[], int i) throws Exception { |
| temp = new File("maven-bundle"); |
| |
| if (i >= args.length) { |
| help(); |
| return; |
| } |
| |
| while (i < args.length && args[i].startsWith("-")) { |
| String option = args[i]; |
| trace("option " + option); |
| if (option.equals("-temp")) |
| temp = getFile(args[++i]); |
| else { |
| help(); |
| error("Invalid option " + option); |
| } |
| i++; |
| } |
| |
| String cmd = args[i++]; |
| |
| trace("temp dir " + temp); |
| IO.delete(temp); |
| temp.mkdirs(); |
| if (!temp.isDirectory()) |
| throw new IOException("Cannot create temp directory"); |
| |
| if (cmd.equals("settings")) |
| settings(); |
| else if (cmd.equals("help")) |
| help(); |
| else if (cmd.equals("bundle")) |
| bundle(args, i); |
| else if (cmd.equals("view")) |
| view(args, i); |
| else |
| error("No such command %s, type help", cmd); |
| } |
| |
| private void help() { |
| System.err.printf("Usage:%n"); |
| System.err |
| .printf(" maven %n" // |
| + " [-temp <dir>] use as temp directory%n" // |
| + " settings show maven settings%n" // |
| + " bundle turn a bundle into a maven bundle%n" // |
| + " [-properties <file>] provide properties, properties starting with javadoc are options for javadoc, like javadoc-tag=...%n" |
| + " [-javadoc <file|url>] where to find the javadoc (zip/dir), otherwise generated%n" // |
| + " [-source <file|url>] where to find the source (zip/dir), otherwise from OSGI-OPT/src%n" // |
| + " [-scm <url>] required scm in pom, otherwise from Bundle-SCM%n" // |
| + " [-url <url>] required project url in pom%n" // |
| + " [-bsn bsn] overrides bsn%n" // |
| + " [-version <version>] overrides version%n" // |
| + " [-developer <email>] developer email%n" // |
| + " [-nodelete] do not delete temp files%n" // |
| + " [-passphrase <gpgp passphrase>] signer password%n"// |
| + " <file|url>%n"); |
| } |
| |
| /** |
| * Show the maven settings |
| * |
| * @throws FileNotFoundException |
| * @throws Exception |
| */ |
| private void settings() throws FileNotFoundException, Exception { |
| File userHome = new File(System.getProperty("user.home")); |
| File m2 = new File(userHome, ".m2"); |
| if (!m2.isDirectory()) { |
| error("There is no m2 directory at %s", userHome); |
| return; |
| } |
| File settings = new File(m2, "settings.xml"); |
| if (!settings.isFile()) { |
| error("There is no settings file at '%s'", settings.getAbsolutePath()); |
| return; |
| } |
| LineCollection lc = new LineCollection(IO.reader(settings)); |
| while (lc.hasNext()) { |
| System.err.println(lc.next()); |
| } |
| } |
| |
| /** |
| * Create a maven bundle. |
| * |
| * @param args |
| * @param i |
| * @throws Exception |
| */ |
| private void bundle(String args[], int i) throws Exception { |
| List<String> developers = new ArrayList<String>(); |
| Properties properties = new Properties(); |
| |
| String scm = null; |
| String passphrase = null; |
| String javadoc = null; |
| String source = null; |
| String output = "bundle.jar"; |
| String url = null; |
| String artifact = null; |
| String group = null; |
| String version = null; |
| boolean nodelete = false; |
| |
| while (i < args.length && args[i].startsWith("-")) { |
| String option = args[i++]; |
| trace("bundle option %s", option); |
| if (option.equals("-scm")) |
| scm = args[i++]; |
| else if (option.equals("-group")) |
| group = args[i++]; |
| else if (option.equals("-artifact")) |
| artifact = args[i++]; |
| else if (option.equals("-version")) |
| version = args[i++]; |
| else if (option.equals("-developer")) |
| developers.add(args[i++]); |
| else if (option.equals("-passphrase")) { |
| passphrase = args[i++]; |
| } else if (option.equals("-url")) { |
| url = args[i++]; |
| } else if (option.equals("-javadoc")) |
| javadoc = args[i++]; |
| else if (option.equals("-source")) |
| source = args[i++]; |
| else if (option.equals("-output")) |
| output = args[i++]; |
| else if (option.equals("-nodelete")) |
| nodelete = true; |
| else if (option.startsWith("-properties")) { |
| InputStream in = null; |
| try { |
| in = new FileInputStream(args[i++]); |
| properties.load(in); |
| } |
| catch (Exception e) {} |
| finally { |
| if (in != null) { |
| in.close(); |
| } |
| } |
| } |
| } |
| |
| if (developers.isEmpty()) { |
| String email = settings.globalGet(Settings.EMAIL, null); |
| if (email == null) |
| error("No developer email set, you can set global default email with: bnd global email Peter.Kriens@aQute.biz"); |
| else |
| developers.add(email); |
| } |
| |
| if (i == args.length) { |
| error("too few arguments, no bundle specified"); |
| return; |
| } |
| |
| if (i != args.length - 1) { |
| error("too many arguments, only one bundle allowed"); |
| return; |
| } |
| |
| String input = args[i++]; |
| |
| Jar binaryJar = getJarFromFileOrURL(input); |
| trace("got %s", binaryJar); |
| if (binaryJar == null) { |
| error("JAR does not exist: %s", input); |
| return; |
| } |
| |
| File original = getFile(temp, "original"); |
| original.mkdirs(); |
| binaryJar.expand(original); |
| binaryJar.calcChecksums(null); |
| |
| Manifest manifest = binaryJar.getManifest(); |
| trace("got manifest"); |
| |
| PomFromManifest pom = new PomFromManifest(manifest); |
| |
| if (scm != null) |
| pom.setSCM(scm); |
| if (url != null) |
| pom.setURL(url); |
| if (artifact != null) |
| pom.setArtifact(artifact); |
| if (artifact != null) |
| pom.setGroup(group); |
| if (version != null) |
| pom.setVersion(version); |
| trace(url); |
| for (String d : developers) |
| pom.addDeveloper(d); |
| |
| Set<String> exports = OSGiHeader.parseHeader(manifest.getMainAttributes().getValue(Constants.EXPORT_PACKAGE)) |
| .keySet(); |
| |
| Jar sourceJar; |
| if (source == null) { |
| trace("Splitting source code"); |
| sourceJar = new Jar("source"); |
| for (Map.Entry<String,Resource> entry : binaryJar.getResources().entrySet()) { |
| if (entry.getKey().startsWith("OSGI-OPT/src")) { |
| sourceJar.putResource(entry.getKey().substring("OSGI-OPT/src/".length()), entry.getValue()); |
| } |
| } |
| copyInfo(binaryJar, sourceJar, "source"); |
| } else { |
| sourceJar = getJarFromFileOrURL(source); |
| } |
| sourceJar.calcChecksums(null); |
| |
| Jar javadocJar; |
| if (javadoc == null) { |
| trace("creating javadoc because -javadoc not used"); |
| javadocJar = javadoc(getFile(original, "OSGI-OPT/src"), exports, manifest, properties); |
| if (javadocJar == null) { |
| error("Cannot find source code in OSGI-OPT/src to generate Javadoc"); |
| return; |
| } |
| copyInfo(binaryJar, javadocJar, "javadoc"); |
| } else { |
| trace("Loading javadoc externally %s", javadoc); |
| javadocJar = getJarFromFileOrURL(javadoc); |
| } |
| javadocJar.calcChecksums(null); |
| |
| addClose(binaryJar); |
| addClose(sourceJar); |
| addClose(javadocJar); |
| |
| trace("creating bundle dir"); |
| File bundle = new File(temp, "bundle"); |
| bundle.mkdirs(); |
| |
| String prefix = pom.getArtifactId() + "-" + pom.getVersion(); |
| File binaryFile = new File(bundle, prefix + ".jar"); |
| File sourceFile = new File(bundle, prefix + "-sources.jar"); |
| File javadocFile = new File(bundle, prefix + "-javadoc.jar"); |
| File pomFile = new File(bundle, "pom.xml").getAbsoluteFile(); |
| trace("creating output files %s, %s,%s, and %s", binaryFile, sourceFile, javadocFile, pomFile); |
| |
| IO.copy(pom.openInputStream(), pomFile); |
| trace("copied pom"); |
| |
| trace("writing binary %s", binaryFile); |
| binaryJar.write(binaryFile); |
| |
| trace("writing source %s", sourceFile); |
| sourceJar.write(sourceFile); |
| |
| trace("writing javadoc %s", javadocFile); |
| javadocJar.write(javadocFile); |
| |
| sign(binaryFile, passphrase); |
| sign(sourceFile, passphrase); |
| sign(javadocFile, passphrase); |
| sign(pomFile, passphrase); |
| |
| trace("create bundle"); |
| Jar bundleJar = new Jar(bundle); |
| addClose(bundleJar); |
| File outputFile = getFile(output); |
| bundleJar.write(outputFile); |
| trace("created bundle %s", outputFile); |
| |
| binaryJar.close(); |
| sourceJar.close(); |
| javadocJar.close(); |
| bundleJar.close(); |
| if (!nodelete) |
| IO.delete(temp); |
| } |
| |
| private void copyInfo(Jar source, Jar dest, String type) throws Exception { |
| source.ensureManifest(); |
| dest.ensureManifest(); |
| copyInfoResource(source, dest, "LICENSE"); |
| copyInfoResource(source, dest, "LICENSE.html"); |
| copyInfoResource(source, dest, "about.html"); |
| |
| Manifest sm = source.getManifest(); |
| Manifest dm = dest.getManifest(); |
| copyInfoHeader(sm, dm, "Bundle-Description", ""); |
| copyInfoHeader(sm, dm, "Bundle-Vendor", ""); |
| copyInfoHeader(sm, dm, "Bundle-Copyright", ""); |
| copyInfoHeader(sm, dm, "Bundle-DocURL", ""); |
| copyInfoHeader(sm, dm, "Bundle-License", ""); |
| copyInfoHeader(sm, dm, "Bundle-Name", " " + type); |
| copyInfoHeader(sm, dm, "Bundle-SymbolicName", "." + type); |
| copyInfoHeader(sm, dm, "Bundle-Version", ""); |
| } |
| |
| private void copyInfoHeader(Manifest sm, Manifest dm, String key, String value) { |
| String v = sm.getMainAttributes().getValue(key); |
| if (v == null) { |
| trace("no source for " + key); |
| return; |
| } |
| |
| if (dm.getMainAttributes().getValue(key) != null) { |
| trace("already have " + key); |
| return; |
| } |
| |
| dm.getMainAttributes().putValue(key, v + value); |
| } |
| |
| private void copyInfoResource(Jar source, Jar dest, String type) { |
| if (source.getResources().containsKey(type) && !dest.getResources().containsKey(type)) |
| dest.putResource(type, source.getResource(type)); |
| } |
| |
| /** |
| * @return |
| * @throws IOException |
| * @throws MalformedURLException |
| */ |
| protected Jar getJarFromFileOrURL(String spec) throws IOException, MalformedURLException { |
| Jar jar; |
| File jarFile = getFile(spec); |
| if (jarFile.exists()) { |
| jar = new Jar(jarFile); |
| } else { |
| URL url = new URL(spec); |
| InputStream in = url.openStream(); |
| try { |
| jar = new Jar(url.getFile(), in); |
| } |
| finally { |
| in.close(); |
| } |
| } |
| addClose(jar); |
| return jar; |
| } |
| |
| private void sign(File file, String passphrase) throws Exception { |
| trace("signing %s", file); |
| File asc = new File(file.getParentFile(), file.getName() + ".asc"); |
| asc.delete(); |
| |
| Command command = new Command(); |
| command.setTrace(); |
| |
| command.add(getProperty("gpgp", "gpg")); |
| if (passphrase != null) |
| command.add("--passphrase", passphrase); |
| command.add("-ab", "--sign"); // not the -b!! |
| command.add(file.getAbsolutePath()); |
| System.err.println(command); |
| StringBuilder stdout = new StringBuilder(); |
| StringBuilder stderr = new StringBuilder(); |
| int result = command.execute(stdout, stderr); |
| if (result != 0) { |
| error("gpg signing %s failed because %s", file, "" + stdout + stderr); |
| } |
| } |
| |
| private Jar javadoc(File source, Set<String> exports, Manifest m, Properties p) throws Exception { |
| File tmp = new File(temp, "javadoc"); |
| tmp.mkdirs(); |
| |
| Command command = new Command(); |
| command.add(getProperty("javadoc", "javadoc")); |
| command.add("-quiet"); |
| command.add("-protected"); |
| // command.add("-classpath"); |
| // command.add(binary.getAbsolutePath()); |
| command.add("-d"); |
| command.add(tmp.getAbsolutePath()); |
| command.add("-charset"); |
| command.add("UTF-8"); |
| command.add("-sourcepath"); |
| command.add(source.getAbsolutePath()); |
| |
| Attributes attr = m.getMainAttributes(); |
| Properties pp = new Properties(p); |
| set(pp, "-doctitle", description(attr)); |
| set(pp, "-header", description(attr)); |
| set(pp, "-windowtitle", name(attr)); |
| set(pp, "-bottom", copyright(attr)); |
| set(pp, "-footer", license(attr)); |
| |
| command.add("-tag"); |
| command.add("Immutable:t:Immutable"); |
| command.add("-tag"); |
| command.add("ThreadSafe:t:ThreadSafe"); |
| command.add("-tag"); |
| command.add("NotThreadSafe:t:NotThreadSafe"); |
| command.add("-tag"); |
| command.add("GuardedBy:mf:Guarded By:"); |
| command.add("-tag"); |
| command.add("security:m:Required Permissions"); |
| command.add("-tag"); |
| command.add("noimplement:t:Consumers of this API must not implement this interface"); |
| |
| for (Enumeration< ? > e = pp.propertyNames(); e.hasMoreElements();) { |
| String key = (String) e.nextElement(); |
| String value = pp.getProperty(key); |
| |
| if (key.startsWith("javadoc")) { |
| key = key.substring("javadoc".length()); |
| removeDuplicateMarker(key); |
| command.add(key); |
| command.add(value); |
| } |
| } |
| for (String packageName : exports) { |
| command.add(packageName); |
| } |
| |
| StringBuilder out = new StringBuilder(); |
| StringBuilder err = new StringBuilder(); |
| |
| System.err.println(command); |
| |
| int result = command.execute(out, err); |
| if (result != 0) { |
| warning("Error during execution of javadoc command: %s\n******************\n%s", out, err); |
| } |
| Jar jar = new Jar(tmp); |
| addClose(jar); |
| return jar; |
| } |
| |
| /** |
| * Generate a license string |
| * |
| * @param attr |
| * @return |
| */ |
| private String license(Attributes attr) { |
| Parameters map = Processor.parseHeader(attr.getValue(Constants.BUNDLE_LICENSE), null); |
| if (map.isEmpty()) |
| return null; |
| |
| StringBuilder sb = new StringBuilder(); |
| String sep = "Licensed under "; |
| for (Entry<String,Attrs> entry : map.entrySet()) { |
| sb.append(sep); |
| String key = entry.getKey(); |
| String link = entry.getValue().get("link"); |
| String description = entry.getValue().get("description"); |
| |
| if (description == null) |
| description = key; |
| |
| if (link != null) { |
| sb.append("<a href='"); |
| sb.append(link); |
| sb.append("'>"); |
| } |
| sb.append(description); |
| if (link != null) { |
| sb.append("</a>"); |
| } |
| sep = ",<br/>"; |
| } |
| |
| return sb.toString(); |
| } |
| |
| /** |
| * Generate the copyright statement. |
| * |
| * @param attr |
| * @return |
| */ |
| private String copyright(Attributes attr) { |
| return attr.getValue(Constants.BUNDLE_COPYRIGHT); |
| } |
| |
| private String name(Attributes attr) { |
| String name = attr.getValue(Constants.BUNDLE_NAME); |
| if (name == null) |
| name = attr.getValue(Constants.BUNDLE_SYMBOLICNAME); |
| return name; |
| } |
| |
| private String description(Attributes attr) { |
| String descr = attr.getValue(Constants.BUNDLE_DESCRIPTION); |
| if (descr == null) |
| descr = attr.getValue(Constants.BUNDLE_NAME); |
| if (descr == null) |
| descr = attr.getValue(Constants.BUNDLE_SYMBOLICNAME); |
| return descr; |
| } |
| |
| private void set(Properties pp, String option, String defaultValue) { |
| String key = "javadoc" + option; |
| String existingValue = pp.getProperty(key); |
| if (existingValue != null) |
| return; |
| |
| pp.setProperty(key, defaultValue); |
| } |
| |
| /** |
| * View - Show the dependency details of an artifact |
| */ |
| |
| static Executor executor = Executors.newCachedThreadPool(); |
| static Pattern GROUP_ARTIFACT_VERSION = Pattern.compile("([^+]+)\\+([^+]+)\\+([^+]+)"); |
| |
| void view(String args[], int i) throws Exception { |
| Maven maven = new Maven(executor); |
| OutputStream out = System.err; |
| |
| List<URI> urls = new ArrayList<URI>(); |
| |
| while (i < args.length && args[i].startsWith("-")) { |
| if ("-r".equals(args[i])) { |
| URI uri = new URI(args[++i]); |
| urls.add(uri); |
| System.err.println("URI for repo " + uri); |
| } else if ("-o".equals(args[i])) { |
| out = new FileOutputStream(args[++i]); |
| } else |
| throw new IllegalArgumentException("Unknown option: " + args[i]); |
| |
| i++; |
| } |
| |
| URI[] urls2 = urls.toArray(new URI[urls.size()]); |
| PrintWriter pw = IO.writer(out); |
| |
| while (i < args.length) { |
| String ref = args[i++]; |
| pw.println("Ref " + ref); |
| |
| Matcher matcher = GROUP_ARTIFACT_VERSION.matcher(ref); |
| if (matcher.matches()) { |
| |
| String group = matcher.group(1); |
| String artifact = matcher.group(2); |
| String version = matcher.group(3); |
| CachedPom pom = maven.getPom(group, artifact, version, urls2); |
| |
| Builder a = new Builder(); |
| a.setProperty("Private-Package", "*"); |
| Set<Pom> dependencies = pom.getDependencies(Scope.compile, urls2); |
| for (Pom dep : dependencies) { |
| System.err.printf("%20s %-20s %10s%n", dep.getGroupId(), dep.getArtifactId(), dep.getVersion()); |
| a.addClasspath(dep.getArtifact()); |
| } |
| pw.println(a.getClasspath()); |
| a.build(); |
| |
| TreeSet<PackageRef> sorted = new TreeSet<PackageRef>(a.getImports().keySet()); |
| for (PackageRef p : sorted) { |
| pw.printf("%-40s\n", p); |
| } |
| // for ( Map.Entry<String, Set<String>> entry : |
| // a.getUses().entrySet()) { |
| // String from = entry.getKey(); |
| // for ( String uses : entry.getValue()) { |
| // System.err.printf("%40s %s\n", from, uses); |
| // from = ""; |
| // } |
| // } |
| a.close(); |
| } else |
| System.err.println("Wrong, must look like group+artifact+version, is " + ref); |
| |
| } |
| } |
| |
| } |