import java.io.File;
import java.util.*;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.MatchingTask;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.util.GlobPatternMapper;
import org.apache.tools.ant.util.SourceFileScanner;
import polyglot.main.Main.TerminationException;
/**
* An ANT <code>Task</code> to facilitate the building of
* of code written in language extensions within the ANT framework. This task
* can be used similarly way to the <code>JavaC</code> task.
*
* <p> This class can
* optionally be subclassed by extensions (to provide compilation for
* a particular language). In that case, subclasses should override the
* init method to set the source file extension
* ({@link PolyglotAntTask#srcExt srcExt}) and extension name
* or class ({@link PolyglotAntTask#extensionName extensionName} or
* {@link PolyglotAntTask#extensionClass extensionClass}) appropriately, and
* override the setter methods for these fields to prevent the user from modifying
* these fields.
*
* <b>This class has not been fully tested.</b>
*
* @see org.apache.tools.ant.Task
*/
public class PolyglotAntTask extends MatchingTask {
/**
* Name of the extension, like the <code>-ext</code> command line option.
*/
protected String extensionName;
/**
* Class of the extension, like the <code>-extclass</code> command line
* option.
*/
protected Class extensionClass;
/**
* Destination directory, like the <code>-d</code> command line option.
*/
protected File destDir;
/**
* The path for source compilation.
*/
protected Path src;
/**
* Bootclasspath, like the <code>-bootclasspath</code> command line option.
*/
protected Path bootclasspath;
/**
* The file extension for sourcefiles, like the <code>-sx</code>
* command line option.
*/
protected String srcExt;
/**
* The file extensions for output files, like the <code>-ox</code>
* command line option.
*/
protected String outputExt;
/**
* A file containing additional command line options to parse, like
* the <code>@<file$gt;</code> command line option.
*/
protected File optionsFile;
/**
* The post compiler to invoke, like the <code>-post</code>
* command line option.
*/
protected String post;
/**
* Indiciates if the Polyglot compiler should produce serialized type
* information in the output files, like the <code>-noserial</code>
* command line option.
*/
protected boolean noSerial;
/**
* Indiciates if the Polyglot compiler should delete the output files
* after compilation, like the <code>-nooutput</code> command line option.
*/
protected boolean noOutput;
/**
* Indiciates if the Polyglot compiler should use fully qualified
* class names, like the <code>-fqcn</code>
* command line option.
*/
protected boolean fqcn;
/**
* Indiciates if the Polyglot compiler should use only compile to the
* output files, like the <code>-c</code>
* command line option.
*/
protected boolean onlyToJava;
/**
* A <code>List</code> of {@link org.apache.tools.ant.types.Commandline.Arguments
* Commandline.Arguments}, to allow the user to specify command line
* options that we do not deal with explicitly.
*/
protected List additionalArgs;
/**
* The classpath to be used for compilation, like the <code>-classpath</code>
* command line option.
*/
protected Path compileClasspath;
/**
* The path for finding source files, like the <code>-sourcepath</code>
* command line option.
*/
protected Path compileSourcepath;
public PolyglotAntTask() { }
public void init() {
super.init();
extensionName = null;
extensionClass = null;
destDir = null;
src = null;
bootclasspath = null;
srcExt = "java";
outputExt = "java";
optionsFile = null;
post = null;
noSerial = false;
noOutput = false;
fqcn = false;
onlyToJava = false;
additionalArgs = null;
compileClasspath = null;
compileSourcepath = null;
}
public void execute() throws BuildException {
dumpDetails();
checkParameters();
// scan source directories and dest directory to build up
File[] compileFiles = getCompileFiles();
if (compileFiles.length == 0) {
// nothing to do. no files to compile
log("No files to compile",
Project.MSG_INFO);
return;
}
// build up the argument list
String[] args = constructArgList(compileFiles);
log("Invoking polyglot with the following arguments: " +
"(enclosing single quotes are not part of the arguments):",
Project.MSG_VERBOSE);
for (int i = 0; i < args.length; i++) {
log(" '" + args[i] + "'", Project.MSG_VERBOSE);
}
// invoke the polyglot compiler.
compile(args);
}
protected File[] getCompileFiles() {
File[] compileFiles = new File[0];
String[] srcList = src.list();
for (int i = 0; i < srcList.length; i++) {
File srcDir = getProject().resolveFile(srcList[i]);
if (!srcDir.exists()) {
throw new BuildException("srcdir \""
+ srcDir.getPath()
+ "\" does not exist!", getLocation());
}
DirectoryScanner ds = this.getDirectoryScanner(srcDir);
String[] files = ds.getIncludedFiles();
compileFiles = scanDir(compileFiles, srcDir, destDir != null ? destDir : srcDir, files);
}
return compileFiles;
}
/**
* Scans the directory looking for source files to be compiled.
* The results are returned in the class variable compileList
*/
protected File[] scanDir(File[] compileFiles, File srcDir, File destDir, String[] files) {
GlobPatternMapper m = new GlobPatternMapper();
m.setFrom("*." + srcExt);
m.setTo("*." + destFileExt());
SourceFileScanner sfs = new SourceFileScanner(this);
File[] newFiles = sfs.restrictAsFiles(files, srcDir, destDir, m);
if (newFiles.length > 0) {
File[] newCompileList = new File[compileFiles.length +
newFiles.length];
System.arraycopy(compileFiles, 0, newCompileList, 0,
compileFiles.length);
System.arraycopy(newFiles, 0, newCompileList,
compileFiles.length, newFiles.length);
compileFiles = newCompileList;
}
return compileFiles;
}
/**
* The file extension of the destination files. Used by the global pattern
* matcher to determine which files need to be compiled.
*/
protected String destFileExt() {
return onlyToJava ? outputExt : "class";
}
/**
* Check to make sure the paramters are consistent with invoking the
* Polyglot compiler.
* @throws BuildException if the parameters are inconsistent.
*/
protected void checkParameters() throws BuildException {
// at most one of extenstionClass and extensionName is set.
if (extensionClass != null && extensionName != null) {
throw new BuildException("At most one of extclass and " +
"extname can be set.", getLocation());
}
if (src == null) {
throw new BuildException("srcdir attribute must be set!",
getLocation());
}
if (src.size() == 0) {
throw new BuildException("srcdir attribute must be set!",
getLocation());
}
if (destDir != null && !destDir.isDirectory()) {
throw new BuildException("destination directory \""
+ destDir
+ "\" does not exist "
+ "or is not a directory", getLocation());
}
}
/**
* Construct the argument list that will be given to the Polyglot
* compiler.
* @param files the files to compile
*/
protected String[] constructArgList(File[] files) {
List argList = new ArrayList();
// set up command line args
if (optionsFile != null) {
argList.add("@" + optionsFile.getPath());
}
if (extensionName != null) {
argList.add("-ext");
argList.add(extensionName);
}
if (extensionClass != null) {
argList.add("-extclass");
argList.add(extensionClass.getName());
}
if (noSerial) {
argList.add("-noserial");
}
if (noOutput) {
argList.add("-nooutput");
}
if (fqcn) {
argList.add("-fqcn");
}
if (onlyToJava) {
argList.add("-c");
}
if (post != null) {
argList.add("-post");
argList.add(post);
}
if (srcExt != null) {
argList.add("-sx");
argList.add(srcExt);
}
if (outputExt != null) {
argList.add("-ox");
argList.add(outputExt);
}
if (destDir != null) {
argList.add("-d");
argList.add(destDir.getPath());
}
if (compileSourcepath != null) {
argList.add("-sourcepath");
argList.add(compileSourcepath.toString());
}
else if (src != null) {
argList.add("-sourcepath");
argList.add(src.toString());
}
if (bootclasspath != null) {
argList.add("-bootclasspath");
argList.add(bootclasspath.toString());
}
if (compileClasspath != null) {
argList.add("-classpath");
argList.add(compileClasspath.toString());
}
if (additionalArgs != null) {
for (Iterator i = additionalArgs.iterator(); i.hasNext(); ) {
Commandline.Argument arg = (Commandline.Argument)i.next();
String[] parts = arg.getParts();
for (int j = 0; j < parts.length; j++) {
argList.add(parts[j]);
}
}
}
// add the files to compile.
int argListSize = argList.size();
String[] args = new String[argListSize + files.length];
argList.toArray(args);
for (int i = 0; i < files.length; ++i) {
args[argListSize + i] = files[i].getPath();
}
return args;
}
/**
* Invoke the Polyglot compiler, with the given command line arguments.
*/
protected void compile(String[] args) {
try {
new polyglot.main.Main().start(args);
}
catch (TerminationException e) {
throw new BuildException(e.getMessage(), e);
}
}
/**
* Dump the settings of the parameters, for debugging purposes.
*/
protected void dumpDetails() {
log("extensionName = " + extensionName, Project.MSG_DEBUG);
log("extensionClass = " + extensionClass, Project.MSG_DEBUG);
log("destdir = " + destDir, Project.MSG_DEBUG);
log("src = " + src, Project.MSG_DEBUG);
log("bootclasspath = " + bootclasspath, Project.MSG_DEBUG);
log("srcExt = " + srcExt, Project.MSG_DEBUG);
log("outputExt = " + outputExt, Project.MSG_DEBUG);
log("optionsFile = " + optionsFile, Project.MSG_DEBUG);
log("post = " + post, Project.MSG_DEBUG);
log("noSerial = " + noSerial, Project.MSG_DEBUG);
log("noOutput = " + noOutput, Project.MSG_DEBUG);
log("fqcn = " + fqcn, Project.MSG_DEBUG);
log("onlyToJava = " + onlyToJava, Project.MSG_DEBUG);
log("additionalArgs = ", Project.MSG_DEBUG);
if (additionalArgs != null) {
for (Iterator i = additionalArgs.iterator(); i.hasNext(); ) {
StringBuffer sb = new StringBuffer();
Commandline.Argument arg = (Commandline.Argument)i.next();
String[] parts = arg.getParts();
sb.append(" '");
for (int j = 0; j < parts.length; j++) {
sb.append(parts[j]);
}
sb.append("'");
log(sb.toString(), Project.MSG_DEBUG);
}
}
log("compileClasspath = " + compileClasspath, Project.MSG_DEBUG);
log("compileSourcepath = " + compileSourcepath, Project.MSG_DEBUG);
log("fileset = " + Arrays.asList(this.getCompileFiles()), Project.MSG_DEBUG);
}
/**
* Adds a command-line argument.
*/
public Commandline.Argument createArg() {
Commandline.Argument argument = new Commandline.Argument();
if (additionalArgs == null) {
additionalArgs = new LinkedList();
}
additionalArgs.add(argument);
return argument;
}
public void setExtname(String extensionName) {
this.extensionName = extensionName;
}
public void setExtclass(Class extensionClass) {
this.extensionClass = extensionClass;
}
public void setDestdir(File destdir) {
this.destDir = destdir;
}
public Path createSrc() {
if (src == null) {
src = new Path(getProject());
}
return src.createPath();
}
public void setSrcdir(Path srcDir) {
if (src == null) {
src = srcDir;
} else {
src.append(srcDir);
}
}
public void setBootclasspath(Path bootclasspath) {
if (this.bootclasspath == null) {
this.bootclasspath = bootclasspath;
} else {
this.bootclasspath.append(bootclasspath);
}
}
public Path createBootclasspath() {
if (bootclasspath == null) {
bootclasspath = new Path(getProject());
}
return bootclasspath.createPath();
}
/**
* Adds a reference to a classpath defined elsewhere.
*/
public void setBootClasspathRef(Reference r) {
createBootclasspath().setRefid(r);
}
public void setSx(String srcExt) {
this.srcExt = srcExt;
}
public void setOx(String outExt) {
this.outputExt = outExt;
}
public void setOptions(File optionsFile) {
this.optionsFile = optionsFile;
}
public void setPost(String post) {
this.post = post;
}
public void setNoserial(boolean noSerial) {
this.noSerial = noSerial;
}
public void setNooutput(boolean noOutput) {
this.noOutput = noOutput;
}
public void setFqcn(boolean fqcn) {
this.fqcn = fqcn;
}
public void setOnlytojava(boolean onlyToJava) {
this.onlyToJava = onlyToJava;
}
public void setClasspath(Path classpath) {
if (compileClasspath == null) {
compileClasspath = classpath;
} else {
compileClasspath.append(classpath) ;
}
}
/**
* Adds a path to the classpath.
*/
public Path createClasspath() {
if (compileClasspath == null) {
compileClasspath = new Path(getProject());
}
return compileClasspath.createPath();
}
/**
* Adds a reference to a classpath defined elsewhere.
*/
public void setClasspathRef(Reference r) {
createClasspath().setRefid(r);
}
/**
* Set the sourcepath to be used for this compilation.
*/
public void setSourcepath(Path sourcepath) {
if (compileSourcepath == null) {
compileSourcepath = sourcepath;
} else {
compileSourcepath.append(sourcepath);
}
}
/** Gets the sourcepath to be used for this compilation. */
public Path getSourcepath() {
return compileSourcepath;
}
/**
* Adds a path to sourcepath.
*/
public Path createSourcepath() {
if (compileSourcepath == null) {
compileSourcepath = new Path(getProject());
}
return compileSourcepath.createPath();
}
/**
* Adds a reference to a source path defined elsewhere.
*/
public void setSourcepathRef(Reference r) {
createSourcepath().setRefid(r);
}
}