org.codehaus.groovy.ant.Groovyc Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of groovy-all-minimal Show documentation
Show all versions of groovy-all-minimal Show documentation
Groovy: A powerful, dynamic language for the JVM
/*
* Copyright 2003-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.ant;
import org.apache.commons.cli.CommandLine ;
import org.apache.commons.cli.Options ;
import org.apache.commons.cli.OptionBuilder ;
import org.apache.commons.cli.PosixParser ;
import org.apache.commons.cli.ParseException ;
import org.apache.tools.ant.AntClassLoader ;
import org.apache.tools.ant.BuildException ;
import org.apache.tools.ant.DefaultLogger ;
import org.apache.tools.ant.DirectoryScanner ;
import org.apache.tools.ant.Project ;
import org.apache.tools.ant.listener.AnsiColorLogger ;
import org.apache.tools.ant.taskdefs.Javac ;
import org.apache.tools.ant.taskdefs.Execute ;
import org.apache.tools.ant.taskdefs.MatchingTask ;
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 groovy.lang.GroovyClassLoader ;
import org.codehaus.groovy.control.CompilationUnit ;
import org.codehaus.groovy.control.CompilerConfiguration ;
import org.codehaus.groovy.tools.ErrorReporter ;
import org.codehaus.groovy.tools.StringHelper ;
import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit ;
import org.codehaus.groovy.tools.javac.JavaCompiler ;
import org.codehaus.groovy.tools.javac.JavaCompilerFactory ;
import java.io.File ;
import java.io.IOException ;
import java.io.PrintWriter ;
import java.io.StringWriter ;
import java.nio.charset.Charset ;
import java.util.ArrayList ;
import java.util.HashMap ;
import java.util.Iterator ;
import java.util.List ;
import java.util.Map ;
/**
* Compiles Groovy source files. This task can take the following arguments:
*
* - srcdir
* - destdir
* - classpath
* - encoding
* - verbose
* - failonerror
* - includeantruntime
* - includejavaruntime
* - memoryInitialSize
* - memoryMaximumSize
* - fork
* - stacktrace
*
* Of these arguments, the srcdir and destdir are required.
*
* When this task executes, it will recursively scan srcdir and destdir looking for Groovy source files
* to compile. This task makes its compile decision based on timestamp.
*
* Based heavily on the Javac implementation in Ant.
*
* @author James Strachan
* @author Hein Meling
* @author Russel Winder
* @version $Revision: 10601 $
*/
public class Groovyc extends MatchingTask
{
private final LoggingHelper log = new LoggingHelper(this);
private Path src;
private File destDir;
private Path compileClasspath;
private Path compileSourcepath;
private String encoding;
//private boolean debug = false;
//private boolean optimize = false;
//private boolean deprecation = false;
//private boolean depend = false;
private boolean verbose = false;
//private String targetAttribute;
//private Path bootclasspath;
//private Path extdirs;
private boolean includeAntRuntime = true;
private boolean includeJavaRuntime = false;
private boolean fork = false;
//private String forkedExecutable = null;
//private boolean nowarn = false;
private String memoryInitialSize;
private String memoryMaximumSize;
//private FacadeTaskHelper facade = null;
protected boolean failOnError = true;
protected boolean listFiles = false;
protected File[] compileList = new File[0];
//private String source;
//private String debugLevel;
//private File tmpDir;
private String updatedProperty;
private String errorProperty;
private boolean taskSuccess = true; // assume the best
private boolean includeDestClasses = true;
//private List updateDirList = new ArrayList();
protected CompilerConfiguration configuration = new CompilerConfiguration();
private Javac javac;
private boolean jointCompilation;
public Groovyc ( ) { }
/**
* Adds a path for source compilation.
*
* @return a nested src element.
*/
public Path createSrc() {
if (src == null) {
src = new Path(getProject());
}
return src.createPath();
}
/**
* Recreate src.
*
* @return a nested src element.
*/
protected Path recreateSrc() {
src = null;
return createSrc();
}
/**
* Set the source directories to find the source Java files.
* @param srcDir the source directories as a path
*/
public void setSrcdir(Path srcDir) {
if (src == null) {
src = srcDir;
}
else {
src.append(srcDir);
}
}
/**
* Gets the source dirs to find the source java files.
* @return the source directorys as a path
*/
public Path getSrcdir() {
return src;
}
/**
* Set the destination directory into which the Java source
* files should be compiled.
* @param destDir the destination director
*/
public void setDestdir(File destDir) {
this.destDir = destDir;
}
/**
* Gets the destination directory into which the java source files
* should be compiled.
* @return the destination directory
*/
public File getDestdir() {
return destDir;
}
/**
* Set the sourcepath to be used for this compilation.
* @param sourcepath the source path
*/
public void setSourcepath(Path sourcepath) {
if (compileSourcepath == null) {
compileSourcepath = sourcepath;
}
else {
compileSourcepath.append(sourcepath);
}
}
/**
* Gets the sourcepath to be used for this compilation.
* @return the source path
*/
public Path getSourcepath() {
return compileSourcepath;
}
/**
* Adds a path to sourcepath.
* @return a sourcepath to be configured
*/
public Path createSourcepath() {
if (compileSourcepath == null) {
compileSourcepath = new Path(getProject());
}
return compileSourcepath.createPath();
}
/**
* Adds a reference to a source path defined elsewhere.
* @param r a reference to a source path
*/
public void setSourcepathRef(Reference r) {
createSourcepath().setRefid(r);
}
/**
* Set the classpath to be used for this compilation.
*
* @param classpath an Ant Path object containing the compilation classpath.
*/
public void setClasspath(Path classpath) {
if (compileClasspath == null) {
compileClasspath = classpath;
}
else {
compileClasspath.append(classpath);
}
}
/**
* Gets the classpath to be used for this compilation.
* @return the class path
*/
public Path getClasspath() {
return compileClasspath;
}
/**
* Adds a path to the classpath.
* @return a class path to be configured
*/
public Path createClasspath() {
if (compileClasspath == null) {
compileClasspath = new Path(getProject());
}
return compileClasspath.createPath();
}
/**
* Adds a reference to a classpath defined elsewhere.
* @param r a reference to a classpath
*/
public void setClasspathRef(Reference r) {
createClasspath().setRefid(r);
}
/**
* If true, list the source files being handed off to the compiler.
* @param list if true list the source files
*/
public void setListfiles(boolean list) {
listFiles = list;
}
/**
* Get the listfiles flag.
* @return the listfiles flag
*/
public boolean getListfiles() {
return listFiles;
}
/**
* Indicates whether the build will continue
* even if there are compilation errors; defaults to true.
* @param fail if true halt the build on failure
*/
public void setFailonerror(boolean fail) {
failOnError = fail;
}
/**
* @param proceed inverse of failonerror
*/
public void setProceed(boolean proceed) {
failOnError = !proceed;
}
/**
* Gets the failonerror flag.
* @return the failonerror flag
*/
public boolean getFailonerror() {
return failOnError;
}
/**
* The initial size of the memory for the underlying VM
* if javac is run externally; ignored otherwise.
* Defaults to the standard VM memory setting.
* (Examples: 83886080, 81920k, or 80m)
* @param memoryInitialSize string to pass to VM
*/
public void setMemoryInitialSize(String memoryInitialSize) {
this.memoryInitialSize = memoryInitialSize;
}
/**
* Gets the memoryInitialSize flag.
* @return the memoryInitialSize flag
*/
public String getMemoryInitialSize() {
return memoryInitialSize;
}
/**
* The maximum size of the memory for the underlying VM
* if javac is run externally; ignored otherwise.
* Defaults to the standard VM memory setting.
* (Examples: 83886080, 81920k, or 80m)
* @param memoryMaximumSize string to pass to VM
*/
public void setMemoryMaximumSize(String memoryMaximumSize) {
this.memoryMaximumSize = memoryMaximumSize;
}
/**
* Gets the memoryMaximumSize flag.
* @return the memoryMaximumSize flag
*/
public String getMemoryMaximumSize() {
return memoryMaximumSize;
}
/**
* Sets the file encoding for generated files.
* @param encoding the file encoding to be used
*/
public void setEncoding(String encoding) {
this.encoding = encoding;
}
/**
* Returns the encoding to be used when creating files.
* @return the file encoding to use
*/
public String getEncoding() {
return encoding;
}
/**
* Returns the encoding to be used when creating files.
* If no encoding value has been set it will default to
* System.properties("file.encoding")
*
* @return the file encoding to use
*/
/******
public String createEncoding() {
if (encoding == null) {
encoding = System.getProperty("file.encoding");
}
return encoding;
}
********/
/**
* Enable verbose compiling which will display which files
* are being compiled
*/
public void setVerbose(boolean verbose) {
configuration.setVerbose( verbose );
}
/**
* Gets the verbose flag.
* @return the verbose flag
*/
public boolean getVerbose() {
return verbose;
}
/**
* If true, includes Ant's own classpath in the classpath.
* @param include if true, includes Ant's own classpath in the classpath
*/
public void setIncludeantruntime(boolean include) {
includeAntRuntime = include;
}
/**
* Gets whether or not the ant classpath is to be included in the classpath.
* @return whether or not the ant classpath is to be included in the classpath
*/
public boolean getIncludeantruntime() {
return includeAntRuntime;
}
/**
* If true, includes the Java runtime libraries in the classpath.
* @param include if true, includes the Java runtime libraries in the classpath
*/
public void setIncludejavaruntime(boolean include) {
includeJavaRuntime = include;
}
/**
* Gets whether or not the java runtime should be included in this
* task's classpath.
* @return the includejavaruntime attribute
*/
public boolean getIncludejavaruntime() {
return includeJavaRuntime;
}
/**
* If true forks the Groovy compiler.
*
* @param f "true|false|on|off|yes|no"
*/
public void setFork ( boolean f ) {
fork = f ;
}
/**
* The property to set on compliation success.
* This property will not be set if the compilation
* fails, or if there are no files to compile.
* @param updatedProperty the property name to use.
*/
public void setUpdatedProperty(String updatedProperty) {
this.updatedProperty = updatedProperty;
}
/**
* The property to set on compliation failure.
* This property will be set if the compilation
* fails.
* @param errorProperty the property name to use.
*/
public void setErrorProperty(String errorProperty) {
this.errorProperty = errorProperty;
}
/**
* This property controls whether to include the
* destination classes directory in the classpath
* given to the compiler.
* The default value is "true".
* @param includeDestClasses the value to use.
*/
public void setIncludeDestClasses(boolean includeDestClasses) {
this.includeDestClasses = includeDestClasses;
}
/**
* Get the value of the includeDestClasses property.
* @return the value.
*/
public boolean isIncludeDestClasses() {
return includeDestClasses;
}
/**
* Get the result of the groovyc task (success or failure).
* @return true if compilation succeeded, or
* was not neccessary, false if the compilation failed.
*/
public boolean getTaskSuccess() {
return taskSuccess;
}
/*
public void setJointCompilationOptions(String options) {
String[] args = StringHelper.tokenizeUnquoted(options);
evalCompilerFlags(args);
}
*/
/**
* Add the configured nested javac task if present to initiate joint compilation.
*/
public void addConfiguredJavac ( final Javac javac ) {
this.javac = javac ;
jointCompilation = true ;
}
/**
* Enable compiler to report stack trace information if a problem occurs
* during compilation.
*/
public void setStacktrace(boolean stacktrace) {
configuration.setDebug(stacktrace);
}
/**
* Executes the task.
* @exception BuildException if an error occurs
*/
public void execute() throws BuildException {
checkParameters();
resetFileLists();
if (javac!=null) jointCompilation=true;
// scan source directories and dest directory to build up
// compile lists
String[] list = src.list();
for (int i = 0; i < list.length; i++) {
File srcDir = getProject().resolveFile(list[i]);
if (!srcDir.exists()) {
throw new BuildException("srcdir \"" + srcDir.getPath() + "\" does not exist!", getLocation());
}
DirectoryScanner ds = this.getDirectoryScanner(srcDir);
String[] files = ds.getIncludedFiles();
scanDir(srcDir, destDir != null ? destDir : srcDir, files);
}
compile();
if (updatedProperty != null
&& taskSuccess
&& compileList.length != 0) {
getProject().setNewProperty(updatedProperty, "true");
}
}
/**
* Clear the list of files to be compiled and copied..
*/
protected void resetFileLists() {
compileList = new File[0];
}
/**
* Scans the directory looking for source files to be compiled.
* The results are returned in the class variable compileList
*
* @param srcDir The source directory
* @param destDir The destination directory
* @param files An array of filenames
*/
protected void scanDir(File srcDir, File destDir, String[] files) {
GlobPatternMapper m = new GlobPatternMapper();
m.setFrom("*.groovy");
m.setTo("*.class");
SourceFileScanner sfs = new SourceFileScanner(this);
File[] newFiles = sfs.restrictAsFiles(files, srcDir, destDir, m);
addToCompileList(newFiles);
if (jointCompilation) {
m.setFrom("*.java");
m.setTo("*.class");
newFiles = sfs.restrictAsFiles(files, srcDir, destDir, m);
addToCompileList(newFiles);
}
}
protected void addToCompileList(File[] newFiles) {
if (newFiles.length > 0) {
File[] newCompileList = new File[compileList.length + newFiles.length];
System.arraycopy(compileList, 0, newCompileList, 0, compileList.length);
System.arraycopy(newFiles, 0, newCompileList, compileList.length, newFiles.length);
compileList = newCompileList;
}
}
/**
* Gets the list of files to be compiled.
* @return the list of files as an array
*/
public File[] getFileList() {
return compileList;
}
protected void checkParameters() throws BuildException {
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());
}
if (encoding != null && !Charset.isSupported(encoding)) {
throw new BuildException("encoding \"" + encoding + "\" not supported.");
}
}
protected void compile() {
if ( compileList.length > 0 ) {
log ( "Compiling " + compileList.length + " source file"
+ ( compileList.length == 1 ? "" : "s" )
+ ( destDir != null ? " to " + destDir : "" ) ) ;
if ( listFiles ) {
for ( int i = 0 ; i < compileList.length ; ++i ) {
String filename = compileList[i].getAbsolutePath ( ) ;
log ( filename ) ;
}
}
if ( fork ) {
final String separator = System.getProperty ( "file.separator" ) ;
final Path classpath = getClasspath ( ) != null ? getClasspath ( ) : new Path ( getProject ( ) ) ;
if ( includeAntRuntime ) { classpath.addExisting ( ( new Path ( getProject ( ) ) ).concatSystemClasspath ( "last" ) ) ; }
if ( includeJavaRuntime ) { classpath.addJavaRuntime ( ) ; }
final ArrayList commandLineList = new ArrayList ( ) ;
commandLineList.add ( System.getProperty ( "java.home" ) + separator + "bin" + separator + "java" ) ;
commandLineList.add ( "-classpath" ) ;
commandLineList.add ( classpath.toString ( ) ) ;
if ( ( memoryInitialSize != null ) && ! memoryInitialSize.equals ( "" ) ) { commandLineList.add ( "-Xms" + memoryInitialSize) ; }
if ( (memoryMaximumSize != null ) && ! memoryMaximumSize.equals ( "" ) ) { commandLineList.add ( "-Xmx" + memoryMaximumSize) ; }
commandLineList.add ( "org.codehaus.groovy.ant.Groovyc" ) ;
commandLineList.add ( "--classpath" ) ;
commandLineList.add ( classpath.toString ( ) ) ;
if ( jointCompilation ) {
commandLineList.add ( "-j" ) ;
for ( Iterator i = javac.getRuntimeConfigurableWrapper ( ).getAttributeMap ( ).entrySet ( ).iterator ( ) ; i.hasNext ( ) ; ) {
final Map.Entry e = (Map.Entry) i.next ( ) ;
final String key = e.getKey ( ).toString ( ) ;
if ( key.indexOf ( "debug" ) != -1 ) { commandLineList.add ( "-Fg" ) ; }
else if ( key.indexOf ( "classpath" ) != -1 ) { }
else { commandLineList.add ( "-J" + e.getKey ( ) + "=" + e.getValue ( ) ) ; }
}
}
commandLineList.add ( destDir.getPath ( ) ) ;
commandLineList.add ( src.toString ( ) ) ;
final String[] commandLine = new String [ commandLineList.size ( ) ] ;
for ( int i = 0 ; i < commandLine.length ; ++i ) { commandLine[ i ] = (String) commandLineList.get ( i ) ; }
final Execute executor = new Execute ( ) ; // new LogStreamHandler ( attributes , Project.MSG_INFO , Project.MSG_WARN ) ) ;
executor.setAntRun ( getProject ( ) ) ;
executor.setWorkingDirectory ( getProject ( ).getBaseDir ( ) ) ;
executor.setCommandline ( commandLine ) ;
try { executor.execute ( ) ; }
catch ( final IOException ioe ) { throw new BuildException ( "Error running forked groovyc." , ioe ) ; }
final int returnCode = executor.getExitValue ( ) ;
if ( returnCode != 0 ) { throw new BuildException ( "Forked groovyc returned error code: " + returnCode ) ; }
}
else {
try {
Path classpath = getClasspath();
if (classpath != null) {
configuration.setClasspath(classpath.toString());
}
configuration.setTargetDirectory(destDir);
if (encoding != null) {
configuration.setSourceEncoding(encoding);
}
CompilationUnit unit = makeCompileUnit();
unit.addSources(compileList);
unit.compile();
}
catch (Exception e) {
StringWriter writer = new StringWriter();
new ErrorReporter( e, false ).write( new PrintWriter(writer) );
String message = writer.toString();
if (failOnError) {
throw new BuildException(message, e, getLocation());
}
else {
log(message, Project.MSG_ERR);
}
}
}
}
}
protected CompilationUnit makeCompileUnit() {
if (javac!=null) {
Map compilerOptions = new HashMap();
compilerOptions.put("stubDir", createTempDir());
configuration.setJointCompilationOptions(compilerOptions);
jointCompilation = true;
}
if (jointCompilation) {
JavaAwareCompilationUnit unit = new JavaAwareCompilationUnit(configuration, buildClassLoaderFor());
if (javac!=null) {
final JavaCompiler compiler = new JavaCompiler() {
public void compile(List files, CompilationUnit cu) {
// forward options
if (javac.getClasspath()==null) {
javac.setClasspath(compileClasspath);
}
if (javac.getSourcepath()==null && compileSourcepath!=null) {
javac.createSourcepath().add(compileSourcepath);
}
if (javac.getEncoding()==null) {
javac.setEncoding(encoding);
}
javac.setDestdir(destDir);
Path p = javac.createSrc();
p.add(src);
Path tmpDir = new Path(getProject());
File dir = (File) cu.getConfiguration().getJointCompilationOptions().get("stubDir");
tmpDir.setLocation(dir);
p.add(tmpDir);
javac.execute();
}
};
unit.setCompilerFactory(new JavaCompilerFactory() {
public JavaCompiler createCompiler(CompilerConfiguration config) {
return compiler;
}
});
}
return unit;
} else {
return new CompilationUnit(configuration, null, buildClassLoaderFor());
}
}
protected File createTempDir() {
File tempFile;
try {
tempFile = File.createTempFile("groovy-generated-", "-java-source");
tempFile.delete();
tempFile.mkdirs();
} catch (IOException e) {
throw new BuildException(e);
}
return tempFile;
}
protected GroovyClassLoader buildClassLoaderFor() {
ClassLoader parent = this.getClass().getClassLoader();
if (parent instanceof AntClassLoader) {
AntClassLoader antLoader = (AntClassLoader) parent;
String[] pathElm = antLoader.getClasspath().split(File.pathSeparator);
List classpath = configuration.getClasspath();
/*
* Iterate over the classpath provided to groovyc, and add any missing path
* entries to the AntClassLoader. This is a workaround, since for some reason
* 'directory' classpath entries were not added to the AntClassLoader' classpath.
*/
for (Iterator iter = classpath.iterator(); iter.hasNext();) {
String cpEntry = (String) iter.next();
boolean found = false;
for (int i = 0; i < pathElm.length; i++) {
if (cpEntry.equals(pathElm[i])) {
found = true;
break;
}
}
/*
* fix for GROOVY-2284
* seems like AntClassLoader doesn't check if the file
* may not exist in the classpath yet
*/
if (!found && new File(cpEntry).exists())
antLoader.addPathElement(cpEntry);
}
}
return new GroovyClassLoader(parent, configuration);
}
private String[] evalCompilerFlags ( final Project project , final String[] args ) {
final Options options = new Options ( ) ;
options.addOption ( OptionBuilder.withLongOpt ( "classpath" ).hasArg ( ).withArgName ( "path" ).withDescription ( "Specify where to find the class files." ).create ( ) ) ;
options.addOption ( OptionBuilder.withArgName ( "property=value" ).withValueSeparator ( '=' ).hasArgs ( 2 ).create ( 'J' ) ) ;
options.addOption ( OptionBuilder.withArgName ( "flag" ).hasArg ( ).create ( 'F' ) ) ;
options.addOption ( OptionBuilder.withLongOpt ( "jointCompilation" ).create ( 'j' ) ) ;
final PosixParser cliParser = new PosixParser ( ) ;
CommandLine cli = null ;
try { cli = cliParser.parse ( options , args ) ; }
catch ( final ParseException e ) { throw new BuildException ( e ) ; }
if ( cli.hasOption ( "classpath" ) ) { setClasspath ( new Path ( project , cli.getOptionValue ( "classpath" ) ) ) ; }
jointCompilation = cli.hasOption ( 'j' ) ;
if ( jointCompilation ) {
final Map compilerOptions = new HashMap ( ) ;
compilerOptions.put ( "namedValues" , cli.getOptionValues ( 'J' ) ) ;
compilerOptions.put ( "flags" , cli.getOptionValues ( 'F' ) ) ;
compilerOptions.put ( "stubDir" , createTempDir ( ) ) ;
configuration.setJointCompilationOptions ( compilerOptions ) ;
}
return cli.getArgs ( ) ;
}
/**
* Provide an entry point for use by the java task. Also used for forking.
*
* FIXME: the includes and excludes are not handled -- there is no command line option for them.
*/
public static void main ( final String[] args ) {
final Project project = new Project ( ) ;
final DefaultLogger logger = new AnsiColorLogger ( ) ;
logger.setMessageOutputLevel ( Project.MSG_INFO ) ;
project.addBuildListener ( logger ) ;
project.init ( ) ;
final Groovyc compiler = new Groovyc ( ) ;
compiler.setProject ( project ) ;
final String[] arguments = compiler.evalCompilerFlags ( project , args ) ;
String dest = "." ;
String src = "." ;
boolean listFiles = false ;
if ( arguments.length > 0 ) { dest = arguments[0] ; }
if ( arguments.length > 1 ) { src = arguments[1] ; }
if ( arguments.length > 2 ) { listFiles = arguments[2].equalsIgnoreCase ( "true" ) ; }
compiler.setSrcdir ( new Path ( project , src ) ) ;
compiler.setDestdir ( project.resolveFile ( dest ) ) ;
compiler.setListfiles ( listFiles ) ;
compiler.execute ( ) ;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy