/* ******************************************************************* | |
* Copyright (c) 2001-2001 Xerox Corporation, | |
* 2002 Palo Alto Research Center, Incorporated (PARC) | |
* 2003-2004 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 | |
* Wes Isberg 2003-2004 changes | |
* ******************************************************************/ | |
package org.eclipse.ajdt.core.ant; | |
import java.io.File; | |
import java.io.FileWriter; | |
import java.io.IOException; | |
import java.io.PrintWriter; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collections; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.StringTokenizer; | |
import org.apache.tools.ant.AntClassLoader; | |
import org.apache.tools.ant.BuildException; | |
import org.apache.tools.ant.Location; | |
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.Execute; | |
import org.apache.tools.ant.taskdefs.Expand; | |
import org.apache.tools.ant.taskdefs.Javac; | |
import org.apache.tools.ant.taskdefs.LogStreamHandler; | |
import org.apache.tools.ant.taskdefs.MatchingTask; | |
import org.apache.tools.ant.taskdefs.Mkdir; | |
import org.apache.tools.ant.taskdefs.PumpStreamHandler; | |
import org.apache.tools.ant.taskdefs.Zip; | |
import org.apache.tools.ant.taskdefs.compilers.DefaultCompilerAdapter; | |
import org.apache.tools.ant.types.Commandline; | |
import org.apache.tools.ant.types.CommandlineJava; | |
import org.apache.tools.ant.types.FileSet; | |
import org.apache.tools.ant.types.Path; | |
import org.apache.tools.ant.types.PatternSet; | |
import org.apache.tools.ant.types.Reference; | |
import org.apache.tools.ant.types.ZipFileSet; | |
import org.apache.tools.ant.util.TaskLogger; | |
import org.aspectj.bridge.AbortException; | |
import org.aspectj.bridge.IMessage; | |
import org.aspectj.bridge.IMessageHandler; | |
import org.aspectj.bridge.IMessageHolder; | |
import org.aspectj.bridge.MessageHandler; | |
import org.aspectj.bridge.MessageUtil; | |
import org.aspectj.bridge.IMessage.Kind; | |
import org.aspectj.tools.ajc.Main; | |
import org.aspectj.tools.ant.taskdefs.ICommandEditor; | |
import org.aspectj.util.FileUtil; | |
import org.aspectj.util.LangUtil; | |
/** | |
* This runs the AspectJ 1.1 compiler, supporting all the command-line options. In 1.1.1, ajc copies resources from input jars, but | |
* you can copy resources from the source directories using sourceRootCopyFilter. When not forking, things will be copied as needed | |
* for each iterative compile, but when forking things are only copied at the completion of a successful compile. | |
* <p> | |
* See the development environment guide for usage documentation. | |
* | |
* @since AspectJ 1.1, Ant 1.5 | |
*/ | |
public class AjcTask extends MatchingTask { | |
/* | |
* This task mainly converts ant specification for ajc, verbosely ignoring improper input. It also has some special features for | |
* non-obvious clients: (1) Javac compiler adapter supported in <code>setupAjc(AjcTask, Javac, File)</code> and | |
* <code>readArguments(String[])</code>; (2) testing is supported by (a) permitting the same specification to be re-run with | |
* added flags (settings once made cannot be removed); and (b) permitting recycling the task with <code>reset()</code> | |
* (untested). | |
* | |
* The parts that do more than convert ant specs are (a) code for forking; (b) code for copying resources. | |
* | |
* If you maintain/upgrade this task, keep in mind: (1) changes to the semantics of ajc (new options, new values permitted, | |
* etc.) will have to be reflected here. (2) the clients: the iajc ant script, Javac compiler adapter, maven clients of iajc, | |
* and testing code. | |
*/ | |
// XXX move static methods after static initializer | |
/** | |
* This method extracts javac arguments to ajc, and add arguments to make ajc behave more like javac in copying resources. | |
* <p> | |
* Pass ajc-specific options using compilerarg sub-element: | |
* | |
* <pre> | |
* <javac srcdir="src"> | |
* <compilerarg compiler="..." line="-argfile src/args.lst"/> | |
* <javac> | |
* </pre> | |
* | |
* Some javac arguments are not supported in this component (yet): | |
* | |
* <pre> | |
* String memoryInitialSize; | |
* boolean includeAntRuntime = true; | |
* boolean includeJavaRuntime = false; | |
* </pre> | |
* | |
* Other javac arguments are not supported in ajc 1.1: | |
* | |
* <pre> | |
* boolean optimize; | |
* String forkedExecutable; | |
* FacadeTaskHelper facade; | |
* boolean depend; | |
* String debugLevel; | |
* Path compileSourcepath; | |
* </pre> | |
* | |
* @param javac the Javac command to implement (not null) | |
* @param ajc the AjcTask to adapt (not null) | |
* @param destDir the File class destination directory (may be null) | |
* @return null if no error, or String error otherwise | |
*/ | |
public String setupAjc(Javac javac) { | |
if (null == javac) { | |
return "null javac"; | |
} | |
AjcTask ajc = this; | |
// no null checks b/c AjcTask handles null input gracefully | |
ajc.setProject(javac.getProject()); | |
ajc.setLocation(javac.getLocation()); | |
ajc.setTaskName("javac-iajc"); | |
ajc.setDebug(javac.getDebug()); | |
ajc.setDeprecation(javac.getDeprecation()); | |
ajc.setFailonerror(javac.getFailonerror()); | |
final boolean fork = javac.isForkedJavac(); | |
ajc.setFork(fork); | |
if (fork) { | |
ajc.setMaxmem(javac.getMemoryMaximumSize()); | |
} | |
ajc.setNowarn(javac.getNowarn()); | |
ajc.setListFileArgs(javac.getListfiles()); | |
ajc.setVerbose(javac.getVerbose()); | |
ajc.setTarget(javac.getTarget()); | |
ajc.setSource(javac.getSource()); | |
ajc.setEncoding(javac.getEncoding()); | |
File javacDestDir = javac.getDestdir(); | |
if (null != javacDestDir) { | |
ajc.setDestdir(javacDestDir); | |
// filter requires dest dir | |
// mimic Javac task's behavior in copying resources, | |
ajc.setSourceRootCopyFilter("**/CVS/*,**/*.java,**/*.aj"); | |
} | |
ajc.setBootclasspath(javac.getBootclasspath()); | |
ajc.setExtdirs(javac.getExtdirs()); | |
ajc.setClasspath(javac.getClasspath()); | |
// ignore srcDir -- all files picked up in recalculated file list | |
// ajc.setSrcDir(javac.getSrcdir()); | |
ajc.addFiles(javac.getFileList()); | |
// arguments can override the filter, add to paths, override options | |
ajc.readArguments(javac.getCurrentCompilerArgs()); | |
return null; | |
} | |
/** | |
* Find aspectjtools.jar on the task or system classpath. Accept <code>aspectj{-}tools{...}.jar</code> mainly to support build | |
* systems using maven-style re-naming (e.g., <code>aspectj-tools-1.1.0.jar</code>. Note that we search the task classpath | |
* first, though an entry on the system classpath would be loaded first, because it seems more correct as the more specific one. | |
* | |
* @return readable File for aspectjtools.jar, or null if not found. | |
*/ | |
public static File findAspectjtoolsJar() { | |
File result = null; | |
ClassLoader loader = AjcTask.class.getClassLoader(); | |
if (loader instanceof AntClassLoader) { | |
AntClassLoader taskLoader = (AntClassLoader) loader; | |
String cp = taskLoader.getClasspath(); | |
String[] cps = LangUtil.splitClasspath(cp); | |
for (int i = 0; (i < cps.length) && (null == result); i++) { | |
result = isAspectjtoolsjar(cps[i]); | |
} | |
} | |
if (null == result) { | |
final Path classpath = Path.systemClasspath; | |
final String[] paths = classpath.list(); | |
for (int i = 0; (i < paths.length) && (null == result); i++) { | |
result = isAspectjtoolsjar(paths[i]); | |
} | |
} | |
return (null == result ? null : result.getAbsoluteFile()); | |
} | |
/** @return File if readable jar with aspectj tools name, or null */ | |
private static File isAspectjtoolsjar(String path) { | |
if (null == path) { | |
return null; | |
} | |
final String prefix = "aspectj"; | |
final String infix = "tools"; | |
final String altInfix = "-tools"; | |
final String suffix = ".jar"; | |
final int prefixLength = 7; // prefix.length(); | |
final int minLength = 16; | |
// prefixLength + infix.length() + suffix.length(); | |
if (!path.endsWith(suffix)) { | |
return null; | |
} | |
int loc = path.lastIndexOf(prefix); | |
if ((-1 != loc) && ((loc + minLength) <= path.length())) { | |
String rest = path.substring(loc + prefixLength); | |
if (-1 != rest.indexOf(File.pathSeparator)) { | |
return null; | |
} | |
if (rest.startsWith(infix) || rest.startsWith(altInfix)) { | |
File result = new File(path); | |
if (result.canRead() && result.isFile()) { | |
return result; | |
} | |
} | |
} | |
return null; | |
} | |
/** | |
* Maximum length (in chars) of command line before converting to an argfile when forking | |
*/ | |
private static final int MAX_COMMANDLINE = 4096; | |
private static final File DEFAULT_DESTDIR = new File(".") { | |
public String toString() { | |
return "(no destination dir specified)"; | |
} | |
}; | |
/** do not throw BuildException on fail/abort message with usage */ | |
private static final String USAGE_SUBSTRING = "AspectJ-specific options"; | |
/** valid -X[...] options other than -Xlint variants */ | |
private static final List VALID_XOPTIONS; | |
/** valid warning (-warn:[...]) variants */ | |
private static final List VALID_WARNINGS; | |
/** valid debugging (-g:[...]) variants */ | |
private static final List VALID_DEBUG; | |
/** | |
* -Xlint variants (error, warning, ignore) | |
* | |
* @see org.aspectj.weaver.Lint | |
*/ | |
private static final List VALID_XLINT; | |
public static final String COMMAND_EDITOR_NAME = AjcTask.class.getName() + ".COMMAND_EDITOR"; | |
static final String[] TARGET_INPUTS = new String[] { "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7" }; | |
static final String[] SOURCE_INPUTS = new String[] { "1.3", "1.4", "1.5", "1.6", "1.7" }; | |
static final String[] COMPLIANCE_INPUTS = new String[] { "-1.3", "-1.4", "-1.5", "-1.6", "-1.7" }; | |
private static final ICommandEditor COMMAND_EDITOR; | |
static { | |
// many now deprecated: reweavable* | |
String[] xs = new String[] { "serializableAspects", "incrementalFile", "lazyTjp", "reweavable", "reweavable:compress", | |
"notReweavable", "noInline", "terminateAfterCompilation", "hasMember", "ajruntimetarget:1.2", | |
"ajruntimetarget:1.5", "addSerialVersionUID" | |
// , "targetNearSource", "OcodeSize", | |
}; | |
VALID_XOPTIONS = Collections.unmodifiableList(Arrays.asList(xs)); | |
xs = new String[] { "constructorName", "packageDefaultMethod", "deprecation", "maskedCatchBlocks", "unusedLocals", | |
"unusedArguments", "unusedImports", "syntheticAccess", "assertIdentifier", "allDeprecation", "allJavadoc", | |
"charConcat", "conditionAssign", | |
"emptyBlock", "fieldHiding", "finally", "indirectStatic", "intfNonInherited", "javadoc", "localHiding", "nls", | |
"noEffectAssign", "pkgDefaultMethod", "semicolon", "unqualifiedField", "unusedPrivate", "unusedThrown", | |
"uselessTypeCheck", "specialParamHiding", "staticReceiver", "syntheticAccess", "none" }; | |
VALID_WARNINGS = Collections.unmodifiableList(Arrays.asList(xs)); | |
xs = new String[] { "none", "lines", "vars", "source" }; | |
VALID_DEBUG = Collections.unmodifiableList(Arrays.asList(xs)); | |
xs = new String[] { "error", "warning", "ignore" }; | |
VALID_XLINT = Collections.unmodifiableList(Arrays.asList(xs)); | |
ICommandEditor editor = null; | |
try { | |
String editorClassName = System.getProperty(COMMAND_EDITOR_NAME); | |
if (null != editorClassName) { | |
ClassLoader cl = AjcTask.class.getClassLoader(); | |
Class editorClass = cl.loadClass(editorClassName); | |
editor = (ICommandEditor) editorClass.newInstance(); | |
} | |
} catch (Throwable t) { | |
System.err.println("Warning: unable to load command editor"); | |
t.printStackTrace(System.err); | |
} | |
COMMAND_EDITOR = editor; | |
} | |
// ---------------------------- state and Ant interface thereto | |
private boolean verbose; | |
private boolean timers; | |
private boolean listFileArgs; | |
private boolean failonerror; | |
private boolean fork; | |
private String maxMem; | |
private TaskLogger logger; | |
// ------- single entries dumped into cmd | |
protected GuardedCommand cmd; | |
// ------- lists resolved in addListArgs() at execute() time | |
private Path srcdir; | |
private Path injars; | |
private Path inpath; | |
private Path classpath; | |
private Path bootclasspath; | |
private Path forkclasspath; | |
private Path extdirs; | |
private Path aspectpath; | |
private Path argfiles; | |
private Path inxmlfiles; | |
private List ignored; | |
private Path sourceRoots; | |
private File xweaveDir; | |
private String xdoneSignal; | |
// ----- added by adapter - integrate better? | |
private List /* File */adapterFiles; | |
private String[] adapterArguments; | |
private IMessageHolder messageHolder; | |
private ICommandEditor commandEditor; | |
// -------- resource-copying | |
/** true if copying injar non-.class files to the output jar */ | |
private boolean copyInjars; | |
private boolean copyInpath; | |
/** non-null if copying all source root files but the filtered ones */ | |
private String sourceRootCopyFilter; | |
/** non-null if copying all inpath dir files but the filtered ones */ | |
private String inpathDirCopyFilter; | |
/** directory sink for classes */ | |
private File destDir; | |
/** zip file sink for classes */ | |
private File outjar; | |
/** track whether we've supplied any temp outjar */ | |
private boolean outjarFixedup; | |
/** | |
* When possibly copying resources to the output jar, pass ajc a fake output jar to copy from, so we don't change the | |
* modification time of the output jar when copying injars/inpath into the actual outjar. | |
*/ | |
private File tmpOutjar; | |
private boolean executing; | |
/** non-null only while executing in same vm */ | |
private Main main; | |
/** true only when executing in other vm */ | |
private boolean executingInOtherVM; | |
/** true if -incremental */ | |
private boolean inIncrementalMode; | |
/** true if -XincrementalFile (i.e, setTagFile) */ | |
private boolean inIncrementalFileMode; | |
/** log command in non-verbose mode */ | |
private boolean logCommand; | |
/** used when forking */ | |
private CommandlineJava javaCmd = new CommandlineJava(); | |
private boolean wasCompilationSuccessful; | |
// also note MatchingTask grabs source files... | |
public AjcTask() { | |
reset(); | |
} | |
/** to use this same Task more than once (testing) */ | |
public void reset() { // XXX possible to reset MatchingTask? | |
// need declare for "all fields initialized in ..." | |
adapterArguments = null; | |
adapterFiles = new ArrayList(); | |
argfiles = null; | |
inxmlfiles = null; | |
executing = false; | |
aspectpath = null; | |
bootclasspath = null; | |
classpath = null; | |
cmd = new GuardedCommand(); | |
copyInjars = false; | |
copyInpath = false; | |
destDir = DEFAULT_DESTDIR; | |
executing = false; | |
executingInOtherVM = false; | |
extdirs = null; | |
failonerror = true; // non-standard default | |
forkclasspath = null; | |
inIncrementalMode = false; | |
inIncrementalFileMode = false; | |
ignored = new ArrayList(); | |
injars = null; | |
inpath = null; | |
listFileArgs = false; | |
maxMem = null; | |
messageHolder = null; | |
outjar = null; | |
sourceRootCopyFilter = null; | |
inpathDirCopyFilter = null; | |
sourceRoots = null; | |
srcdir = null; | |
tmpOutjar = null; | |
verbose = false; | |
timers = false; | |
xweaveDir = null; | |
xdoneSignal = null; | |
logCommand = false; | |
javaCmd = new CommandlineJava(); | |
} | |
protected void ignore(String ignored) { | |
this.ignored.add(ignored + " at " + getLocation()); | |
} | |
// ---------------------- option values | |
// used by entries with internal commas | |
protected String validCommaList(String list, List valid, String label) { | |
return validCommaList(list, valid, label, valid.size()); | |
} | |
protected String validCommaList(String list, List valid, String label, int max) { | |
StringBuffer result = new StringBuffer(); | |
StringTokenizer st = new StringTokenizer(list, ","); | |
int num = 0; | |
while (st.hasMoreTokens()) { | |
String token = st.nextToken().trim(); | |
num++; | |
if (num > max) { | |
ignore("too many entries for -" + label + ": " + token); | |
break; | |
} | |
if (!valid.contains(token)) { | |
ignore("bad commaList entry for -" + label + ": " + token); | |
} else { | |
if (0 < result.length()) { | |
result.append(","); | |
} | |
result.append(token); | |
} | |
} | |
return (0 == result.length() ? null : result.toString()); | |
} | |
public void setIncremental(boolean incremental) { | |
cmd.addFlag("-incremental", incremental); | |
inIncrementalMode = incremental; | |
} | |
public void setLogCommand(boolean logCommand) { | |
this.logCommand = logCommand; | |
} | |
public void setHelp(boolean help) { | |
cmd.addFlag("-help", help); | |
} | |
public void setVersion(boolean version) { | |
cmd.addFlag("-version", version); | |
} | |
public void setXTerminateAfterCompilation(boolean b) { | |
cmd.addFlag("-XterminateAfterCompilation", b); | |
} | |
public void setXReweavable(boolean reweavable) { | |
cmd.addFlag("-Xreweavable", reweavable); | |
} | |
public void setXJoinpoints(String optionalJoinpoints) { | |
cmd.addFlag("-Xjoinpoints:" + optionalJoinpoints, true); | |
} | |
public void setCheckRuntimeVersion(boolean b) { | |
cmd.addFlag("-checkRuntimeVersion:" + b, true); | |
} | |
public void setXNoWeave(boolean b) { | |
if (logger != null) { | |
logger.warning("the noweave option is no longer required and is being ignored"); | |
} | |
} | |
public void setNoWeave(boolean b) { | |
if (logger != null) { | |
logger.warning("the noweave option is no longer required and is being ignored"); | |
} | |
} | |
public void setXNotReweavable(boolean notReweavable) { | |
cmd.addFlag("-XnotReweavable", notReweavable); | |
} | |
public void setXaddSerialVersionUID(boolean addUID) { | |
cmd.addFlag("-XaddSerialVersionUID", addUID); | |
} | |
public void setXNoInline(boolean noInline) { | |
cmd.addFlag("-XnoInline", noInline); | |
} | |
public void setShowWeaveInfo(boolean showweaveinfo) { | |
cmd.addFlag("-showWeaveInfo", showweaveinfo); | |
} | |
public void setNowarn(boolean nowarn) { | |
cmd.addFlag("-nowarn", nowarn); | |
} | |
public void setDeprecation(boolean deprecation) { | |
cmd.addFlag("-deprecation", deprecation); | |
} | |
public void setWarn(String warnings) { | |
warnings = validCommaList(warnings, VALID_WARNINGS, "warn"); | |
cmd.addFlag("-warn:" + warnings, (null != warnings)); | |
} | |
public void setDebug(boolean debug) { | |
cmd.addFlag("-g", debug); | |
} | |
public void setDebugLevel(String level) { | |
level = validCommaList(level, VALID_DEBUG, "g"); | |
cmd.addFlag("-g:" + level, (null != level)); | |
} | |
public void setEmacssym(boolean emacssym) { | |
cmd.addFlag("-emacssym", emacssym); | |
} | |
public void setCrossrefs(boolean on) { | |
cmd.addFlag("-crossrefs", on); | |
} | |
/** | |
* -Xlint - set default level of -Xlint messages to warning (same as </code>-Xlint:warning</code>) | |
*/ | |
public void setXlintwarnings(boolean xlintwarnings) { | |
cmd.addFlag("-Xlint", xlintwarnings); | |
} | |
/** | |
* -Xlint:{error|warning|info} - set default level for -Xlint messages | |
* | |
* @param xlint the String with one of error, warning, ignored | |
*/ | |
public void setXlint(String xlint) { | |
xlint = validCommaList(xlint, VALID_XLINT, "Xlint", 1); | |
cmd.addFlag("-Xlint:" + xlint, (null != xlint)); | |
} | |
/** | |
* -Xlintfile {lint.properties} - enable or disable specific forms of -Xlint messages based on a lint properties file (default | |
* is <code>org/aspectj/weaver/XLintDefault.properties</code>) | |
* | |
* @param xlintFile the File with lint properties | |
*/ | |
public void setXlintfile(File xlintFile) { | |
cmd.addFlagged("-Xlintfile", xlintFile.getAbsolutePath()); | |
} | |
public void setPreserveAllLocals(boolean preserveAllLocals) { | |
cmd.addFlag("-preserveAllLocals", preserveAllLocals); | |
} | |
public void setNoImportError(boolean noImportError) { | |
cmd.addFlag("-warn:-unusedImport", noImportError); | |
} | |
public void setEncoding(String encoding) { | |
cmd.addFlagged("-encoding", encoding); | |
} | |
public void setLog(File file) { | |
cmd.addFlagged("-log", file.getAbsolutePath()); | |
} | |
public void setProceedOnError(boolean proceedOnError) { | |
cmd.addFlag("-proceedOnError", proceedOnError); | |
} | |
public void setVerbose(boolean verbose) { | |
cmd.addFlag("-verbose", verbose); | |
this.verbose = verbose; | |
} | |
public void setTimers(boolean timers) { | |
cmd.addFlag("-timers", timers); | |
this.timers = timers; | |
} | |
public void setListFileArgs(boolean listFileArgs) { | |
this.listFileArgs = listFileArgs; | |
} | |
public void setReferenceInfo(boolean referenceInfo) { | |
cmd.addFlag("-referenceInfo", referenceInfo); | |
} | |
public void setTime(boolean time) { | |
cmd.addFlag("-time", time); | |
} | |
public void setNoExit(boolean noExit) { | |
cmd.addFlag("-noExit", noExit); | |
} | |
public void setFailonerror(boolean failonerror) { | |
this.failonerror = failonerror; | |
} | |
/** | |
* @return true if fork was set | |
*/ | |
public boolean isForked() { | |
return fork; | |
} | |
public void setFork(boolean fork) { | |
this.fork = fork; | |
} | |
public void setMaxmem(String maxMem) { | |
this.maxMem = maxMem; | |
} | |
/** support for nested <jvmarg> elements */ | |
public Commandline.Argument createJvmarg() { | |
return this.javaCmd.createVmArgument(); | |
} | |
// ---------------- | |
public void setTagFile(File file) { | |
inIncrementalMode = true; | |
cmd.addFlagged(Main.CommandController.TAG_FILE_OPTION, file.getAbsolutePath()); | |
inIncrementalFileMode = true; | |
} | |
public void setOutjar(File file) { | |
if (DEFAULT_DESTDIR != destDir) { | |
String e = "specifying both output jar (" + file + ") and destination dir (" + destDir + ")"; | |
throw new BuildException(e); | |
} | |
outjar = file; | |
outjarFixedup = false; | |
tmpOutjar = null; | |
} | |
public void setOutxml(boolean outxml) { | |
cmd.addFlag("-outxml", outxml); | |
} | |
public void setOutxmlfile(String name) { | |
cmd.addFlagged("-outxmlfile", name); | |
} | |
public void setDestdir(File dir) { | |
if (null != outjar) { | |
String e = "specifying both output jar (" + outjar + ") and destination dir (" + dir + ")"; | |
throw new BuildException(e); | |
} | |
cmd.addFlagged("-d", dir.getAbsolutePath()); | |
destDir = dir; | |
} | |
/** | |
* @param input a String in TARGET_INPUTS | |
*/ | |
public void setTarget(String input) { | |
String ignore = cmd.addOption("-target", TARGET_INPUTS, input); | |
if (null != ignore) { | |
ignore(ignore); | |
} | |
} | |
/** | |
* Language compliance level. If not set explicitly, eclipse default holds. | |
* | |
* @param input a String in COMPLIANCE_INPUTS | |
*/ | |
public void setCompliance(String input) { | |
String ignore = cmd.addOption(null, COMPLIANCE_INPUTS, input); | |
if (null != ignore) { | |
ignore(ignore); | |
} | |
} | |
/** | |
* Source compliance level. If not set explicitly, eclipse default holds. | |
* | |
* @param input a String in SOURCE_INPUTS | |
*/ | |
public void setSource(String input) { | |
String ignore = cmd.addOption("-source", SOURCE_INPUTS, input); | |
if (null != ignore) { | |
ignore(ignore); | |
} | |
} | |
/** | |
* Flag to copy all non-.class contents of injars to outjar after compile completes. Requires both injars and outjar. | |
* | |
* @param doCopy | |
*/ | |
public void setCopyInjars(boolean doCopy) { | |
ignore("copyInJars"); | |
log("copyInjars not required since 1.1.1.\n", Project.MSG_WARN); | |
// this.copyInjars = doCopy; | |
} | |
/** | |
* Option to copy all files from all source root directories except those specified here. If this is specified and sourceroots | |
* are specified, then this will copy all files except those specified in the filter pattern. Requires sourceroots. | |
* | |
* @param filter a String acceptable as an excludes filter for an Ant Zip fileset. | |
*/ | |
public void setSourceRootCopyFilter(String filter) { | |
this.sourceRootCopyFilter = filter; | |
} | |
/** | |
* Option to copy all files from all inpath directories except the files specified here. If this is specified and inpath | |
* directories are specified, then this will copy all files except those specified in the filter pattern. Requires inpath. If | |
* the input does not contain "**\/*.class", then this prepends it, to avoid overwriting woven classes with unwoven input. | |
* | |
* @param filter a String acceptable as an excludes filter for an Ant Zip fileset. | |
*/ | |
public void setInpathDirCopyFilter(String filter) { | |
if (null != filter) { | |
if (-1 == filter.indexOf("**/*.class")) { | |
filter = "**/*.class," + filter; | |
} | |
} | |
this.inpathDirCopyFilter = filter; | |
} | |
public void setX(String input) { // ajc-only eajc-also docDone | |
StringTokenizer tokens = new StringTokenizer(input, ",", false); | |
while (tokens.hasMoreTokens()) { | |
String token = tokens.nextToken().trim(); | |
if (1 < token.length()) { | |
// new special case: allow -Xset:anything | |
if (VALID_XOPTIONS.contains(token) || token.indexOf("set:") == 0 || token.indexOf("joinpoints:") == 0) { | |
cmd.addFlag("-X" + token, true); | |
} else { | |
ignore("-X" + token); | |
} | |
} | |
} | |
} | |
public void setXDoneSignal(String doneSignal) { | |
this.xdoneSignal = doneSignal; | |
} | |
/** direct API for testing */ | |
public void setMessageHolder(IMessageHolder holder) { | |
this.messageHolder = holder; | |
} | |
/** | |
* Setup custom message handling. | |
* | |
* @param className the String fully-qualified-name of a class reachable from this object's class loader, implementing | |
* IMessageHolder, and having a public no-argument constructor. | |
* @throws BuildException if unable to create instance of className | |
*/ | |
public void setMessageHolderClass(String className) { | |
try { | |
Class mclass = Class.forName(className); | |
IMessageHolder holder = (IMessageHolder) mclass.newInstance(); | |
setMessageHolder(holder); | |
} catch (Throwable t) { | |
String m = "unable to instantiate message holder: " + className; | |
throw new BuildException(m, t); | |
} | |
} | |
/** direct API for testing */ | |
public void setCommandEditor(ICommandEditor editor) { | |
this.commandEditor = editor; | |
} | |
/** | |
* Setup command-line filter. To do this staticly, define the environment variable | |
* <code>org.aspectj.tools.ant.taskdefs.AjcTask.COMMAND_EDITOR</code> with the <code>className</code> parameter. | |
* | |
* @param className the String fully-qualified-name of a class reachable from this object's class loader, implementing | |
* ICommandEditor, and having a public no-argument constructor. | |
* @throws BuildException if unable to create instance of className | |
*/ | |
public void setCommandEditorClass(String className) { // skip Ant interface? | |
try { | |
Class mclass = Class.forName(className); | |
setCommandEditor((ICommandEditor) mclass.newInstance()); | |
} catch (Throwable t) { | |
String m = "unable to instantiate command editor: " + className; | |
throw new BuildException(m, t); | |
} | |
} | |
// ---------------------- Path lists | |
/** | |
* Add path elements to source path and return result. Elements are added even if they do not exist. | |
* | |
* @param source the Path to add to - may be null | |
* @param toAdd the Path to add - may be null | |
* @return the (never-null) Path that results | |
*/ | |
protected Path incPath(Path source, Path toAdd) { | |
if (null == source) { | |
source = new Path(project); | |
} | |
if (null != toAdd) { | |
source.append(toAdd); | |
} | |
return source; | |
} | |
public void setSourcerootsref(Reference ref) { | |
createSourceRoots().setRefid(ref); | |
} | |
public void setSourceRoots(Path roots) { | |
sourceRoots = incPath(sourceRoots, roots); | |
} | |
public Path createSourceRoots() { | |
if (sourceRoots == null) { | |
sourceRoots = new Path(project); | |
} | |
return sourceRoots.createPath(); | |
} | |
public void setXWeaveDir(File file) { | |
if ((null != file) && file.isDirectory() && file.canRead()) { | |
xweaveDir = file; | |
} | |
} | |
public void setInjarsref(Reference ref) { | |
createInjars().setRefid(ref); | |
} | |
public void setInpathref(Reference ref) { | |
createInpath().setRefid(ref); | |
} | |
public void setInjars(Path path) { | |
injars = incPath(injars, path); | |
} | |
public void setInpath(Path path) { | |
inpath = incPath(inpath, path); | |
} | |
public Path createInjars() { | |
if (injars == null) { | |
injars = new Path(project); | |
} | |
return injars.createPath(); | |
} | |
public Path createInpath() { | |
if (inpath == null) { | |
inpath = new Path(project); | |
} | |
return inpath.createPath(); | |
} | |
public void setClasspath(Path path) { | |
classpath = incPath(classpath, path); | |
} | |
public void setClasspathref(Reference classpathref) { | |
createClasspath().setRefid(classpathref); | |
} | |
public Path createClasspath() { | |
if (classpath == null) { | |
classpath = new Path(project); | |
} | |
return classpath.createPath(); | |
} | |
public void setBootclasspath(Path path) { | |
bootclasspath = incPath(bootclasspath, path); | |
} | |
public void setBootclasspathref(Reference bootclasspathref) { | |
createBootclasspath().setRefid(bootclasspathref); | |
} | |
public Path createBootclasspath() { | |
if (bootclasspath == null) { | |
bootclasspath = new Path(project); | |
} | |
return bootclasspath.createPath(); | |
} | |
public void setForkclasspath(Path path) { | |
forkclasspath = incPath(forkclasspath, path); | |
} | |
public void setForkclasspathref(Reference forkclasspathref) { | |
createForkclasspath().setRefid(forkclasspathref); | |
} | |
public Path createForkclasspath() { | |
if (forkclasspath == null) { | |
forkclasspath = new Path(project); | |
} | |
return forkclasspath.createPath(); | |
} | |
public void setExtdirs(Path path) { | |
extdirs = incPath(extdirs, path); | |
} | |
public void setExtdirsref(Reference ref) { | |
createExtdirs().setRefid(ref); | |
} | |
public Path createExtdirs() { | |
if (extdirs == null) { | |
extdirs = new Path(project); | |
} | |
return extdirs.createPath(); | |
} | |
public void setAspectpathref(Reference ref) { | |
createAspectpath().setRefid(ref); | |
} | |
public void setAspectpath(Path path) { | |
aspectpath = incPath(aspectpath, path); | |
} | |
public Path createAspectpath() { | |
if (aspectpath == null) { | |
aspectpath = new Path(project); | |
} | |
return aspectpath.createPath(); | |
} | |
public void setSrcDir(Path path) { | |
srcdir = incPath(srcdir, path); | |
} | |
public Path createSrc() { | |
return createSrcdir(); | |
} | |
public Path createSrcdir() { | |
if (srcdir == null) { | |
srcdir = new Path(project); | |
} | |
return srcdir.createPath(); | |
} | |
/** @return true if in incremental mode (command-line or file) */ | |
public boolean isInIncrementalMode() { | |
return inIncrementalMode; | |
} | |
/** @return true if in incremental file mode */ | |
public boolean isInIncrementalFileMode() { | |
return inIncrementalFileMode; | |
} | |
public void setArgfilesref(Reference ref) { | |
createArgfiles().setRefid(ref); | |
} | |
public void setArgfiles(Path path) { // ajc-only eajc-also docDone | |
argfiles = incPath(argfiles, path); | |
} | |
public Path createArgfiles() { | |
if (argfiles == null) { | |
argfiles = new Path(project); | |
} | |
return argfiles.createPath(); | |
} | |
public void setInxmlref(Reference ref) { | |
createArgfiles().setRefid(ref); | |
} | |
public void setInxml(Path path) { // ajc-only eajc-also docDone | |
inxmlfiles = incPath(inxmlfiles, path); | |
} | |
public Path createInxml() { | |
if (inxmlfiles == null) { | |
inxmlfiles = new Path(project); | |
} | |
return inxmlfiles.createPath(); | |
} | |
// ------------------------------ run | |
/** | |
* Compile using ajc per settings. | |
* | |
* @exception BuildException if the compilation has problems or if there were compiler errors and failonerror is true. | |
*/ | |
public void execute() throws BuildException { | |
this.logger = new TaskLogger(this); | |
if (executing) { | |
throw new IllegalStateException("already executing"); | |
} else { | |
executing = true; | |
} | |
setupOptions(); | |
verifyOptions(); | |
try { | |
String[] args = makeCommand(); | |
if (logCommand) { | |
log("ajc " + Arrays.asList(args)); | |
} else { | |
logVerbose("ajc " + Arrays.asList(args)); | |
} | |
if (!fork) { | |
wasCompilationSuccessful = executeInSameVM(args); | |
} else { // when forking, Adapter handles failonerror | |
wasCompilationSuccessful = executeInOtherVM(args); | |
} | |
} catch (BuildException e) { | |
throw e; | |
} catch (Throwable x) { | |
this.logger.error(Main.renderExceptionForUser(x)); | |
throw new BuildException("IGNORE -- See " + LangUtil.unqualifiedClassName(x) + " rendered to ant logger"); | |
} finally { | |
executing = false; | |
if (null != tmpOutjar) { | |
tmpOutjar.delete(); | |
} | |
} | |
} | |
boolean wasCompilationSuccessful() { | |
return wasCompilationSuccessful; | |
} | |
/** | |
* Halt processing. This tells main in the same vm to quit. It fails when running in forked mode. | |
* | |
* @return true if not in forked mode and main has quit or been told to quit | |
*/ | |
public boolean quit() { | |
if (executingInOtherVM) { | |
return false; | |
} | |
Main me = main; | |
if (null != me) { | |
me.quit(); | |
} | |
return true; | |
} | |
// package-private for testing | |
String[] makeCommand() { | |
ArrayList result = new ArrayList(); | |
if (0 < ignored.size()) { | |
for (Iterator iter = ignored.iterator(); iter.hasNext();) { | |
logVerbose("ignored: " + iter.next()); | |
} | |
} | |
// when copying resources, use temp jar for class output | |
// then copy temp jar contents and resources to output jar | |
if ((null != outjar) && !outjarFixedup) { | |
if (copyInjars || copyInpath || (null != sourceRootCopyFilter) || (null != inpathDirCopyFilter)) { | |
String path = outjar.getAbsolutePath(); | |
int len = FileUtil.zipSuffixLength(path); | |
path = path.substring(0, path.length() - len) + ".tmp.jar"; | |
tmpOutjar = new File(path); | |
} | |
if (null == tmpOutjar) { | |
cmd.addFlagged("-outjar", outjar.getAbsolutePath()); | |
} else { | |
cmd.addFlagged("-outjar", tmpOutjar.getAbsolutePath()); | |
} | |
outjarFixedup = true; | |
} | |
result.addAll(cmd.extractArguments()); | |
addListArgs(result); | |
String[] command = (String[]) result.toArray(new String[0]); | |
if (null != commandEditor) { | |
command = commandEditor.editCommand(command); | |
} else if (null != COMMAND_EDITOR) { | |
command = COMMAND_EDITOR.editCommand(command); | |
} | |
return command; | |
} | |
/** | |
* Create any pseudo-options required to implement some of the macro options | |
* | |
* @throws BuildException if options conflict | |
*/ | |
protected void setupOptions() { | |
if (null != xweaveDir) { | |
if (DEFAULT_DESTDIR != destDir) { | |
throw new BuildException("weaveDir forces destdir"); | |
} | |
if (null != outjar) { | |
throw new BuildException("weaveDir forces outjar"); | |
} | |
if (null != injars) { | |
throw new BuildException("weaveDir incompatible with injars now"); | |
} | |
if (null != inpath) { | |
throw new BuildException("weaveDir incompatible with inpath now"); | |
} | |
File injar = zipDirectory(xweaveDir); | |
setInjars(new Path(getProject(), injar.getAbsolutePath())); | |
setDestdir(xweaveDir); | |
} | |
} | |
protected File zipDirectory(File dir) { | |
File tempDir = new File("."); | |
try { | |
tempDir = File.createTempFile("AjcTest", ".tmp"); | |
tempDir.mkdirs(); | |
tempDir.deleteOnExit(); // XXX remove zip explicitly.. | |
} catch (IOException e) { | |
// ignore | |
} | |
// File result = new File(tempDir, | |
String filename = "AjcTask-" + System.currentTimeMillis() + ".zip"; | |
File result = new File(filename); | |
Zip zip = new Zip(); | |
zip.setProject(getProject()); | |
zip.setDestFile(result); | |
zip.setTaskName(getTaskName() + " - zip"); | |
FileSet fileset = new FileSet(); | |
fileset.setDir(dir); | |
zip.addFileset(fileset); | |
zip.execute(); | |
Delete delete = new Delete(); | |
delete.setProject(getProject()); | |
delete.setTaskName(getTaskName() + " - delete"); | |
delete.setDir(dir); | |
delete.execute(); | |
Mkdir mkdir = new Mkdir(); | |
mkdir.setProject(getProject()); | |
mkdir.setTaskName(getTaskName() + " - mkdir"); | |
mkdir.setDir(dir); | |
mkdir.execute(); | |
return result; | |
} | |
/** | |
* @throw BuildException if options conflict | |
*/ | |
protected void verifyOptions() { | |
StringBuffer sb = new StringBuffer(); | |
if (fork && isInIncrementalMode() && !isInIncrementalFileMode()) { | |
sb.append("can fork incremental only using tag file.\n"); | |
} | |
if (((null != inpathDirCopyFilter) || (null != sourceRootCopyFilter)) && (null == outjar) && (DEFAULT_DESTDIR == destDir)) { | |
final String REQ = " requires dest dir or output jar.\n"; | |
if (null == inpathDirCopyFilter) { | |
sb.append("sourceRootCopyFilter"); | |
} else if (null == sourceRootCopyFilter) { | |
sb.append("inpathDirCopyFilter"); | |
} else { | |
sb.append("sourceRootCopyFilter and inpathDirCopyFilter"); | |
} | |
sb.append(REQ); | |
} | |
if (0 < sb.length()) { | |
throw new BuildException(sb.toString()); | |
} | |
} | |
/** | |
* Run the compile in the same VM by loading the compiler (Main), setting up any message holders, doing the compile, and | |
* converting abort/failure and error messages to BuildException, as appropriate. | |
* | |
* @return true on successful compilation | |
* @throws BuildException if abort or failure messages or if errors and failonerror. | |
* | |
*/ | |
protected boolean executeInSameVM(String[] args) { | |
if (null != maxMem) { | |
log("maxMem ignored unless forked: " + maxMem, Project.MSG_WARN); | |
} | |
IMessageHolder holder = messageHolder; | |
int numPreviousErrors; | |
if (null == holder) { | |
MessageHandler mhandler = new MessageHandler(true); | |
final IMessageHandler delegate; | |
delegate = new AntMessageHandler(this.logger, this.verbose, false); | |
mhandler.setInterceptor(delegate); | |
holder = mhandler; | |
numPreviousErrors = 0; | |
} else { | |
numPreviousErrors = holder.numMessages(IMessage.ERROR, true); | |
} | |
AjdtCommandForAnt command = new AjdtCommandForAnt(); | |
{ | |
Main newmain = new Main(); | |
newmain.setCommand(command); | |
newmain.setHolder(holder); | |
newmain.setCompletionRunner(new Runnable() { | |
public void run() { | |
doCompletionTasks(); | |
} | |
}); | |
if (null != main) { | |
MessageUtil.fail(holder, "still running prior main"); | |
return false; | |
} | |
main = newmain; | |
} | |
main.runMain(args, false); | |
if (failonerror) { | |
int errs = holder.numMessages(IMessage.ERROR, false); | |
errs -= numPreviousErrors; | |
if (0 < errs) { | |
String m = errs + " errors"; | |
MessageUtil.print(System.err, holder, "", MessageUtil.MESSAGE_ALL, MessageUtil.PICK_ERROR, true); | |
throw new BuildException(m); | |
} | |
} | |
// Throw BuildException if there are any fail or abort | |
// messages. | |
// The BuildException message text has a list of class names | |
// for the exceptions found in the messages, or the | |
// number of fail/abort messages found if there were | |
// no exceptions for any of the fail/abort messages. | |
// The interceptor message handler should have already | |
// printed the messages, including any stack traces. | |
// HACK: this ignores the Usage message | |
{ | |
IMessage[] fails = holder.getMessages(IMessage.FAIL, true); | |
if (!LangUtil.isEmpty(fails)) { | |
StringBuffer sb = new StringBuffer(); | |
String prefix = "fail due to "; | |
int numThrown = 0; | |
for (int i = 0; i < fails.length; i++) { | |
String message = fails[i].getMessage(); | |
if (LangUtil.isEmpty(message)) { | |
message = "<no message>"; | |
} else if (-1 != message.indexOf(USAGE_SUBSTRING)) { | |
continue; | |
} | |
Throwable t = fails[i].getThrown(); | |
if (null != t) { | |
numThrown++; | |
sb.append(prefix); | |
sb.append(LangUtil.unqualifiedClassName(t.getClass())); | |
String thrownMessage = t.getMessage(); | |
if (!LangUtil.isEmpty(thrownMessage)) { | |
sb.append(" \"" + thrownMessage + "\""); | |
} | |
} | |
sb.append("\"" + message + "\""); | |
prefix = ", "; | |
} | |
if (0 < sb.length()) { | |
sb.append(" (" + numThrown + " exceptions)"); | |
throw new BuildException(sb.toString()); | |
} | |
} | |
} | |
return command.isFailed(); | |
} | |
/** | |
* Execute in a separate VM. Differences from normal same-VM execution: | |
* <ul> | |
* <li>ignores any message holder {class} set</li> | |
* <li>No resource-copying between interative runs</li> | |
* <li>failonerror fails when process interface fails to return negative values</li> | |
* </ul> | |
* | |
* @param args String[] of the complete compiler command to execute | |
* @return true on successful compilation | |
* | |
* @see DefaultCompilerAdapter#executeExternalCompile(String[], int) | |
* @throws BuildException if ajc aborts (negative value) or if failonerror and there were compile errors. | |
*/ | |
protected boolean executeInOtherVM(String[] args) { | |
javaCmd.setClassname(Main.class.getName()); | |
final Path vmClasspath = javaCmd.createClasspath(getProject()); | |
{ | |
File aspectjtools = null; | |
int vmClasspathSize = vmClasspath.size(); | |
if ((null != forkclasspath) && (0 != forkclasspath.size())) { | |
vmClasspath.addExisting(forkclasspath); | |
} else { | |
aspectjtools = findAspectjtoolsJar(); | |
if (null != aspectjtools) { | |
vmClasspath.createPathElement().setLocation(aspectjtools); | |
} | |
} | |
int newVmClasspathSize = vmClasspath.size(); | |
if (vmClasspathSize == newVmClasspathSize) { | |
String m = "unable to find aspectjtools to fork - "; | |
if (null != aspectjtools) { | |
m += "tried " + aspectjtools.toString(); | |
} else if (null != forkclasspath) { | |
m += "tried " + forkclasspath.toString(); | |
} else { | |
m += "define forkclasspath or put aspectjtools on classpath"; | |
} | |
throw new BuildException(m); | |
} | |
} | |
if (null != maxMem) { | |
javaCmd.setMaxmemory(maxMem); | |
} | |
File tempFile = null; | |
int numArgs = args.length; | |
args = GuardedCommand.limitTo(args, MAX_COMMANDLINE, getLocation()); | |
if (args.length != numArgs) { | |
tempFile = new File(args[1]); | |
} | |
try { | |
boolean setMessageHolderOnForking = (this.messageHolder != null); | |
String[] javaArgs = javaCmd.getCommandline(); | |
String[] both = new String[javaArgs.length + args.length + (setMessageHolderOnForking ? 2 : 0)]; | |
System.arraycopy(javaArgs, 0, both, 0, javaArgs.length); | |
System.arraycopy(args, 0, both, javaArgs.length, args.length); | |
if (setMessageHolderOnForking) { | |
both[both.length - 2] = "-messageHolder"; | |
both[both.length - 1] = this.messageHolder.getClass().getName(); | |
} | |
// try to use javaw instead on windows | |
if (both[0].endsWith("java.exe")) { | |
String path = both[0]; | |
path = path.substring(0, path.length() - 4); | |
path = path + "w.exe"; | |
File javaw = new File(path); | |
if (javaw.canRead() && javaw.isFile()) { | |
both[0] = path; | |
} | |
} | |
logVerbose("forking " + Arrays.asList(both)); | |
int result = execInOtherVM(both); | |
if (0 > result) { | |
throw new BuildException("failure[" + result + "] running ajc"); | |
} else if (failonerror && (0 < result)) { | |
throw new BuildException("compile errors: " + result); | |
} | |
// when forking, do completion only at end and when successful | |
doCompletionTasks(); | |
} finally { | |
if (null != tempFile) { | |
tempFile.delete(); | |
} | |
} | |
// if we get here, then compilation was successful | |
return true; | |
} | |
/** | |
* Execute in another process using the same JDK and the base directory of the project. XXX correct? | |
* | |
* @param args | |
* @return | |
*/ | |
protected int execInOtherVM(String[] args) { | |
try { | |
Project project = getProject(); | |
PumpStreamHandler handler = new LogStreamHandler(this, verbose ? Project.MSG_VERBOSE : Project.MSG_INFO, | |
Project.MSG_WARN); | |
// replace above two lines with what follows as an aid to debugging when running the unit tests.... | |
// LogStreamHandler handler = new LogStreamHandler(this, | |
// Project.MSG_INFO, Project.MSG_WARN) { | |
// | |
// ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
// /* (non-Javadoc) | |
// * @see org.apache.tools.ant.taskdefs.PumpStreamHandler#createProcessOutputPump(java.io.InputStream, | |
// java.io.OutputStream) | |
// */ | |
// protected void createProcessErrorPump(InputStream is, | |
// OutputStream os) { | |
// super.createProcessErrorPump(is, baos); | |
// } | |
// | |
// /* (non-Javadoc) | |
// * @see org.apache.tools.ant.taskdefs.LogStreamHandler#stop() | |
// */ | |
// public void stop() { | |
// byte[] written = baos.toByteArray(); | |
// System.err.print(new String(written)); | |
// super.stop(); | |
// } | |
// }; | |
Execute exe = new Execute(handler); | |
exe.setAntRun(project); | |
exe.setWorkingDirectory(project.getBaseDir()); | |
exe.setCommandline(args); | |
try { | |
if (executingInOtherVM) { | |
String s = "already running in other vm?"; | |
throw new BuildException(s, location); | |
} | |
executingInOtherVM = true; | |
exe.execute(); | |
} finally { | |
executingInOtherVM = false; | |
} | |
return exe.getExitValue(); | |
} catch (IOException e) { | |
String m = "Error executing command " + Arrays.asList(args); | |
throw new BuildException(m, e, location); | |
} | |
} | |
// ------------------------------ setup and reporting | |
/** @return null if path null or empty, String rendition otherwise */ | |
protected static void addFlaggedPath(String flag, Path path, List list) { | |
if (!LangUtil.isEmpty(flag) && ((null != path) && (0 < path.size()))) { | |
list.add(flag); | |
list.add(path.toString()); | |
} | |
} | |
/** | |
* Add to list any path or plural arguments. | |
*/ | |
protected void addListArgs(List list) throws BuildException { | |
addFlaggedPath("-classpath", classpath, list); | |
addFlaggedPath("-bootclasspath", bootclasspath, list); | |
addFlaggedPath("-extdirs", extdirs, list); | |
addFlaggedPath("-aspectpath", aspectpath, list); | |
addFlaggedPath("-injars", injars, list); | |
addFlaggedPath("-inpath", inpath, list); | |
addFlaggedPath("-sourceroots", sourceRoots, list); | |
if (argfiles != null) { | |
String[] files = argfiles.list(); | |
for (int i = 0; i < files.length; i++) { | |
File argfile = project.resolveFile(files[i]); | |
if (check(argfile, files[i], false, location)) { | |
list.add("-argfile"); | |
list.add(argfile.getAbsolutePath()); | |
} | |
} | |
} | |
if (inxmlfiles != null) { | |
String[] files = inxmlfiles.list(); | |
for (int i = 0; i < files.length; i++) { | |
File inxmlfile = project.resolveFile(files[i]); | |
if (check(inxmlfile, files[i], false, location)) { | |
list.add("-xmlConfigured"); | |
list.add(inxmlfile.getAbsolutePath()); | |
} | |
} | |
} | |
if (srcdir != null) { | |
// todo: ignore any srcdir if any argfiles and no explicit includes | |
String[] dirs = srcdir.list(); | |
for (int i = 0; i < dirs.length; i++) { | |
File dir = project.resolveFile(dirs[i]); | |
check(dir, dirs[i], true, location); | |
// relies on compiler to prune non-source files | |
String[] files = getDirectoryScanner(dir).getIncludedFiles(); | |
for (int j = 0; j < files.length; j++) { | |
File file = new File(dir, files[j]); | |
if (FileUtil.hasSourceSuffix(file)) { | |
if (!list.contains(file.getAbsolutePath())) { | |
list.add(file.getAbsolutePath()); | |
} | |
} | |
} | |
} | |
} | |
if (0 < adapterFiles.size()) { | |
for (Iterator iter = adapterFiles.iterator(); iter.hasNext();) { | |
File file = (File) iter.next(); | |
if (file.canRead() && FileUtil.hasSourceSuffix(file)) { | |
list.add(file.getAbsolutePath()); | |
} else { | |
this.logger.warning("skipping file: " + file); | |
} | |
} | |
} | |
} | |
/** | |
* Throw BuildException unless file is valid. | |
* | |
* @param file the File to check | |
* @param name the symbolic name to print on error | |
* @param isDir if true, verify file is a directory | |
* @param loc the Location used to create sensible BuildException | |
* @return | |
* @throws BuildException unless file valid | |
*/ | |
protected final boolean check(File file, String name, boolean isDir, Location loc) { | |
loc = loc != null ? loc : location; | |
if (file == null) { | |
throw new BuildException(name + " is null!", loc); | |
} | |
if (!file.exists()) { | |
throw new BuildException(file + " doesn't exist!", loc); | |
} | |
if (isDir ^ file.isDirectory()) { | |
String e = file + " should" + (isDir ? "" : "n't") + " be a directory!"; | |
throw new BuildException(e, loc); | |
} | |
return true; | |
} | |
/** | |
* Called when compile or incremental compile is completing, this completes the output jar or directory by copying resources if | |
* requested. Note: this is a callback run synchronously by the compiler. That means exceptions thrown here are caught by | |
* Main.run(..) and passed to the message handler. | |
*/ | |
protected void doCompletionTasks() { | |
if (!executing) { | |
throw new IllegalStateException("should be executing"); | |
} | |
if (null != outjar) { | |
completeOutjar(); | |
} else { | |
completeDestdir(); | |
} | |
if (null != xdoneSignal) { | |
MessageUtil.info(messageHolder, xdoneSignal); | |
} | |
} | |
/** | |
* Complete the destination directory by copying resources from the source root directories (if the filter is specified) and | |
* non-.class files from the input jars (if XCopyInjars is enabled). | |
*/ | |
private void completeDestdir() { | |
if (!copyInjars && (null == sourceRootCopyFilter) && (null == inpathDirCopyFilter)) { | |
return; | |
} else if ((destDir == DEFAULT_DESTDIR) || !destDir.canWrite()) { | |
String s = "unable to copy resources to destDir: " + destDir; | |
throw new BuildException(s); | |
} | |
final Project project = getProject(); | |
if (copyInjars) { // XXXX remove as unused since 1.1.1 | |
if (null != inpath) { | |
log("copyInjars does not support inpath.\n", Project.MSG_WARN); | |
} | |
String taskName = getTaskName() + " - unzip"; | |
String[] paths = injars.list(); | |
if (!LangUtil.isEmpty(paths)) { | |
PatternSet patternSet = new PatternSet(); | |
patternSet.setProject(project); | |
patternSet.setIncludes("**/*"); | |
patternSet.setExcludes("**/*.class"); | |
for (int i = 0; i < paths.length; i++) { | |
Expand unzip = new Expand(); | |
unzip.setProject(project); | |
unzip.setTaskName(taskName); | |
unzip.setDest(destDir); | |
unzip.setSrc(new File(paths[i])); | |
unzip.addPatternset(patternSet); | |
unzip.execute(); | |
} | |
} | |
} | |
if ((null != sourceRootCopyFilter) && (null != sourceRoots)) { | |
String[] paths = sourceRoots.list(); | |
if (!LangUtil.isEmpty(paths)) { | |
Copy copy = new Copy(); | |
copy.setProject(project); | |
copy.setTodir(destDir); | |
for (int i = 0; i < paths.length; i++) { | |
FileSet fileSet = new FileSet(); | |
fileSet.setDir(new File(paths[i])); | |
fileSet.setIncludes("**/*"); | |
fileSet.setExcludes(sourceRootCopyFilter); | |
copy.addFileset(fileSet); | |
} | |
copy.execute(); | |
} | |
} | |
if ((null != inpathDirCopyFilter) && (null != inpath)) { | |
String[] paths = inpath.list(); | |
if (!LangUtil.isEmpty(paths)) { | |
Copy copy = new Copy(); | |
copy.setProject(project); | |
copy.setTodir(destDir); | |
boolean gotDir = false; | |
for (int i = 0; i < paths.length; i++) { | |
File inpathDir = new File(paths[i]); | |
if (inpathDir.isDirectory() && inpathDir.canRead()) { | |
if (!gotDir) { | |
gotDir = true; | |
} | |
FileSet fileSet = new FileSet(); | |
fileSet.setDir(inpathDir); | |
fileSet.setIncludes("**/*"); | |
fileSet.setExcludes(inpathDirCopyFilter); | |
copy.addFileset(fileSet); | |
} | |
} | |
if (gotDir) { | |
copy.execute(); | |
} | |
} | |
} | |
} | |
/** | |
* Complete the output jar by copying resources from the source root directories if the filter is specified. and non-.class | |
* files from the input jars if enabled. | |
*/ | |
private void completeOutjar() { | |
if (((null == tmpOutjar) || !tmpOutjar.canRead()) | |
|| (!copyInjars && (null == sourceRootCopyFilter) && (null == inpathDirCopyFilter))) { | |
return; | |
} | |
Zip zip = new Zip(); | |
Project project = getProject(); | |
zip.setProject(project); | |
zip.setTaskName(getTaskName() + " - zip"); | |
zip.setDestFile(outjar); | |
ZipFileSet zipfileset = new ZipFileSet(); | |
zipfileset.setProject(project); | |
zipfileset.setSrc(tmpOutjar); | |
zipfileset.setIncludes("**/*.class"); | |
zip.addZipfileset(zipfileset); | |
if (copyInjars) { | |
String[] paths = injars.list(); | |
if (!LangUtil.isEmpty(paths)) { | |
for (int i = 0; i < paths.length; i++) { | |
File jarFile = new File(paths[i]); | |
zipfileset = new ZipFileSet(); | |
zipfileset.setProject(project); | |
zipfileset.setSrc(jarFile); | |
zipfileset.setIncludes("**/*"); | |
zipfileset.setExcludes("**/*.class"); | |
zip.addZipfileset(zipfileset); | |
} | |
} | |
} | |
if ((null != sourceRootCopyFilter) && (null != sourceRoots)) { | |
String[] paths = sourceRoots.list(); | |
if (!LangUtil.isEmpty(paths)) { | |
for (int i = 0; i < paths.length; i++) { | |
File srcRoot = new File(paths[i]); | |
FileSet fileset = new FileSet(); | |
fileset.setProject(project); | |
fileset.setDir(srcRoot); | |
fileset.setIncludes("**/*"); | |
fileset.setExcludes(sourceRootCopyFilter); | |
zip.addFileset(fileset); | |
} | |
} | |
} | |
if ((null != inpathDirCopyFilter) && (null != inpath)) { | |
String[] paths = inpath.list(); | |
if (!LangUtil.isEmpty(paths)) { | |
for (int i = 0; i < paths.length; i++) { | |
File inpathDir = new File(paths[i]); | |
if (inpathDir.isDirectory() && inpathDir.canRead()) { | |
FileSet fileset = new FileSet(); | |
fileset.setProject(project); | |
fileset.setDir(inpathDir); | |
fileset.setIncludes("**/*"); | |
fileset.setExcludes(inpathDirCopyFilter); | |
zip.addFileset(fileset); | |
} | |
} | |
} | |
} | |
zip.execute(); | |
} | |
// -------------------------- compiler adapter interface extras | |
/** | |
* Add specified source files. | |
*/ | |
void addFiles(File[] paths) { | |
for (int i = 0; i < paths.length; i++) { | |
addFile(paths[i]); | |
} | |
} | |
/** | |
* Add specified source file. | |
*/ | |
void addFile(File path) { | |
if (null != path) { | |
adapterFiles.add(path); | |
} | |
} | |
/** | |
* Read arguments in as if from a command line, mainly to support compiler adapter compilerarg subelement. | |
* | |
* @param args the String[] of arguments to read | |
*/ | |
public void readArguments(String[] args) { // XXX slow, stupid, unmaintainable | |
if ((null == args) || (0 == args.length)) { | |
return; | |
} | |
/** String[] wrapper with increment, error reporting */ | |
class Args { | |
final String[] args; | |
int index = 0; | |
Args(String[] args) { | |
this.args = args; // not null or empty | |
} | |
boolean hasNext() { | |
return index < args.length; | |
} | |
String next() { | |
String err = null; | |
if (!hasNext()) { | |
err = "need arg for flag " + args[args.length - 1]; | |
} else { | |
String s = args[index++]; | |
if (null == s) { | |
err = "null value"; | |
} else { | |
s = s.trim(); | |
if (0 == s.trim().length()) { | |
err = "no value"; | |
} else { | |
return s; | |
} | |
} | |
} | |
err += " at [" + index + "] of " + Arrays.asList(args); | |
throw new BuildException(err); | |
} | |
} // class Args | |
Args in = new Args(args); | |
String flag; | |
while (in.hasNext()) { | |
flag = in.next(); | |
if ("-1.3".equals(flag)) { | |
setCompliance(flag); | |
} else if ("-1.4".equals(flag)) { | |
setCompliance(flag); | |
} else if ("-1.5".equals(flag)) { | |
setCompliance("1.5"); | |
} else if ("-argfile".equals(flag)) { | |
setArgfiles(new Path(project, in.next())); | |
} else if ("-aspectpath".equals(flag)) { | |
setAspectpath(new Path(project, in.next())); | |
} else if ("-classpath".equals(flag)) { | |
setClasspath(new Path(project, in.next())); | |
} else if ("-extdirs".equals(flag)) { | |
setExtdirs(new Path(project, in.next())); | |
} else if ("-Xcopyinjars".equals(flag)) { | |
setCopyInjars(true); // ignored - will be flagged by setter | |
} else if ("-g".equals(flag)) { | |
setDebug(true); | |
} else if (flag.startsWith("-g:")) { | |
setDebugLevel(flag.substring(2)); | |
} else if ("-deprecation".equals(flag)) { | |
setDeprecation(true); | |
} else if ("-d".equals(flag)) { | |
setDestdir(new File(in.next())); | |
} else if ("-crossrefs".equals(flag)) { | |
setCrossrefs(true); | |
} else if ("-emacssym".equals(flag)) { | |
setEmacssym(true); | |
} else if ("-encoding".equals(flag)) { | |
setEncoding(in.next()); | |
} else if ("-Xfailonerror".equals(flag)) { | |
setFailonerror(true); | |
} else if ("-fork".equals(flag)) { | |
setFork(true); | |
} else if ("-forkclasspath".equals(flag)) { | |
setForkclasspath(new Path(project, in.next())); | |
} else if ("-help".equals(flag)) { | |
setHelp(true); | |
} else if ("-incremental".equals(flag)) { | |
setIncremental(true); | |
} else if ("-injars".equals(flag)) { | |
setInjars(new Path(project, in.next())); | |
} else if ("-inpath".equals(flag)) { | |
setInpath(new Path(project, in.next())); | |
} else if ("-Xlistfileargs".equals(flag)) { | |
setListFileArgs(true); | |
} else if ("-Xmaxmem".equals(flag)) { | |
setMaxmem(in.next()); | |
} else if ("-Xmessageholderclass".equals(flag)) { | |
setMessageHolderClass(in.next()); | |
} else if ("-noexit".equals(flag)) { | |
setNoExit(true); | |
} else if ("-noimport".equals(flag)) { | |
setNoExit(true); | |
} else if ("-noExit".equals(flag)) { | |
setNoExit(true); | |
} else if ("-noImportError".equals(flag)) { | |
setNoImportError(true); | |
} else if ("-noWarn".equals(flag)) { | |
setNowarn(true); | |
} else if ("-noexit".equals(flag)) { | |
setNoExit(true); | |
} else if ("-outjar".equals(flag)) { | |
setOutjar(new File(in.next())); | |
} else if ("-outxml".equals(flag)) { | |
setOutxml(true); | |
} else if ("-outxmlfile".equals(flag)) { | |
setOutxmlfile(in.next()); | |
} else if ("-preserveAllLocals".equals(flag)) { | |
setPreserveAllLocals(true); | |
} else if ("-proceedOnError".equals(flag)) { | |
setProceedOnError(true); | |
} else if ("-referenceInfo".equals(flag)) { | |
setReferenceInfo(true); | |
} else if ("-source".equals(flag)) { | |
setSource(in.next()); | |
} else if ("-Xsourcerootcopyfilter".equals(flag)) { | |
setSourceRootCopyFilter(in.next()); | |
} else if ("-sourceroots".equals(flag)) { | |
setSourceRoots(new Path(project, in.next())); | |
} else if ("-Xsrcdir".equals(flag)) { | |
setSrcDir(new Path(project, in.next())); | |
} else if ("-Xtagfile".equals(flag)) { | |
setTagFile(new File(in.next())); | |
} else if ("-target".equals(flag)) { | |
setTarget(in.next()); | |
} else if ("-time".equals(flag)) { | |
setTime(true); | |
} else if ("-time".equals(flag)) { | |
setTime(true); | |
} else if ("-verbose".equals(flag)) { | |
setVerbose(true); | |
} else if ("-showWeaveInfo".equals(flag)) { | |
setShowWeaveInfo(true); | |
} else if ("-version".equals(flag)) { | |
setVersion(true); | |
} else if ("-warn".equals(flag)) { | |
setWarn(in.next()); | |
} else if (flag.startsWith("-warn:")) { | |
setWarn(flag.substring(6)); | |
} else if ("-Xlint".equals(flag)) { | |
setXlintwarnings(true); | |
} else if (flag.startsWith("-Xlint:")) { | |
setXlint(flag.substring(7)); | |
} else if ("-Xlintfile".equals(flag)) { | |
setXlintfile(new File(in.next())); | |
} else if ("-XterminateAfterCompilation".equals(flag)) { | |
setXTerminateAfterCompilation(true); | |
} else if ("-Xreweavable".equals(flag)) { | |
setXReweavable(true); | |
} else if ("-XnotReweavable".equals(flag)) { | |
setXNotReweavable(true); | |
} else if (flag.startsWith("@")) { | |
File file = new File(flag.substring(1)); | |
if (file.canRead()) { | |
setArgfiles(new Path(project, file.getPath())); | |
} else { | |
ignore(flag); | |
} | |
} else { | |
File file = new File(flag); | |
if (file.isFile() && file.canRead() && FileUtil.hasSourceSuffix(file)) { | |
addFile(file); | |
} else { | |
ignore(flag); | |
} | |
} | |
} | |
} | |
protected void logVerbose(String text) { | |
if (this.verbose) { | |
this.logger.info(text); | |
} else { | |
this.logger.verbose(text); | |
} | |
} | |
/** | |
* Commandline wrapper that only permits addition of non-empty values and converts to argfile form if necessary. | |
*/ | |
public static class GuardedCommand { | |
Commandline command; | |
// int size; | |
static boolean isEmpty(String s) { | |
return ((null == s) || (0 == s.trim().length())); | |
} | |
GuardedCommand() { | |
command = new Commandline(); | |
} | |
void addFlag(String flag, boolean doAdd) { | |
if (doAdd && !isEmpty(flag)) { | |
command.createArgument().setValue(flag); | |
// size += 1 + flag.length(); | |
} | |
} | |
/** @return null if added or ignoreString otherwise */ | |
String addOption(String prefix, String[] validOptions, String input) { | |
if (isEmpty(input)) { | |
return null; | |
} | |
for (int i = 0; i < validOptions.length; i++) { | |
if (input.equals(validOptions[i])) { | |
if (isEmpty(prefix)) { | |
addFlag(input, true); | |
} else { | |
addFlagged(prefix, input); | |
} | |
return null; | |
} | |
} | |
return (null == prefix ? input : prefix + " " + input); | |
} | |
void addFlagged(String flag, String argument) { | |
if (!isEmpty(flag) && !isEmpty(argument)) { | |
command.addArguments(new String[] { flag, argument }); | |
// size += 1 + flag.length() + argument.length(); | |
} | |
} | |
// private void addFile(File file) { | |
// if (null != file) { | |
// String path = file.getAbsolutePath(); | |
// addFlag(path, true); | |
// } | |
// } | |
List extractArguments() { | |
ArrayList result = new ArrayList(); | |
String[] cmds = command.getArguments(); | |
if (!LangUtil.isEmpty(cmds)) { | |
result.addAll(Arrays.asList(cmds)); | |
} | |
return result; | |
} | |
/** | |
* Adjust args for size if necessary by creating an argument file, which should be deleted by the client after the compiler | |
* run has completed. | |
* | |
* @param max the int maximum length of the command line (in char) | |
* @return the temp File for the arguments (if generated), for deletion when done. | |
* @throws IllegalArgumentException if max is negative | |
*/ | |
static String[] limitTo(String[] args, int max, Location location) { | |
if (max < 0) { | |
throw new IllegalArgumentException("negative max: " + max); | |
} | |
// sigh - have to count anyway for now | |
int size = 0; | |
for (int i = 0; (i < args.length) && (size < max); i++) { | |
size += 1 + (null == args[i] ? 0 : args[i].length()); | |
} | |
if (size <= max) { | |
return args; | |
} | |
File tmpFile = null; | |
PrintWriter out = null; | |
// adapted from DefaultCompilerAdapter.executeExternalCompile | |
try { | |
String userDirName = System.getProperty("user.dir"); | |
File userDir = new File(userDirName); | |
tmpFile = File.createTempFile("argfile", "", userDir); | |
out = new PrintWriter(new FileWriter(tmpFile)); | |
for (int i = 0; i < args.length; i++) { | |
out.println(args[i]); | |
} | |
out.flush(); | |
return new String[] { "-argfile", tmpFile.getAbsolutePath() }; | |
} catch (IOException e) { | |
throw new BuildException("Error creating temporary file", e, location); | |
} finally { | |
if (out != null) { | |
try { | |
out.close(); | |
} catch (Throwable t) { | |
} | |
} | |
} | |
} | |
} | |
private static class AntMessageHandler implements IMessageHandler { | |
private TaskLogger logger; | |
private final boolean taskLevelVerbose; | |
private final boolean handledMessage; | |
public AntMessageHandler(TaskLogger logger, boolean taskVerbose, boolean handledMessage) { | |
this.logger = logger; | |
this.taskLevelVerbose = taskVerbose; | |
this.handledMessage = handledMessage; | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see org.aspectj.bridge.IMessageHandler#handleMessage(org.aspectj.bridge.IMessage) | |
*/ | |
public boolean handleMessage(IMessage message) throws AbortException { | |
Kind messageKind = message.getKind(); | |
String messageText = message.toString(); | |
if (messageKind == IMessage.ABORT) { | |
this.logger.error(messageText); | |
} else if (messageKind == IMessage.DEBUG) { | |
this.logger.debug(messageText); | |
} else if (messageKind == IMessage.ERROR) { | |
this.logger.error(messageText); | |
} else if (messageKind == IMessage.FAIL) { | |
this.logger.error(messageText); | |
} else if (messageKind == IMessage.INFO) { | |
if (this.taskLevelVerbose) { | |
this.logger.info(messageText); | |
} else { | |
this.logger.verbose(messageText); | |
} | |
} else if (messageKind == IMessage.WARNING) { | |
this.logger.warning(messageText); | |
} else if (messageKind == IMessage.WEAVEINFO) { | |
this.logger.info(messageText); | |
} else if (messageKind == IMessage.TASKTAG) { | |
// ignore | |
} else { | |
throw new BuildException("Unknown message kind from AspectJ compiler: " + messageKind.toString()); | |
} | |
return handledMessage; | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see org.aspectj.bridge.IMessageHandler#isIgnoring(org.aspectj.bridge.IMessage.Kind) | |
*/ | |
public boolean isIgnoring(Kind kind) { | |
return false; | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see org.aspectj.bridge.IMessageHandler#dontIgnore(org.aspectj.bridge.IMessage.Kind) | |
*/ | |
public void dontIgnore(Kind kind) { | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see org.aspectj.bridge.IMessageHandler#ignore(org.aspectj.bridge.IMessage.Kind) | |
*/ | |
public void ignore(Kind kind) { | |
} | |
} | |
} |