diff options
author | Andy Clement <aclement@pivotal.io> | 2019-02-01 12:42:09 -0800 |
---|---|---|
committer | Andy Clement <aclement@pivotal.io> | 2019-02-01 12:42:09 -0800 |
commit | aac7e58448f0e5a961075baca670dac1ae53ce98 (patch) | |
tree | 556fc80a0eee42b870de96bf6cbb41cc6fb452e4 /build/src/main | |
parent | 5eec03130695a6b918792ec7b87a2f69a20ed778 (diff) | |
download | aspectj-aac7e58448f0e5a961075baca670dac1ae53ce98.tar.gz aspectj-aac7e58448f0e5a961075baca670dac1ae53ce98.zip |
mavenizing build - wip
Diffstat (limited to 'build/src/main')
23 files changed, 8425 insertions, 0 deletions
diff --git a/build/src/main/java./aspectj/internal/tools/ant/taskdefs/AJInstaller.java b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/AJInstaller.java new file mode 100644 index 000000000..6bc2fc09b --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/AJInstaller.java @@ -0,0 +1,364 @@ +/* ******************************************************************* + * 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 Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +//XXX INCLUDES CODE FROM ANT -- UNDER APACHE LICENSE +package org.aspectj.internal.tools.ant.taskdefs; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringBufferInputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Copy; +import org.apache.tools.ant.taskdefs.Delete; +import org.apache.tools.ant.taskdefs.Expand; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.PatternSet; + +public class AJInstaller extends MatchingTask { + static final String INCLUDE_CLASSES = "$installer$/org/aspectj/*.class"; + static final String MAIN_CLASS = "$installer$.org.aspectj.Main"; + static final String CONTENTS_FILE = "$installer$/org/aspectj/resources/contents.txt"; + private String htmlSrc; + + public void setHtmlSrc(String v) { htmlSrc = v; } + + private String resourcesSrc; + + public void setResourcesSrc(String v) { resourcesSrc = v; } + + private String mainclass; + + public void setMainclass(String v) { mainclass = v; } + + private File installerClassJar; + + public void setInstallerclassjar(String v) { + installerClassJar = project.resolveFile(v); + } + + protected List contentsNames = new ArrayList(); + + protected long contentsBytes = 0; + + protected void addToContents(File file, String vPath) { + contentsNames.add(vPath); + contentsBytes += file.length(); + } + + String[] getFiles(File baseDir) { + DirectoryScanner ds = new DirectoryScanner(); + setBasedir(baseDir.getAbsolutePath()); + ds.setBasedir(baseDir); + //ds.setIncludes(new String [] {pattern}); + ds.scan(); + return ds.getIncludedFiles(); + } + + protected Copy getCopyTask() { + Copy cd = (Copy)project.createTask("copy"); + if (null == cd) { + log("project.createTask(\"copy\") failed - direct", Project.MSG_VERBOSE); + cd = new Copy(); + cd.setProject(getProject()); + } + return cd; + } + protected void finishZipOutputStream(ZipOutputStream zOut) throws IOException, BuildException { + writeContents(zOut); + writeManifest(zOut); + File tempDir = setupTempDir(); + String tmp = tempDir.getAbsolutePath(); + + // installer class files + Expand expand = new Expand(); + expand.setProject(getProject()); + expand.setSrc(installerClassJar); + expand.setDest(new File(tmp)); + PatternSet patterns = new PatternSet(); + patterns.setIncludes(INCLUDE_CLASSES); + expand.addPatternset(patterns); + expand.execute(); + + // move the correct resource files into the jar + Copy cd = getCopyTask(); + fileset = new FileSet(); + fileset.setDir(new File(resourcesSrc)); + fileset.setIncludes("*"); + fileset.setExcludes("contents.txt,properties.txt"); + cd.addFileset(fileset); + cd.setTodir(new File(tmp+"/$installer$/org/aspectj/resources")); + cd.execute(); + project.getGlobalFilterSet().addFilter("installer.main.class", this.mainclass); + Copy cf = getCopyTask(); + fileset = new FileSet(); + fileset.setDir(new File(resourcesSrc)); + fileset.setIncludes("properties.txt"); + cf.setFiltering(true); + cf.addFileset(fileset); + cf.setTodir(new File(tmp+"/$installer$/org/aspectj/resources")); + cf.execute(); + // move the correct resource files into the jar + cd = getCopyTask(); + fileset = new FileSet(); + fileset.setDir(new File(htmlSrc)); + fileset.setIncludes("*"); + cd.addFileset(fileset); + cd.setTodir(new File(tmp+"/$installer$/org/aspectj/resources")); + cd.execute(); + // now move these files into the jar + setBasedir(tmp); + writeFiles(zOut, getFiles(tempDir)); + // and delete the tmp dir + Delete dt = (Delete)project.createTask("delete"); + if (null == dt) { + dt = new Delete(); + dt.setProject(getProject()); + } + dt.setDir(tempDir); + dt.execute(); + tempDir = null; + } + + static final char NEWLINE = '\n'; + + protected void writeContents(ZipOutputStream zOut) throws IOException { + // write to a StringBuffer + StringBuffer buf = new StringBuffer(); + buf.append(contentsBytes); + buf.append(NEWLINE); + for (Iterator i = contentsNames.iterator(); i.hasNext(); ) { + String name = (String)i.next(); + buf.append(name); + buf.append(NEWLINE); + } + zipFile(new StringBufferInputStream(buf.toString()), zOut, CONTENTS_FILE, System.currentTimeMillis()); + } + + protected void writeManifest(ZipOutputStream zOut) throws IOException { + // write to a StringBuffer + StringBuffer buf = new StringBuffer(); + buf.append("Manifest-Version: 1.0"); + buf.append(NEWLINE); + buf.append("Main-Class: " + MAIN_CLASS); + buf.append(NEWLINE); + zipFile(new StringBufferInputStream(buf.toString()), zOut, "META-INF/MANIFEST.MF", System.currentTimeMillis()); + } + + //XXX cut-and-paste from Zip super-class (under apache license) + private File zipFile; + private File baseDir; + private boolean doCompress = true; + protected String archiveType = "zip"; + + /** + * This is the name/location of where to + * create the .zip file. + */ + public void setZipfile(String zipFilename) { + zipFile = project.resolveFile(zipFilename); + } + + /** + * This is the base directory to look in for + * things to zip. + */ + public void setBasedir(String baseDirname) { + baseDir = project.resolveFile(baseDirname); + } + + /** + * Sets whether we want to compress the files or only store them. + */ + public void setCompress(String compress) { + doCompress = Project.toBoolean(compress); + } + + protected void initZipOutputStream(ZipOutputStream zOut) + throws IOException, BuildException + { + } + + protected void zipDir(File dir, ZipOutputStream zOut, String vPath) + throws IOException + { + } + + protected void zipFile(InputStream in, ZipOutputStream zOut, String vPath, + long lastModified) + throws IOException + { + ZipEntry ze = new ZipEntry(vPath); + ze.setTime(lastModified); + + /* + * XXX ZipOutputStream.putEntry expects the ZipEntry to know its + * size and the CRC sum before you start writing the data when using + * STORED mode. + * + * This forces us to process the data twice. + * + * I couldn't find any documentation on this, just found out by try + * and error. + */ + if (!doCompress) { + long size = 0; + CRC32 cal = new CRC32(); + if (!in.markSupported()) { + // Store data into a byte[] + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buffer = new byte[8 * 1024]; + int count = 0; + do { + size += count; + cal.update(buffer, 0, count); + bos.write(buffer, 0, count); + count = in.read(buffer, 0, buffer.length); + } while (count != -1); + in = new ByteArrayInputStream(bos.toByteArray()); + } else { + in.mark(Integer.MAX_VALUE); + byte[] buffer = new byte[8 * 1024]; + int count = 0; + do { + size += count; + cal.update(buffer, 0, count); + count = in.read(buffer, 0, buffer.length); + } while (count != -1); + in.reset(); + } + ze.setSize(size); + ze.setCrc(cal.getValue()); + } + zOut.putNextEntry(ze); + byte[] buffer = new byte[8 * 1024]; + int count = 0; + do { + zOut.write(buffer, 0, count); + count = in.read(buffer, 0, buffer.length); + } while (count != -1); + } + + protected void zipFile(File file, ZipOutputStream zOut, String vPath) + throws IOException + { + if ( !vPath.startsWith("$installer$") ) { + addToContents(file, vPath); + } + FileInputStream fIn = new FileInputStream(file); + try { + zipFile(fIn, zOut, vPath, file.lastModified()); + } finally { + fIn.close(); + } + } + private File setupTempDir() throws BuildException { + File tmpDirF = null; + File tmpDir = null; + try { + tmpDirF = File.createTempFile("tgz", ".di"); + tmpDir = new File(tmpDirF.getParentFile(), "AJInstaller"); + tmpDirF.delete(); + } catch (IOException e) { + // retrying below + } + if (null == tmpDir || !tmpDir.mkdirs()) { + tmpDir = new File("AJInstaller.finishZipOutputStream.tmp"); + if (!tmpDir.mkdirs()) { + throw new BuildException("unable to make temp dir"); + } + } + return tmpDir; + } + + public void execute() throws BuildException { + if (installerClassJar == null) { + throw new BuildException("installerClassJar attribute must be set!"); + } + if (!installerClassJar.canRead() + || !installerClassJar.getPath().endsWith(".jar")) { + throw new BuildException("not readable jar:" + installerClassJar); + } +// if (installerClassDir == null) { +// throw new BuildException("installerClassDir attribute must be set!"); +// } +// if (!installerClassDir.exists()) { +// throw new BuildException("no such directory: installerClassDir=" + installerClassDir); +// } + if (baseDir == null) { + throw new BuildException("basedir attribute must be set!"); + } + if (!baseDir.exists()) { + throw new BuildException("basedir does not exist!"); + } + DirectoryScanner ds = super.getDirectoryScanner(baseDir); + String[] files = ds.getIncludedFiles(); + String[] dirs = ds.getIncludedDirectories(); + log("Building installer: "+ zipFile.getAbsolutePath()); + ZipOutputStream zOut = null; + try { + zOut = new ZipOutputStream(new FileOutputStream(zipFile)); + if (doCompress) { + zOut.setMethod(ZipOutputStream.DEFLATED); + } else { + zOut.setMethod(ZipOutputStream.STORED); + } + initZipOutputStream(zOut); + writeDirs(zOut, dirs); + writeFiles(zOut, files); + finishZipOutputStream(zOut); // deletes temp dir + } catch (IOException ioe) { + String msg = "Problem creating " + archiveType + " " + ioe.getMessage(); + throw new BuildException(msg, ioe, location); + } finally { + if (zOut != null) { + try { + // close up + zOut.close(); + } + catch (IOException e) {} + } + } + } + + protected void writeDirs(ZipOutputStream zOut, String[] dirs) throws IOException { + for (int i = 0; i < dirs.length; i++) { + File f = new File(baseDir,dirs[i]); + String name = dirs[i].replace(File.separatorChar,'/')+"/"; + zipDir(f, zOut, name); + } + } + + protected void writeFiles(ZipOutputStream zOut, String[] files) throws IOException { + for (int i = 0; i < files.length; i++) { + File f = new File(baseDir,files[i]); + String name = files[i].replace(File.separatorChar,'/'); + zipFile(f, zOut, name); + } + } + +} diff --git a/build/src/main/java./aspectj/internal/tools/ant/taskdefs/AJPush.java b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/AJPush.java new file mode 100644 index 000000000..26ab7ce5f --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/AJPush.java @@ -0,0 +1,91 @@ +/* ******************************************************************* + * 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 Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.text.DecimalFormat; +import java.util.Properties; + +import org.apache.tools.ant.taskdefs.Mkdir; + +public class AJPush extends ConditionalTask { + private File src; + + public void setSrc(String v) { src = project.resolveFile(v); } + + private String key; + + public void setKey(String v) { key = v; } + + File releaseDir = null; + File downloadDir = null; + boolean waiting = false; + + public void execute() throws org.apache.tools.ant.BuildException { + //File releaseDir = src.getParentFile(); + // todo: dependency on ant script variable name aj.release.dir + releaseDir = project.resolveFile(project.getProperty("aj.release.dir")); + // todo: dependency on ant script variable name download.dir + downloadDir = project.resolveFile(project.getProperty("download.dir")); + // For testing make sure these directories are made + Mkdir mkdir = (Mkdir) project.createTask("mkdir"); + mkdir.setDir(releaseDir); + mkdir.execute(); + mkdir = (Mkdir) project.createTask("mkdir"); + mkdir.setDir(downloadDir); + mkdir.execute(); + log("Pushing from " + releaseDir + " to " + downloadDir); + // add info to release.txt + try { + File releaseFile = new File(releaseDir, "release.txt"); + File downloadFile = new File(downloadDir, "release.txt"); + if (!releaseFile.canRead()) { + releaseFile.createNewFile(); + } + addReleaseInfo(src, releaseFile); + // copy to staging web server + project.copyFile(src, new File(downloadDir, src.getName())); + project.copyFile(releaseFile, downloadFile); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + void addReleaseInfo(File file, File propFile) throws IOException { + Properties props = new Properties(); + if (propFile.canRead()) { + props.load(new FileInputStream(propFile)); + } + file.createNewFile(); // create new only if necessary + long bytes = file.length(); + DecimalFormat df = new DecimalFormat(); + df.setGroupingSize(3); + String bytesString = df.format(bytes); + props.put("release." + key + ".size.bytes", bytesString); + props.put("release." + key + ".date", project.getProperty("build.date")); + props.put("release." + key + ".filename", file.getName()); + props.put("release.date", project.getProperty("build.date")); + props.put("release.version", project.getProperty("build.version.short")); + props.put("release.versionName", project.getProperty("build.version.long")); + String userName = System.getProperty("user.name"); + if (userName != null) { + props.put("release." + key + ".username", userName); + } + props.store(new FileOutputStream(propFile), null); + } + +} diff --git a/build/src/main/java./aspectj/internal/tools/ant/taskdefs/AntBuilder.java b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/AntBuilder.java new file mode 100644 index 000000000..d65c50501 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/AntBuilder.java @@ -0,0 +1,833 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * 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://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ +package org.aspectj.internal.tools.ant.taskdefs; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Target; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.Copy; +import org.apache.tools.ant.taskdefs.Javac; +import org.apache.tools.ant.taskdefs.Zip; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.ZipFileSet; +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.Result; +import org.aspectj.internal.tools.build.Util; + +/** + * Implement Builder in Ant. + */ +public class AntBuilder extends Builder { + private static final boolean FORCE_FORK_FOR_LIBRARIES = false; + + /** + * Factory for a Builder. + * + * @param config the String configuration, where only substrings "verbose" and "useEclipseCompiles" are significant + * @param project the owning Project for all tasks (not null) + * @param tempDir the File path to a temporary dir for side effects (may be null) + * @return a Builder for this project and configuration + */ + public static Builder getBuilder(String config, Project project, File tempDir) { + boolean useEclipseCompiles = false; + boolean verbose = false; + if (null != config) { + if (-1 != config.indexOf("useEclipseCompiles")) { + useEclipseCompiles = true; + } + if (-1 != config.indexOf("verbose")) { + verbose = true; + } + } + // Messager handler = new Messager(); // debugging + Messager handler = new ProjectMessager(project); + Builder result = new ProductBuilder(project, tempDir, useEclipseCompiles, handler); + if (verbose) { + result.setVerbose(true); + } + return result; + } + + private static String resultToTargetName(Result result) { + return result.getName(); + } + + /** + * Ensure targets exist for this module and all antecedants, so topoSort can work. + */ + private static void makeTargetsForResult(final Result result, final Hashtable<String,Target> targets) { + final String resultTargetName = resultToTargetName(result); + 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(resultToTargetName(reqResult)); + } + if (0 < depends.length()) { + target.setDepends(depends.toString()); + } + targets.put(resultTargetName, target); + + // then recursively add any required results + for (int i = 0; i < reqs.length; i++) { + Result reqResult = reqs[i]; + makeTargetsForResult(reqResult, targets); + } + } + } + + private final Project project; + + protected AntBuilder(Project project, File tempDir, boolean useEclipseCompiles, Messager handler) { + super(tempDir, useEclipseCompiles, handler); + this.project = project; + Util.iaxIfNull(project, "project"); + } + + /** + * Initialize task with project and "ajbuild-" + name as name. (Using bm- prefix distinguishes these tasks from tasks found in + * the build script.) + * + * @param task the Task to initialize - not null + * @param name the String name suffix for the task + * @return true unless some error + */ + protected boolean setupTask(Task task, String name) { + task.setProject(project); + task.setTaskName("ajbuild-" + name); + return true; + } + + /** + * Copy file, optionally filtering. (Filters set in project.) + * + * @param fromFile the readable File source to copy + * @param toFile the writable File destination file + * @param boolean filter if true, enable filtering + * @see org.aspectj.internal.tools.build.Builder#copyFile(File, File, boolean) + */ + @Override + protected boolean copyFile(File fromFile, File toFile, boolean filter) { + Copy copy = makeCopyTask(filter); + copy.setFile(fromFile); + copy.setTofile(toFile); + executeTask(copy); + return true; + } + + /** + * (Filters set in project.) + * + * @see org.aspectj.internal.tools.ant.taskdefs.Builder#copyFiles(File, File, String, String, boolean) + */ + @Override + protected boolean copyFiles(File fromDir, File toDir, String includes, String excludes, boolean filter) { + Copy copy = makeCopyTask(filter); + copy.setTodir(toDir); + FileSet fileset = new FileSet(); + fileset.setDir(fromDir); + if (null != includes) { + fileset.setIncludes(includes); + } + if (null != excludes) { + fileset.setExcludes(excludes); + } + copy.addFileset(fileset); + executeTask(copy); + + return false; + } + + protected void copyFileset(File toDir, FileSet fileSet, boolean filter) { + Copy copy = makeCopyTask(filter); + copy.addFileset(fileSet); + copy.setTodir(toDir); + executeTask(copy); + } + + /** + * @param filter if FILTER_ON, use filters + */ + protected Copy makeCopyTask(boolean filter) { + Copy copy = new Copy(); + setupTask(copy, "copy"); + if (FILTER_ON == filter) { + copy.setFiltering(true); + } + return copy; + } + + protected void dumpMinFile(Result result, File classesDir, List<String> 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); + } + + } + + @Override + protected boolean compile(Result result, File classesDir, boolean useExistingClasses, List<String> 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; + boolean isJava8Compile = false; + for (File file: result.getSrcDirs()) { + path.createPathElement().setLocation(file); + if (!isJava5Compile + && (Util.Constants.JAVA5_SRC.equals(file.getName()) || + Util.Constants.JAVA5_TESTSRC.equals(file.getName()) || + new File(file.getParent(), ".isJava5").exists())) { + isJava5Compile = true; + } + if (new File(file.getParent(),".isJava8").exists()) { + isJava8Compile = true; + } + if (!hasSourceDirectories) { + hasSourceDirectories = true; + } + } + if (!hasSourceDirectories) { + return true; // nothing to compile - ok + } + // XXX test whether build.compiler property takes effect automatically + // I suspect it requires the proper adapter setup. + Javac javac = new Javac(); + setupTask(javac, "javac"); + javac.setIncludeantruntime(false); + javac.setDestdir(classesDir); + javac.setSrcdir(path); + javac.setVerbose(verbose); + path = null; + + // -- classpath + Path classpath = new Path(project); + boolean hasLibraries = setupClasspath(result, classpath); + if (hasLibraries && FORCE_FORK_FOR_LIBRARIES) { + javac.setFork(true); // otherwise never releases library jars + // can we build under 1.4, but fork javac 1.5 compile? + } + // also fork if using 1.5? + + // -- set output directory + classpath.createPathElement().setLocation(classesDir); + javac.setClasspath(classpath); + + // misc + javac.setDebug(true); + if (isJava8Compile) { + javac.setSource("1.8"); + javac.setTarget("1.8"); + } else if (isJava5Compile) { + // *cough* + javac.setSource("1.6"); + javac.setTarget("1.6"); + } else { + javac.setTarget("1.1"); // 1.1 class files - Javac in 1.4 uses 1.4 + javac.setSource("1.3"); + } + // compile + boolean passed = false; + BuildException failure = null; + try { + passed = executeTask(AspectJSupport.wrapIfNeeded(result, javac)); + } catch (BuildException e) { + failure = e; + } catch (Error e) { + failure = new BuildException(e); + } 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... + } + return passed; + } + + public boolean setupClasspath(Result result, Path classpath) { // XXX fix test access + boolean hasLibraries = false; + // required libraries + for (Iterator iter = result.getLibJars().iterator(); iter.hasNext();) { + File file = (File) iter.next(); + classpath.createPathElement().setLocation(file); + if (!hasLibraries) { + hasLibraries = true; + } + } + // Westodo Kind kind = result.getKind(); + Result[] reqs = result.getRequired(); + // required modules and their exported libraries + 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 = requiredResult.getExportedLibJars().iterator(); iterator.hasNext();) { + classpath.createPathElement().setLocation((File) iterator.next()); + } + } + return hasLibraries; + } + + /** + * Merge classes directory and any merge jars into module jar with any specified manifest file. META-INF directories are + * excluded. + */ + @Override + protected boolean assemble(Result result, File classesDir, List<String> errors) { + if (!buildingEnabled) { + return false; + } + if (!result.outOfDate()) { + return true; + } + + // ---- zip result up + Zip zip = new Zip(); + setupTask(zip, "zip"); + zip.setDestFile(result.getOutputFile()); + ZipFileSet zipfileset = null; + + // -- merge any resources in any of the src directories + for (Iterator iter = result.getSrcDirs().iterator(); iter.hasNext();) { + File srcDir = (File) iter.next(); + zipfileset = new ZipFileSet(); + zipfileset.setProject(project); + zipfileset.setDir(srcDir); + zipfileset.setIncludes(RESOURCE_PATTERN); + zip.addZipfileset(zipfileset); + } + + final Module module = result.getModule(); + + File metaInfDir = new File(classesDir, "META-INF"); + Util.deleteContents(metaInfDir); + + // -- manifest + File manifest = new File(module.moduleDir, module.name + ".mf.txt"); // XXXFileLiteral + if (Util.canReadFile(manifest)) { + if (Util.canReadDir(metaInfDir) || metaInfDir.mkdirs()) { + // Jar spec requires a MANIFEST.MF not a manifest.mf + copyFile(manifest, new File(metaInfDir, "MANIFEST.MF"), FILTER_ON); // XXXFileLiteral + } else { + errors.add("have manifest, but unable to create " + metaInfDir); + return false; + } + } + + zipfileset = new ZipFileSet(); + zipfileset.setProject(project); + 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("assemble " + 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(result.getOutputFile()); + } catch (BuildException e) { + errors.add("BuildException zipping " + module + ": " + e.getMessage()); + return false; + } finally { + result.clearOutOfDate(); + } + } + + /** + * @see org.aspectj.internal.tools.build.Builder#buildAntecedants(Module) + */ + @Override + protected Result[] getAntecedantResults(Result moduleResult) { + Hashtable<String,Target> targets = new Hashtable<String, Target>(); + makeTargetsForResult(moduleResult, targets); + String targetName = resultToTargetName(moduleResult); + // bug: doc says topoSort returns String, but returns Target + Collection<Target> 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 Result[0]; + } + ArrayList<String> toReturn = new ArrayList<String>(); + for (Iterator<Target> iter = result.iterator(); iter.hasNext();) { + Target target = iter.next(); + String name = target.getName(); + if (null == name) { + throw new Error("null name?"); + } else { + toReturn.add(name); + } + } + // topoSort always returns target name + if ((1 == size) && targetName.equals(toReturn.get(0)) && !moduleResult.outOfDate()) { + return new Result[0]; + } + return Result.getResults(toReturn.toArray(new String[0])); + } + + /** + * Generate Module.assembledJar with merge of itself and all antecedants + */ + @Override + protected boolean assembleAll(Result result, Messager handler) { + if (!buildingEnabled) { + return false; + } + if (!result.outOfDate()) { + return true; + } + + Util.iaxIfNull(result, "result"); + Util.iaxIfNull(handler, "handler"); + if (!result.getKind().isAssembly()) { + throw new IllegalStateException("not assembly: " + result); + } + + // ---- zip result up + Zip zip = new Zip(); + setupTask(zip, "zip"); + zip.setDestFile(result.getOutputFile()); + ZipFileSet zipfileset = null; + final Module module = result.getModule(); + List<File> known = result.findJarRequirements(); + removeLibraryFilesToSkip(module, known); + // -- merge any antecedents, less any manifest + for (File jarFile: known) { + zipfileset = new ZipFileSet(); + zipfileset.setProject(project); + zipfileset.setSrc(jarFile); + zipfileset.setIncludes("**/*"); + String name = jarFile.getName(); + name = name.substring(0, name.length() - 4); // ".jar".length() + // required includes self - exclude manifest from others + if (!module.name.equals(name)) { + 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); + } + + try { + handler.log("assembling all " + module + " in " + result.getOutputFile()); + if (verbose) { + handler.log("knownAntecedants: " + known); + } + return executeTask(zip); + } catch (BuildException e) { + handler.logException("BuildException zipping " + module, e); + return false; + } finally { + result.clearOutOfDate(); + } + } + + /** + * @see org.aspectj.internal.tools.ant.taskdefs.Builder#buildInstaller(BuildSpec, String) + */ + @Override + protected boolean buildInstaller(BuildSpec buildSpec, String targDirPath) { + return false; + } + + /** task.execute() and any advice */ + protected boolean executeTask(Task task) { + if (!buildingEnabled) { + return false; + } + task.execute(); + return true; + } + + /** + * Support for compiling basic AspectJ projects. Projects may only compile all (and only) their source directories; aspectpath, + * inpath, etc. are not supported. To load the compiler, this assumes the user has either defined a project property + * "aspectj.home" or that there exists <code>{module-dir}/lib/aspectj/lib/aspectj[tools|rt].jar</code>. + */ + static class AspectJSupport { + static final String AJCTASK = "org.aspectj.tools.ant.taskdefs.AjcTask"; + static final String ASPECTJRT_JAR_VARIABLE = "ASPECTJRT_LIB"; + static final String LIBASPECTJ_RPATH = "/lib/aspectj"; + static final Map nameToAspectjrtjar = new HashMap(); + static final String NONE = "NONE"; + + /** + * If this module should be compiled with AspectJ, return a task to do so. + * + * @param module the Module to compile + * @param javac the Javac compile commands + * @return javac or a Task to compile with AspectJ if needed + */ + static Task wrapIfNeeded(Result result, Javac javac) { + final Project project = javac.getProject(); + Path runtimeJar = null; + final Module module = result.getModule(); + if (runtimeJarOnClasspath(result)) { + // yes aspectjrt.jar on classpath + } 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(Result result) { + for (File file: result.getLibJars()) { + if ("aspectjrt.jar".equals(file.getName())) { + return true; + } + } + return false; + } + + static Path getAspectJLib(Project project, Module module, String name) { + Path result = null; + String[] libDirNames = { "aspectj.home", "ASPECTJ_HOME", LIBASPECTJ_RPATH }; + String[] libDirs = new String[libDirNames.length]; + for (int i = 0; i < libDirNames.length; i++) { + if (LIBASPECTJ_RPATH == libDirNames[i]) { + libDirs[i] = module.getFullPath(LIBASPECTJ_RPATH); + } else { + libDirs[i] = project.getProperty(libDirNames[i]); + } + if (null != libDirs[i]) { + libDirs[i] = Util.path(libDirs[i], "lib"); + result = new Path(project, Util.path(libDirs[i], name)); + String path = result.toString(); + if (new File(path).canRead()) { + return result; + } + } + } + String m = "unable to find " + name + " in " + Arrays.asList(libDirs); + throw new BuildException(m); + } + + /** + * Wrap AspectJ compiler as Task. Only works for javac-like source compilation of everything under srcDir. Written + * reflectively to compile in the build module, which can't depend on the whole tree. + * + * @param javac the Javac specification + * @param toolsJar the Path to the aspectjtools.jar + * @param runtimeJar the Path to the aspectjrt.jar + * @return javac or another Task invoking the AspectJ compiler + */ + @SuppressWarnings("unchecked") + static Task aspectJTask(Javac javac, Path toolsJar, Path runtimeJar) { + Object task = null; + String url = null; + try { + url = "file:" + toolsJar.toString().replace('\\', '/'); + URL[] cp = new URL[] { new URL(url) }; + ClassLoader parent = Task.class.getClassLoader(); + ClassLoader loader = new URLClassLoader(cp, parent); + Class c = loader.loadClass(AJCTASK); + task = c.newInstance(); + // Westodo Project project = javac.getProject(); + Method m = c.getMethod("setupAjc", new Class[] { Javac.class }); + m.invoke(task, new Object[] { javac }); + m = c.getMethod("setFork", new Class[] { boolean.class }); + m.invoke(task, new Object[] { Boolean.TRUE }); + m = c.getMethod("setForkclasspath", new Class[] { Path.class }); + m.invoke(task, new Object[] { toolsJar }); + m = c.getMethod("setSourceRoots", new Class[] { Path.class }); + m.invoke(task, new Object[] { javac.getSrcdir() }); + if (null != runtimeJar) { + m = c.getMethod("setClasspath", new Class[] { Path.class }); + m.invoke(task, new Object[] { runtimeJar }); + } + } catch (BuildException e) { + throw e; + } catch (Throwable t) { + StringBuffer sb = new StringBuffer(); + sb.append("classpath="); + sb.append(url); + throw new BuildException(sb.toString(), t); + } + return (Task) task; + } + + private AspectJSupport() { + throw new Error("no instances"); + } + } +} + +// finally caught by failing to comply with proper ant initialization +// /** +// * Build a module that has a build script. +// * @param buildSpec the module to build +// * @param buildScript the script file +// * @throws BuildException if build fails +// */ +// private void buildByScript(BuildSpec buildSpec, File buildScript) +// throws BuildException { +// Ant ant = new Ant(); +// ant.setProject(getProject()); +// ant.setAntfile(buildScript.getAbsolutePath()); +// ant.setDescription("building module " + buildSpec.module); +// ant.setDir(buildScript.getParentFile()); +// ant.setInheritAll(true); +// ant.setInheritRefs(false); +// ant.setLocation(getLocation()); +// ant.setOwningTarget(getOwningTarget()); +// // by convention, for build.xml, use module name to publish +// ant.setTarget(buildSpec.module); +// ant.setTaskName("ant"); +// loadAntProperties(ant, buildSpec); +// ant.execute(); +// } +// +// /** override definitions */ +// private void loadAntProperties(Ant ant, BuildSpec buildSpec) { +// Property property = ant.createProperty(); +// property.setName(BuildSpec.baseDir_NAME); +// property.setFile(buildSpec.baseDir); +// property = ant.createProperty(); +// property.setName(buildSpec.distDir_NAME); +// property.setFile(buildSpec.distDir); +// property = ant.createProperty(); +// property.setName(BuildSpec.tempDir_NAME); +// property.setFile(buildSpec.tempDir); +// property = ant.createProperty(); +// property.setName(BuildSpec.jarDir_NAME); +// property.setFile(buildSpec.jarDir); +// property = ant.createProperty(); +// property.setName(BuildSpec.stagingDir_NAME); +// property.setFile(buildSpec.stagingDir); +// } + +/** + * Segregate product-building API's from module-building APIs for clarity. These are called by the superclass if the BuildSpec + * warrants. XXX extremely brittle/arbitrary assumptions. + * + * @see BuildModule for assumptions + */ +class ProductBuilder extends AntBuilder { + + private static String getProductInstallResourcesSrc(BuildSpec buildSpec) { + final String resourcesName = "installer-resources"; // XXXFileLiteral + File dir = buildSpec.productDir.getParentFile(); + if (null == dir) { + return Util.path(new String[] { "..", "..", resourcesName }); + } + dir = dir.getParentFile(); + if (null == dir) { + return Util.path("..", resourcesName); + } else { + dir = new File(dir, resourcesName); + return dir.getPath(); + } + } + + private static String getProductInstallerFileName(BuildSpec buildSpec) { // XXXFileLiteral + return "aspectj-" + buildSpec.productDir.getName() + "-" + Util.shortVersion(buildSpec.version) + ".jar"; + } + + /** + * Calculate name of main, typically InitialCap, and hence installer class. + * + * @return $$installer$$.org.aspectj." + ProductName + "Installer" + */ + + private static String getProductInstallerMainClass(BuildSpec buildSpec) { + String productName = buildSpec.productDir.getName(); + String initial = productName.substring(0, 1).toUpperCase(); + productName = initial + productName.substring(1); + return "$installer$.org.aspectj." + productName + "Installer"; // XXXNameLiteral + } + + /** @see Builder.getBuilder(String, Project, File) */ + ProductBuilder(Project project, File tempDir, boolean useEclipseCompiles, Messager handler) { + super(project, tempDir, useEclipseCompiles, handler); + } + + /** + * Delegate for super.buildProduct(..) template method. + */ + @Override + 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); + } + copy.addFileset(fileset); + return executeTask(copy); + } + + /** + * Delegate for super.buildProduct(..) template method. + */ + @Override + protected boolean copyNonBinaries(BuildSpec buildSpec, File distDir, File targDir) { + // filter-copy everything but the binaries + Copy copy = makeCopyTask(true); + copy.setTodir(targDir); + Util.iaxIfNotCanReadDir(distDir, "product dist directory"); + FileSet fileset = new FileSet(); + fileset.setDir(distDir); + fileset.setExcludes(Builder.BINARY_SOURCE_PATTERN); + copy.addFileset(fileset); + return executeTask(copy); + } + + @Override + protected boolean buildInstaller(BuildSpec buildSpec, String targDirPath) { + if (buildSpec.verbose) { + handler.log("creating installer for " + buildSpec); + } + AJInstaller installer = new AJInstaller(); + setupTask(installer, "installer"); + installer.setBasedir(targDirPath); + // installer.setCompress(); + File installSrcDir = new File(buildSpec.productDir, "install"); // XXXFileLiteral + Util.iaxIfNotCanReadDir(installSrcDir, "installSrcDir"); + installer.setHtmlSrc(installSrcDir.getPath()); + String resourcePath = getProductInstallResourcesSrc(buildSpec); + File resourceSrcDir = new File(resourcePath); + Util.iaxIfNotCanReadDir(resourceSrcDir, "resourceSrcDir"); + installer.setResourcesSrc(resourcePath); + String name = getProductInstallerFileName(buildSpec); + File outFile = new File(buildSpec.jarDir, name); + installer.setZipfile(outFile.getPath()); + installer.setMainclass(getProductInstallerMainClass(buildSpec)); + installer.setInstallerclassjar(getBuildJar(buildSpec)); + return executeTask(installer); + + // -- test installer XXX + // create text setup file + // run installer with setup file + // cleanup installed product + } + + private String getBuildJar(BuildSpec buildSpec) { + return buildSpec.baseDir.getPath() + "/lib/build/build.jar"; // XXX + } + + // private Module moduleForReplaceFile(File replaceFile, Modules modules) { + // String jarName = moduleAliasFor(replaceFile.getName().toLowerCase()); + // if (jarName.endsWith(".jar") || jarName.endsWith(".zip")) { // XXXFileLiteral + // jarName = jarName.substring(0, jarName.length()-4); + // } else { + // throw new IllegalArgumentException("can only replace .[jar|zip]"); + // } + // boolean assembleAll = jarName.endsWith("-all"); + // String name = (!assembleAll ? jarName : jarName.substring(0, jarName.length()-4)); + // return modules.getModule(name); + // } + // +} + +class ProjectMessager extends Messager { + private final Project project; + + public ProjectMessager(Project project) { + Util.iaxIfNull(project, "project"); + this.project = project; + } + + @Override + public boolean log(String s) { + project.log(s); + return true; + } + + @Override + public boolean error(String s) { + project.log(s, Project.MSG_ERR); + return true; + } + + @Override + public boolean logException(String context, Throwable thrown) { + project.log(context + Util.renderException(thrown), Project.MSG_ERR); + return true; + } + +}
\ No newline at end of file diff --git a/build/src/main/java./aspectj/internal/tools/ant/taskdefs/BuildModule.java b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/BuildModule.java new file mode 100644 index 000000000..6ac6b5ba6 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/BuildModule.java @@ -0,0 +1,165 @@ +/* ******************************************************************* + * 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 Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.ant.taskdefs; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Location; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.Path; +import org.aspectj.internal.tools.build.BuildSpec; +import org.aspectj.internal.tools.build.Builder; + +/** + * Ant interface to build a product or module, including any required modules. + * @see Builder + */ +public class BuildModule extends Task { // quickie hack... + + public static void main(String[] args) { + TestBuildModule.main(args); + } + + private static File pathToFile(Path path) { + if (null != path) { + String[] list = path.list(); + if ((null == list) || (1 != list.length)) { + throw new IllegalArgumentException("expected exactly 1 element"); + } + return new File(list[0]); + } + return null; + } + BuildSpec buildSpec; + + public BuildModule() { + buildSpec = new BuildSpec(); + setTaskName("ajbuild"); + } + + public void setModuledir(Path moduleDir) { + buildSpec.moduleDir = pathToFile(moduleDir); + } + + public void setModule(String module) { // XXX handle multiple modules, same builder + buildSpec.module = module; + } + + public void setVersion(String version) { + buildSpec.version = version; + } + public void setBasedir(Path baseDir) { + buildSpec.baseDir = pathToFile(baseDir); + } + + public void setJardir(Path jarDir) { + buildSpec.jarDir = pathToFile(jarDir); + } + + public void setTrimtesting(boolean trimTesting) { + buildSpec.trimTesting = trimTesting; + } + + public void setAssembleall(boolean assembleAll) { + buildSpec.assembleAll = assembleAll; + } + + public void setRebuild(boolean rebuild) { + buildSpec.rebuild = rebuild; + } + + public void setFailonerror(boolean failonerror) { + buildSpec.failonerror = failonerror; + } + + public void setCreateinstaller(boolean create) { + buildSpec.createInstaller = create; + } + + public void setVerbose(boolean verbose) { + buildSpec.verbose = verbose; + } + + public void setBuildConfig(String buildConfig) { + buildSpec.buildConfig = buildConfig; + } + + // --------------------------------------------------------- product build + + public void setProductdir(Path productDir) { + buildSpec.productDir = pathToFile(productDir); + } + + public void setTempdir(Path tempDir) { + buildSpec.tempDir = pathToFile(tempDir); + } + + public void setDistdir(Path distdir) { + buildSpec.distDir = pathToFile(distdir); + } + + public void execute() throws BuildException { + final BuildSpec buildSpec = this.buildSpec; + this.buildSpec = new BuildSpec(); + build(buildSpec); + } + + private void build(BuildSpec buildSpec) throws BuildException { + final boolean failonerror = buildSpec.failonerror; + Builder builder = null; + try { + // try using script first if not a product + boolean built = false; + if ((null == buildSpec.productDir) && (null != buildSpec.moduleDir)) { + File buildScript = new File(buildSpec.moduleDir, "build.xml"); // XXXFileLiteral + if (buildScript.canRead()) { + built = buildByScript(buildSpec, buildScript); + if (!built) { + log("unable to build " + + buildSpec + + " using script: " + + buildScript.getAbsolutePath()); + } + } + } + if (!built) { + builder = AntBuilder.getBuilder( + buildSpec.buildConfig, + getProject(), + buildSpec.tempDir); + if (!builder.build(buildSpec) && failonerror) { + Location loc = getLocation(); + throw new BuildException("error building " + buildSpec, loc); + } + } + } catch (BuildException e) { + throw e; + } catch (Throwable t) { + Location loc = getLocation(); + throw new BuildException("error building " + buildSpec, t, loc); + } finally { + if (null != builder) { + builder.cleanup(); + } + } + } + + boolean buildByScript(BuildSpec buildSpec, File buildScript) + throws BuildException { + return false; + } +} +
\ No newline at end of file diff --git a/build/src/main/java./aspectj/internal/tools/ant/taskdefs/Checklics.java b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/Checklics.java new file mode 100644 index 000000000..904ba4656 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/Checklics.java @@ -0,0 +1,676 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * 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://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Reference; + +/** + * Check that included .java files contain license and copyright strings for MPL 1.0 (default), Apache, or CPL. Use list="true" to + * get a list of known license variants {license}-{copyrightHolder} todo reimplement with regexp and jdiff FileLine utilities + */ +public class Checklics extends MatchingTask { + /* + * This does not enforce that copyrights are correct/current, only that they exist. E.g., the default behavior requires MPL but + * permits either Xerox or PARC copyright holders and any valid year. + */ + public static final String MPL_TAG = "mpl"; + public static final String APACHE_TAG = "apache"; + public static final String CPL_IBM_PARC_TAG = "cpl-ibm|parc"; + public static final String CPL_IBM_TAG = "cpl-ibm"; + public static final String MPL_XEROX_PARC_TAG = "mpl-parc|xerox"; + public static final String MPL_ONLY_TAG = "mpl-only"; + public static final String MPL_PARC_TAG = "mpl-parc"; + public static final String PARC_COPYRIGHT_TAG = "parc-copy"; + public static final String CPL_IBM_PARC_XEROX_TAG = "cpl-ibm|parc|xerox"; + public static final String CPL_IBM_PARC_XEROX_OTHERS_TAG = "cpl-ibm|parc|xerox|others"; + public static final String EPL_CPL_IBM_PARC_XEROX_OTHERS_TAG = "epl-cpl-ibm|parc|xerox|vmware|others"; + public static final String DEFAULT = EPL_CPL_IBM_PARC_XEROX_OTHERS_TAG; + + static final Map<String,License> LICENSES; // unmodifiable Map + + static { + final String CONTRIBUTORS = "Contributors"; + final String XEROX = "Xerox"; + final String PARC = "Palo Alto Research Center"; + final String APACHE = "The Apache Software Foundation"; + final String IBM = "IBM"; + final String VMWARE = "VMware"; + final String IBM_LONG = "International Business Machines"; + final String LIC_APL = "Apache Software Foundation (http://www.apache.org/)"; + final String LIC_MPL = "http://aspectj.org/MPL/"; + final String LIC_CPL = "Eclipse Public License"; + final String LIC_ECPL = " Public License"; + License APL = new License(APACHE_TAG, LIC_APL, APACHE); + License MPL = new License(MPL_TAG, LIC_MPL, XEROX); + License MPL_XEROX_PARC = new License(DEFAULT, LIC_MPL, XEROX, PARC); + License CPL_IBM_PARC = new License(CPL_IBM_PARC_TAG, LIC_CPL, new String[] { IBM_LONG, IBM, PARC }); + License CPL_IBM_PARC_XEROX = new License(CPL_IBM_PARC_XEROX_TAG, LIC_CPL, new String[] { IBM_LONG, IBM, PARC, XEROX }); + + License CPL_IBM_PARC_XEROX_OTHERS = new License(CPL_IBM_PARC_XEROX_OTHERS_TAG, LIC_CPL, new String[] { IBM_LONG, IBM, PARC, + XEROX, CONTRIBUTORS }); + License EPL_CPL_IBM_PARC_XEROX_OTHERS = new License(EPL_CPL_IBM_PARC_XEROX_OTHERS_TAG, LIC_ECPL, new String[] { IBM_LONG, + IBM, PARC, XEROX, VMWARE, CONTRIBUTORS }); + License CPL_IBM = new License(CPL_IBM_TAG, LIC_CPL, IBM, IBM_LONG); + License MPL_ONLY = new License(MPL_ONLY_TAG, LIC_MPL); + License MPL_PARC = new License(MPL_PARC_TAG, LIC_MPL, PARC); + License PARC_COPYRIGHT = new License(PARC_COPYRIGHT_TAG, null, PARC); + LICENSES = new Hashtable<String,License>(); + LICENSES.put(APL.tag, APL); + LICENSES.put(MPL.tag, MPL); + LICENSES.put(MPL_PARC.tag, MPL_PARC); + LICENSES.put(MPL_XEROX_PARC.tag, MPL_XEROX_PARC); + LICENSES.put(CPL_IBM_PARC.tag, CPL_IBM_PARC); + LICENSES.put(MPL_ONLY.tag, MPL_ONLY); + LICENSES.put(CPL_IBM.tag, CPL_IBM); + LICENSES.put(PARC_COPYRIGHT.tag, PARC_COPYRIGHT); + LICENSES.put(CPL_IBM_PARC_XEROX.tag, CPL_IBM_PARC_XEROX); + LICENSES.put(CPL_IBM_PARC_XEROX_OTHERS.tag, CPL_IBM_PARC_XEROX_OTHERS); + LICENSES.put(EPL_CPL_IBM_PARC_XEROX_OTHERS.tag, EPL_CPL_IBM_PARC_XEROX_OTHERS); + } + + /** @param args String[] { < sourcepath > {, < licenseTag > } } */ + public static void main(String[] args) { + switch (args.length) { + case 1: + runDirect(args[0], null, false); + break; + case 2: + runDirect(args[0], args[1], false); + break; + default: + String options = "{replace-headers|get-years|list|{licenseTag}}"; + System.err.println("java {me} sourcepath " + options); + break; + } + } + + /** + * Run the license check directly + * + * @param sourcepaths String[] of paths to source directories + * @param license the String tag for the license, if any + * @param failonerror boolean flag to pass to Checklics + * @throws IllegalArgumentException if sourcepaths is empty + * @return total number of failed licenses + */ + public static int runDirect(String sourcepath, String license, boolean failonerror) { + if ((null == sourcepath) || (1 > sourcepath.length())) { + throw new IllegalArgumentException("bad sourcepath: " + sourcepath); + } + Checklics me = new Checklics(); + Project p = new Project(); + p.setName("direct interface to Checklics"); + p.setBasedir("."); + me.setProject(p); + me.setFailOnError(failonerror); + me.setSourcepath(new Path(p, sourcepath)); + if (null != license) { + if ("replace-headers".equals(license)) { + me.setReplaceheaders(true); + } else if ("get-years".equals(license)) { + me.setGetYears(true); + } else if ("list".equals(license)) { + me.setList(true); + } else { + me.setLicense(license); + } + } + me.execute(); + return me.failed; + } + + private Path sourcepath; + private License license; + private boolean list; + private String streamTag; + private boolean failOnError; + private boolean getYears; + private boolean replaceHeaders; + private int failed; + private int passed; + + private boolean printDirectories; + + /** @param list if true, don't run but list known license tags */ + public void setList(boolean list) { + this.list = list; + } + + public void setPrintDirectories(boolean print) { + printDirectories = print; + } + + /** + * When failOnError is true, if any file failed, throw BuildException listing number of files that file failed to pass license + * check + * + * @param fail if true, report errors by throwing BuildException + */ + public void setFailOnError(boolean fail) { + this.failOnError = fail; + } + + /** @param tl mpl | apache | cpl */ + public void setLicense(String tl) { + License input = LICENSES.get(tl); + if (null == input) { + throw new BuildException("no license known for " + tl); + } + license = input; + } + + public void setSourcepath(Path path) { + if (sourcepath == null) { + sourcepath = path; + } else { + sourcepath.append(path); + } + } + + public Path createSourcepath() { + return sourcepath == null ? (sourcepath = new Path(project)) : sourcepath.createPath(); + } + + public void setSourcepathRef(Reference id) { + createSourcepath().setRefid(id); + } + + /** @param out "out" or "err" */ + public void setOutputStream(String out) { + this.streamTag = out; + } + + public void setReplaceheaders(boolean replaceHeaders) { + this.replaceHeaders = replaceHeaders; + } + + public void setGetYears(boolean getYears) { + this.getYears = getYears; + } + + /** list known licenses or check source tree */ + @Override + public void execute() throws BuildException { + if (list) { + list(); + } else if (replaceHeaders) { + replaceHeaders(); + } else if (getYears) { + getYears(); + } else { + checkLicenses(); + } + } + + private PrintStream getOut() { + return ("err".equals(streamTag) ? System.err : System.out); + } + + interface FileVisitor { + void visit(File file); + } + + /** visit all .java files in all directories... */ + private void visitAll(FileVisitor visitor) { + // List filelist = new ArrayList(); + String[] dirs = sourcepath.list(); + for (int i = 0; i < dirs.length; i++) { + File dir = project.resolveFile(dirs[i]); + String[] files = getDirectoryScanner(dir).getIncludedFiles(); + for (int j = 0; j < files.length; j++) { + File file = new File(dir, files[j]); + String path = file.getPath(); + if (path.endsWith(".java")) { + visitor.visit(file); + } + } + } + } + + private void replaceHeaders() { + class YearVisitor implements FileVisitor { + @Override + public void visit(File file) { + HeaderInfo info = Header.checkFile(file); + if (!Header.replaceHeader(file, info)) { + throw new BuildException("failed to replace header for " + file + " using " + info); + } + } + } + visitAll(new YearVisitor()); + } + + private void getYears() { + final PrintStream out = getOut(); + class YearVisitor implements FileVisitor { + @Override + public void visit(File file) { + HeaderInfo info = Header.checkFile(file); + out.println(info.toString()); + } + } + visitAll(new YearVisitor()); + } + + private void checkLicenses() throws BuildException { + if (null == license) { + setLicense(DEFAULT); + } + final License license = this.license; // being paranoid... + if (null == license) { + throw new BuildException("no license"); + } + final PrintStream out = getOut(); + + class Visitor implements FileVisitor { + int failed = 0; + int passed = 0; + + @Override + public void visit(File file) { + if (license.checkFile(file)) { + passed++; + } else { + failed++; + String path = file.getPath(); + if (!license.foundLicense()) { + out.println(license.tag + " LICENSE FAIL: " + path); + } + if (!license.foundCopyright()) { + out.println(license.tag + " COPYRIGHT FAIL: " + path); + } + } + } + } + Visitor visitor = new Visitor(); + visitAll(visitor); + this.failed = visitor.failed; + this.passed = visitor.passed; + if (0 < visitor.failed) { + getOut().println("Total passed: " + visitor.passed + (visitor.failed == 0 ? "" : " failed: " + visitor.failed)); + if (failOnError) { + throw new BuildException(failed + " files failed license check"); + } + } + } + + private void list() { + Iterator enu = LICENSES.keySet().iterator(); + StringBuffer sb = new StringBuffer(); + sb.append("known license keys:"); + boolean first = true; + while (enu.hasNext()) { + sb.append((first ? " " : ", ") + enu.next()); + if (first) { + first = false; + } + } + getOut().println(sb.toString()); + } + + /** + * Encapsulate license and copyright specifications to check files use hokey string matching. + */ + public static class License { + /** acceptable years for copyright prefix to company - append " " */ + static final String[] YEARS = // remove older after license xfer? + new String[] { "2002 ", "2003 ", "2004 ", "2005", "2006", "2007", "2008", + "2009", "2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2001 ", "2000 ", + "1999 " }; + public final String tag; + public final String license; + private final String[] copyright; + private boolean gotLicense; + private boolean gotCopyright; + + License(String tag, String license) { + this(tag, license, (String[]) null); + } + + License(String tag, String license, String copyright) { + this(tag, license, new String[] { copyright }); + } + + License(String tag, String license, String copyright, String altCopyright) { + this(tag, license, new String[] { copyright, altCopyright }); + } + + License(String tag, String license, String[] copyright) { + this.tag = tag; + if ((null == tag) || (0 == tag.length())) { + throw new IllegalArgumentException("null tag"); + } + this.license = license; + this.copyright = copyright; + } + + public final boolean gotValidFile() { + return foundLicense() && foundCopyright(); + } + + /** @return true if no license sought or if some license found */ + public final boolean foundLicense() { + return ((null == license) || gotLicense); + } + + /** @return true if no copyright sought or if some copyright found */ + public final boolean foundCopyright() { + return ((null == copyright) || gotCopyright); + } + + public boolean checkFile(final File file) { + clear(); + // boolean result = false; + BufferedReader input = null; + int lineNum = 0; + try { + input = new BufferedReader(new FileReader(file)); + String line; + while (!gotValidFile() && (line = input.readLine()) != null) { + lineNum++; + checkLine(line); + } + } catch (IOException e) { + System.err.println("reading line " + lineNum + " of " + file); + e.printStackTrace(System.err); + } finally { + if (null != input) { + try { + input.close(); + } catch (IOException e) { + } // ignore + } + } + return gotValidFile(); + } + + @Override + public String toString() { + return tag; + } + + private void checkLine(String line) { + if ((null == line) || (0 == line.length())) { + return; + } + if (!gotLicense && (null != license) && (-1 != line.indexOf(license))) { + gotLicense = true; + } + if (!gotCopyright && (null != copyright)) { + int loc; + for (int j = 0; !gotCopyright && (j < YEARS.length); j++) { + if (-1 != (loc = line.indexOf(YEARS[j]))) { + loc += YEARS[j].length(); + String afterLoc = line.substring(loc).trim(); + for (int i = 0; !gotCopyright && (i < copyright.length); i++) { + if (0 == afterLoc.indexOf(copyright[i])) { + gotCopyright = true; + } + } + } + } + } + } + + private void clear() { + if (gotLicense) { + gotLicense = false; + } + if (gotCopyright) { + gotCopyright = false; + } + } + } // class License +} + +class HeaderInfo { + /** File for which this is the info */ + public final File file; + + /** unmodifiable List of String years */ + public final List years; + + /** last line of license */ + public final int lastLine; + + /** last line of license */ + public final boolean hasLicense; + + public HeaderInfo(File file, int lastLine, List<String> years, boolean hasLicense) { + this.lastLine = lastLine; + this.file = file; + this.hasLicense = hasLicense; + List<String> newYears = new ArrayList<String>(); + newYears.addAll(years); + Collections.sort(newYears); + this.years = Collections.unmodifiableList(newYears); + if ((null == file) || !file.canWrite()) { + throw new IllegalArgumentException("bad file: " + this); + } + if (!hasLicense) { + if ((0 > lastLine) || (65 < lastLine)) { + throw new IllegalArgumentException("bad last line: " + this); + } + } else { + if ((null == years) || (1 > years.size())) { + throw new IllegalArgumentException("no years: " + this); + } + if ((20 > lastLine) || (65 < lastLine)) { + throw new IllegalArgumentException("bad last line: " + this); + } + } + } + + @Override + public String toString() { + return file.getPath() + ":" + lastLine + " " + years; + } + + public void writeHeader(PrintWriter writer) { + if (!hasLicense) { + writer.println(TOP); + writer.println(PARC_ONLY); + writeRest(writer); + } else { + final int size = years.size(); + if (1 > size) { + throw new Error("no years found in " + toString()); + } + String first = (String) years.get(0); + String last = (String) years.get(size - 1); + boolean lastIs2002 = "2002".equals(last); + String xlast = last; + if (lastIs2002) { // 2002 was PARC + xlast = (String) (size > 1 ? years.get(size - 2) : null); + // 1999-2002 Xerox implies 1999-2001 Xerox + if (first.equals(xlast) && !"2001".equals(xlast)) { + xlast = "2001"; + } + } + String xyears = first + "-" + xlast; + if (first.equals(last)) { + xyears = first; + } + + writer.println(TOP); + if (!lastIs2002) { // Xerox only + writer.println(XEROX_PREFIX + xyears + XEROX_SUFFIX + ". "); + } else if (size == 1) { // PARC only + writer.println(PARC_ONLY); + } else { // XEROX plus PARC + writer.println(XEROX_PREFIX + xyears + XEROX_SUFFIX + ", "); + writer.println(PARC); + } + writeRest(writer); + } + } + + void writeRest(PrintWriter writer) { + writer.println(" * All rights reserved. "); + writer.println(" * This program and the accompanying materials are made available "); + writer.println(" * under the terms of the Eclipse Public License v1.0 "); + writer.println(" * which accompanies this distribution and is available at "); + writer.println(" * http://www.eclipse.org/legal/epl-v10.html "); + writer.println(" * "); + writer.println(" * Contributors: "); + writer.println(" * Xerox/PARC initial implementation "); + writer.println(" * ******************************************************************/"); + writer.println(""); + } + + public static final String TOP = "/* *******************************************************************"; + public static final String PARC = " * 2002 Palo Alto Research Center, Incorporated (PARC)."; + public static final String PARC_ONLY = " * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC)."; + public static final String XEROX_PREFIX = " * Copyright (c) "; + public static final String XEROX_SUFFIX = " Xerox Corporation"; + /* + * /* ******************************************************************* Copyright (c) 1998-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 Eclipse Public License v1.0 which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: Xerox/PARC initial implementation ****************************************************************** + */ +} + +/** + * header search/replace using hokey string matching + */ +class Header { + + /** replace the header in file */ + public static boolean replaceHeader(File file, HeaderInfo info) { + // ArrayList years = new ArrayList(); + // int endLine = 0; + BufferedReader input = null; + PrintWriter output = null; + FileWriter outWriter = null; + int lineNum = 0; + boolean result = false; + final File inFile = new File(file.getPath() + ".tmp"); + try { + File outFile = new File(file.getPath()); + if (!file.renameTo(inFile) || !inFile.canRead()) { + throw new Error("unable to rename " + file + " to " + inFile); + } + outWriter = new FileWriter(outFile); + input = new BufferedReader(new FileReader(inFile)); + output = new PrintWriter(outWriter, true); + info.writeHeader(output); + String line; + while (null != (line = input.readLine())) { + lineNum++; + if (lineNum > info.lastLine) { + output.println(line); + } + } + } catch (IOException e) { + System.err.println("writing line " + lineNum + " of " + file); + e.printStackTrace(System.err); + result = false; + } finally { + if (null != input) { + try { + input.close(); + } catch (IOException e) { + result = false; + } + } + if (null != outWriter) { + try { + outWriter.close(); + } catch (IOException e) { + result = false; + } + } + result = inFile.delete(); + } + return result; + } + + public static HeaderInfo checkFile(final File file) { + ArrayList<String> years = new ArrayList<String>(); + int endLine = 0; + BufferedReader input = null; + int lineNum = 0; + try { + input = new BufferedReader(new FileReader(file)); + String line; + while (null != (line = input.readLine())) { + lineNum++; + String ll = line.trim(); + if (ll.startsWith("package ") || ll.startsWith("import ")) { + break; // ignore default package w/o imports + } + if (checkLine(line, years)) { + endLine = lineNum; + break; + } + } + } catch (IOException e) { + System.err.println("reading line " + lineNum + " of " + file); + e.printStackTrace(System.err); + } finally { + if (null != input) { + try { + input.close(); + } catch (IOException e) { + } // ignore + } + } + return new HeaderInfo(file, endLine, years, endLine > 0); + } + + /** + * Add any years found (as String) to years, and return true at the first end-of-comment + * + * @return true if this line has end-of-comment + */ + private static boolean checkLine(String line, ArrayList<String> years) { + if ((null == line) || (0 == line.length())) { + return false; + } + int loc; + int start = 0; + + while ((-1 != (loc = line.indexOf("199", start)) || (-1 != (loc = line.indexOf("200", start))))) { + char c = line.charAt(loc + 3); + if ((c <= '9') && (c >= '0')) { + years.add(line.substring(loc, loc + 4)); + } + start = loc + 4; + } + + return (-1 != line.indexOf("*/")); + } + +} // class Header + diff --git a/build/src/main/java./aspectj/internal/tools/ant/taskdefs/ConditionalTask.java b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/ConditionalTask.java new file mode 100644 index 000000000..fdff0d7c1 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/ConditionalTask.java @@ -0,0 +1,187 @@ +/* ******************************************************************* + * 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 Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; +import java.util.Vector; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; + +public abstract class ConditionalTask extends Task { + + public final static String TRUE = "true"; + + private List<If> ifs; + protected List<If> ifs() { + return ifs != null ? ifs : (ifs = new Vector<If>()); + } + + public If createIf() { + If i = new If(); + ifs().add(i); + return i; + } + + public If createIf(String name, String equals, boolean strict) { + If i = createIf(); + i.setName(name); + i.setEquals(equals); + i.setStrict(strict); + return i; + } + + public If createIf(String name, String equals) { + return createIf(name, equals, false); + } + + public If createIf(String name) { + return createIf(name, TRUE, false); + } + + public If createIf(String name, boolean strict) { + return createIf(name, TRUE, strict); + } + + public void setIfs(String ifs) { + StringTokenizer tok = new StringTokenizer(ifs, ",;: ", false); + while (tok.hasMoreTokens()) { + String next = tok.nextToken(); + int iequals = next.lastIndexOf("="); + String equals; + String name; + boolean strict; + If i = createIf(); + if (iequals != -1) { + name = next.substring(0, iequals); + equals = next.substring(iequals + 1); + strict = true; + } else { + name = next.substring(0); + equals = TRUE; + strict = false; + } + i.setName(name); + i.setEquals(equals); + i.setStrict(strict); + } + } + + public void setIf(String ifStr) { + setIfs(ifStr); + } + + public class If { + public If() { + this(null, null); + } + public If(String name) { + this(name, TRUE); + } + public If(String name, String equals) { + setName(name); + setEquals(equals); + } + private String name; + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + private String equals; + public void setEquals(String equals) { + this.equals = equals; + } + public String getEquals() { + return equals; + } + private boolean strict = false; + public void setStrict(boolean strict) { + this.strict = strict; + } + public boolean isStrict() { + return strict; + } + public boolean isOk(String prop) { + return isOk(prop, isStrict()); + } + //XXX Need a better boolean parser + public boolean isOk(String prop, boolean isStrict) { + if (isStrict) { + return prop != null && prop.equals(getEquals()); + } else { + if (isOk(prop, true)) { + return true; + } + if (prop == null || isFalse(getEquals())) { + return true; + } + if ( (isTrue(getEquals()) && isTrue(prop)) || + (isFalse(getEquals()) && isFalse(prop)) ) { + return true; + } + return false; + } + } + private boolean isFalse(String prop) { + return isOneOf(prop, falses) || isOneOf(prop, complement(trues)); + } + private boolean isTrue(String prop) { + return isOneOf(prop, trues) || isOneOf(prop, complement(falses)); + } + private boolean isOneOf(String prop, String[] strings) { + for (int i = 0; i < strings.length; i++) { + if (strings[i].equals(prop)) { + return true; + } + } + return false; + } + private String[] complement(String[] strings) { + for (int i = 0; i < strings.length; i++) { + strings[i] = "!" + strings[i]; + } + return strings; + } + } + + final static String[] falses = { "false", "no" }; + final static String[] trues = { "true", "yes" }; + + protected boolean checkIfs() { + return getFalses().size() == 0; + } + + protected List<String> getFalses() { + Iterator<If> iter = ifs().iterator(); + List<String> result = new Vector<String>(); + while (iter.hasNext()) { + If next = (If) iter.next(); + String name = next.getName(); + String prop = project.getProperty(name); + if (prop == null) { + prop = project.getUserProperty(name); + } + if (!next.isOk(prop)) { + result.add(name); + } + } + return result; + } + + public abstract void execute() throws BuildException; +} diff --git a/build/src/main/java./aspectj/internal/tools/ant/taskdefs/CopyAndInlineStylesheet.java b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/CopyAndInlineStylesheet.java new file mode 100644 index 000000000..664616c07 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/CopyAndInlineStylesheet.java @@ -0,0 +1,120 @@ +/* ******************************************************************* + * 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 Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintStream; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.Mkdir; + +public class CopyAndInlineStylesheet extends Task { + + private File file; + public void setFile(String file) { + this.file = project.resolveFile(file); + } + + private File todir; + public void setTodir(String todir) { + this.todir = project.resolveFile(todir); + } + + + public void execute() throws BuildException { + try { + if (todir == null) { + throw new BuildException("must set 'todir' attribute"); + } + if (file == null) { + throw new BuildException("must set 'file' attribute"); + } + log("copying html from" + file + " to " + todir.getAbsolutePath()); + + File toFile = new File(todir, file.getName()); + + Mkdir mkdir = (Mkdir) project.createTask("mkdir"); + mkdir.setDir(todir); + mkdir.execute(); + + BufferedReader in = new BufferedReader(new FileReader(file)); + PrintStream out = new PrintStream(new FileOutputStream(toFile)); + + outer: + while (true) { + String line = in.readLine(); + if (line == null) break; + if (isStyleSheet(line)) { + doStyleSheet(line, out, file); + while (true) { + String line2 = in.readLine(); + if (line2 == null) break outer; + out.println(line2); + } + } else { + out.println(line); + } + } + + in.close(); + out.close(); + } catch (IOException e) { + throw new BuildException(e.getMessage()); + } + } + + private static void doStyleSheet(String line, PrintStream out, File file) throws IOException { + int srcIndex = line.indexOf("href"); + int startQuotIndex = line.indexOf('"', srcIndex); + int endQuotIndex = line.indexOf('"', startQuotIndex + 1); + + String stylesheetLocation = line.substring(startQuotIndex + 1, endQuotIndex); + + File styleSheetFile = new File(file.getParent(), stylesheetLocation); + + out.println("<style type=\"text/css\">"); + out.println("<!--"); + + BufferedReader inStyle = new BufferedReader(new FileReader(styleSheetFile)); + + while (true) { + String line2 = inStyle.readLine(); + if (line2 == null) break; + out.println(line2); + } + inStyle.close(); + + out.println("-->"); + out.println("</style>"); + } + + + private static boolean isStyleSheet(String line) throws IOException { + line = line.toLowerCase(); + int len = line.length(); + int i = 0; + + while (true) { + if (i == len) return false; + if (! Character.isWhitespace(line.charAt(i))) break; + } + + return line.startsWith("<link", i); + } +} diff --git a/build/src/main/java./aspectj/internal/tools/ant/taskdefs/StripNonBodyHtml.java b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/StripNonBodyHtml.java new file mode 100644 index 000000000..a33505d95 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/StripNonBodyHtml.java @@ -0,0 +1,244 @@ +/* ******************************************************************* + * 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 Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.taskdefs.MatchingTask; + +/** + * Task to convert html source files into files with only body content. + * + * <p> This task can take the following arguments:</p> + * + * <ul> + * <li>srcdir</li> + * <li>destdir</li> + * <li>include</li> + * <li>exclude</li> + * </ul> + * + * <p>Of these arguments, only <b>sourcedir</b> is required.</p> + * + * <p> When this task executes, it will scan the srcdir based on the + * include and exclude properties.</p> + */ + +public class StripNonBodyHtml extends MatchingTask { + + private File srcDir; + private File destDir = null; + + public void setSrcdir(File srcDir) { + this.srcDir = srcDir; + } + + public void setDestdir(File destDir) { + this.destDir = destDir; + } + + public void execute() throws BuildException { + if (srcDir == null) { + throw new BuildException("srcdir attribute must be set!"); + } + if (!srcDir.exists()) { + throw new BuildException("srcdir does not exist!"); + } + if (!srcDir.isDirectory()) { + throw new BuildException("srcdir is not a directory!"); + } + if (destDir != null) { + if (!destDir.exists()) { + throw new BuildException("destdir does not exist!"); + } + if (!destDir.isDirectory()) { + throw new BuildException("destdir is not a directory!"); + } + } + + DirectoryScanner ds = super.getDirectoryScanner(srcDir); + String[] files = ds.getIncludedFiles(); + + log("stripping " + files.length + " files"); + int stripped = 0; + for (int i = 0, len = files.length; i < len; i++) { + if (processFile(files[i])) { + stripped++; + } else { + log(files[i] + " not stripped"); + } + } + log(stripped + " files successfully stripped"); + } + + boolean processFile(String filename) throws BuildException { + File srcFile = new File(srcDir, filename); + File destFile; + if (destDir == null) { + destFile = srcFile; + } else { + destFile = new File(destDir, filename); + destFile.getParentFile().mkdirs(); + } + try { + return strip(srcFile, destFile); + } catch (IOException e) { + throw new BuildException(e); + } + } + + private boolean strip(File f, File g) throws IOException { + BufferedInputStream in = + new BufferedInputStream(new FileInputStream(f)); + String s = readToString(in); + in.close(); + return writeBodyTo(s, g); + } + + private ByteArrayOutputStream temp = new ByteArrayOutputStream(); + private byte[] buf = new byte[2048]; + + private String readToString(InputStream in) throws IOException { + ByteArrayOutputStream temp = this.temp; + byte[] buf = this.buf; + String s = ""; + try { + while (true) { + int i = in.read(buf, 0, 2048); + if (i == -1) break; + temp.write(buf, 0, i); + + } + s = temp.toString(); + } finally { + temp.reset(); + } + return s; + } + + private boolean writeBodyTo(String s, File f) throws IOException { + int start;//, end; + try { + start = findStart(s); + findEnd(s, start); + } catch (ParseException e) { + return false; // if we get confused, just don't write the file. + } + s = processBody(s,f); + BufferedOutputStream out = + new BufferedOutputStream(new FileOutputStream(f)); + + out.write(s.getBytes()); + out.close(); + return true; + } + + /** + * Process body. This implemenation strips text + * between <!-- start strip --> + * and <!-- end strip --> + * inclusive. + */ + private String processBody(String body, File file) { + if (null == body) return body; + final String START = "<!-- start strip -->"; + final String END = "<!-- end strip -->"; + return stripTags(body, file.toString(), START, END); + } + + /** + * Strip 0..n substrings in input: "s/${START}.*${END}//g" + * @param input the String to strip + * @param source the name of the source for logging purposes + * @param start the starting tag (case sensitive) + * @param end the ending tag (case sensitive) + */ + String stripTags(String input, final String SOURCE, + final String START, final String END) { + if (null == input) return input; + StringBuffer buffer = new StringBuffer(input.length()); + String result = input; + int curLoc = 0; + while (true) { + int startLoc = input.indexOf(START, curLoc); + if (-1 == startLoc) { + buffer.append(input.substring(curLoc)); + result = buffer.toString(); + break; // <------------ valid exit + } else { + int endLoc = input.indexOf(END, startLoc); + if (-1 == endLoc) { + log(SOURCE + " stripTags - no end tag - startLoc=" + startLoc); + break; // <------------ invalid exit + } else if (endLoc < startLoc) { + log(SOURCE + " stripTags - impossible: startLoc=" + + startLoc + " > endLoc=" + endLoc); + break; // <------------ invalid exit + } else { + buffer.append(input.substring(curLoc, startLoc)); + curLoc = endLoc + END.length(); + } + } + } + return result; + } + + private int findStart(String s) throws ParseException { + int len = s.length(); + int start = 0; + while (true) { + start = s.indexOf("<body", start); + if (start == -1) { + start = s.indexOf("<BODY", start); + if (start == -1) throw barf(); + } + start = start + 5; + if (start >= len) throw barf(); + char ch = s.charAt(start); + if (ch == '>') return start + 1; + if (Character.isWhitespace(ch)) { + start = s.indexOf('>', start); + if (start == -1) return -1; + return start + 1; + } + } + } + + private int findEnd(String s, int start) throws ParseException { + int end; + end = s.indexOf("</body>", start); + if (end == -1) { + end = s.indexOf("</BODY>", start); + if (end == -1) throw barf(); + } + return end; + } + + private static class ParseException extends Exception { + private static final long serialVersionUID = -1l; + } + + private static ParseException barf() { + return new ParseException(); + } +} diff --git a/build/src/main/java./aspectj/internal/tools/ant/taskdefs/TestBuildModule.java b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/TestBuildModule.java new file mode 100644 index 000000000..bd3b99609 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/TestBuildModule.java @@ -0,0 +1,79 @@ +/* ******************************************************************* + * 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 Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.ant.taskdefs; + +import java.io.File; +import java.util.Arrays; + +import org.apache.tools.ant.Project; +import org.aspectj.internal.tools.build.BuildSpec; +import org.aspectj.internal.tools.build.Builder; +import org.aspectj.internal.tools.build.Util; + +public class TestBuildModule { +// private static boolean REBUILD = false; + private static final String SYNTAX = "java {classname} <[product|module]dir>"; + public static void main(String[] args) { + + if ((null == args) || (1 > args.length) + || !Util.canReadDir(new File(args[0]))) { + System.err.println(SYNTAX); + return; + } + File dir = new File(args[0]); + // create a module + if (Util.canReadDir(new File(dir, "dist"))) { + createProduct(args); + } else if (Util.canReadFile(new File(dir, ".classpath"))) { + createModule(args); + } else { + System.err.println(SYNTAX); + } + } + + static void createModule(String[] args) { + File moduleDir = new File(args[0]); + File baseDir = moduleDir.getParentFile(); + if (null == baseDir) { + baseDir = new File("."); + } + File jarDir = new File(baseDir, "aj-build-jars"); + if (!(Util.canReadDir(jarDir) || jarDir.mkdirs())) { + System.err.println("createModule unable to create " + jarDir); + return; + } + + // set module dir or basedir plus module name + BuildSpec buildSpec = new BuildSpec(); + buildSpec.moduleDir = moduleDir; + buildSpec.jarDir = jarDir; + buildSpec.verbose = true; + buildSpec.failonerror = true; + buildSpec.trimTesting = true; + buildSpec.rebuild = true; + + File tempDir = null; + Project project = new Project(); + project.setProperty("verbose", "true"); + project.setName("TestBuildModule.createModule" + Arrays.asList(args)); + Builder builder = AntBuilder.getBuilder("", project, tempDir); + builder.build(buildSpec); + } + + static void createProduct(String[] args) { + throw new Error("unimplemented"); + } +} + diff --git a/build/src/main/java./aspectj/internal/tools/ant/taskdefs/VersionUptodate.java b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/VersionUptodate.java new file mode 100644 index 000000000..92b271c69 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/VersionUptodate.java @@ -0,0 +1,144 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * 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://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; + +/** + * Check if version source file has the specified build version, + * and ensure a tag file reflects whether it does or not. + */ +public class VersionUptodate extends Task { + public VersionUptodate() {} + + private String buildVersion; + private File versionSource; + private File versionTagFile; + + /** + * @param buildVersion String expected as Version.text - required + */ + public void setVersion(String buildVersion) { + this.buildVersion = buildVersion; + } + + /** + * @param versionSource the File Version.java containing text constant + * - required + */ + public void setVersionSourceFile(File versionSource) { + this.versionSource = versionSource; + } + + /** + * @param versionTagFile the File whose existence signals that the version + * is uptodate after this task executes - required. + */ + public void setVersionTagFile(File versionTagFile) { + this.versionTagFile = versionTagFile; + } + + /** + * If the Version.java source file contains the correct + * build version, then create the output tag file, + * else delete it if it exists. + * @throws BuildException if tagFile not creatable and version is incorrect + * or if version is correct and tagFile cannot be deleted. + */ + public void execute() throws BuildException { + if (null == buildVersion) { + throw new BuildException("require buildVersion"); + } + if ((null == versionSource) || !versionSource.canRead()){ + throw new BuildException("require versionSource"); + } + if (null == versionTagFile){ + throw new BuildException("require versionTagFile"); + } + if (sameVersion(versionSource, buildVersion)) { + if (!versionTagFile.exists()) { + createFile(versionTagFile, buildVersion); + } + } else if (null == versionTagFile) { + throw new BuildException("no tag file, and version out of date"); + } else if (versionTagFile.exists()) { + if (!versionTagFile.delete()) { + throw new BuildException("version out of date, but cannot delete " + versionTagFile); + } + } + } + + /** + * Detect whether version is correct in Java sources. + * @param versionSource + * @param buildVersion + * @return boolean + */ + private boolean sameVersion(File versionSource, String buildVersion) { + // XXX build and load instead of scanning? + FileReader fileReader = null; + try { + fileReader = new FileReader(versionSource); + BufferedReader reader = new BufferedReader(fileReader); + String line; + while (null != (line = reader.readLine())) { + int loc = line.indexOf("static final String text = "); + if (-1 != loc) { + return (-1 != line.indexOf(buildVersion , loc)); + } + } + return false; + } catch (IOException e) { + return false; + } finally { + if (null != fileReader) { + try { + fileReader.close(); + } catch (IOException e) { + } + } + } + } + + /** + * Create file with contents + */ + private void createFile(File versionTagFile, String contents) { + FileWriter writer = null; + try { + writer = new FileWriter(versionTagFile); + char[] buf = new char[contents.length()]; + contents.getChars(0, buf.length, buf, 0); + writer.write(contents); + } catch (IOException e) { + throw new BuildException("writing " + versionTagFile, e); + } finally { + if (null != writer) { + try { + writer.close(); + } catch (IOException e){ + // ignore + } + } + } + } + +} + diff --git a/build/src/main/java./aspectj/internal/tools/ant/taskdefs/taskdefs.properties b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/taskdefs.properties new file mode 100644 index 000000000..884077a85 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/ant/taskdefs/taskdefs.properties @@ -0,0 +1,21 @@ +ajinstaller=org.aspectj.internal.tools.ant.taskdefs.AJInstaller +ajpush=org.aspectj.internal.tools.ant.taskdefs.AJPush +ajbuild=org.aspectj.internal.tools.ant.taskdefs.BuildModule +versionuptodate=org.aspectj.internal.tools.ant.taskdefs.VersionUptodate +checklics=org.aspectj.internal.tools.ant.taskdefs.Checklics +stripnonbodyhtml=org.aspectj.internal.tools.ant.taskdefs.StripNonBodyHtml + +# ajclean=org.aspectj.internal.tools.ant.taskdefs.AJclean +# ajcvs=org.aspectj.internal.tools.ant.taskdefs.Ajcvs +# ajikes=org.aspectj.internal.tools.ant.taskdefs.AJikes +# ajinit=org.aspectj.internal.tools.ant.taskdefs.AjInit +# checkrelease=org.aspectj.internal.tools.ant.taskdefs.Checkrelease +# clear=org.aspectj.internal.tools.ant.taskdefs.Clear +# inlinestylesheetaj=org.aspectj.internal.tools.ant.taskdefs.CopyAndInlineStyleshee +# ensure=org.aspectj.internal.tools.ant.taskdefs.Ensure +# ensureproperties=org.aspectj.internal.tools.ant.taskdefs.EnsureProperties +# newdir=org.aspectj.internal.tools.ant.taskdefs.Newdir +# overwrite=org.aspectj.internal.tools.ant.taskdefs.Overwrite +# props2filters=org.aspectj.internal.tools.ant.taskdefs.Props2Filters +# tgz=org.aspectj.internal.tools.ant.taskdefs.Tgz +# vmcheck=org.aspectj.internal.tools.ant.taskdefs.VMCheck diff --git a/build/src/main/java./aspectj/internal/tools/build/BuildSpec.java b/build/src/main/java./aspectj/internal/tools/build/BuildSpec.java new file mode 100644 index 000000000..ed0a453e1 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/build/BuildSpec.java @@ -0,0 +1,80 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC), + * 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://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + + +import java.io.File; + +/** + * Open struct for specifying builds for both modules and products. + * Separated from bulder to permit this to build many modules + * concurrently. + */ +public class BuildSpec { + public static final String DEFAULT_VERSION = "DEVELOPMENT"; + // shared + public File baseDir; + public File moduleDir; + public File jarDir; + public File tempDir; + public File stagingDir; + public String buildConfig; + public String version; + public boolean rebuild; + public boolean trimTesting; + public boolean assembleAll; + public boolean failonerror; + public boolean verbose; + + // building products + public File productDir; + public boolean createInstaller; + public File distDir; + + // building modules + public String module; + public BuildSpec() { + version = DEFAULT_VERSION; + } + + public boolean isProduct() { + return (Util.canReadDir(productDir)); + } + + public boolean isModule() { + return (!isProduct() && Util.canReadDir(moduleDir)); + } + + public boolean isValid() { + return (isProduct() || isModule()); + } + + public String toString() { + if (null != productDir) { + return "product " + productDir.getName(); + } else if (null != moduleDir) { + return "module " + moduleDir.getName(); + } else { + return "<bad BuildSpec - " + + " baseDir=" + baseDir + + " jarDir=" + jarDir + + " buildConfig=" + buildConfig + + " module=" + module + + ">"; + } + } +} + diff --git a/build/src/main/java./aspectj/internal/tools/build/Builder.java b/build/src/main/java./aspectj/internal/tools/build/Builder.java new file mode 100644 index 000000000..72f53e901 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/build/Builder.java @@ -0,0 +1,676 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC), + * 2003 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://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.build; + +import java.io.File; +import java.io.FileFilter; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +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.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: + * <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> + * <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> + * 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> + * <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>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> + * </ul> + * <p> + * 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. + */ +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. + */ + + public static final String RESOURCE_PATTERN; + + public static final String BINARY_SOURCE_PATTERN; + + public static final String ALL_PATTERN; + + /** enable copy filter semantics */ + protected static final boolean FILTER_ON = true; + + /** 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<String> SKIP_LIBRARIES; + + private static final String ERROR_KEY = "error loading properties"; + + private static final Properties PROPS; + static { + PROPS = new Properties(); + List<String> skips = Collections.emptyList(); + String resourcePattern = "**/*.txt,**/*.rsc,**/*.gif,**/*.properties"; + String allPattern = "**/*"; + String binarySourcePattern = "**/*.rsc,**/*.gif,**/*.jar,**/*.zip"; + String name = Builder.class.getName().replace('.', '/') + ".properties"; + try { + 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"); + skips = commaStrings(PROPS.getProperty(SKIP_LIBRARIES_KEY)); + } catch (Throwable t) { + if (t instanceof ThreadDeath) { + throw (ThreadDeath) t; + } + String m = "error loading " + name + ": " + t.getClass() + " " + t; + PROPS.setProperty(ERROR_KEY, m); + } + SKIP_LIBRARIES = skips; + ALL_PATTERN = allPattern; + 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. + * @return unmodifiable List (String) of String delimited by comma in text + */ + public static List commaStrings(String text) { + if ((null == text) || (0 == text.length())) { + return Collections.EMPTY_LIST; + } + List<String> strings = new ArrayList<String>(); + StringTokenizer tok = new StringTokenizer(text, ","); + while (tok.hasMoreTokens()) { + String token = tok.nextToken().trim(); + if (0 < token.length()) { + strings.add(token); + } + } + return Collections.unmodifiableList(strings); + } + + /** + * Map delivered-jar name to created-module name + * + * @param jarName + * the String (lowercased) of the jar/zip to map + */ + private String moduleAliasFor(String jarName) { + String result = PROPS.getProperty("alias." + jarName, jarName); + if (verbose && result.equals(jarName)) { + String m = "expected alias for " + jarName; + handler.error(m + PROPS.getProperty(ERROR_KEY, "")); + } + 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; + } + + 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<String> errors = new ArrayList<String>(); + try { + return buildAll(result, errors); + } finally { + if (0 < errors.size()) { + String label = "error building " + buildSpec + ": "; + for (Iterator<String> 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 final boolean isLogging() { + return (verbose && (null != this.handler)); + } + + 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()) { + 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<String> doneList = new ArrayList<String>(); + if ((null != buildList) && (0 < buildList.length)) { + if (isLogging()) { + 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 (!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<String> errors) { + if (!result.outOfDate()) { + return true; + } + if (isLogging()) { + handler.log("building " + result); + } + 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(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) { + 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<File> found = new ArrayList<File>(); + 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<ProductModule> results = new ArrayList<ProductModule>(); + for (File file: found) { + 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 + * @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(); + final String fname = library.getName(); + if (null != fname) { + for (Iterator iter = SKIP_LIBRARIES.iterator(); iter.hasNext();) { + String name = (String) iter.next(); + if (fname.equals(name)) { + liter.remove(); + break; + } + } + } + } + } + + /** + * @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<String> errors); + + /** + * Assemble the module distribution from the classesDir, saving String + * errors. + * + * @see #removeLibraryFilesToSkip(Module, File) + */ + abstract protected boolean assemble(Result result, File classesDir, + List<String> errors); + + /** + * Assemble the module distribution from the classesDir and all + * antecendants, saving String errors. + * + * @see #removeLibraryFilesToSkip(Module, File) + */ + 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/main/java./aspectj/internal/tools/build/Builder.properties b/build/src/main/java./aspectj/internal/tools/build/Builder.properties new file mode 100644 index 000000000..3838514f4 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/build/Builder.properties @@ -0,0 +1,20 @@ +# documented in build/readme-build-module.html + +# alias product/dist/lib/{name} to project jar +# used by Builder.moduleAliasFor(String) +alias.aspectjtools.jar=ajbrowser-all.jar +alias.aspectjrt.jar=aspectj5rt-all.jar +alias.aspectjweaver.jar=loadtime5-all.jar +# alias.aspectjlib.jar=org.aspectj.lib.jar +alias.org.aspectj.matcher.jar=org.aspectj.matcher-all.jar + +# libraries to avoid bundling (IBM JRE different, etc.) +# see build/readme-build-module.html, Builder.SKIP_LIBRARIES +skip.libraries=asm-3.1.jar,core.jar,graphics.jar,ant.jar,tools.jar,bcel-verifier.jar,asm-3.1.jar,junit.jar,xml-apis.jar,xercesImpl.jar,commons.jar,jrockit.jar + +# Ant patterns to gather and omit resources +# files copied during module jar assembly +resource.pattern=**/*.txt,**/*.rsc,**/*.gif,**/*.properties,**/*.xml,**/*.dtd +# files not filtered when copied during product assembly +binarySource.pattern=**/*.rsc,**/*.gif,**/*.jar,**/*.zip +all.pattern=**/* diff --git a/build/src/main/java./aspectj/internal/tools/build/Messager.java b/build/src/main/java./aspectj/internal/tools/build/Messager.java new file mode 100644 index 000000000..4752fd645 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/build/Messager.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * 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 Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + +/** logging stub XXX replace */ +public class Messager { + public Messager() { + } + public boolean log(String s) { + System.out.println(s); + return true; + } + + public boolean error(String s) { + System.out.println(s); + return true; + } + + public boolean logException(String context, Throwable thrown) { + System.err.println(context); + thrown.printStackTrace(System.err); + return true; + } +} + + + + + + + diff --git a/build/src/main/java./aspectj/internal/tools/build/Module.java b/build/src/main/java./aspectj/internal/tools/build/Module.java new file mode 100644 index 000000000..3f0afbfd9 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/build/Module.java @@ -0,0 +1,754 @@ +/* ******************************************************************* + * 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 Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.build; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.ArrayList; +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; +import org.aspectj.internal.tools.build.Util.OSGIBundle; +import org.aspectj.internal.tools.build.Util.OSGIBundle.RequiredBundle; + + +/** + * 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 { + private static final String[] ATTS = new String[] { "exported", "kind", + "path", "sourcepath" }; + +// private static final int getATTSIndex(String key) { +// for (int i = 0; i < ATTS.length; i++) { +// if (ATTS[i].equals(key)) +// return i; +// } +// return -1; +// } + + /** + * @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<File> sourceFiles(File srcDir) { + ArrayList<File> result = new ArrayList<File>(); + sourceFiles(srcDir, result); + return result.iterator(); + } + + private static void sourceFiles(File srcDir, List<File> 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]); + } + } + } + + private static void addIfNew(List<File> source, List<File> sink) { + for (File item: source) { + if (!sink.contains(item)) { + sink.add(item); + } + } + } + + /** + * Recursively find antecedant jars. + * + * @see findKnownJarAntecedants() + */ + static void doFindJarRequirements(Result result, List<File> known) { + Util.iaxIfNull(result, "result"); + Util.iaxIfNull(known, "known"); + 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); + doFindJarRequirements(requiredResult, known); + } + } + } + + /** @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 + } + +// /** @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; + + public final String name; + + /** reference back to collection for creating required 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; + + /** File list of library jars */ + private final List<File> libJars; + + /** List of classpath variables */ + private final List<String> classpathVariables; + + /** + * List of library jars exported to clients (duplicates some libJars + * entries) + */ + private final List<File> exportedLibJars; + + /** File list of source directories */ + private final List<File> srcDirs; + + /** properties from the modules {name}.properties file */ + private final Properties properties; + + /** List of required modules */ + private final List<Module> requiredModules; + + /** logger */ + private final Messager messager; + + Module(File moduleDir, File jarDir, String name, Modules modules, + Messager messager) { + Util.iaxIfNotCanReadDir(moduleDir, "moduleDir"); + Util.iaxIfNotCanReadDir(jarDir, "jarDir"); + Util.iaxIfNull(name, "name"); + Util.iaxIfNull(modules, "modules"); + this.moduleDir = moduleDir; + this.libJars = new ArrayList<File>(); + this.exportedLibJars = new ArrayList<File>(); + this.requiredModules = new ArrayList<Module>(); + this.srcDirs = new ArrayList<File>(); + this.classpathVariables = new ArrayList<String>(); + this.properties = new Properties(); + this.name = name; + this.modules = modules; + this.messager = messager; + this.moduleJar = new File(jarDir, name + ".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 Modules registry of known modules, including this one */ + public Modules getModules() { + return modules; + } + + /** + * @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 static boolean outOfDate(Result result) { + File outputFile = result.getOutputFile(); + if (!(outputFile.exists() && outputFile.canRead())) { + return true; + } + final long time = outputFile.lastModified(); + File file; + for (Iterator<File> iter = result.getSrcDirs().iterator(); iter.hasNext();) { + File srcDir = iter.next(); + for (Iterator<File> srcFiles = sourceFiles(srcDir); srcFiles.hasNext();) { + file = srcFiles.next(); + if (outOfDate(time, file)) { + return true; + } + } + } + // required modules + 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; + } + + + + public String toString() { + return name; + } + + public String toLongString() { + return "Module [name=" + name + ", srcDirs=" + srcDirs + ", required=" + + requiredModules + ", moduleJar=" + moduleJar + ", libJars=" + + libJars + "]"; + } + + public Result getResult(Kind kind) { + return kind.assemble ? (kind.normal ? releaseAll : testAll) + : (kind.normal ? release : test); + } + + List<File> srcDirs(Result result) { + myResult(result); + return srcDirs; + } + + List<File> libJars(Result result) { + myResult(result); + return libJars; + } + + List<String> classpathVariables(Result result) { + myResult(result); + return classpathVariables; + } + + List<File> exportedLibJars(Result result) { + myResult(result); + return exportedLibJars; + } + + List<Module> 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() { + boolean cp = initClasspath(); + boolean mf = initManifest(); + if (!cp && !mf) { + return false; + } + return initProperties() && reviewInit() && initResults(); + } + + /** read OSGI manifest.mf file XXX hacked */ + private boolean initManifest() { + File metaInf = new File(moduleDir, "META-INF"); + if (!metaInf.canRead() || !metaInf.isDirectory()) { + return false; + } + File file = new File(metaInf, "MANIFEST.MF"); // XXXFileLiteral + if (!file.exists()) { + return false; // ok, not OSGI + } + InputStream fin = null; + OSGIBundle bundle = null; + try { + fin = new FileInputStream(file); + bundle = new OSGIBundle(fin); + } catch (IOException e) { + messager.logException("IOException reading " + file, e); + return false; + } finally { + Util.closeSilently(fin); + } + RequiredBundle[] bundles = bundle.getRequiredBundles(); + for (int i = 0; i < bundles.length; i++) { + RequiredBundle required = bundles[i]; + update("src", "/" + required.name, required.text, false); + } + String[] libs = bundle.getClasspath(); + for (int i = 0; i < libs.length; i++) { + update("lib", libs[i], libs[i], false); + } + + return true; + } + + /** read eclipse .classpath file XXX line-oriented hack */ + private boolean initClasspath() { + // meaning testsrc directory, junit library, etc. + File file = new File(moduleDir, ".classpath"); // XXXFileLiteral + if (!file.exists()) { + return false; // OSGI??? + } + FileReader fin = null; + try { + fin = new FileReader(file); + BufferedReader reader = new BufferedReader(fin); + String line; + XMLItem item = new XMLItem("classpathentry", new ICB()); + while (null != (line = reader.readLine())) { + line = line.trim(); + // dumb - only handle comment-only lines + if (!line.startsWith("<?xml") && !line.startsWith("<!--")) { + item.acceptLine(line); + } + } + return (0 < (srcDirs.size() + libJars.size())); + } catch (IOException e) { + messager.logException("IOException reading " + file, e); + } finally { + if (null != fin) { + try { + fin.close(); + } catch (IOException e) { + } // ignore + } + } + return false; + } + +// private boolean update(String toString, String[] attributes) { +// String kind = attributes[getATTSIndex("kind")]; +// String path = attributes[getATTSIndex("path")]; +// String exp = attributes[getATTSIndex("exported")]; +// boolean exported = ("true".equals(exp)); +// return update(kind, path, toString, 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) { + requiredModules.add(req); + return true; + } else { + messager.error("update unable to create required module: " + + moduleName); + } + } else { // src dir + String fullPath = getFullPath(path); + File srcDir = new File(fullPath); + if (srcDir.canRead() && srcDir.isDirectory()) { + srcDirs.add(srcDir); + return true; + } else { + messager.error("not a src dir: " + srcDir); + } + } + } else if ("lib".equals(kind)) { + libPath = path; + } else if ("var".equals(kind)) { + final String JAVA_HOME = "JAVA_HOME/"; + if (path.startsWith(JAVA_HOME)) { + path = path.substring(JAVA_HOME.length()); + String home = System.getProperty("java.home"); + if (null != home) { + libPath = Util.path(home, path); + File f = new File(libPath); + if (!f.exists() && home.endsWith("jre")) { + f = new File(home).getParentFile(); + libPath = Util.path(f.getPath(), path); + } + } + } + if (null == libPath) { + warnVariable(path, toString); + classpathVariables.add(path); + } + } else if ("con".equals(kind)) { + // 'special' for container pointing at AspectJ runtime... + if (path.equals("org.eclipse.ajdt.core.ASPECTJRT_CONTAINER")) { + classpathVariables.add("ASPECTJRT_LIB"); + } else { + if (-1 == path.indexOf("JRE")) { // warn non-JRE containers + messager.log("cannot handle con yet: " + toString); + } + } + } else if ("out".equals(kind) || "output".equals(kind)) { + // ignore output entries + } else { + messager.log("unrecognized kind " + kind + " in " + toString); + } + if (null != libPath) { + File libJar = new File(libPath); + if (!libJar.exists()) { + libJar = new File(getFullPath(libPath)); + } + if (libJar.canRead() && libJar.isFile()) { + libJars.add(libJar); + if (exported) { + exportedLibJars.add(libJar); + } + return true; + } else { + messager.error("no such library jar " + libJar + " from " + + toString); + } + } + return false; + } + + private void warnVariable(String path, String toString) { + String[] known = { "JRE_LIB", "ASPECTJRT_LIB", "JRE15_LIB" }; + for (int i = 0; i < known.length; i++) { + if (known[i].equals(path)) { + return; + } + } + messager.log("Module cannot handle var yet: " + toString); + } + + /** @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 java5 source dirs + * if not running in a Java 5 VM. + * @return true if initialization post-processing worked + */ + protected boolean reviewInit() { + try { + for (ListIterator iter = srcDirs.listIterator(); iter.hasNext();) { + File srcDir = (File) iter.next(); + String lcname = srcDir.getName().toLowerCase(); + 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(); + } + } + } 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; + if (path.startsWith("/")) { + fullPath = modules.baseDir.getAbsolutePath() + path; + } else { + fullPath = moduleDir.getAbsolutePath() + "/" + path; + } + // check for absolute paths (untested - none in our modules so far) + File testFile = new File(fullPath); + // System.out.println("Module.getFullPath: " + fullPath + " - " + + // testFile.getAbsolutePath()); + if (!testFile.exists()) { + testFile = new File(path); + if (testFile.exists() && testFile.isAbsolute()) { + fullPath = path; + } + } + return fullPath; + } + + class ICB implements XMLItem.ICallback { + public void end(Properties attributes) { + String kind = attributes.getProperty("kind"); + String path = attributes.getProperty("path"); + String exp = attributes.getProperty("exported"); + boolean exported = ("true".equals(exp)); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + attributes.list(new PrintStream(bout)); + update(kind, path, bout.toString(), exported); + } + } + + public static class XMLItem { + 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() { + input.setLength(0); + for (int i = 0; i < attributes.length; i++) { + attributes[i] = null; + } + entityName = null; + attributeName = null; + } + + String[] tokenize(String line) { + final String DELIM = " \n\t\\<>\"="; + StringTokenizer st = new StringTokenizer(line, DELIM, true); + ArrayList<String> result = new ArrayList<String>(); + StringBuffer quote = new StringBuffer(); + boolean inQuote = false; + while (st.hasMoreTokens()) { + String s = st.nextToken(); + if ((1 == s.length()) && (-1 != DELIM.indexOf(s))) { + if ("\"".equals(s)) { // end quote (or escaped) + if (inQuote) { + inQuote = false; + quote.append("\""); + result.add(quote.toString()); + quote.setLength(0); + } else { + quote.append("\""); + inQuote = true; + } + } else { + result.add(s); + } + } else { // not a delimiter + if (inQuote) { + quote.append(s); + } else { + result.add(s); + } + } + } + return (String[]) result.toArray(new String[0]); + } + + public void acceptLine(String line) { + String[] tokens = tokenize(line); + for (int i = 0; i < tokens.length; i++) { + next(tokens[i]); + } + } + + private Properties attributesToProperties() { + Properties result = new Properties(); + for (int i = 0; i < attributes.length; i++) { + String a = attributes[i]; + if (null != a) { + result.setProperty(ATTS[i], a); + } + } + return result; + } + + void errorIfNotNull(String name, String value) { + if (null != value) { + 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. + */ + public void next(String s) { + if ((null == s) || (0 == s.length())) { + return; + } + input.append(s); + s = s.trim(); + if (0 == s.length()) { + return; + } + if ("<".equals(s)) { + errorIfNotNull("entityName", entityName); + errorIfNotNull("attributeName", attributeName); + } else if (">".equals(s)) { + errorIfNull("entityName", entityName); + if ("/".equals(attributeName)) { + attributeName = null; + } else { + errorIfNotNull("attributeName", attributeName); + } + if (activeEntity()) { + callback.end(attributesToProperties()); + } + entityName = null; + } else if ("=".equals(s)) { + errorIfNull("entityName", entityName); + errorIfNull("attributeName", attributeName); + } else if (s.startsWith("\"")) { + errorIfNull("entityName", entityName); + errorIfNull("attributeName", attributeName); + writeAttribute(attributeName, s); + attributeName = null; + } else { + if (null == entityName) { + reset(); + entityName = s; + } else if (null == attributeName) { + attributeName = s; + } else { + System.out + .println("unknown state - not value, attribute, or entity: " + + s); + } + } + } + + void readAttribute(String s) { + for (int i = 0; i < ATTS.length; i++) { + if (s.equals(ATTS[i])) { + attributes[i] = ATT_STARTED; + break; + } + } + } + + void writeAttribute(String name, String value) { + for (int i = 0; i < ATTS.length; i++) { + if (name.equals(ATTS[i])) { + if (!value.startsWith("\"") || !value.endsWith("\"")) { + error("bad attribute value: " + value); + } + value = value.substring(1, value.length() - 1); + attributes[i] = value; + return; + } + } + } + + void error(String s) { + throw new Error(s + " at input " + input); + } + } +} diff --git a/build/src/main/java./aspectj/internal/tools/build/Modules.java b/build/src/main/java./aspectj/internal/tools/build/Modules.java new file mode 100644 index 000000000..83686820e --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/build/Modules.java @@ -0,0 +1,66 @@ +/* ******************************************************************* + * 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 Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + +import java.io.File; +import java.util.Hashtable; + +/** + * Registration and factory for modules + * @see Module + * @see Builder + */ +public class Modules { + + private final Hashtable<String,Module> modules = new Hashtable<String,Module>(); + public final File baseDir; + public final File jarDir; + private final Messager handler; + + public Modules(File baseDir, File jarDir, Messager handler) { + this.baseDir = baseDir; + this.jarDir = jarDir; + this.handler = handler; + Util.iaxIfNotCanReadDir(baseDir, "baseDir"); + Util.iaxIfNotCanReadDir(jarDir, "jarDir"); + Util.iaxIfNull(handler, "handler"); + } + + + /** + * Get module associated with name. + * @return fail if unable to find or create module {name}. + */ + public Module getModule(String name) { + if (null == name) { + return null; + } + Module result = (Module) modules.get(name); + if (null == result) { + File moduleDir = new File(baseDir, name); + if (!Util.canReadDir(moduleDir)) { + handler.error("not a module: " + name); + } else { + result = new Module(moduleDir, jarDir, name, this, handler); + if (result.valid) { + modules.put(name, result); + } else { + handler.error("invalid module: " + result.toLongString()); + } + } + } + return result; + } +}
\ No newline at end of file diff --git a/build/src/main/java./aspectj/internal/tools/build/ProductModule.java b/build/src/main/java./aspectj/internal/tools/build/ProductModule.java new file mode 100644 index 000000000..c695a4f8e --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/build/ProductModule.java @@ -0,0 +1,70 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * 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://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ +package org.aspectj.internal.tools.build; + +import java.io.File; + +/** + * Struct associating module with target product distribution jar + * and assembly instructions. + * When building product distributions, a zero-length jar file + * in the dist directory may signify a module to be built, + * renamed, and included in the distribution. + */ +public class ProductModule { + /** name of distribution directory in product directory */ + private static final String DIST = "dist"; + + /** top-level product directory being produced */ + public final File productDir; + + /** path to file in distribution template dir for this module jar */ + public final File replaceFile; + + /** relative path within distribution of this product module jar */ + public final String relativePath; + + /** the module jar is the file to replace */ + public final Module module; + + /** if true, assemble all when building module */ + public final boolean assembleAll; + + public ProductModule(File productDir, File replaceFile, Module module, boolean assembleAll) { + this.replaceFile = replaceFile; + this.module = module; + this.productDir = productDir; + this.assembleAll = assembleAll; + Util.iaxIfNull(module, "module"); + Util.iaxIfNotCanReadDir(productDir, "productDir"); + Util.iaxIfNotCanReadFile(replaceFile, "replaceFile"); + String productDirPath = productDir.getAbsolutePath(); + String replaceFilePath = replaceFile.getAbsolutePath(); + if (!replaceFilePath.startsWith(productDirPath)) { + String m = "\"" + replaceFilePath + + "\" does not start with \"" + + productDirPath + + "\""; + throw new IllegalArgumentException(m); + } + replaceFilePath = replaceFilePath.substring(1+productDirPath.length()); + if (!replaceFilePath.startsWith(DIST)) { + String m = "\"" + replaceFilePath + + "\" does not start with \"" + DIST + "\""; + throw new IllegalArgumentException(m); + } + relativePath = replaceFilePath.substring(1 + DIST.length()); + } + public String toString() { + return "" + module + " for " + productDir; + } +} diff --git a/build/src/main/java./aspectj/internal/tools/build/Result.java b/build/src/main/java./aspectj/internal/tools/build/Result.java new file mode 100644 index 000000000..e12ba4e29 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/build/Result.java @@ -0,0 +1,387 @@ +/* ******************************************************************* + * 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<String,Result> nameToResult = new HashMap<String, Result>(); + + 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<Result> requiredResults; + + /** List of library jars */ + private final List<File> libJars; + + /** List of classpath variables */ + private final List<String> classpathVariables; + + transient String toLongString; + + /** + * List of library jars exported to clients (duplicates some libJars + * entries) + */ + private final List<File> exportedLibJars; + + /** List of source directories */ + private final List<File> 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<File>(); + this.exportedLibJars = new ArrayList<File>(); + this.srcDirs = new ArrayList<File>(); + this.classpathVariables = new ArrayList<String>(); + this.requiredResults = new ArrayList<Result>(); + 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 clearOutOfDate() { + outOfDateSet = false; + outOfDate = false; + } + + public boolean outOfDate() { + if (!outOfDateSet) { + outOfDate = Module.outOfDate(this); + outOfDateSet = true; + } + return outOfDate; + } + + /** @return List (File) of jar's required */ + public List<File> findJarRequirements() { + ArrayList<File> result = new ArrayList<File>(); + Module.doFindJarRequirements(this, result); + return result; + } + + /** @return unmodifiable List of String classpath variables */ + public List<String> 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<File> getExportedLibJars() { + return safeList(exportedLibJars); + } + + /** + * @return unmodifiable list of required library files, guaranteed readable + */ + public List<File> 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<File> 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 <T> List<T> safeList(List<T> l) { + requiredDone(); + return Collections.unmodifiableList(l); + } + + private Result[] safeResults(List<Result> 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<File> iter = srcDirs.listIterator(); iter.hasNext();) { + File srcDir = 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<File> iter = libJars.listIterator(); iter.hasNext();) { + File libJar = 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() { + final 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<Module> modules = module.requiredModules(this); + final boolean adoptTests = !kind.normal || isTestingModule(module); + for (Module required: modules) { + if (adoptTests) { + // testing builds can rely on other release and test results + requiredResults.add(required.getResult(TEST)); + requiredResults.add(required.getResult(RELEASE)); + } else if (!isTestingModule(required)){ + // release builds can only rely on non-testing results + // from non-testing modules + requiredResults.add(required.getResult(RELEASE)); + } // else skip release dependencies on testing-* (testing-util) + } + } + + 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; + } + } + +} diff --git a/build/src/main/java./aspectj/internal/tools/build/SampleGatherer.java b/build/src/main/java./aspectj/internal/tools/build/SampleGatherer.java new file mode 100644 index 000000000..0b023e6c5 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/build/SampleGatherer.java @@ -0,0 +1,1049 @@ +/* ******************************************************************* + * Copyright (c) 2003 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://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wes Isberg initial implementation + * ******************************************************************/ + +/* + * A quickie hack to extract sample code from testable sources. + * This could reuse a lot of code from elsewhere, + * but currently doesn't, + * to keep it in the build module which avoids dependencies. + * (Too bad we can't use scripting languages...) + */ +package org.aspectj.internal.tools.build; + +import java.io.*; +import java.text.DateFormat; +import java.util.*; + +/** + * This gathers sample code delimited with [START..END]-SAMPLE + * from source files under a base directory, + * along with any <code>@author</code> info. + * <pre>// START-SAMPLE {anchorName} {anchorText} + * ... sample code ... + * // END-SAMPLE {anchorName} + * </pre> + * where {anchorName} need not be unique and might be + * hierarchical wrt "-", e.g., "genus-species-individual". + */ +public class SampleGatherer { + + /** EOL String for gathered lines */ + public static final String EOL = "\n"; // XXX + + static final String START = "START-SAMPLE"; + static final String END = "END-SAMPLE"; + static final String AUTHOR = "@author"; + static final String FLAG = "XXX"; + +// private static void test(String[] args){ +// String[] from = new String[] { "<pre>", "</pre>" }; +// String[] to = new String[] { "<pre>", "</pre>" }; +// String source = "in this <pre> day and </pre> age of <pre and /pre>"; +// System.err.println("from " + source); +// System.err.println(" to " + SampleUtil.replace(source, from, to)); +// source = "<pre> day and </pre>"; +// System.err.println("from " + source); +// System.err.println(" to " + SampleUtil.replace(source, from, to)); +// source = "<pre day and </pre"; +// System.err.println("from " + source); +// System.err.println(" to " + SampleUtil.replace(source, from, to)); +// source = "<pre> day and </pre> age"; +// System.err.println("from " + source); +// System.err.println(" to " + SampleUtil.replace(source, from, to)); +// source = "in this <pre> day and </pre> age"; +// System.err.println("from " + source); +// System.err.println(" to " + SampleUtil.replace(source, from, to)); +// +// } + /** + * Emit samples gathered from any input args. + * @param args the String[] of paths to files or directories to search + * @throws IOException if unable to read a source file + */ + public static void main(String[] args) throws IOException { + if ((null == args) || (0 == args.length)) { + String cname = SampleGatherer.class.getName(); + System.err.println("java " + cname + " [dir|file]"); + return; + } + Samples result = new Samples(); + for (int i = 0; i < args.length; i++) { + result = gather(new File(args[i]), result); + } + + StringBuffer sb = HTMLSamplesRenderer.ME.render(result, null); + + File out = new File("../docs/dist/doc/sample-code.html"); + FileOutputStream fos = new FileOutputStream(out); + fos.write(sb.toString().getBytes()); + fos.close(); + System.out.println("see file:///" + out); + } + + /** + * Gather samples from a source file or directory + * @param source the File file or directory to start with + * @param sink the Samples collection to add to + * @return sink or a new Samples collection with any samples found + * @throws IOException if unable to read a source file + */ + public static Samples gather(File source, Samples sink) + throws IOException { + if (null == sink) { + sink = new Samples(); + } + if (null == source) { + source = new File("."); + } + doGather(source, sink); + return sink; + } + private static String trimCommentEnd(String line, int start) { + if (null == line) { + return ""; + } + if ((start > 0) && (start < line.length())) { + line = line.substring(start); + } + line = line.trim(); + if (line.endsWith("*/")) { + line = line.substring(0, line.length()-2).trim(); + } else if (line.endsWith("-->")) { + line = line.substring(0, line.length()-3).trim(); + } + return line; + } + + private static void doGather(File source, Samples sink) + throws IOException { + if (source.isFile()) { + if (isSource(source)) { + gatherFromFile(source, sink); + } + } else if (source.isDirectory() && source.canRead()) { + File[] files = source.listFiles(); + for (int i = 0; i < files.length; i++) { + doGather(files[i], sink); + } + } + } + + private static boolean isSource(File file) { + if ((null == file) || !file.isFile() || !file.canRead()) { + return false; + } + String path = file.getName().toLowerCase(); + String[] suffixes = Sample.Kind.SOURCE_SUFFIXES; + for (int i = 0; i < suffixes.length; i++) { + if (path.endsWith(suffixes[i])) { + return true; + } + } + return false; + } + + private static void gatherFromFile(final File source, final Samples sink) + throws IOException { + Reader reader = null; + try { + String author = null; + StringBuffer sampleCode = new StringBuffer(); + String anchorName = null; + String anchorTitle = null; + ArrayList flags = new ArrayList(); + int startLine = -1; // seeking + int endLine = Integer.MAX_VALUE; // not seeking + reader = new FileReader(source); + LineNumberReader lineReader = new LineNumberReader(reader); + String line; + + while (null != (line = lineReader.readLine())) { // XXX naive + // found start? + int loc = line.indexOf(START); + if (-1 != loc) { + int lineNumber = lineReader.getLineNumber(); + if (-1 != startLine) { + abort("unexpected " + START, source, line, lineNumber); + } + startLine = lineNumber; + endLine = -1; + anchorName = trimCommentEnd(line, loc + START.length()); + loc = anchorName.indexOf(" "); + if (-1 == loc) { + anchorTitle = null; + } else { + anchorTitle = anchorName.substring(1+loc).trim(); + anchorName = anchorName.substring(0, loc); + } + continue; + } + + // found end? + loc = line.indexOf(END); + if (-1 != loc) { + int lineNumber = lineReader.getLineNumber(); + if (Integer.MAX_VALUE == endLine) { + abort("unexpected " + END, source, line, lineNumber); + } + String newtag = trimCommentEnd(line, loc + END.length()); + if ((newtag.length() > 0) && !newtag.equals(anchorName)) { + String m = "expected " + anchorName + + " got " + newtag; + abort(m, source, line, lineNumber); + } + endLine = lineNumber; + Sample sample = new Sample(anchorName, + anchorTitle, + author, + sampleCode.toString(), + source, + startLine, + endLine, + (String[]) flags.toArray(new String[flags.size()])); + sink.addSample(sample); + + // back to seeking start + sampleCode.setLength(0); + startLine = -1; + endLine = Integer.MAX_VALUE; + continue; + } + + // found author? + loc = line.indexOf(AUTHOR); + if (-1 != loc) { + author = trimCommentEnd(line, loc + AUTHOR.length()); + } + // found flag comment? + loc = line.indexOf(FLAG); + if (-1 != loc) { + flags.add(trimCommentEnd(line, loc + FLAG.length())); + } + + // reading? + if ((-1 != startLine) && (-1 == endLine)) { + sampleCode.append(line); + sampleCode.append(EOL); + } + } + if (-1 == endLine) { + abort("incomplete sample", source, "", lineReader.getLineNumber()); + } + } finally { + if (null != reader) { + reader.close(); + } + } + } + private static void abort(String why, File file, String line, int lineNumber) + throws Abort { + throw new Abort(why + " at " + file + ":" + lineNumber + ": " + line); + } +// private static void delay(Object toDelay) { +// synchronized (toDelay) { // XXX sleep instead? +// toDelay.notifyAll(); +// } +// } + static class Abort extends IOException { + private static final long serialVersionUID = -1l; + Abort(String s) { + super(s); + } + } +} + +/** + * Data associated with sample code - struct class. + */ +class Sample { + public static final String ASPECTJ_TEAM = "The AspectJ Team"; + + /** sort by anchorName, file path, and start/end location */ + static Comparator<Sample> NAME_SOURCE_COMPARER = new Comparator<Sample>() { + public int compare(Sample left, Sample right) { + if (null == left) { + return (null == right ? 0 : -1); + } + if (null == right) { + return 1; + } + int result = left.anchorName.compareTo(right.anchorName); + if (0 != result) { + return result; + } + result = left.sourcePath.compareTo(right.sourcePath); + if (0 != result) { + return result; + } + result = right.startLine - left.startLine; + if (0 != result) { + return result; + } + return right.endLine - left.endLine; + } + }; + + /** sort by author, then NAME_SOURCE_COMPARER */ + static Comparator<Sample> AUTHOR_NAME_SOURCE_COMPARER = new Comparator<Sample>() { + public int compare(Sample left, Sample right) { + if (null == left) { + return (null == right ? 0 : -1); + } + if (null == right) { + return 1; + } + int result = left.author.compareTo(right.author); + if (0 != result) { + return result; + } + return NAME_SOURCE_COMPARER.compare(left,right); + } + }; + + final String anchorName; + final String anchorTitle; + final String author; + final String sampleCode; + final File sourcePath; + final int startLine; + final int endLine; + final Kind kind; + /** List of String flags found in the sample */ + final List flags; + public Sample( + String anchorName, + String anchorTitle, + String author, + String sampleCode, + File sourcePath, + int startLine, + int endLine, + String[] flags) { + this.anchorName = anchorName; + this.anchorTitle = anchorTitle; + this.author = (null != author ? author : ASPECTJ_TEAM); + this.sampleCode = sampleCode; + this.sourcePath = sourcePath; + this.startLine = startLine; + this.endLine = endLine; + this.kind = Kind.getKind(sourcePath); +// List theFlags; + if ((null == flags) || (0 == flags.length)) { + this.flags = Collections.EMPTY_LIST; + } else { + this.flags = Collections.unmodifiableList(Arrays.asList(flags)); + } + } + + public String toString() { + return sampleCode; + } + + public static class Kind { + + /** lowercase source suffixes identify files to gather samples from */ + public static final String[] SOURCE_SUFFIXES = new String[] + { ".java", ".aj", ".sh", ".ksh", + ".txt", ".text", ".html", ".htm", ".xml" }; + static final Kind XML = new Kind(); + static final Kind HTML = new Kind(); + static final Kind PROGRAM = new Kind(); + static final Kind SCRIPT = new Kind(); + static final Kind TEXT = new Kind(); + static final Kind OTHER = new Kind(); + public static Kind getKind(File file) { + if (null == file) { + return OTHER; + } + String name = file.getName().toLowerCase(); + if ((name.endsWith(".java") || name.endsWith(".aj"))) { + return PROGRAM; + } + if ((name.endsWith(".html") || name.endsWith(".htm"))) { + return HTML; + } + if ((name.endsWith(".sh") || name.endsWith(".ksh"))) { + return SCRIPT; + } + if ((name.endsWith(".txt") || name.endsWith(".text"))) { + return TEXT; + } + if (name.endsWith(".xml")) { + return XML; + } + return OTHER; + } + private Kind() { + } + } +} + +/** + * type-safe Collection of samples. + */ +class Samples { + private ArrayList<Sample> samples = new ArrayList<Sample>(); + int size() { + return samples.size(); + } + void addSample(Sample sample) { + samples.add(sample); + } + /** + * @return List copy, sorted by Sample.NAME_SOURCE_COMPARER + */ + List<Sample> getSortedSamples() { + return getSortedSamples(Sample.NAME_SOURCE_COMPARER); + } + + List<Sample> getSortedSamples(Comparator<Sample> comparer) { + ArrayList<Sample> result = new ArrayList<Sample>(); + result.addAll(samples); + Collections.sort(result, comparer); + return result; + } +} + + +/** + * Render samples by using method visitors. + */ +class SamplesRenderer { + public static SamplesRenderer ME = new SamplesRenderer(); + protected SamplesRenderer() { + } + public static final String EOL = "\n"; // XXX + public static final String INFO = + "<p>This contains contributions from the AspectJ community of " + + "<ul><li>sample code for AspectJ programs,</li>" + + "<li>sample code for extensions to AspectJ tools using the public API's,</li>" + + "<li>sample scripts for invoking AspectJ tools, and </li> " + + "<li>documentation trails showing how to do given tasks" + + " using AspectJ, AJDT, or various IDE or deployment" + + " environments.</li></ul></p>" + + "<p>Find complete source files in the AspectJ CVS repository at " + + "<code>org.aspectj/modules/docs/sandbox</code>. " + + "For instructions on downloading code from the CVS repository, " + + "see the <a href=\"doc/faq.html#q:buildingsource\">FAQ entry " + + "\"buildingsource\"</a>.</p>"; + + public static final String COPYRIGHT = + "<p><small>Copyright 2003 Contributors. All Rights Reserved. " + + "This sample code is made available under the Common Public "
+ "License version 1.0 available at " + + "<a href=\"http://www.eclipse.org/legal/epl-v10.html\">" + + "http://www.eclipse.org/legal/epl-v10.html</a>." + + "Contributors are listed in this document as authors. " + + "Permission to republish portions of this sample code " + + "is hereby granted if the publication acknowledges " + + "the author by name and " + + "the source by reference to the AspectJ project home page " + + " at http://eclipse.org/aspectj.</small></p>" + + EOL; + + /** template algorithm to render */ + public final StringBuffer render(Samples samples, StringBuffer sink) { + if (null == sink) { + sink = new StringBuffer(); + } + if ((null == samples) || (0 == samples.size())) { + return sink; + } + startList(samples, sink); + List list = samples.getSortedSamples(); + String anchorName = null; + for (ListIterator iter = list.listIterator(); + iter.hasNext();) { + Sample sample = (Sample) iter.next(); + String newAnchorName = sample.anchorName; + if ((null == anchorName) + || (!anchorName.equals(newAnchorName))) { + endAnchorName(anchorName, sink); + startAnchorName(newAnchorName, sample.anchorTitle, sink); + anchorName = newAnchorName; + } + render(sample, sink); + } + endAnchorName(anchorName, sink); + endList(samples, sink); + return sink; + } + protected void startList(Samples samples, StringBuffer sink) { + sink.append("Printing " + samples.size() + " samples"); + sink.append(EOL); + } + + protected void startAnchorName(String name, String title, StringBuffer sink) { + sink.append("anchor " + name); + sink.append(EOL); + } + + protected void render(Sample sample, StringBuffer sink) { + SampleUtil.render(sample, "=", ", ",sink); + sink.setLength(sink.length()-2); + sink.append(EOL); + } + + /** + * @param name the String name being ended - ignore if null + * @param sink + */ + protected void endAnchorName(String name, StringBuffer sink) { + if (null == name) { + return; + } + } + + protected void endList(Samples samples, StringBuffer sink) { + sink.append("Printed " + samples.size() + " samples"); + sink.append(EOL); + } + +} + +// XXX need DocBookSamplesRenderer + +/** + * Output the samples as a single HTML file, with a table of contents + * and sorting the samples by their anchor tags. + */ +class HTMLSamplesRenderer extends SamplesRenderer { + public static SamplesRenderer ME = new HTMLSamplesRenderer(); + // XXX move these + public static boolean doHierarchical = true; + public static boolean doFlags = false; + + + final StringBuffer tableOfContents; + final StringBuffer sampleSection; + String[] lastAnchor = new String[0]; + String currentAnchor; + String currentAuthor; + + protected HTMLSamplesRenderer() { + sampleSection = new StringBuffer(); + tableOfContents = new StringBuffer(); + } + + protected void startAnchorName(String name, String title, StringBuffer sink) { + if (doHierarchical) { + doContentTree(name); + } + // ---- now do anchor + tableOfContents.append(" <li><a href=\"#" + name); + if ((null == title) || (0 == title.length())) { + title = name; + } + tableOfContents.append("\">" + title + "</a></li>"); + tableOfContents.append(EOL); + currentAnchor = name; + } + + protected void startList(Samples samples, StringBuffer sink) { + } + + protected void render(Sample sample, StringBuffer sink) { + if (null != currentAnchor) { + if (!currentAnchor.equals(sample.anchorName)) { + String m = "expected " + currentAnchor + + " got " + sample.anchorName; + throw new Error(m); + } + currentAnchor = null; + } + + // do heading then code + renderHeading(sample.anchorName, sample.anchorTitle, sampleSection); + if (sample.kind == Sample.Kind.HTML) { + renderHTML(sample); + } else if (sample.kind == Sample.Kind.XML) { + renderXML(sample); + } else { + renderPre(sample); + } + } + + protected boolean doRenderAuthor(Sample sample) { + return (null != sample.author); + // && !sample.author.equals(currentAuthor) + } + + protected void renderStandardHeader(Sample sample) { + // XXX starting same as pre + if (doRenderAuthor(sample)) { + currentAuthor = sample.author; + sampleSection.append(" <p>| " + currentAuthor); + sampleSection.append(EOL); + } + sampleSection.append(" | "); + sampleSection.append(SampleUtil.renderCodePath(sample.sourcePath)); + sampleSection.append(":" + sample.startLine); + sampleSection.append(" |"); + sampleSection.append(EOL); + sampleSection.append("<p>"); + sampleSection.append(EOL); + if (doFlags) { + boolean flagHeaderDone = false; + for (Iterator iter = sample.flags.iterator(); iter.hasNext();) { + String flag = (String) iter.next(); + if (!flagHeaderDone) { + sampleSection.append("<p>Comments flagged:<ul>"); + sampleSection.append(EOL); + flagHeaderDone = true; + } + sampleSection.append("<li>"); + sampleSection.append(flag); + sampleSection.append("</li>"); + } + if (flagHeaderDone) { + sampleSection.append("</ul>"); + sampleSection.append(EOL); + } + } + } + + protected void renderXML(Sample sample) { + renderStandardHeader(sample); + sampleSection.append(" <pre>"); + sampleSection.append(EOL); + sampleSection.append(prepareXMLSample(sample.sampleCode)); + sampleSection.append(EOL); + sampleSection.append(" </pre>"); + sampleSection.append(EOL); + } + + protected void renderHTML(Sample sample) { + renderStandardHeader(sample); + sampleSection.append(EOL); + sampleSection.append(prepareHTMLSample(sample.sampleCode)); + sampleSection.append(EOL); + } + + protected void renderPre(Sample sample) { + renderStandardHeader(sample); + sampleSection.append(" <pre>"); + sampleSection.append(EOL); + sampleSection.append(prepareCodeSample(sample.sampleCode)); + sampleSection.append(" </pre>"); + sampleSection.append(EOL); + } + + protected void endAnchorName(String name, StringBuffer sink) { + if (null == name) { + return; + } + currentAnchor = null; + currentAuthor = null; // authors don't span anchors + } + + protected void endList(Samples samples, StringBuffer sink) { + sink.append("<html>"); + sink.append(EOL); + sink.append("<title>AspectJ sample code</title>"); + sink.append(EOL); + sink.append("<body>"); + sink.append(EOL); + sink.append(" <a name=\"top\"></a>"); + sink.append(EOL); + sink.append(" <h1>AspectJ sample code</h1>"); + sink.append(INFO); + sink.append(EOL); + sink.append(COPYRIGHT); + sink.append(EOL); + sink.append("<p><small>Generated on "); + sink.append(DateFormat.getDateInstance().format(new Date())); + sink.append(" by SamplesGatherer</small>"); + sink.append(EOL); + sink.append(" <h2>Contents</h2>"); + sink.append(EOL); + sink.append(" <ul>"); + sink.append(EOL); + sink.append(tableOfContents.toString()); + // unwind to common prefix, if necessary + for (int i = 0; i < lastAnchor.length ; i++) { + sink.append(" </ul>"); + } + + sink.append(" <li><a href=\"#authorIndex\">Author Index</a></li>"); + sink.append(" </ul>"); + sink.append(" <h2>Listings</h2>"); + sink.append(EOL); + sink.append(sampleSection.toString()); + renderAuthorIndex(samples, sink); + sink.append("</body></html>"); + sink.append(EOL); + } + + protected String prepareXMLSample(String sampleCode) { + String[] from = new String[] {"\t", "<"}; + String[] to = new String[] {" ", "<"}; + return (SampleUtil.replace(sampleCode, from, to)); + } + + protected String prepareHTMLSample(String sampleCode) { + String[] from = new String[20]; + String[] to = new String[20]; + for (int i = 0; i < to.length; i++) { + String h = "h" + i + ">"; + from[i] = "<" + h; + to[i] = "<p><b>"; + from[++i] = "</" + h; + to[i] = "</b></p><p>"; + } + return (SampleUtil.replace(sampleCode, from, to)); + } + + protected String prepareCodeSample(String sampleCode) { + String[] from = new String[] { "<pre>", "</pre>" }; + String[] to = new String[] { "<pre>", "</pre>" }; + return (SampleUtil.replace(sampleCode, from, to)); + } + + protected void renderHeading(String anchor, String title, StringBuffer sink) { + sink.append(" <a name=\"" + anchor + "\"></a>"); + sink.append(EOL); + if ((null == title) || (0 == title.length())) { + title = anchor; + } + sink.append(" <h3>" + title + "</h3>"); + sink.append(EOL); + sink.append("<a href=\"#top\">back to top</a>"); + sink.append(EOL); + } + + /** + * Manage headings in both table of contents and listings. + * @param name the String anchor + */ + protected void doContentTree(String name) { + if (name.equals(lastAnchor)) { + return; + } + // ---- handle trees + String[] parts = SampleUtil.splitAnchorName(name); + //String[] lastAnchor = (String[]) lastAnchors.peek(); + int firstDiff = SampleUtil.commonPrefix(parts, lastAnchor); + // unwind to common prefix, if necessary + if (firstDiff+1 < lastAnchor.length) { + for (int i = 1; i < lastAnchor.length-firstDiff ; i++) { + tableOfContents.append(" </ul>"); + tableOfContents.append(EOL); + } + } + // build up prefix + StringBuffer branchAnchor = new StringBuffer(); + for (int i = 0; i < firstDiff;) { + branchAnchor.append(parts[i]); + i++; + branchAnchor.append("-"); + } + // emit leading headers, but not anchor itself + for (int i = firstDiff; i < (parts.length-1); i++) { + branchAnchor.append(parts[i]); + String prefixName = branchAnchor.toString(); + branchAnchor.append("-"); + tableOfContents.append(" <li><a href=\"#"); + tableOfContents.append(prefixName); + tableOfContents.append("\">" + prefixName + "</a></li>"); + tableOfContents.append(EOL); + tableOfContents.append(" <ul>"); + tableOfContents.append(EOL); + + renderHeading(prefixName, prefixName, sampleSection); + } + lastAnchor = parts; + } + + protected void renderAuthorIndex(Samples samples, StringBuffer sink) { + sink.append("<h2><a name=\"authorIndex\"></a>Author Index</h2>"); + List list = samples.getSortedSamples(Sample.AUTHOR_NAME_SOURCE_COMPARER); + String lastAuthor = null; + for (ListIterator iter = list.listIterator(); iter.hasNext();) { + Sample sample = (Sample)iter.next(); + String author = sample.author; + if (!author.equals(lastAuthor)) { + if (null != lastAuthor) { + sink.append("</li></ul>"); + } + sink.append("<li>"); + sink.append(author); + sink.append(EOL); + sink.append("<ul>"); + sink.append(EOL); + lastAuthor = author; + } + sink.append(" <li><a href=\"#"); + sink.append(sample.anchorName); + sink.append("\">"); + if (null == sample.anchorTitle) { + sink.append(sample.anchorName); + } else { + sink.append(sample.anchorTitle); + } + sink.append("</a></li>"); + } + } +} + +class SampleUtil { + public static final String SAMPLE_BASE_DIR_NAME = "sandbox"; + + public static void simpleRender(Samples result, StringBuffer sink) { + List sortedSamples = result.getSortedSamples(); + int i = 0; + for (ListIterator iter = sortedSamples.listIterator(); + iter.hasNext();) { + Sample sample = (Sample) iter.next(); + sink.append(i++ + ": " + sample); + } + } + + /** result struct for getPackagePath */ + static class JavaFile { + /** input File possibly signifying a java file */ + final File path; + + /** String java path suffix in form "com/company/Bar.java" + * null if this is not a java file + */ + final String javaPath; + + /** any prefix before java path suffix in the original path */ + final String prefix; + + /** error handling */ + final Throwable thrown; + JavaFile(File path, String javaPath, String prefix, Throwable thrown) { + this.path = path; + this.javaPath = javaPath; + this.prefix = prefix; + this.thrown = thrown; + } + } + + /** + * Read any package statement in the file to determine + * the package path of the file + * @param path the File to seek the package in + * @return the JavaFile with the components of the path + */ + public static JavaFile getJavaFile(File path) { + if (null == path) { + throw new IllegalArgumentException("null path"); + } + String result = path.getPath().replace('\\', '/'); + String packag = ""; + String javaPath = null; + String prefix = null; + Throwable thrown = null; + if (result.endsWith(".java") || result.endsWith(".aj")) { + FileReader reader = null; + try { + reader = new FileReader(path); + BufferedReader br = new BufferedReader(reader); + String line; + while (null != (line = br.readLine())) { + int loc = line.indexOf("package"); + if (-1 != loc) { + int end = line.indexOf(";"); + if (-1 == loc) { + String m = "unterminated package statement \""; + throw new Error(m + line + "\" in " + path); + } + packag = (line.substring(loc + 7, end) + ".") + .trim() + .replace('.', '/'); + break; + } + loc = line.indexOf("import"); + if (-1 != loc) { + break; + } + } + } catch (IOException e) { + thrown = e; + } finally { + if (null != reader) { + try { + reader.close(); + } catch (IOException e1) { + // ignore + } + } + } + if (null == thrown) { + javaPath = packag + path.getName(); + int loc = result.indexOf(javaPath); + if (-1 == loc) { + String m = "expected suffix " + javaPath + " in "; + throw new Error(m + result); + } + prefix = result.substring(0, loc); + } + } + return new JavaFile(path, javaPath, prefix, thrown); + } + + /** + * Extract file path relative to base of package directory + * and directory in SAMPLE_BASE_DIR_NAME for this file. + * @param path the File to render from SAMPLE_BASE_DIR_NAME + * @return String "baseDir {path}" + */ + public static String renderCodePath(File path) { + JavaFile javaFile = getJavaFile(path); + if (javaFile.thrown != null) { + throw new Error(javaFile.thrown.getClass() + + ": " + javaFile.thrown.getMessage()); + } + + String file = javaFile.javaPath; // can be null... + String prefix = javaFile.prefix; + if (prefix == null) { + prefix = path.getPath().replace('\\', '/'); + } + int loc = prefix.lastIndexOf(SAMPLE_BASE_DIR_NAME); + if (-1 == loc) { + String m = "not after " + SAMPLE_BASE_DIR_NAME; + throw new IllegalArgumentException(m + "?: " + path); + } + prefix = prefix.substring(loc + 1 + SAMPLE_BASE_DIR_NAME.length()); + + if (file == null) { + int slash = prefix.lastIndexOf('/'); + if (-1 == slash) { + file = prefix; + prefix = ""; + } else { + file = prefix.substring(slash+1); + prefix = prefix.substring(0, slash); + } + } + if (prefix.endsWith("/")) { + prefix = prefix.substring(0, prefix.length()-1); + } + return (prefix + " " + file).trim(); + } + + public static int commonPrefix(String[] lhs, String[] rhs) { + final int max = smallerSize(lhs, rhs); + int firstDiff = 0; + while (firstDiff < max) { + if (!lhs[firstDiff].equals(rhs[firstDiff])) { + break; + } + firstDiff++; + } + return firstDiff; + } + + private static int smallerSize(Object[] one, Object[] two) { + if ((null == one) || (null == two)) { + return 0; + } + return (one.length > two.length ? two.length : one.length); + } + + public static String[] splitAnchorName(Sample sample) { + return splitAnchorName(sample.anchorName); + } + + public static String[] splitAnchorName(String anchorName) { + ArrayList<String> result = new ArrayList<String>(); + int start = 0; + int loc = anchorName.indexOf("-", start); + String next; + while (loc != -1) { + next = anchorName.substring(start, loc); + result.add(next); + start = loc+1; + loc = anchorName.indexOf("-", start); + } + next = anchorName.substring(start); + result.add(next); + return (String[]) result.toArray(new String[result.size()]); + } + /** + * Replace literals with literals in source string + * @param source the String to modify + * @param from the String[] of literals to replace + * @param to the String[] of literals to use when replacing + * @return the String source as modified by the replaces + */ + public static String replace(String source, String[] from, String[] to) { + if ((null == source) || (0 == source.length())) { + return source; + } + if (from.length != to.length) { + throw new IllegalArgumentException("unmatched from/to"); + } + StringBuffer result = new StringBuffer(); + int LEN = source.length(); + int start = 0; + for (int i = 0; i < LEN; i++) { + String suffix = source.substring(i); + for (int j = 0; j < from.length; j++) { + if (suffix.startsWith(from[j])) { + result.append(source.substring(start, i)); + result.append(to[j]); + start = i + from[j].length(); + i = start-1; + break; + } + } + } + if (start < source.length()) { + result.append(source.substring(start)); + } + return result.toString(); + } + + public static void render( + Sample sample, + String fieldDelim, + String valueDelim, + StringBuffer sink) { + if ((null == sink) || (null == sample)) { + return; + } + if (null == fieldDelim) { + fieldDelim = ""; + } + if (null == valueDelim) { + valueDelim = ""; + } + sink.append("anchorName"); + sink.append(valueDelim); + sink.append(sample.anchorName); + sink.append(fieldDelim); + sink.append("author"); + sink.append(valueDelim); + sink.append(sample.author); + sink.append(fieldDelim); + sink.append("sourcePath"); + sink.append(valueDelim); + sink.append(sample.sourcePath.toString()); + sink.append(fieldDelim); + sink.append("startLine"); + sink.append(valueDelim); + sink.append(sample.startLine); + sink.append(fieldDelim); + sink.append("endLine"); + sink.append(valueDelim); + sink.append(sample.endLine); + sink.append(fieldDelim); + sink.append("sampleCode"); + sink.append(valueDelim); + sink.append(sample.sampleCode.toString()); + sink.append(fieldDelim); + } + private SampleUtil(){} +} diff --git a/build/src/main/java./aspectj/internal/tools/build/Util.java b/build/src/main/java./aspectj/internal/tools/build/Util.java new file mode 100644 index 000000000..2fc2dcb72 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/build/Util.java @@ -0,0 +1,483 @@ +/* ******************************************************************* + * 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 Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + +import java.io.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.StringTokenizer; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import java.util.jar.Attributes.Name; + +/** + * Build-only utilities. + * Many mirror utils module APIs. + */ +public class Util { + public static class Constants { + public static final String TESTSRC = "testsrc"; + public static final String JAVA5_SRC = "java5-src"; + public static final String JAVA5_TESTSRC = "java5-testsrc"; + } + // XXX quick hack for Java 5 support + public static final boolean JAVA5_VM; + static { + boolean java5VM = false; + try { + java5VM = (null != Class.forName("java.lang.annotation.Annotation")); + } catch (Throwable t) { + // ignore + } + JAVA5_VM = java5VM; + } + + /** + * Map version in long form to short, + * e.g., replacing "alpha" with "a" + */ + public static String shortVersion(String version) { + version = Util.replace(version, "alpha", "a"); + version = Util.replace(version, "beta", "b"); + version = Util.replace(version, "candidate", "rc"); + version = Util.replace(version, "development", "d"); + version = Util.replace(version, "dev", "d"); + return version; + } + + /** + * Replace any instances of {replace} in {input} with {with}. + * @param input the String to search/replace + * @param replace the String to search for in input + * @param with the String to replace with in input + * @return input if it has no replace, otherwise a new String + */ + public static String replace(String input, String replace, String with) { + int loc = input.indexOf(replace); + if (-1 != loc) { + String result = input.substring(0, loc); + result += with; + int start = loc + replace.length(); + if (start < input.length()) { + result += input.substring(start); + } + input = result; + } + return input; + } + + /** @return false if filter returned false for any file in baseDir subtree */ + public static boolean visitFiles(File baseDir, FileFilter filter) { + Util.iaxIfNotCanReadDir(baseDir, "baseDir"); + Util.iaxIfNull(filter, "filter"); + File[] files = baseDir.listFiles(); + boolean passed = true; + for (int i = 0; passed && (i < files.length); i++) { + passed = files[i].isDirectory() + ? visitFiles(files[i], filter) + : filter.accept(files[i]); + } + return passed; + } + + /** @throws IllegalArgumentException if cannot read dir */ + public static void iaxIfNotCanReadDir(File dir, String name) { + if (!canReadDir(dir)) { + throw new IllegalArgumentException(name + " dir not readable: " + dir); + } + } + + /** @throws IllegalArgumentException if cannot read file */ + public static void iaxIfNotCanReadFile(File file, String name) { + if (!canReadFile(file)) { + throw new IllegalArgumentException(name + " file not readable: " + file); + } + } + + /** @throws IllegalArgumentException if cannot write dir */ + public static void iaxIfNotCanWriteDir(File dir, String name) { + if (!canWriteDir(dir)) { + throw new IllegalArgumentException(name + " dir not writeable: " + dir); + } + } + + /** @throws IllegalArgumentException if input is null */ + public static void iaxIfNull(Object input, String name) { + if (null == input) { + throw new IllegalArgumentException("null " + name); + } + } + + /** render exception to String */ + public static String renderException(Throwable thrown) { + if (null == thrown) { + return "(Throwable) null"; + } + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw, true); + pw.println(thrown.getMessage()); + thrown.printStackTrace(pw); + pw.flush(); + return sw.getBuffer().toString(); + } + + /** @return true if dir is a writable directory */ + public static boolean canWriteDir(File dir) { + return (null != dir) && dir.canWrite() && dir.isDirectory(); + } + + public static String path(String first, String second) { + return first + File.separator + second; + } + + public static String path(String[] segments) { + StringBuffer sb = new StringBuffer(); + if ((null != segments)) { + for (int i = 0; i < segments.length; i++) { + if (0 < i) { + sb.append(File.separator); + } + sb.append(segments[i]); + } + } + return sb.toString(); + } + + /** @return true if dir is a readable directory */ + public static boolean canReadDir(File dir) { + return (null != dir) && dir.canRead() && dir.isDirectory(); + } + + /** @return true if dir is a readable file */ + public static boolean canReadFile(File file) { + return (null != file) && file.canRead() && file.isFile(); + } + + /** + * Delete file or directory. + * @param dir the File file or directory to delete. + * @return true if all contents of dir were deleted + */ + public static boolean delete(File dir) { + return deleteContents(dir) && dir.delete(); + } + + /** + * Delete contents of directory. + * The directory itself is not deleted. + * @param dir the File directory whose contents should be deleted. + * @return true if all contents of dir were deleted + */ + public static boolean deleteContents(File dir) { + if ((null == dir) || !dir.canWrite()) { + return false; + } else if (dir.isDirectory()) { + File[] files = dir.listFiles(); + for (int i = 0; i < files.length; i++) { + if (!deleteContents(files[i]) || !files[i].delete()) { + return false; + } + } + } + return true; + } + + /** @return File temporary directory with the given prefix */ + public static File makeTempDir(String prefix) { + if (null == prefix) { + prefix = "tempDir"; + } + File tempFile = null; + for (int i = 0; i < 10; i++) { + try { + tempFile = File.createTempFile(prefix,"tmp"); + tempFile.delete(); + if (tempFile.mkdirs()) { + break; + } + tempFile = null; + } catch (IOException e) { + } + } + return tempFile; + } + /** + * Close stream with the usual checks. + * @param stream the InputStream to close - ignored if null + * @return null if closed without IOException, message otherwise + */ + public static String close(Writer stream) { + String result = null; + if (null != stream) { + try { + stream.close(); + } catch(IOException e) { + result = e.getMessage(); + } + } + return result; + } + + /** + * @param list the Object[] to test + * @return true if list is null or empty + */ + public static boolean isEmpty(Object[] list) { + return ((null == list) || (0 == list.length)); + } + + public static void closeSilently(InputStream in) { + if (null != in) { + try { + in.close(); + } catch (IOException e) { + // do nothing + } + } + } + + public static void closeSilently(Reader in) { + if (null != in) { + try { + in.close(); + } catch (IOException e) { + // do nothing + } + } + } + + /** + * Report whether actual has different members than expected + * @param expected the String[] of expected members (none null) + * @param actual the String[] of actual members + * @param sb StringBuffer sink for any differences in membership + * @return true if any diffs found and sink updated + */ + public static final boolean reportMemberDiffs(String[] expected, String[] actual, StringBuffer sb) { + expected = copy(expected); + actual = copy(actual); + int hits = 0; + for (int i = 0; i < expected.length; i++) { + int curHit = hits; + for (int j = 0; (curHit == hits) && (j < actual.length); j++) { + if (null == expected[i]) { + throw new IllegalArgumentException("null at " + i); + } + if (expected[i].equals(actual[j])) { + expected[i] = null; + actual[j] = null; + hits++; + } + } + } + if ((hits != expected.length) || (hits != actual.length)) { + sb.append("unexpected ["); + String prefix = ""; + for (int i = 0; i < actual.length; i++) { + if (null != actual[i]) { + sb.append(prefix); + prefix = ", "; + sb.append("\""); + sb.append(actual[i]); + sb.append("\""); + } + } + sb.append("] missing ["); + prefix = ""; + for (int i = 0; i < expected.length; i++) { + if (null != expected[i]) { + sb.append(prefix); + prefix = ", "; + sb.append("\""); + sb.append(expected[i]); + sb.append("\""); + } + } + sb.append("]"); + return true; + } + return false; + } + + private static final String[] copy(String[] ra) { + if (null == ra) { + return new String[0]; + } + String[] result = new String[ra.length]; + System.arraycopy(ra, 0, result, 0, ra.length); + return result; + } + + /** + * Support for OSGI bundles read from manifest files. + * Currently very limited, and will only support the subset of + * features that we use. + * sources: + * http://www-128.ibm.com/developerworks/library/os-ecl-osgi/index.html + * http://help.eclipse.org/help30/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/osgi/org/osgi/framework/Constants.html + */ + public static class OSGIBundle { + public static final Name BUNDLE_NAME = new Name("Bundle-Name"); + + public static final Name BUNDLE_SYMBOLIC_NAME = new Name( + "Bundle-SymbolicName"); + + public static final Name BUNDLE_VERSION = new Name("Bundle-Version"); + + public static final Name BUNDLE_ACTIVATOR = new Name("Bundle-Activator"); + + public static final Name BUNDLE_VENDOR = new Name("Bundle-Vendor"); + + public static final Name REQUIRE_BUNDLE = new Name("Require-Bundle"); + + public static final Name IMPORT_PACKAGE = new Name("Import-Package"); + + public static final Name BUNDLE_CLASSPATH = new Name("Bundle-ClassPath"); + + /** unmodifiable list of all valid OSGIBundle Name's */ + public static final List NAMES; + static { + ArrayList names = new ArrayList(); + names.add(BUNDLE_NAME); + names.add(BUNDLE_SYMBOLIC_NAME); + names.add(BUNDLE_VERSION); + names.add(BUNDLE_ACTIVATOR); + names.add(BUNDLE_VENDOR); + names.add(REQUIRE_BUNDLE); + names.add(IMPORT_PACKAGE); + names.add(BUNDLE_CLASSPATH); + NAMES = Collections.unmodifiableList(names); + } + + private final Manifest manifest; + + private final Attributes attributes; + + /** + * + * @param manifestInputStream + * the InputStream of the manifest.mf - will be closed. + * @throws IOException + * if unable to read or close the manifest input stream. + */ + public OSGIBundle(InputStream manifestInputStream) throws IOException { + manifest = new Manifest(); + manifest.read(manifestInputStream); + manifestInputStream.close(); + attributes = manifest.getMainAttributes(); + } + + public String getAttribute(Name attributeName) { + return attributes.getValue(attributeName); + } + + public String[] getClasspath() { + String cp = getAttribute(OSGIBundle.BUNDLE_CLASSPATH); + if (null == cp) { + return new String[0]; + } + StringTokenizer st = new StringTokenizer(cp, " ,"); + String[] result = new String[st.countTokens()]; + int i = 0; + while (st.hasMoreTokens()) { + result[i++] = st.nextToken(); + } + return result; + } + + /** + * XXX ugly/weak hack only handles a single version comma + * {name};bundle-version="[1.5.0,1.5.5]";resolution:=optional + * @return + */ + public RequiredBundle[] getRequiredBundles() { + String value = getAttribute(OSGIBundle.REQUIRE_BUNDLE); + if (null == value) { + return new RequiredBundle[0]; + } + StringTokenizer st = new StringTokenizer(value, " ,"); + RequiredBundle[] result = new RequiredBundle[st.countTokens()]; + int i = 0; + int skips = 0; + while (st.hasMoreTokens()) { + String token = st.nextToken(); + int first = token.indexOf("\""); + if (-1 != first) { + if (!st.hasMoreTokens()) { + throw new IllegalArgumentException(token); + } + // just assume only one quoted "," for version? + token += "," + st.nextToken(); + skips++; + } + result[i++] = new RequiredBundle(token); + } + if (skips > 0) { + RequiredBundle[] patch = new RequiredBundle[result.length-skips]; + System.arraycopy(result, 0, patch, 0, patch.length); + result = patch; + } + return result; + } + + /** + * Wrap each dependency on another bundle + */ + public static class RequiredBundle { + + /** unparsed entry text, for debugging */ + final String text; + + /** Symbolic name of the required bundle */ + final String name; + + /** if not null, then start/end versions of required bundle + * in the format of the corresponding manifest entry + */ + final String versions; + + /** if true, then required bundle is optional */ + final boolean optional; + + private RequiredBundle(String entry) { + text = entry; + StringTokenizer st = new StringTokenizer(entry, ";"); + name = st.nextToken(); + String vers = null; + String opt = null; + // bundle-version="[1.5.0,1.5.5]";resolution:=optiona + final String RESOLUTION = "resolution:="; + final String VERSION = "bundle-version=\""; + while (st.hasMoreTokens()) { + String token = st.nextToken(); + if (token.startsWith(VERSION)) { + int start = VERSION.length(); + int end = token.lastIndexOf("\""); + vers = token.substring(start, end); + // e.g., [1.5.0,1.5.5) + } else if (token.startsWith(RESOLUTION)) { + int start = RESOLUTION.length(); + int end = token.length(); + opt = token.substring(start, end); + } + } + versions = vers; + optional = "optional".equals(opt); + } + } + } +} + diff --git a/build/src/main/java./aspectj/internal/tools/build/package.html b/build/src/main/java./aspectj/internal/tools/build/package.html new file mode 100644 index 000000000..3fa443812 --- /dev/null +++ b/build/src/main/java./aspectj/internal/tools/build/package.html @@ -0,0 +1,12 @@ +<html> +<!-- todo +- backed off on doing product installer builds directly; + the one installer is now built using Ant. + +- +--> +<body> +The build taskdef relies on the classes in this package for +behavior independent of Ant. +</body> +</html> diff --git a/build/src/main/java/$installer$/org/aspectj/Main.java b/build/src/main/java/$installer$/org/aspectj/Main.java new file mode 100644 index 000000000..748b035ae --- /dev/null +++ b/build/src/main/java/$installer$/org/aspectj/Main.java @@ -0,0 +1,1861 @@ +/* ******************************************************************* + * Copyright (c) 2000-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 Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package $installer$.org.aspectj; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JEditorPane; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; + +/** + * Invoke the Installer gui. There are two ways to run without GUI by passing parameters to main: + * <ol> + * <li>pass <code>-text {pathToPropertiesFile}</code>: + * <ul> + * <li>"-text".equals(arg[0])</li> + * <li>arg[1] is the path to a properties file which defines name="output.dir" value="{path to output dir}" name="context.javaPath" + * value="{path to JDKDIR}", i.e,. + * + * <pre> + * output.dir=c:/latest + * "context.javaPath=c:/apps/jdk1.3.1 + * </pre> + * + * </li> + * <li>outputDir must be created and empty (i.e., no overwriting</li> + * <li>the VM being invoked should be the target vm</li> + * </ul> + * </li> + * <li>pass <code>-to {pathToTargetDir}</code>: + * <ul> + * <li>"-to".equals(arg[0])</li> + * <li>arg[1] is the path to a writable install directory.</li> + * </ul> + * </li> + */ +public class Main { + public static void main(String[] args) { + Options.loadArgs(args); + boolean hasGui = true; + Properties properties = new Properties(); + InputStream istream = null; + try { + istream = Main.class.getResourceAsStream(Installer.RESOURCE_DIR + "/properties.txt"); + if (istream == null) { + System.err.println("unable to load properties.txt using Main.class - exiting"); + Main.exit(-1); + } + properties.load(istream); + // when running outside GUI, load values into properties + // so that property-value resolution works + // (otherwise, could just set values below). + // XXX not sure if this indirection is actually needed. + if (null != Options.textProperties) { + istream.close(); + istream = new FileInputStream(Options.textProperties); + properties.load(istream); + hasGui = false; + } else if (null != Options.targetDir) { + String path = null; + try { + path = Options.targetDir.getCanonicalPath(); + } catch (IOException e) { + path = Options.targetDir.getAbsolutePath(); + } + String javaPath = ConfigureLauncherPane.getDefaultJavaHomeLocation(); + if (null == javaPath) { + System.err.println("using GUI - unable to find java"); + } else { + properties.setProperty("output.dir", path); + properties.setProperty("context.javaPath", javaPath); + hasGui = false; + } + } + } catch (IOException ioe) { + handleException(ioe); + } finally { + if (null != istream) { + try { + istream.close(); + } catch (IOException e) { + } // ignore + } + } + + try { + String className = (String) properties.get("installer.main.class"); + //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + Installer installer = (Installer) Class.forName(className).newInstance(); + InstallContext installerContext = new InstallContext(properties); + installerContext.setHasGui(hasGui); + installer.setContext(installerContext); + if (installerContext.hasGui()) { // let context force whether or not to run gui + installer.runGUI(); + } else { + // set output dir and java path in context after minimal validation + String propName = "output.dir"; + String propValue = properties.getProperty(propName); + if (null == propValue) { + throw new Exception("expecting property " + propName); + } + String outputDirName = propValue; + propName = "context.javaPath"; + propValue = properties.getProperty(propName); + if (null == propValue) { + throw new Exception("expecting property " + propName); + } + String javaPath = propValue; + File outputDir = new File(outputDirName); + if (!outputDir.isDirectory()) { + throw new Exception("not a dir outputDirName: " + outputDirName + " dir: " + outputDir); + } + if (!outputDir.canWrite()) { + throw new Exception("cannot write outputDirName: " + outputDirName + " dir: " + outputDir); + } + InstallContext context = installer.getContext(); // todo: why not use installerContext? + context.setOutputDir(outputDir); + context.javaPath = new File(javaPath); + // todo: check javaPath for ... bin/java? lib/rt.jar? + if (!outputDir.isDirectory() || !outputDir.canRead()) { + throw new Exception("invalid javaPath: " + javaPath); + } + // directly set context and run + WizardPane.setContext(installerContext); + installer.run(); + } + } catch (Exception e) { + handleException(e); + } + } + + public static void handleException(Throwable e) { + System.out.println("internal error: " + e.toString()); + e.printStackTrace(); + Main.exit(-1); + } + + /** indirection for System.exit - todo apply cleanup here as necessary */ + public static void exit(int value) { + System.exit(value); + } +} // class Main + +class Options { + public static boolean verbose = false; + public static String textProperties = null; + public static File targetDir = null; + public static boolean forceError1 = false; + public static boolean forceError2 = false; + public static boolean forceHandConfigure = false; + + public static void loadArgs(String[] args) { + if (args == null) { + return; + } + for (int i = 0; i < args.length; i++) { + String arg = args[i]; + if (arg == null) { + continue; + } + + if (arg.equals("-verbose")) { + verbose = true; + } else if (arg.equals("-forceError1")) { + forceError1 = true; + } else if (arg.equals("-forceError2")) { + forceError2 = true; + } else if (arg.equals("-forceHandConfigure")) { + forceHandConfigure = true; + } else if (arg.equals("-text")) { + if (i + 1 < args.length) { + textProperties = args[++i]; + } + } else if (arg.equals("-to")) { + String next = "no argument"; + if (i + 1 < args.length) { + next = args[++i]; + File targDir = new File(next); + if (targDir.isDirectory() && targDir.canWrite()) { + targetDir = targDir; + } + } + if (null == targetDir) { + System.err.println("invalid -to dir: " + next); + } + } + } + } +} + +/** tools installer installs the entire 1.1+ distribution */ +class ToolsInstaller extends Installer { + public String getTitle() { + return "Installer for AspectJ(TM)"; + } + + public String getPrefix() { + return "tools"; + } + + public String getReadmeFilename() { + return "README-AspectJ.html"; + } + + public ToolsInstaller() { + InstallPane installPane = new InstallPane(true); + setInstallPane(installPane); + panes = new WizardPane[] { new IntroPane(), new ConfigureLauncherPane(), new LocationPane(), installPane, new FinishPane() }; + } +} + +class DocsInstaller extends Installer { + public String getTitle() { + return "AspectJ(TM) Documentation and Examples Installer"; + } + + public String getPrefix() { + return "docs"; + } + + public DocsInstaller() { + InstallPane installPane = new InstallPane(false); + setInstallPane(installPane); + panes = new WizardPane[] { new IntroPane(), new LocationPane(), installPane, new FinishPane() }; + } +} + +class AJDEForJBuilderInstaller extends Installer { + public String getTitle() { + return "AspectJ(TM) Support for JBuilder"; + } + + public String getPrefix() { + return "ajdeForJBuilder"; + } + + public AJDEForJBuilderInstaller() { + InstallPane installPane = new InstallPane(false); + setInstallPane(installPane); + panes = new WizardPane[] { new IntroPane(), new LocationPane() { + public String getDefaultLocation() { + if (context.onWindows()) { + // check some default locations + String[] paths = { "c:\\JBuilder6\\lib\\ext", "c:\\apps\\JBuilder6\\lib\\ext", + "c:\\Program Files\\JBuilder6\\lib\\ext" }; + int pathIndex = 0; + for (; pathIndex < paths.length; pathIndex++) { + if (new File(paths[pathIndex]).exists()) { + return paths[pathIndex]; + } + } + return "c:\\JBuilder6\\lib\\ext"; + } else { + return "/usr/JBuilder6/lib/ext"; + } + } + + /** + * Make sure that the old jar file gets removed. + */ + public void verify() { + File jbuilder = new File(location.getText() + "/../../lib/jbuilder.jar"); + if (!jbuilder.exists() && hasGui()) { + int ret = JOptionPane.showConfirmDialog(frame, "The location you specified does not seem to be a " + + "valid JBuilder install directory." + " Continue?", "Confirm Install", JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (ret != JOptionPane.YES_OPTION) { + Main.exit(-1); + } else { + // do nothing + } + } + + File oldFile = new File(location.getText() + "/ajbuilder.jar"); + if (oldFile.exists() && hasGui()) { + int ret = JOptionPane.showConfirmDialog(frame, + "This old version of AJDE for JBuilder (\"ajbuilder.jar\") exists" + + " and must be removed from the install directory." + " OK to delete?", "Confirm Delete", + JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); + if (ret != JOptionPane.YES_OPTION) { + Main.exit(-1); + } else { + oldFile.delete(); + } + } + } + }, installPane, new FinishPane() }; + } +} + +class AJDEForForteInstaller extends Installer { + public String getTitle() { + return "AspectJ(TM) Support for Forte 4J"; + } + + public String getPrefix() { + return "ajdeForForte"; + } + + private String installLoc = ""; + + public AJDEForForteInstaller() { + InstallPane installPane = new InstallPane(false); + setInstallPane(installPane); + panes = new WizardPane[] { new IntroPane(), new LocationPane() { + public String getDefaultLocation() { + if (context.onWindows()) { + // check some default locations + String[] paths = { "c:\\forte4j\\modules", "c:\\apps\\forte4j\\modules", "c:\\Program Files\\forte4j\\modules" }; + int pathIndex = 0; + for (; pathIndex < paths.length; pathIndex++) { + if (new File(paths[pathIndex]).exists()) { + return paths[pathIndex]; + } + } + return "c:\\forte4j\\modules"; + } else { + return "/usr/forte4j/modules"; + } + } + + public void verify() { + File forte = new File(location.getText() + "/../lib/openide.jar"); + installLoc = location.getText(); + if (!forte.exists() && hasGui()) { + int ret = JOptionPane.showConfirmDialog(frame, "The location you specified does not seem to be a " + + "valid Forte install directory." + " Continue?", "Confirm Install", JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (ret != JOptionPane.YES_OPTION) { + Main.exit(-1); + } else { + // do nothing + } + } + } + }, installPane, new FinishPane() { + public void finalActions() { // todo verify dir ../lib/ext exists? + // !!! this should be done with two install locations, not by moving a file + new File(installLoc + "/../lib/ext/aspectjrt.jar").delete(); + new File(installLoc + "/aspectjrt.jar").renameTo(new File((installLoc + "/../lib/ext/aspectjrt.jar"))); + new File(installLoc + "/aspectjrt.jar").delete(); + } + } }; + } +} + +class SrcInstaller extends Installer { + public String getTitle() { + return "AspectJ(TM) Compiler and Core Tools Sources Installer"; + } + + public String getPrefix() { + return "sources"; + } + + public SrcInstaller() { + InstallPane installPane = new InstallPane(false); + setInstallPane(installPane); + panes = new WizardPane[] { new IntroPane(), new LocationPane(), installPane, new FinishPane() }; + } +} + +abstract class Installer { + static final String EXIT_MESSAGE = "Are you sure you want to cancel the installation?"; + static final String EXIT_TITLE = "Exiting installer"; + /** + * relative directory in jar from package $installer$.org.aspectj for loading resources - todo must be tracked during build + */ + public static final String RESOURCE_DIR = "resources"; + + JFrame frame; + InstallContext context; + /** special pane that actually does the installation */ + InstallPane installPane; + + public Installer() { + } + + protected void setInstallPane(InstallPane installPane) { + this.installPane = installPane; + } + + public InstallPane getInstallPane() { + return installPane; + } + + /** directly run the install pane, if any */ + public void run() { + if (null != installPane) { + installPane.run(); + } + } + + public abstract String getPrefix(); + + public String getReadmeFilename() { + return "README-" + getPrefix().toUpperCase() + ".html"; + } + + public void setContext(InstallContext context) { + this.context = context; + context.installer = this; + } + + public InstallContext getContext() { + return context; + } + + public String getTitle() { + return "AspectJ(TM) Installer"; + } + + public int getWidth() { + return 640; + } + + public int getHeight() { + return 460; + } + + protected WizardPane[] panes = new WizardPane[0]; + + public WizardPane[] getPanes() { + return panes; + } + + public int findPaneIndex(WizardPane pane) { + for (int i = 0; i < panes.length; i++) { + if (panes[i] == pane) { + return i; + } + } + return -1; + } + + Component header, footer, body; + + public void runGUI() { + frame = new JFrame(getTitle()); + WindowListener wl = new WindowAdapter() { + public void windowClosing(WindowEvent arg0) { + Main.exit(-1); // -1 unless exiting through done button + } + }; + frame.addWindowListener(wl); + + if (Options.forceError1) { + throw new RuntimeException("forced error1 for testing purposes"); + } + + Dimension size = Toolkit.getDefaultToolkit().getScreenSize(); + + int x = (int) (size.getWidth() - getWidth()) / 2; + int y = (int) (size.getHeight() - getHeight()) / 2; + + //include a few sanity checks on starting position + if (x < 0) { + x = 0; + } + if (x > 600) { + x = 600; + } + if (y < 0) { + y = 0; + } + if (y > 400) { + y = 400; + } + + frame.setLocation(x, y); + frame.setSize(getWidth(), getHeight()); + moveToPane(getPanes()[0]); + frame.setVisible(true); + } + + public void moveToPane(WizardPane pane) { + WizardPane.setContext(this.context); + + Dimension size = frame.getContentPane().getSize(); + + JPanel contents = new JPanel(); + contents.setLayout(new BorderLayout()); + header = makeHeader(); + contents.add(header, BorderLayout.NORTH); + + body = pane.getPanel(); + contents.add(body, BorderLayout.CENTER); + + footer = pane.getButtons(); + contents.add(footer, BorderLayout.SOUTH); + + contents.revalidate(); + contents.setSize(size); + + frame.setContentPane(contents); + + //XXX deal with threading here? + pane.run(); + } + + public Icon loadImage(String name) { + return new javax.swing.ImageIcon(this.getClass().getResource(name)); + } + + public Component makeHeader() { + return new JLabel(loadImage(Installer.RESOURCE_DIR + "/aspectjBanner.gif")); + } + + public ActionListener makeNextAction(final WizardPane pane) { + int nextPaneIndex = findPaneIndex(pane) + 1; + if (nextPaneIndex >= getPanes().length) { + return null; + } + + final WizardPane nextPane = getPanes()[nextPaneIndex]; + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + pane.finish(); + moveToPane(nextPane); + } + }; + } + + public ActionListener makeBackAction(final WizardPane pane) { + int nextPaneIndex = findPaneIndex(pane) - 1; + if (nextPaneIndex < 0) { + return null; + } + + final WizardPane nextPane = getPanes()[nextPaneIndex]; + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + moveToPane(nextPane); + } + }; + } + + public ActionListener makeCancelAction(WizardPane pane) { + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + int ret = JOptionPane.showConfirmDialog(frame, EXIT_MESSAGE, EXIT_TITLE, JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (ret == JOptionPane.YES_OPTION) { + Main.exit(-1); + } + } + }; + } + + public ActionListener makeFinishAction(WizardPane pane) { + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + Main.exit(0); + } + }; + } +} + +// willing to go up to 3 levels deep to find either jre or jdk + +// jre\[*\]lib\ext +// jdk*\lib\tools.jar + +/***** + * final static int MAX_DEPTH = 4; public static void findPaths(String prefix, File currentDir, int currentDepth) { if (currentDepth + * > MAX_DEPTH) return; if (!currentDir.exists() || !currentDir.isDirectory()) return; File [] files = currentDir.listFiles(); if + * (files == null) return; for (int i=0; i<files.length; i++) { if (files[i] == null) continue; if (!files[i].isDirectory()) + * continue; if (files[i].getName().startsWith(prefix)) { System.out.println("found: " + files[i]); } else { findPaths(prefix, + * files[i], currentDepth + 1); } } } + * + * public static void findPaths(String prefix) { File [] files = File.listRoots(); for (int i=1; i<files.length; i++) { if + * (!files[i].isDirectory()) continue; if (files[i].getName().toLowerCase().startsWith(prefix)) { System.out.println("found: " + + * files[i]); } else { findPaths(prefix, files[i], 1); } } } + *****/ + +class InstallContext { + public InstallContext(Map properties) { + this.properties = properties; + properties.put("user.home", System.getProperty("user.home")); + //System.out.println("new install context"); + } + + private File outputDir; + + public void setOutputDir(File outputDir) { + this.outputDir = outputDir; + + properties.put("installer.output.dir", outputDir.getAbsolutePath()); + properties.put("installer.output.dir.bin", new File(outputDir, "bin").getAbsolutePath()); + properties.put("installer.output.dir.doc", new File(outputDir, "doc").getAbsolutePath()); + properties.put("installer.output.aspectjrt", new File(new File(outputDir, "lib"), "aspectjrt.jar").getAbsolutePath()); + properties.put("installer.output.readme", new File(outputDir, installer.getReadmeFilename()).getAbsolutePath()); + } + + public File getOutputDir() { + return outputDir; + } + + private boolean hasGui; + public File javaPath; + public File toolsJarPath; + + public Installer installer; + + private Map properties; + + public boolean hasGui() { + return hasGui; + } + + public void setHasGui(boolean hasGui) { + if (this.hasGui != hasGui) { + this.hasGui = hasGui; + } + } + + public Font getFont() { + return new Font("Serif", Font.PLAIN, 14); + } + + public String getOS() { + return System.getProperty("os.name"); + } + + public boolean onOS2() { + return getOS().equals("OS2") || getOS().equals("OS/2"); + } + + public boolean onWindows() { + return getOS().startsWith("Windows") || onOS2(); + } + + public boolean onWindowsPro() { + return getOS().equals("Windows NT") || getOS().equals("Windows 2000") || getOS().equals("Windows XP") + || getOS().equals("Windows Vista") || getOS().equals("Windows 7") || getOS().startsWith("Windows 8"); + } + + public boolean onMacintosh() { + return getOS().startsWith("Mac"); + } + + public boolean onUnix() { + return !onWindows(); + } + + static final String[] TEXT_EXTENSIONS = { ".txt", ".text", ".htm", ".html", ".java", ".ajava", "README", ".lst" }; + + public boolean isTextFile(File file) { + String name = file.getName(); + + for (int i = 0; i < TEXT_EXTENSIONS.length; i++) { + if (name.endsWith(TEXT_EXTENSIONS[i])) { + return true; + } + } + + return false; + } + + public void handleException(Throwable e) { + System.out.println("internal error: " + e.toString()); + e.printStackTrace(); + if (hasGui()) { + JOptionPane.showMessageDialog(installer.frame, e.toString(), "Unexpected Exception", JOptionPane.ERROR_MESSAGE); + } + } + + final static String OVERWRITE_MESSAGE = "Overwrite file "; + final static String OVERWRITE_TITLE = "Overwrite?"; + + final static String[] OVERWRITE_OPTIONS = { "Yes", "No", "Yes to all" //, "No to all" + }; + + final static int OVERWRITE_YES = 0; + final static int OVERWRITE_NO = 1; + final static int OVERWRITE_ALL = 2; + //final static int OVERWRITE_NONE = 3; + + int overwriteState = OVERWRITE_NO; + + boolean shouldOverwrite(final File file) { + //System.out.println("overwrite: " + file + " state " + overwriteState); + if (overwriteState == OVERWRITE_ALL) { + return true; + //if (overwriteState == OVERWRITE_NONE) return false; + } + + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + int ret = JOptionPane.showOptionDialog(installer.frame, OVERWRITE_MESSAGE + file.getPath(), OVERWRITE_TITLE, + JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, OVERWRITE_OPTIONS, + OVERWRITE_OPTIONS[OVERWRITE_YES]); + + overwriteState = ret; + } + }); + } catch (InvocationTargetException ite) { + handleException(ite.getTargetException()); + } catch (InterruptedException ie) { + } + + return overwriteState == OVERWRITE_YES || overwriteState == OVERWRITE_ALL; + } + + public Map getProperties() { + return properties; + } +} + +abstract class WizardPane { + static InstallContext context; + + protected JButton backButton = null; + protected JButton nextButton = null; + protected JButton cancelButton = null; + + public static void setContext(InstallContext con) { + context = con; + } + + public abstract JPanel makePanel(); + + protected JTextArea makeTextArea(String data) { + JTextArea text = new JTextArea(data); + text.setOpaque(false); + text.setFont(context.getFont()); + text.setEditable(false); + return text; + } + + /** @return false only if there is an InstallContext saying there is no GUI */ + protected boolean hasGui() { + final InstallContext icontext = context; + return ((null == icontext) || icontext.hasGui()); + } + + public static String stringFromStream(InputStream stream) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "US-ASCII")); + + StringBuffer ret = new StringBuffer(); + int data; + while ((data = reader.read()) != -1) { + ret.append((char) data); + } + return ret.toString(); + } + + public static String removeHead(String text) { + int startIndex = text.indexOf("<head>"); + int stopIndex = text.indexOf("</head>"); + if (startIndex == -1 || stopIndex == -1) { + return text; + } + stopIndex += 7; + return text.substring(0, startIndex) + text.substring(stopIndex); + } + + static String styleHeader = "<head></head>";/* + * <STYLE TYPE=\"text/css\"><!--\n" + " h2 {\n" + " font-size: x-large;\n" + + * " font-family: Serif;\n" + " font-weight: normal;\n" + " }\n" + " p {\n" + + * " font-family: Serif;\n" + " font-weight: normal;\n" + //" color:black;\n" + * + "}</head>\n"; + */ + + public static String applyProperties(String text, Map map) { + // ${name} -> map.get(name).toString() + int lastIndex = 0; + StringBuffer buf = new StringBuffer(); + + int startIndex; + while ((startIndex = text.indexOf("${", lastIndex)) != -1) { + int endIndex = text.indexOf('}', startIndex); + //XXX internal error here + if (endIndex == -1) { + break; + } + buf.append(text.substring(lastIndex, startIndex)); + String key = text.substring(startIndex + 2, endIndex); + lastIndex = endIndex + 1; + Object replaceText = (map == null ? null : map.get(key)); + //System.out.println("key: " + key + " -> " + replaceText); + if (replaceText == null) { + replaceText = "NOT_FOUND"; + } + buf.append(replaceText.toString()); + } + buf.append(text.substring(lastIndex)); + + return buf.toString(); + } + + public static String applyProperties(String text) { + return applyProperties(text, (context == null ? null : context.getProperties())); + } + + protected String loadText(String filename) { + String fullname = Installer.RESOURCE_DIR + "/" + filename; + //context.installer.getPrefix() + "-" + filename; + + try { + String text = stringFromStream(getClass().getResourceAsStream(fullname)); + text = styleHeader + removeHead(text); + text = applyProperties(text); + //System.out.println(text); + return text; + } catch (IOException e) { + context.handleException(e); + return ""; + } + } + + protected JEditorPane makeHTMLArea(String filename) { + JEditorPane editorPane = new JEditorPane("text/html", loadText(filename)); + /* + * { public void paint(Graphics g) { Graphics2D g2 = (Graphics2D)g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + * RenderingHints.VALUE_ANTIALIAS_ON); super.paint(g2); } }; + */ + editorPane.setEditable(false); + editorPane.setOpaque(false); + return editorPane; + } + + protected void setHTMLArea(JEditorPane pane, String filename) { + pane.setText(loadText(filename)); + } + + protected JPanel makeLocationBox(String label, JTextField textField, JButton browseButton) { + JPanel box = new JPanel(); + box.setLayout(new BoxLayout(box, BoxLayout.X_AXIS)); + + textField.setFont(context.getFont()); + textField.selectAll(); + box.add(textField); + + box.add(browseButton); + Border border = BorderFactory.createTitledBorder(label); + final int INSET = 8; + border = new CompoundBorder(border, new EmptyBorder(1, INSET, INSET, INSET)); + box.setBorder(border); + + return box; + } + + private JPanel panel = null; + + public JPanel getPanel() { + if (panel == null) { + panel = makePanel(); + panel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); + } + return panel; + } + + protected void setListener(JButton button, ActionListener listener) { + if (listener == null) { + button.setEnabled(false); + } else { + button.addActionListener(listener); + } + } + + protected Component makeButtons(Installer installer) { + JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + + backButton = new JButton("Back"); + setListener(backButton, installer.makeBackAction(this)); + panel.add(backButton); + + nextButton = new JButton("Next"); + setListener(nextButton, installer.makeNextAction(this)); + panel.add(nextButton); //.setDefaultCapable(true); + + JLabel space = new JLabel(); + space.setPreferredSize(new Dimension(20, 0)); + panel.add(space); + + cancelButton = new JButton("Cancel"); + setListener(cancelButton, installer.makeCancelAction(this)); + panel.add(cancelButton); + + return panel; + } + + private Component buttons = null; + + public Component getButtons() { + if (buttons == null) { + buttons = makeButtons(context.installer); + //buttons.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); + } + context.installer.frame.getRootPane().setDefaultButton(nextButton); + return buttons; + } + + public void finish() { + if (Options.forceError2) { + throw new RuntimeException("forced error2 for testing purposes"); + } + } + + public void run() { + } +} + +class IntroPane extends WizardPane { + public JPanel makePanel() { + Component text = makeHTMLArea("intro.html"); + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(text); + return panel; + } +} + +class FinishPane extends WizardPane { + public JPanel makePanel() { + Component text = makeHTMLArea("finish.html"); + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(text); + finalActions(); + return panel; + } + + public Component makeButtons(Installer installer) { + Component ret = super.makeButtons(installer); + nextButton.setText("Finish"); + nextButton.setEnabled(true); + nextButton.addActionListener(installer.makeFinishAction(this)); + backButton.setEnabled(false); + cancelButton.setEnabled(false); + return ret; + } + + public void finalActions() { + } +} + +class LocationPane extends WizardPane implements ActionListener { + //XXX need more sophisticated default location finding + //XXX would like to find the place they last chose... + public String getDefaultLocation() { + if (context.onWindows()) { + return "c:\\aspectj1.9"; + } else { + return new File(System.getProperty("user.home"), "aspectj1.9").getAbsolutePath(); + } + } + + protected JTextField location; + + public JPanel makePanel() { + Component text = makeHTMLArea("location.html"); + + location = new JTextField(getDefaultLocation()); + JButton browse = new JButton("Browse..."); + browse.addActionListener(this); + + JPanel locationBox = makeLocationBox("installation directory", location, browse); + + GridBagLayout bag = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + + JPanel panel = new JPanel(bag); + c.fill = GridBagConstraints.BOTH; + c.weightx = 1.0; + c.gridwidth = GridBagConstraints.REMAINDER; + bag.setConstraints(text, c); + panel.add(text); + + c.weighty = 1.0; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridwidth = GridBagConstraints.REMAINDER; + bag.setConstraints(locationBox, c); + panel.add(locationBox); + + //XXX set next button to read install + //context.nextButton.setText("Install"); + + return panel; + } + + public Component makeButtons(Installer installer) { + Component ret = super.makeButtons(installer); + nextButton.setText("Install"); + return ret; + } + + public void actionPerformed(ActionEvent e) { + JFileChooser chooser = new JFileChooser(); // { + // public void approveSelection() { + // System.out.println("approved selection"); + // } + //}; //field.getText()); + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + int returnVal = chooser.showDialog(location, "Select"); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = chooser.getSelectedFile(); + if (!file.isDirectory()) { + file = file.getParentFile(); + } + String name = file.getPath(); + location.setText(name); + location.selectAll(); + } + } + + /** + * Override to do any additional checks. + */ + protected void verify() { + } + + public void finish() { + verify(); + context.setOutputDir(new File(location.getText())); + } +} + +class ConfigureLauncherPane extends WizardPane { + /* + * //XXX check that the returned file is valid public String getDefaultJavaLocation() { String name = "java"; if + * (context.onWindows()) name += ".exe"; + * + * if (Options.verbose) { System.out.println("java.home: " + System.getProperty("java.home")); System.out.println(" java: " + + * new File(new File(System.getProperty("java.home"), "bin"), name)); System.out.println(" java: " + new File(new + * File(System.getProperty("java.home"), "bin"), name).getPath()); } + * + * return new File(new File(System.getProperty("java.home"), "bin"), name).getPath(); } + */ + + public static String getDefaultJavaHomeLocation() { + if (!Options.forceHandConfigure) { + File javaHome = findJavaHome(); + if (javaHome != null) { + return javaHome.getPath(); + } + } + return null; + } + + public void chooseFile(JTextField field) { + JFileChooser chooser = new JFileChooser(); //field.getText()); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + int returnVal = chooser.showDialog(field, "Select"); + if (returnVal == JFileChooser.APPROVE_OPTION) { + String name = chooser.getSelectedFile().getPath(); + field.setText(name); + field.selectAll(); + } + } + + public ActionListener makeJavaLocationBrowseListener() { + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + chooseFile(javaLocation); + } + }; + } + + // public ActionListener makeToolsJarLocationBrowseListener() { + // return new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // chooseFile(toolsJarLocation); + // } + // }; + // } + + private JTextField javaLocation; + + //private JTextField toolsJarLocation; + + public JPanel makePanel() { + String javaPath = getDefaultJavaHomeLocation(); + //String toolsJarPath = getDefaultToolsJarLocation(); + + Component text; + if (javaPath == null) { + javaPath = "<java home not found>"; + text = makeHTMLArea("configure-hand.html"); + } else { + text = makeHTMLArea("configure-auto.html"); + } + + javaLocation = new JTextField(javaPath); + JButton javaLocationBrowse = new JButton("Browse..."); + javaLocationBrowse.addActionListener(makeJavaLocationBrowseListener()); + + JPanel javaLocationBox = makeLocationBox("java home directory", javaLocation, javaLocationBrowse); + + // toolsJarLocation = new JTextField(toolsJarPath); + // JButton toolsJarLocationBrowse = new JButton("Browse..."); + // toolsJarLocationBrowse.addActionListener(makeToolsJarLocationBrowseListener()); + + // JPanel toolsJarLocationBox = makeLocationBox("full path to tools.jar", toolsJarLocation, toolsJarLocationBrowse); + + GridBagLayout bag = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + + JPanel panel = new JPanel(bag); + c.fill = GridBagConstraints.BOTH; + c.weightx = 1.0; + c.weighty = 1.0; + //c.ipady = 10; + c.gridwidth = GridBagConstraints.REMAINDER; + bag.setConstraints(text, c); + panel.add(text); + + c.weighty = 0.0; + //c.fill = GridBagConstraints.VERTICAL; + bag.setConstraints(javaLocationBox, c); + panel.add(javaLocationBox); + + // c.weighty = 0.25; + // JLabel space = new JLabel(); + // bag.setConstraints(space, c); + // panel.add(space); + + // c.weighty = 0.0; + // bag.setConstraints(toolsJarLocationBox, c); + // panel.add(toolsJarLocationBox); + + c.weighty = 0.5; + JLabel space = new JLabel(); + bag.setConstraints(space, c); + panel.add(space); + + return panel; + } + + public void finish() { + context.javaPath = new File(javaLocation.getText()); + // context.toolsJarPath = new File(toolsJarLocation.getText()); + + //XXX need much more work on helping the user get these paths right + // if (context.javaPath.isDirectory()) { + // context.javaPath = new File(context.javaPath, "java"); + // } + // if (context.toolsJarPath.isDirectory()) { + // context.toolsJarPath = new File(context.toolsJarPath, "tools.jar"); + // } + } + + //XXX add user.home to prefixes in a rational way + public static final String[] windowsPaths = { "c:\\jdk", "c:\\apps\\jdk", "${user.home}\\jdk" }; + + public static final String[] unixPaths = { "/usr/local/bin/jdk", "/usr/bin/jdk", "/usr/bin/jdk", "${user.home}/jdk" }; + + public static final String[] suffixes = { "1.3.1", "1.3", "1.2", "13", "12", "2", "", "1.4" }; + + public static boolean windows = true; + + public static boolean isLegalJavaHome(File home) { + File bin = new File(home, "bin"); + return new File(bin, "java").isFile() || new File(bin, "java.exe").isFile(); + } + + public static boolean isLegalJDKHome(File home) { + File lib = new File(home, "lib"); + return new File(lib, "tools.jar").isFile(); + } + + public static File findJavaHome() { + String s = System.getProperty("java.home"); + File javaHome = null; + if (s != null) { + javaHome = new File(s); + if (isLegalJDKHome(javaHome)) { + return javaHome; + } + if (isLegalJavaHome(javaHome)) { + File parent = javaHome.getParentFile(); + if (parent != null && isLegalJDKHome(parent)) { + return parent; + } + } + } + + String[] paths; + if (windows) { + paths = windowsPaths; + } else { + paths = unixPaths; + } + + for (int suffixIndex = 0; suffixIndex < suffixes.length; suffixIndex++) { + String suffix = suffixes[suffixIndex]; + for (int prefixIndex = 0; prefixIndex < paths.length; prefixIndex++) { + String prefix = paths[prefixIndex]; + prefix = applyProperties(prefix); + File test = new File(prefix + suffix); + if (isLegalJavaHome(test)) { + if (isLegalJDKHome(test)) { + return test; + } else if (javaHome == null) { + javaHome = test; + } + } + } + } + return javaHome; + } +} + +class InstallPane extends WizardPane { + private JProgressBar progressBar; + private JTextField progressItem; + private JEditorPane message; + + private boolean makeLaunchScripts = false; + + public InstallPane(boolean makeLaunchScripts) { + this.makeLaunchScripts = makeLaunchScripts; + } + + public JPanel makePanel() { + message = makeHTMLArea("install-start.html"); + + progressBar = new JProgressBar(); + + progressItem = new JTextField(); + progressItem.setOpaque(false); + progressItem.setFont(context.getFont()); + progressItem.setEditable(false); + + GridBagLayout bag = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + + JPanel panel = new JPanel(bag); + c.fill = GridBagConstraints.BOTH; + c.weightx = 1.0; + c.weighty = 1.0; + //c.ipady = 10; + c.gridwidth = GridBagConstraints.REMAINDER; + bag.setConstraints(message, c); + panel.add(message); + + c.weighty = 0.0; + //c.fill = GridBagConstraints.VERTICAL; + bag.setConstraints(progressBar, c); + panel.add(progressBar); + + c.weighty = 0.1; + JLabel space = new JLabel(); + bag.setConstraints(space, c); + panel.add(space); + + c.weighty = 0.0; + bag.setConstraints(progressItem, c); + panel.add(progressItem); + + c.weighty = 0.5; + space = new JLabel(); + bag.setConstraints(space, c); + panel.add(space); + + return panel; + } + + class InstallRunner implements Runnable { + public InstallRunner() { + } + + public void run() { + try { + new CurrentJarUnpacker(context, InstallPane.this).unpack(Installer.RESOURCE_DIR + "/contents.txt", + context.getOutputDir()); + + if (makeLaunchScripts) { + LaunchScriptMaker lsm = new LaunchScriptMaker(context); + lsm.writeScript("ajc"); + lsm.writeScript("ajdoc"); + //lsm.writeScript("ajdb"); + lsm.writeScript("ajbrowser"); + + // Moved to the bin dir in 1.2.1 + // we should now come back and make the generation of this + // script uniform with those above. + lsm.writeAJLaunchScript("aj", false); + lsm.writeAJLaunchScript("aj5", true); + } + if (hasGui()) { + progressBar.setValue(100); + setHTMLArea(message, "install-finish.html"); + } + } catch (IOException ioe) { + context.handleException(ioe); + } + + if (hasGui()) { + cancelButton.setEnabled(false); + nextButton.setEnabled(true); + } + } + } + + public Component makeButtons(Installer installer) { + Component ret = super.makeButtons(installer); + //nextButton.setText("Finish"); + nextButton.setEnabled(false); + //nextButton.addActionListener(installer.makeFinishAction(this)); + backButton.setEnabled(false); + return ret; + } + + public void run() { + Thread thread = new Thread(new InstallRunner()); + thread.start(); + } + + public void progressMessage(final String message) { + if (!hasGui()) { + return; + } + try { + //XXX performance tradeoff between invokeAndWait and invokeLater... + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + progressItem.setText(message); + } + }); + } catch (InvocationTargetException ite) { + context.handleException(ite.getTargetException()); + } catch (InterruptedException ie) { + } + } + + int nBytes = 0; + int bytesWritten = 0; + + public void progressBytesWritten(int bytes) { + if (!hasGui()) { + return; + } + bytesWritten += bytes; + final int PCT = (int) (100.0 * bytesWritten / nBytes); + //System.out.println("bytesWritten: " + bytesWritten); + try { + //XXX performance tradeoff between invokeAndWait and invokeLater... + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + progressBar.setValue(PCT); + } + }); + } catch (InvocationTargetException ite) { + context.handleException(ite.getTargetException()); + } catch (InterruptedException ie) { + } + } +} + +class CurrentJarUnpacker { + InstallContext context; + InstallPane installPane; + + public CurrentJarUnpacker(InstallContext context, InstallPane installPane) { + this.context = context; + this.installPane = installPane; + } + + public File makeOutputFile(String name, File outputFile) { + int index; + int lastIndex = 0; + + while ((index = name.indexOf('/', lastIndex)) != -1) { + outputFile = new File(outputFile, name.substring(lastIndex, index)); + lastIndex = index + 1; + } + + return new File(outputFile, name.substring(lastIndex)); + } + + final static int BUF_SIZE = 4096; + + public void writeStream(InputStream zis, File outputFile) throws IOException { + if (outputFile.exists()) { + if (!context.shouldOverwrite(outputFile)) { + return; + } + } + + installPane.progressMessage("writing " + outputFile.getAbsolutePath()); + + outputFile.getParentFile().mkdirs(); + + if (context.isTextFile(outputFile)) { + writeTextStream(zis, outputFile); + } else { + writeBinaryStream(zis, outputFile); + } + } + + public void writeBinaryStream(InputStream zis, File outputFile) throws IOException { + byte[] buffer = new byte[BUF_SIZE]; + int nRead = 0; + + OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile)); + + while ((nRead = zis.read(buffer)) != -1) { + os.write(buffer, 0, nRead); + installPane.progressBytesWritten(nRead); + } + os.close(); + } + + public void writeTextStream(InputStream zis, File outputFile) throws IOException { + BufferedWriter os = new BufferedWriter(new FileWriter(outputFile)); + BufferedReader r = new BufferedReader(new InputStreamReader(zis, "US-ASCII")); + + String l; + while ((l = r.readLine()) != null) { + os.write(l); + os.newLine(); + installPane.progressBytesWritten(l.length() + 1); + } + os.close(); + } + + public void writeResource(String name, File outputDir) throws IOException { + File outputFile = makeOutputFile(name, outputDir); + //System.out.println("finding name: " + name); + writeStream(getClass().getResourceAsStream("/" + name), outputFile); + } + + public void unpack(String contentsName, File outputDir) throws IOException { + URL url = getClass().getResource(contentsName); + InputStream stream = url.openStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "US-ASCII")); + + String line = reader.readLine(); + installPane.nBytes = Integer.parseInt(line); + + while ((line = reader.readLine()) != null) { + writeResource(line, outputDir); + } + + installPane.progressMessage("done writing"); + } +} + +class LaunchScriptMaker { + static final String toolsPackage = "org.aspectj.tools"; + + InstallContext context; + + public LaunchScriptMaker(InstallContext context) { + this.context = context; + } + + /** + * + */ + public void writeAJLaunchScript(String name, boolean isJava5) throws IOException { + if (!context.onUnix()) { + if (context.onOS2()) { + name += ".cmd"; + } else if (context.onWindows()) { + name += ".bat"; + } + } + + File destDir = new File(context.getOutputDir(), "bin"); + destDir.mkdirs(); + File file = new File(destDir, name); + + PrintStream ps = getPrintStream(file); + writeAJLaunchScriptContent(ps, isJava5); + ps.close(); + + if (context.onUnix()) { + makeExecutable(file); + } + } + + /** + * @param ps + */ + private void writeAJLaunchScriptContent(PrintStream ps, boolean isJava5) { + if (context.onUnix()) { + writeUnixHeader(ps); + if (isJava5) { + writeAJ5UnixLaunchLine(ps); + } else { + writeAJUnixLaunchLine(ps); + } + } else { + writeWindowsHeader(ps); + if (isJava5) { + writeAJ5WindowsLaunchLine(ps); + } else { + writeAJWindowsLaunchLine(ps); + } + } + } + + /** + * @param ps + */ + private void writeAJWindowsLaunchLine(PrintStream ps) { + ps.println("\"%JAVA_HOME%\\bin\\java\" -classpath " + "\"%ASPECTJ_HOME%\\lib\\aspectjweaver.jar\"" + + " \"-Djava.system.class.loader=org.aspectj.weaver.loadtime.WeavingURLClassLoader\"" + + " \"-Daj.class.path=%ASPECTPATH%;%CLASSPATH%\"" + " \"-Daj.aspect.path=%ASPECTPATH%\"" + " " + + makeScriptArgs(false)); + } + + /** + * @param ps + */ + private void writeAJ5WindowsLaunchLine(PrintStream ps) { + ps.println("\"%JAVA_HOME%\\bin\\java\" -classpath " + "\"%ASPECTJ_HOME%\\lib\\aspectjweaver.jar;%CLASSPATH%\"" + + " \"-javaagent:%ASPECTJ_HOME%\\lib\\aspectjweaver.jar\"" + " " + makeScriptArgs(false)); + } + + /** + * @param ps + */ + private void writeAJUnixLaunchLine(PrintStream ps) { + ps.println("\"$JAVA_HOME/bin/java\" -classpath" + " \"$ASPECTJ_HOME/lib/aspectjweaver.jar\"" + + " \"-Djava.system.class.loader=org.aspectj.weaver.loadtime.WeavingURLClassLoader\"" + + " \"-Daj.class.path=$ASPECTPATH:$CLASSPATH\"" + " \"-Daj.aspect.path=$ASPECTPATH\"" + " " + makeScriptArgs(true)); + } + + /** + * @param ps + */ + private void writeAJ5UnixLaunchLine(PrintStream ps) { + ps.println("\"$JAVA_HOME/bin/java\" -classpath" + " \"$ASPECTJ_HOME/lib/aspectjweaver.jar:$CLASSPATH\"" + + " \"-javaagent:$ASPECTJ_HOME/lib/aspectjweaver.jar\"" + " " + makeScriptArgs(true)); + } + + private void writeWindowsHeader(PrintStream ps) { + ps.println("@echo off"); + ps.println("REM This file generated by AspectJ installer"); + ps.println("REM Created on " + new java.util.Date() + " by " + System.getProperty("user.name")); + ps.println(""); + ps.println("if \"%JAVA_HOME%\" == \"\" set JAVA_HOME=" + context.javaPath.getAbsolutePath()); + ps.println("if \"%ASPECTJ_HOME%\" == \"\" set ASPECTJ_HOME=" + context.getOutputDir().getAbsolutePath()); + ps.println(""); + + ps.println("if exist \"%JAVA_HOME%\\bin\\java.exe\" goto haveJava"); + ps.println("if exist \"%JAVA_HOME%\\bin\\java.bat\" goto haveJava"); + ps.println("if exist \"%JAVA_HOME%\\bin\\java\" goto haveJava"); + + ps.println("echo java does not exist as %JAVA_HOME%\\bin\\java"); + ps.println("echo please fix the JAVA_HOME environment variable"); + ps.println(":haveJava"); + } + + private void writeWindowsLaunchLine(String className, PrintStream ps) { + ps.println("\"%JAVA_HOME%\\bin\\java\" -classpath " + + // "\"%ASPECTJ_HOME%\\lib\\aspectjtools.jar;%CLASSPATH%\""+ + "\"%ASPECTJ_HOME%\\lib\\aspectjtools.jar;%JAVA_HOME%\\lib\\tools.jar;%CLASSPATH%\"" + " -Xmx64M " + className + //" -defaultClasspath " + "\"%CLASSPATH%\"" + + " " + makeScriptArgs(false)); + } + + private void writeUnixHeader(PrintStream ps) { + File binsh = new File(File.separator + "bin", "sh"); + if (binsh.canRead()) { + ps.println("#!" + binsh.getPath()); + } + ps.println("# This file generated by AspectJ installer"); + ps.println("# Created on " + new java.util.Date() + " by " + System.getProperty("user.name")); + ps.println(""); + ps.println("if [ \"$JAVA_HOME\" = \"\" ] ; then JAVA_HOME=" + quote(true, false, context.javaPath.getAbsolutePath())); + ps.println("fi"); + ps.println("if [ \"$ASPECTJ_HOME\" = \"\" ] ; then ASPECTJ_HOME=" + quote(true, false, context.getOutputDir())); + ps.println("fi"); + ps.println(""); + } + + private void writeUnixLaunchLine(String className, PrintStream ps) { + String sep = File.pathSeparator; + ps.println("\"$JAVA_HOME/bin/java\" -classpath " + "\"$ASPECTJ_HOME/lib/aspectjtools.jar" + sep + + "$JAVA_HOME/lib/tools.jar" + sep + "$CLASSPATH\"" + " -Xmx64M " + className + " " + makeScriptArgs(true)); + } + + private void makeExecutable(File file) { + try { + Runtime curRuntime = Runtime.getRuntime(); + curRuntime.exec("chmod 777 " + quote(true, false, file)); + } catch (Throwable t) { + // ignore any errors that occur while trying to chmod + } + } + + private String makeScriptArgs(boolean unixStyle) { + if (unixStyle) { + return "\"$@\""; + } else if (context.onWindowsPro()) { + return "%*"; + } else { + return "%1 %2 %3 %4 %5 %6 %7 %8 %9"; + } + } + + private String quote(boolean unixStyle, boolean forceQuotes, File file) { + return quote(unixStyle, forceQuotes, file.getAbsolutePath()); + } + + private String quote(boolean unixStyle, boolean forceQuotes, String s) { + if (context.onWindows() && unixStyle) { + s = s.replace('\\', '/'); + } + + if (!forceQuotes && s.indexOf(' ') == -1) { + return s; + } + return "\"" + s + "\""; + } + + private File makeScriptFile(String name, boolean unixStyle) throws IOException { + if (!unixStyle) { + if (context.onOS2()) { + name += ".cmd"; + } else if (context.onWindows()) { + name += ".bat"; + } + } + + //XXX probably want a context.getOutputBinDir() + File bindir = new File(context.getOutputDir(), "bin"); + bindir.mkdirs(); + File file = new File(bindir, name); + return file; + } + + private PrintStream getPrintStream(File file) throws IOException { + return new PrintStream(new BufferedOutputStream(new FileOutputStream(file))); + } + + String makeClassPathVar(boolean unixStyle) { + if (unixStyle) { + return "$CLASSPATH"; + } else { + return "%CLASSPATH%"; + } + } + + public String makeClassPath(boolean unixStyle) throws IOException { + return context.toolsJarPath.getAbsolutePath() + File.pathSeparator + + + //XXX want context.getOutputLibDir() + new File(new File(context.getOutputDir(), "lib"), "aspectjtools.jar").getAbsolutePath() + File.pathSeparator + + makeClassPathVar(unixStyle); + } + + public void writeScript(String className, PrintStream ps, boolean unixStyle) throws IOException { + if (unixStyle) { + writeUnixHeader(ps); + writeUnixLaunchLine(className, ps); + } else { + writeWindowsHeader(ps); + writeWindowsLaunchLine(className, ps); + } + + /* + * ps.print(quote(unixStyle, false, context.javaPath.getAbsolutePath())); ps.print(" "); ps.print("-classpath "); + * ps.print(quote(unixStyle, true, makeClassPath(unixStyle))); ps.print(" "); ps.print("-Xmx64M "); ps.print(className); + * ps.print(" "); ps.print(makeScriptArgs(unixStyle)); + */ + } + + public void writeScript(String className, boolean unixStyle) throws IOException { + File file = makeScriptFile(className, unixStyle); + if (!checkExistingFile(file)) { + return; + } + PrintStream ps = getPrintStream(file); + writeScript(toolsPackage + '.' + className + ".Main", ps, unixStyle); + ps.close(); + //??? unixStyle vs. onUnix() + if (context.onUnix()) { + makeExecutable(file); + } + } + + public boolean checkExistingFile(File file) { + if (!file.exists()) { + return true; + } + + return context.shouldOverwrite(file); + } + + /* + * final static String OVERWRITE_MESSAGE = "Overwrite launch script "; final static String OVERWRITE_TITLE = "Overwrite?"; + * + * final static String[] OVERWRITE_OPTIONS = { "Yes", "No", "Yes to all", "No to all" }; + * + * final static int OVERWRITE_YES = 0; final static int OVERWRITE_NO = 1; final static int OVERWRITE_ALL = 2; final static int + * OVERWRITE_NONE = 3; + * + * int overwriteState = OVERWRITE_NO; boolean shouldOverwrite(final File file) { if (overwriteState == OVERWRITE_ALL) return + * true; if (overwriteState == OVERWRITE_NONE) return false; + * + * try { SwingUtilities.invokeAndWait(new Runnable() { public void run() { int ret = + * JOptionPane.showOptionDialog(context.installer.frame, OVERWRITE_MESSAGE+file.getPath(), OVERWRITE_TITLE, + * JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, OVERWRITE_OPTIONS, OVERWRITE_OPTIONS[OVERWRITE_YES]); + * + * overwriteState = ret; } }); } catch (InvocationTargetException ite) { context.handleException(ite.getTargetException()); } + * catch (InterruptedException ie) { } + * + * return overwriteState == OVERWRITE_YES || overwriteState == OVERWRITE_ALL; } + */ + + public void writeScript(String className) throws IOException { + writeScript(className, true); + if (context.onWindows()) { + writeScript(className, false); + } + } +} + +class JarUnpacker { + InstallContext context; + InstallPane installPane; + + public JarUnpacker(InstallContext context, InstallPane installPane) { + this.context = context; + this.installPane = installPane; + } + + public File makeOutputFile(String name, File outputFile) { + int index; + int lastIndex = 0; + + while ((index = name.indexOf('/', lastIndex)) != -1) { + outputFile = new File(outputFile, name.substring(lastIndex, index)); + lastIndex = index + 1; + } + + return new File(outputFile, name.substring(lastIndex)); + } + + final static int BUF_SIZE = 4096; + + public void writeStream(ZipInputStream zis, File outputFile) throws IOException { + if (outputFile.exists()) { + if (!context.shouldOverwrite(outputFile)) { + return; + } + } + + installPane.progressMessage("writing " + outputFile.getAbsolutePath()); + + outputFile.getParentFile().mkdirs(); + + if (context.isTextFile(outputFile)) { + writeTextStream(zis, outputFile); + } else { + writeBinaryStream(zis, outputFile); + } + } + + public void writeBinaryStream(ZipInputStream zis, File outputFile) throws IOException { + byte[] buffer = new byte[BUF_SIZE]; + int nRead = 0; + + OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile)); + + while ((nRead = zis.read(buffer)) != -1) { + os.write(buffer, 0, nRead); + installPane.progressBytesWritten(nRead); + } + os.close(); + } + + public void writeTextStream(ZipInputStream zis, File outputFile) throws IOException { + BufferedWriter os = new BufferedWriter(new FileWriter(outputFile)); + BufferedReader r = new BufferedReader(new InputStreamReader(zis, "US-ASCII")); + + String l; + while ((l = r.readLine()) != null) { + os.write(l); + os.newLine(); + installPane.progressBytesWritten(l.length() + 1); + } + os.close(); + } + + public void writeEntry(ZipInputStream zis, ZipEntry entry, File outputDir) throws IOException { + if (entry.isDirectory()) { + return; + } + + String name = entry.getName(); + File outputFile = makeOutputFile(name, outputDir); + writeStream(zis, outputFile); + } + + public void unpack(String jarName, File outputDir) throws IOException { + URL url = getClass().getResource(jarName); + InputStream stream = url.openStream(); + ZipInputStream zis = new ZipInputStream(stream); + // int i = 0; + + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + // final String name = entry.getName(); + writeEntry(zis, entry, outputDir); + // + } + installPane.progressMessage("done writing"); + } +} |