aboutsummaryrefslogtreecommitdiffstats
path: root/build/src/org/aspectj/internal/tools/build/Module.java
diff options
context:
space:
mode:
Diffstat (limited to 'build/src/org/aspectj/internal/tools/build/Module.java')
-rw-r--r--build/src/org/aspectj/internal/tools/build/Module.java501
1 files changed, 501 insertions, 0 deletions
diff --git a/build/src/org/aspectj/internal/tools/build/Module.java b/build/src/org/aspectj/internal/tools/build/Module.java
new file mode 100644
index 000000000..9c6022d18
--- /dev/null
+++ b/build/src/org/aspectj/internal/tools/build/Module.java
@@ -0,0 +1,501 @@
+/* *******************************************************************
+ * Copyright (c) 1999-2001 Xerox Corporation,
+ * 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * Xerox/PARC initial implementation
+ * ******************************************************************/
+
+
+package org.aspectj.internal.tools.build;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+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;
+
+/**
+ * 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.
+ * This implementation infers attributes from two
+ * files in the module directory:
+ * <ul>
+ * <li>an Eclipse project <code>.classpath</code> file
+ * containing required libraries and modules
+ * (collectively, "antecedants")
+ * </li>
+ * <li>a file <code>{moduleName}.mf.txt</code> is taken as
+ * the manifest of any .jar file produced, after filtering.
+ * </li>
+ * </ul>
+ *
+ * @see Builder
+ * @see Modules#getModule(String)
+ */
+public class Module {
+
+ /** @return true if file is null or cannot be read or was
+ * last modified after time
+ */
+ private static boolean outOfDate(long time, File file) {
+ return ((null == file)
+ || !file.canRead()
+ || (file.lastModified() > time));
+ }
+
+ /** @return all source files under srcDir */
+ private static Iterator sourceFiles(File srcDir) {
+ ArrayList result = new ArrayList();
+ sourceFiles(srcDir, result);
+ return result.iterator();
+ }
+
+ private static void sourceFiles(File srcDir, List result) {
+ if ((null == srcDir) || !srcDir.canRead() || !srcDir.isDirectory()) {
+ return;
+ }
+ File[] files = srcDir.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ if (files[i].isDirectory()) {
+ sourceFiles(files[i], result);
+ } else if (isSourceFile(files[i])) {
+ result.add(files[i]);
+ }
+ }
+ }
+
+ /**
+ * Recursively find antecedant jars.
+ * @see findKnownJarAntecedants()
+ */
+ private static void doFindKnownJarAntecedants(Module module, ArrayList known) {
+ Util.iaxIfNull(module, "module");
+ Util.iaxIfNull(known, "known");
+
+ for (Iterator iter = module.getLibJars().iterator(); iter.hasNext();) {
+ File libJar = (File) iter.next();
+ if (!skipLibraryJarAntecedant(libJar)
+ && !known.contains(libJar)) { // XXX what if same referent, diff path...
+ known.add(libJar);
+ }
+ }
+ for (Iterator iter = module.getRequired().iterator(); iter.hasNext();) {
+ Module required = (Module) iter.next();
+ File requiredJar = required.getModuleJar();
+ if (!known.contains(requiredJar)) {
+ known.add(requiredJar);
+ doFindKnownJarAntecedants(required, known);
+ }
+ }
+ }
+
+ /** XXX gack explicitly skip Ant */
+ private static boolean skipLibraryJarAntecedant(File libJar) {
+ if (null == libJar) {
+ return true;
+ }
+ String path = libJar.getPath().replace('\\', '/');
+ return (-1 == path.indexOf("/lib/ant/lib/"));
+ }
+
+ /**@return true if this is a source file */
+ private static boolean isSourceFile(File file) {
+ String path = file.getPath();
+ return (path.endsWith(".java") || path.endsWith(".aj")); // XXXFileLiteral
+ }
+
+ public final boolean valid;
+
+ public final File moduleDir;
+
+ public final String name;
+
+ /** reference back to collection for creating required modules */
+ final Modules modules;
+
+ /** path to output jar - may not exist */
+ private final File moduleJar;
+
+ /** path to fully-assembed jar - may not exist */
+ private final File assembledJar;
+
+ /** File list of library jars */
+ private final List libJars;
+
+ /** File list of source directories */
+ private final List srcDirs;
+
+ /** properties from the modules {name}.properties file */
+ private final Properties properties;
+
+ /** Module list of required modules */
+ private final List required;
+
+ /** List of File that are newer than moduleJar. Null until requested */
+ //private List newerFiles;
+ /** 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;
+
+ /** if true, trim testing-related source directories, modules, and libraries */
+ private final boolean trimTesting;
+
+ /** logger */
+ private final Messager messager;
+
+ Module(File moduleDir,
+ File jarDir,
+ String name,
+ Modules modules,
+ boolean trimTesting,
+ 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.libJars = new ArrayList();
+ this.required = new ArrayList();
+ this.srcDirs = new ArrayList();
+ this.properties = new Properties();
+ this.name = name;
+ this.modules = modules;
+ this.messager = messager;
+ this.moduleJar = new File(jarDir, name + ".jar");
+ this.assembledJar = new File(jarDir, name + "-all.jar");
+ 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 required modules String names*/
+ public List getRequired() {
+ return Collections.unmodifiableList(required);
+ }
+
+ /** @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 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;
+ }
+ }
+ } finally {
+ outOfDateSet = true;
+ }
+ }
+ return outOfDate;
+ }
+ /**
+ * 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;
+ }
+
+ public String toLongString() {
+ return
+ "Module [name="
+ + name
+ + ", srcDirs="
+ + srcDirs
+ + ", required="
+ + required
+ + ", moduleJar="
+ + moduleJar
+ + ", libJars="
+ + libJars
+ + "]";
+ }
+
+ private boolean init() {
+ return initClasspath() && initProperties() && reviewInit();
+ }
+
+ /** read eclipse .classpath file XXX line-oriented hack */
+ private boolean initClasspath() {
+ // meaning testsrc directory, junit library, etc.
+ File file = new File(moduleDir, ".classpath"); // XXXFileLiteral
+ FileReader fin = null;
+ try {
+ fin = new FileReader(file);
+ BufferedReader reader = new BufferedReader(fin);
+ String line;
+ String lastKind = null;
+ while (null != (line = reader.readLine())) {
+ lastKind = parseLine(line, lastKind);
+ }
+ return (0 < srcDirs.size());
+ } catch (IOException e) {
+ messager.logException("IOException reading " + file, e);
+ } finally {
+ if (null != fin) {
+ try { fin.close(); }
+ catch (IOException e) {} // ignore
+ }
+ }
+ return false;
+ }
+
+ /** @return true if any properties were read correctly */
+ private boolean initProperties() {
+ File file = new File(moduleDir, name + ".properties"); // XXXFileLiteral
+ if (!Util.canReadFile(file)) {
+ return true; // no properties to read
+ }
+ FileInputStream fin = null;
+ try {
+ fin = new FileInputStream(file);
+ properties.load(fin);
+ return true;
+ } catch (IOException e) {
+ messager.logException("IOException reading " + file, e);
+ return false;
+ } finally {
+ if (null != fin) {
+ try { fin.close(); }
+ catch (IOException e) {} // ignore
+ }
+ }
+ }
+
+ /**
+ * Post-process initialization.
+ * This implementation trims testing-related source
+ * directories, libraries, and modules if trimTesting is enabled/true.
+ * To build testing modules, trimTesting must be false.
+ * @return true if initialization post-processing worked
+ */
+ protected boolean reviewInit() {
+ if (!trimTesting) {
+ return true;
+ }
+ try {
+ for (ListIterator iter = srcDirs.listIterator(); iter.hasNext();) {
+ File srcDir = (File) iter.next();
+ String name = srcDir.getName();
+ if ("testsrc".equals(name.toLowerCase())) { // XXXFileLiteral
+ iter.remove(); // XXX if verbose log
+ }
+ }
+ 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;
+ }
+
+ private String parseLine(String line, String lastKind) {
+ if (null == line) {
+ return null;
+ }
+ String kind;
+ int loc = line.indexOf("kind=\"");
+ if ((-1 == loc) || (loc + 9 > line.length())) {
+ // no kind string - fail unless have lastKind
+ if (null == lastKind) {
+ return null;
+ } else {
+ kind = lastKind;
+ }
+ } else { // have kind string - get kind
+ loc += 6; // past kind="
+ kind = line.substring(loc, loc+3);
+ }
+
+ // now look for value
+ loc = line.indexOf("path=\"");
+ if (-1 == loc) { // no value - return lastKind
+ return kind;
+ }
+ loc += 6; // past path="
+ int end = line.indexOf("\"", loc);
+ if (-1 == end) {
+ throw new Error("unterminated path in " + line);
+ }
+ final String path = line.substring(loc, end);
+
+ if ("src".equals(kind)) {
+ if (path.startsWith("/")) { // module
+ String moduleName = path.substring(1);
+ Module req = modules.getModule(moduleName);
+ if (null != req) {
+ required.add(req);
+ } else {
+ messager.error("unable to create required module: " + moduleName);
+ }
+ } else { // src dir
+ File srcDir = new File(moduleDir, path);
+ if (srcDir.canRead() && srcDir.isDirectory()) {
+ srcDirs.add(srcDir);
+ } else {
+ messager.error("not a src dir: " + srcDir);
+ }
+ }
+ } else if ("lib".equals(kind)) {
+ String libPath = path.startsWith("/")
+ ? modules.baseDir.getAbsolutePath() + path
+ : path;
+ File libJar = new File(libPath);
+ if (libJar.canRead() && libJar.isFile()) {
+ libJars.add(libJar);
+ } else {
+ messager.error("no such library jar " + libJar + " from " + line);
+ }
+ } else if ("var".equals(kind)) {
+ if (!"JRE_LIB".equals(path)) {
+ messager.log("cannot handle var yet: " + line);
+ }
+ } else if ("con".equals(kind)) {
+ messager.log("cannot handle con yet: " + line);
+ } else if ("out".equals(kind)) {
+ // ignore output entries
+ } else {
+ messager.log("unrecognized kind " + kind + " in " + line);
+ }
+ return null;
+ }
+
+ /** @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;
+ }
+}
+