Package org.codehaus.groovy.ant

Source Code of org.codehaus.groovy.ant.Groovyc

/*
* Copyright 2003-2010 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.GroovyClassLoader;
import groovy.lang.GroovyResourceLoader;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.PosixParser;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.RuntimeConfigurable;
import org.apache.tools.ant.taskdefs.Execute;
import org.apache.tools.ant.taskdefs.Javac;
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 org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.SourceExtensionHandler;
import org.codehaus.groovy.tools.ErrorReporter;
import org.codehaus.groovy.tools.FileSystemCompiler;
import org.codehaus.groovy.tools.RootLoader;
import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

/**
* Compiles Groovy source files. This task can take the following arguments:
* <ul>
* <li>srcdir</li>
* <li>destdir</li>
* <li>classpath</li>
* <li>encoding</li>
* <li>verbose</li>
* <li>failonerror</li>
* <li>includeantruntime</li>
* <li>includejavaruntime</li>
* <li>memoryInitialSize</li>
* <li>memoryMaximumSize</li>
* <li>fork</li>
* <li>stacktrace</li>
* <li>stubdir</li>
* </ul>
* Of these arguments, the <b>srcdir</b> and <b>destdir</b> are required.
* <p/>
* <p>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.</p>
* <p/>
* <p>Based heavily on the Javac implementation in Ant.</p>
*
* @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
* @author Hein Meling
* @author <a href="mailto:russel.winder@concertant.com">Russel Winder</a>
* @author Danno Ferrin
* @version $Revision: 21603 $
*/
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 stacktrace = false;
    private boolean verbose = false;
    private boolean includeAntRuntime = true;
    private boolean includeJavaRuntime = false;
    private boolean fork = false;
    private File forkJDK;
    private String memoryInitialSize;
    private String memoryMaximumSize;
    private String scriptExtension = "*.groovy";
    private String targetBytecode = null;

    protected boolean failOnError = true;
    protected boolean listFiles = false;
    protected File[] compileList = new File[0];

    private String updatedProperty;
    private String errorProperty;
    private boolean taskSuccess = true; // assume the best
    private boolean includeDestClasses = true;

    protected CompilerConfiguration configuration;
    private Javac javac;
    private boolean jointCompilation;

    private List<File> temporaryFiles = new ArrayList<File>(2);
    private File stubDir;
    private boolean keepStubs;

    private Set<String> scriptExtensions = new LinkedHashSet<String>();

    /**
     * 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 directories as a path
     */
    public Path getSrcdir() {
        return src;
    }

    /**
     * Set the extension to use when searching for Groovy source files.
     * Accepts extensions in the form *.groovy, .groovy or groovy
     *
     * @param scriptExtension the extension of Groovy source files
     */
    public void setScriptExtension(String scriptExtension) {
        if (scriptExtension.startsWith("*.")) {
            this.scriptExtension = scriptExtension;
        } else if (scriptExtension.startsWith(".")) {
            this.scriptExtension = "*" + scriptExtension;
        } else {
            this.scriptExtension = "*." + scriptExtension;
        }
    }

    /**
     * Get the extension to use when searching for Groovy source files.
     *
     * @return the extension of Groovy source files
     */
    public String getScriptExtension() {
        return scriptExtension;
    }

    /**
     * Sets the bytecode compatibility mode
     *
     * @param version the bytecode compatibility mode
     */
    public void setTargetBytecode(String version) {
        if (CompilerConfiguration.PRE_JDK5.equals(version) || CompilerConfiguration.POST_JDK5.equals(version)) {
            this.targetBytecode = version;
        }
    }

    /**
     * Retrieves the compiler bytecode compatibility mode.
     *
     * @return bytecode compatibility mode. Can be either <tt>1.5</tt> or <tt>1.4</tt>.
     */
    public String getTargetBytecode() {
        return this.targetBytecode;
    }

    /**
     * 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.
     * Default is false.
     *
     * @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;
    }

    /**
     * Enable verbose compiling which will display which files
     * are being compiled. Default is false.
     */
    public void setVerbose(boolean verbose) {
        this.verbose = verbose;
    }

    /**
     * Gets the verbose flag.
     *
     * @return the verbose flag
     */
    public boolean getVerbose() {
        return verbose;
    }

    /**
     * If true, includes Ant's own classpath in the classpath. Default is true.
     * If setting to false and using groovyc in conjunction with AntBuilder
     * you might need to explicitly add the Groovy jar(s) to the groovyc
     * classpath using a nested classpath task.
     *
     * @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. Default is false.
     *
     * @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. Default is false.
     *
     * @param f "true|false|on|off|yes|no"
     */
    public void setFork(boolean f) {
        fork = f;
    }

    /**
     * The JDK Home to use when forked.
     *
     * @param home the java.home value to use, default is the current JDK's home
     */
    public void setJavaHome(File home) {
        forkJDK = home;
    }

    /**
     * The property to set on compilation 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 compilation 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 necessary, 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. Default is false.
     */
    public void setStacktrace(boolean stacktrace) {
        this.stacktrace = stacktrace;
    }

    /**
     * Executes the task.
     *
     * @throws BuildException if an error occurs
     */
    public void execute() throws BuildException {
        checkParameters();
        resetFileLists();
        loadRegisteredScriptExtensions();

        if (javac != null) jointCompilation = true;

        // scan source directories and dest directory to build up
        // compile lists
        String[] list = src.list();
        for (String filename : list) {
            File file = getProject().resolveFile(filename);
            if (!file.exists()) {
                throw new BuildException("srcdir \"" + file.getPath() + "\" does not exist!", getLocation());
            }
            DirectoryScanner ds = this.getDirectoryScanner(file);
            String[] files = ds.getIncludedFiles();
            scanDir(file, destDir != null ? destDir : file, 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];
        scriptExtensions = new LinkedHashSet<String>();
    }

    /**
     * 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();
        SourceFileScanner sfs = new SourceFileScanner(this);
        File[] newFiles;
        for (String extension : getScriptExtensions()) {
            m.setFrom("*." + extension);
            m.setTo("*.class");
            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() {
        try {
            if (compileList.length > 0) {
                log("Compiling " + compileList.length + " source file"
                        + (compileList.length == 1 ? "" : "s")
                        + (destDir != null ? " to " + destDir : ""));

                if (listFiles) {
                    for (File srcFile : compileList) {
                        log(srcFile.getAbsolutePath());
                    }
                }

                Path classpath = getClasspath() != null ? getClasspath() : new Path(getProject());
                // extract joint options, some get pushed up...
                List<String> jointOptions = new ArrayList<String>();
                if (jointCompilation) {
                    RuntimeConfigurable rc = javac.getRuntimeConfigurableWrapper();
                    for (Iterator i = rc.getAttributeMap().entrySet().iterator(); i.hasNext();) {
                        final Map.Entry e = (Map.Entry) i.next();
                        final String key = e.getKey().toString();
                        final String value = getProject().replaceProperties(e.getValue().toString());
                        if (key.contains("debug")) {
                            String level = "";
                            if (javac.getDebugLevel() != null) {
                                level = ":" + javac.getDebugLevel();
                            }
                            jointOptions.add("-Fg" + level);
                        } else if (key.contains("debugLevel")) {
                            // ignore, taken care of in debug
                        } else if ((key.contains("nowarn"))
                                || (key.contains("verbose"))
                                || (key.contains("deprecation"))) {
                            // false is default, so something to do only in true case
                            if ("on".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value) || "yes".equalsIgnoreCase("value"))
                                jointOptions.add("-F" + key);
                        } else if (key.contains("classpath")) {
                            classpath.add(javac.getClasspath());
                        } else if ((key.contains("depend"))
                                || (key.contains("extdirs"))
                                || (key.contains("encoding"))
                                || (key.contains("source"))
                                || (key.contains("target"))
                                || (key.contains("verbose"))) { // TODO remove extra verbose?
                            jointOptions.add("-J" + key + "=" + value);
                        } else {
                            log("The option " + key + " cannot be set on the contained <javac> element. The option will be ignored", Project.MSG_WARN);
                        }
                        // includes? excludes?
                    }
                    // ant's <javac> supports nested <compilerarg value=""> elements (there can be multiple of them)
                    // for additional options to be passed to javac.
                    Enumeration children = rc.getChildren();
                    while (children.hasMoreElements()) {
                        RuntimeConfigurable childrc = (RuntimeConfigurable) children.nextElement();
                        if (childrc.getElementTag().equals("compilerarg")) {
                            for (Iterator i = childrc.getAttributeMap().entrySet().iterator(); i.hasNext();) {
                                final Map.Entry e = (Map.Entry) i.next();
                                final String key = e.getKey().toString();
                                if (key.equals("value")) {
                                    final String value = getProject().replaceProperties(e.getValue().toString());
                                    StringTokenizer st = new StringTokenizer(value, " ");
                                    while (st.hasMoreTokens()) {
                                        String optionStr = st.nextToken();
                                        jointOptions.add(optionStr.replace("-X", "-FX"));
                                    }
                                }
                            }
                        }
                    }
                }

                String separator = System.getProperty("file.separator");
                List<String> commandLineList = new ArrayList<String>();

                if (fork) {
                    String javaHome;
                    if (forkJDK != null) {
                        javaHome = forkJDK.getPath();
                    } else {
                        javaHome = System.getProperty("java.home");
                    }
                    if (includeAntRuntime) {
                        classpath.addExisting((new Path(getProject())).concatSystemClasspath("last"));
                    }
                    if (includeJavaRuntime) {
                        classpath.addJavaRuntime();
                    }

                    commandLineList.add(javaHome + separator + "bin" + separator + "java");
                    commandLineList.add("-classpath");
                    commandLineList.add(classpath.toString());

                    final String fileEncodingProp = System.getProperty("file.encoding");
                    if ((fileEncodingProp != null) && !fileEncodingProp.equals("")) {
                        commandLineList.add("-Dfile.encoding=" + fileEncodingProp);
                    }
                    if (targetBytecode != null) {
                        commandLineList.add("-Dgroovy.target.bytecode=" + targetBytecode);
                    }

                    if ((memoryInitialSize != null) && !memoryInitialSize.equals("")) {
                        commandLineList.add("-Xms" + memoryInitialSize);
                    }
                    if ((memoryMaximumSize != null) && !memoryMaximumSize.equals("")) {
                        commandLineList.add("-Xmx" + memoryMaximumSize);
                    }
                    if (!"*.groovy".equals(getScriptExtension())) {
                        String tmpExtension = getScriptExtension();
                        if (tmpExtension.startsWith("*.")) tmpExtension = tmpExtension.substring(1);
                        commandLineList.add("-Dgroovy.default.scriptExtension=" + tmpExtension);
                    }
                    commandLineList.add("org.codehaus.groovy.tools.FileSystemCompiler");
                }
                commandLineList.add("--classpath");
                commandLineList.add(classpath.toString());
                if (jointCompilation) {
                    commandLineList.add("-j");
                    commandLineList.addAll(jointOptions);
                }
                commandLineList.add("-d");
                commandLineList.add(destDir.getPath());
                if (encoding != null) {
                    commandLineList.add("--encoding");
                    commandLineList.add(encoding);
                }
                if (stacktrace) {
                    commandLineList.add("-e");
                }

                // check to see if an external file is needed
                int count = 0;
                if (fork) {

                    for (File srcFile : compileList) {
                        count += srcFile.getPath().length();
                    }
                    for (Object commandLineArg : commandLineList) {
                        count += commandLineArg.toString().length();
                    }
                    count += compileList.length;
                    count += commandLineList.size();
                }
                // 32767 is the command line length limit on Windows
                if (fork && (count > 32767)) {
                    try {
                        File tempFile = File.createTempFile("groovyc-files-", ".txt");
                        temporaryFiles.add(tempFile);
                        PrintWriter pw = new PrintWriter(new FileWriter(tempFile));
                        for (File srcFile : compileList) {
                            pw.println(srcFile.getPath());
                        }
                        pw.close();
                        commandLineList.add("@" + tempFile.getPath());
                    } catch (IOException e) {
                        log("Error creating file list", e, Project.MSG_ERR);
                    }
                } else {
                    for (File srcFile : compileList) {
                        commandLineList.add(srcFile.getPath());
                    }
                }
                final String[] commandLine = new String[commandLineList.size()];
                for (int i = 0; i < commandLine.length; ++i) {
                    commandLine[i] = commandLineList.get(i);
                }
                log("Compilation arguments:", Project.MSG_VERBOSE);
                log(DefaultGroovyMethods.join(commandLine, "\n"), Project.MSG_VERBOSE);

                if (fork) {
                    // use the main method in FileSystemCompiler
                    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) {

                        if (failOnError) {
                            throw new BuildException("Forked groovyc returned error code: " + returnCode);
                        } else {
                            log("Forked groovyc returned error code: " + returnCode, Project.MSG_ERR);
                        }
                    }
                } else {
                    // hand crank it so we can add our own compiler configuration
                    try {
                        Options options = FileSystemCompiler.createCompilationOptions();

                        PosixParser cliParser = new PosixParser();

                        CommandLine cli;
                        cli = cliParser.parse(options, commandLine);

                        configuration = FileSystemCompiler.generateCompilerConfigurationFromOptions(cli);
                        configuration.setScriptExtensions(getScriptExtensions());
                        String tmpExtension = getScriptExtension();
                        if (tmpExtension.startsWith("*.")) tmpExtension = tmpExtension.substring(1);
                        configuration.setDefaultScriptExtension(tmpExtension);

                        // Load the file name list
                        String[] filenames = FileSystemCompiler.generateFileNamesFromOptions(cli);
                        boolean fileNameErrors = filenames == null;

                        fileNameErrors = fileNameErrors && !FileSystemCompiler.validateFiles(filenames);

                        if (targetBytecode != null) {
                            configuration.setTargetBytecode(targetBytecode);
                        }

                        if (!fileNameErrors) {
                            FileSystemCompiler.doCompilation(configuration, makeCompileUnit(), filenames);
                        }

                    } catch (Exception re) {
                        Throwable t = re;
                        if ((re.getClass() == RuntimeException.class) && (re.getCause() != null)) {
                            // unwrap to the real exception
                            t = re.getCause();
                        }
                        StringWriter writer = new StringWriter();
                        new ErrorReporter(t, false).write(new PrintWriter(writer));
                        String message = writer.toString();

                        if (failOnError) {
                            log(message, Project.MSG_INFO);
                            throw new BuildException("Compilation Failed", t, getLocation());
                        } else {
                            log(message, Project.MSG_ERR);
                        }
                    }
                }
            }
        } finally {
            for (File temporaryFile : temporaryFiles) {
                try {
                    FileSystemCompiler.deleteRecursive(temporaryFile);
                } catch (Throwable t) {
                    System.err.println("error: could not delete temp files - " + temporaryFile.getPath());
                }
            }
        }
    }

    protected CompilationUnit makeCompileUnit() {
        Map<String, Object> options = configuration.getJointCompilationOptions();
        if (options != null) {
            if (keepStubs) {
                options.put("keepStubs", Boolean.TRUE);
            }
            if (stubDir != null) {
                options.put("stubDir", stubDir);
            } else {
                try {
                    File tempStubDir = FileSystemCompiler.createTempDir();
                    temporaryFiles.add(tempStubDir);
                    options.put("stubDir", tempStubDir);
                } catch (IOException ioe) {
                    throw new BuildException(ioe);
                }
            }
            return new JavaAwareCompilationUnit(configuration, buildClassLoaderFor());
        } else {
            return new CompilationUnit(configuration, null, buildClassLoaderFor());
        }
    }


    protected GroovyClassLoader buildClassLoaderFor() {
        ClassLoader parent = getIncludeantruntime()
                ? getClass().getClassLoader()
                : new AntClassLoader(new RootLoader(new URL[0], null), getProject(), getClasspath());
        if (parent instanceof AntClassLoader) {
            AntClassLoader antLoader = (AntClassLoader) parent;
            String[] pathElm = antLoader.getClasspath().split(File.pathSeparator);
            List<String> 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 (String cpEntry : classpath) {
                boolean found = false;
                for (String path : pathElm) {
                    if (cpEntry.equals(path)) {
                        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);
            }
        }

        GroovyClassLoader loader = new GroovyClassLoader(parent, configuration);
        // in command line we don't need to do script lookups
        loader.setResourceLoader(new GroovyResourceLoader() {
            public URL loadGroovySource(String filename) throws MalformedURLException {
                return null;
            }
        });

        return loader;
    }

    /**
     * Set the stub directory into which the Java source stub
     * files should be generated. The directory need not exist
     * and will not be deleted automatically - though its contents
     * will be cleared unless 'keepStubs' is true. Ignored when forked.
     *
     * @param stubDir the stub directory
     */
    public void setStubdir(File stubDir) {
        jointCompilation = true;
        this.stubDir = stubDir;
    }

    /**
     * Gets the stub directory into which the Java source stub
     * files should be generated
     *
     * @return the stub directory
     */
    public File getStubdir() {
        return stubDir;
    }

    /**
     * Set the keepStubs flag. Defaults to false. Set to true for debugging.
     * Ignored when forked.
     *
     * @param keepStubs should stubs be retained
     */
    public void setKeepStubs(boolean keepStubs) {
        this.keepStubs = keepStubs;
    }

    /**
     * Gets the keepStubs flag.
     *
     * @return the keepStubs flag
     */
    public boolean getKeepStubs() {
        return keepStubs;
    }

    private Set<String> getScriptExtensions() {
        return scriptExtensions;
    }

    private void loadRegisteredScriptExtensions() {
        if (scriptExtensions.isEmpty()) {

            scriptExtensions.add(getScriptExtension().substring(2)); // first extension will be the one set explicitly on <groovyc>

            Path classpath = getClasspath() != null ? getClasspath() : new Path(getProject());
            final String[] pe = classpath.list();
            final GroovyClassLoader loader = new GroovyClassLoader(getClass().getClassLoader());
            for (String file : pe) {
                loader.addClasspath(file);
            }
            scriptExtensions.addAll(SourceExtensionHandler.getRegisteredExtensions(loader));
        }
    }
}
TOP

Related Classes of org.codehaus.groovy.ant.Groovyc

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.