blob: 7637c453a62971ff0ce32d8a8eef7d39b1064a5f [file] [log] [blame]
/*
* 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 groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import groovy.util.AntBuilder;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.tools.ErrorReporter;
/**
* Executes a series of Groovy statements.
*
* <p>Statements can
* either be read in from a text file using the <i>src</i> attribute or from
* between the enclosing groovy tags.</p>
*
* @version $Id$
*/
public class Groovy extends Task
{
private final LoggingHelper log = new LoggingHelper(this);
/**
* files to load
*/
private Vector filesets = new Vector();
/**
* input file
*/
private File srcFile = null;
/**
* input command
*/
private String command = "";
/**
* Results Output file.
*/
private File output = null;
/**
* Append to an existing file or overwrite it?
*/
private boolean append = false;
private Path classpath;
/**
* Compiler configuration.
*
* Used to specify the debug output to print stacktraces in case something fails.
* TODO: Could probably be reused to specify the encoding of the files to load or other properties.
*/
private CompilerConfiguration configuration = new CompilerConfiguration();
/**
* Enable compiler to report stack trace information if a problem occurs
* during compilation.
* @param stacktrace
*/
public void setStacktrace(boolean stacktrace) {
configuration.setDebug(stacktrace);
}
/**
* Set the name of the file to be run. The folder of the file is automatically added to the classpath.
* Required unless statements are enclosed in the build file
*/
public void setSrc(final File srcFile) {
this.srcFile = srcFile;
}
/**
* Set an inline command to execute.
* NB: Properties are not expanded in this text.
*/
public void addText(String txt) {
log("addText('"+txt+"')", Project.MSG_VERBOSE);
this.command += txt;
}
/**
* Adds a set of files (nested fileset attribute).
*/
public void addFileset(FileSet set) {
filesets.addElement(set);
}
/**
* Set the output file;
* optional, defaults to the Ant log.
*/
public void setOutput(File output) {
this.output = output;
}
/**
* whether output should be appended to or overwrite
* an existing file. Defaults to false.
*
* @since Ant 1.5
*/
public void setAppend(boolean append) {
this.append = append;
}
/**
* Sets the classpath for loading.
* @param classpath The classpath to set
*/
public void setClasspath(final Path classpath) {
this.classpath = classpath;
}
/**
* Returns a new path element that can be configured.
* Gets called for instance by Ant when it encounters a nested <classpath> element.
*/
public Path createClasspath() {
if (this.classpath == null) {
this.classpath = new Path(getProject());
}
return this.classpath.createPath();
}
/**
* Set the classpath for loading
* using the classpath reference.
*/
public void setClasspathRef(final Reference r) {
createClasspath().setRefid(r);
}
/**
* Gets the classpath.
* @return Returns a Path
*/
public Path getClasspath() {
return classpath;
}
/**
* Load the file and then execute it
*/
public void execute() throws BuildException {
log.debug("execute()");
command = command.trim();
if (srcFile == null && command.length() == 0
&& filesets.isEmpty()) {
throw new BuildException("Source file does not exist!", getLocation());
}
if (srcFile != null && !srcFile.exists()) {
throw new BuildException("Source file does not exist!", getLocation());
}
// deal with the filesets
for (int i = 0; i < filesets.size(); i++) {
FileSet fs = (FileSet) filesets.elementAt(i);
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
File srcDir = fs.getDir(getProject());
String[] srcFiles = ds.getIncludedFiles();
}
try {
PrintStream out = System.out;
try {
if (output != null) {
log.verbose("Opening PrintStream to output file " + output);
out = new PrintStream(
new BufferedOutputStream(
new FileOutputStream(output
.getAbsolutePath(),
append)));
}
// if there are no groovy statements between the enclosing Groovy tags
// then read groovy statements in from a text file using the src attribute
if (command == null || command.trim().length() == 0) {
createClasspath().add(new Path(getProject(), srcFile.getParentFile().getCanonicalPath()));
command = getText(new BufferedReader(new FileReader(srcFile)));
}
if (command != null) {
execGroovy(command,out);
} else {
throw new BuildException("Source file does not exist!", getLocation());
}
} finally {
if (out != null && out != System.out) {
out.close();
}
}
} catch (IOException e) {
throw new BuildException(e, getLocation());
}
log.verbose("statements executed successfully");
}
private static String getText(BufferedReader reader) throws IOException {
StringBuffer answer = new StringBuffer();
// reading the content of the file within a char buffer allow to keep the correct line endings
char[] charBuffer = new char[4096];
int nbCharRead = 0;
while ((nbCharRead = reader.read(charBuffer)) != -1) {
// appends buffer
answer.append(charBuffer, 0, nbCharRead);
}
reader.close();
return answer.toString();
}
/**
* read in lines and execute them
*/
protected void runStatements(Reader reader, PrintStream out)
throws IOException {
log.debug("runStatements()");
StringBuffer txt = new StringBuffer();
String line = "";
BufferedReader in = new BufferedReader(reader);
while ((line = in.readLine()) != null) {
line = getProject().replaceProperties(line);
if (line.indexOf("--") >= 0) {
txt.append("\n");
}
}
// Catch any statements not followed by ;
if (!txt.equals("")) {
execGroovy(txt.toString(), out);
}
}
/**
* Exec the statement.
*/
protected void execGroovy(final String txt, final PrintStream out) {
log.debug("execGroovy()");
// Check and ignore empty statements
if ("".equals(txt.trim())) {
return;
}
log.verbose("Groovy: " + txt);
//log(getClasspath().toString(),Project.MSG_VERBOSE);
Object mavenPom = null;
final Project project = getProject();
final ClassLoader baseClassLoader;
// treat the case Ant is run through Maven, and
if ("org.apache.commons.grant.GrantProject".equals(project.getClass().getName())) {
try {
final Object propsHandler = project.getClass().getMethod("getPropsHandler", new Class[0]).invoke(project, new Object[0]);
final Field contextField = propsHandler.getClass().getDeclaredField("context");
contextField.setAccessible(true);
final Object context = contextField.get(propsHandler);
mavenPom = InvokerHelper.invokeMethod(context, "getProject", new Object[0]);
}
catch (Exception e) {
throw new BuildException("Impossible to retrieve Maven's Ant project: " + e.getMessage(), getLocation());
}
// let ASM lookup "root" classloader
Thread.currentThread().setContextClassLoader(GroovyShell.class.getClassLoader());
// load groovy into "root.maven" classloader instead of "root" so that
// groovy script can access Maven classes
baseClassLoader = mavenPom.getClass().getClassLoader();
} else {
baseClassLoader = GroovyShell.class.getClassLoader();
}
final GroovyClassLoader classLoader = new GroovyClassLoader(baseClassLoader);
addClassPathes(classLoader);
final GroovyShell groovy = new GroovyShell(classLoader, new Binding(), configuration);
try {
final Script script = groovy.parse(txt);
script.setProperty("ant", new AntBuilder(this));
script.setProperty("project", project);
script.setProperty("properties", new AntProjectPropertiesDelegate(project));
script.setProperty("target", getOwningTarget());
script.setProperty("task", this);
if (mavenPom != null) {
script.setProperty("pom", mavenPom);
}
script.run();
} catch (CompilationFailedException e) {
StringWriter writer = new StringWriter();
new ErrorReporter( e, false ).write( new PrintWriter(writer) );
String message = writer.toString();
throw new BuildException("Script Failed: "+ message, getLocation());
}
}
/**
* Adds the class pathes (if any)
* @param classLoader the classloader to configure
*/
protected void addClassPathes(final GroovyClassLoader classLoader)
{
if (classpath != null)
{
for (int i = 0; i < classpath.list().length; i++)
{
classLoader.addClasspath(classpath.list()[i]);
}
}
}
/**
* print any results in the statement.
*/
protected void printResults(PrintStream out) {
log.debug("printResults()");
StringBuffer line = new StringBuffer();
out.println(line);
line = new StringBuffer();
out.println();
}
}