CompilerRun.java
/* *******************************************************************
* 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 v 2.0
* which accompanies this distribution and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
*
* Contributors:
* Xerox/PARC initial implementation
* Wes Isberg 2003 updates
* ******************************************************************/
package org.aspectj.testing.harness.bridge;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.aspectj.bridge.ICommand;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.MessageHandler;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.bridge.ReflectionFactory;
import org.aspectj.testing.ajde.CompileCommand;
import org.aspectj.testing.run.IRunIterator;
import org.aspectj.testing.run.IRunStatus;
import org.aspectj.testing.run.WrappedRunIterator;
import org.aspectj.testing.taskdefs.AjcTaskCompileCommand;
import org.aspectj.testing.util.options.Option;
import org.aspectj.testing.util.options.Option.Family;
import org.aspectj.testing.util.options.Option.InvalidInputException;
import org.aspectj.testing.util.options.Options;
import org.aspectj.testing.util.options.Values;
import org.aspectj.testing.xml.SoftMessage;
import org.aspectj.testing.xml.XMLWriter;
import org.aspectj.util.FileUtil;
import org.aspectj.util.LangUtil;
/**
* Run the compiler once.
* The lifecycle is as follows:
* <ul>
* <li>Spec (specification) is created.</li>
* <li>This is created using the Spec.</li>
* <li>setupAjcRun(Sandbox, Validator) is invoked,
* at which point this populates the shared sandbox
* with values derived from the spec and also
* sets up internal state based on both the sandbox
* and the spec.</li>
* <li>run(IRunStatus) is invoked, and this runs the compiler
* based on internal state, the spec, and the sandbox.</li>
* </ul>
* Programmer notes:
* <ul>
* <li>Paths are resolved absolutely, which fails to test the
* compiler's ability to find files relative to a source base</li>
* <li>This does not enforce the lifecycle.</li>
* <li>This must be used as the initial compile
* before doing an incremental compile.
* In that case, staging must be enabled.</li>
* </ul>
*/
public class CompilerRun implements IAjcRun {
// static final String JAVAC_COMPILER
// = JavacCompileCommand.class.getName();
static final String[] RA_String = new String[0];
static final String[] JAR_SUFFIXES = new String[] { ".jar", ".zip" };
static final String[] SOURCE_SUFFIXES =
(String[]) FileUtil.SOURCE_SUFFIXES.toArray(new String[0]);
/** specifications, set on construction */
Spec spec;
//------------ calculated during setup
/** get shared stuff during setup */
Sandbox sandbox;
/**
* During run, these String are passed as the source and arg files to compile.
* The list is set up in setupAjcRun(..), when arg files are prefixed with "@".
*/
final List<String> arguments;
/**
* During run, these String are collapsed and passed as the injar option.
* The list is set up in setupAjcRun(..).
*/
final List<String> injars;
/**
* During run, these String are collapsed and passed as the inpath option.
* The list is set up in setupAjcRun(..),
* which extracts only directories from the files attribute.
*/
final List<String> inpaths;
private CompilerRun(Spec spec) {
if (null == spec) {
throw new IllegalArgumentException("null spec");
}
this.spec = spec;
arguments = new ArrayList<>();
injars = new ArrayList<>();
inpaths = new ArrayList<>();
}
/**
* Select from input String[] if readable directories
* @param inputs String[] of input - null ignored
* @param baseDir the base directory of the input
* @return String[] of input that end with any input
*/
public static String[] selectDirectories(String[] inputs, File baseDir) {
if (LangUtil.isEmpty(inputs)) {
return new String[0];
}
ArrayList<String> result = new ArrayList<>();
for (String input : inputs) {
if (null == input) {
continue;
}
File inputFile = new File(baseDir, input);
if (inputFile.canRead() && inputFile.isDirectory()) {
result.add(input);
}
}
return (String[]) result.toArray(new String[0]);
}
/**
* Select from input String[] based on suffix-matching
* @param inputs String[] of input - null ignored
* @param suffixes String[] of suffix selectors - null ignored
* @param ignoreCase if true, ignore case
* @return String[] of input that end with any input
*/
public static String[] endsWith(String[] inputs, String[] suffixes, boolean ignoreCase) {
if (LangUtil.isEmpty(inputs) || LangUtil.isEmpty(suffixes)) {
return new String[0];
}
if (ignoreCase) {
String[] temp = new String[suffixes.length];
for (int i = 0; i < temp.length; i++) {
String suff = suffixes[i];
temp[i] = (null == suff ? null : suff.toLowerCase());
}
suffixes = temp;
}
ArrayList<String> result = new ArrayList<>();
for (String s : inputs) {
String input = s;
if (null == input) {
continue;
}
if (!ignoreCase) {
input = input.toLowerCase();
}
for (String suffix : suffixes) {
if (null == suffix) {
continue;
}
if (input.endsWith(suffix)) {
result.add(input);
break;
}
}
}
return (String[]) result.toArray(new String[0]);
}
/**
* This checks that the spec is reasonable and does setup:
* <ul>
* <li>calculate and set sandbox testBaseSrcDir as {Sandbox.testBaseDir}/
* {Spec.testSrcDirOffset}/<li>
* <li>get the list of source File to compile as {Sandbox.testBaseSrcDir} /
* {Spec.getPaths..}</li>
* <li>get the list of extraClasspath entries to add to default classpath as
* {Sandbox.testBaseSrcDir} / {Spec.classpath..}</li>
* <li>get the list of aspectpath entries to use as the aspectpath as
* {Sandbox. testBaseSrcDir} / {Spec.aspectpath..}</li>
* </ul>
* All sources must be readable at this time,
* unless spec.badInput is true (for invalid-input tests).
* If staging, the source files and source roots are copied
* to a separate staging directory so they can be modified
* for incremental tests. Note that (as of this writing) the
* compiler only handles source roots for incremental tests.
* @param classesDir the File
* @see org.aspectj.testing.harness.bridge.AjcTest.IAjcRun#setup(File, File)
* @throws AbortException containing IOException or IllegalArgumentException
* if the staging operations fail
*/
public boolean setupAjcRun(Sandbox sandbox, Validator validator) {
if (!validator.nullcheck(spec.getOptionsArray(), "localOptions")
|| !validator.nullcheck(sandbox, "sandbox")
|| !validator.nullcheck(spec.compiler, "compilerName")
|| !validator.canRead(Globals.F_aspectjrt_jar, "aspectjrt.jar")
|| !validator.canRead(
Globals.F_testingclient_jar,
"testing-client.jar")) {
return false;
}
this.sandbox = sandbox;
String rdir = spec.testSrcDirOffset;
File testBaseSrcDir;
if ((null == rdir) || (0 == rdir.length())) {
testBaseSrcDir = sandbox.testBaseDir;
} else {
testBaseSrcDir = new File(sandbox.testBaseDir, rdir);
// XXX what if rdir is two levels deep?
if (!validator
.canReadDir(testBaseSrcDir, "sandbox.testBaseSrcDir")) {
return false;
}
}
// Sources come as relative paths - check read, copy if staging.
// This renders paths absolute before run(RunStatusI) is called.
// For a compile run to support relative paths + source base,
// change so the run calculates the paths (differently when staging)
final String[] inpathPaths;
final String[] injarPaths;
final String[] srcPaths;
{
final String[] paths = spec.getPathsArray();
srcPaths =
endsWith(
paths,
CompilerRun.SOURCE_SUFFIXES,
true);
injarPaths =
endsWith(paths, CompilerRun.JAR_SUFFIXES, true);
inpathPaths =
selectDirectories(paths, testBaseSrcDir);
if (!spec.badInput) {
int found = inpathPaths.length + injarPaths.length + srcPaths.length;
if (paths.length != found) {
validator.fail("found " + found + " of " + paths.length + " sources");
}
}
}
// validate readable for sources
if (!spec.badInput) {
if (!validator.canRead(testBaseSrcDir, srcPaths, "sources")
// see validation of inpathPaths below due to ambiguous base dir
|| !validator.canRead(
testBaseSrcDir,
spec.argfiles,
"argfiles")
|| !validator.canRead(
testBaseSrcDir,
spec.classpath,
"classpath")
|| !validator.canRead(
testBaseSrcDir,
spec.aspectpath,
"aspectpath")
|| !validator.canRead(
testBaseSrcDir,
spec.sourceroots,
"sourceroots")
|| !validator.canRead(
testBaseSrcDir,
spec.extdirs,
"extdirs")) {
return false;
}
}
int numSources =
srcPaths.length
+ injarPaths.length
+ inpathPaths.length
+ spec.argfiles.length
+ spec.sourceroots.length;
if (!spec.badInput && (numSources < 1)) {
validator.fail(
"no input jars, arg files, or source files or roots");
return false;
}
final File[] argFiles =
FileUtil.getBaseDirFiles(testBaseSrcDir, spec.argfiles);
final File[] injarFiles =
FileUtil.getBaseDirFiles(testBaseSrcDir, injarPaths);
final File[] inpathFiles =
FileUtil.getBaseDirFiles(testBaseSrcDir, inpathPaths);
final File[] aspectFiles =
FileUtil.getBaseDirFiles(testBaseSrcDir, spec.aspectpath);
final File[] extdirFiles =
FileUtil.getBaseDirFiles(testBaseSrcDir, spec.extdirs);
final File[] classFiles =
FileUtil.getBaseDirFiles(testBaseSrcDir, spec.classpath);
final File[] xlintFiles = (null == spec.xlintfile ? new File[0]
: FileUtil.getBaseDirFiles(testBaseSrcDir, new String[] {spec.xlintfile}));
// injars might be outjars in the classes dir...
for (int i = 0; i < injarFiles.length; i++) {
if (!injarFiles[i].exists()) {
injarFiles[i] = new File(sandbox.classesDir, injarPaths[i]);
}
}
for (int i = 0; i < inpathFiles.length; i++) {
if (!inpathFiles[i].exists()) {
inpathFiles[i] = new File(sandbox.classesDir, inpathPaths[i]);
}
}
// moved after solving any injars that were outjars
if (!validator.canRead(injarFiles, "injars")
|| !validator.canRead(injarFiles, "injars")) {
return false;
}
// hmm - duplicates validation above, verifying getBaseDirFiles?
if (!spec.badInput) {
if (!validator.canRead(argFiles, "argFiles")
|| !validator.canRead(injarFiles, "injarFiles")
|| !validator.canRead(inpathFiles, "inpathFiles")
|| !validator.canRead(aspectFiles, "aspectFiles")
|| !validator.canRead(classFiles, "classFiles")
|| !validator.canRead(xlintFiles, "xlintFiles")) {
return false;
}
}
final File[] srcFiles;
File[] sourcerootFiles = new File[0];
// source text files are copied when staging incremental tests
if (!spec.isStaging()) {
// XXX why this? was always? || (testBaseSrcDir != sandbox.stagingDir))) {
srcFiles =
FileUtil.getBaseDirFiles(
testBaseSrcDir,
srcPaths,
CompilerRun.SOURCE_SUFFIXES);
if (!LangUtil.isEmpty(spec.sourceroots)) {
sourcerootFiles =
FileUtil.getBaseDirFiles(
testBaseSrcDir,
spec.sourceroots,
null);
}
} else { // staging - copy files
if (spec.badInput) {
validator.info(
"badInput ignored - files checked when staging");
}
try {
// copy all files, then remove tagged ones
// XXX make copyFiles support a filter?
srcFiles =
FileUtil.copyFiles(
testBaseSrcDir,
srcPaths,
sandbox.stagingDir);
if (!LangUtil.isEmpty(spec.sourceroots)) {
sourcerootFiles =
FileUtil.copyFiles(
testBaseSrcDir,
spec.sourceroots,
sandbox.stagingDir);
// delete incremental files in sourceroot after copying // XXX inefficient
// an incremental file has an extra "." in name
// most .java files don't, because they are named after
// the principle type they contain, and simple type names
// have no dots.
FileFilter pickIncFiles = new FileFilter() {
public boolean accept(File file) {
if (file.isDirectory()) {
// continue recursion
return true;
}
String path = file.getPath();
// only source files are relevant to staging
if (!FileUtil.hasSourceSuffix(path)) {
return false;
}
int first = path.indexOf(".");
int last = path.lastIndexOf(".");
return (first != last);
}
};
for (File sourcerootFile : sourcerootFiles) {
FileUtil.deleteContents(
sourcerootFile,
pickIncFiles,
false);
}
if (0 < sourcerootFiles.length) {
FileUtil.sleepPastFinalModifiedTime(
sourcerootFiles);
}
}
File[] files =
FileUtil.getBaseDirFiles(sandbox.stagingDir, srcPaths);
if (0 < files.length) {
FileUtil.sleepPastFinalModifiedTime(files);
}
} catch (IllegalArgumentException e) {
validator.fail("staging - bad input", e);
return false;
} catch (IOException e) {
validator.fail("staging - operations", e);
return false;
}
}
if (!spec.badInput
&& !validator.canRead(srcFiles, "copied paths")) {
return false;
}
arguments.clear();
if (!LangUtil.isEmpty(xlintFiles)) {
arguments.add("-Xlintfile");
String sr = FileUtil.flatten(xlintFiles, null);
arguments.add(sr);
}
if (spec.outjar != null) {
arguments.add("-outjar");
arguments.add(new File(sandbox.classesDir,spec.outjar).getPath());
}
if (!LangUtil.isEmpty(extdirFiles)) {
arguments.add("-extdirs");
String sr = FileUtil.flatten(extdirFiles, null);
arguments.add(sr);
}
if (!LangUtil.isEmpty(sourcerootFiles)) {
arguments.add("-sourceroots");
String sr = FileUtil.flatten(sourcerootFiles, null);
arguments.add(sr);
}
if (!LangUtil.isEmpty(srcFiles)) {
arguments.addAll(Arrays.asList(FileUtil.getPaths(srcFiles)));
}
injars.clear();
if (!LangUtil.isEmpty(injarFiles)) {
injars.addAll(Arrays.asList(FileUtil.getPaths(injarFiles)));
}
inpaths.clear();
if (!LangUtil.isEmpty(inpathFiles)) {
inpaths.addAll(Arrays.asList(FileUtil.getPaths(inpathFiles)));
}
if (!LangUtil.isEmpty(argFiles)) {
String[] ra = FileUtil.getPaths(argFiles);
for (String s : ra) {
arguments.add("@" + s);
}
if (!spec.badInput && spec.isStaging) {
validator.fail(
"warning: files listed in argfiles not staged");
}
}
// save classpath and aspectpath in sandbox for this and other clients
final boolean checkReadable = !spec.badInput;
int size = spec.includeClassesDir ? 3 : 2;
File[] cp = new File[size + classFiles.length];
System.arraycopy(classFiles, 0, cp, 0, classFiles.length);
int index = classFiles.length;
if (spec.includeClassesDir) {
cp[index++] = sandbox.classesDir;
}
cp[index++] = Globals.F_aspectjrt_jar;
cp[index++] = Globals.F_testingclient_jar;
sandbox.compilerRunInit(this, testBaseSrcDir, aspectFiles,
checkReadable, cp, checkReadable, null);
// XXX todo set bootclasspath if set for forking?
return true;
}
/**
* Setup result evaluation and command line, run, and evaluate result.
* <li>setup an AjcMessageHandler using the expected messages from
* {@link Spec#getMessages()}.<li>
* <li>heed any globals interpreted into a TestSetup by reading
* {@link Spec@getOptions()}. For a list of supported globals, see
* {@link setupArgs(ArrayList, IMessageHandler}.</li>
* <li>construct a command line, using as classpath
* {@link Sandbox.classpathToString()}<li>
* <li>construct a compiler using {@link Spec#compiler}
* or any overriding value set in TestSetup.<li>
* <li>Just before running, set the compiler in the sandbox using
* {@link Sandbox.setCompiler(ICommand)}.<li>
* <li>After running, report AjcMessageHandler results to the status parameter.
* If the AjcMessageHandler reports a failure, then send info messages
* for the Spec, TestSetup, and command line.<li>
* @see org.aspectj.testing.run.IRun#run(IRunStatus)
*/
public boolean run(IRunStatus status) {
if (null == spec.testSetup) {
MessageUtil.abort(
status,
"no test setup - adoptParentValues not called");
return false;
} else if (!spec.testSetup.result) {
MessageUtil.abort(status, spec.testSetup.failureReason);
return false;
}
AjcMessageHandler handler =
new AjcMessageHandler(spec.getMessages());
handler.init();
boolean handlerResult = false;
boolean result = false;
boolean commandResult = false;
ArrayList<String> argList = new ArrayList<>();
final Spec.TestSetup setupResult = spec.testSetup;
try {
if (spec.outjar == null) {
argList.add("-d");
String outputDirPath = sandbox.classesDir.getAbsolutePath();
try { // worth it to try for canonical?
outputDirPath = sandbox.classesDir.getCanonicalPath();
} catch (IOException e) {
MessageUtil.abort(
status,
"canonical " + sandbox.classesDir,
e);
}
argList.add(outputDirPath);
}
String path = sandbox.classpathToString(this);
if (!LangUtil.isEmpty(path)) {
argList.add("-classpath");
argList.add(path);
}
path = sandbox.getBootclasspath(this);
if (!LangUtil.isEmpty(path)) {
argList.add("-bootclasspath");
argList.add(path);
}
path = sandbox.aspectpathToString(this);
if (!LangUtil.isEmpty(path)) {
argList.add("-aspectpath");
argList.add(path);
}
if (0 < injars.size()) {
argList.add("-injars");
argList.add(
FileUtil.flatten(
(String[]) injars.toArray(new String[0]),
null));
}
if (0 < inpaths.size()) {
argList.add("-inpath");
argList.add(
FileUtil.flatten(
(String[]) inpaths.toArray(new String[0]),
null));
}
// put specified arguments last, for better badInput tests
argList.addAll(setupResult.commandOptions);
// add both java/aspectj and argfiles
argList.addAll(arguments);
// XXX hack - seek on request as a side effect. reimplement as listener
if (null != setupResult.seek) {
String slopPrefix = Spec.SEEK_MESSAGE_PREFIX + " slop - ";
PrintStream slop =
MessageUtil.handlerPrintStream(
status,
IMessage.INFO,
System.err,
slopPrefix);
List found =
FileUtil.lineSeek(
setupResult.seek,
arguments,
false,
slop);
if (!LangUtil.isEmpty(found)) {
for (Object o : found) {
MessageUtil.info(
status,
Spec.SEEK_MESSAGE_PREFIX + o);
}
}
}
ICommand compiler = spec.reuseCompiler
// throws IllegalStateException if null
? sandbox.getCommand(this)
: ReflectionFactory.makeCommand(setupResult.compilerName, status);
DirChanges dirChanges = null;
if (null == compiler) {
MessageUtil.fail(
status,
"unable to make compiler " + setupResult.compilerName);
return false;
} else {
if (setupResult.compilerName != Spec.DEFAULT_COMPILER) {
MessageUtil.info(
status,
"compiler: " + setupResult.compilerName);
}
if (status.aborted()) {
MessageUtil.debug(
status,
"aborted, but compiler valid?: " + compiler);
} else {
// same DirChanges handling for JavaRun, CompilerRun, IncCompilerRun
// XXX around advice or template method/class
if (!LangUtil.isEmpty(spec.dirChanges)) {
LangUtil.throwIaxIfFalse(
1 == spec.dirChanges.size(),
"expecting 0..1 dirChanges");
dirChanges =
new DirChanges(
(DirChanges.Spec) spec.dirChanges.get(0));
if (!dirChanges
.start(status, sandbox.classesDir)) {
return false; // setup failed
}
}
MessageUtil.info(
status,
compiler + "(" + argList + ")");
sandbox.setCommand(compiler, this);
String[] args = (String[]) argList.toArray(RA_String);
commandResult = compiler.runCommand(args, handler);
}
}
handlerResult = handler.passed();
if (!handlerResult) {
return false;
} else {
result = (commandResult == handler.expectingCommandTrue());
if (!result) {
String m =
commandResult
? "compile did not fail as expected"
: "compile failed unexpectedly";
MessageUtil.fail(status, m);
} else if (null != dirChanges) {
result = dirChanges.end(status, sandbox.testBaseDir);
}
}
return result;
} finally {
if (!handlerResult) { // more debugging context in case of failure
MessageUtil.info(handler, spec.toLongString());
MessageUtil.info(handler, "" + argList);
if (null != setupResult) {
MessageUtil.info(handler, "" + setupResult);
}
}
handler.report(status);
// XXX weak - actual messages not reported in real-time, no fast-fail
}
}
public String toString() {
return "CompilerRun(" + spec + ")";
}
/**
* Initializer/factory for CompilerRun
* any path or file is relative to this test base dir
*/
public static class Spec extends AbstractRunSpec {
public static final String XMLNAME = "compile";
public static final String DEFAULT_COMPILER =
ReflectionFactory.ECLIPSE;
static final String SEEK_PREFIX = "-seek:";
static final String SEEK_MESSAGE_PREFIX = "found: ";
private static final CRSOptions CRSOPTIONS = new CRSOptions();
/**
* Retitle description to title, paths to files, do comment,
* staging, badInput,
* do dirChanges, and print no chidren.
*/
private static final AbstractRunSpec.XMLNames NAMES =
new AbstractRunSpec.XMLNames(
AbstractRunSpec.XMLNames.DEFAULT,
"title",
null,
null,
null,
"files",
null,
null,
null,
false,
false,
true);
/**
* If the source version warrants, add a -bootclasspath
* entry to the list of arguments to add.
* This will fail and return an error String if the
* required library is not found.
* @param sourceVersion the String (if any) describing the -source option
* (expecting one of [null, "1.3", "1.4", "1.5"].
* @param compilerName the String name of the target compiler
* @param toAdd the ArrayList to add -bootclasspath to
* @return the String describing any errors, or null if no errors
*/
private static String updateBootclasspathForSourceVersion(
String sourceVersion,
String compilerName,
List<String> toAdd) {
if (null == sourceVersion) {
return null;
}
if (3 != sourceVersion.length()) {
throw new IllegalArgumentException(
"bad version: " + sourceVersion);
}
if (null == toAdd) {
throw new IllegalArgumentException("null toAdd");
}
int version = sourceVersion.charAt(2) - '0';
switch (version) {
case (3) :
if (Globals.supportsJava("1.4")) {
if (!FileUtil.canReadFile(Globals.J2SE13_RTJAR)) {
return "no 1.3 libraries to handle -source 1.3";
}
toAdd.add("-bootclasspath");
toAdd.add(Globals.J2SE13_RTJAR.getAbsolutePath());
}
break;
case (4) :
if (!Globals.supportsJava("1.4")) {
if (ReflectionFactory
.ECLIPSE
.equals(compilerName)) {
return "run eclipse under 1.4 to handle -source 1.4";
}
if (!FileUtil.canReadFile(Globals.J2SE14_RTJAR)) {
return "no 1.4 libraries to handle -source 1.4";
}
toAdd.add("-bootclasspath");
toAdd.add(Globals.J2SE14_RTJAR.getAbsolutePath());
}
break;
case (5) :
return "1.5 not supported in CompilerRun";
case (0) :
// ignore - no version specified
break;
default :
throw new Error("unexpected version: " + version);
}
return null;
}
static CRSOptions testAccessToCRSOptions() {
return CRSOPTIONS;
}
static Options testAccessToOptions() {
return CRSOPTIONS.getOptions();
}
private static String[] copy(String[] input) {
if (null == input) {
return null;
}
String[] result = new String[input.length];
System.arraycopy(input, 0, result, 0, input.length);
return result;
}
protected String compiler;
// use same command - see also IncCompiler.Spec.fresh
protected boolean reuseCompiler;
protected boolean permitAnyCompiler;
protected boolean includeClassesDir;
protected TestSetup testSetup;
protected String[] argfiles = new String[0];
protected String[] aspectpath = new String[0];
protected String[] classpath = new String[0];
protected String[] sourceroots = new String[0];
protected String[] extdirs = new String[0];
/** src path = {suiteParentDir}/{testBaseDirOffset}/{testSrcDirOffset}/{path} */
protected String testSrcDirOffset;
protected String xlintfile;
protected String outjar;
public Spec() {
super(XMLNAME);
setXMLNames(NAMES);
compiler = DEFAULT_COMPILER;
}
protected void initClone(Spec spec)
throws CloneNotSupportedException {
super.initClone(spec);
spec.argfiles = copy(argfiles);
spec.aspectpath = copy(aspectpath);
spec.classpath = copy(classpath);
spec.compiler = compiler;
spec.includeClassesDir = includeClassesDir;
spec.reuseCompiler = reuseCompiler;
spec.permitAnyCompiler = permitAnyCompiler;
spec.sourceroots = copy(sourceroots);
spec.extdirs = copy(extdirs);
spec.outjar = outjar;
spec.testSetup = null;
if (null != testSetup) {
spec.testSetup = (TestSetup) testSetup.clone();
}
spec.testSrcDirOffset = testSrcDirOffset;
}
public Object clone() throws CloneNotSupportedException {
Spec result = new Spec();
initClone(result);
return result;
}
public void setIncludeClassesDir(boolean include) {
this.includeClassesDir = include;
}
public void setReuseCompiler(boolean reuse) {
this.reuseCompiler = reuse;
}
public void setPermitAnyCompiler(boolean permitAny) {
this.permitAnyCompiler = permitAny;
}
public void setCompiler(String compilerName) {
this.compiler = compilerName;
}
public void setTestSrcDirOffset(String s) {
if (null != s) {
testSrcDirOffset = s;
}
}
/** override to set dirToken to Sandbox.CLASSES and default suffix to ".class" */
public void addDirChanges(DirChanges.Spec spec) {
if (null == spec) {
return;
}
spec.setDirToken(Sandbox.CLASSES_DIR);
spec.setDefaultSuffix(".class");
super.addDirChanges(spec);
}
public String toLongString() {
return getPrintName() + "(" + super.containedSummary() + ")";
}
public String toString() {
return getPrintName() + "(" + super.containedSummary() + ")";
}
/** bean mapping for writers */
public void setFiles(String paths) {
addPaths(paths);
}
/**
* Add to default classpath
* (which includes aspectjrt.jar and testing-client.jar).
* @param files comma-delimited list of classpath entries - ignored if
* null or empty
*/
public void setClasspath(String files) {
if (!LangUtil.isEmpty(files)) {
classpath = XMLWriter.unflattenList(files);
}
}
/**
* Set source roots, deleting any old ones
* @param files comma-delimited list of directories
* - ignored if null or empty
*/
public void setSourceroots(String dirs) {
if (!LangUtil.isEmpty(dirs)) {
sourceroots = XMLWriter.unflattenList(dirs);
}
}
public void setXlintfile(String path) {
xlintfile = path;
}
public void setOutjar(String path) {
outjar = path;
}
/**
* Set extension dirs, deleting any old ones
* @param files comma-delimited list of directories
* - ignored if null or empty
*/
public void setExtdirs(String dirs) {
if (!LangUtil.isEmpty(dirs)) {
extdirs = XMLWriter.unflattenList(dirs);
}
}
/**
* Set aspectpath, deleting any old ones
* @param files comma-delimited list of aspect jars - ignored if null or
* empty
*/
public void setAspectpath(String files) {
if (!LangUtil.isEmpty(files)) {
aspectpath = XMLWriter.unflattenList(files);
}
}
/**
* Set argfiles, deleting any old ones
* @param files comma-delimited list of argfiles - ignored if null or empty
*/
public void setArgfiles(String files) {
if (!LangUtil.isEmpty(files)) {
argfiles = XMLWriter.unflattenList(files);
}
}
/** @return String[] copy of argfiles array */
public String[] getArgfilesArray() {
String[] argfiles = this.argfiles;
if (LangUtil.isEmpty(argfiles)) {
return new String[0];
}
return (String[]) copy(argfiles);
}
/**
* Make a copy of the array.
* @return an array with the same component type as source
* containing same elements, even if null.
* @throws IllegalArgumentException if source is null
*/
public static final Object[] copy(Object[] source) {
LangUtil.throwIaxIfNull(source, "source");
final Class c = source.getClass().getComponentType();
Object[] result = (Object[]) Array.newInstance(c, source.length);
System.arraycopy(source, 0, result, 0, result.length);
return result;
}
/**
* This implementation skips if:
* <ul>
* <li>incremental test, but using ajc (not eclipse)</li>
* <li>usejavac, but javac is not available on the classpath</li>
* <li>eclipse, but -usejavac or -preprocess test</li>
* <li>-source 1.4, but running under 1.2 (XXX design)</li>
* <li>local/global option conflicts (-lenient/-strict)</li>
* <li>semantic conflicts (e.g., -lenient/-strict)</li>
* </ul>
* @return false if this wants to be skipped, true otherwise
*/
protected boolean doAdoptParentValues(
RT parentRuntime,
IMessageHandler handler) {
if (!super.doAdoptParentValues(parentRuntime, handler)) {
return false;
}
testSetup = setupArgs(handler);
if (!testSetup.result) {
skipMessage(handler, testSetup.failureReason);
}
return testSetup.result;
}
private String getShortCompilerName() {
String compilerClassName = compiler;
if (null != testSetup) {
compilerClassName = testSetup.compilerName;
}
if (null != compilerClassName) {
int loc = compilerClassName.lastIndexOf(".");
if (-1 != loc) {
compilerClassName =
compilerClassName.substring(loc + 1);
}
}
return compilerClassName;
}
/** @return a CompilerRun with this as spec if setup completes successfully. */
public IRunIterator makeRunIterator(
Sandbox sandbox,
Validator validator) {
CompilerRun run = new CompilerRun(this);
if (run.setupAjcRun(sandbox, validator)) {
// XXX need name for compilerRun
return new WrappedRunIterator(this, run);
}
return null;
}
protected String getPrintName() {
return "CompilerRun.Spec " + getShortCompilerName();
}
/**
* Each non-incremental run, fold the global flags in with
* the run flags, which may involve adding or removing from
* either list, depending on the flag prefix:
* <ul>
* <li>-foo: use -foo unless forced off.<li>
* <li>^foo: (force off) remove any -foo option from the run flags</li>
* <li>!foo: (force on) require the -foo flag </li>
* </ul>
* If there is a force conflict, then the test is skipped
* ("skipping" info message, TestSetup.result is false).
* This means an option local to the test which was specified
* without forcing may be overridden by a globally-forced option.
* <p>
* There are some flags which are interpreted by the test
* and removed from the list of flags passed to the command
* (see testFlag()):
* <ul>
* <li>eclipse: use the new eclipse compiler (can force)</li>
* <li>ajc: use the old ajc compiler (can force)</li>
* <li>ignoreWarnings: ignore warnings in result evaluations (no force)</li>
* </ul>
* <p>
* There are some flags which are inconsistent with each other.
* These are treated as conflicts and the test is skipped:
* <ul>
* <li>lenient, strict</li>
* </ul>
* <p>
* <p>
* This also interprets any relevant System properties,
* e.g., from <code>JavaRun.BOOTCLASSPATH_KEY</code>.
* <p>
* Finally, compiler limitations are enforced here by skipping
* tests which the compiler cannot do:
* <ul>
* <li>eclipse does not do -lenient, -strict, -usejavac, -preprocess,
* -XOcodeSize, -XSerializable, XaddSafePrefix,
* -XserializableAspects,-XtargetNearSource</li>
* <li>ajc does not run in incremental (staging) mode,
* nor with -usejavac if javac is not on the classpath</li>
* </ul>
* <u>Errors</u>:This will remove an arg not prefixed by [-|!|^] after
* providing an info message.
* <u>TestSetup Result</u>:
* If this completes successfully, then TestSetup.result is true,
* and commandOptions is not null, and any test flags (ignore warning,
* compiler) are reflected in the TestSetup.
* If this fails, then TestSetup.result is false,
* and a TestSetup.failreason is set.
* This means the test is skipped.
* @return TestSetup with results
* (TestSetup result=false if the run should not continue)
*/
protected TestSetup setupArgs(IMessageHandler handler) {
// warning: HarnessSelectionTest checks for specific error wording
final Spec spec = this;
final TestSetup result = new TestSetup();
result.compilerName = spec.compiler;
// urk - s.b. null, but expected
Values values = gatherValues(result);
if ((null == values) || (null != result.failureReason)) {
return checkResult(result);
}
// send info messages about
// forced staging when -incremental
// or staging but no -incremental flag
Option.Family getFamily =
CRSOPTIONS.crsIncrementalOption.getFamily();
final boolean haveIncrementalFlag =
(null != values.firstInFamily(getFamily));
if (spec.isStaging()) {
if (!haveIncrementalFlag) {
MessageUtil.info(
handler,
"staging but no -incremental flag");
}
} else if (haveIncrementalFlag) {
spec.setStaging(true);
MessageUtil.info(handler, "-incremental forcing staging");
}
if (hasInvalidOptions(values, result)) {
return checkResult(result);
}
// set compiler in result
getFamily = CRSOPTIONS.ajccompilerOption.getFamily();
Option.Value compiler = values.firstInFamily(getFamily);
if (null != compiler) {
result.compilerName
= CRSOPTIONS.compilerClassName(compiler.option);
if (null == result.compilerName) {
result.failureReason =
"unable to get class name for " + compiler;
return checkResult(result);
}
}
String compilerName =
(null == result.compilerName
? spec.compiler
: result.compilerName);
// check compiler semantics
if (hasCompilerSpecErrors(compilerName, values, result)) {
return checkResult(result);
}
// add toadd and finish result
ArrayList args = new ArrayList();
String[] rendered = values.render();
if (!LangUtil.isEmpty(rendered)) {
args.addAll(Arrays.asList(rendered));
}
// update bootclasspath
getFamily = CRSOPTIONS.crsSourceOption.getFamily();
Option.Value source = values.firstInFamily(getFamily);
if (null != source) {
String sourceVersion = source.unflatten()[1];
ArrayList toAdd = new ArrayList();
/*String err =*/
updateBootclasspathForSourceVersion(
sourceVersion,
spec.compiler,
toAdd);
args.addAll(toAdd);
}
result.commandOptions = args;
result.result = true;
return checkResult(result);
}
/**
* Ensure exit invariant:
* <code>result.result == (null == result.failureReason)
* == (null != result.commandOptions)</code>
* @param result the TestSetup to verify
* @return result
* @throws Error if invariant is not true
*/
TestSetup checkResult(TestSetup result) {
String err = null;
if (null == result) {
err = "null result";
} else if (result.result != (null == result.failureReason)) {
err =
result.result
? "expected no failure: " + result.failureReason
: "fail for no reason";
} else if (result.result != (null != result.commandOptions)) {
err =
result.result
? "expected command options"
: "unexpected command options";
}
if (null != err) {
throw new Error(err);
}
return result;
}
boolean hasInvalidOptions(Values values, TestSetup result) {
// not supporting 1.0 options any more
for (Object o : CRSOPTIONS.invalidOptions) {
Option option = (Option) o;
if (null != values.firstOption(option)) {
result.failureReason =
"invalid option in harness: " + option;
return true;
}
}
return false;
}
boolean hasCompilerSpecErrors(
String compilerName,
Values values,
TestSetup result) {
/*
* Describe any semantic conflicts between options.
* This skips:
* - old 1.0 options, including lenient v. strict
* - old ajc options, include !incremental and usejavac w/o javac
* - invalid eclipse options (mostly ajc)
* @param compilerName the String name of the target compiler
* @return a String describing any conflicts, or null if none
*/
if (!permitAnyCompiler
&& (!(ReflectionFactory.ECLIPSE.equals(compilerName)
|| ReflectionFactory.OLD_AJC.equals(compilerName)
|| CRSOptions.AJDE_COMPILER.equals(compilerName)
|| CRSOptions.AJCTASK_COMPILER.equals(compilerName)
|| permitAnyCompiler
))) {
//|| BUILDER_COMPILER.equals(compilerName))
result.failureReason =
"unrecognized compiler: " + compilerName;
return true;
}
// not supporting ajc right now
if (null
!= values.firstOption(CRSOPTIONS.ajccompilerOption)) {
result.failureReason = "ajc not supported";
return true;
}
// not supporting 1.0 options any more
for (Object o : CRSOPTIONS.ajc10Options) {
Option option = (Option) o;
if (null != values.firstOption(option)) {
result.failureReason = "old ajc 1.0 option: " + option;
return true;
}
}
return false;
}
protected Values gatherValues(TestSetup result) {
final Spec spec = this;
// ---- local option values
final Values localValues;
final Options options = CRSOPTIONS.getOptions();
try {
String[] input = getOptionsArray();
// this handles reading options,
// flattening two-String options, etc.
localValues = options.acceptInput(input);
// all local values should be picked up
String err = Options.missedMatchError(input, localValues);
if (!LangUtil.isEmpty(err)) {
result.failureReason = err;
return null;
}
} catch (InvalidInputException e) {
result.failureReason = e.getFullMessage();
return null;
}
// ---- global option values
StringBuffer errs = new StringBuffer();
final Values globalValues =
spec.runtime.extractOptions(options, true, errs);
if (errs.length() > 0) {
result.failureReason = errs.toString();
return null;
}
final Values combined =
Values.wrapValues(
new Values[] { localValues, globalValues });
String err = combined.resolve();
if (null != err) {
result.failureReason = err;
return null;
}
return handleTestArgs(combined, result);
}
// final int len = globalValues.length() + localValues.length();
// final Option.Value[] combinedValues = new Option.Value[len];
// System.arraycopy(
// globalValues,
// 0,
// combinedValues,
// 0,
// globalValues.length());
// System.arraycopy(
// localValues,
// 0,
// combinedValues,
// globalValues.length(),
// localValues.length());
//
// result.compilerName = spec.compiler;
// if (0 < combinedValues.length) {
// // this handles option forcing, etc.
// String err = Options.resolve(combinedValues);
// if (null != err) {
// result.failureReason = err;
// return null;
// }
// if (!handleTestArgs(combinedValues, result)) {
// return null;
// }
// }
// return Values.wrapValues(combinedValues);
// }
/**
* This interprets and nullifies values for the test.
* @param values the Option.Value[] being processed
* @param result the TestSetup to modify
* @return false if error (caller should return), true otherwise
*/
Values handleTestArgs(Values values, final TestSetup result) {
final Option.Family compilerFamily =
CRSOPTIONS.ajccompilerOption.getFamily();
Values.Selector selector = new Values.Selector() {
protected boolean accept(Option.Value value) {
if (null == value) {
return false;
}
Option option = value.option;
if (compilerFamily.sameFamily(option.getFamily())) {
if (value.prefix.isSet()) {
String compilerClass
= CRSOPTIONS.compilerClassName(option);
if (null == compilerClass) {
result.failureReason =
"unrecognized compiler: " + value;
throw Values.Selector.STOP;
}
if (!CRSOPTIONS.compilerIsLoadable(option)) {
result.failureReason =
"unable to load compiler: " + option;
throw Values.Selector.STOP;
}
result.compilerName = compilerClass;
}
return true;
} else if (
CRSOPTIONS.crsIgnoreWarnings.sameOptionIdentifier(
option)) {
result.ignoreWarnings = value.prefix.isSet();
result.ignoreWarningsSet = true;
return true;
}
return false;
}
};
return values.nullify(selector);
}
// /**
// * This interprets and nullifies values for the test.
// * @param values the Option.Value[] being processed
// * @param result the TestSetup to modify
// * @return false if error (caller should return), true otherwise
// */
// boolean handleTestArgs(Option.Value[] values, TestSetup result) {
// if (!LangUtil.isEmpty(values)) {
// for (int i = 0; i < values.length; i++) {
// Option.Value value = values[i];
// if (null == value) {
// continue;
// }
// Option option = value.option;
// if (option.sameOptionFamily(ECLIPSE_OPTION)) {
// if (!value.prefix.isSet()) {
// values[i] = null;
// continue;
// }
// String compilerClass =
// (String) COMPILER_OPTION_TO_CLASSNAME.get(
// option);
// if (null == compilerClass) {
// result.failureReason =
// "unrecognized compiler: " + value;
// return false;
// }
// result.compilerName = compilerClass;
// values[i] = null;
// } else if (
// option.sameOptionFamily(crsIgnoreWarnings)) {
// result.ignoreWarnings = value.prefix.isSet();
// result.ignoreWarningsSet = true;
// values[i] = null;
// }
// }
// }
// return true;
// }
// XXX need keys, cache...
/** @return index of global in argList, ignoring first char */
protected int indexOf(String global, ArrayList argList) {
int max = argList.size();
for (int i = 0; i < max; i++) {
if (global
.equals(((String) argList.get(i)).substring(1))) {
return i;
}
}
return -1;
}
/**
* Write this out as a compile element as defined in
* AjcSpecXmlReader.DOCTYPE.
* @see AjcSpecXmlReader#DOCTYPE
* @see IXmlWritable#writeXml(XMLWriter)
*/
public void writeXml(XMLWriter out) {
out.startElement(xmlElementName, false);
if (!LangUtil.isEmpty(testSrcDirOffset)) {
out.printAttribute("dir", testSrcDirOffset);
}
super.writeAttributes(out);
if (!DEFAULT_COMPILER.equals(compiler)) {
out.printAttribute("compiler", compiler);
}
if (reuseCompiler) {
out.printAttribute("reuseCompiler", "true");
}
// test-only feature
// if (permitAnyCompiler) {
// out.printAttribute("permitAnyCompiler", "true");
// }
if (includeClassesDir) {
out.printAttribute("includeClassesDir", "true");
}
if (!LangUtil.isEmpty(argfiles)) {
out.printAttribute(
"argfiles",
XMLWriter.flattenFiles(argfiles));
}
if (!LangUtil.isEmpty(aspectpath)) {
out.printAttribute(
"aspectpath",
XMLWriter.flattenFiles(aspectpath));
}
if (!LangUtil.isEmpty(sourceroots)) {
out.printAttribute(
"sourceroots",
XMLWriter.flattenFiles(sourceroots));
}
if (!LangUtil.isEmpty(extdirs)) {
out.printAttribute(
"extdirs",
XMLWriter.flattenFiles(extdirs));
}
out.endAttributes();
if (!LangUtil.isEmpty(dirChanges)) {
DirChanges.Spec.writeXml(out, dirChanges);
}
SoftMessage.writeXml(out, getMessages());
out.endElement(xmlElementName);
}
/**
* Encapsulate the directives that can be set using
* global arguments supplied in {@link Spec.getOptions()}.
* This supports changing the compiler and ignoring warnings.
*/
class TestSetup {
/** null unless overriding the compiler to be used */
String compilerName;
/**
* true if we should tell AjcMessageHandler whether
* to ignore warnings in its result evaluation
*/
boolean ignoreWarningsSet;
/** if telling AjcMessageHandler, what we tell it */
boolean ignoreWarnings;
/** false if setup failed */
boolean result;
/** if setup failed, this has the reason why */
String failureReason;
/** beyond running test, also seek text in sources */
String seek;
/** if setup completed, this has the combined global/local options */
List commandOptions;
public Object clone() {
TestSetup testSetup = new TestSetup();
testSetup.compilerName = compilerName;
testSetup.ignoreWarnings = ignoreWarnings;
testSetup.ignoreWarningsSet = ignoreWarningsSet;
testSetup.result = result;
testSetup.failureReason = failureReason;
testSetup.seek = seek;
if (null != commandOptions) {
testSetup.commandOptions = new ArrayList();
testSetup.commandOptions.addAll(commandOptions);
}
return testSetup;
}
public String toString() {
return "TestSetup("
+ (null == compilerName ? "" : compilerName + " ")
+ (!ignoreWarningsSet
? ""
: (ignoreWarnings ? "" : "do not ")
+ "ignore warnings ")
+ (result ? "" : "setup failed")
+ ")";
}
}
/**
* Options-related stuff in the spec.
*/
static class CRSOptions {
// static final String BUILDER_COMPILER =
// "org.aspectj.ajdt.internal.core.builder.Builder.Command";
static final String AJDE_COMPILER =
CompileCommand.class.getName();
static final String AJCTASK_COMPILER =
AjcTaskCompileCommand.class.getName();
private final Map compilerOptionToLoadable = new TreeMap();
/*
* The options field in a compiler test permits some arbitrary
* command-line options to be set. It does not permit things
* like classpath, aspectpath, files, etc. which are set
* using other fields in the test specification, so the options
* permitted are a subset of those permitted on the command-line.
*
* Global options specified on the harness command-line are
* adopted for the compiler command-line if they are permitted
* in the options field. That means we have to detect each
* permitted option, rather than just letting all through
* for the compiler.
*
* Conversely, some options are targeted not at the compiler,
* but at the test itself (e.g., to ignore warnings, or to
* select a compiler.
*
* The harness can run many compilers, and they differ in
* which options are permitted. You can specify a compiler
* as an option (e.g., -eclipse). So the set of constraints
* on the list of permitted options can differ from test to test.
*
* The following code sets up the lists of permitted options
* and breaks out subsets for different compiler-variant checks.
* Most options are created but not named, but some options
* are named to detect corresponding values for further
* processing. e.g., the compiler options are saved so
* we can do per-compiler option verification.
*
*/
private final Options crsOptions;
private final Family compilerFamily;
private final Option crsIncrementalOption;
private final Option crsSourceOption;
// these are options handled/absorbed by CompilerRun
private final Option crsIgnoreWarnings;
private final Option eclipseOption;
private final Option buildercompilerOption;
private final Option ajdecompilerOption;
private final Option javacOption;
private final Option ajctaskcompilerOption;
private final Option ajccompilerOption;
private final Map compilerOptionToClassname;
private final Set compilerOptions;
// compiler verification - permit but flag ajc 1.0 options
private final List ajc10Options;
private final List invalidOptions;
private CRSOptions() {
crsOptions = new Options(true);
Option.Factory factory = new Option.Factory("CompilerRun");
// compiler options go in map
eclipseOption =
factory.create(
"eclipse",
"compiler",
Option.FORCE_PREFIXES,
false);
compilerFamily = eclipseOption.getFamily();
buildercompilerOption =
factory.create(
"builderCompiler",
"compiler",
Option.FORCE_PREFIXES,
false);
ajctaskcompilerOption =
factory.create(
"ajctaskCompiler",
"compiler",
Option.FORCE_PREFIXES,
false);
ajdecompilerOption =
factory.create(
"ajdeCompiler",
"compiler",
Option.FORCE_PREFIXES,
false);
ajccompilerOption =
factory.create(
"ajc",
"compiler",
Option.FORCE_PREFIXES,
false);
javacOption =
factory.create(
"javac",
"compiler",
Option.FORCE_PREFIXES,
false);
Map<Option,String> map = new TreeMap<>();
map.put(eclipseOption, ReflectionFactory.ECLIPSE);
//map.put(BUILDERCOMPILER_OPTION, BUILDER_COMPILER);
map.put(
ajctaskcompilerOption,
AJCTASK_COMPILER);
map.put(ajdecompilerOption, AJDE_COMPILER);
map.put(ajccompilerOption, ReflectionFactory.OLD_AJC);
//map.put(JAVAC_OPTION, "XXX javac option not supported");
compilerOptionToClassname =
Collections.unmodifiableMap(map);
compilerOptions =
Collections.unmodifiableSet(
compilerOptionToClassname.keySet());
// options not permitted in the harness
List<Option> list = new ArrayList<>();
list.add(factory.create("workingdir"));
list.add(factory.create("argfile"));
list.add(factory.create("sourceroots"));
list.add(factory.create("outjar"));
invalidOptions = Collections.unmodifiableList(list);
// other options added directly
crsIncrementalOption = factory.create("incremental");
crsIgnoreWarnings = factory.create("ignoreWarnings");
crsSourceOption =
factory
.create(
"source",
"source",
Option.FORCE_PREFIXES,
false,
new String[][] { new String[] { "1.3", "1.4", "1.5" }
});
// ajc 1.0 options
// workingdir above in invalid options
list = new ArrayList();
list.add(factory.create("usejavac"));
list.add(factory.create("preprocess"));
list.add(factory.create("nocomment"));
list.add(factory.create("porting"));
list.add(factory.create("XOcodeSize"));
list.add(factory.create("XTargetNearSource"));
list.add(factory.create("XaddSafePrefix"));
list.add(
factory.create(
"lenient",
"lenient",
Option.FORCE_PREFIXES,
false));
list.add(
factory.create(
"strict",
"lenient",
Option.FORCE_PREFIXES,
false));
ajc10Options = Collections.unmodifiableList(list);
// -warn:.. and -g/-g:.. are not exclusive
if (!(factory.setupFamily("debug", true)
&& factory.setupFamily("warning", true))) {
System.err.println("CompilerRun debug/warning fail!");
}
Option[] options =
new Option[] {
crsIncrementalOption,
crsIgnoreWarnings,
crsSourceOption,
factory.create(
"Xlint",
"XLint",
Option.FORCE_PREFIXES,
true),
factory.create("verbose"),
factory.create("emacssym"),
factory.create("referenceInfo"),
factory.create("nowarn"),
factory.create("deprecation"),
factory.create("noImportError"),
factory.create("proceedOnError"),
factory.create("preserveAllLocals"),
factory.create(
"warn",
"warning",
Option.STANDARD_PREFIXES,
true),
factory.create(
"g",
"debug",
Option.STANDARD_PREFIXES,
false),
factory.create(
"g:",
"debug",
Option.STANDARD_PREFIXES,
true),
factory.create(
"1.3",
"compliance",
Option.FORCE_PREFIXES,
false),
factory.create(
"1.4",
"compliance",
Option.FORCE_PREFIXES,
false),
factory.create(
"1.5",
"compliance",
Option.FORCE_PREFIXES,
false),
factory
.create(
"target",
"target",
Option.FORCE_PREFIXES,
false,
new String[][] { new String[] {
"1.1",
"1.2",
"1.3",
"1.4",
"1.5" }}),
factory.create("XnoInline"),
factory.create("XterminateAfterCompilation"),
factory.create("Xreweavable"),
factory.create("XnotReweavable"),
factory.create("XserializableAspects")
};
// among options not permitted: extdirs...
for (Option option : options) {
crsOptions.addOption(option);
}
for (Object compilerOption : compilerOptions) {
crsOptions.addOption((Option) compilerOption);
}
// these are recognized but records with them are skipped
for (Object ajc10Option : ajc10Options) {
crsOptions.addOption((Option) ajc10Option);
}
crsOptions.freeze();
}
Options getOptions() {
return crsOptions;
}
/**
* @return unmodifiable Set of options sharing the
* family "compiler".
*/
Set compilerOptions() {
return compilerOptions;
}
/**
* @param option the compiler Option to get name for
* @return null if option is null or not a compiler option,
* or the fully-qualified classname of the ICommand
* implementing the compiler.
*/
String compilerClassName(Option option) {
if ((null == option)
|| (!compilerFamily.sameFamily(option.getFamily()))) {
return null;
}
return (String) compilerOptionToClassname.get(option);
}
/**
* Check that the compiler class associated with a compiler
* option can be loaded. This check only happens once;
* the result is cached (by compilerOption key) for later calls.
* @param compilerOption the Option (family compiler) to check
* @return true if compiler class for this option can be loaded
*/
boolean compilerIsLoadable(Option compilerOption) {
LangUtil.throwIaxIfNull(compilerOption, "compilerName");
synchronized (compilerOptionToLoadable) {
Boolean result =
(Boolean) compilerOptionToLoadable.get(
compilerOption);
if (null == result) {
MessageHandler sink = new MessageHandler();
String compilerClassname =
(String) compilerOptionToClassname.get(
compilerOption);
if (null == compilerClassname) {
result = Boolean.FALSE;
} else {
ICommand c =
ReflectionFactory.makeCommand(
compilerClassname,
sink);
if ((null == c)
|| sink.hasAnyMessage(
IMessage.ERROR,
true)) {
result = Boolean.FALSE;
} else {
result = Boolean.TRUE;
}
}
compilerOptionToLoadable.put(
compilerOption,
result);
}
return result;
}
}
} // CompilerRun.Spec.CRSOptions
} // CompilerRun.Spec
} // CompilerRun