diff options
author | wisberg <wisberg> | 2005-06-09 00:11:27 +0000 |
---|---|---|
committer | wisberg <wisberg> | 2005-06-09 00:11:27 +0000 |
commit | bdbc4dea8d098067d58b743d68e816640a11cb6e (patch) | |
tree | 80cd643f330645e97096c000da01dfb989bf081b /build/src | |
parent | 0f3b086e25318c7652885f12f5aeeaaac62b1ddc (diff) | |
download | aspectj-bdbc4dea8d098067d58b743d68e816640a11cb6e.tar.gz aspectj-bdbc4dea8d098067d58b743d68e816640a11cb6e.zip |
Now targeting Module results (release, test, release-all, test-all) rather than modules (with variants), since that conflated test and release classes. uptodate check needs work.
Diffstat (limited to 'build/src')
5 files changed, 1306 insertions, 934 deletions
diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/AntBuilder.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/AntBuilder.java index 0288f1f20..d7bd8436d 100644 --- a/build/src/org/aspectj/internal/tools/ant/taskdefs/AntBuilder.java +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/AntBuilder.java @@ -40,18 +40,14 @@ import org.aspectj.internal.tools.build.BuildSpec; import org.aspectj.internal.tools.build.Builder; import org.aspectj.internal.tools.build.Messager; import org.aspectj.internal.tools.build.Module; -import org.aspectj.internal.tools.build.Modules; -import org.aspectj.internal.tools.build.ProductModule; +import org.aspectj.internal.tools.build.Result; import org.aspectj.internal.tools.build.Util; +import org.aspectj.internal.tools.build.Result.Kind; /** * Implement Builder in Ant. */ public class AntBuilder extends Builder { - /* - * warning: This just constructs and uses Ant Task objects, - * which in some cases causes the tasks to fail. - */ /** * Factory for a Builder. @@ -72,7 +68,7 @@ public class AntBuilder extends Builder { verbose = true; } } - Messager handler = new ProjectMessager(project); + Messager handler = new Messager(); // TODO new ProjectMessager(project); Builder result = new ProductBuilder(project, tempDir, useEclipseCompiles, handler); if (verbose) { result.setVerbose(true); @@ -80,51 +76,89 @@ public class AntBuilder extends Builder { return result; } + private static String resultToTargetName(Result result) { + return result.getName(); + } /** - * Make and register target for this module and antecedants. - * This ensures that the (direct) depends list is generated - * for each target. - * This depends on topoSort to detect cycles. XXX unverified + * Ensure targets exist for this module and all antecedants, + * so topoSort can work. */ - private static void makeTargetsForModule( - final Module module, - final Hashtable targets, - final boolean rebuild) { - Target target = (Target) targets.get(module.name); - if (null == target) { - // first add the target - target = new Target(); - target.setName(module.name); - List req = module.getRequired(); - StringBuffer depends = new StringBuffer(); - boolean first = true; - for (Iterator iterator = req.iterator(); iterator.hasNext();) { - Module reqModule = (Module) iterator.next(); - if (rebuild || reqModule.outOfDate(false)) { + private static void makeTargetsForResult( + final Result result, + final Hashtable targets) { + final String resultTargetName = resultToTargetName(result); + Target target = (Target) targets.get(resultTargetName); + if (null == target) { + // first add the target + target = new Target(); + target.setName(resultTargetName); + + Result[] reqs = result.getRequired(); + StringBuffer depends = new StringBuffer(); + boolean first = true; + for (int i = 0; i < reqs.length; i++) { + Result reqResult = reqs[i]; if (!first) { depends.append(","); } else { first = false; } - depends.append(reqModule.name); + depends.append(resultToTargetName(reqResult)); } - } - if (0 < depends.length()) { - target.setDepends(depends.toString()); - } - targets.put(module.name, target); + if (0 < depends.length()) { + target.setDepends(depends.toString()); + } + targets.put(resultTargetName, target); - // then recursively add any required modules - for (Iterator iterator = module.getRequired().iterator(); - iterator.hasNext(); - ) { - Module reqModule = (Module) iterator.next(); - if (rebuild || reqModule.outOfDate(false)) { - makeTargetsForModule(reqModule, targets, rebuild); - } - } + // then recursively add any required results + for (int i = 0; i < reqs.length; i++) { + Result reqResult = reqs[i]; + makeTargetsForResult(reqResult, targets); + } + } } - } +// private static void edited_makeTargetsForResult( +// final Result result, +// final Hashtable targets, +// final boolean rebuild) { +// String resultTargetName = result.getOutputFile().getName(); +// Target target = (Target) targets.get(resultTargetName); +// if (null == target) { +// // first add the target +// target = new Target(); +// target.setName(resultTargetName); +// Kind kind = result.getKind(); +// Module module = result.getModule(); +// Kind compileKind = Result.kind(module.isNormal(), !Result.ASSEMBLE); +// Result compileResult = module.getResult(compileKind); +// Result[] req = compileResult.getRequired(); +// StringBuffer depends = new StringBuffer(); +// boolean first = true; +// for (int i = 0; i < req.length; i++) { +// Result reqResult = req[i]; +// if (!first) { +// depends.append(","); +// } else { +// first = false; +// } +// depends.append(reqResult.name); +// } +// if (0 < depends.length()) { +// target.setDepends(depends.toString()); +// } +// targets.put(module.name, target); +// +// // then recursively add any required modules +// for (Iterator iterator = compileResult.getRequired().iterator(); +// iterator.hasNext(); +// ) { +// Module reqModule = (Module) iterator.next(); +// if (rebuild || reqModule.outOfDate(kind, false)) { +// makeTargetsForResult(reqModule, targets, rebuild, kind); +// } +// } +// } +// } private final Project project; @@ -208,18 +242,43 @@ public class AntBuilder extends Builder { } return copy; } - + protected void dumpMinFile(Result result,File classesDir, List errors) { + String name = result.getName() + "-empty"; + File minFile = new File(classesDir, name); + FileWriter fw = null; + try { + fw = new FileWriter(minFile); + fw.write(name); + } catch (IOException e) { + errors.add("IOException writing " + + name + + " to " + + minFile + + ": " + + Util.renderException(e)); + } finally { + Util.close(fw); + } + + } protected boolean compile( - Module module, + Result result, File classesDir, boolean useExistingClasses, List errors) { + if (!classesDir.exists() && !classesDir.mkdirs()) { + errors.add("compile - unable to create " + classesDir); + return false; + } + if (useExistingClasses) { + return true; + } // -- source paths Path path = new Path(project); boolean hasSourceDirectories = false; boolean isJava5Compile = false; - for (Iterator iter = module.getSrcDirs().iterator(); iter.hasNext();) { + for (Iterator iter = result.getSrcDirs().iterator(); iter.hasNext();) { File file = (File) iter.next(); path.createPathElement().setLocation(file); if (!isJava5Compile @@ -231,31 +290,9 @@ public class AntBuilder extends Builder { hasSourceDirectories = true; } } - if (!classesDir.exists() && !classesDir.mkdirs()) { - errors.add("compile - unable to create " + classesDir); - return false; - } - if (!hasSourceDirectories) { // none - dump minimal file and exit - File minFile = new File(classesDir, module.name); - FileWriter fw = null; - try { - fw = new FileWriter(minFile); - fw.write(module.name); - } catch (IOException e) { - errors.add("IOException writing " - + module.name - + " to " - + minFile - + ": " - + Util.renderException(e)); - } finally { - Util.close(fw); - } + if (!hasSourceDirectories) { return true; // nothing to compile - ok } - if (useExistingClasses) { - return true; - } // XXX test whether build.compiler property takes effect automatically // I suspect it requires the proper adapter setup. Javac javac = new Javac(); @@ -268,18 +305,19 @@ public class AntBuilder extends Builder { // -- classpath Path classpath = new Path(project); - boolean hasLibraries = setupClasspath(module, classpath); + boolean hasLibraries = setupClasspath(result, classpath); // need to add system classes?? - boolean inEclipse = true; // XXX detect, fork only in eclipse - if (hasLibraries && inEclipse) { - javac.setFork(true); // XXX otherwise never releases library jars - } +// boolean inEclipse = true; // XXX detect, fork only in eclipse +// if (hasLibraries && inEclipse) { // if fork, on compiler failure, no report +// javac.setFork(true); // TODO XXX otherwise never releases library jars +// } // also fork if using 1.5? // can we build under 1.4, but fork javac 1.5 compile? // -- set output directory classpath.createPathElement().setLocation(classesDir); javac.setClasspath(classpath); + // misc javac.setDebug(true); if (!isJava5Compile) { @@ -293,7 +331,7 @@ public class AntBuilder extends Builder { boolean passed = false; BuildException failure = null; try { - passed = executeTask(AspectJSupport.wrapIfNeeded(module, javac)); + passed = executeTask(AspectJSupport.wrapIfNeeded(result, javac)); } catch (BuildException e) { failure = e; } catch (Error e) { @@ -301,36 +339,43 @@ public class AntBuilder extends Builder { } catch (RuntimeException e) { failure = new BuildException(e); } finally { + if (!passed) { + String args = "" + Arrays.asList(javac.getCurrentCompilerArgs()); + if ("[]".equals(args)) { + args = "{" + result.toLongString() + "}"; + } + String m = "BuildException compiling " + result.toLongString() + args + + (null == failure? "" : ": " + Util.renderException(failure)); + //debuglog System.err.println(m); + errors.add(m); + } javac.init(); // be nice to let go of classpath libraries... } - if (!passed) { - String args = "" + Arrays.asList(javac.getCurrentCompilerArgs()); - errors.add("BuildException compiling " + module.toLongString() + args - + (null == failure? "" : ": " + Util.renderException(failure))); - } return passed; } - public boolean setupClasspath(Module module, Path classpath) { // XXX fix test access + public boolean setupClasspath(Result result, Path classpath) { // XXX fix test access boolean hasLibraries = false; // required libraries - for (Iterator iter = module.getLibJars().iterator(); iter.hasNext();) { + for (Iterator iter = result.getLibJars().iterator(); iter.hasNext();) { File file = (File) iter.next(); classpath.createPathElement().setLocation(file); if (!hasLibraries) { hasLibraries = true; } } + Kind kind = result.getKind(); + Result[] reqs = result.getRequired(); // required modules and their exported libraries - for (Iterator iter = module.getRequired().iterator(); iter.hasNext();) { - Module required = (Module) iter.next(); - classpath.createPathElement().setLocation(required.getModuleJar()); + for (int i = 0; i < reqs.length; i++) { + Result requiredResult = reqs[i]; + classpath.createPathElement().setLocation(requiredResult.getOutputFile()); if (!hasLibraries) { hasLibraries = true; } // also put on classpath libraries exported from required module // XXX exported modules not supported - for (Iterator iterator = required.getExportedLibJars().iterator(); + for (Iterator iterator = requiredResult.getExportedLibJars().iterator(); iterator.hasNext(); ) { classpath.createPathElement().setLocation((File) iterator.next()); @@ -344,18 +389,19 @@ public class AntBuilder extends Builder { * with any specified manifest file. * META-INF directories are excluded. */ - protected boolean assemble(Module module, File classesDir, List errors) { + protected boolean assemble(Result result, File classesDir, List errors) { if (!buildingEnabled) { return false; } + // ---- zip result up Zip zip = new Zip(); setupTask(zip, "zip"); - zip.setDestFile(module.getModuleJar()); + zip.setDestFile(result.getOutputFile()); ZipFileSet zipfileset = null; // -- merge any resources in any of the src directories - for (Iterator iter = module.getSrcDirs().iterator(); iter.hasNext();) { + for (Iterator iter = result.getSrcDirs().iterator(); iter.hasNext();) { File srcDir = (File) iter.next(); zipfileset = new ZipFileSet(); zipfileset.setProject(project); @@ -364,25 +410,27 @@ public class AntBuilder extends Builder { zip.addZipfileset(zipfileset); } + final Module module = result.getModule(); // -- merge any merge jars - List mergeJars = module.getMerges(); - removeLibraryFilesToSkip(module, mergeJars); -// final boolean useManifest = false; - if (0 < mergeJars.size()) { - for (Iterator iter = mergeJars.iterator(); iter.hasNext();) { - File mergeJar = (File) iter.next(); - zipfileset = new ZipFileSet(); - zipfileset.setProject(project); - zipfileset.setSrc(mergeJar); - zipfileset.setIncludes("**/*"); - zipfileset.setExcludes("META-INF/manifest.mf"); // XXXFileLiteral - zipfileset.setExcludes("meta-inf/manifest.MF"); - zipfileset.setExcludes("META-INF/MANIFEST.mf"); - zipfileset.setExcludes("meta-inf/MANIFEST.MF"); - zip.addZipfileset(zipfileset); - } - } - // merge classes; put any meta-inf/manifest.mf here +// TODO removing-merges +// List mergeJars = result.getMerges(); +// removeLibraryFilesToSkip(module, mergeJars); +//// final boolean useManifest = false; +// if (0 < mergeJars.size()) { +// for (Iterator iter = mergeJars.iterator(); iter.hasNext();) { +// File mergeJar = (File) iter.next(); +// zipfileset = new ZipFileSet(); +// zipfileset.setProject(project); +// zipfileset.setSrc(mergeJar); +// zipfileset.setIncludes("**/*"); +// zipfileset.setExcludes("META-INF/manifest.mf"); // XXXFileLiteral +// zipfileset.setExcludes("meta-inf/manifest.MF"); +// zipfileset.setExcludes("META-INF/MANIFEST.mf"); +// zipfileset.setExcludes("meta-inf/MANIFEST.MF"); +// zip.addZipfileset(zipfileset); +// } +// } +// // merge classes; put any meta-inf/manifest.mf here File metaInfDir = new File(classesDir, "META-INF"); Util.deleteContents(metaInfDir); @@ -403,32 +451,38 @@ public class AntBuilder extends Builder { zipfileset.setDir(classesDir); zipfileset.setIncludes("**/*"); zip.addZipfileset(zipfileset); + File[] contents = classesDir.listFiles(); + if ((null == contents) || (0 == contents.length)) { + // *something* to zip up + dumpMinFile(result, classesDir,errors); + } try { - handler.log("assembling " + module + " in " + module.getModuleJar()); + handler.log("assembling " + module + " in " + result.getOutputFile()); return executeTask(zip) // zip returns true when it doesn't create zipfile // because there are no entries to add, so verify done - && Util.canReadFile(module.getModuleJar()); + && Util.canReadFile(result.getOutputFile()); } catch (BuildException e) { errors.add("BuildException zipping " + module + ": " + e.getMessage()); return false; } finally { - module.clearOutOfDate(); + result.clearOutOfDate(); } } /** * @see org.aspectj.internal.tools.build.Builder#buildAntecedants(Module) */ - protected String[] getAntecedantModuleNames(Module module, boolean rebuild) { + protected Result[] getAntecedantResults(Result moduleResult) { Hashtable targets = new Hashtable(); - makeTargetsForModule(module, targets, rebuild); - // XXX bug: doc says topoSort returns String, but returns Target - Collection result = project.topoSort(module.name, targets); - // XXX is it topoSort that should detect cycles? + makeTargetsForResult(moduleResult, targets); + String targetName = resultToTargetName(moduleResult); + // bug: doc says topoSort returns String, but returns Target + Collection result = project.topoSort(targetName, targets); + // fyi, we don't rely on topoSort to detect cycles - see buildAll int size = result.size(); if (0 == result.size()) { - return new String[0]; + return new Result[0]; } ArrayList toReturn = new ArrayList(); for (Iterator iter = result.iterator(); iter.hasNext();) { @@ -440,35 +494,36 @@ public class AntBuilder extends Builder { toReturn.add(name); } } - // topoSort always returns module.name + // topoSort always returns target name if ((1 == size) - && module.name.equals(toReturn.get(0)) - && !module.outOfDate(false)) { - return new String[0]; + && targetName.equals(toReturn.get(0)) + && !moduleResult.outOfDate(false)) { + return new Result[0]; } - return (String[]) toReturn.toArray(new String[0]); + return Result.getResults((String[]) toReturn.toArray(new String[0])); } /** * Generate Module.assembledJar with merge of itself and all antecedants */ - protected boolean assembleAll(Module module, Messager handler) { + protected boolean assembleAll(Result result, Messager handler) { + //System.out.println("assembling " + result); if (!buildingEnabled) { return false; } - Util.iaxIfNull(module, "module"); + Util.iaxIfNull(result, "result"); Util.iaxIfNull(handler, "handler"); - if (module.outOfDate(false)) { - throw new IllegalStateException("module out of date: " + module); + if (!result.getKind().isAssembly()) { + throw new IllegalStateException("not assembly: " + result); } // ---- zip result up Zip zip = new Zip(); setupTask(zip, "zip"); - zip.setDestFile(module.getAssembledJar()); + zip.setDestFile(result.getOutputFile()); ZipFileSet zipfileset = null; - - List known = module.findKnownJarAntecedants(); + final Module module = result.getModule(); + List known = result.findKnownJarAntecedants(); removeLibraryFilesToSkip(module, known); // -- merge any antecedents, less any manifest for (Iterator iter = known.iterator(); iter.hasNext();) { @@ -487,11 +542,14 @@ public class AntBuilder extends Builder { // merge the module jar itself, including same manifest (?) zipfileset = new ZipFileSet(); zipfileset.setProject(project); - zipfileset.setSrc(module.getModuleJar()); + Kind normal = Result.kind(result.getKind().isNormal(), !Result.ASSEMBLE); + File src = module.getResult(normal).getOutputFile(); + zipfileset.setSrc(src); zip.addZipfileset(zipfileset); try { - handler.log("assembling all " + module + " in " + module.getAssembledJar()); + handler.log("assembling all " + module + + " in " + result.getOutputFile()); if (verbose) { handler.log("knownAntecedants: " + known); } @@ -500,7 +558,7 @@ public class AntBuilder extends Builder { handler.logException("BuildException zipping " + module, e); return false; } finally { - module.clearOutOfDate(); + result.clearOutOfDate(); } } @@ -543,25 +601,28 @@ public class AntBuilder extends Builder { * @param javac the Javac compile commands * @return javac or a Task to compile with AspectJ if needed */ - static Task wrapIfNeeded(Module module, Javac javac) { + static Task wrapIfNeeded(Result result, Javac javac) { final Project project = javac.getProject(); Path runtimeJar = null; - if (runtimeJarOnClasspath(module)) { + final Module module = result.getModule(); + if (runtimeJarOnClasspath(result)) { // yes aspectjrt.jar on classpath - } else if (module.getClasspathVariables().contains(ASPECTJRT_JAR_VARIABLE)) { + } else if (result.getClasspathVariables().contains(ASPECTJRT_JAR_VARIABLE)) { // yes, in variables - find aspectjrt.jar to add to classpath runtimeJar = getAspectJLib(project, module, "aspectjrt.jar"); } else { // no + //System.out.println("javac " + result + " " + javac.getClasspath()); return javac; } + //System.out.println("aspectj " + result + " " + javac.getClasspath()); Path aspectjtoolsJar = getAspectJLib(project, module, "aspectjtools.jar"); return aspectJTask(javac, aspectjtoolsJar, runtimeJar); } /** @return true if aspectjrt.jar is on classpath */ - private static boolean runtimeJarOnClasspath(Module module) { - for (Iterator iter = module.getLibJars().iterator(); iter.hasNext();) { + private static boolean runtimeJarOnClasspath(Result result) { + for (Iterator iter = result.getLibJars().iterator(); iter.hasNext();) { File file = (File) iter.next(); if ("aspectjrt.jar".equals(file.getName())) { return true; @@ -741,113 +802,34 @@ class ProductBuilder extends AntBuilder { } /** - * Build product by discovering any modules to build, - * building those, assembling the product distribution, - * and optionally creating an installer for it. - * @return true on success + * Delegate for super.buildProduct(..) template method. */ - protected boolean buildProduct(BuildSpec buildSpec) - throws BuildException { - Util.iaxIfNull(buildSpec, "buildSpec"); - // XXX if installer and not out of date, do not rebuild unless rebuild set - - if (!buildSpec.trimTesting) { - buildSpec.trimTesting = true; - handler.log("testing trimmed for " + buildSpec); + protected boolean copyBinaries(BuildSpec buildSpec, File distDir, File targDir, String excludes) { + Copy copy = makeCopyTask(false); + copy.setTodir(targDir); + FileSet fileset = new FileSet(); + fileset.setDir(distDir); + fileset.setIncludes(Builder.BINARY_SOURCE_PATTERN); + if (null != excludes) { + fileset.setExcludes(excludes); } - Util.iaxIfNotCanReadDir(buildSpec.productDir, "productDir"); - Util.iaxIfNotCanReadDir(buildSpec.baseDir, "baseDir"); - Util.iaxIfNotCanWriteDir(buildSpec.distDir, "distDir"); + copy.addFileset(fileset); + return executeTask(copy); + } - // ---- discover modules to build, and build them - Modules modules = new Modules( - buildSpec.baseDir, - buildSpec.jarDir, - buildSpec.trimTesting, - handler); - ProductModule[] productModules = discoverModules(buildSpec.productDir, modules); - for (int i = 0; i < productModules.length; i++) { - if (buildSpec.verbose) { - handler.log("building product module " + productModules[i]); - } - if (!buildProductModule(productModules[i])) { - return false; - } - } - if (buildSpec.verbose) { - handler.log("assembling product module for " + buildSpec); - } - - // ---- assemble product distribution - final String productName = buildSpec.productDir.getName(); - final File targDir = new File(buildSpec.distDir, productName); - final String targDirPath = targDir.getPath(); - if (targDir.canWrite()) { - Util.deleteContents(targDir); - } - - if (!targDir.canWrite() && !targDir.mkdirs()) { - if (buildSpec.verbose) { - handler.log("buildProduct unable to create " + targDir); - } - return false; - } + /** + * Delegate for super.buildProduct(..) template method. + */ + protected boolean copyNonBinaries(BuildSpec buildSpec, File distDir, File targDir) { // filter-copy everything but the binaries Copy copy = makeCopyTask(true); copy.setTodir(targDir); - File distDir = new File(buildSpec.productDir, "dist"); // XXXFileLiteral Util.iaxIfNotCanReadDir(distDir, "product dist directory"); FileSet fileset = new FileSet(); fileset.setDir(distDir); fileset.setExcludes(Builder.BINARY_SOURCE_PATTERN); copy.addFileset(fileset); - if (!executeTask(copy)) { - return false; - } - - // copy binaries (but not module flag files) - String excludes = null; - { - StringBuffer buf = new StringBuffer(); - for (int i = 0; i < productModules.length; i++) { - if (0 < buf.length()) { - buf.append(","); - } - buf.append(productModules[i].relativePath); - } - if (0 < buf.length()) { - excludes = buf.toString(); - } - } - copy = makeCopyTask(false); - copy.setTodir(targDir); - fileset = new FileSet(); - fileset.setDir(distDir); - fileset.setIncludes(Builder.BINARY_SOURCE_PATTERN); - if (null != excludes) { - fileset.setExcludes(excludes); - } - copy.addFileset(fileset); - if (!executeTask(copy)) { - return false; - } - - // copy binaries associated with module flag files - for (int i = 0; i < productModules.length; i++) { - ProductModule product = productModules[i]; - String targPath = Util.path(targDirPath, product.relativePath); - File jarFile = (product.assembleAll - ? product.module.getAssembledJar() - : product.module.getModuleJar() ); - copyFile(jarFile, new File(targPath), FILTER_OFF); - } - handler.log("created product in " + targDir); - // ---- create installer - if (buildSpec.createInstaller) { - return buildInstaller(buildSpec, targDirPath); - } else { - return true; - } + return executeTask(copy); } protected boolean buildInstaller(BuildSpec buildSpec, String targDirPath) { diff --git a/build/src/org/aspectj/internal/tools/build/Builder.java b/build/src/org/aspectj/internal/tools/build/Builder.java index b9303ffb8..cfac0192d 100644 --- a/build/src/org/aspectj/internal/tools/build/Builder.java +++ b/build/src/org/aspectj/internal/tools/build/Builder.java @@ -26,87 +26,88 @@ import java.util.Properties; import java.util.StringTokenizer; import org.apache.tools.ant.BuildException; +import org.aspectj.internal.tools.build.Result.Kind; /** - * Template class to build (eclipse) modules (and, weakly, products), - * including any required modules. - * When building modules, this assumes: + * Template class to build (eclipse) modules (and, weakly, products), including + * any required modules. When building modules, this assumes: * <ul> * <li>the name of the module is the base name of the module directory</li> * <li>all module directories are in the same base (workspace) directory</li> * <li>the name of the target module jar is {moduleName}.jar</li> * <li>a module directory contains a <code>.classpath</code> file with - * (currently line-parseable) entries per Eclipse (XML) conventions</li> - * <li><code>Builder.RESOURCE_PATTERN</code> - * identifies all resources to copy to output.</li> + * (currently line-parseable) entries per Eclipse (XML) conventions</li> + * <li><code>Builder.RESOURCE_PATTERN</code> identifies all resources to copy + * to output.</li> * <li>This can safely trim test-related code: - * <ul> - * <li>source directories named "testsrc"</li> - * <li>libraries named "junit.jar"</li> - * <li>required modules whose names start with "testing"</li> - * </ul> - * <li>A file <code>{moduleDir}/{moduleName}.properties</code> - * is a property file possibly - * containing entries defining requirements to be merged with the output jar - * (deprecated mechanism - use assembleAll or products)</li> + * <ul> + * <li>source directories named "testsrc"</li> + * <li>libraries named "junit.jar"</li> + * <li>required modules whose names start with "testing"</li> + * </ul> + * <li>A file <code>{moduleDir}/{moduleName}.properties</code> is a property + * file possibly containing entries defining requirements to be merged with the + * output jar (deprecated mechanism - use assembleAll or products)</li> * </ul> - * This currently provides no control over the compile or assembly process, - * but clients can harvest <code>{moduleDir}/bin</code> directories to re-use - * the results of eclipse compiles. + * This currently provides no control over the compile or assembly process, but + * clients can harvest <code>{moduleDir}/bin</code> directories to re-use the + * results of eclipse compiles. * <p> * When building products, this assumes: * <ul> * <li>the installer-resources directory is a peer of the products directory, - * itself the parent of the particular product directory.</li> + * itself the parent of the particular product directory.</li> * <li>the dist, jar, product, and base (module) directory are set</li> - * <li>the product distribution consists of all (and only) the files - * in the dist sub-directory of the product directory</li> + * <li>the product distribution consists of all (and only) the files in the + * dist sub-directory of the product directory</li> * <li>files in the dist sub-directory that are empty and end with .jar - * represent modules to build, either as named or through aliases - * known here.</li> - * <li>When assembling the distribution, all non-binary files are to - * be filtered.<li> - * <li>the name of the product installer is aspectj-{productName}-{version}.jar, - * where {productName} is the base name of the product directory</li> + * represent modules to build, either as named or through aliases known here.</li> + * <li>When assembling the distribution, all non-binary files are to be + * filtered. + * <li> + * <li>the name of the product installer is + * aspectj-{productName}-{version}.jar, where {productName} is the base name of + * the product directory</li> * </ul> * <p> - * When run using main(String[]), all relevant Ant libraries and properties - * must be defined. + * When run using main(String[]), all relevant Ant libraries and properties must + * be defined. * <p> - * Written to compile standalone. Refactor if using utils, bridge, etc. + * Written to compile standalone. Refactor if using utils, bridge, etc. */ public abstract class Builder { - /** - * This has only weak forms for build instructions needed: - * - resource pattern - * - compiler selection and control - * - * Both assumed and generated paths are scattered; - * see XXXNameLiteral and XXXFileLiteral. - * - * Builder is supposed to be thread-safe, but currently caches build - * properties to tunnel for filters. hmm. - */ + /** + * This has only weak forms for build instructions needed: - resource + * pattern - compiler selection and control + * + * Both assumed and generated paths are scattered; see XXXNameLiteral and + * XXXFileLiteral. + * + * Builder is supposed to be thread-safe, but currently caches build + * properties to tunnel for filters. hmm. + */ - public static final String RESOURCE_PATTERN; + public static final String RESOURCE_PATTERN; - public static final String BINARY_SOURCE_PATTERN; + public static final String BINARY_SOURCE_PATTERN; - public static final String ALL_PATTERN; + public static final String ALL_PATTERN; - /** enable copy filter semantics */ - protected static final boolean FILTER_ON = true; + /** enable copy filter semantics */ + protected static final boolean FILTER_ON = true; - /** disable copy filter semantics */ - protected static final boolean FILTER_OFF = false; + /** disable copy filter semantics */ + protected static final boolean FILTER_OFF = false; /** define libraries to skip as comma-delimited values for this key */ private static final String SKIP_LIBRARIES_KEY = "skip.libraries"; - + /** List (String) names of libraries to skip during assembly */ private static final List SKIP_LIBRARIES; + private static final String ERROR_KEY = "error loading properties"; + private static final Properties PROPS; static { PROPS = new Properties(); @@ -116,8 +117,9 @@ public abstract class Builder { String binarySourcePattern = "**/*.rsc,**/*.gif,**/*.jar,**/*.zip"; String name = Builder.class.getName().replace('.', '/') + ".properties"; try { - InputStream in = Builder.class.getClassLoader().getResourceAsStream(name); - PROPS.load(in); + InputStream in = Builder.class.getClassLoader() + .getResourceAsStream(name); + PROPS.load(in); allPattern = PROPS.getProperty("all.pattern"); resourcePattern = PROPS.getProperty("resource.pattern"); binarySourcePattern = PROPS.getProperty("binarySource.pattern"); @@ -134,12 +136,13 @@ public abstract class Builder { BINARY_SOURCE_PATTERN = binarySourcePattern; RESOURCE_PATTERN = resourcePattern; } - + /** - * Splits strings into an unmodifable <code>List</code> of String - * using comma as the delimiter and trimming whitespace from the result. - * - * @param text <code>String</code> to split. + * Splits strings into an unmodifable <code>List</code> of String using + * comma as the delimiter and trimming whitespace from the result. + * + * @param text + * <code>String</code> to split. * @return unmodifiable List (String) of String delimited by comma in text */ public static List commaStrings(String text) { @@ -156,9 +159,12 @@ public abstract class Builder { } return Collections.unmodifiableList(strings); } + /** * Map delivered-jar name to created-module name - * @param jarName the String (lowercased) of the jar/zip to map + * + * @param jarName + * the String (lowercased) of the jar/zip to map */ private String moduleAliasFor(String jarName) { String result = PROPS.getProperty("alias." + jarName, jarName); @@ -168,405 +174,425 @@ public abstract class Builder { } return result; } - protected final Messager handler; - protected boolean buildingEnabled; - - private final File tempDir; - private final ArrayList tempFiles; - private final boolean useEclipseCompiles; - - protected boolean verbose; - - protected Builder( - File tempDir, - boolean useEclipseCompiles, - Messager handler) { - Util.iaxIfNull(handler, "handler"); - this.useEclipseCompiles = useEclipseCompiles; - this.handler = handler; - this.tempFiles = new ArrayList(); - if ((null == tempDir) - || !tempDir.canWrite() - || !tempDir.isDirectory()) { - this.tempDir = Util.makeTempDir("Builder"); - } else { - this.tempDir = tempDir; - } - buildingEnabled = true; - } - - /** tell builder to stop or that it's ok to run */ - public void setBuildingEnabled(boolean enabled) { - buildingEnabled = enabled; - } - - public void setVerbose(boolean verbose) { - this.verbose = verbose; - } - - public boolean build(BuildSpec buildSpec) { - if (!buildingEnabled) { - return false; - } - if (null == buildSpec.productDir) { // ensure module properties - // derive moduleDir from baseDir + module - if (null == buildSpec.moduleDir) { - if (null == buildSpec.baseDir) { - throw new BuildException("require baseDir or moduleDir"); - } else if (null == buildSpec.module) { - throw new BuildException("require module with baseDir"); - } else { - if (null == buildSpec.baseDir) { - buildSpec.baseDir = new File("."); // user.home? - } - buildSpec.moduleDir = - new File(buildSpec.baseDir, buildSpec.module); - } - } else if (null == buildSpec.baseDir) { - // derive baseDir from moduleDir parent - buildSpec.baseDir = buildSpec.moduleDir.getParentFile(); - // rule: base is parent - if (null == buildSpec.baseDir) { - buildSpec.baseDir = new File("."); // user.home? - } - handler.log("Builder using derived baseDir: " - + buildSpec.baseDir); - } - Util.iaxIfNotCanReadDir(buildSpec.moduleDir, "moduleDir"); - - if (null == buildSpec.module) { - // derive module name from directory - buildSpec.module = buildSpec.moduleDir.getName(); - if (null == buildSpec.module) { - throw new BuildException( - "no name, even from " + buildSpec.moduleDir); - } - } - } - - if (null != buildSpec.productDir) { - return buildProduct(buildSpec); - } - if (buildSpec.trimTesting - && (-1 != buildSpec.module.indexOf("testing"))) { // XXXNameLiteral - String warning = - "Warning - cannot trimTesting for testing modules: "; - handler.log(warning + buildSpec.module); - } - Messager handler = new Messager(); - Modules modules = - new Modules( - buildSpec.baseDir, - buildSpec.jarDir, - buildSpec.trimTesting, - handler); - - final Module moduleToBuild = modules.getModule(buildSpec.module); - ArrayList errors = new ArrayList(); - try { - return buildAll( - moduleToBuild, - errors, - buildSpec.rebuild, - buildSpec.assembleAll); - } finally { - if (0 < errors.size()) { - String label = "error building " + buildSpec + ": "; - for (Iterator iter = errors.iterator(); iter.hasNext();) { - handler.error(label + iter.next()); - } - } - } - } - - /** - * Clean up any temporary files, etc. after build completes - */ - public boolean cleanup() { - boolean noErr = true; - for (ListIterator iter = tempFiles.listIterator(); - iter.hasNext(); - ) { - File file = (File) iter.next(); - if (!Util.deleteContents(file) || !file.delete()) { - if (noErr) { - noErr = false; - } - handler.log("unable to clean up " + file); - } - } - return noErr; - } - - /** - * Build a module with all antecedants. - * @param module the Module to build - * @param errors the List sink for errors, if any - * @return false after successful build, when module jar should exist - */ - protected boolean buildAll( - Module module, - List errors, - boolean rebuild, - boolean assembleAll) { - String[] buildList = getAntecedantModuleNames(module, rebuild); - if ((null != buildList) && (0 < buildList.length)) { - final Modules modules = module.getModules(); - final Messager handler = this.handler; - final boolean log = (verbose && (null != handler)); -// final boolean verbose = this.verbose; - if (log) { - handler.log( - "modules to build: " + Arrays.asList(buildList)); - } - for (int i = 0; i < buildList.length; i++) { - - if (!buildingEnabled) { - return false; - } - String modName = buildList[i]; - if (log) { - handler.log("building " + modName); - } - Module next = modules.getModule(modName); - if (!buildOnly(next, errors)) { - return false; - } - } - } - if (assembleAll && !assembleAll(module, handler)) { - return false; - } - return true; - } - - /** - * Build a module but no antecedants. - * @param module the Module to build - * @param errors the List sink for errors, if any - * @return false after successful build, when module jar should exist - */ - protected boolean buildOnly(Module module, List errors) { - if (!buildingEnabled) { - return false; - } - final File classesDir; + + protected final Messager handler; + + protected boolean buildingEnabled; + + private final File tempDir; + + private final ArrayList tempFiles; + + private final boolean useEclipseCompiles; + + protected boolean verbose; + + protected Builder(File tempDir, boolean useEclipseCompiles, Messager handler) { + Util.iaxIfNull(handler, "handler"); + this.useEclipseCompiles = useEclipseCompiles; + this.handler = handler; + this.tempFiles = new ArrayList(); + if ((null == tempDir) || !tempDir.canWrite() || !tempDir.isDirectory()) { + this.tempDir = Util.makeTempDir("Builder"); + } else { + this.tempDir = tempDir; + } + buildingEnabled = true; + } + + /** tell builder to stop or that it's ok to run */ + public void setBuildingEnabled(boolean enabled) { + buildingEnabled = enabled; + } + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + private void verifyBuildSpec(BuildSpec buildSpec) { + if (null == buildSpec.productDir) { // ensure module properties + // derive moduleDir from baseDir + module + if (null == buildSpec.moduleDir) { + if (null == buildSpec.baseDir) { + throw new BuildException("require baseDir or moduleDir"); + } else if (null == buildSpec.module) { + throw new BuildException("require module with baseDir"); + } else { + if (null == buildSpec.baseDir) { + buildSpec.baseDir = new File("."); // user.home? + } + buildSpec.moduleDir = new File(buildSpec.baseDir, + buildSpec.module); + } + } else if (null == buildSpec.baseDir) { + // derive baseDir from moduleDir parent + buildSpec.baseDir = buildSpec.moduleDir.getParentFile(); + // rule: base is parent + if (null == buildSpec.baseDir) { + buildSpec.baseDir = new File("."); // user.home? + } + handler.log("Builder using derived baseDir: " + + buildSpec.baseDir); + } + Util.iaxIfNotCanReadDir(buildSpec.moduleDir, "moduleDir"); + if (null == buildSpec.module) { + // derive module name from directory + buildSpec.module = buildSpec.moduleDir.getName(); + if (null == buildSpec.module) { + throw new BuildException("no name, even from " + + buildSpec.moduleDir); + } + } + } + } + + /** + * Find the Result (and hence Module and Modules) for this BuildSpec. + */ + protected Result specifyResultFor(BuildSpec buildSpec) { + if (buildSpec.trimTesting + && (-1 != buildSpec.module.indexOf("testing"))) { // XXXNameLiteral + String warning = "Warning - cannot trimTesting for testing modules: "; + handler.log(warning + buildSpec.module); + } + Messager handler = new Messager(); + Modules modules = new Modules(buildSpec.baseDir, buildSpec.jarDir, + handler); + + final Module moduleToBuild = modules.getModule(buildSpec.module); + Kind kind = Result.kind(buildSpec.trimTesting, + buildSpec.assembleAll); + return moduleToBuild.getResult(kind); + } + + public final boolean build(BuildSpec buildSpec) { + if (!buildingEnabled) { + return false; + } + verifyBuildSpec(buildSpec); + + if (null != buildSpec.productDir) { + return buildProduct(buildSpec); + } + Result result = specifyResultFor(buildSpec); + ArrayList errors = new ArrayList(); + try { + return buildAll(result, errors); + } finally { + if (0 < errors.size()) { + String label = "error building " + buildSpec + ": "; + for (Iterator iter = errors.iterator(); iter.hasNext();) { + String m = label + iter.next(); + handler.error(m); + } + } + } + } + + /** + * Clean up any temporary files, etc. after build completes + */ + public boolean cleanup() { + boolean noErr = true; + for (ListIterator iter = tempFiles.listIterator(); iter.hasNext();) { + File file = (File) iter.next(); + if (!Util.deleteContents(file) || !file.delete()) { + if (noErr) { + noErr = false; + } + handler.log("unable to clean up " + file); + } + } + return noErr; + } + + protected Result[] skipUptodate(Result[] results) { + if (null == results) { + return new Result[0]; + } + Result[] done = new Result[results.length]; + int to = 0; + for (int i = 0; i < done.length; i++) { + if ((null != results[i]) && results[i].outOfDate(false)) { + done[to++] = results[i]; + } + } + if (to < results.length) { + Result[] newdone = new Result[to]; + System.arraycopy(done, 0, newdone, 0, newdone.length); + done = newdone; + } + return done; + } + + /** + * Build a result with all antecedants. + * + * @param result + * the Result to build + * @param errors + * the List sink for errors, if any + * @return false after successful build, when module jar should exist + */ + protected final boolean buildAll(Result result, List errors) { + Result[] buildList = skipUptodate(getAntecedantResults(result)); + ArrayList doneList = new ArrayList(); + if ((null != buildList) && (0 < buildList.length)) { + final Modules modules = result.getModule().getModules(); + final Messager handler = this.handler; + final boolean log = (verbose && (null != handler)); + if (log) { + handler.log("modules to build: " + Arrays.asList(buildList)); + } + for (int i = 0; i < buildList.length; i++) { + Result required = buildList[i]; + if (!buildingEnabled) { + return false; + } + String requiredName = required.getName(); + if (!doneList.contains(requiredName)) { + doneList.add(requiredName); + if (required.outOfDate(false)) { + if (log) { + handler.log("building " + requiredName); + } + if (!buildOnly(required, errors)) { + return false; + } + } + } + } + } + return true; + } + + /** + * Build a module but no antecedants. + * + * @param module + * the Module to build + * @param errors + * the List sink for errors, if any + * @return false after successful build, when module jar should exist + */ + protected final boolean buildOnly(Result result, List errors) { + if (!buildingEnabled) { + return false; + } + if (result.getKind().assemble) { + return assembleAll(result, handler); + } + Module module = result.getModule(); + final File classesDir; if (useEclipseCompiles) { classesDir = new File(module.moduleDir, "bin"); // FileLiteral } else { String name = "classes-" + System.currentTimeMillis(); classesDir = new File(tempDir, name); } - if (verbose) { - handler.log("buildOnly " + module); - } - try { - return ( - compile(module, classesDir, useEclipseCompiles, errors)) - && assemble(module, classesDir, errors); - } finally { - if (!useEclipseCompiles && !Util.delete(classesDir)) { - errors.add("buildOnly unable to delete " + classesDir); - } - } - } - - /** - * Register temporary file or directory to be deleted when - * the build is complete, even if an Exception is thrown. - */ - protected void addTempFile(File tempFile) { - if (null != tempFile) { - tempFiles.add(tempFile); - } - } - /** - * Build product by discovering any modules to build, - * building those, assembling the product distribution, - * and optionally creating an installer for it. - * @return true on success - */ - protected boolean buildProduct(BuildSpec buildSpec) - throws BuildException { - Util.iaxIfNull(buildSpec, "buildSpec"); - // XXX if installer and not out of date, do not rebuild unless rebuild set - - if (!buildSpec.trimTesting) { - buildSpec.trimTesting = true; - handler.log("testing trimmed for " + buildSpec); - } - Util.iaxIfNotCanReadDir(buildSpec.productDir, "productDir"); - Util.iaxIfNotCanReadDir(buildSpec.baseDir, "baseDir"); - Util.iaxIfNotCanWriteDir(buildSpec.distDir, "distDir"); - - // ---- discover modules to build, and build them - Modules modules = - new Modules( - buildSpec.baseDir, - buildSpec.jarDir, - buildSpec.trimTesting, - handler); - ProductModule[] productModules = - discoverModules(buildSpec.productDir, modules); - for (int i = 0; i < productModules.length; i++) { - if (buildSpec.verbose) { - handler.log( - "building product module " + productModules[i]); - } - if (!buildProductModule(productModules[i])) { - return false; - } - } - if (buildSpec.verbose) { - handler.log("assembling product module for " + buildSpec); - } - - // ---- assemble product distribution - final String productName = buildSpec.productDir.getName(); - final File targDir = new File(buildSpec.distDir, productName); - final String targDirPath = targDir.getPath(); - if (targDir.canWrite()) { - Util.deleteContents(targDir); - } - - if (!targDir.canWrite() && !targDir.mkdirs()) { - if (buildSpec.verbose) { - handler.log("buildProduct unable to create " + targDir); - } - return false; - } - // filter-copy everything but the binaries - File distDir = new File(buildSpec.productDir, "dist"); - // XXXFileLiteral - String excludes = Builder.BINARY_SOURCE_PATTERN; - String includes = Builder.ALL_PATTERN; - if (!copyFiles(distDir, targDir, includes, excludes, FILTER_ON)) { - return false; - } - - // copy binaries (but not module flag files) - excludes = null; - { - StringBuffer buf = new StringBuffer(); - for (int i = 0; i < productModules.length; i++) { - if (0 < buf.length()) { - buf.append(","); - } - buf.append(productModules[i].relativePath); - } - if (0 < buf.length()) { - excludes = buf.toString(); - } - } - includes = Builder.BINARY_SOURCE_PATTERN; - if (!copyFiles(distDir, - targDir, - includes, - excludes, - FILTER_OFF)) { - return false; - } - // duplicate code? - // copy binaries associated with module flag files - for (int i = 0; i < productModules.length; i++) { - ProductModule product = productModules[i]; - String targPath = Util.path(targDirPath, product.relativePath); - File jarFile = - (product.assembleAll - ? product.module.getAssembledJar() - : product.module.getModuleJar()); - copyFile(jarFile, new File(targPath), FILTER_OFF); - } - handler.log("created product in " + targDir); - - // ---- create installer - if (buildSpec.createInstaller) { - return buildInstaller(buildSpec, targDirPath); - } else { - return true; - } - } - - protected boolean buildProductModule(ProductModule module) { - boolean noRebuild = false; - ArrayList errors = new ArrayList(); - try { - return buildAll( - module.module, - errors, - noRebuild, - module.assembleAll); - } finally { - for (Iterator iter = errors.iterator(); iter.hasNext();) { - handler.error( - "error building " + module + ": " + iter.next()); - } - } - } - - /** - * Discover any modules that might need to be built - * in order to assemble the product distribution. - * This interprets empty .jar files as module deliverables. - */ - protected ProductModule[] discoverModules( - File productDir, - Modules modules) { - final ArrayList found = new ArrayList(); - FileFilter filter = new FileFilter() {// empty jar files - public boolean accept(File file) { - if ((null != file) - && file.canRead() - && file.getPath().endsWith( - ".jar") // XXXFileLiteral - && (0l == file.length())) { - found.add(file); - } - return true; - } - }; - Util.visitFiles(productDir, filter); - ArrayList results = new ArrayList(); - for (Iterator iter = found.iterator(); iter.hasNext();) { - File file = (File) iter.next(); - String jarName = moduleAliasFor(file.getName().toLowerCase()); - if (jarName.endsWith(".jar") - || jarName.endsWith(".zip")) { // XXXFileLiteral - jarName = jarName.substring(0, jarName.length() - 4); - } else { - handler.log("can only replace .[jar|zip]: " + file); - // XXX error? - } - boolean assembleAll = jarName.endsWith("-all"); - // XXXFileLiteral - String name = - (!assembleAll - ? jarName - : jarName.substring(0, jarName.length() - 4)); - Module module = modules.getModule(name); - if (null == module) { - handler.log("unable to find module for " + file); - } else { - results.add( - new ProductModule( - productDir, - file, - module, - assembleAll)); - } - } - return (ProductModule[]) results.toArray(new ProductModule[0]); - } + if (verbose) { + handler.log("buildOnly " + module); + } + try { + return (compile(result, classesDir,useEclipseCompiles, errors)) + && assemble(result, classesDir, errors); + } finally { + if (!useEclipseCompiles && !Util.delete(classesDir)) { + errors.add("buildOnly unable to delete " + classesDir); + } + } + } + + /** + * Register temporary file or directory to be deleted when the build is + * complete, even if an Exception is thrown. + */ + protected void addTempFile(File tempFile) { + if (null != tempFile) { + tempFiles.add(tempFile); + } + } + + /** + * Build product by discovering any modules to build, building those, + * assembling the product distribution, and optionally creating an installer + * for it. + * + * @return true on success + */ + protected final boolean buildProduct(BuildSpec buildSpec) + throws BuildException { + Util.iaxIfNull(buildSpec, "buildSpec"); + + if (!buildSpec.trimTesting) { + buildSpec.trimTesting = true; + handler.log("testing trimmed for " + buildSpec); + } + Util.iaxIfNotCanReadDir(buildSpec.productDir, "productDir"); + Util.iaxIfNotCanReadDir(buildSpec.baseDir, "baseDir"); + Util.iaxIfNotCanWriteDir(buildSpec.distDir, "distDir"); + + // ---- discover modules to build, and build them + Modules modules = new Modules(buildSpec.baseDir, buildSpec.jarDir, + handler); + ProductModule[] productModules = discoverModules(buildSpec.productDir, + modules); + for (int i = 0; i < productModules.length; i++) { + if (buildSpec.verbose) { + handler.log("building product module " + productModules[i]); + } + if (!buildProductModule(productModules[i])) { + return false; + } + } + if (buildSpec.verbose) { + handler.log("assembling product module for " + buildSpec); + } + + // ---- assemble product distribution + final String productName = buildSpec.productDir.getName(); + final File targDir = new File(buildSpec.distDir, productName); + final String targDirPath = targDir.getPath(); + if (targDir.canWrite()) { + Util.deleteContents(targDir); + } + + if (!targDir.canWrite() && !targDir.mkdirs()) { + if (buildSpec.verbose) { + handler.log("buildProduct unable to create " + targDir); + } + return false; + } + + // copy non-binaries (with filter) + File distDir = new File(buildSpec.productDir, "dist"); + if (!copyNonBinaries(buildSpec, distDir, targDir)) { + return false; + } + + // copy binaries (but not module flag files) + String excludes = null; + { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < productModules.length; i++) { + if (0 < buf.length()) { + buf.append(","); + } + buf.append(productModules[i].relativePath); + } + if (0 < buf.length()) { + excludes = buf.toString(); + } + } + + if (!copyBinaries(buildSpec, distDir, targDir, excludes)) { + return false; + } + + // copy binaries associated with module flag files + for (int i = 0; i < productModules.length; i++) { + final ProductModule product = productModules[i]; + final Kind kind = Result.kind(Result.NORMAL, product.assembleAll); + Result result = product.module.getResult(kind); + String targPath = Util.path(targDirPath, product.relativePath); + File jarFile = result.getOutputFile(); + copyFile(jarFile, new File(targPath), FILTER_OFF); + } + handler.log("created product in " + targDir); + + // ---- create installer + if (buildSpec.createInstaller) { + return buildInstaller(buildSpec, targDirPath); + } else { + return true; + } + } + + protected boolean copyBinaries(BuildSpec buildSpec, File distDir, + File targDir, String excludes) { + String includes = Builder.BINARY_SOURCE_PATTERN; + return copyFiles(distDir, targDir, includes, excludes, FILTER_OFF); + } + + /** + * filter-copy everything but the binaries + */ + protected boolean copyNonBinaries(BuildSpec buildSpec, File distDir, + File targDir) { + String excludes = Builder.BINARY_SOURCE_PATTERN; + String includes = Builder.ALL_PATTERN; + return copyFiles(distDir, targDir, includes, excludes, FILTER_ON); + } + + protected final boolean buildProductModule(ProductModule module) { + boolean noRebuild = false; + ArrayList errors = new ArrayList(); + try { + Kind productKind = Result.kind(Result.NORMAL, Result.ASSEMBLE); + Result result = module.module.getResult(productKind); + return buildAll(result, errors); + } finally { + for (Iterator iter = errors.iterator(); iter.hasNext();) { + handler.error("error building " + module + ": " + iter.next()); + } + } + } + + /** + * Discover any modules that might need to be built in order to assemble the + * product distribution. This interprets empty .jar files as module + * deliverables. + */ + protected ProductModule[] discoverModules(File productDir, Modules modules) { + final ArrayList found = new ArrayList(); + FileFilter filter = new FileFilter() {// empty jar files + public boolean accept(File file) { + if ((null != file) && file.canRead() + && file.getPath().endsWith(".jar") // XXXFileLiteral + && (0l == file.length())) { + found.add(file); + } + return true; + } + }; + Util.visitFiles(productDir, filter); + ArrayList results = new ArrayList(); + for (Iterator iter = found.iterator(); iter.hasNext();) { + File file = (File) iter.next(); + String jarName = moduleAliasFor(file.getName().toLowerCase()); + if (jarName.endsWith(".jar") || jarName.endsWith(".zip")) { // XXXFileLiteral + jarName = jarName.substring(0, jarName.length() - 4); + } else { + handler.log("can only replace .[jar|zip]: " + file); + // XXX error? + } + boolean assembleAll = jarName.endsWith("-all"); + // XXXFileLiteral + String name = (!assembleAll ? jarName : jarName.substring(0, + jarName.length() - 4)); + Module module = modules.getModule(name); + if (null == module) { + handler.log("unable to find module for " + file); + } else { + results.add(new ProductModule(productDir, file, module, + assembleAll)); + } + } + return (ProductModule[]) results.toArray(new ProductModule[0]); + } /** * Subclasses should query whether to include library files in the assembly. - * @param module the Module being built - * @param libraries the List of File path to the jar to consider assembling + * + * @param module + * the Module being built + * @param libraries + * the List of File path to the jar to consider assembling * @return true if the jar should be included, false otherwise. */ protected void removeLibraryFilesToSkip(Module module, List libraries) { for (ListIterator liter = libraries.listIterator(); liter.hasNext();) { - File library= (File) liter.next(); + File library = (File) liter.next(); final String fname = library.getName(); if (null != fname) { for (Iterator iter = SKIP_LIBRARIES.iterator(); iter.hasNext();) { @@ -579,74 +605,72 @@ public abstract class Builder { } } } - - /** - * @return String[] names of modules to build for this module - */ - abstract protected String[] getAntecedantModuleNames( - Module toBuild, - boolean rebuild); - - /** - * Compile module classes to classesDir, saving String errors. - * @param module the Module to compile - * @param classesDir the File directory to compile to - * @param useExistingClasses if true, don't recompile - * and ensure classes are available - * @param errors the List to add error messages to - */ - abstract protected boolean compile( - Module module, - File classesDir, - boolean useExistingClasses, - List errors); - - /** - * Assemble the module distribution from the classesDir, saving String errors. + + /** + * @return String[] names of results to build for this module + */ + abstract protected Result[] getAntecedantResults(Result toBuild); + + /** + * Compile module classes to classesDir, saving String errors. + * + * @param module + * the Module to compile + * @param classesDir + * the File directory to compile to + * @param useExistingClasses + * if true, don't recompile and ensure classes are available + * @param errors + * the List to add error messages to + */ + abstract protected boolean compile(Result result, File classesDir, + boolean useExistingClasses, List errors); + + /** + * Assemble the module distribution from the classesDir, saving String + * errors. + * * @see #removeLibraryFilesToSkip(Module, File) - */ - abstract protected boolean assemble( - Module module, - File classesDir, - List errors); - - /** - * Assemble the module distribution from the classesDir and all antecendants, - * saving String errors. + */ + abstract protected boolean assemble(Result result, File classesDir, + List errors); + + /** + * Assemble the module distribution from the classesDir and all + * antecendants, saving String errors. + * * @see #removeLibraryFilesToSkip(Module, File) - */ - abstract protected boolean assembleAll( - Module module, - Messager handler); - - /** - * Generate the installer for this product to targDirPath - */ - abstract protected boolean buildInstaller( - BuildSpec buildSpec, - String targDirPath); - - /** - * Copy fromFile to toFile, optionally filtering contents - */ - abstract protected boolean copyFile( - File fromFile, - File toFile, - boolean filter); - - /** - * Copy toDir any fromDir included files without any exluded files, - * optionally filtering contents. - * @param fromDir File dir to read from - error if not readable - * @param toDir File dir to write to - error if not writable - * @param included String Ant pattern of included files (if null, include all) - * @param excluded String Ant pattern of excluded files (if null, exclude none) - * @param filter if FILTER_ON, then filter file contents using global token/value pairs - */ - abstract protected boolean copyFiles( - File fromDir, - File toDir, - String included, - String excluded, - boolean filter); + */ + abstract protected boolean assembleAll(Result result, Messager handler); + + /** + * Generate the installer for this product to targDirPath + */ + abstract protected boolean buildInstaller(BuildSpec buildSpec, + String targDirPath); + + /** + * Copy fromFile to toFile, optionally filtering contents + */ + abstract protected boolean copyFile(File fromFile, File toFile, + boolean filter); + + /** + * Copy toDir any fromDir included files without any exluded files, + * optionally filtering contents. + * + * @param fromDir + * File dir to read from - error if not readable + * @param toDir + * File dir to write to - error if not writable + * @param included + * String Ant pattern of included files (if null, include all) + * @param excluded + * String Ant pattern of excluded files (if null, exclude none) + * @param filter + * if FILTER_ON, then filter file contents using global + * token/value pairs + */ + abstract protected boolean copyFiles(File fromDir, File toDir, + String included, String excluded, boolean filter); } diff --git a/build/src/org/aspectj/internal/tools/build/Module.java b/build/src/org/aspectj/internal/tools/build/Module.java index 8539e6c8c..d37b8bed4 100644 --- a/build/src/org/aspectj/internal/tools/build/Module.java +++ b/build/src/org/aspectj/internal/tools/build/Module.java @@ -21,13 +21,15 @@ import java.io.FileReader; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Properties; import java.util.StringTokenizer; +import org.aspectj.internal.tools.build.Result.Kind; + + /** * This represents an (eclipse) build module/unit used by a Builder to compile * classes and/or assemble zip file of classes, optionally with all antecedants. @@ -97,16 +99,18 @@ public class Module { * * @see findKnownJarAntecedants() */ - private static void doFindKnownJarAntecedants(Module module, List known) { - Util.iaxIfNull(module, "module"); + static void doFindKnownJarAntecedants(Result result, List known) { + Util.iaxIfNull(result, "result"); Util.iaxIfNull(known, "known"); - addIfNew(module.getLibJars(), known); - for (Iterator iter = module.getRequired().iterator(); iter.hasNext();) { - Module required = (Module) iter.next(); - File requiredJar = required.getModuleJar(); + addIfNew(result.getLibJars(), known); + addIfNew(result.getExportedLibJars(), known); + Result[] reqs = result.getRequired(); + for (int i = 0; i < reqs.length; i++) { + Result requiredResult = reqs[i]; + File requiredJar = requiredResult.getOutputFile(); if (!known.contains(requiredJar)) { known.add(requiredJar); - doFindKnownJarAntecedants(required, known); + doFindKnownJarAntecedants(requiredResult, known); } } } @@ -117,6 +121,31 @@ public class Module { return (path.endsWith(".java") || path.endsWith(".aj")); // XXXFileLiteral } + /** @return List of File of any module or library jar ending with suffix */ + private static ArrayList findJarsBySuffix(String suffix, Kind kind, + List libJars, List required) { + ArrayList result = new ArrayList(); + if (null != suffix) { + // library jars + for (Iterator iter = libJars.iterator(); iter.hasNext();) { + File file = (File) iter.next(); + if (file.getPath().endsWith(suffix)) { + result.add(file); + } + } + // module jars + for (Iterator iter = required.iterator(); iter.hasNext();) { + Module module = (Module) iter.next(); + Result moduleResult = module.getResult(kind); + File file = moduleResult.getOutputFile(); + if (file.getPath().endsWith(suffix)) { + result.add(file); + } + } + } + return result; + } + public final boolean valid; public final File moduleDir; @@ -124,7 +153,15 @@ public class Module { public final String name; /** reference back to collection for creating required modules */ - final Modules modules; + private final Modules modules; + + private final Result release; + + private final Result test; + + private final Result testAll; + + private final Result releaseAll; /** path to output jar - may not exist */ private final File moduleJar; @@ -151,7 +188,7 @@ public class Module { private final Properties properties; /** Module list of required modules */ - private final List required; + private final List requiredModules; /** List of File that are newer than moduleJar. Null until requested */ // private List newerFiles; @@ -161,23 +198,22 @@ public class Module { /** true if we have calculated whether this is out of date */ private boolean outOfDateSet; - /** if true, trim testing-related source directories, modules, and libraries */ - private final boolean trimTesting; - /** logger */ private final Messager messager; + private final File jarDir; + Module(File moduleDir, File jarDir, String name, Modules modules, - boolean trimTesting, Messager messager) { + Messager messager) { Util.iaxIfNotCanReadDir(moduleDir, "moduleDir"); Util.iaxIfNotCanReadDir(jarDir, "jarDir"); Util.iaxIfNull(name, "name"); Util.iaxIfNull(modules, "modules"); this.moduleDir = moduleDir; - this.trimTesting = trimTesting; + this.jarDir = jarDir; this.libJars = new ArrayList(); this.exportedLibJars = new ArrayList(); - this.required = new ArrayList(); + this.requiredModules = new ArrayList(); this.srcDirs = new ArrayList(); this.classpathVariables = new ArrayList(); this.properties = new Properties(); @@ -186,128 +222,65 @@ public class Module { this.messager = messager; this.moduleJar = new File(jarDir, name + ".jar"); this.assembledJar = new File(jarDir, name + "-all.jar"); + this.release = new Result(Result.RELEASE, this, jarDir); + this.releaseAll = new Result(Result.RELEASE_ALL, this, jarDir); + this.test = new Result(Result.TEST, this, jarDir); + this.testAll = new Result(Result.TEST_ALL, this, jarDir); valid = init(); } - /** @return path to output jar - may not exist */ - public File getModuleJar() { - return moduleJar; - } - - /** @return path to output assembled jar - may not exist */ - public File getAssembledJar() { - return assembledJar; - } - - /** @return unmodifiable List of String classpath variables */ - public List getClasspathVariables() { - return Collections.unmodifiableList(classpathVariables); - } - - /** @return unmodifiable List of required modules String names */ - public List getRequired() { - return Collections.unmodifiableList(required); - } - - /** @return unmodifiable list of exported library files, guaranteed readable */ - public List getExportedLibJars() { - return Collections.unmodifiableList(exportedLibJars); - } - - /** @return unmodifiable list of required library files, guaranteed readable */ - public List getLibJars() { - return Collections.unmodifiableList(libJars); - } - - /** @return unmodifiable list of source directories, guaranteed readable */ - public List getSrcDirs() { - return Collections.unmodifiableList(srcDirs); - } /** @return Modules registry of known modules, including this one */ public Modules getModules() { return modules; } - /** @return List of File jar paths to be merged into module-dist */ - public List getMerges() { - String value = properties.getProperty(name + ".merges"); - if ((null == value) || (0 == value.length())) { - return Collections.EMPTY_LIST; - } - ArrayList result = new ArrayList(); - StringTokenizer st = new StringTokenizer(value); - while (st.hasMoreTokens()) { - result.addAll(findJarsBySuffix(st.nextToken())); - } - return result; - } - - public void clearOutOfDate() { - outOfDate = false; - outOfDateSet = false; - } - /** + * @param kind + * the Kind of the result to recalculate * @param recalculate * if true, then force recalculation * @return true if the target jar for this module is older than any source * files in a source directory or any required modules or any * libraries or if any libraries or required modules are missing */ - public boolean outOfDate(boolean recalculate) { - if (recalculate) { - outOfDateSet = false; - } - if (!outOfDateSet) { - outOfDate = false; - try { - if (!(moduleJar.exists() && moduleJar.canRead())) { - return outOfDate = true; - } - final long time = moduleJar.lastModified(); - File file; - for (Iterator iter = srcDirs.iterator(); iter.hasNext();) { - File srcDir = (File) iter.next(); - for (Iterator srcFiles = sourceFiles(srcDir); srcFiles - .hasNext();) { - file = (File) srcFiles.next(); - if (outOfDate(time, file)) { - return outOfDate = true; - } - } - } - // required modules - for (Iterator iter = getRequired().iterator(); iter.hasNext();) { - Module required = (Module) iter.next(); - file = required.getModuleJar(); - if (outOfDate(time, file)) { - return outOfDate = true; - } - } - // libraries - for (Iterator iter = getLibJars().iterator(); iter.hasNext();) { - file = (File) iter.next(); - if (outOfDate(time, file)) { - return outOfDate = true; - } + public static boolean outOfDate(Result result, boolean recalculate) { + File outputFile = result.getOutputFile(); + if (!(outputFile.exists() && outputFile.canRead())) { + return true; + } + final long time = outputFile.lastModified(); + File file; + for (Iterator iter = result.getSrcDirs().iterator(); iter.hasNext();) { + File srcDir = (File) iter.next(); + for (Iterator srcFiles = sourceFiles(srcDir); srcFiles.hasNext();) { + file = (File) srcFiles.next(); + if (outOfDate(time, file)) { + return true; } - } finally { - outOfDateSet = true; } } - return outOfDate; + // required modules + final Kind kind = result.getKind(); + Result[] reqs = result.getRequired(); + for (int i = 0; i < reqs.length; i++) { + Result requiredResult = reqs[i]; + file = requiredResult.getOutputFile(); + if (outOfDate(time, file)) { + return true; + } + } + // libraries + for (Iterator iter = result.getLibJars().iterator(); iter.hasNext();) { + file = (File) iter.next(); + if (outOfDate(time, file)) { + return true; + } + } + return false; } - /** - * Add any (File) library jar or (File) required module jar to the List - * known, if not added already. - */ - public ArrayList findKnownJarAntecedants() { - ArrayList result = new ArrayList(); - doFindKnownJarAntecedants(this, result); - return result; - } + public String toString() { return name; @@ -315,12 +288,45 @@ public class Module { public String toLongString() { return "Module [name=" + name + ", srcDirs=" + srcDirs + ", required=" - + required + ", moduleJar=" + moduleJar + ", libJars=" + + requiredModules + ", moduleJar=" + moduleJar + ", libJars=" + libJars + "]"; } + public Result getResult(Kind kind) { + return kind.assemble ? (kind.normal ? releaseAll : testAll) + : (kind.normal ? release : test); + } + + List srcDirs(Result result) { + myResult(result); + return srcDirs; + } + List libJars(Result result) { + myResult(result); + return libJars; + } + List classpathVariables(Result result) { + myResult(result); + return classpathVariables; + } + List exportedLibJars(Result result) { + myResult(result); + return exportedLibJars; + } + List requiredModules(Result result) { + myResult(result); + return requiredModules; + } + + private void myResult(Result result) { + if ((null == result) || this != result.getModule()) { + throw new IllegalArgumentException("not my result: " + result + ": " + this); + } + } + private boolean init() { - return initClasspath() && initProperties() && reviewInit(); + return initClasspath() && initProperties() && reviewInit() + && initResults(); } /** read eclipse .classpath file XXX line-oriented hack */ @@ -336,7 +342,7 @@ public class Module { while (null != (line = reader.readLine())) { line = line.trim(); // dumb - only handle comment-only lines - if (!line.startsWith("<?xml") && ! line.startsWith("<!--")) { + if (!line.startsWith("<?xml") && !line.startsWith("<!--")) { item.acceptLine(line); } } @@ -358,18 +364,19 @@ public class Module { String kind = attributes[getATTSIndex("kind")]; String path = attributes[getATTSIndex("path")]; String exp = attributes[getATTSIndex("exported")]; - boolean exported = ("true".equals(exp)); + boolean exported = ("true".equals(exp)); return update(kind, path, toString, exported); } - - private boolean update(String kind, String path, String toString, boolean exported) { + + private boolean update(String kind, String path, String toString, + boolean exported) { String libPath = null; if ("src".equals(kind)) { if (path.startsWith("/")) { // module String moduleName = path.substring(1); Module req = modules.getModule(moduleName); if (null != req) { - required.add(req); + requiredModules.add(req); return true; } else { messager.error("update unable to create required module: " @@ -468,13 +475,8 @@ public class Module { } /** - * Post-process initialization. This implementation trims testing-related - * source directories, libraries, and modules if trimTesting is - * enabled/true. For modules whose names start with "testing", - * testing-related sources are trimmed, but this does not trim dependencies - * on other modules prefixed "testing" or on testing libraries like junit. - * That means testing modules can be built with trimTesting enabled. - * + * Post-process initialization. This implementation trims java5 source dirs + * if not running in a Java 5 VM. * @return true if initialization post-processing worked */ protected boolean reviewInit() { @@ -482,44 +484,26 @@ public class Module { for (ListIterator iter = srcDirs.listIterator(); iter.hasNext();) { File srcDir = (File) iter.next(); String lcname = srcDir.getName().toLowerCase(); - if (trimTesting - && (Util.Constants.TESTSRC.equals(lcname) || Util.Constants.JAVA5_TESTSRC - .equals(lcname))) { - iter.remove(); - } else if (!Util.JAVA5_VM + if (!Util.JAVA5_VM && (Util.Constants.JAVA5_SRC.equals(lcname) || Util.Constants.JAVA5_TESTSRC .equals(lcname))) { // assume optional for pre-1.5 builds iter.remove(); } } - if (!trimTesting) { - return true; - } - if (!name.startsWith("testing")) { - for (ListIterator iter = libJars.listIterator(); iter.hasNext();) { - File libJar = (File) iter.next(); - String name = libJar.getName(); - if ("junit.jar".equals(name.toLowerCase())) { // XXXFileLiteral - iter.remove(); // XXX if verbose log - } - } - for (ListIterator iter = required.listIterator(); iter - .hasNext();) { - Module required = (Module) iter.next(); - String name = required.name; - // XXX testing-util only ? - if (name.toLowerCase().startsWith("testing")) { // XXXFileLiteral - iter.remove(); // XXX if verbose log - } - } - } } catch (UnsupportedOperationException e) { return false; // failed XXX log also if verbose } return true; } + /** + * After reviewInit, setup four kinds of results. + */ + protected boolean initResults() { + return true; // results initialized lazily + } + /** resolve path absolutely, assuming / means base of modules dir */ public String getFullPath(String path) { String fullPath; @@ -541,29 +525,6 @@ public class Module { return fullPath; } - /** @return List of File of any module or library jar ending with suffix */ - private ArrayList findJarsBySuffix(String suffix) { - ArrayList result = new ArrayList(); - if (null != suffix) { - // library jars - for (Iterator iter = getLibJars().iterator(); iter.hasNext();) { - File file = (File) iter.next(); - if (file.getPath().endsWith(suffix)) { - result.add(file); - } - } - // module jars - for (Iterator iter = getRequired().iterator(); iter.hasNext();) { - Module module = (Module) iter.next(); - File file = module.getModuleJar(); - if (file.getPath().endsWith(suffix)) { - result.add(file); - } - } - } - return result; - } - class ICB implements XMLItem.ICallback { public void end(Properties attributes) { String kind = attributes.getProperty("kind"); @@ -580,23 +541,28 @@ public class Module { public interface ICallback { void end(Properties attributes); } + static final String START_NAME = "classpathentry"; static final String ATT_STARTED = "STARTED"; final ICallback callback; + final StringBuffer input = new StringBuffer(); final String[] attributes = new String[ATTS.length]; + final String targetEntity; + String entityName; + String attributeName; XMLItem(String targetEntity, ICallback callback) { this.callback = callback; this.targetEntity = targetEntity; reset(); - + } private void reset() { @@ -630,7 +596,7 @@ public class Module { } else { result.add(s); } - } else { // not a delimiter + } else { // not a delimiter if (inQuote) { quote.append(s); } else { @@ -647,7 +613,7 @@ public class Module { next(tokens[i]); } } - + private Properties attributesToProperties() { Properties result = new Properties(); for (int i = 0; i < attributes.length; i++) { @@ -664,17 +630,17 @@ public class Module { error("Did not expect " + name + ": " + value); } } - + void errorIfNull(String name, String value) { if (null == value) { error("expected value for " + name); } } - + boolean activeEntity() { return targetEntity.equals(entityName); } - + /** * Assumes that comments and "<?xml"-style lines are removed. */ @@ -716,7 +682,9 @@ public class Module { } else if (null == attributeName) { attributeName = s; } else { - System.out.println("unknown state - not value, attribute, or entity: " + s); + System.out + .println("unknown state - not value, attribute, or entity: " + + s); } } } @@ -748,4 +716,3 @@ public class Module { } } } - diff --git a/build/src/org/aspectj/internal/tools/build/Modules.java b/build/src/org/aspectj/internal/tools/build/Modules.java index 55b2fa800..39ace3485 100644 --- a/build/src/org/aspectj/internal/tools/build/Modules.java +++ b/build/src/org/aspectj/internal/tools/build/Modules.java @@ -28,13 +28,11 @@ public class Modules { public final File baseDir; public final File jarDir; private final Messager handler; - public final boolean trimTesting; - public Modules(File baseDir, File jarDir, boolean trimTesting, Messager handler) { + public Modules(File baseDir, File jarDir, Messager handler) { this.baseDir = baseDir; this.jarDir = jarDir; this.handler = handler; - this.trimTesting = trimTesting; Util.iaxIfNotCanReadDir(baseDir, "baseDir"); Util.iaxIfNotCanReadDir(jarDir, "jarDir"); Util.iaxIfNull(handler, "handler"); @@ -55,7 +53,7 @@ public class Modules { if (!Util.canReadDir(moduleDir)) { handler.error("not a module: " + name); } else { - result = new Module(moduleDir, jarDir, name, this, trimTesting, handler); + result = new Module(moduleDir, jarDir, name, this, handler); if (result.valid) { modules.put(name, result); } else { diff --git a/build/src/org/aspectj/internal/tools/build/Result.java b/build/src/org/aspectj/internal/tools/build/Result.java new file mode 100644 index 000000000..3efd095bb --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/Result.java @@ -0,0 +1,401 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wes Isberg initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.build; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +/** + * Represents a prospective build result and any requirements for it. Used for + * [testing|normal][jar|assembled-jar|classesDir?]. + */ +public class Result { + public static final boolean NORMAL = true; + + public static final boolean ASSEMBLE = true; + + static final Kind RELEASE = new Kind("RELEASE", NORMAL, !ASSEMBLE); + + static final Kind RELEASE_ALL = new Kind("RELEASE_ALL", NORMAL, ASSEMBLE); + + static final Kind TEST = new Kind("TEST", !NORMAL, !ASSEMBLE); + + static final Kind TEST_ALL = new Kind("TEST_ALL", !NORMAL, ASSEMBLE); + + private static final Kind[] KINDS = { RELEASE, TEST, RELEASE_ALL, TEST_ALL }; + + private static final HashMap nameToResult = new HashMap(); + + public static boolean isTestingJar(String name) { + name = name.toLowerCase(); + return "junit.jar".equals(name); + } + + public static boolean isTestingDir(String name) { + name = name.toLowerCase(); + return (Util.Constants.TESTSRC.equals(name) || Util.Constants.JAVA5_TESTSRC + .equals(name)); + } + + public static boolean isTestingModule(Module module) { + String name = module.name.toLowerCase(); + return name.startsWith("testing") || "tests".equals(name); + } + + public static synchronized Result getResult(String name) { + if (null == name) { + throw new IllegalArgumentException("null name"); + } + return (Result) nameToResult.get(name); + } + + public static Result[] getResults(String[] names) { + if (null == names) { + return new Result[0]; + } + Result[] results = new Result[names.length]; + + for (int i = 0; i < results.length; i++) { + String name = names[i]; + if (null == name) { + String m = "no name at " + i + ": " + Arrays.asList(names); + throw new IllegalArgumentException(m); + } + Result r = Result.getResult(name); + if (null == r) { + String m = "no result [" + i + "]: " + name + ": " + + Arrays.asList(names); + throw new IllegalArgumentException(m); + } + results[i] = r; + } + return results; + + } + + public static Kind[] KINDS() { + Kind[] result = new Kind[KINDS.length]; + System.arraycopy(KINDS, 0, result, 0, result.length); + return result; + } + + public static void iaxUnlessNormal(Result result) { + if ((null == result) || !result.getKind().normal) { + throw new IllegalArgumentException("not normal: " + result); + } + } + + public static void iaxUnlessAssembly(Result result) { + if ((null == result) || !result.getKind().assemble) { + throw new IllegalArgumentException("not assembly: " + result); + } + } + + public static Kind kind(boolean normal, boolean assemble) { + return (normal == NORMAL ? (assemble == ASSEMBLE ? RELEASE_ALL + : RELEASE) : (assemble == ASSEMBLE ? TEST_ALL : TEST)); + } + + public static class Kind { + final String name; + + final boolean normal; + + final boolean assemble; + + private Kind(String name, boolean normal, boolean assemble) { + this.name = name; + this.normal = normal; + this.assemble = assemble; + } + + public final boolean isAssembly() { + return assemble; + } + + public final boolean isNormal() { + return normal; + } + + public final String toString() { + return name; + } + } + + /** path to output jar - may not exist */ + private final File outputFile; + + /** list of required Result */ + private final List requiredResults; + + /** File list of library jars */ + private final List libJars; + + /** String list of classpath variables */ + private final List classpathVariables; + + transient String toLongString; + + /** + * File list of library jars exported to clients (duplicates some libJars + * entries) + */ + private final List exportedLibJars; + + /** File list of source directories */ + private final List srcDirs; + + /** true if this has calculated List fields. */ + private boolean requiredDone; + + /** true if this has been found to be out of date */ + private boolean outOfDate; + + /** true if we have calculated whether this is out of date */ + private boolean outOfDateSet; + + private final Kind kind; + + private final Module module; + + private final String name; + + Result(Kind kind, Module module, File jarDir) { + this.kind = kind; + this.module = module; + this.libJars = new ArrayList(); + this.exportedLibJars = new ArrayList(); + this.srcDirs = new ArrayList(); + this.classpathVariables = new ArrayList(); + this.requiredResults = new ArrayList(); + String name = module.name; + if (!kind.normal) { + name += "-test"; + } + if (kind.assemble) { + name += "-all"; + } + this.name = name; + this.outputFile = new File(jarDir, name + ".jar"); + nameToResult.put(name, this); + } + + public String getName() { + return name; + } + + public File getOutputFile() { + return outputFile; + } + + public void rebuilding() { + outOfDate = true; + outOfDateSet = true; + if (outputFile.exists()) { + outputFile.delete(); + if (outputFile.exists()) { + throw new Error("unable to delete existing " + outputFile); + } + } + } + + public void clearOutOfDate() { + outOfDateSet = false; + outOfDate = false; + } + + public boolean outOfDate(boolean recalculate) { + if (recalculate) { + clearOutOfDate(); + } + if (!outOfDateSet) { + outOfDate = Module.outOfDate(this, recalculate); + outOfDateSet = true; + } + return outOfDate; + } + + public List findKnownJarAntecedants() { + ArrayList result = new ArrayList(); + Module.doFindKnownJarAntecedants(this, result); + return result; + } + + /** @return unmodifiable List of String classpath variables */ + public List getClasspathVariables() { + return safeList(classpathVariables); + } + + // + /** @return unmodifiable List of required modules String names */ + public Result[] getRequired() { + return safeResults(requiredResults); + } + + /** + * @return unmodifiable list of exported library files, guaranteed readable + */ + public List getExportedLibJars() { + return safeList(exportedLibJars); + } + + /** + * @return unmodifiable list of required library files, guaranteed readable + */ + public List getLibJars() { + requiredDone(); + return safeList(libJars); + } + + /** + * @return unmodifiable list of required library files, guaranteed readable + */ + // public List getMerges() { + // requiredDone(); + // return safeList(merges); + // } + /** @return unmodifiable list of source directories, guaranteed readable */ + public List getSrcDirs() { + return safeList(srcDirs); + } + + public Module getModule() { + return module; + } + + public Kind getKind() { + return kind; + } + + public String toLongString() { + if (null == toLongString) { + toLongString = name + "[outputFile=" + outputFile + + ", requiredResults=" + requiredResults + ", srcDirs=" + + srcDirs + ", libJars=" + libJars + "]"; + } + return toLongString; + } + + public String toString() { + return name; + } + + private List safeList(List l) { + requiredDone(); + return Collections.unmodifiableList(l); + } + + private Result[] safeResults(List list) { + requiredDone(); + if (null == list) { + return new Result[0]; + } + return (Result[]) list.toArray(new Result[0]); + } + + private void initSrcDirs() { + srcDirs.addAll(getModule().srcDirs(this)); + if (getKind().normal) { + // trim testing source directories + for (ListIterator iter = srcDirs.listIterator(); iter.hasNext();) { + File srcDir = (File) iter.next(); + if (isTestingDir(srcDir.getName())) { + iter.remove(); + } + } + } + } + + private void initLibJars() { + libJars.addAll(getModule().libJars(this)); + if (getKind().normal && !isTestingModule(getModule())) { + // trim testing libraries + for (ListIterator iter = libJars.listIterator(); iter.hasNext();) { + File libJar = (File) iter.next(); + if (isTestingJar(libJar.getName())) { + iter.remove(); + } + } + } + } + + private void assertKind(Kind kind) { + if (kind != getKind()) { + throw new IllegalArgumentException("expected " + getKind() + + " got " + kind); + } + } + + private void initRequiredResults() { + Module module = getModule(); + final Kind kind = getKind(); + if (kind.assemble) { + if (kind.normal) { + assertKind(RELEASE_ALL); + requiredResults.add(module.getResult(RELEASE)); + } else { + assertKind(TEST_ALL); + requiredResults.add(module.getResult(TEST)); + requiredResults.add(module.getResult(RELEASE)); + } + } else if (!kind.normal) { + assertKind(TEST); + requiredResults.add(module.getResult(RELEASE)); + } else { + assertKind(RELEASE); + } + // externally-required: + List modules = module.requiredModules(this); + final boolean thisIsTestingModule = isTestingModule(module); + for (Iterator iter = modules.iterator(); iter.hasNext();) { + module = (Module) iter.next(); + if (thisIsTestingModule || !kind.normal) { + // testing builds can rely on other release and test results + requiredResults.add(module.getResult(TEST)); + requiredResults.add(module.getResult(RELEASE)); + } else { + // release builds can only rely on non-testing results + // from non-testing modules + requiredResults.add(module.getResult(RELEASE)); + } + } + } + + private void initClasspathVariables() { + // no difference + classpathVariables.addAll(getModule().classpathVariables(this)); + } + + private void initExportedLibJars() { + // no difference + exportedLibJars.addAll(getModule().exportedLibJars(this)); + } + + private synchronized void requiredDone() { + if (!requiredDone) { + initSrcDirs(); + initLibJars(); + initRequiredResults(); + initClasspathVariables(); + initExportedLibJars(); + requiredDone = true; + } + } + +} |