Update to latest bndlib. (FELIX-2176)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1027238 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java b/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
index cb990a1..11c8863 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
@@ -3,1132 +3,1113 @@
import java.io.*;
import java.util.*;
import java.util.jar.*;
-import java.util.regex.*;
import java.util.zip.*;
import aQute.bnd.make.*;
+import aQute.bnd.maven.*;
import aQute.bnd.service.*;
/**
* Include-Resource: ( [name '=' ] file )+
- *
+ *
* Private-Package: package-decl ( ',' package-decl )*
- *
+ *
* Export-Package: package-decl ( ',' package-decl )*
- *
+ *
* Import-Package: package-decl ( ',' package-decl )*
- *
- * @version $Revision: 1.20 $
+ *
+ * @version $Revision: 1.27 $
*/
public class Builder extends Analyzer {
- private static final int SPLIT_MERGE_LAST = 1;
- private static final int SPLIT_MERGE_FIRST = 2;
- private static final int SPLIT_ERROR = 3;
- private static final int SPLIT_FIRST = 4;
- private static final int SPLIT_DEFAULT = 0;
+ private static final int SPLIT_MERGE_LAST = 1;
+ private static final int SPLIT_MERGE_FIRST = 2;
+ private static final int SPLIT_ERROR = 3;
+ private static final int SPLIT_FIRST = 4;
+ private static final int SPLIT_DEFAULT = 0;
- private static final File[] EMPTY_FILE = new File[0];
+ List<File> sourcePath = new ArrayList<File>();
- List<File> sourcePath = new ArrayList<File>();
- Pattern NAME_URL = Pattern
- .compile("(.*)(http://.*)");
+ Make make = new Make(this);
- Make make = new Make(this);
+ public Builder(Processor parent) {
+ super(parent);
+ }
- public Builder(Processor parent) {
- super(parent);
- }
+ public Builder() {
+ }
- public Builder() {
+ public Map<String, Clazz> analyzeBundleClasspath(Jar dot,
+ Map<String, Map<String, String>> bundleClasspath,
+ Map<String, Map<String, String>> contained,
+ Map<String, Map<String, String>> referred,
+ Map<String, Set<String>> uses) throws IOException {
+ return super.analyzeBundleClasspath(dot, bundleClasspath, contained, referred, uses);
}
public Jar build() throws Exception {
- if (getProperty(NOPE) != null)
- return null;
+ init();
+ if (isTrue(getProperty(NOBUNDLES)))
+ return null;
- String sub = getProperty(SUB);
- if (sub != null && sub.trim().length() > 0)
- error("Specified "
- + SUB
- + " but calls build() instead of builds() (might be a programmer error)");
+ if (getProperty(CONDUIT) != null)
+ error("Specified " + CONDUIT
+ + " but calls build() instead of builds() (might be a programmer error");
- if (getProperty(CONDUIT) != null)
- error("Specified "
- + CONDUIT
- + " but calls build() instead of builds() (might be a programmer error");
+ dot = new Jar("dot");
+ addClose(dot);
+ try {
+ long modified = Long.parseLong(getProperty("base.modified"));
+ dot.updateModified(modified, "Base modified");
+ } catch (Exception e) {
+ }
- dot = new Jar("dot");
- addClose(dot);
- try {
- long modified = Long.parseLong(getProperty("base.modified"));
- dot.updateModified(modified, "Base modified");
- } catch (Exception e) {
- }
+ doExpand(dot);
+ doIncludeResources(dot);
+ doConditional(dot);
+ dot = doWab(dot);
- doExpand(dot);
- doIncludeResources(dot);
+ // NEW!
+ // Check if we override the calculation of the
+ // manifest. We still need to calculated it because
+ // we need to have analyzed the classpath.
- doConditional(dot);
+ Manifest manifest = calcManifest();
- // NEW!
- // Check if we override the calculation of the
- // manifest. We still need to calculated it because
- // we need to have analyzed the classpath.
+ String mf = getProperty(MANIFEST);
+ if (mf != null) {
+ File mff = getFile(mf);
+ if (mff.isFile()) {
+ try {
+ InputStream in = new FileInputStream(mff);
+ manifest = new Manifest(in);
+ in.close();
+ } catch (Exception e) {
+ error(MANIFEST + " while reading manifest file", e);
+ }
+ } else {
+ error(MANIFEST + ", no such file " + mf);
+ }
+ }
- Manifest manifest = calcManifest();
+ if (getProperty(NOMANIFEST) == null)
+ dot.setManifest(manifest);
+ else
+ dot.setDoNotTouchManifest();
- String mf = getProperty(MANIFEST);
- if (mf != null) {
- File mff = getFile(mf);
- if (mff.isFile()) {
- try {
- InputStream in = new FileInputStream(mff);
- manifest = new Manifest(in);
- in.close();
- } catch (Exception e) {
- error(MANIFEST + " while reading manifest file", e);
- }
- } else {
- error(MANIFEST + ", no such file " + mf);
- }
- }
+ // This must happen after we analyzed so
+ // we know what it is on the classpath
+ addSources(dot);
- if (getProperty(NOMANIFEST) == null)
- dot.setManifest(manifest);
- else
- dot.setNoManifest(true);
+ if (getProperty(POM) != null)
+ dot.putResource("pom.xml", new PomResource(dot.getManifest()));
- // This must happen after we analyzed so
- // we know what it is on the classpath
- addSources(dot);
- if (getProperty(POM) != null)
- doPom(dot);
+ if (!isNoBundle())
+ doVerify(dot);
- doVerify(dot);
+ if (dot.getResources().isEmpty())
+ error("The JAR is empty: " + dot.getName());
- if (dot.getResources().isEmpty())
- error("The JAR is empty");
+ dot.updateModified(lastModified(), "Last Modified Processor");
+ dot.setName(getBsn());
- dot.updateModified(lastModified(), "Last Modified Processor");
- dot.setName(getBsn());
+ sign(dot);
- sign(dot);
- return dot;
- }
+ doSaveManifest(dot);
+ return dot;
+ }
- /**
- * Sign the jar file.
- *
- * -sign : <alias> [ ';' 'password:=' <password> ] [ ';' 'keystore:='
- * <keystore> ] [ ';' 'sign-password:=' <pw> ] ( ',' ... )*
- *
- * @return
+ /**
+ * Allow any local initialization by subclasses before we build. Default is
+ * do nothing.
+ */
+ public void init() throws Exception {
+
+ }
+
+ /**
+ * Turn this normal bundle in a web and add any resources.
+ *
+ * @throws Exception
+ */
+ private Jar doWab(Jar dot) throws Exception {
+ String wab = getProperty(WAB);
+ String wablib = getProperty(WABLIB);
+ if (wab == null && wablib == null)
+ return dot;
+
+ setProperty(BUNDLE_CLASSPATH, append("WEB-INF/classes", getProperty(BUNDLE_CLASSPATH)));
+
+ Jar next = new Jar(dot.getName());
+ addClose(next);
+
+ for (Map.Entry<String, Resource> entry : dot.getResources().entrySet()) {
+ String path = entry.getKey();
+ if (path.indexOf('/') > 0 && !Character.isUpperCase(path.charAt(0))) {
+ trace("wab: moving: %s", path);
+ next.putResource("WEB-INF/classes/" + path, entry.getValue());
+ } else {
+ trace("wab: not moving: %s", path);
+ next.putResource(path, entry.getValue());
+ }
+ }
+
+ Map<String, Map<String, String>> clauses = parseHeader(getProperty(WABLIB));
+ for (String key : clauses.keySet()) {
+ File f = getFile(key);
+ addWabLib(next, f);
+ }
+ doIncludeResource(next, wab);
+ return next;
+ }
+
+ /**
+ * Add a wab lib to the jar.
+ *
+ * @param f
+ */
+ private void addWabLib(Jar dot, File f) throws Exception {
+ if (f.exists()) {
+ Jar jar = new Jar(f);
+ jar.setDoNotTouchManifest();
+ addClose(jar);
+ String path = "WEB-INF/lib/" + f.getName();
+ dot.putResource(path, new JarResource(jar));
+ setProperty(BUNDLE_CLASSPATH, append(getProperty(BUNDLE_CLASSPATH), path));
+
+ Manifest m = jar.getManifest();
+ String cp = m.getMainAttributes().getValue("Class-Path");
+ if (cp != null) {
+ Collection<String> parts = split(cp, ",");
+ for (String part : parts) {
+ File sub = getFile(f.getParentFile(), part);
+ if (!sub.exists() || !sub.getParentFile().equals(f.getParentFile())) {
+ warning(
+ "Invalid Class-Path entry %s in %s, must exist and must reside in same directory",
+ sub, f);
+ } else {
+ addWabLib(dot, sub);
+ }
+ }
+ }
+ } else {
+ error("WAB lib does not exist %s", f);
+ }
+ }
+
+ /**
+ * Get the manifest and write it out separately if -savemanifest is set
+ *
+ * @param dot
+ */
+ private void doSaveManifest(Jar dot) throws IOException {
+ String output = getProperty(SAVEMANIFEST);
+ if (output == null)
+ return;
+
+ File f = getFile(output);
+ if (f.isDirectory()) {
+ f = new File(f, "MANIFEST.MF");
+ }
+ f.delete();
+ f.getParentFile().mkdirs();
+ OutputStream out = new FileOutputStream(f);
+ try {
+ Jar.writeManifest(dot.getManifest(), out);
+ } finally {
+ out.close();
+ }
+ changedFile(f);
+ }
+
+ protected void changedFile(File f) {
+ }
+
+ /**
+ * Sign the jar file.
+ *
+ * -sign : <alias> [ ';' 'password:=' <password> ] [ ';' 'keystore:='
+ * <keystore> ] [ ';' 'sign-password:=' <pw> ] ( ',' ... )*
+ *
+ * @return
+ */
+
+ void sign(Jar jar) throws Exception {
+ String signing = getProperty("-sign");
+ if (signing == null)
+ return;
+
+ trace("Signing %s, with %s", getBsn(), signing);
+ List<SignerPlugin> signers = getPlugins(SignerPlugin.class);
+
+ Map<String, Map<String, String>> infos = parseHeader(signing);
+ for (Map.Entry<String, Map<String, String>> entry : infos.entrySet()) {
+ for (SignerPlugin signer : signers) {
+ signer.sign(this, entry.getKey());
+ }
+ }
+ }
+
+ public boolean hasSources() {
+ return isTrue(getProperty(SOURCES));
+ }
+
+ protected String getImportPackages() {
+ String ip = super.getImportPackages();
+ if (ip != null)
+ return ip;
+
+ return "*";
+ }
+
+ private void doConditional(Jar dot) throws Exception {
+ Map<String, Map<String, String>> conditionals = getHeader(CONDITIONAL_PACKAGE);
+ if (conditionals.isEmpty())
+ return;
+
+ int size;
+ do {
+ size = dot.getDirectories().size();
+ analyze();
+ analyzed = false;
+ Map<String, Map<String, String>> imports = getImports();
+
+ // Match the packages specified in conditionals
+ // against the imports. Any match must become a
+ // Private-Package
+ Map<String, Map<String, String>> filtered = merge(CONDITIONAL_PACKAGE, conditionals,
+ imports, new HashSet<String>(), null);
+
+ // Imports can also specify a private import. These
+ // packages must also be copied to the bundle
+ for (Map.Entry<String, Map<String, String>> entry : getImports().entrySet()) {
+ String type = entry.getValue().get(IMPORT_DIRECTIVE);
+ if (type != null && type.equals("private"))
+ filtered.put(entry.getKey(), entry.getValue());
+ }
+
+ // remove existing packages to prevent merge errors
+ filtered.keySet().removeAll(dot.getPackages());
+ doExpand(dot, CONDITIONAL_PACKAGE + " Private imports", Instruction
+ .replaceWithInstruction(filtered), false);
+ } while (dot.getDirectories().size() > size);
+ analyzed = true;
+ }
+
+ /**
+ * Intercept the call to analyze and cleanup versions after we have analyzed
+ * the setup. We do not want to cleanup if we are going to verify.
+ */
+
+ public void analyze() throws Exception {
+ super.analyze();
+ cleanupVersion(imports);
+ cleanupVersion(exports);
+ String version = getProperty(BUNDLE_VERSION);
+ if (version != null)
+ setProperty(BUNDLE_VERSION, cleanupVersion(version));
+ }
+
+ public void cleanupVersion(Map<String, Map<String, String>> mapOfMap) {
+ for (Iterator<Map.Entry<String, Map<String, String>>> e = mapOfMap.entrySet().iterator(); e
+ .hasNext();) {
+ Map.Entry<String, Map<String, String>> entry = e.next();
+ Map<String, String> attributes = entry.getValue();
+ if (attributes.containsKey("version")) {
+ attributes.put("version", cleanupVersion(attributes.get("version")));
+ }
+ }
+ }
+
+ /**
+ *
*/
+ private void addSources(Jar dot) {
+ if (!hasSources())
+ return;
- void sign(Jar jar) throws Exception {
- String signing = getProperty("-sign");
- if (signing == null)
- return;
+ Set<String> packages = new HashSet<String>();
- trace("Signing %s, with %s", getBsn(), signing);
- List<SignerPlugin> signers = getPlugins(SignerPlugin.class);
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ getProperties().store(out, "Generated by BND, at " + new Date());
+ dot.putResource("OSGI-OPT/bnd.bnd", new EmbeddedResource(out.toByteArray(), 0));
+ out.close();
+ } catch (Exception e) {
+ error("Can not embed bnd file in JAR: " + e);
+ }
- Map<String, Map<String, String>> infos = parseHeader(signing);
- for (Map.Entry<String, Map<String, String>> entry : infos.entrySet()) {
- for (SignerPlugin signer : signers) {
- signer.sign(this, entry.getKey());
- }
- }
- }
+ for (Iterator<String> cpe = classspace.keySet().iterator(); cpe.hasNext();) {
+ String path = cpe.next();
+ path = path.substring(0, path.length() - ".class".length()) + ".java";
+ String pack = getPackage(path).replace('.', '/');
+ if (pack.length() > 1)
+ pack = pack + "/";
+ boolean found = false;
+ String[] fixed = { "packageinfo", "package.html", "module-info.java",
+ "package-info.java" };
+ for (Iterator<File> i = getSourcePath().iterator(); i.hasNext();) {
+ File root = i.next();
+ File f = getFile(root, path);
+ if (f.exists()) {
+ found = true;
+ if (!packages.contains(pack)) {
+ packages.add(pack);
+ File bdir = getFile(root, pack);
+ for (int j = 0; j < fixed.length; j++) {
+ File ff = getFile(bdir, fixed[j]);
+ if (ff.isFile()) {
+ dot.putResource("OSGI-OPT/src/" + pack + fixed[j],
+ new FileResource(ff));
+ }
+ }
+ }
+ dot.putResource("OSGI-OPT/src/" + path, new FileResource(f));
+ }
+ }
+ if (!found) {
+ for (Jar jar : classpath) {
+ Resource resource = jar.getResource(path);
+ if (resource != null) {
+ dot.putResource("OSGI-OPT/src", resource);
+ } else {
+ resource = jar.getResource("OSGI-OPT/src/" + path);
+ if (resource != null) {
+ dot.putResource("OSGI-OPT/src", resource);
+ }
+ }
+ }
+ }
+ if (getSourcePath().isEmpty())
+ warning("Including sources but " + SOURCEPATH
+ + " does not contain any source directories ");
+ // TODO copy from the jars where they came from
+ }
+ }
- public boolean hasSources() {
- return isTrue(getProperty(SOURCES));
- }
+ boolean firstUse = true;
- protected String getImportPackages() {
- String ip = super.getImportPackages();
- if (ip != null)
- return ip;
+ public Collection<File> getSourcePath() {
+ if (firstUse) {
+ firstUse = false;
+ String sp = getProperty(SOURCEPATH);
+ if (sp != null) {
+ Map<String, Map<String, String>> map = parseHeader(sp);
+ for (Iterator<String> i = map.keySet().iterator(); i.hasNext();) {
+ String file = i.next();
+ if (!isDuplicate(file)) {
+ File f = getFile(file);
+ if (!f.isDirectory()) {
+ error("Adding a sourcepath that is not a directory: " + f);
+ } else {
+ sourcePath.add(f);
+ }
+ }
+ }
+ }
+ }
+ return sourcePath;
+ }
- return "*";
- }
+ private void doVerify(Jar dot) throws Exception {
+ Verifier verifier = new Verifier(dot, getProperties());
+ verifier.setPedantic(isPedantic());
- private void doConditional(Jar dot) throws IOException {
- Map<String, Map<String, String>> conditionals = getHeader(CONDITIONAL_PACKAGE);
- if ( conditionals.isEmpty() )
- return;
-
- int size;
- do {
- size = dot.getDirectories().size();
- analyze();
- analyzed = false;
- Map<String, Map<String, String>> imports = getImports();
+ // Give the verifier the benefit of our analysis
+ // prevents parsing the files twice
+ verifier.setClassSpace(classspace, contained, referred, uses);
+ verifier.verify();
+ getInfo(verifier);
+ }
- // Match the packages specified in conditionals
- // against the imports. Any match must become a
- // Private-Package
- Map<String, Map<String, String>> filtered = merge(
- CONDITIONAL_PACKAGE, conditionals, imports,
- new HashSet<String>(), null);
+ private void doExpand(Jar jar) throws IOException {
+ if (getClasspath().size() == 0
+ && (getProperty(EXPORT_PACKAGE) != null || getProperty(EXPORT_PACKAGE) != null || getProperty(PRIVATE_PACKAGE) != null))
+ warning("Classpath is empty. Private-Package and Export-Package can only expand from the classpath when there is one");
- // Imports can also specify a private import. These
- // packages must also be copied to the bundle
- for (Map.Entry<String, Map<String, String>> entry : getImports()
- .entrySet()) {
- String type = entry.getValue().get(IMPORT_DIRECTIVE);
- if (type != null && type.equals("private"))
- filtered.put(entry.getKey(), entry.getValue());
- }
+ Map<Instruction, Map<String, String>> privateMap = Instruction
+ .replaceWithInstruction(getHeader(PRIVATE_PACKAGE));
+ Map<Instruction, Map<String, String>> exportMap = Instruction
+ .replaceWithInstruction(getHeader(EXPORT_PACKAGE));
- // remove existing packages to prevent merge errors
- filtered.keySet().removeAll(dot.getPackages());
- doExpand(dot, CONDITIONAL_PACKAGE + " Private imports",
- replaceWitInstruction(filtered, CONDITIONAL_PACKAGE), false);
- } while (dot.getDirectories().size() > size);
- analyzed = true;
- }
+ if (isTrue(getProperty(Constants.UNDERTEST))) {
+ privateMap.putAll(Instruction.replaceWithInstruction(parseHeader(getProperty(
+ Constants.TESTPACKAGES, "test;presence:=optional"))));
+ }
+ if (!privateMap.isEmpty())
+ doExpand(jar, "Private-Package, or -testpackages", privateMap, true);
- /**
- * Intercept the call to analyze and cleanup versions after we have analyzed
- * the setup. We do not want to cleanup if we are going to verify.
+ if (!exportMap.isEmpty() ) {
+ Jar exports = new Jar("exports");
+ doExpand(exports, EXPORT_PACKAGE, exportMap, true);
+ jar.addAll(exports);
+ exports.close();
+ }
+
+ if (!isNoBundle()) {
+ if (privateMap.isEmpty() && exportMap.isEmpty() && !isResourceOnly()
+ && getProperty(EXPORT_CONTENTS) == null) {
+ warning("None of Export-Package, Provide-Package, Private-Package, -testpackages, or -exportcontents is set, therefore no packages will be included");
+ }
+ }
+ }
+
+ /**
+ *
+ * @param jar
+ * @param name
+ * @param instructions
+ */
+ private void doExpand(Jar jar, String name, Map<Instruction, Map<String, String>> instructions,
+ boolean mandatory) {
+ Set<Instruction> superfluous = removeMarkedDuplicates(instructions.keySet());
+
+ for (Iterator<Jar> c = getClasspath().iterator(); c.hasNext();) {
+ Jar now = c.next();
+ doExpand(jar, instructions, now, superfluous);
+ }
+
+ if (mandatory && superfluous.size() > 0) {
+ StringBuilder sb = new StringBuilder();
+ String del = "Instructions in " + name + " that are never used: ";
+ for (Iterator<Instruction> i = superfluous.iterator(); i.hasNext();) {
+ Instruction p = i.next();
+ sb.append(del);
+ sb.append(p.toString());
+ del = "\n ";
+ }
+ sb.append("\nClasspath: ");
+ sb.append(Processor.join(getClasspath()));
+ sb.append("\n");
+
+ warning(sb.toString());
+ if (isPedantic())
+ diagnostics = true;
+ }
+ }
+
+ /**
+ * Iterate over each directory in the class path entry and check if that
+ * directory is a desired package.
+ *
+ * @param included
+ * @param classpathEntry
+ */
+ private void doExpand(Jar jar, Map<Instruction, Map<String, String>> included,
+ Jar classpathEntry, Set<Instruction> superfluous) {
+
+ loop: for (Map.Entry<String, Map<String, Resource>> directory : classpathEntry
+ .getDirectories().entrySet()) {
+ String path = directory.getKey();
+
+ if (doNotCopy.matcher(getName(path)).matches())
+ continue;
+
+ if (directory.getValue() == null)
+ continue;
+
+ String pack = path.replace('/', '.');
+ Instruction instr = matches(included.keySet(), pack, superfluous);
+ if (instr != null) {
+ // System.out.println("Pattern match: " + pack + " " +
+ // instr.getPattern() + " " + instr.isNegated());
+ if (!instr.isNegated()) {
+ Map<String, Resource> contents = directory.getValue();
+
+ // What to do with split packages? Well if this
+ // directory already exists, we will check the strategy
+ // and react accordingly.
+ boolean overwriteResource = true;
+ if (jar.hasDirectory(path)) {
+ Map<String, String> directives = included.get(instr);
+
+ switch (getSplitStrategy((String) directives.get(SPLIT_PACKAGE_DIRECTIVE))) {
+ case SPLIT_MERGE_LAST:
+ overwriteResource = true;
+ break;
+
+ case SPLIT_MERGE_FIRST:
+ overwriteResource = false;
+ break;
+
+ case SPLIT_ERROR:
+ error(diagnostic(pack, getClasspath(), classpathEntry.source));
+ continue loop;
+
+ case SPLIT_FIRST:
+ continue loop;
+
+ default:
+ warning(diagnostic(pack, getClasspath(), classpathEntry.source));
+ overwriteResource = false;
+ break;
+ }
+ }
+
+ jar.addDirectory(contents, overwriteResource);
+
+ String key = path + "/bnd.info";
+ Resource r = jar.getResource(key);
+ if (r != null)
+ jar.putResource(key, new PreprocessResource(this, r));
+
+ if (hasSources()) {
+ String srcPath = "OSGI-OPT/src/" + path;
+ Map<String, Resource> srcContents = classpathEntry.getDirectories().get(
+ srcPath);
+ if (srcContents != null) {
+ jar.addDirectory(srcContents, overwriteResource);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Analyze the classpath for a split package
+ *
+ * @param pack
+ * @param classpath
+ * @param source
+ * @return
+ */
+ private String diagnostic(String pack, List<Jar> classpath, File source) {
+ // Default is like merge-first, but with a warning
+ // Find the culprits
+ pack = pack.replace('.', '/');
+ List<Jar> culprits = new ArrayList<Jar>();
+ for (Iterator<Jar> i = classpath.iterator(); i.hasNext();) {
+ Jar culprit = (Jar) i.next();
+ if (culprit.getDirectories().containsKey(pack)) {
+ culprits.add(culprit);
+ }
+ }
+ return "Split package "
+ + pack
+ + "\nUse directive -split-package:=(merge-first|merge-last|error|first) on Export/Private Package instruction to get rid of this warning\n"
+ + "Package found in " + culprits + "\n" + "Reference from " + source + "\n"
+ + "Classpath " + classpath;
+ }
+
+ private int getSplitStrategy(String type) {
+ if (type == null)
+ return SPLIT_DEFAULT;
+
+ if (type.equals("merge-last"))
+ return SPLIT_MERGE_LAST;
+
+ if (type.equals("merge-first"))
+ return SPLIT_MERGE_FIRST;
+
+ if (type.equals("error"))
+ return SPLIT_ERROR;
+
+ if (type.equals("first"))
+ return SPLIT_FIRST;
+
+ error("Invalid strategy for split-package: " + type);
+ return SPLIT_DEFAULT;
+ }
+
+ private Instruction matches(Collection<Instruction> instructions, String pack,
+ Set<Instruction> superfluousPatterns) {
+ for (Instruction pattern : instructions) {
+ if (pattern.matches(pack)) {
+ if (superfluousPatterns != null)
+ superfluousPatterns.remove(pattern);
+ return pattern;
+ }
+ }
+ return null;
+ }
+
+ private Map<String, Map<String, String>> getHeader(String string) {
+ if (string == null)
+ return Collections.emptyMap();
+ return parseHeader(getProperty(string));
+ }
+
+ /**
+ * Parse the Bundle-Includes header. Files in the bundles Include header are
+ * included in the jar. The source can be a directory or a file.
+ *
+ * @throws IOException
+ * @throws FileNotFoundException
+ */
+ private void doIncludeResources(Jar jar) throws Exception {
+ String includes = getProperty("Bundle-Includes");
+ if (includes == null) {
+ includes = getProperty(INCLUDERESOURCE);
+ if (includes == null || includes.length() == 0)
+ includes = getProperty("Include-Resource");
+ } else
+ warning("Please use -includeresource instead of Bundle-Includes");
+
+ doIncludeResource(jar, includes);
+
+ }
+
+ private void doIncludeResource(Jar jar, String includes) throws Exception {
+ Map<String, Map<String, String>> clauses = parseHeader(includes);
+ doIncludeResource(jar, clauses);
+ }
+
+ private void doIncludeResource(Jar jar, Map<String, Map<String, String>> clauses)
+ throws ZipException, IOException, Exception {
+ for (Map.Entry<String, Map<String, String>> entry : clauses.entrySet()) {
+ doIncludeResource(jar, entry.getKey(), entry.getValue());
+ }
+ }
+
+ private void doIncludeResource(Jar jar, String name, Map<String, String> extra)
+ throws ZipException, IOException, Exception {
+ boolean preprocess = false;
+ if (name.startsWith("{") && name.endsWith("}")) {
+ preprocess = true;
+ name = name.substring(1, name.length() - 1).trim();
+ }
+
+ String parts[] = name.split("\\s*=\\s*");
+ String source = parts[0];
+ String destination = parts[0];
+ if (parts.length == 2)
+ source = parts[1];
+
+ if (source.startsWith("@")) {
+ extractFromJar(jar, source.substring(1), parts.length == 1 ? "" : destination);
+ } else if (extra.containsKey("literal")) {
+ String literal = (String) extra.get("literal");
+ Resource r = new EmbeddedResource(literal.getBytes("UTF-8"), 0);
+ String x = (String) extra.get("extra");
+ if (x != null)
+ r.setExtra(x);
+ jar.putResource(name, r);
+ } else {
+ File sourceFile;
+ String destinationPath;
+
+ sourceFile = getFile(source);
+ if (parts.length == 1) {
+ // Directories should be copied to the root
+ // but files to their file name ...
+ if (sourceFile.isDirectory())
+ destinationPath = "";
+ else
+ destinationPath = sourceFile.getName();
+ } else {
+ destinationPath = parts[0];
+ }
+ // Handle directories
+ if (sourceFile.isDirectory()) {
+ destinationPath = doResourceDirectory(jar, extra, preprocess, sourceFile,
+ destinationPath);
+ return;
+ }
+
+ // destinationPath = checkDestinationPath(destinationPath);
+
+ if (!sourceFile.exists()) {
+ noSuchFile(jar, name, extra, source, destinationPath);
+ } else
+ copy(jar, destinationPath, sourceFile, preprocess, extra);
+ }
+ }
+
+ private String doResourceDirectory(Jar jar, Map<String, String> extra, boolean preprocess,
+ File sourceFile, String destinationPath) throws Exception {
+ String filter = extra.get("filter:");
+ boolean flatten = isTrue(extra.get("flatten:"));
+ boolean recursive = true;
+ String directive = extra.get("recursive:");
+ if (directive != null) {
+ recursive = isTrue(directive);
+ }
+
+ InstructionFilter iFilter = null;
+ if (filter != null) {
+ iFilter = new InstructionFilter(Instruction.getPattern(filter), recursive);
+ } else {
+ iFilter = new InstructionFilter(null, recursive);
+ }
+
+ Map<String, File> files = newMap();
+ resolveFiles(sourceFile, iFilter, recursive, destinationPath, files, flatten);
+
+ for (Map.Entry<String, File> entry : files.entrySet()) {
+ copy(jar, entry.getKey(), entry.getValue(), preprocess, extra);
+ }
+ return destinationPath;
+ }
+
+ private void resolveFiles(File dir, FileFilter filter, boolean recursive, String path,
+ Map<String, File> files, boolean flatten) {
+
+ if (Analyzer.doNotCopy.matcher(dir.getName()).matches()) {
+ return;
+ }
+
+ File[] fs = dir.listFiles(filter);
+ for (File file : fs) {
+ if (file.isDirectory()) {
+ if (recursive) {
+ String nextPath;
+ if (flatten)
+ nextPath = path;
+ else
+ nextPath = appendPath(path, file.getName());
+
+ resolveFiles(file, filter, recursive, nextPath, files, flatten);
+ }
+ // Directories are ignored otherwise
+ } else {
+ String p = appendPath(path, file.getName());
+ if (files.containsKey(p))
+ warning("Include-Resource overwrites entry %s from file %s", p, file);
+ files.put(p, file);
+ }
+ }
+ }
+
+ private void noSuchFile(Jar jar, String clause, Map<String, String> extra, String source,
+ String destinationPath) throws Exception {
+ Jar src = getJarFromName(source, "Include-Resource " + source);
+ if (src != null) {
+ JarResource jarResource = new JarResource(src);
+ jar.putResource(destinationPath, jarResource);
+ } else {
+ Resource lastChance = make.process(source);
+ if (lastChance != null) {
+ String x = extra.get("extra");
+ if (x != null)
+ lastChance.setExtra(x);
+ jar.putResource(destinationPath, lastChance);
+ } else
+ error("Input file does not exist: " + source);
+ }
+ }
+
+ /**
+ * Extra resources from a Jar and add them to the given jar. The clause is
+ * the
+ *
+ * @param jar
+ * @param clauses
+ * @param i
+ * @throws ZipException
+ * @throws IOException
+ */
+ private void extractFromJar(Jar jar, String source, String destination) throws ZipException,
+ IOException {
+ // Inline all resources and classes from another jar
+ // optionally appended with a modified regular expression
+ // like @zip.jar!/META-INF/MANIFEST.MF
+ int n = source.lastIndexOf("!/");
+ Instruction instr = null;
+ if (n > 0) {
+ instr = Instruction.getPattern(source.substring(n + 2));
+ source = source.substring(0, n);
+ }
+
+ // Pattern filter = null;
+ // if (n > 0) {
+ // String fstring = source.substring(n + 2);
+ // source = source.substring(0, n);
+ // filter = wildcard(fstring);
+ // }
+ Jar sub = getJarFromName(source, "extract from jar");
+ if (sub == null)
+ error("Can not find JAR file " + source);
+ else {
+ jar.addAll(sub, instr, destination);
+ }
+ }
+
+ private void copy(Jar jar, String path, File from, boolean preprocess, Map<String, String> extra)
+ throws Exception {
+ if (doNotCopy.matcher(from.getName()).matches())
+ return;
+
+ if (from.isDirectory()) {
+
+ File files[] = from.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ copy(jar, appendPath(path, files[i].getName()), files[i], preprocess, extra);
+ }
+ } else {
+ if (from.exists()) {
+ Resource resource = new FileResource(from);
+ if (preprocess) {
+ resource = new PreprocessResource(this, resource);
+ }
+ String x = extra.get("extra");
+ if (x != null)
+ resource.setExtra(x);
+ if (path.endsWith("/"))
+ path = path + from.getName();
+ jar.putResource(path, resource);
+
+ if (isTrue(extra.get(LIB_DIRECTIVE))) {
+ setProperty(BUNDLE_CLASSPATH, append(getProperty(BUNDLE_CLASSPATH), path));
+ }
+ } else {
+ error("Input file does not exist: " + from);
+ }
+ }
+ }
+
+ private String getName(String where) {
+ int n = where.lastIndexOf('/');
+ if (n < 0)
+ return where;
+
+ return where.substring(n + 1);
+ }
+
+ public void setSourcepath(File[] files) {
+ for (int i = 0; i < files.length; i++)
+ addSourcepath(files[i]);
+ }
+
+ public void addSourcepath(File cp) {
+ if (!cp.exists())
+ warning("File on sourcepath that does not exist: " + cp);
+
+ sourcePath.add(cp);
+ }
+
+ public void close() {
+ super.close();
+ }
+
+ /**
+ * Build Multiple jars. If the -sub command is set, we filter the file with
+ * the given patterns.
+ *
+ * @return
+ * @throws Exception
+ */
+ public Jar[] builds() throws Exception {
+ begin();
+
+ // Are we acting as a conduit for another JAR?
+ String conduit = getProperty(CONDUIT);
+ if (conduit != null) {
+ Map<String, Map<String, String>> map = parseHeader(conduit);
+ Jar[] result = new Jar[map.size()];
+ int n = 0;
+ for (String file : map.keySet()) {
+ Jar c = new Jar(getFile(file));
+ addClose(c);
+ String name = map.get(file).get("name");
+ if (name != null)
+ c.setName(name);
+
+ result[n++] = c;
+ }
+ return result;
+ }
+
+ List<Jar> result = new ArrayList<Jar>();
+ List<Builder> builders;
+
+ builders = getSubBuilders();
+
+ for (Builder builder : builders) {
+ try {
+ Jar jar = builder.build();
+ jar.setName(builder.getBsn());
+ result.add(jar);
+ } catch (Exception e) {
+ error("Sub Building " + builder.getBsn(), e);
+ }
+ if (builder != this)
+ getInfo(builder, builder.getBsn() + ": ");
+ }
+ return result.toArray(new Jar[result.size()]);
+ }
+
+ /**
+ * Answer a list of builders that represent this file or a list of files
+ * specified in -sub. This list can be empty. These builders represents to
+ * be created artifacts and are each scoped to such an artifacts. The
+ * builders can be used to build the bundles or they can be used to find out
+ * information about the to be generated bundles.
+ *
+ * @return List of 0..n builders representing artifacts.
+ * @throws Exception
+ */
+ public List<Builder> getSubBuilders() throws Exception {
+ String sub = (String) getProperty(SUB);
+ if (sub == null || sub.trim().length() == 0 || EMPTY_HEADER.equals(sub))
+ return Arrays.asList(this);
+
+ List<Builder> builders = new ArrayList<Builder>();
+ if (isTrue(getProperty(NOBUNDLES)))
+ return builders;
+
+ Set<Instruction> subs = Instruction.replaceWithInstruction(parseHeader(sub)).keySet();
+
+ List<File> members = new ArrayList<File>(Arrays.asList(getBase().listFiles()));
+
+ nextFile: while (members.size() > 0) {
+
+ File file = members.remove(0);
+
+ // Check if the file is one of our parents
+ Processor p = this;
+ while (p != null) {
+ if (file.equals(p.getPropertiesFile()))
+ continue nextFile;
+ p = p.getParent();
+ }
+
+ // if
+ // (file.getCanonicalFile().equals(getPropertiesFile().getCanonicalFile()))
+ // continue nextFile;
+
+ for (Iterator<Instruction> i = subs.iterator(); i.hasNext();) {
+
+ Instruction instruction = i.next();
+ if (instruction.matches(file.getName())) {
+
+ if (!instruction.isNegated()) {
+
+ Builder builder = getSubBuilder();
+ if (builder != null) {
+ builder.setProperties(file);
+ addClose(builder);
+ builders.add(builder);
+ }
+ }
+
+ // Because we matched (even though we could be negated)
+ // we skip any remaining searches
+ continue nextFile;
+ }
+ }
+ }
+ return builders;
+ }
+
+ public Builder getSubBuilder() throws Exception {
+ Builder builder = new Builder(this);
+ builder.setBase(getBase());
+
+ for (Jar file : getClasspath()) {
+ builder.addClasspath(file);
+ }
+
+ return builder;
+ }
+
+ /**
+ * A macro to convert a maven version to an OSGi version
+ */
+
+ public String _maven_version(String args[]) {
+ if (args.length > 2)
+ error("${maven_version} macro receives too many arguments " + Arrays.toString(args));
+ else if (args.length < 2)
+ error("${maven_version} macro has no arguments, use ${maven_version;1.2.3-SNAPSHOT}");
+ else {
+ return cleanupVersion(args[1]);
+ }
+ return null;
+ }
+
+ public String _permissions(String args[]) throws IOException {
+ StringBuilder sb = new StringBuilder();
+
+ for (String arg : args) {
+ if ("packages".equals(arg) || "all".equals(arg)) {
+ for (String imp : getImports().keySet()) {
+ if (!imp.startsWith("java.")) {
+ sb.append("(org.osgi.framework.PackagePermission \"");
+ sb.append(imp);
+ sb.append("\" \"import\")\r\n");
+ }
+ }
+ for (String exp : getExports().keySet()) {
+ sb.append("(org.osgi.framework.PackagePermission \"");
+ sb.append(exp);
+ sb.append("\" \"export\")\r\n");
+ }
+ } else if ("admin".equals(arg) || "all".equals(arg)) {
+ sb.append("(org.osgi.framework.AdminPermission)");
+ } else if ("permissions".equals(arg))
+ ;
+ else
+ error("Invalid option in ${permissions}: %s", arg);
+ }
+ return sb.toString();
+ }
+
+ /**
+ *
*/
+ public void removeBundleSpecificHeaders() {
+ Set<String> set = new HashSet<String>(Arrays.asList(BUNDLE_SPECIFIC_HEADERS));
+ setForceLocal(set);
+ }
- public void analyze() throws IOException {
- super.analyze();
- cleanupVersion(imports);
- cleanupVersion(exports);
- String version = getProperty(BUNDLE_VERSION);
- if (version != null)
- setProperty(BUNDLE_VERSION, cleanupVersion(version));
- }
+ /**
+ * Check if the given resource is in scope of this bundle. That is, it
+ * checks if the Include-Resource includes this resource or if it is a class
+ * file it is on the class path and the Export-Pacakge or Private-Package
+ * include this resource.
+ *
+ * For now, include resources are skipped.
+ *
+ * @param f
+ * @return
+ */
+ public boolean isInScope(Collection<File> resources) throws Exception {
+ Map<String, Map<String, String>> clauses = parseHeader(getProperty(Constants.EXPORT_PACKAGE));
+ clauses.putAll(parseHeader(getProperty(Constants.PRIVATE_PACKAGE)));
+ if (isTrue(getProperty(Constants.UNDERTEST))) {
+ clauses.putAll(parseHeader(getProperty(Constants.TESTPACKAGES,
+ "test;presence:=optional")));
+ }
+ Collection<Instruction> instructions = Instruction.replaceWithInstruction(clauses).keySet();
- public void cleanupVersion(Map<String, Map<String, String>> mapOfMap) {
- for (Iterator<Map.Entry<String, Map<String, String>>> e = mapOfMap
- .entrySet().iterator(); e.hasNext();) {
- Map.Entry<String, Map<String, String>> entry = e.next();
- Map<String, String> attributes = entry.getValue();
- if (attributes.containsKey("version")) {
- attributes.put("version", cleanupVersion(attributes
- .get("version")));
- }
- }
- }
+ for (File r : resources) {
+ String cpEntry = getClasspathEntrySuffix(r);
+ if (cpEntry != null) {
+ String pack = Clazz.getPackage(cpEntry);
+ Instruction i = matches(instructions, pack, null);
+ if (i != null)
+ return !i.isNegated();
+ }
+ }
+ return false;
+ }
- /**
- *
- */
- private void addSources(Jar dot) {
- if (!hasSources())
- return;
+ /**
+ * Answer the string of the resource that it has in the container.
+ *
+ * @param resource
+ * The resource to look for
+ * @return
+ * @throws Exception
+ */
+ public String getClasspathEntrySuffix(File resource) throws Exception {
+ for (Jar jar : getClasspath()) {
+ File source = jar.getSource();
+ if (source != null) {
+ source = source.getCanonicalFile();
+ String sourcePath = source.getAbsolutePath();
+ String resourcePath = resource.getAbsolutePath();
- Set<String> packages = new HashSet<String>();
+ if (resourcePath.startsWith(sourcePath)) {
+ // Make sure that the path name is translated correctly
+ // i.e. on Windows the \ must be translated to /
+ String filePath = resourcePath.substring(sourcePath.length() + 1);
- try {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- getProperties().store(out, "Generated by BND, at " + new Date());
- dot.putResource("OSGI-OPT/bnd.bnd", new EmbeddedResource(out
- .toByteArray(), 0));
- out.close();
- } catch (Exception e) {
- error("Can not embed bnd file in JAR: " + e);
- }
-
- for (Iterator<String> cpe = classspace.keySet().iterator(); cpe
- .hasNext();) {
- String path = cpe.next();
- path = path.substring(0, path.length() - ".class".length())
- + ".java";
- String pack = getPackage(path).replace('.', '/');
- if (pack.length() > 1)
- pack = pack + "/";
- boolean found = false;
- String[] fixed = { "packageinfo", "package.html",
- "module-info.java", "package-info.java" };
- for (Iterator<File> i = getSourcePath().iterator(); i.hasNext();) {
- File root = i.next();
- File f = getFile(root, path);
- if (f.exists()) {
- found = true;
- if (!packages.contains(pack)) {
- packages.add(pack);
- File bdir = getFile(root, pack);
- for (int j = 0; j < fixed.length; j++) {
- File ff = getFile(bdir, fixed[j]);
- if (ff.isFile()) {
- dot.putResource("OSGI-OPT/src/" + pack
- + fixed[j], new FileResource(ff));
- }
- }
- }
- dot
- .putResource("OSGI-OPT/src/" + path,
- new FileResource(f));
- }
- }
- if (!found) {
- for (Jar jar : classpath) {
- Resource resource = jar.getResource(path);
- if (resource != null) {
- dot.putResource("OSGI-OPT/src", resource);
- } else {
- resource = jar.getResource("OSGI-OPT/src/" + path);
- if (resource != null) {
- dot.putResource("OSGI-OPT/src", resource);
- }
- }
- }
- }
- if (getSourcePath().isEmpty())
- warning("Including sources but " + SOURCEPATH
- + " does not contain any source directories ");
- // TODO copy from the jars where they came from
- }
- }
-
- boolean firstUse = true;
-
- public Collection<File> getSourcePath() {
- if (firstUse) {
- firstUse = false;
- String sp = getProperty(SOURCEPATH);
- if (sp != null) {
- Map<String, Map<String, String>> map = parseHeader(sp);
- for (Iterator<String> i = map.keySet().iterator(); i.hasNext();) {
- String file = i.next();
- if (!isDuplicate(file)) {
- File f = getFile(file);
- if (!f.isDirectory()) {
- error("Adding a sourcepath that is not a directory: "
- + f);
- } else {
- sourcePath.add(f);
- }
- }
- }
- }
- }
- return sourcePath;
- }
-
- private void doVerify(Jar dot) throws Exception {
- Verifier verifier = new Verifier(dot, getProperties());
- verifier.setPedantic(isPedantic());
-
- // Give the verifier the benefit of our analysis
- // prevents parsing the files twice
- verifier.setClassSpace(classspace, contained, referred, uses);
- verifier.verify();
- getInfo(verifier);
- }
-
- private void doExpand(Jar jar) throws IOException {
- if (getClasspath().size() == 0
- && (getProperty(EXPORT_PACKAGE) != null || getProperty(PRIVATE_PACKAGE) != null))
- warning("Classpath is empty. Private-Package and Export-Package can only expand from the classpath when there is one");
-
- Map<Instruction, Map<String, String>> privateMap = replaceWitInstruction(
- getHeader(PRIVATE_PACKAGE), PRIVATE_PACKAGE);
- Map<Instruction, Map<String, String>> exportMap = replaceWitInstruction(
- getHeader(EXPORT_PACKAGE), EXPORT_PACKAGE);
-
- if (isTrue(getProperty(Constants.UNDERTEST))) {
- privateMap.putAll(replaceWitInstruction(parseHeader(getProperty(
- Constants.TESTPACKAGES, "test;presence:=optional")),
- TESTPACKAGES));
- }
- if (!privateMap.isEmpty())
- doExpand(jar, "Private-Package, or -testpackages", privateMap, true);
-
- if (!exportMap.isEmpty()) {
- Jar exports = new Jar("exports");
- doExpand(exports, "Export-Package", exportMap, true);
- jar.addAll(exports);
- exports.close();
- }
-
- if (privateMap.isEmpty() && exportMap.isEmpty() && !isResourceOnly()) {
- warning("Neither Export-Package, Private-Package, -testpackages is set, therefore no packages will be included");
- }
- }
-
- /**
- *
- * @param jar
- * @param name
- * @param instructions
- */
- private void doExpand(Jar jar, String name,
- Map<Instruction, Map<String, String>> instructions,
- boolean mandatory) {
- Set<Instruction> superfluous = removeMarkedDuplicates(instructions
- .keySet());
-
- for (Iterator<Jar> c = getClasspath().iterator(); c.hasNext();) {
- Jar now = c.next();
- doExpand(jar, instructions, now, superfluous);
- }
-
- if (mandatory && superfluous.size() > 0) {
- StringBuffer sb = new StringBuffer();
- String del = "Instructions in " + name + " that are never used: ";
- for (Iterator<Instruction> i = superfluous.iterator(); i.hasNext();) {
- Instruction p = i.next();
- sb.append(del);
- sb.append(p.getPattern());
- del = ", ";
- }
- warning(sb.toString());
- }
- }
-
- /**
- * Iterate over each directory in the class path entry and check if that
- * directory is a desired package.
- *
- * @param included
- * @param classpathEntry
- */
- private void doExpand(Jar jar,
- Map<Instruction, Map<String, String>> included, Jar classpathEntry,
- Set<Instruction> superfluous) {
-
- loop: for (Map.Entry<String, Map<String, Resource>> directory : classpathEntry
- .getDirectories().entrySet()) {
- String path = directory.getKey();
-
- if (doNotCopy.matcher(getName(path)).matches())
- continue;
-
- if (directory.getValue() == null)
- continue;
-
- String pack = path.replace('/', '.');
- Instruction instr = matches(included, pack, superfluous);
- if (instr != null) {
- // System.out.println("Pattern match: " + pack + " " +
- // instr.getPattern() + " " + instr.isNegated());
- if (!instr.isNegated()) {
- Map<String, Resource> contents = directory.getValue();
-
- // What to do with split packages? Well if this
- // directory already exists, we will check the strategy
- // and react accordingly.
- boolean overwriteResource = true;
- if (jar.hasDirectory(path)) {
- Map<String, String> directives = included.get(instr);
-
- switch (getSplitStrategy((String) directives
- .get(SPLIT_PACKAGE_DIRECTIVE))) {
- case SPLIT_MERGE_LAST:
- overwriteResource = true;
- break;
-
- case SPLIT_MERGE_FIRST:
- overwriteResource = false;
- break;
-
- case SPLIT_ERROR:
- error(diagnostic(pack, getClasspath(),
- classpathEntry.source));
- continue loop;
-
- case SPLIT_FIRST:
- continue loop;
-
- default:
- warning(diagnostic(pack, getClasspath(),
- classpathEntry.source));
- overwriteResource = false;
- break;
- }
- }
-
- jar.addDirectory(contents, overwriteResource);
-
- String key = path + "/bnd.info";
- Resource r = jar.getResource(key);
- if (r != null)
- jar.putResource(key, new PreprocessResource(this, r));
-
- if (hasSources()) {
- String srcPath = "OSGI-OPT/src/" + path;
- Map<String, Resource> srcContents = classpathEntry
- .getDirectories().get(srcPath);
- if (srcContents != null) {
- jar.addDirectory(srcContents, overwriteResource);
- }
- }
- }
- }
- }
- }
-
- /**
- * Analyze the classpath for a split package
- *
- * @param pack
- * @param classpath
- * @param source
- * @return
- */
- private String diagnostic(String pack, List<Jar> classpath, File source) {
- // Default is like merge-first, but with a warning
- // Find the culprits
- pack = pack.replace('.', '/');
- List<Jar> culprits = new ArrayList<Jar>();
- for (Iterator<Jar> i = classpath.iterator(); i.hasNext();) {
- Jar culprit = (Jar) i.next();
- if (culprit.getDirectories().containsKey(pack)) {
- culprits.add(culprit);
- }
- }
- return "Split package "
- + pack
- + "\nUse directive -split-package:=(merge-first|merge-last|error|first) on Export/Private Package instruction to get rid of this warning\n"
- + "Package found in " + culprits + "\n"
- + "Reference from " + source + "\n" + "Classpath "
- + classpath;
- }
-
- private int getSplitStrategy(String type) {
- if (type == null)
- return SPLIT_DEFAULT;
-
- if (type.equals("merge-last"))
- return SPLIT_MERGE_LAST;
-
- if (type.equals("merge-first"))
- return SPLIT_MERGE_FIRST;
-
- if (type.equals("error"))
- return SPLIT_ERROR;
-
- if (type.equals("first"))
- return SPLIT_FIRST;
-
- error("Invalid strategy for split-package: " + type);
- return SPLIT_DEFAULT;
- }
-
- private Instruction matches(
- Map<Instruction, Map<String, String>> instructions, String pack,
- Set<Instruction> superfluousPatterns) {
- for (Instruction pattern : instructions.keySet()) {
- if (pattern.matches(pack)) {
- superfluousPatterns.remove(pattern);
- return pattern;
- }
- }
- return null;
- }
-
- private Map<String, Map<String, String>> getHeader(String string) {
- if (string == null)
- return Collections.emptyMap();
- return parseHeader(getProperty(string));
- }
-
- /**
- * Parse the Bundle-Includes header. Files in the bundles Include header are
- * included in the jar. The source can be a directory or a file.
- *
- * @throws IOException
- * @throws FileNotFoundException
- */
- private void doIncludeResources(Jar jar) throws Exception {
- String includes = getProperty("Bundle-Includes");
- if (includes == null) {
- includes = getProperty(INCLUDERESOURCE);
- if (includes == null)
- includes = getProperty("Include-Resource");
- } else
- warning("Please use -includeresource instead of Bundle-Includes");
-
- if (includes == null)
- return;
-
- Map<String, Map<String, String>> clauses = parseHeader(includes);
-
- for (Iterator<Map.Entry<String, Map<String, String>>> i = clauses
- .entrySet().iterator(); i.hasNext();) {
- Map.Entry<String, Map<String, String>> entry = i.next();
- doIncludeResource(jar, entry.getKey(), entry.getValue());
- }
- }
-
- private void doIncludeResource(Jar jar, String name,
- Map<String, String> extra) throws ZipException, IOException,
- Exception {
- boolean preprocess = false;
- if (name.startsWith("{") && name.endsWith("}")) {
- preprocess = true;
- name = name.substring(1, name.length() - 1).trim();
- }
-
- if (name.startsWith("@")) {
- extractFromJar(jar, name.substring(1));
- } else if (extra.containsKey("literal")) {
- String literal = (String) extra.get("literal");
- Resource r = new EmbeddedResource(literal.getBytes("UTF-8"), 0);
- String x = (String) extra.get("extra");
- if (x != null)
- r.setExtra(x);
- jar.putResource(name, r);
- } else {
- String source;
- File sourceFile;
- String destinationPath;
-
- String parts[] = name.split("\\s*=\\s*");
- if (parts.length == 1) {
- // Just a copy, destination path defined by
- // source path.
- source = parts[0];
- sourceFile = getFile(source);
- // Directories should be copied to the root
- // but files to their file name ...
- if (sourceFile.isDirectory())
- destinationPath = "";
- else
- destinationPath = sourceFile.getName();
- } else {
- source = parts[1];
- sourceFile = getFile(source);
- destinationPath = parts[0];
-
- // Handle directories
- if (sourceFile.isDirectory()) {
- destinationPath = doResourceDirectory(jar, extra,
- preprocess, sourceFile, destinationPath);
- return;
- }
- }
-
- //destinationPath = checkDestinationPath(destinationPath);
-
- if (!sourceFile.exists()) {
- noSuchFile(jar, name, extra, source, destinationPath);
- } else
- copy(jar, destinationPath, sourceFile, preprocess, extra);
- }
- }
-
- private String doResourceDirectory(Jar jar, Map<String, String> extra,
- boolean preprocess, File sourceFile, String destinationPath)
- throws Exception {
- String filter = extra.get("filter:");
- boolean flatten = isTrue(extra.get("flatten:"));
- boolean recursive = true;
- String directive = extra.get("recursive:");
- if (directive != null) {
- recursive = isTrue(directive);
- }
-
- InstructionFilter iFilter = null;
- if (filter != null) {
- iFilter = new InstructionFilter(Instruction
- .getPattern(filter), recursive);
- } else {
- iFilter = new InstructionFilter(null, recursive);
- }
-
- destinationPath = checkDestinationPath(destinationPath);
-
- File[] files = resolveFiles(sourceFile, iFilter, recursive);
- for (File file : files) {
- String dp;
- if (flatten) {
- if (destinationPath.length() == 0) {
- dp = file.getName();
- } else {
- dp = destinationPath + "/"
- + file.getName();
- }
- } else {
- dp = destinationPath
- + file.getParentFile().getAbsolutePath()
- .substring(
- sourceFile
- .getAbsolutePath()
- .length()).replace('\\','/');
- if (dp.length() > 0) {
- dp += "/" + file.getName();
- } else {
- dp = file.getName();
- }
- }
- copy(jar, dp, file, preprocess, extra);
- }
- return destinationPath;
- }
-
- private String checkDestinationPath(String destinationPath) {
-
- // Some people insist on ending a directory with
- // a slash ... it now also works if you do /=dir
- if (destinationPath.endsWith("/"))
- destinationPath = destinationPath.substring(0, destinationPath
- .length() - 1);
- return destinationPath;
- }
-
- private File[] resolveFiles(File dir, FileFilter filter, boolean recursive) {
- return resolveFiles(dir, filter, null, recursive);
- }
-
- private File[] resolveFiles(File dir, FileFilter filter, File[] files,
- boolean recursive) {
- if (files == null) {
- files = EMPTY_FILE;
- }
-
- if (Analyzer.doNotCopy.matcher(dir.getName()).matches()) {
- return files;
- }
-
- File[] fs = dir.listFiles(filter);
- for (File file : fs) {
- if (file.isDirectory()) {
- if (recursive) {
- files = resolveFiles(file, filter, files, recursive);
- }
- } else {
- if (files.length == 0) {
- files = new File[] { file };
- } else {
- File[] newFiles = new File[files.length + 1];
- System.arraycopy(files, 0, newFiles, 0, files.length);
- newFiles[newFiles.length - 1] = file;
- files = newFiles;
- }
- }
- }
- return files;
- }
-
- private void noSuchFile(Jar jar, String clause, Map<String, String> extra,
- String source, String destinationPath) throws Exception {
- Jar src = getJarFromName(source, "Include-Resource " + source);
- if (src != null) {
- JarResource jarResource = new JarResource(src);
- jar.putResource(destinationPath, jarResource);
- } else {
- Resource lastChance = make.process(source);
- if (lastChance != null) {
- String x = extra.get("extra");
- if (x != null)
- lastChance.setExtra(x);
- jar.putResource(destinationPath, lastChance);
- } else
- error("Input file does not exist: " + source);
- }
- }
-
- /**
- * Extra resources from a Jar and add them to the given jar. The clause is
- * the
- *
- * @param jar
- * @param clauses
- * @param i
- * @throws ZipException
- * @throws IOException
- */
- private void extractFromJar(Jar jar, String name) throws ZipException,
- IOException {
- // Inline all resources and classes from another jar
- // optionally appended with a modified regular expression
- // like @zip.jar!/META-INF/MANIFEST.MF
- int n = name.lastIndexOf("!/");
- Pattern filter = null;
- if (n > 0) {
- String fstring = name.substring(n + 2);
- name = name.substring(0, n);
- filter = wildcard(fstring);
- }
- Jar sub = getJarFromName(name, "extract from jar");
- if (sub == null)
- error("Can not find JAR file " + name);
- else
- jar.addAll(sub, filter);
- }
-
- private Pattern wildcard(String spec) {
- StringBuffer sb = new StringBuffer();
- for (int j = 0; j < spec.length(); j++) {
- char c = spec.charAt(j);
- switch (c) {
- case '.':
- sb.append("\\.");
- break;
-
- case '*':
- // test for ** (all directories)
- if (j < spec.length() - 1 && spec.charAt(j + 1) == '*') {
- sb.append(".*");
- j++;
- } else
- sb.append("[^/]*");
- break;
- default:
- sb.append(c);
- break;
- }
- }
- String s = sb.toString();
- try {
- return Pattern.compile(s);
- } catch (Exception e) {
- error("Invalid regular expression on wildcarding: " + spec
- + " used *");
- }
- return null;
- }
-
- private void copy(Jar jar, String path, File from, boolean preprocess,
- Map<String, String> extra) throws Exception {
- if (doNotCopy.matcher(from.getName()).matches())
- return;
-
- if (from.isDirectory()) {
- String next = path;
- if (next.length() != 0 && ! next.endsWith("/"))
- next += '/';
-
- File files[] = from.listFiles();
- for (int i = 0; i < files.length; i++) {
- copy(jar, next + files[i].getName(), files[i], preprocess,
- extra);
- }
- } else {
- if (from.exists()) {
- Resource resource = new FileResource(from);
- if (preprocess) {
- resource = new PreprocessResource(this, resource);
- }
- String x = extra.get("extra");
- if (x != null)
- resource.setExtra(x);
- if ( path.endsWith("/"))
- path = path + from.getName();
- jar.putResource(path, resource);
- } else {
- error("Input file does not exist: " + from);
- }
- }
- }
-
- private String getName(String where) {
- int n = where.lastIndexOf('/');
- if (n < 0)
- return where;
-
- return where.substring(n + 1);
- }
-
- public void setSourcepath(File[] files) {
- for (int i = 0; i < files.length; i++)
- addSourcepath(files[i]);
- }
-
- public void addSourcepath(File cp) {
- if (!cp.exists())
- warning("File on sourcepath that does not exist: " + cp);
-
- sourcePath.add(cp);
- }
-
- /**
- * Create a POM reseource for Maven containing as much information as
- * possible from the manifest.
- *
- * @param output
- * @param builder
- * @throws FileNotFoundException
- * @throws IOException
- */
- public void doPom(Jar dot) throws FileNotFoundException, IOException {
- {
- Manifest manifest = dot.getManifest();
- String name = manifest.getMainAttributes().getValue(
- Analyzer.BUNDLE_NAME);
- String description = manifest.getMainAttributes().getValue(
- Analyzer.BUNDLE_DESCRIPTION);
- String docUrl = manifest.getMainAttributes().getValue(
- Analyzer.BUNDLE_DOCURL);
- String version = manifest.getMainAttributes().getValue(
- Analyzer.BUNDLE_VERSION);
- String bundleVendor = manifest.getMainAttributes().getValue(
- Analyzer.BUNDLE_VENDOR);
- ByteArrayOutputStream s = new ByteArrayOutputStream();
- PrintStream ps = new PrintStream(s);
- String bsn = manifest.getMainAttributes().getValue(
- Analyzer.BUNDLE_SYMBOLICNAME);
- String licenses = manifest.getMainAttributes().getValue(
- BUNDLE_LICENSE);
-
- if (bsn == null) {
- error("Can not create POM unless Bundle-SymbolicName is set");
- return;
- }
-
- bsn = bsn.trim();
- int n = bsn.lastIndexOf('.');
- if (n <= 0) {
- error("Can not create POM unless Bundle-SymbolicName contains a .");
- ps.close();
- s.close();
- return;
- }
- String groupId = bsn.substring(0, n);
- String artifactId = bsn.substring(n + 1);
- ps
- .println("<project xmlns='http://maven.apache.org/POM/4.0.0' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd'>");
- ps.println(" <modelVersion>4.0.0</modelVersion>");
- ps.println(" <groupId>" + groupId + "</groupId>");
-
- n = artifactId.indexOf(';');
- if (n > 0)
- artifactId = artifactId.substring(0, n).trim();
-
- ps.println(" <artifactId>" + artifactId + "</artifactId>");
- ps.println(" <version>" + version + "</version>");
- if (description != null) {
- ps.println(" <description>");
- ps.print(" ");
- ps.println(description);
- ps.println(" </description>");
- }
- if (name != null) {
- ps.print(" <name>");
- ps.print(name);
- ps.println("</name>");
- }
- if (docUrl != null) {
- ps.print(" <url>");
- ps.print(docUrl);
- ps.println("</url>");
- }
-
- if (bundleVendor != null) {
- Matcher m = NAME_URL.matcher(bundleVendor);
- String namePart = bundleVendor;
- String urlPart = null;
- if (m.matches()) {
- namePart = m.group(1);
- urlPart = m.group(2);
- }
- ps.println(" <organization>");
- ps.print(" <name>");
- ps.print(namePart.trim());
- ps.println("</name>");
- if (urlPart != null) {
- ps.print(" <url>");
- ps.print(urlPart.trim());
- ps.println("</url>");
- }
- ps.println(" </organization>");
- }
- if (licenses != null) {
- ps.println(" <licenses>");
- Map<String, Map<String, String>> map = parseHeader(licenses);
- for (Iterator<Map.Entry<String, Map<String, String>>> e = map
- .entrySet().iterator(); e.hasNext();) {
- Map.Entry<String, Map<String, String>> entry = e.next();
- ps.println(" <license>");
- Map<String, String> values = entry.getValue();
- print(ps, values, "name", "name", (String) values
- .get("url"));
- print(ps, values, "url", "url", null);
- print(ps, values, "distribution", "distribution", "repo");
- ps.println(" </license>");
- }
- ps.println(" </licenses>");
- }
- ps.println("</project>");
- ps.close();
- s.close();
- dot
- .putResource("pom.xml", new EmbeddedResource(s
- .toByteArray(), 0));
- }
- }
-
- /**
- * Utility function to print a tag from a map
- *
- * @param ps
- * @param values
- * @param string
- * @param tag
- * @param object
- */
- private void print(PrintStream ps, Map<String, String> values,
- String string, String tag, String object) {
- String value = (String) values.get(string);
- if (value == null)
- value = object;
- if (value == null)
- return;
- ps.println(" <" + tag + ">" + value.trim() + "</" + tag + ">");
- }
-
- public void close() {
- super.close();
- }
-
- /**
- * Build Multiple jars. If the -sub command is set, we filter the file with
- * the given patterns.
- *
- * @return
- * @throws Exception
- */
- public Jar[] builds() throws Exception {
- begin();
-
- // Are we acting as a conduit for another JAR?
- String conduit = getProperty(CONDUIT);
- if (conduit != null) {
- Map<String, Map<String, String>> map = parseHeader(conduit);
- Jar[] result = new Jar[map.size()];
- int n = 0;
- for (String file : map.keySet()) {
- Jar c = new Jar(getFile(file));
- addClose(c);
- String name = map.get(file).get("name");
- if (name != null)
- c.setName(name);
-
- result[n++] = c;
- }
- return result;
- }
-
- // If no -sub property, then reuse this builder object
- // other wise, build all the sub parts.
- String sub = getProperty(SUB);
- if (sub == null) {
- Jar jar = build();
- if (jar == null)
- return new Jar[0];
-
- return new Jar[] { jar };
- }
-
- List<Jar> result = new ArrayList<Jar>();
-
- // Get the Instruction objects that match the sub header
- Set<Instruction> subs = replaceWitInstruction(parseHeader(sub), SUB)
- .keySet();
-
- // Get the member files of this directory
- List<File> members = new ArrayList<File>(Arrays.asList(getBase()
- .listFiles()));
-
- getProperties().remove(SUB);
- // For each member file
- nextFile: while (members.size() > 0) {
-
- File file = members.remove(0);
- if (file.equals(getPropertiesFile()))
- continue nextFile;
-
- for (Iterator<Instruction> i = subs.iterator(); i.hasNext();) {
-
- Instruction instruction = i.next();
- if (instruction.matches(file.getName())) {
-
- if (!instruction.isNegated()) {
-
- Builder builder = null;
- try {
- builder = getSubBuilder();
- addClose(builder);
- builder.setProperties(file);
- builder.setProperty(SUB, "");
- // Recursively build
- // TODO
- Jar jar = builder.build();
- jar.setName(builder.getBsn());
- result.add(jar);
- } catch (Exception e) {
- e.printStackTrace();
- error("Sub Building " + file, e);
- }
- if (builder != null)
- getInfo(builder, file.getName() + ": ");
- }
-
- // Because we matched (even though we could be negated)
- // we skip any remaining searches
- continue nextFile;
- }
- }
- }
- setProperty(SUB, sub);
- return result.toArray(new Jar[result.size()]);
- }
-
- public Builder getSubBuilder() throws Exception {
- Builder builder = new Builder(this);
- builder.setBase(getBase());
-
- for (Jar file : getClasspath()) {
- builder.addClasspath(file);
- }
-
- return builder;
- }
-
- /**
- * A macro to convert a maven version to an OSGi version
- */
-
- public String _maven_version(String args[]) {
- if (args.length > 2)
- error("${maven_version} macro receives too many arguments "
- + Arrays.toString(args));
- else if (args.length < 2)
- error("${maven_version} macro has no arguments, use ${maven_version;1.2.3-SNAPSHOT}");
- else {
- return cleanupVersion(args[1]);
- }
- return null;
- }
-
- public String _permissions(String args[]) throws IOException {
- StringBuilder sb = new StringBuilder();
-
- for (String arg : args) {
- if ("packages".equals(arg) || "all".equals(arg)) {
- for (String imp : getImports().keySet()) {
- if (!imp.startsWith("java.")) {
- sb.append("(org.osgi.framework.PackagePermission \"");
- sb.append(imp);
- sb.append("\" \"import\")\r\n");
- }
- }
- for (String exp : getExports().keySet()) {
- sb.append("(org.osgi.framework.PackagePermission \"");
- sb.append(exp);
- sb.append("\" \"export\")\r\n");
- }
- } else if ("admin".equals(arg) || "all".equals(arg)) {
- sb.append("(org.osgi.framework.AdminPermission)");
- } else if ("permissions".equals(arg))
- ;
- else
- error("Invalid option in ${permissions}: %s", arg);
- }
- return sb.toString();
- }
-
- public void removeBundleSpecificHeaders() {
- Set<String> set = new HashSet<String>(Arrays
- .asList(BUNDLE_SPECIFIC_HEADERS));
- setForceLocal(set);
- }
+ return filePath.replace(File.separatorChar, '/');
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BundlePlugin.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BundlePlugin.java
index 94b34a3..756064a 100644
--- a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BundlePlugin.java
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BundlePlugin.java
@@ -63,6 +63,7 @@
import aQute.lib.osgi.Analyzer;
import aQute.lib.osgi.Builder;
+import aQute.lib.osgi.Constants;
import aQute.lib.osgi.EmbeddedResource;
import aQute.lib.osgi.FileResource;
import aQute.lib.osgi.Jar;
@@ -395,7 +396,7 @@
dumpManifest( "BND Manifest:", jar.getManifest(), getLog() );
- String[] removeHeaders = builder.getProperty( Analyzer.REMOVE_HEADERS, "" ).split( "," );
+ String[] removeHeaders = builder.getProperty( Constants.REMOVEHEADERS, "" ).split( "," );
mergeMavenManifest( currentProject, jar, removeHeaders, getLog() );
builder.setJar( jar );
@@ -852,7 +853,7 @@
properties.put( Analyzer.BUNDLE_VERSION, getMaven2OsgiConverter().getVersion(currentProject.getVersion()) );
// remove the extraneous Include-Resource and Private-Package entries from generated manifest
- properties.put( Analyzer.REMOVE_HEADERS, Analyzer.INCLUDE_RESOURCE + ',' + Analyzer.PRIVATE_PACKAGE );
+ properties.put( Constants.REMOVEHEADERS, Analyzer.INCLUDE_RESOURCE + ',' + Analyzer.PRIVATE_PACKAGE );
header( properties, Analyzer.BUNDLE_DESCRIPTION, currentProject.getDescription() );
StringBuffer licenseText = printLicenses( currentProject.getLicenses() );
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/ManifestPlugin.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/ManifestPlugin.java
index 4503840..5a136cd 100644
--- a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/ManifestPlugin.java
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/ManifestPlugin.java
@@ -100,14 +100,14 @@
public Manifest getManifest( MavenProject project, Jar[] classpath ) throws IOException, MojoFailureException,
- MojoExecutionException
+ MojoExecutionException, Exception
{
return getManifest( project, new Properties(), new Properties(), classpath );
}
public Manifest getManifest( MavenProject project, Map instructions, Properties properties, Jar[] classpath )
- throws IOException, MojoFailureException, MojoExecutionException
+ throws IOException, MojoFailureException, MojoExecutionException, Exception
{
Analyzer analyzer = getAnalyzer( project, instructions, properties, classpath );
@@ -143,14 +143,15 @@
}
- protected Analyzer getAnalyzer( MavenProject project, Jar[] classpath ) throws IOException, MojoExecutionException
+ protected Analyzer getAnalyzer( MavenProject project, Jar[] classpath )
+ throws IOException, MojoExecutionException, Exception
{
return getAnalyzer( project, new LinkedHashMap(), new Properties(), classpath );
}
protected Analyzer getAnalyzer( MavenProject project, Map instructions, Properties properties, Jar[] classpath )
- throws IOException, MojoExecutionException
+ throws IOException, MojoExecutionException, Exception
{
File file = project.getArtifact().getFile();
if ( file == null )