0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.apisupport.project;
0043:
0044: import org.netbeans.modules.apisupport.project.spi.NbModuleProvider;
0045: import java.beans.PropertyChangeEvent;
0046: import java.beans.PropertyChangeListener;
0047: import java.beans.PropertyChangeSupport;
0048: import java.io.File;
0049: import java.io.IOException;
0050: import java.net.URI;
0051: import java.net.URL;
0052: import java.util.ArrayList;
0053: import java.util.Arrays;
0054: import java.util.Collection;
0055: import java.util.Collections;
0056: import java.util.HashMap;
0057: import java.util.HashSet;
0058: import java.util.Iterator;
0059: import java.util.List;
0060: import java.util.Map;
0061: import java.util.Set;
0062: import java.util.TreeMap;
0063: import java.util.regex.Matcher;
0064: import java.util.regex.Pattern;
0065: import javax.swing.event.ChangeListener;
0066: import org.netbeans.api.java.classpath.ClassPath;
0067: import org.netbeans.api.java.platform.JavaPlatform;
0068: import org.netbeans.api.java.platform.JavaPlatformManager;
0069: import org.netbeans.api.project.ProjectManager;
0070: import org.netbeans.modules.apisupport.project.universe.TestModuleDependency;
0071: import org.netbeans.modules.apisupport.project.universe.ModuleEntry;
0072: import org.netbeans.modules.apisupport.project.universe.ModuleList;
0073: import org.netbeans.spi.project.support.ant.AntProjectEvent;
0074: import org.netbeans.spi.project.support.ant.AntProjectHelper;
0075: import org.netbeans.spi.project.support.ant.AntProjectListener;
0076: import org.netbeans.spi.project.support.ant.PropertyEvaluator;
0077: import org.netbeans.spi.project.support.ant.PropertyProvider;
0078: import org.netbeans.spi.project.support.ant.PropertyUtils;
0079: import org.openide.ErrorManager;
0080: import org.openide.filesystems.FileObject;
0081: import org.openide.filesystems.FileUtil;
0082: import org.openide.modules.InstalledFileLocator;
0083: import org.openide.util.ChangeSupport;
0084: import org.openide.util.Mutex;
0085: import org.openide.util.RequestProcessor;
0086: import org.openide.util.WeakListeners;
0087: import org.w3c.dom.Element;
0088:
0089: /**
0090: * Property evaluator for {@link NbModuleProject}.
0091: * Has two special behaviors of note:
0092: * 1. Does not call ModuleList until it really needs to.
0093: * 2. Is reset upon project.xml changes.
0094: * @author Jesse Glick, Martin Krauskopf
0095: */
0096: final class Evaluator implements PropertyEvaluator,
0097: PropertyChangeListener, AntProjectListener {
0098:
0099: private final NbModuleProject project;
0100: private final NbModuleProvider typeProvider;
0101: private final PropertyChangeSupport pcs = new PropertyChangeSupport(
0102: this );
0103:
0104: private PropertyEvaluator delegate;
0105: private boolean loadedModuleList = false;
0106:
0107: /** See issue #69440 for more details. */
0108: private boolean runInAtomicAction;
0109:
0110: private static class TestClasspath {
0111:
0112: private final String compile;
0113: private final String runtime;
0114: private final String testCompile;
0115: private final String testRuntime;
0116:
0117: public TestClasspath(String compile, String runtime,
0118: String testCompile, String testRuntime) {
0119: this .compile = compile;
0120: this .runtime = runtime;
0121: this .testCompile = testCompile;
0122: this .testRuntime = testRuntime;
0123: }
0124:
0125: public String getCompileClasspath() {
0126: return compile + ':' + testCompile;
0127: }
0128:
0129: public String getRuntimeClasspath() {
0130: return runtime + ':' + testRuntime;
0131: }
0132:
0133: private static TestClasspath getOrEmpty(Map testsCPs,
0134: String testtype) {
0135: TestClasspath tcp = (TestClasspath) testsCPs.get(testtype);
0136: if (tcp == null) {
0137: // create with empty classpaths
0138: tcp = new TestClasspath("", "", "", ""); // NOI18N
0139: }
0140: return tcp;
0141: }
0142: }
0143:
0144: public Evaluator(NbModuleProject project,
0145: NbModuleProvider typeProvider) {
0146: this .project = project;
0147: this .typeProvider = typeProvider;
0148: delegate = createEvaluator(null);
0149: delegate.addPropertyChangeListener(this );
0150: project.getHelper().addAntProjectListener(this );
0151: }
0152:
0153: public String getProperty(String prop) {
0154: PropertyEvaluator eval = delegatingEvaluator(false);
0155: assert eval != this ;
0156: String v = eval.getProperty(prop);
0157: if ((v == null && isModuleListDependentProperty(prop))
0158: || isModuleListDependentValue(v)) {
0159: return delegatingEvaluator(true).getProperty(prop);
0160: } else {
0161: return v;
0162: }
0163: }
0164:
0165: public String evaluate(String text) {
0166: String v = delegatingEvaluator(false).evaluate(text);
0167: if (isModuleListDependentValue(v)) {
0168: return delegatingEvaluator(true).evaluate(text);
0169: } else {
0170: return v;
0171: }
0172: }
0173:
0174: public Map<String, String> getProperties() {
0175: return delegatingEvaluator(true).getProperties();
0176: }
0177:
0178: private boolean isModuleListDependentProperty(String p) {
0179: return p.equals("module.classpath")
0180: || // NOI18N
0181: p.equals("cp") || p.endsWith(".cp")
0182: || p.endsWith(".cp.extra") || // NOI18N
0183: p.equals("cluster") || // NOI18N
0184: // MODULENAME.dir, but not module.jar.dir or the like:
0185: (p.endsWith(".dir") && p.lastIndexOf('.',
0186: p.length() - 5) == -1); // NOI18N
0187: }
0188:
0189: private static final Pattern ANT_PROP_REGEX = Pattern
0190: .compile("\\$\\{([a-zA-Z0-9._-]+)\\}"); // NOI18N
0191:
0192: private boolean isModuleListDependentValue(String v) {
0193: if (v == null) {
0194: return false;
0195: }
0196: Matcher m = ANT_PROP_REGEX.matcher(v);
0197: while (m.find()) {
0198: if (isModuleListDependentProperty(m.group(1))) {
0199: return true;
0200: }
0201: }
0202: return false;
0203: }
0204:
0205: public void addPropertyChangeListener(
0206: PropertyChangeListener listener) {
0207: pcs.addPropertyChangeListener(listener);
0208: }
0209:
0210: public void removePropertyChangeListener(
0211: PropertyChangeListener listener) {
0212: pcs.removePropertyChangeListener(listener);
0213: }
0214:
0215: private PropertyEvaluator delegatingEvaluator(final boolean reset) {
0216: return ProjectManager.mutex().readAccess(
0217: new Mutex.Action<PropertyEvaluator>() {
0218: public PropertyEvaluator run() {
0219: synchronized (Evaluator.this ) {
0220: if (reset && !loadedModuleList) {
0221: reset();
0222: if (Util.err
0223: .isLoggable(ErrorManager.INFORMATIONAL)) {
0224: Util.err
0225: .log("Needed to reset evaluator in "
0226: + project
0227: + "due to use of module-list-dependent property; now cp="
0228: + delegate
0229: .getProperty("cp"));
0230: }
0231: }
0232: return delegate;
0233: }
0234: }
0235: });
0236: }
0237:
0238: private void reset() {
0239: ProjectManager.mutex().readAccess(new Mutex.Action<Void>() {
0240: public Void run() {
0241: ModuleList moduleList;
0242: try {
0243: moduleList = project.getModuleList();
0244: } catch (IOException e) {
0245: Util.err.notify(ErrorManager.INFORMATIONAL, e);
0246: // but leave old evaluator in place for now
0247: return null;
0248: }
0249: synchronized (Evaluator.this ) {
0250: loadedModuleList = true;
0251: delegate
0252: .removePropertyChangeListener(Evaluator.this );
0253: delegate = createEvaluator(moduleList);
0254: delegate.addPropertyChangeListener(Evaluator.this );
0255: // XXX better to compute diff between previous and new values and fire just those
0256: pcs.firePropertyChange(null, null, null);
0257: return null;
0258: }
0259: }
0260: });
0261: }
0262:
0263: public void propertyChange(PropertyChangeEvent evt) {
0264: if ("netbeans.dest.dir".equals(evt.getPropertyName())
0265: || evt.getPropertyName() == null) {
0266: // Module list may have changed.
0267: reset();
0268: } else {
0269: Util.err.log("Refiring property change from delegate in "
0270: + evt.getPropertyName() + " for " + project);
0271: pcs.firePropertyChange(evt.getPropertyName(), evt
0272: .getOldValue(), evt.getNewValue());
0273: }
0274: }
0275:
0276: public void configurationXmlChanged(AntProjectEvent ev) {
0277: if (!runInAtomicAction
0278: && ev.getPath().equals(
0279: AntProjectHelper.PROJECT_XML_PATH)) {
0280: reset();
0281: }
0282: }
0283:
0284: public void propertiesChanged(AntProjectEvent ev) {
0285: /* TODO: Not needed now? Put here at least some comment. */
0286: }
0287:
0288: /** See issue #69440 for more details. */
0289: public void setRunInAtomicAction(boolean runInAtomicAction) {
0290: assert ProjectManager.mutex().isWriteAccess();
0291: this .runInAtomicAction = runInAtomicAction;
0292: }
0293:
0294: public void removeListeners() {
0295: project.getHelper().removeAntProjectListener(this );
0296: delegate.removePropertyChangeListener(this );
0297: }
0298:
0299: /**
0300: * Create a property evaluator: private project props, shared project props, various defaults.
0301: * Synch with nbbuild/templates/projectized.xml.
0302: * @param ml this module list, or may be left null to skip all properties which require knowledge of other modules
0303: */
0304: private PropertyEvaluator createEvaluator(ModuleList ml) {
0305: // XXX a lot of this duplicates ModuleList.parseProperties... can they be shared?
0306: PropertyProvider predefs = project.getHelper()
0307: .getStockPropertyPreprovider();
0308: Map<String, String> stock = new HashMap<String, String>();
0309: File dir = project.getProjectDirectoryFile();
0310: NbModuleProvider.NbModuleType type = typeProvider
0311: .getModuleType();
0312: File nbroot;
0313: if (type == NbModuleProvider.NETBEANS_ORG) {
0314: nbroot = ModuleList.findNetBeansOrg(dir);
0315: assert nbroot != null : "netbeans.org-type module not in a complete netbeans.org source root "
0316: + dir;
0317: stock.put("nb_all", nbroot.getAbsolutePath()); // NOI18N
0318: // Only needed for netbeans.org modules, since for external modules suite.properties suffices.
0319: stock.put("netbeans.dest.dir", new File(nbroot,
0320: ModuleList.DEST_DIR_IN_NETBEANS_ORG)
0321: .getAbsolutePath()); // NOI18N
0322: } else {
0323: nbroot = null;
0324: }
0325: String codeNameBase = project.getCodeNameBase();
0326: if (ml != null) {
0327: // Register *.dir for nb.org modules. There is no equivalent for external modules.
0328: for (ModuleEntry e : ml.getAllEntriesSoft()) {
0329: String nborgPath = e.getNetBeansOrgPath();
0330: if (nborgPath != null) {
0331: // #48449: intern these; number is (size of modules.xml) * (# of loaded module projects)
0332: stock.put((nborgPath + ".dir").intern(), e
0333: .getClusterDirectory().getAbsolutePath()
0334: .intern()); // NOI18N
0335: }
0336: }
0337: ModuleEntry this Entry = ml.getEntry(codeNameBase);
0338: if (this Entry != null) { // can be null e.g. for a broken suite component module
0339: assert nbroot == null
0340: ^ this Entry.getNetBeansOrgPath() != null : this Entry;
0341: File clusterDir = this Entry.getClusterDirectory();
0342: stock.put("cluster", clusterDir.getAbsolutePath()); // NOI18N
0343: }
0344: }
0345: List<PropertyProvider> providers = new ArrayList<PropertyProvider>();
0346: providers.add(PropertyUtils.fixedPropertyProvider(stock));
0347: // XXX should listen to changes in values of properties which refer to property files:
0348: if (type == NbModuleProvider.SUITE_COMPONENT) {
0349: providers.add(project.getHelper().getPropertyProvider(
0350: "nbproject/private/suite-private.properties")); // NOI18N
0351: providers.add(project.getHelper().getPropertyProvider(
0352: "nbproject/suite.properties")); // NOI18N
0353: PropertyEvaluator baseEval = PropertyUtils
0354: .sequentialPropertyEvaluator(predefs, providers
0355: .toArray(new PropertyProvider[providers
0356: .size()]));
0357: String suiteDirS = baseEval.getProperty("suite.dir"); // NOI18N
0358: if (suiteDirS != null) {
0359: File suiteDir = PropertyUtils.resolveFile(dir,
0360: suiteDirS);
0361: providers
0362: .add(PropertyUtils
0363: .propertiesFilePropertyProvider(new File(
0364: suiteDir,
0365: "nbproject"
0366: + File.separatorChar
0367: + "private"
0368: + File.separatorChar
0369: + "platform-private.properties"))); // NOI18N
0370: providers.add(PropertyUtils
0371: .propertiesFilePropertyProvider(new File(
0372: suiteDir, "nbproject"
0373: + File.separatorChar
0374: + "platform.properties"))); // NOI18N
0375: }
0376: } else if (type == NbModuleProvider.STANDALONE) {
0377: providers.add(project.getHelper().getPropertyProvider(
0378: "nbproject/private/platform-private.properties")); // NOI18N
0379: providers.add(project.getHelper().getPropertyProvider(
0380: "nbproject/platform.properties")); // NOI18N
0381: }
0382: if (type == NbModuleProvider.SUITE_COMPONENT
0383: || type == NbModuleProvider.STANDALONE) {
0384: PropertyEvaluator baseEval = PropertyUtils
0385: .sequentialPropertyEvaluator(predefs, providers
0386: .toArray(new PropertyProvider[providers
0387: .size()]));
0388: providers.add(new Util.UserPropertiesFileProvider(baseEval,
0389: dir));
0390: baseEval = PropertyUtils.sequentialPropertyEvaluator(
0391: predefs, providers
0392: .toArray(new PropertyProvider[providers
0393: .size()]));
0394: class DestDirProvider extends Util.ComputedPropertyProvider {
0395: public DestDirProvider(PropertyEvaluator eval) {
0396: super (eval);
0397: }
0398:
0399: protected Map<String, String> getProperties(
0400: Map<String, String> inputPropertyValues) {
0401: String platformS = inputPropertyValues
0402: .get("nbplatform.active"); // NOI18N
0403: if (platformS != null) {
0404: return Collections.singletonMap(
0405: "netbeans.dest.dir", "${nbplatform."
0406: + platformS
0407: + ".netbeans.dest.dir}"); // NOI18N
0408: } else {
0409: return Collections.emptyMap();
0410: }
0411: }
0412:
0413: protected Set<String> inputProperties() {
0414: return Collections.singleton("nbplatform.active"); // NOI18N
0415: }
0416: }
0417: providers.add(new DestDirProvider(baseEval));
0418: }
0419: if (type == NbModuleProvider.NETBEANS_ORG) {
0420: // For local definitions of nbjdk.* properties:
0421: File nbbuild = new File(nbroot, "nbbuild"); // NOI18N
0422: providers.add(PropertyUtils
0423: .propertiesFilePropertyProvider(new File(nbbuild,
0424: "user.build.properties"))); // NOI18N
0425: providers.add(PropertyUtils
0426: .propertiesFilePropertyProvider(new File(nbbuild,
0427: "site.build.properties"))); // NOI18N
0428: providers.add(PropertyUtils
0429: .propertiesFilePropertyProvider(new File(System
0430: .getProperty("user.home"),
0431: ".nbbuild.properties"))); // NOI18N
0432: }
0433: PropertyEvaluator baseEval = PropertyUtils
0434: .sequentialPropertyEvaluator(predefs,
0435: providers
0436: .toArray(new PropertyProvider[providers
0437: .size()]));
0438: providers.add(new NbJdkProvider(baseEval));
0439: providers.add(project.getHelper().getPropertyProvider(
0440: AntProjectHelper.PRIVATE_PROPERTIES_PATH));
0441: providers.add(project.getHelper().getPropertyProvider(
0442: AntProjectHelper.PROJECT_PROPERTIES_PATH));
0443: Map<String, String> defaults = new HashMap<String, String>();
0444: if (codeNameBase != null) { // #121856
0445: defaults.put("code.name.base.dashes", codeNameBase.replace(
0446: '.', '-')); // NOI18N
0447: }
0448: defaults.put("module.jar.dir", "modules"); // NOI18N
0449: defaults.put("module.jar.basename",
0450: "${code.name.base.dashes}.jar"); // NOI18N
0451: defaults.put("module.jar",
0452: "${module.jar.dir}/${module.jar.basename}"); // NOI18N
0453: defaults.put("manifest.mf", "manifest.mf"); // NOI18N
0454: defaults.put("src.dir", "src"); // NOI18N
0455: defaults.put("build.classes.dir", "build/classes"); // NOI18N
0456: defaults.put("test.unit.src.dir", "test/unit/src"); // NOI18N
0457: defaults.put("test.qa-functional.src.dir",
0458: "test/qa-functional/src"); // NOI18N
0459: defaults.put("test.qa-performance.src.dir",
0460: "test/qa-performance/src"); // NOI18N
0461: defaults.put("build.test.unit.classes.dir",
0462: "build/test/unit/classes"); // NOI18N
0463: defaults.put("javac.source", "1.4"); // NOI18N
0464: defaults.put("test.user.dir",
0465: new File(dir, "build/testuserdir").getAbsolutePath()); // NOI18N
0466: providers.add(PropertyUtils.fixedPropertyProvider(defaults));
0467: if (ml != null) {
0468: providers.add(PropertyUtils
0469: .fixedPropertyProvider(Collections.singletonMap(
0470: "module.classpath",
0471: computeModuleClasspath(ml)))); // NOI18N
0472: Map<String, String> buildDefaults = new HashMap<String, String>();
0473: buildDefaults.put("cp.extra", ""); // NOI18N
0474: buildDefaults.put("cp", "${module.classpath}:${cp.extra}"); // NOI18N
0475: buildDefaults.put("run.cp",
0476: computeRuntimeModuleClasspath(ml)
0477: + ":${cp.extra}:${build.classes.dir}"); // NOI18N
0478:
0479: baseEval = PropertyUtils.sequentialPropertyEvaluator(
0480: predefs, providers
0481: .toArray(new PropertyProvider[providers
0482: .size()]));
0483: buildDefaults.put("test.unit.cp.extra", ""); // NOI18N
0484: String testJars; // #68685 - follow Ant script
0485: if (type == NbModuleProvider.NETBEANS_ORG) {
0486: // Cf. nbbuild/templates/projectized.xml#test-lib-init
0487: buildDefaults.put("xtest.home", "${nb_all}/xtest"); // NOI18N
0488: testJars = "${xtest.home}/lib/junit.jar:" + // NOI18N
0489: "${xtest.home}/lib/nbjunit.jar:" + // NOI18N
0490: "${xtest.home}/lib/nbjunit-ide.jar:" + // NOI18N
0491: "${xtest.home}/lib/insanelib.jar"; // NOI18N
0492: } else {
0493: // Cf. apisupport/harness/release/build.xml#test-lib-init
0494: testJars = "${test.unit.lib.cp}:"
0495: + // NOI18N
0496: // XXX this is ugly, try to look for the JAR using wildcards instead
0497: "${netbeans.dest.dir}/ide6/modules/ext/junit-3.8.1.jar:"
0498: + // NOI18N
0499: "${netbeans.dest.dir}/java2/modules/ext/junit-3.8.2.jar:"
0500: + // NOI18N
0501: "${netbeans.dest.dir}/java2/modules/ext/junit-4.1.jar:"
0502: + // NOI18N
0503: "${netbeans.dest.dir}/testtools/modules/ext/nbjunit.jar:"
0504: + // NOI18N
0505: "${netbeans.dest.dir}/testtools/modules/ext/insanelib.jar:"
0506: + // NOI18N
0507: "${netbeans.dest.dir}/testtools/modules/org-netbeans-modules-nbjunit.jar:"
0508: + // NOI18N, new for 6.0
0509: "${netbeans.dest.dir}/testtools/modules/org-netbeans-modules-nbjunit-ide.jar:"
0510: + // NOI18N, new for 6.0
0511: "${netbeans.home}/../ide6/modules/ext/junit-3.8.1.jar:"
0512: + // NOI18N
0513: "${netbeans.home}/../java2/modules/ext/junit-3.8.2.jar:"
0514: + // NOI18N
0515: "${netbeans.home}/../java2/modules/ext/junit-4.1.jar:"
0516: + // NOI18N
0517: "${netbeans.home}/../testtools/modules/ext/nbjunit.jar:"
0518: + // NOI18N
0519: "${netbeans.home}/../testtools/modules/ext/insanelib.jar:"
0520: + // NOI18N
0521: "${netbeans.home}/../testtools/modules/org-netbeans-modules-nbjunit.jar:"
0522: + // NOI18N, new for 6.0
0523: "${netbeans.home}/../testtools/modules/org-netbeans-modules-nbjunit-ide.jar:"
0524: + // NOI18N, new for 6.0
0525: "${netbeans.user}/modules/ext/nbjunit.jar:"
0526: + // NOI18N
0527: "${netbeans.user}/modules/ext/insanelib.jar:"
0528: + // NOI18N
0529: "${netbeans.dest.dir}/../../xtest/lib/junit.jar:"
0530: + // NOI18N
0531: "${netbeans.dest.dir}/../../xtest/lib/nbjunit.jar:"
0532: + // NOI18N
0533: "${netbeans.dest.dir}/../../xtest/lib/insanelib.jar:"
0534: + // NOI18N
0535: "${netbeans.user}/modules/org-netbeans-modules-nbjunit.jar:"
0536: + // NOI18N, new for 6.0
0537: "${netbeans.user}/modules/org-netbeans-modules-nbjunit-ide.jar"; // NOI18N, new for 6.0
0538: }
0539: Map<String, TestClasspath> testsCPs = computeTestingClassPaths(
0540: ml, baseEval);
0541: TestClasspath tcp = TestClasspath.getOrEmpty(testsCPs,
0542: "unit"); // NOI18N
0543:
0544: buildDefaults.put("test.unit.cp",
0545: "${cp}:${cluster}/${module.jar}:" + testJars
0546: + ":${test.unit.cp.extra}:"
0547: + tcp.getCompileClasspath()); // NOI18N
0548: buildDefaults.put("test.unit.run.cp.extra", ""); // NOI18N
0549: buildDefaults.put("test.unit.run.cp",
0550: "${test.unit.cp}:${build.test.unit.classes.dir}:${test.unit.run.cp.extra}:"
0551: + tcp.getRuntimeClasspath()); // NOI18N
0552: // #61085: need to treat qa-functional tests the same way...
0553: buildDefaults.put("test.qa-functional.cp.extra", ""); // NOI18N
0554: // No idea how XTest finds these, some weird magic, so no Ant script to match up to:
0555: String jemmyJar = findJemmyJar(baseEval);
0556: if (jemmyJar != null) {
0557: buildDefaults.put("jemmy.jar", jemmyJar); // NOI18N
0558: }
0559: String jelly2NbJar = findJelly2NbJar(baseEval);
0560: if (jelly2NbJar != null) {
0561: buildDefaults.put("jelly2-nb.jar", jelly2NbJar); // NOI18N
0562: }
0563: tcp = TestClasspath.getOrEmpty(testsCPs, "qa-functional"); // NOI18N
0564: buildDefaults.put("test.qa-functional.cp", testJars
0565: + // NOI18N
0566: ":${netbeans.home}/../testtools/modules/ext/nbjunit-ide.jar"
0567: + // NOI18N
0568: ":${netbeans.user}/testtools/modules/ext/nbjunit.jar"
0569: + // NOI18N
0570: ":${jemmy.jar}" + // NOI18N
0571: ":${jelly2-nb.jar}" + // NOI18N
0572: ":${test.qa-functional.cp.extra}:" + // NOI18N
0573: tcp.compile + ':' + tcp.testCompile);
0574: buildDefaults.put("build.test.qa-functional.classes.dir",
0575: "build/test/qa-functional/classes"); // NOI18N
0576: buildDefaults
0577: .put("test.qa-functional.run.cp",
0578: "${test.qa-functional.cp}:${build.test.qa-functional.classes.dir}"
0579: + ':' + tcp.runtime + ':'
0580: + tcp.testRuntime); // NOI18N
0581: providers.add(PropertyUtils
0582: .fixedPropertyProvider(buildDefaults));
0583: }
0584: // skip a bunch of properties irrelevant here - NBM stuff, etc.
0585: return PropertyUtils
0586: .sequentialPropertyEvaluator(predefs,
0587: providers
0588: .toArray(new PropertyProvider[providers
0589: .size()]));
0590: }
0591:
0592: private final class NbJdkProvider implements PropertyProvider,
0593: PropertyChangeListener { // #63541: JDK selection
0594:
0595: private final PropertyEvaluator eval;
0596: private final ChangeSupport changeSupport = new ChangeSupport(
0597: this );
0598: private final PropertyChangeListener weakListener = WeakListeners
0599: .propertyChange(this , null);
0600:
0601: public NbJdkProvider(PropertyEvaluator eval) {
0602: this .eval = eval;
0603: eval.addPropertyChangeListener(weakListener);
0604: JavaPlatformManager.getDefault().addPropertyChangeListener(
0605: weakListener);
0606: }
0607:
0608: public final Map<String, String> getProperties() {
0609: Map<String, String> props = new HashMap<String, String>();
0610: String home = eval.getProperty("nbjdk.home"); // NOI18N
0611: if (home == null) {
0612: String active = eval.getProperty("nbjdk.active"); // NOI18N
0613: if (active != null && !active.equals("default")) { // NOI18N
0614: home = eval.getProperty("platforms." + active
0615: + ".home"); // NOI18N
0616: if (home != null) {
0617: props.put("nbjdk.home", home); // NOI18N
0618: }
0619: }
0620: }
0621: if (home == null) {
0622: JavaPlatform platform = JavaPlatformManager
0623: .getDefault().getDefaultPlatform();
0624: if (platform != null) {
0625: Collection<FileObject> installs = platform
0626: .getInstallFolders();
0627: if (installs.size() == 1) {
0628: home = FileUtil.toFile(
0629: installs.iterator().next())
0630: .getAbsolutePath();
0631: }
0632: }
0633: }
0634: String bootcp = null;
0635: if (home != null) {
0636: FileObject homeFO = FileUtil.toFileObject(FileUtil
0637: .normalizeFile(new File(home)));
0638: if (homeFO != null) {
0639: for (JavaPlatform platform : JavaPlatformManager
0640: .getDefault().getInstalledPlatforms()) {
0641: if (new HashSet<FileObject>(platform
0642: .getInstallFolders())
0643: .equals(Collections.singleton(homeFO))) {
0644: // Matching JDK is registered, so look up its real bootcp.
0645: StringBuffer bootcpSB = new StringBuffer();
0646: ClassPath boot = platform
0647: .getBootstrapLibraries();
0648: boot
0649: .removePropertyChangeListener(weakListener);
0650: boot
0651: .addPropertyChangeListener(weakListener);
0652: for (ClassPath.Entry entry : boot.entries()) {
0653: URL u = entry.getURL();
0654: if (u.toExternalForm().endsWith("!/")) { // NOI18N
0655: URL nested = FileUtil
0656: .getArchiveFile(u);
0657: if (nested != null) {
0658: u = nested;
0659: }
0660: }
0661: if ("file".equals(u.getProtocol())) { // NOI18N
0662: File f = new File(URI.create(u
0663: .toExternalForm()));
0664: if (bootcpSB.length() > 0) {
0665: bootcpSB
0666: .append(File.pathSeparatorChar);
0667: }
0668: bootcpSB
0669: .append(f.getAbsolutePath());
0670: }
0671: }
0672: bootcp = bootcpSB.toString();
0673: break;
0674: }
0675: }
0676: }
0677: if (bootcp == null) {
0678: bootcp = "${nbjdk.home}/jre/lib/rt.jar".replace(
0679: '/', File.separatorChar); // NOI18N
0680: }
0681: }
0682: if (bootcp == null) {
0683: // Real fallback...
0684: bootcp = "${sun.boot.class.path}"; // NOI18N
0685: }
0686: props.put("nbjdk.bootclasspath", bootcp); // NOI18N
0687: if (home != null) {
0688: props.put("tools.jar", home
0689: + "/lib/tools.jar".replace('/',
0690: File.separatorChar)); // NOI18N
0691: }
0692: if (Util.err.isLoggable(ErrorManager.INFORMATIONAL)) {
0693: Map<String, String> _props = new TreeMap<String, String>(
0694: eval.getProperties());
0695: Iterator<String> it = _props.keySet().iterator();
0696: while (it.hasNext()) {
0697: String k = it.next();
0698: if (!k.startsWith("nbjdk.")
0699: && !k.startsWith("platforms.")) { // NOI18N
0700: it.remove();
0701: }
0702: }
0703: _props.putAll(props);
0704: Util.err.log("JDK-related properties of " + project
0705: + ": " + _props);
0706: }
0707: return props;
0708: }
0709:
0710: public final void addChangeListener(ChangeListener l) {
0711: changeSupport.addChangeListener(l);
0712: }
0713:
0714: public final void removeChangeListener(ChangeListener l) {
0715: changeSupport.removeChangeListener(l);
0716: }
0717:
0718: public final void propertyChange(PropertyChangeEvent evt) {
0719: String p = evt.getPropertyName();
0720: if (p != null && !p.startsWith("nbjdk.")
0721: && !p.startsWith("platforms.")
0722: && // NOI18N
0723: !p.equals(ClassPath.PROP_ENTRIES)
0724: && !p
0725: .equals(JavaPlatformManager.PROP_INSTALLED_PLATFORMS)) {
0726: return;
0727: }
0728: if (!changeSupport.hasListeners()) {
0729: return;
0730: }
0731: final Mutex.Action<Void> action = new Mutex.Action<Void>() {
0732: public Void run() {
0733: changeSupport.fireChange();
0734: return null;
0735: }
0736: };
0737: // See ProjectProperties.PP.fireChange for explanation of this threading stuff:
0738: if (ProjectManager.mutex().isWriteAccess()) {
0739: ProjectManager.mutex().readAccess(action);
0740: } else if (ProjectManager.mutex().isReadAccess()) {
0741: action.run();
0742: } else {
0743: RequestProcessor.getDefault().post(new Runnable() {
0744: public void run() {
0745: ProjectManager.mutex().readAccess(action);
0746: }
0747: });
0748: }
0749: }
0750:
0751: }
0752:
0753: /**
0754: * Get an Ant location for the root of jemmy.jar.
0755: */
0756: private String findJemmyJar(PropertyEvaluator eval) {
0757: File f = project.getNbrootFile("jemmy/builds/jemmy.jar", eval); // NOI18N
0758: if (f == null) {
0759: // try to find jemmy.jar installed by Jemmy module
0760: f = InstalledFileLocator.getDefault().locate(
0761: "modules/ext/jemmy.jar",
0762: "org.netbeans.modules.jemmy", false); // NOI18N
0763: }
0764: if (f != null) {
0765: return f.getAbsolutePath();
0766: } else {
0767: return null;
0768: }
0769: }
0770:
0771: /**
0772: * Get an Ant location for the root of jemmy.jar.
0773: */
0774: private String findJelly2NbJar(PropertyEvaluator eval) {
0775: File f = project.getNbrootFile(
0776: "jellytools/builds/jelly2-nb.jar", eval); // NOI18N
0777: if (f == null) {
0778: // try to find jelly2-nb.jar installed by Jellytools module
0779: f = InstalledFileLocator.getDefault().locate(
0780: "modules/ext/jelly2-nb.jar",
0781: "org.netbeans.modules.jellytools", false); // NOI18N
0782: }
0783: if (f != null) {
0784: return f.getAbsolutePath();
0785: } else {
0786: return null;
0787: }
0788: }
0789:
0790: /**
0791: * Should be similar to impl in ParseProjectXml.
0792: */
0793: private String computeModuleClasspath(ModuleList ml) {
0794: Element data = project.getPrimaryConfigurationData();
0795: Element moduleDependencies = Util.findElement(data,
0796: "module-dependencies",
0797: NbModuleProjectType.NAMESPACE_SHARED); // NOI18N
0798: assert moduleDependencies != null : "Malformed metadata in "
0799: + project;
0800: StringBuffer cp = new StringBuffer();
0801: for (Element dep : Util.findSubElements(moduleDependencies)) {
0802: if (Util.findElement(dep, "compile-dependency", // NOI18N
0803: NbModuleProjectType.NAMESPACE_SHARED) == null) {
0804: continue;
0805: }
0806: Element cnbEl = Util.findElement(dep, "code-name-base", // NOI18N
0807: NbModuleProjectType.NAMESPACE_SHARED);
0808: String cnb = Util.findText(cnbEl);
0809: ModuleEntry module = ml.getEntry(cnb);
0810: if (module == null) {
0811: Util.err.log(ErrorManager.WARNING,
0812: "Warning - could not find dependent module "
0813: + cnb
0814: + " for "
0815: + FileUtil.getFileDisplayName(project
0816: .getProjectDirectory()));
0817: continue;
0818: }
0819: File moduleJar = module.getJarLocation();
0820: if (cp.length() > 0) {
0821: cp.append(File.pathSeparatorChar);
0822: }
0823: cp.append(moduleJar.getAbsolutePath());
0824: cp.append(module.getClassPathExtensions());
0825: }
0826: ModuleEntry myself = ml.getEntry(project.getCodeNameBase());
0827: if (myself == null) {
0828: // ???
0829: return "";
0830: }
0831: cp.append(myself.getClassPathExtensions());
0832: return cp.toString();
0833: }
0834:
0835: /**
0836: * Follows transitive runtime dependencies.
0837: * @see "issue #70206"
0838: */
0839: private String computeRuntimeModuleClasspath(ModuleList ml) {
0840: Set<String> unprocessed = new HashSet<String>();
0841: unprocessed.add(project.getCodeNameBase());
0842: Set<String> processed = new HashSet<String>();
0843: StringBuffer cp = new StringBuffer();
0844: while (!unprocessed.isEmpty()) { // crude breadth-first search
0845: Iterator<String> it = unprocessed.iterator();
0846: String cnb = it.next();
0847: it.remove();
0848: if (processed.add(cnb)) {
0849: ModuleEntry module = ml.getEntry(cnb);
0850: if (module == null) {
0851: Util.err
0852: .log(
0853: ErrorManager.WARNING,
0854: "Warning - could not find dependent module "
0855: + cnb
0856: + " for "
0857: + FileUtil
0858: .getFileDisplayName(project
0859: .getProjectDirectory()));
0860: continue;
0861: }
0862: if (!cnb.equals(project.getCodeNameBase())) { // build/classes for this is special
0863: if (cp.length() > 0) {
0864: cp.append(File.pathSeparatorChar);
0865: }
0866: cp
0867: .append(module.getJarLocation()
0868: .getAbsolutePath());
0869: cp.append(module.getClassPathExtensions());
0870: }
0871: String[] newDeps = module.getRunDependencies();
0872: unprocessed.addAll(Arrays.asList(newDeps));
0873: }
0874: }
0875: ModuleEntry myself = ml.getEntry(project.getCodeNameBase());
0876: if (myself != null) {
0877: // #76341: must include <class-path-extension>s in ${run.cp} too.
0878: cp.append(myself.getClassPathExtensions());
0879: }
0880: return cp.toString();
0881: }
0882:
0883: /**
0884: * Gives a map from test type (e.g. <em>unit</em> or <em>qa-functional</em>)
0885: * to the {@link TestClasspath test classpath} according to the content in
0886: * the project's metadata (<em>project.xml<em>).
0887: */
0888: private Map<String, TestClasspath> computeTestingClassPaths(
0889: ModuleList ml, PropertyEvaluator evaluator) {
0890: Map<String, TestClasspath> classpaths = new HashMap<String, TestClasspath>();
0891: ProjectXMLManager pxm = new ProjectXMLManager(project);
0892: Map<String, Set<TestModuleDependency>> testTypes = pxm
0893: .getTestDependencies(ml);
0894:
0895: String testDistDir = evaluator.getProperty("test.dist.dir"); // NOI18N
0896: if (testDistDir == null) {
0897: NbModuleProvider.NbModuleType type = typeProvider
0898: .getModuleType();
0899: if (type == NbModuleProvider.NETBEANS_ORG) {
0900: // test.dist.dir = ${nb_all}/nbbuild/build/testdist
0901: String nball = evaluator.getProperty("nb_all"); // NOI18N
0902: testDistDir = nball + File.separatorChar + "nbbuild"
0903: + File.separatorChar + "build"
0904: + File.separatorChar + "testdist"; // NOI18N
0905: } else if (type == NbModuleProvider.SUITE_COMPONENT) {
0906: // test.dist.dir = ${suite.dir}/build/testdist
0907: String suiteDir = evaluator.getProperty("suite.dir"); // NOI18N
0908: testDistDir = suiteDir + File.separatorChar + "build"
0909: + File.separatorChar + "testdist"; // NOI18N
0910: } else {
0911: // standalone module
0912: // test.dist.dir = ${module.dir}/build/testdist
0913: String moduleDir = evaluator.getProperty("module.dir"); // NOI18N
0914: testDistDir = moduleDir + File.separatorChar + "build"
0915: + File.separatorChar + "testdist"; // NOI18N
0916: }
0917: }
0918: for (Map.Entry<String, Set<TestModuleDependency>> entry : testTypes
0919: .entrySet()) {
0920: computeTestType(entry.getKey(), new File(testDistDir),
0921: entry.getValue(), classpaths, ml);
0922: }
0923: return classpaths;
0924: }
0925:
0926: private void computeTestType(String ttName, File testDistDir,
0927: Set<TestModuleDependency> ttModules,
0928: Map<String, TestClasspath> classpaths, ModuleList ml) {
0929:
0930: Set<String> compileCnds = new HashSet<String>();
0931: Set<String> runtimeCnds = new HashSet<String>();
0932: Set<String> testCompileCnds = new HashSet<String>();
0933: Set<String> testRuntimeCnds = new HashSet<String>();
0934:
0935: Set<String> processedRecursive = new HashSet<String>();
0936: for (TestModuleDependency td : ttModules) {
0937: String cnd = td.getModule().getCodeNameBase();
0938: if (td.isTest()) {
0939: if (td.isCompile()) {
0940: testCompileCnds.add(cnd);
0941: }
0942: testRuntimeCnds.add(cnd);
0943: }
0944: if (td.isRecursive()) {
0945: // scan cp recursively
0946: processTestEntryRecursive(td, compileCnds, runtimeCnds,
0947: processedRecursive, ml);
0948: } else {
0949: runtimeCnds.add(cnd);
0950: if (td.isCompile()) {
0951: compileCnds.add(cnd);
0952: }
0953: }
0954: }
0955: TestClasspath testClasspath = new TestClasspath(
0956: mergePaths(compileCnds, false, ttName, testDistDir, ml),
0957: mergePaths(runtimeCnds, false, ttName, testDistDir, ml),
0958: mergePaths(testCompileCnds, true, ttName, testDistDir,
0959: ml), mergePaths(testRuntimeCnds, true, ttName,
0960: testDistDir, ml));
0961:
0962: classpaths.put(ttName, testClasspath);
0963: }
0964:
0965: private void processTestEntryRecursive(TestModuleDependency td,
0966: Set<String> compileCnds, Set<String> runtimeCnds,
0967: Set<String> processedRecursive, ModuleList ml) {
0968: Set<String> unprocessed = new HashSet<String>();
0969:
0970: unprocessed.add(td.getModule().getCodeNameBase());
0971: while (!unprocessed.isEmpty()) { // crude breadth-first search
0972: Iterator<String> it = unprocessed.iterator();
0973: String cnb = it.next();
0974: it.remove();
0975: if (processedRecursive.add(cnb)) {
0976: ModuleEntry module = ml.getEntry(cnb);
0977: if (module == null) {
0978: Util.err
0979: .log(
0980: ErrorManager.WARNING,
0981: "Warning - could not find dependent module "
0982: + cnb
0983: + " for "
0984: + FileUtil
0985: .getFileDisplayName(project
0986: .getProjectDirectory()));
0987: continue;
0988: }
0989: if (!cnb.equals(project.getCodeNameBase())) { // build/classes for this is special
0990: runtimeCnds.add(cnb);
0991: if (td.isCompile()) {
0992: compileCnds.add(cnb);
0993: }
0994: }
0995: String[] newDeps = module.getRunDependencies();
0996: unprocessed.addAll(Arrays.asList(newDeps));
0997: }
0998: }
0999: }
1000:
1001: private String mergePaths(Set<String> cnbs, boolean test,
1002: String testtype, File testDistDir, ModuleList ml) {
1003: StringBuffer cps = new StringBuffer();
1004: for (String cnb : cnbs) {
1005: ModuleEntry module = ml.getEntry(cnb);
1006: if (cps.length() > 0) {
1007: cps.append(':');
1008: }
1009: if (test) {
1010: // we need to get cluster name
1011: File clusterDir = module.getClusterDirectory();
1012: if (clusterDir != null) {
1013: String clusterName = clusterDir.getName();
1014: char s = File.separatorChar;
1015: File jarFile = new File(testDistDir, testtype + s
1016: + clusterName + s + cnb.replace('.', '-')
1017: + s + "tests.jar"); // NOI18N
1018: cps.append(jarFile.getPath());
1019: }
1020:
1021: } else {
1022: cps.append(module.getJarLocation().getPath());
1023: }
1024: }
1025: return cps.toString();
1026: }
1027:
1028: }
|