package com.redhat.ceylon.tools.p2; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.UUID; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import com.redhat.ceylon.cmr.api.ArtifactContext; import com.redhat.ceylon.cmr.api.ModuleQuery; import com.redhat.ceylon.cmr.api.ModuleVersionDetails; import com.redhat.ceylon.cmr.api.RepositoryManager; import com.redhat.ceylon.cmr.ceylon.OutputRepoUsingTool; import com.redhat.ceylon.cmr.impl.IOUtils; import com.redhat.ceylon.cmr.impl.ShaSigner; import com.redhat.ceylon.common.log.Logger; import com.redhat.ceylon.common.tool.Argument; import com.redhat.ceylon.common.tool.Description; import com.redhat.ceylon.common.tool.Hidden; import com.redhat.ceylon.common.tool.OptionArgument; import com.redhat.ceylon.common.tool.Summary; import com.redhat.ceylon.common.tools.CeylonTool; import com.redhat.ceylon.common.tools.ModuleSpec; import com.redhat.ceylon.model.cmr.ArtifactResult; import com.redhat.ceylon.model.cmr.JDKUtils; @Summary("Generates p2 repository metadata suitable for Eclipse") @Description("This is EXPERIMENTAL" + "\n\n" + "Given a list of modules, and optionally a category file and prefix, this tool " + "generates `content.xml` and `artifacts.xml` files in the output directory, and " + "packs the folders in `${output}/features/*` into jars.") @Hidden public class CeylonP2Tool extends OutputRepoUsingTool { private static final String KNOWN_EXPRESSION_TYPE = "providedCapabilities.exists(p | p.namespace == 'osgi.bundle')"; private List<ModuleSpec> modules; private File categories; private String categoryPrefix; private String repositoryName = "P2 repository"; public CeylonP2Tool() { super(CeylonP2Messages.RESOURCE_BUNDLE); } @Argument(argumentName="module", multiplicity="+") public void setModules(List<String> modules) { setModuleSpecs(ModuleSpec.parseEachList(modules)); } public void setModuleSpecs(List<ModuleSpec> modules) { this.modules = modules; } @OptionArgument(argumentName="categories") @Description("Specify a `categories.xml` file to be used for the list of categories") public void setCategories(File categories) { this.categories = categories; } @OptionArgument(argumentName="category-prefix") @Description("Specify the prefix for categories, for example if you have a category "+ "named 'x' and a prefix of 'com.foo.bar' we will generate a unit named "+ "'com.foo.bar.x' for your category") public void setCategoryPrefix(String categoryPrefix) { this.categoryPrefix = categoryPrefix; } @OptionArgument(argumentName="repository-name") @Description("Specify an output repository name") public void setRepositoryName(String repositoryName) { this.repositoryName = repositoryName; } @Override protected boolean includeJDK() { return true; } @Override protected boolean needsSystemRepo() { return false; } @Override public void initialize(CeylonTool mainTool) { } @Override public void run() throws Exception { RepositoryManager repoManager = getRepositoryManager(); Map<String, ModuleInfo> allModules = new HashMap<>(); for (ModuleSpec module : modules) { String version = findModuleVersion(module); msg("collecting.modules", module.toString()); newline(); collectModules(repoManager, module.getName(), version, allModules); } // now purge empty modules purgeMissingModules(allModules); Map<String, Feature> features = collectFeatures(); Map<String, Category> categoriesByName = null; if(categories != null){ categoriesByName = parseCategories(features); } msg("generating.artifacts"); newline(); printArtifacts(allModules, features); msg("generating.content"); newline(); printContent(allModules, features, categoriesByName); } private String findModuleVersion(ModuleSpec module) throws IOException { if(module.isVersioned()) return module.getVersion(); /* * FIXME: we should look in the compiled repo as well, except we set our output to the output p2 distrib * and the car repo is in a normal "rep" so we can't look in the output repo. * Another thing that doesn't work is that the list of dependencies would include transitive ones like * the distrib, even for the SDK where we only want the SDK and its dependencies that are not in the distrib, * so we use the same trick as the build does for ceylon-copy to put cars in osgi/dist/plugins/ which is * offline and no-default-repos on invocation. * A better strategy would be to not start from a module list and just scan osgi/dist/plugins jars and skip * the CMR entirely, but in the future I suspect we want this tool to also do the copying step and stuff * so let's leave it like this for now. */ // try from source first ModuleVersionDetails fromSource = getVersionFromSource(module.getName()); if(fromSource != null){ // is it the version we're after? return fromSource.getVersion(); }else{ Collection<ModuleVersionDetails> versions = getModuleVersions(getRepositoryManager(), module.getName(), module.getVersion(), ModuleQuery.Type.JVM, null, null); if (versions.isEmpty()) { String err = getModuleNotFoundErrorMessage(getRepositoryManager(), module.getName(), module.getVersion()); throw new CeylonP2ToolException(err); } return versions.iterator().next().getVersion(); } } private void purgeMissingModules(Map<String, ModuleInfo> allModules) { Set<String> toClean = new HashSet<String>(); for(Entry<String, ModuleInfo> entry : allModules.entrySet()){ if(entry.getValue() == null) toClean.add(entry.getKey()); } for(String key : toClean) allModules.remove(key); } private Map<String, Category> parseCategories(Map<String, Feature> features) throws SAXException, IOException, ParserConfigurationException { msg("parsing.categories", categories.getPath()); newline(); DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document document = builder.parse(categories); Element root = document.getDocumentElement(); NodeList categories = root.getElementsByTagName("category-def"); Map<String, Category> categoriesByName = new HashMap<>(); for(int i=0;i<categories.getLength();i++){ Element category = (Element) categories.item(i); String name = category.getAttribute("name"); String label = category.getAttribute("label"); String description = getContent(category, "description"); categoriesByName.put(name, new Category(name, label, description)); msg("category.found", name); newline(); } NodeList featureList = root.getElementsByTagName("feature"); for(int i=0;i<featureList.getLength();i++){ Element feature = (Element) featureList.item(i); String featureName = feature.getAttribute("id"); NodeList categoryList = feature.getElementsByTagName("category"); Element categoryElem = (Element) categoryList.item(0); String categoryName = categoryElem.getAttribute("name"); Category category = categoriesByName.get(categoryName); category.setFeature(features.get(featureName)); } NodeList iuList = root.getElementsByTagName("iu"); for(int i=0;i<iuList.getLength();i++){ Element iu = (Element) iuList.item(i); NodeList categoryList = iu.getElementsByTagName("category"); Element categoryElem = (Element) categoryList.item(0); String categoryName = categoryElem.getAttribute("name"); Category category = categoriesByName.get(categoryName); NodeList queryList = iu.getElementsByTagName("query"); if(queryList.getLength() != 1) throw new CeylonP2ToolException("Don't know how to handle iu element with no query or more than one in category file"); Element queryElem = (Element) queryList.item(0); String expression = getContent(queryElem, "expression"); if(expression == null) throw new CeylonP2ToolException("Don't know how to handle iu element with no query/expression or more than one in category file"); if(!expression.equals(KNOWN_EXPRESSION_TYPE)) throw new CeylonP2ToolException("Don't know how to handle iu element with query/expression of: '"+expression+ "'. We only know how to handle: '"+KNOWN_EXPRESSION_TYPE+"'."); category.setAllJars(); } return categoriesByName; } private Map<String,Feature> collectFeatures() throws ParserConfigurationException, SAXException, IOException { File featuresDir = new File(out, "features"); msg("collecting.features", featuresDir.getPath()); newline(); Map<String,Feature> ret = new HashMap<>(); if(!featuresDir.exists()) return ret; for(File feature : featuresDir.listFiles()){ if(feature.isDirectory()){ collectFeature(ret, feature); } } return ret; } static String getContent(Element root, String name) { NodeList nodes = root.getElementsByTagName(name); if(nodes.getLength() != 1) return null; Node item = nodes.item(0); return item.getTextContent().trim(); } private void collectFeature(Map<String, Feature> ret, File feature) throws ParserConfigurationException, SAXException, IOException { File manifest = new File(feature, "feature.xml"); DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document document = builder.parse(manifest); Element root = document.getDocumentElement(); File jar = new File(feature.getParentFile(), feature.getName()+".jar"); if(jar.exists()) jar.delete(); File zipFolder = IOUtils.zipFolder(feature); Files.copy(zipFolder.toPath(), jar.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); zipFolder.delete(); Feature f = new Feature(root.getAttribute("id"), root.getAttribute("version"), alignOn4k(manifest.length()), jar.length(), root); ret.put(f.name, f); msg("feature.found", f.name, feature.getPath()); newline(); msg("feature.zipped", f.name, jar.getPath()); newline(); } private long alignOn4k(long length) { return ((long)Math.ceil(length / 4096)) * 4096; } @Override protected Logger createLogger() { return new Logger(){ @Override public void error(String str) { print("[ERROR]: "+str); } private void print(String string) { if(verbose == null) return; try { errorAppend(string); errorNewline(); } catch (IOException e) { throw new RuntimeException(e); } } @Override public void warning(String str) { print("[WARNING]: "+str); } @Override public void info(String str) { print("[INFO]: "+str); } @Override public void debug(String str) { print("[DEBUG]: "+str); } }; } private void printContent(Map<String, ModuleInfo> allModules, Map<String, Feature> features, Map<String, Category> categoriesByName) throws XMLStreamException, IOException{ File outFile = new File(out, "content.xml"); Writer fileWriter = new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"); XMLStreamWriter stupidWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(fileWriter); XMLStreamWriter writer = new IndentingWriter(stupidWriter); writer.writeStartDocument("UTF-8", "1.0"); writer.writeProcessingInstruction("metadataRepository", "version='1.1.0'"); // FIXME: is this supposed to be configurable? writer.writeStartElement("repository"); writer.writeAttribute("name", repositoryName); writer.writeAttribute("type", "org.eclipse.equinox.internal.p2.metadata.repository.LocalMetadataRepository"); writer.writeAttribute("version", "1.0.0"); commonProperties(writer); { writer.writeStartElement("units"); writer.writeAttribute("size", String.valueOf(allModules.size()+(features.size()*2)+categoriesByName.size())); for(Map.Entry<String, ModuleInfo> entries : allModules.entrySet()){ ModuleInfo moduleInfo = entries.getValue(); printUnit(writer, moduleInfo); } for(Feature feature : features.values()){ printUnitFeature(writer, feature, true); printUnitFeature(writer, feature, false); } for(Category category : categoriesByName.values()){ printUnitCategory(writer, category, allModules.values()); } writer.writeEndElement(); } writer.writeEndElement(); writer.writeEndDocument(); writer.flush(); writer.close(); } private void printUnitCategory(XMLStreamWriter writer, Category category, Collection<ModuleInfo> modules) throws XMLStreamException { writer.writeStartElement("unit"); String name = categoryPrefix + "." + category.name; // the real algo is too weird to reproduce String versionQualifier = UUID.randomUUID().toString(); String version = "1.0.0."+versionQualifier; writer.writeAttribute("id", name); writer.writeAttribute("version", version); { writer.writeStartElement("properties"); writer.writeAttribute("size", "3"); { writer.writeEmptyElement("property"); writer.writeAttribute("name", "org.eclipse.equinox.p2.name"); writer.writeAttribute("value", category.label); } { writer.writeEmptyElement("property"); writer.writeAttribute("name", "org.eclipse.equinox.p2.description"); writer.writeAttribute("value", category.description); } { writer.writeEmptyElement("property"); writer.writeAttribute("name", "org.eclipse.equinox.p2.type.category"); writer.writeAttribute("value", "true"); } writer.writeEndElement(); } { writer.writeStartElement("provides"); writer.writeAttribute("size", "1"); { writer.writeEmptyElement("provided"); writer.writeAttribute("namespace", "org.eclipse.equinox.p2.iu"); writer.writeAttribute("name", name); writer.writeAttribute("version", version); } writer.writeEndElement(); } { writer.writeStartElement("requires"); if(category.feature != null){ writer.writeAttribute("size", "1"); { writer.writeEmptyElement("required"); writer.writeAttribute("namespace", "org.eclipse.equinox.p2.iu"); writer.writeAttribute("name", category.feature.name+".feature.group"); writer.writeAttribute("range", "["+category.feature.version+","+category.feature.version+"]"); } }else if(category.allJars){ writer.writeAttribute("size", String.valueOf(modules.size())); for(ModuleInfo mod : modules){ writer.writeEmptyElement("required"); writer.writeAttribute("namespace", "org.eclipse.equinox.p2.iu"); writer.writeAttribute("name", mod.name); writer.writeAttribute("range", "["+mod.osgiVersion+","+mod.osgiVersion+"]"); } } writer.writeEndElement(); } { writer.writeEmptyElement("touchpoint"); writer.writeAttribute("id", "null"); writer.writeAttribute("version", "0.0.0"); } writer.writeEndElement(); } private void printUnit(XMLStreamWriter writer, ModuleInfo moduleInfo) throws XMLStreamException { writer.writeStartElement("unit"); writer.writeAttribute("id", moduleInfo.name); writer.writeAttribute("version", moduleInfo.osgiVersion); writer.writeAttribute("singleton", "false"); { writer.writeEmptyElement("update"); writer.writeAttribute("id", moduleInfo.name); writer.writeAttribute("range", "[0.0.0,"+moduleInfo.osgiVersion+")"); writer.writeAttribute("severity", "0"); } { writer.writeStartElement("properties"); SortedMap<String,String> properties = moduleInfo.getOsgiProperties(); writer.writeAttribute("size", String.valueOf(properties.size())); for(Map.Entry<String,String> property : properties.entrySet()){ writer.writeEmptyElement("property"); writer.writeAttribute("name", getUnitPropertyName(property.getKey())); writer.writeAttribute("value", property.getValue()); } writer.writeEndElement(); } { writer.writeStartElement("provides"); List<ModuleSpec> exportedPackages = moduleInfo.getExportedPackages(); writer.writeAttribute("size", String.valueOf(3 + exportedPackages.size())); { writer.writeEmptyElement("provided"); writer.writeAttribute("namespace", "org.eclipse.equinox.p2.iu"); writer.writeAttribute("name", moduleInfo.name); writer.writeAttribute("version", moduleInfo.osgiVersion); } { writer.writeEmptyElement("provided"); writer.writeAttribute("namespace", "osgi.bundle"); writer.writeAttribute("name", moduleInfo.name); writer.writeAttribute("version", moduleInfo.osgiVersion); } for(ModuleSpec pkg : exportedPackages){ writer.writeEmptyElement("provided"); writer.writeAttribute("namespace", "java.package"); writer.writeAttribute("name", pkg.getName()); writer.writeAttribute("version", pkg.isVersioned() ? pkg.getVersion() : "0.0.0"); } { writer.writeEmptyElement("provided"); writer.writeAttribute("namespace", "org.eclipse.equinox.p2.eclipse.type"); writer.writeAttribute("name", "bundle"); writer.writeAttribute("version", "1.0.0"); } writer.writeEndElement(); } { writer.writeStartElement("requires"); List<ModuleSpec> importedPackages = moduleInfo.getImportedPackages(); List<ModuleSpec> importedModules = moduleInfo.getImportedModules(); writer.writeAttribute("size", String.valueOf(importedModules.size() + importedPackages.size())); for(ModuleSpec mod : importedModules){ writer.writeEmptyElement("required"); writer.writeAttribute("namespace", "osgi.bundle"); writer.writeAttribute("name", mod.getName()); writer.writeAttribute("range", mod.getVersion()); } for(ModuleSpec pkg : importedPackages){ writer.writeEmptyElement("required"); writer.writeAttribute("namespace", "java.package"); writer.writeAttribute("name", pkg.getName()); writer.writeAttribute("range", pkg.isVersioned() ? pkg.getVersion() : "0.0.0"); } writer.writeEndElement(); } { writer.writeStartElement("artifacts"); writer.writeAttribute("size", "1"); { writer.writeEmptyElement("artifact"); writer.writeAttribute("classifier", "osgi.bundle"); writer.writeAttribute("id", moduleInfo.name); writer.writeAttribute("version", moduleInfo.osgiVersion); } writer.writeEndElement(); } { writer.writeEmptyElement("touchpoint"); writer.writeAttribute("id", "org.eclipse.equinox.p2.osgi"); writer.writeAttribute("version", "1.0.0"); } { writer.writeStartElement("touchpointData"); writer.writeAttribute("size", "1"); { writer.writeStartElement("instructions"); writer.writeAttribute("size", "2"); { writer.writeStartElement("instruction"); writer.writeAttribute("key", "manifest"); writer.writeCharacters("Bundle-SymbolicName: "+moduleInfo.getAttribute("Bundle-SymbolicName") +"\nBundle-Version: "+moduleInfo.getAttribute("Bundle-Version")); writer.writeEndElement(); } { writer.writeStartElement("instruction"); writer.writeAttribute("key", "zipped"); writer.writeCharacters("true"); writer.writeEndElement(); } writer.writeEndElement(); } writer.writeEndElement(); } writer.writeEndElement(); } private void printUnitFeature(XMLStreamWriter writer, Feature feature, boolean group) throws XMLStreamException { writer.writeStartElement("unit"); String name = feature.name+(group ? ".feature.group" : ".feature.jar"); writer.writeAttribute("id", name); writer.writeAttribute("version", feature.version); if(group){ writer.writeAttribute("singleton", "false"); { writer.writeEmptyElement("update"); writer.writeAttribute("id", name); writer.writeAttribute("range", "[0.0.0,"+feature.version+")"); writer.writeAttribute("severity", "0"); } } { writer.writeStartElement("properties"); SortedMap<String,String> properties = feature.getProperties(); writer.writeAttribute("size", String.valueOf(properties.size()+(group?1:0))); for(Map.Entry<String,String> property : properties.entrySet()){ writer.writeEmptyElement("property"); writer.writeAttribute("name", property.getKey()); writer.writeAttribute("value", property.getValue()); } if(group){ writer.writeEmptyElement("property"); writer.writeAttribute("name", "org.eclipse.equinox.p2.type.group"); writer.writeAttribute("value", "true"); } writer.writeEndElement(); } { writer.writeStartElement("provides"); writer.writeAttribute("size", group ? "1" : "3"); { writer.writeEmptyElement("provided"); writer.writeAttribute("namespace", "org.eclipse.equinox.p2.iu"); writer.writeAttribute("name", name); writer.writeAttribute("version", feature.version); } if(!group){ { writer.writeEmptyElement("provided"); writer.writeAttribute("namespace", "org.eclipse.equinox.p2.eclipse.type"); writer.writeAttribute("name", "feature"); writer.writeAttribute("version", "1.0.0"); } { writer.writeEmptyElement("provided"); writer.writeAttribute("namespace", "org.eclipse.update.feature"); writer.writeAttribute("name", feature.name); writer.writeAttribute("version", feature.version); } } writer.writeEndElement(); } if(group){ writer.writeStartElement("requires"); List<ModuleSpec> importedModules = feature.getImportedModules(); writer.writeAttribute("size", String.valueOf(importedModules.size()+1)); for(ModuleSpec mod : importedModules){ writer.writeEmptyElement("required"); writer.writeAttribute("namespace", "org.eclipse.equinox.p2.iu"); writer.writeAttribute("name", mod.getName()); writer.writeAttribute("range", "["+mod.getVersion()+","+mod.getVersion()+"]"); } { writer.writeStartElement("required"); writer.writeAttribute("namespace", "org.eclipse.equinox.p2.iu"); writer.writeAttribute("name", feature.name+".feature.jar"); writer.writeAttribute("range", "["+feature.version+","+feature.version+"]"); { writer.writeStartElement("filter"); writer.writeCharacters("(org.eclipse.update.install.features=true)"); writer.writeEndElement(); } writer.writeEndElement(); } writer.writeEndElement(); }else{ writer.writeStartElement("filter"); writer.writeCharacters("(org.eclipse.update.install.features=true)"); writer.writeEndElement(); { writer.writeStartElement("artifacts"); writer.writeAttribute("size", "1"); { writer.writeEmptyElement("artifact"); writer.writeAttribute("classifier", "org.eclipse.update.feature"); writer.writeAttribute("id", feature.name); writer.writeAttribute("version", feature.version); } writer.writeEndElement(); } } { writer.writeEmptyElement("touchpoint"); writer.writeAttribute("id", group ? "null" : "org.eclipse.equinox.p2.osgi"); writer.writeAttribute("version", group ? "0.0.0" : "1.0.0"); } if(!group){ writer.writeStartElement("touchpointData"); writer.writeAttribute("size", "1"); { writer.writeStartElement("instructions"); writer.writeAttribute("size", "1"); { writer.writeStartElement("instruction"); writer.writeAttribute("key", "zipped"); writer.writeCharacters("true"); writer.writeEndElement(); } writer.writeEndElement(); } writer.writeEndElement(); } { writer.writeStartElement("licenses"); writer.writeAttribute("size", "1"); { writer.writeStartElement("license"); writer.writeAttribute("uri", ""); writer.writeAttribute("url", ""); writer.writeCharacters(feature.getLicense()); writer.writeEndElement(); } writer.writeEndElement(); } { writer.writeStartElement("copyright"); writer.writeCharacters(feature.getCopyright()); writer.writeEndElement(); } writer.writeEndElement(); } private String getUnitPropertyName(String key) { switch(key){ case "Bundle-Name": return "org.eclipse.equinox.p2.name"; case "Bundle-Description": return "org.eclipse.equinox.p2.description"; case "Bundle-DocUrl": return "org.eclipse.equinox.p2.doc.url"; case "Bundle-Vendor": return "org.eclipse.equinox.p2.provider"; } return key; } private void printArtifacts(Map<String, ModuleInfo> allModules, Map<String, Feature> features) throws IOException, XMLStreamException, FactoryConfigurationError { File outFile = new File(out, "artifacts.xml"); Writer fileWriter = new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"); XMLStreamWriter stupidWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(fileWriter); XMLStreamWriter writer = new IndentingWriter(stupidWriter); writer.writeStartDocument("UTF-8", "1.0"); writer.writeProcessingInstruction("artifactRepository", "version='1.1.0'"); // FIXME: is this supposed to be configurable? writer.writeStartElement("repository"); writer.writeAttribute("name", repositoryName); writer.writeAttribute("type", "org.eclipse.equinox.p2.artifact.repository.simpleRepository"); writer.writeAttribute("version", "1"); commonProperties(writer); { writer.writeStartElement("mappings"); writer.writeAttribute("size", "3"); { writer.writeEmptyElement("rule"); writer.writeAttribute("filter", "(& (classifier=osgi.bundle))"); writer.writeAttribute("output", "${repoUrl}/plugins/${id}_${version}.jar"); } { writer.writeEmptyElement("rule"); writer.writeAttribute("filter", "(& (classifier=binary))"); writer.writeAttribute("output", "${repoUrl}/binary/${id}_${version}"); } { writer.writeEmptyElement("rule"); writer.writeAttribute("filter", "(& (classifier=org.eclipse.update.feature))"); writer.writeAttribute("output", "${repoUrl}/features/${id}_${version}.jar"); } writer.writeEndElement(); } { writer.writeStartElement("artifacts"); writer.writeAttribute("size", String.valueOf(allModules.size()+features.size())); for(Map.Entry<String, ModuleInfo> entries : allModules.entrySet()){ ModuleInfo moduleInfo = entries.getValue(); // skip missing modules if(moduleInfo == null) continue; String version = moduleInfo.osgiVersion; writer.writeStartElement("artifact"); writer.writeAttribute("classifier", "osgi.bundle"); writer.writeAttribute("id", moduleInfo.name); writer.writeAttribute("version", version); { writer.writeStartElement("properties"); writer.writeAttribute("size", "3"); { writer.writeEmptyElement("property"); writer.writeAttribute("name", "artifact.size"); writer.writeAttribute("value", String.valueOf(moduleInfo.jar.length())); } { writer.writeEmptyElement("property"); writer.writeAttribute("name", "download.size"); writer.writeAttribute("value", String.valueOf(moduleInfo.jar.length())); } { writer.writeEmptyElement("property"); writer.writeAttribute("name", "download.md5"); writer.writeAttribute("value", ShaSigner.md5(moduleInfo.jar)); } writer.writeEndElement(); } writer.writeEndElement(); } for(Feature feature : features.values()){ writer.writeStartElement("artifact"); writer.writeAttribute("classifier", "org.eclipse.update.feature"); writer.writeAttribute("id", feature.name); writer.writeAttribute("version", feature.version); { writer.writeStartElement("properties"); writer.writeAttribute("size", "3"); { writer.writeEmptyElement("property"); writer.writeAttribute("name", "artifact.size"); writer.writeAttribute("value", String.valueOf(feature.manifestSize)); } { writer.writeEmptyElement("property"); writer.writeAttribute("name", "download.size"); writer.writeAttribute("value", String.valueOf(feature.jarSize)); } { writer.writeEmptyElement("property"); writer.writeAttribute("name", "download.contentType"); writer.writeAttribute("value", "application/zip"); } writer.writeEndElement(); } writer.writeEndElement(); } writer.writeEndElement(); } writer.writeEndElement(); writer.writeEndDocument(); writer.flush(); writer.close(); } private void commonProperties(XMLStreamWriter writer) throws XMLStreamException { writer.writeStartElement("properties"); writer.writeAttribute("size", "2"); { writer.writeEmptyElement("property"); writer.writeAttribute("name", "p2.timestamp"); writer.writeAttribute("value", String.valueOf(new Date().getTime())); } { writer.writeEmptyElement("property"); writer.writeAttribute("name", "p2.compressed"); writer.writeAttribute("value", "false"); } writer.writeEndElement(); } static String fixOsgiVersion(String version) { int firstDot = version.indexOf('.'); if(firstDot == -1) return version + ".0.0"; int secondDot = version.indexOf('.', firstDot+1); if(secondDot == -1) return version + ".0"; return version; } private void collectModules(RepositoryManager repoManager, String name, String version, Map<String, ModuleInfo> allModules) throws IOException { // ignore JDK dependencies if(CeylonP2Tool.skipModule(name)) return; String key = name+"/"+version; if(allModules.containsKey(key)) return; ArtifactResult artifact = repoManager.getArtifactResult(new ArtifactContext(name, version, ArtifactContext.CAR, ArtifactContext.JAR)); File artifactJar = null; if(artifact == null){ // try to find it in the plugins folder File pluginJar = new File(out, "plugins/"+name+"_"+version+".jar"); if(pluginJar.exists()){ artifactJar = pluginJar; } }else{ artifactJar = artifact.artifact(); } allModules.put(key, artifactJar != null ? new ModuleInfo(name, version, artifactJar) : null); if(artifact == null){ errorMsg("module.not.found", name+"/"+version, out+"/plugins"); }else{ msg("module.found", name+"/"+version, artifact != null ? artifact.repositoryDisplayString() : artifactJar.getPath()); newline(); for(ArtifactResult dep : artifact.dependencies()){ // FIXME: deal with optionals collectModules(repoManager, dep.name(), dep.version(), allModules); } } } public static boolean skipModule(String name) { return JDKUtils.isJDKModule(name) || JDKUtils.isOracleJDKModule(name) // this one is "provided" || "org.osgi.core".equals(name); } }