Index: apisupport/harness/release/README =================================================================== RCS file: /cvs/apisupport/harness/release/README,v --- apisupport/harness/release/README 26 Dec 2005 23:42:28 -0000 1.28 +++ apisupport/harness/release/README 30 Dec 2005 03:55:41 -0000 @@ -561,8 +561,8 @@ netbeans.home - location of "platform*" cluster of the NetBeans IDE installation you are running. *Only defined* when you run an Ant script *inside* the IDE. -nbjdk.active - name of a Java platform (JDK) to use when building and -running. Will try to load (e.g. from ${userdir}/build.properties, where Java +nbjdk.active [since 5.1 M1] - name of a Java platform (JDK) to use when building +and running. Will try to load (e.g. from ${userdir}/build.properties, where Java Platform Manager stores its information) the properties nbjdk.home=${platforms.${nbjdk.active}.home} @@ -662,8 +662,9 @@ test-unit-sys-prop. - will set a system property when running your unit tests. -tools.jar - set to location of tools.jar in active JDK. Useful in case your module -needs to compile against JDK-only classes. Meaningless on Mac OS X. +tools.jar [since 5.1 M1] - set to location of tools.jar in active JDK. Useful in +case your module needs to compile against JDK-only classes. Meaningless on Mac +OS X. To support builds of JNLP based applications (described at http://installer.netbeans.org/docs/jnlpInstaller.html) additional may be used @@ -819,12 +820,14 @@ branding.token - optional token defining an application branding. disabled.clusters - comma-separated list of cluster names in the target platform -(e.g. "ide6") which should be completely excluded from the application. +(e.g. "ide6") which should be completely excluded from the application. Since 5.1 M1, +should be stored in platform.properties, not project.properties. disabled.modules - comma-separated list of code name bases of modules in the target platform (e.g. "org.netbeans.modules.autoupdate") which should be excluded from the application. There is no need to specify modules already -excluded by ${disabled.clusters}. +excluded by ${disabled.clusters}. Since 5.1 M1, should be stored in +platform.properties, not project.properties. modules - list of contained modules. Path format; entries resolved against the suite directory as needed. Index: apisupport/harness/release/build.xml =================================================================== RCS file: /cvs/apisupport/harness/release/build.xml,v --- apisupport/harness/release/build.xml 28 Dec 2005 20:22:07 -0000 1.9 +++ apisupport/harness/release/build.xml 30 Dec 2005 03:55:41 -0000 @@ -42,8 +42,21 @@ + + + + + - + Index: apisupport/project/src/org/netbeans/modules/apisupport/project/ui/customizer/ModuleProperties.java =================================================================== RCS file: /cvs/apisupport/project/src/org/netbeans/modules/apisupport/project/ui/customizer/ModuleProperties.java,v --- apisupport/project/src/org/netbeans/modules/apisupport/project/ui/customizer/ModuleProperties.java 26 Dec 2005 23:42:19 -0000 1.10 +++ apisupport/project/src/org/netbeans/modules/apisupport/project/ui/customizer/ModuleProperties.java 30 Dec 2005 03:55:41 -0000 @@ -128,7 +128,7 @@ if (def == null) { def = ""; // NOI18N } - if (def.equals(value)) { + if (value == null || def.equals(value)) { getProjectProperties().remove(key); } else { getProjectProperties().setProperty(key, value); Index: apisupport/project/src/org/netbeans/modules/apisupport/project/ui/customizer/SuiteProperties.java =================================================================== RCS file: /cvs/apisupport/project/src/org/netbeans/modules/apisupport/project/ui/customizer/SuiteProperties.java,v --- apisupport/project/src/org/netbeans/modules/apisupport/project/ui/customizer/SuiteProperties.java 26 Dec 2005 23:42:16 -0000 1.25 +++ apisupport/project/src/org/netbeans/modules/apisupport/project/ui/customizer/SuiteProperties.java 30 Dec 2005 03:55:41 -0000 @@ -24,6 +24,7 @@ import org.netbeans.modules.apisupport.project.ui.customizer.CustomizerComponentFactory.SuiteSubModulesListModel; import org.netbeans.modules.apisupport.project.universe.NbPlatform; import org.netbeans.spi.project.support.ant.AntProjectHelper; +import org.netbeans.spi.project.support.ant.EditableProperties; import org.netbeans.spi.project.support.ant.PropertyEvaluator; /** @@ -170,20 +171,26 @@ SuiteUtils.replaceSubModules(this); } - if (changedDisabledModules) { - String[] separated = (String[])disabledModules.clone(); - for (int i = 0; i < disabledModules.length - 1; i++) { - separated[i] = disabledModules[i] + ','; + if (changedDisabledModules || changedDisabledClusters) { + EditableProperties ep = getHelper().getProperties("nbproject/platform.properties"); // NOI18N + if (changedDisabledModules) { + String[] separated = (String[]) disabledModules.clone(); + for (int i = 0; i < disabledModules.length - 1; i++) { + separated[i] = disabledModules[i] + ','; + } + ep.setProperty(DISABLED_MODULES_PROPERTY, separated); + // Do not want it left in project.properties if it was there before (from 5.0): + setProperty(DISABLED_MODULES_PROPERTY, (String) null); } - setProperty(DISABLED_MODULES_PROPERTY, separated); - } - - if (changedDisabledClusters) { - String[] separated = (String[])disabledClusters.clone(); - for (int i = 0; i < disabledClusters.length - 1; i++) { - separated[i] = disabledClusters[i] + ','; + if (changedDisabledClusters) { + String[] separated = (String[]) disabledClusters.clone(); + for (int i = 0; i < disabledClusters.length - 1; i++) { + separated[i] = disabledClusters[i] + ','; + } + ep.setProperty(DISABLED_CLUSTERS_PROPERTY, separated); + setProperty(DISABLED_CLUSTERS_PROPERTY, (String) null); } - setProperty(DISABLED_CLUSTERS_PROPERTY, separated); + getHelper().putProperties("nbproject/platform.properties", ep); // NOI18N } super.storeProperties(); Index: nbbuild/antsrc/org/netbeans/nbbuild/JarWithModuleAttributes.java =================================================================== RCS file: /cvs/nbbuild/antsrc/org/netbeans/nbbuild/JarWithModuleAttributes.java,v --- nbbuild/antsrc/org/netbeans/nbbuild/JarWithModuleAttributes.java 24 Sep 2005 00:37:28 -0000 1.8 +++ nbbuild/antsrc/org/netbeans/nbbuild/JarWithModuleAttributes.java 30 Dec 2005 03:55:44 -0000 @@ -29,7 +29,6 @@ /** * Task just like but predefines various module attributes. - * Would not be necessary if this were implemented: http://issues.apache.org/bugzilla/show_bug.cgi?id=34366 * Cf. projectized.xml#jar * @author Jesse Glick */ @@ -72,10 +71,6 @@ if (cp != null) { added.addConfiguredAttribute(new Manifest.Attribute("Class-Path", cp)); } - String ideDeps = getProject().getProperty("ide.dependencies"); - if (ideDeps != null) { - added.addConfiguredAttribute(new Manifest.Attribute("OpenIDE-Module-IDE-Dependencies", ideDeps)); - } String moduleDeps = getProject().getProperty("module.dependencies"); if (moduleDeps != null) { added.addConfiguredAttribute(new Manifest.Attribute("OpenIDE-Module-Module-Dependencies", moduleDeps)); @@ -130,13 +125,8 @@ } } SortedMap/**/ additions = new TreeMap(); - String[] deps = {ideDeps, moduleDeps}; - for (int i = 0; i < 2; i++) { - String dep = deps[i]; - if (dep == null) { - continue; - } - String[] individualDeps = COMMA_SPACE.split(dep); + if (moduleDeps != null) { + String[] individualDeps = COMMA_SPACE.split(moduleDeps); for (int j = 0; j < individualDeps.length; j++) { Matcher m = IMPL_DEP.matcher(individualDeps[j]); if (m.matches()) { @@ -170,7 +160,7 @@ } else { added.addConfiguredAttribute(new Manifest.Attribute("OpenIDE-Module-Specification-Version", specVersBase)); } - } else if ((ideDeps != null && ideDeps.indexOf('=') != -1) || (moduleDeps != null && moduleDeps.indexOf('=') != -1)) { + } else if (moduleDeps != null && moduleDeps.indexOf('=') != -1) { getProject().log("Warning: in " + ownCnb + ", not using spec.version.base, yet declaring implementation dependencies; may lead to problems with Auto Update", Project.MSG_WARN); } else if (implVers != null) { try { Index: nbbuild/antsrc/org/netbeans/nbbuild/ModuleListParser.java =================================================================== RCS file: /cvs/nbbuild/antsrc/org/netbeans/nbbuild/ModuleListParser.java,v --- nbbuild/antsrc/org/netbeans/nbbuild/ModuleListParser.java 13 Nov 2005 09:51:07 -0000 1.27 +++ nbbuild/antsrc/org/netbeans/nbbuild/ModuleListParser.java 30 Dec 2005 03:55:45 -0000 @@ -294,7 +294,8 @@ String cnb2 = XMLUtil.findText(cnbEl2); prereqs.add(cnb2); } - Entry entry = new Entry(cnb, jar, (File[]) exts.toArray(new File[exts.size()]), dir, path, (String[]) prereqs.toArray(new String[prereqs.size()])); + String cluster = fakeproj.getProperty("cluster.dir"); // may be null + Entry entry = new Entry(cnb, jar, (File[]) exts.toArray(new File[exts.size()]), dir, path, (String[]) prereqs.toArray(new String[prereqs.size()]), cluster); if (entries.containsKey(cnb)) { throw new IOException("Duplicated module " + cnb + ": found in " + entries.get(cnb) + " and " + entry); } else { @@ -390,7 +391,7 @@ exts[l] = new File(dir, pieces[l].replace('/', File.separatorChar)); } } - Entry entry = new Entry(codenamebase, m, exts, dir, null, null); + Entry entry = new Entry(codenamebase, m, exts, dir, null, null, clusters[i].getName()); if (entries.containsKey(codenamebase)) { throw new IOException("Duplicated module " + codenamebase + ": found in " + entries.get(codenamebase) + " and " + entry); } else { @@ -550,14 +551,16 @@ private final File sourceLocation; private final String netbeansOrgPath; private final String[] buildPrerequisites; + private final String clusterName; - Entry(String cnb, File jar, File[] classPathExtensions, File sourceLocation, String netbeansOrgPath, String[] buildPrerequisites) { + Entry(String cnb, File jar, File[] classPathExtensions, File sourceLocation, String netbeansOrgPath, String[] buildPrerequisites, String clusterName) { this.cnb = cnb; this.jar = jar; this.classPathExtensions = classPathExtensions; this.sourceLocation = sourceLocation; this.netbeansOrgPath = netbeansOrgPath; this.buildPrerequisites = buildPrerequisites; + this.clusterName = clusterName; } /** @@ -594,6 +597,18 @@ */ public String[] getBuildPrerequisites() { return buildPrerequisites; + } + + /** + * Return the name of the cluster in which this module resides. + * If this entry represents an external module in source form, + * then the cluster will be null. If the module represents a netbeans.org + * module or a binary module in a platform, then the cluster name will + * be the (base) name of the directory containing the "modules" subdirectory + * (sometimes "lib" or "core") where the JAR is. + */ + public String getClusterName() { + return clusterName; } public String toString() { Index: nbbuild/antsrc/org/netbeans/nbbuild/ParseProjectXml.java =================================================================== RCS file: /cvs/nbbuild/antsrc/org/netbeans/nbbuild/ParseProjectXml.java,v --- nbbuild/antsrc/org/netbeans/nbbuild/ParseProjectXml.java 15 Sep 2005 15:57:33 -0000 1.31 +++ nbbuild/antsrc/org/netbeans/nbbuild/ParseProjectXml.java 30 Dec 2005 03:55:45 -0000 @@ -20,13 +20,21 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.jar.Attributes; import java.util.jar.JarFile; +import java.util.regex.Pattern; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; @@ -98,16 +106,6 @@ javadocPackagesProperty = s; } - private String ideDependenciesProperty; - /** - * Set the property to set a list of - * OpenIDE-Module-IDE-Dependencies to, based on the list of stated - * run-time dependencies. - */ - public void setIdeDependenciesProperty(String s) { - ideDependenciesProperty = s; - } - private String moduleDependenciesProperty; /** * Set the property to set a list of @@ -154,6 +152,16 @@ moduleClassPathProperty = s; } + private File publicPackageJarDir; + /** + * Set the location of a directory in which to look for and create + * JARs containing just the public packages of appropriate + * compile-time dependencies. + */ + public void setPublicPackageJarDir(File d) { + publicPackageJarDir = d; + } + private String classPathExtensionsProperty; /** * Set the property to set the declared Class-Path attribute to. @@ -248,36 +256,25 @@ } } ModuleListParser modules = null; + Dep[] deps = null; if (moduleDependenciesProperty != null || moduleClassPathProperty != null) { String nball = getProject().getProperty("nb_all"); Hashtable properties = getProject().getProperties(); properties.put("project", project.getAbsolutePath()); modules = new ModuleListParser(properties, getModuleType(pDoc), getProject()); + deps = getDeps(pDoc, modules); } - if (ideDependenciesProperty != null || moduleDependenciesProperty != null) { - Dep[] deps = getDeps(pDoc); - if (ideDependenciesProperty != null) { - Dep ide = null; - for (int i = 0; i < deps.length; i++) { - if (deps[i].codenamebase.equals("IDE")) { - ide = deps[i]; - break; - } - } - if (ide != null) { - define(ideDependenciesProperty, ide.toString(modules)); - } - } + if (moduleDependenciesProperty != null) { if (moduleDependenciesProperty != null) { StringBuffer b = new StringBuffer(); for (int i = 0; i < deps.length; i++) { - if (deps[i].codenamebase.equals("IDE")) { + if (!deps[i].run) { continue; } if (b.length() > 0) { b.append(", "); } - b.append(deps[i].toString(modules)); + b.append(deps[i]); } if (b.length() > 0) { define(moduleDependenciesProperty, b.toString()); @@ -293,7 +290,7 @@ define(codeNameBaseSlashesProperty, cnb.replace('.', '/')); } if (moduleClassPathProperty != null) { - String cp = computeClasspath(pDoc, modules); + String cp = computeClasspath(pDoc, modules, deps); if (cp != null) { define(moduleClassPathProperty, cp); } @@ -419,12 +416,20 @@ } private final class Dep { - /** will be e.g. org.netbeans.modules.form or IDE */ + private final ModuleListParser modules; + /** will be e.g. org.netbeans.modules.form */ public String codenamebase; public String release = null; public String spec = null; public boolean impl = false; - public String toString(ModuleListParser modules) throws IOException, BuildException { + public boolean compile = false; + public boolean run = false; + + public Dep(ModuleListParser modules) { + this.modules = modules; + } + + public String toString() throws BuildException { StringBuffer b = new StringBuffer(codenamebase); if (release != null) { b.append('/'); @@ -445,21 +450,87 @@ } return b.toString(); } - private String implementationVersionOf(ModuleListParser modules, String cnb) throws IOException { - File jar = computeClasspathModuleLocation(modules, cnb); + + private String implementationVersionOf(ModuleListParser modules, String cnb) throws BuildException { + File jar = computeClasspathModuleLocation(modules, cnb, null, null); if (!jar.isFile()) { throw new BuildException("No such classpath entry: " + jar, getLocation()); } - JarFile jarFile = new JarFile(jar, false); try { - return jarFile.getManifest().getMainAttributes().getValue("OpenIDE-Module-Implementation-Version"); - } finally { - jarFile.close(); + JarFile jarFile = new JarFile(jar, false); + try { + return jarFile.getManifest().getMainAttributes().getValue("OpenIDE-Module-Implementation-Version"); + } finally { + jarFile.close(); + } + } catch (IOException e) { + throw new BuildException(e, getLocation()); + } + } + + private boolean matches(Attributes attr) { + if (release != null) { + String givenCodeName = attr.getValue("OpenIDE-Module"); + int slash = givenCodeName.indexOf('/'); + int givenRelease = -1; + if (slash != -1) { + assert codenamebase.equals(givenCodeName.substring(0, slash)); + givenRelease = Integer.parseInt(givenCodeName.substring(slash + 1)); + } + int dash = release.indexOf('-'); + if (dash == -1) { + if (Integer.parseInt(release) != givenRelease) { + return false; + } + } else { + int lower = Integer.parseInt(release.substring(0, dash)); + int upper = Integer.parseInt(release.substring(dash + 1)); + if (givenRelease < lower || givenRelease > upper) { + return false; + } + } + } + if (spec != null) { + String givenSpec = attr.getValue("OpenIDE-Module-Specification-Version"); + if (givenSpec == null) { + return false; + } + // XXX cannot use org.openide.modules.SpecificationVersion from here + int[] specVals = digitize(spec); + int[] givenSpecVals = digitize(givenSpec); + int len1 = specVals.length; + int len2 = givenSpecVals.length; + int max = Math.max(len1, len2); + for (int i = 0; i < max; i++) { + int d1 = ((i < len1) ? specVals[i] : 0); + int d2 = ((i < len2) ? givenSpecVals[i] : 0); + if (d1 < d2) { + break; + } else if (d1 > d2) { + return false; + } + } } + if (impl) { + if (attr.getValue("OpenIDE-Module-Implementation-Version") == null) { + return false; + } + } + return true; + } + private int[] digitize(String spec) throws NumberFormatException { + StringTokenizer tok = new StringTokenizer(spec, "."); + int len = tok.countTokens(); + int[] digits = new int[len]; + for (int i = 0; i < len; i++) { + digits[i] = Integer.parseInt(tok.nextToken()); + } + return digits; } + } - private Dep[] getDeps(Document pDoc) throws BuildException { + private Dep[] getDeps(Document pDoc, ModuleListParser modules) throws BuildException { Element cfg = getConfig(pDoc); Element md = XMLUtil.findElement(cfg, "module-dependencies", NBM_NS); if (md == null) { @@ -470,7 +541,7 @@ Iterator it = l.iterator(); while (it.hasNext()) { Element dep = (Element)it.next(); - Dep d = new Dep(); + Dep d = new Dep(modules); Element cnb = XMLUtil.findElement(dep, "code-name-base", NBM_NS); if (cnb == null) { throw new BuildException("No ", getLocation()); @@ -479,12 +550,10 @@ if (t == null) { throw new BuildException("No text in ", getLocation()); } - if (t.equals("org.openide")) { - t = "IDE"; - } d.codenamebase = t; Element rd = XMLUtil.findElement(dep, "run-dependency", NBM_NS); if (rd != null) { + d.run = true; Element rv = XMLUtil.findElement(rd, "release-version", NBM_NS); if (rv != null) { t = XMLUtil.findText(rv); @@ -501,13 +570,13 @@ } d.spec = t; } - // added in /2: Element iv = XMLUtil.findElement(rd, "implementation-version", NBM_NS); if (iv != null) { d.impl = true; } - deps.add(d); } + d.compile = XMLUtil.findElement(dep, "compile-dependency", NBM_NS) != null; + deps.add(d); } return (Dep[])deps.toArray(new Dep[deps.size()]); } @@ -536,73 +605,73 @@ } } - private String computeClasspath(Document pDoc, ModuleListParser modules) throws BuildException, IOException, SAXException { - Element data = getConfig(pDoc); - Element moduleDependencies = XMLUtil.findElement(data, "module-dependencies", NBM_NS); - List/**/ deps = XMLUtil.findSubElements(moduleDependencies); + private String computeClasspath(Document pDoc, ModuleListParser modules, Dep[] deps) throws BuildException, IOException, SAXException { + String myCnb = getCodeNameBase(pDoc); StringBuffer cp = new StringBuffer(); - Iterator it = deps.iterator(); - while (it.hasNext()) { - Element dep = (Element)it.next(); - if (XMLUtil.findElement(dep, "compile-dependency", NBM_NS) == null) { + String excludedClustersProp = getProject().getProperty("disabled.clusters"); + Set/**/ excludedClusters = excludedClustersProp != null ? + new HashSet(Arrays.asList(excludedClustersProp.split(" *, *"))) : + null; + String excludedModulesProp = getProject().getProperty("disabled.modules"); + Set/**/ excludedModules = excludedModulesProp != null ? + new HashSet(Arrays.asList(excludedModulesProp.split(" *, *"))) : + null; + for (int i = 0; i < deps.length; i++) { + Dep dep = deps[i]; + if (!dep.compile) { continue; } - if (cp.length() > 0) { - cp.append(':'); + String cnb = dep.codenamebase; + File depJar = computeClasspathModuleLocation(modules, cnb, excludedClusters, excludedModules); + + Attributes attr; + JarFile jarFile = new JarFile(depJar, false); + try { + attr = jarFile.getManifest().getMainAttributes(); + } finally { + jarFile.close(); } - Element cnbEl = XMLUtil.findElement(dep, "code-name-base", NBM_NS); - String cnb = XMLUtil.findText(cnbEl); - if ("org.openide".equals (cnb)) { - // XXX special handling of splited openide, can be removed - // after 5.0 release or when apisupport improved - getProject ().log ("Do not depend on org.openide anymore, depend on its libraries. Update " + getProjectFile (), getProject().MSG_WARN); - - cp.append (computeClasspathModuleLocation (modules, "org.openide.util").getAbsolutePath ()); - cp.append(File.pathSeparatorChar); - cp.append (computeClasspathModuleLocation (modules, "org.openide.util.enumerations").getAbsolutePath ()); - cp.append(File.pathSeparatorChar); - cp.append (computeClasspathModuleLocation (modules, "org.openide.filesystems").getAbsolutePath ()); - cp.append(File.pathSeparatorChar); - cp.append (computeClasspathModuleLocation (modules, "org.openide.modules").getAbsolutePath ()); - cp.append(File.pathSeparatorChar); - cp.append (computeClasspathModuleLocation (modules, "org.openide.awt").getAbsolutePath ()); - cp.append(File.pathSeparatorChar); - cp.append (computeClasspathModuleLocation (modules, "org.openide.dialogs").getAbsolutePath ()); - cp.append(File.pathSeparatorChar); - cp.append (computeClasspathModuleLocation (modules, "org.openide.loaders").getAbsolutePath ()); - cp.append(File.pathSeparatorChar); - cp.append (computeClasspathModuleLocation (modules, "org.openide.nodes").getAbsolutePath ()); - cp.append(File.pathSeparatorChar); - cp.append (computeClasspathModuleLocation (modules, "org.openide.explorer").getAbsolutePath ()); - cp.append(File.pathSeparatorChar); - cp.append (computeClasspathModuleLocation (modules, "org.openide.actions").getAbsolutePath ()); - cp.append(File.pathSeparatorChar); - cp.append (computeClasspathModuleLocation (modules, "org.openide.text").getAbsolutePath ()); - cp.append(File.pathSeparatorChar); - cp.append (computeClasspathModuleLocation (modules, "org.openide.windows").getAbsolutePath ()); - cp.append(File.pathSeparatorChar); - cp.append (computeClasspathModuleLocation (modules, "org.openide.options").getAbsolutePath ()); - cp.append(File.pathSeparatorChar); - cp.append (computeClasspathModuleLocation (modules, "org.openide.compat").getAbsolutePath ()); - - continue; + if (!dep.matches(attr)) { // #68631 + throw new BuildException("Cannot compile against a module: " + depJar + " because of dependency: " + dep, getLocation()); } - - cp.append(computeClasspathModuleLocation(modules, cnb).getAbsolutePath()); + + List/**/ additions = new ArrayList(); + additions.add(depJar); // #52354: look for s in dependent modules. ModuleListParser.Entry entry = modules.findByCodeNameBase(cnb); if (entry != null) { File[] exts = entry.getClassPathExtensions(); - for (int i = 0; i < exts.length; i++) { + for (int j = 0; j < exts.length; j++) { + additions.add(exts[j]); + } + } + + if (!dep.impl) { + String friends = attr.getValue("OpenIDE-Module-Friends"); + if (friends != null && !Arrays.asList(friends.split(" *, *")).contains(myCnb)) { + throw new BuildException("The module " + myCnb + " is not a friend of " + depJar, getLocation()); + } + String pubpkgs = attr.getValue("OpenIDE-Module-Public-Packages"); + if ("-".equals(pubpkgs)) { + throw new BuildException("The module " + depJar + " has no public packages and so cannot be compiled against", getLocation()); + } else if (pubpkgs != null) { + File splitJar = createPublicPackageJar(additions, pubpkgs, publicPackageJarDir, cnb); + additions.clear(); + additions.add(splitJar); + } + } + + Iterator it = additions.iterator(); + while (it.hasNext()) { + if (cp.length() > 0) { cp.append(':'); - cp.append(exts[i].getAbsolutePath()); } + cp.append(((File) it.next()).getAbsolutePath()); } } // Also look for s for myself and put them in my own classpath. - String cnb = getCodeNameBase(pDoc); - ModuleListParser.Entry entry = modules.findByCodeNameBase(cnb); + ModuleListParser.Entry entry = modules.findByCodeNameBase(myCnb); assert entry != null; File[] exts = entry.getClassPathExtensions(); for (int i = 0; i < exts.length; i++) { @@ -612,14 +681,17 @@ return cp.toString(); } - private File computeClasspathModuleLocation(ModuleListParser modules, String cnb) throws BuildException { + private File computeClasspathModuleLocation(ModuleListParser modules, String cnb, Set/**/ excludedClusters, Set/**/ excludedModules) throws BuildException { ModuleListParser.Entry module = modules.findByCodeNameBase(cnb); if (module == null) { throw new BuildException("No dependent module " + cnb, getLocation()); } - // XXX if that module is projectized, check its public - // packages; if it has none, halt the build, unless we are - // declaring an impl dependency + if (excludedClusters != null && excludedClusters.contains(module.getClusterName())) { // #68716 + throw new BuildException("Module " + cnb + " part of cluster " + module.getClusterName() + " which is excluded from the target platform", getLocation()); + } + if (excludedModules != null && excludedModules.contains(cnb)) { // again #68716 + throw new BuildException("Module " + cnb + " excluded from the target platform", getLocation()); + } return module.getJar(); } @@ -646,6 +718,80 @@ list.append(reltext); } return list != null ? list.toString() : null; + } + + /** + * Create a compact JAR containing only classes in public packages. + * Forces the compiler to honor public package restrictions. + * @see "#59792" + */ + private File createPublicPackageJar(List/**/ jars, String pubpkgs, File dir, String cnb) throws IOException { + File ppjar = new File(dir, cnb.replace('.', '-') + ".jar"); + if (ppjar.exists()) { + // Check if it is up to date first. Must be as new as any input JAR. + boolean uptodate = true; + long stamp = ppjar.lastModified(); + Iterator it = jars.iterator(); + while (it.hasNext()) { + File jar = (File) it.next(); + if (jar.lastModified() > stamp) { + uptodate = false; + break; + } + } + if (uptodate) { + log("Distilled " + ppjar + " was already up to date", Project.MSG_VERBOSE); + return ppjar; + } + } + log("Distilling " + ppjar + " from " + jars); + String corePattern = pubpkgs. + replaceAll(" +", ""). + replaceAll("\\.", "/"). + replaceAll(",", "|"). + replaceAll("\\*\\*", "(.+/)?"). + replaceAll("\\*", ""); + Pattern p = Pattern.compile("(" + corePattern + ")[^/]+\\.class"); + // E.g.: (org/netbeans/api/foo/|org/netbeans/spi/foo/)[^/]+\.class + OutputStream os = new FileOutputStream(ppjar); + try { + ZipOutputStream zos = new ZipOutputStream(os); + Iterator it = jars.iterator(); + while (it.hasNext()) { + File jar = (File) it.next(); + InputStream is = new FileInputStream(jar); + try { + ZipInputStream zis = new ZipInputStream(is); + ZipEntry inEntry; + while ((inEntry = zis.getNextEntry()) != null) { + String path = inEntry.getName(); + if (!p.matcher(path).matches()) { + continue; + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buf = new byte[4096]; + int read; + while ((read = zis.read(buf)) != -1) { + baos.write(buf, 0, read); + } + byte[] data = baos.toByteArray(); + ZipEntry outEntry = new ZipEntry(path); + outEntry.setSize(data.length); + CRC32 crc = new CRC32(); + crc.update(data); + outEntry.setCrc(crc.getValue()); + zos.putNextEntry(outEntry); + zos.write(data); + } + } finally { + is.close(); + } + } + zos.close(); + } finally { + os.close(); + } + return ppjar; } } Index: nbbuild/templates/projectized.xml =================================================================== RCS file: /cvs/nbbuild/templates/projectized.xml,v --- nbbuild/templates/projectized.xml 28 Dec 2005 20:22:06 -0000 1.63 +++ nbbuild/templates/projectized.xml 30 Dec 2005 03:55:45 -0000 @@ -20,7 +20,18 @@ - + + +