GroovyCompiler.java
/*
* Copyright (C) 2025 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.gmavenplus.util;
import org.apache.maven.plugin.logging.Log;
import org.codehaus.gmavenplus.model.GroovyCompileConfiguration;
import org.codehaus.gmavenplus.model.GroovyDocConfiguration;
import org.codehaus.gmavenplus.model.GroovyStubConfiguration;
import org.codehaus.gmavenplus.model.Link;
import org.apache.maven.shared.model.fileset.FileSet;
import org.apache.maven.shared.model.fileset.util.FileSetManager;
import org.codehaus.gmavenplus.groovyworkarounds.GroovyDocTemplateInfo;
import java.util.ArrayList;
import java.util.Properties;
import org.codehaus.gmavenplus.model.internal.Version;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.CodeSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.codehaus.gmavenplus.util.ReflectionUtils.*;
/**
* Handles the actual compilation logic, separated from the Mojo to allow forked execution.
*
* @author Keegan Witt
*/
public class GroovyCompiler {
/**
* Groovy 5.0.0-beta-1 version.
*/
protected static final Version GROOVY_5_0_0_BETA_1 = new Version(5, 0, 0, "beta-1");
/**
* Groovy 5.0.0-alpha-13 version.
*/
protected static final Version GROOVY_5_0_0_ALPHA13 = new Version(5, 0, 0, "alpha-13");
/**
* Groovy 5.0.0-alpha-11 version.
*/
protected static final Version GROOVY_5_0_0_ALPHA11 = new Version(5, 0, 0, "alpha-11");
/**
* Groovy 5.0.0-alpha-8 version.
*/
protected static final Version GROOVY_5_0_0_ALPHA8 = new Version(5, 0, 0, "alpha-8");
/**
* Groovy 5.0.0-alpha-3 version.
*/
protected static final Version GROOVY_5_0_0_ALPHA3 = new Version(5, 0, 0, "alpha-3");
/**
* Groovy 5.0.0-alpha-1 version.
*/
protected static final Version GROOVY_5_0_0_ALPHA1 = new Version(5, 0, 0, "alpha-1");
/**
* Groovy 4.0.27 version.
*/
protected static final Version GROOVY_4_0_27 = new Version(4, 0, 27);
/**
* Groovy 4.0.24 version.
*/
protected static final Version GROOVY_4_0_24 = new Version(4, 0, 24);
/**
* Groovy 4.0.21 version.
*/
protected static final Version GROOVY_4_0_21 = new Version(4, 0, 21);
/**
* Groovy 4.0.11 version.
*/
protected static final Version GROOVY_4_0_16 = new Version(4, 0, 16);
/**
* Groovy 4.0.11 version.
*/
protected static final Version GROOVY_4_0_11 = new Version(4, 0, 11);
/**
* Groovy 4.0.6 version.
*/
protected static final Version GROOVY_4_0_6 = new Version(4, 0, 6);
/**
* Groovy 4.0.2 version.
*/
protected static final Version GROOVY_4_0_2 = new Version(4, 0, 2);
/**
* Groovy 4.0.0 beta-1 version.
*/
protected static final Version GROOVY_4_0_0_BETA1 = new Version(4, 0, 0, "beta-1");
/**
* Groovy 4.0.0 alpha-3 version.
*/
protected static final Version GROOVY_4_0_0_ALPHA3 = new Version(4, 0, 0, "alpha-3");
/**
* Groovy 4.0.0 alpha-1 version.
*/
protected static final Version GROOVY_4_0_0_ALPHA1 = new Version(4, 0, 0, "alpha-1");
/**
* Groovy 3.0.8 version.
*/
protected static final Version GROOVY_3_0_8 = new Version(3, 0, 8);
/**
* Groovy 3.0.6 version.
*/
protected static final Version GROOVY_3_0_6 = new Version(3, 0, 6);
/**
* Groovy 3.0.5 version.
*/
protected static final Version GROOVY_3_0_5 = new Version(3, 0, 5);
/**
* Groovy 3.0.3 version.
*/
protected static final Version GROOVY_3_0_3 = new Version(3, 0, 3);
/**
* Groovy 3.0.0 beta-2 version.
*/
protected static final Version GROOVY_3_0_0_BETA2 = new Version(3, 0, 0, "beta-2");
/**
* Groovy 3.0.0 beta-1 version.
*/
protected static final Version GROOVY_3_0_0_BETA1 = new Version(3, 0, 0, "beta-1");
/**
* Groovy 3.0.0 alpha-4 version.
*/
protected static final Version GROOVY_3_0_0_ALPHA4 = new Version(3, 0, 0, "alpha-4");
/**
* Groovy 3.0.0 alpha-2 version.
*/
protected static final Version GROOVY_3_0_0_ALPHA2 = new Version(3, 0, 0, "alpha-2");
/**
* Groovy 3.0.0 alpha-1 version.
*/
protected static final Version GROOVY_3_0_0_ALPHA1 = new Version(3, 0, 0, "alpha-1");
/**
* Groovy 2.6.0 alpha-4 version.
*/
protected static final Version GROOVY_2_6_0_ALPHA4 = new Version(2, 6, 0, "alpha-4");
/**
* Groovy 2.6.0 alpha-1 version.
*/
protected static final Version GROOVY_2_6_0_ALPHA1 = new Version(2, 6, 0, "alpha-1");
/**
* Groovy 2.5.7 version.
*/
protected static final Version GROOVY_2_5_7 = new Version(2, 5, 7);
/**
* Groovy 2.5.3 version.
*/
protected static final Version GROOVY_2_5_3 = new Version(2, 5, 3);
/**
* Groovy 2.5.0 alpha-1 version.
*/
protected static final Version GROOVY_2_5_0_ALPHA1 = new Version(2, 5, 0, "alpha-1");
/**
* Groovy 2.3.3 version.
*/
protected static final Version GROOVY_2_3_3 = new Version(2, 3, 3);
/**
* Groovy 1.8.2 version.
*/
protected static final Version GROOVY_1_8_2 = new Version(1, 8, 2);
/**
* Groovy 1.8.3 version.
*/
protected static final Version GROOVY_1_8_3 = new Version(1, 8, 3);
/**
* Groovy 2.1.3 version.
*/
protected static final Version GROOVY_2_1_3 = new Version(2, 1, 3);
/**
* Groovy 2.1.0 beta-1 version.
*/
protected static final Version GROOVY_2_1_0_BETA1 = new Version(2, 1, 0, "beta-1");
/**
* Groovy 2.0.0 beta-3 version.
*/
protected static final Version GROOVY_2_0_0_BETA3 = new Version(2, 0, 0, "beta-3");
/**
* Groovy 1.9.0 beta-1 version.
*/
protected static final Version GROOVY_1_9_0_BETA1 = new Version(1, 9, 0, "beta-1");
/**
* Groovy 1.9.0 beta-3 version.
*/
protected static final Version GROOVY_1_9_0_BETA3 = new Version(1, 9, 0, "beta-3");
/**
* Groovy 1.6.0 version.
*/
protected static final Version GROOVY_1_6_0 = new Version(1, 6, 0);
/**
* Groovy 1.6.0 RC-2 version.
*/
protected static final Version GROOVY_1_6_0_RC2 = new Version(1, 6, 0, "RC-2");
/**
* Groovy 1.5.2 version.
*/
protected static final Version GROOVY_1_5_2 = new Version(1, 5, 2);
/**
* Java 1.7 version.
*/
protected static final Version JAVA_1_7 = new Version(1, 7);
/**
* Java 1.8 version.
*/
protected static final Version JAVA_1_8 = new Version(1, 8);
/**
* Java 1.8 version.
*/
protected static final Version JAVA_12 = new Version(12);
private final ClassWrangler classWrangler;
private final Log log;
public GroovyCompiler(ClassWrangler classWrangler, Log log) {
this.classWrangler = classWrangler;
this.log = log;
}
public void compile(GroovyCompileConfiguration configuration) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
if (configuration.getSources() == null || configuration.getSources().isEmpty()) {
log.info("No sources specified for compilation. Skipping.");
return;
}
if (!configuration.isSkipBytecodeCheck()) {
verifyGroovyVersionSupportsTargetBytecode(configuration.getTargetBytecode());
}
// get classes we need with reflection
Class<?> compilerConfigurationClass = classWrangler.getClass("org.codehaus.groovy.control.CompilerConfiguration");
Class<?> compilationUnitClass = classWrangler.getClass("org.codehaus.groovy.control.CompilationUnit");
Class<?> groovyClassLoaderClass = classWrangler.getClass("groovy.lang.GroovyClassLoader");
// setup compile options
Object compilerConfiguration = setupCompilerConfiguration(configuration, compilerConfigurationClass);
Object groovyClassLoader = invokeConstructor(findConstructor(groovyClassLoaderClass, ClassLoader.class, compilerConfigurationClass), classWrangler.getClassLoader(), compilerConfiguration);
Object transformLoader = invokeConstructor(findConstructor(groovyClassLoaderClass, ClassLoader.class), classWrangler.getClassLoader());
// add Groovy sources
Object compilationUnit = setupCompilationUnit(configuration.getSources(), compilerConfigurationClass, compilationUnitClass, groovyClassLoaderClass, compilerConfiguration, groovyClassLoader, transformLoader);
// compile the classes
invokeMethod(findMethod(compilationUnitClass, "compile"), compilationUnit);
// log compiled classes
List<?> classes = (List<?>) invokeMethod(findMethod(compilationUnitClass, "getClasses"), compilationUnit);
log.info("Compiled " + classes.size() + " file" + (classes.size() != 1 ? "s" : "") + ".");
}
public void generateGroovyDoc(GroovyDocConfiguration configuration) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
if (configuration.getSourceDirectories() == null || configuration.getSourceDirectories().length == 0) {
log.info("No source directories specified for GroovyDoc generation. Skipping.");
return;
}
// Note: minGroovyVersion check is usually done by caller (Mojo) but strict check is good.
// GroovyDoc supports 1.5.2+ generally (LinkArgument) or 1.6+ for properties?
// AbstractGroovyDocMojo checks groovyVersionSupportsAction() which uses mojo defined min version.
// We can skip strict min version check here and rely on feature detection or caller.
// get classes we need with reflection
Class<?> groovyDocToolClass = classWrangler.getClass(configuration.getGroovyDocToolClass() == null ? "org.codehaus.groovy.tools.groovydoc.GroovyDocTool" : configuration.getGroovyDocToolClass());
Class<?> outputToolClass = classWrangler.getClass(configuration.getOutputToolClass() == null ? "org.codehaus.groovy.tools.groovydoc.OutputTool" : configuration.getOutputToolClass());
Class<?> fileOutputToolClass = classWrangler.getClass(configuration.getFileOutputToolClass() == null ? "org.codehaus.groovy.tools.groovydoc.FileOutputTool" : configuration.getFileOutputToolClass());
Class<?> resourceManagerClass = classWrangler.getClass(configuration.getResourceManagerClass() == null ? "org.codehaus.groovy.tools.groovydoc.ResourceManager" : configuration.getResourceManagerClass());
Class<?> classpathResourceManagerClass = classWrangler.getClass(configuration.getClasspathResourceManagerClass() == null ? "org.codehaus.groovy.tools.groovydoc.ClasspathResourceManager" : configuration.getClasspathResourceManagerClass());
// set up GroovyDoc options
if (configuration.isAttachGroovyDocAnnotation()) {
if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_3_0_0_ALPHA4)) {
System.setProperty("runtimeGroovydoc", "true");
} else {
log.warn("Requested to enable attaching GroovyDoc annotation, but your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support it (must be " + GROOVY_3_0_0_ALPHA4 + " or newer). Ignoring enableGroovyDocAnnotation parameter.");
}
}
Object fileOutputTool = invokeConstructor(findConstructor(fileOutputToolClass));
Object classpathResourceManager = invokeConstructor(findConstructor(classpathResourceManagerClass));
FileSetManager fileSetManager = new FileSetManager();
List<String> sourceDirectoriesStrings = new ArrayList<>();
for (FileSet sourceDirectory : configuration.getSourceDirectories()) {
sourceDirectoriesStrings.add(sourceDirectory.getDirectory());
}
GroovyDocTemplateInfo groovyDocTemplateInfo = new GroovyDocTemplateInfo(classWrangler.getGroovyVersion());
List<?> groovyDocLinks = setupLinks(configuration);
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_1_6_0_RC2) && configuration.getDocProperties() != null && !configuration.getDocProperties().isEmpty()) {
log.warn("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support GroovyDoc documentation properties (docTitle, footer, header, displayAuthor, overviewFile, and scope). You need Groovy 1.6-RC-2 or newer to support this. Ignoring properties.");
}
// prevent Java stubs from overwriting GroovyDoc
List<String> groovyDocSources = setupGroovyDocSources(configuration.getSourceDirectories(), fileSetManager);
// instantiate GroovyDocTool
Object groovyDocTool = createGroovyDocTool(groovyDocToolClass, resourceManagerClass, configuration.getDocProperties(), classpathResourceManager, sourceDirectoriesStrings, groovyDocTemplateInfo, groovyDocLinks, configuration);
// generate GroovyDoc
performGroovyDocGeneration(configuration.getOutputDirectory(), groovyDocToolClass, outputToolClass, fileOutputTool, groovyDocSources, groovyDocTool);
}
@SuppressWarnings({"rawtypes", "unchecked"})
protected List<?> setupLinks(GroovyDocConfiguration configuration) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
List linksList = new ArrayList();
if (configuration.getLinks() != null && !configuration.getLinks().isEmpty()) {
Class<?> linkArgumentClass = null;
if (configuration.getLinkArgumentClass() == null) {
if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_6_0_RC2)) {
linkArgumentClass = classWrangler.getClass("org.codehaus.groovy.tools.groovydoc.LinkArgument");
} else if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_5_2)) {
linkArgumentClass = classWrangler.getClass("org.codehaus.groovy.ant.Groovydoc$LinkArgument");
}
} else {
linkArgumentClass = classWrangler.getClass(configuration.getLinkArgumentClass());
}
if (linkArgumentClass != null) {
Method setHref = findMethod(linkArgumentClass, "setHref", String.class);
Method setPackages = findMethod(linkArgumentClass, "setPackages", String.class);
for (Link link : configuration.getLinks()) {
Object linkArgument = invokeConstructor(findConstructor(linkArgumentClass));
invokeMethod(setHref, linkArgument, link.getHref());
invokeMethod(setPackages, linkArgument, link.getPackages());
linksList.add(linkArgument);
}
} else {
log.warn("Requested to use GroovyDoc links, but your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support it (must be 1.5.2 or newer). Ignoring links parameter.");
}
}
return linksList;
}
protected Object createGroovyDocTool(final Class<?> groovyDocToolClass, final Class<?> resourceManagerClass, final Properties docProperties, final Object classpathResourceManager, final List<String> sourceDirectories, final GroovyDocTemplateInfo groovyDocTemplateInfo, final List<?> groovyDocLinks, GroovyDocConfiguration configuration) throws InvocationTargetException, IllegalAccessException, InstantiationException {
Object groovyDocTool;
String[] defaultDocTemplates = configuration.getDefaultDocTemplates();
String[] defaultPackageTemplates = configuration.getDefaultPackageTemplates();
String[] defaultClassTemplates = configuration.getDefaultClassTemplates();
if ((ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_4_0_27) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA1)) || ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_5_0_0_BETA_1)) {
groovyDocTool = invokeConstructor(findConstructor(groovyDocToolClass, resourceManagerClass, String[].class, String[].class, String[].class, String[].class, List.class, String.class, Properties.class),
classpathResourceManager,
sourceDirectories.toArray(new String[0]),
defaultDocTemplates == null ? groovyDocTemplateInfo.defaultDocTemplates() : defaultDocTemplates,
defaultPackageTemplates == null ? groovyDocTemplateInfo.defaultPackageTemplates() : defaultPackageTemplates,
defaultClassTemplates == null ? groovyDocTemplateInfo.defaultClassTemplates() : defaultClassTemplates,
groovyDocLinks,
configuration.getLanguageLevel(),
docProperties
);
} else if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_6_0_RC2)) {
groovyDocTool = invokeConstructor(findConstructor(groovyDocToolClass, resourceManagerClass, String[].class, String[].class, String[].class, String[].class, List.class, Properties.class),
classpathResourceManager,
sourceDirectories.toArray(new String[0]),
defaultDocTemplates == null ? groovyDocTemplateInfo.defaultDocTemplates() : defaultDocTemplates,
defaultPackageTemplates == null ? groovyDocTemplateInfo.defaultPackageTemplates() : defaultPackageTemplates,
defaultClassTemplates == null ? groovyDocTemplateInfo.defaultClassTemplates() : defaultClassTemplates,
groovyDocLinks,
docProperties
);
} else if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_5_2)) {
groovyDocTool = invokeConstructor(findConstructor(groovyDocToolClass, resourceManagerClass, String.class, String[].class, String[].class, String[].class, List.class),
classpathResourceManager,
sourceDirectories.get(0),
defaultDocTemplates == null ? groovyDocTemplateInfo.defaultDocTemplates() : defaultDocTemplates,
defaultPackageTemplates == null ? groovyDocTemplateInfo.defaultPackageTemplates() : defaultPackageTemplates,
defaultClassTemplates == null ? groovyDocTemplateInfo.defaultClassTemplates() : defaultClassTemplates,
groovyDocLinks
);
if (sourceDirectories.size() > 1) {
log.warn("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support more than one GroovyDoc source directory (must be 1.6-RC-2 or newer). Only using first source directory (" + sourceDirectories.get(0) + ").");
}
} else {
groovyDocTool = invokeConstructor(findConstructor(groovyDocToolClass, resourceManagerClass, String.class, String[].class, String[].class, String[].class),
classpathResourceManager,
sourceDirectories.get(0),
defaultDocTemplates == null ? groovyDocTemplateInfo.defaultDocTemplates() : defaultDocTemplates,
defaultPackageTemplates == null ? groovyDocTemplateInfo.defaultPackageTemplates() : defaultPackageTemplates,
defaultClassTemplates == null ? groovyDocTemplateInfo.defaultClassTemplates() : defaultClassTemplates
);
if (sourceDirectories.size() > 1) {
log.warn("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support more than one GroovyDoc source directory (must be 1.6-RC-2 or newer). Only using first source directory (" + sourceDirectories.get(0) + ").");
}
}
return groovyDocTool;
}
protected List<String> setupGroovyDocSources(final FileSet[] sourceDirectories, final FileSetManager fileSetManager) {
List<String> javaSources = new ArrayList<>();
List<String> groovySources = new ArrayList<>();
List<String> possibleGroovyStubs = new ArrayList<>();
for (FileSet sourceDirectory : sourceDirectories) {
String[] sources = fileSetManager.getIncludedFiles(sourceDirectory);
for (String source : sources) {
if (source.endsWith(".java") && !javaSources.contains(source)) {
javaSources.add(source);
} else if (!groovySources.contains(source)) {
groovySources.add(source);
possibleGroovyStubs.add(source.replaceFirst("\\." + FileUtils.getFileExtension(source), ".java"));
}
}
}
javaSources.removeAll(possibleGroovyStubs);
List<String> groovyDocSources = new ArrayList<>();
groovyDocSources.addAll(javaSources);
groovyDocSources.addAll(groovySources);
return groovyDocSources;
}
protected void performGroovyDocGeneration(final File outputDirectory, final Class<?> groovyDocToolClass, final Class<?> outputToolClass, final Object fileOutputTool, final List<String> groovyDocSources, final Object groovyDocTool) throws InvocationTargetException, IllegalAccessException {
log.debug("Adding sources to generate GroovyDoc for:");
if (log.isDebugEnabled()) {
for (String groovyDocSource : groovyDocSources) {
log.debug(" " + groovyDocSource);
}
}
if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_6_0_RC2)) {
invokeMethod(findMethod(groovyDocToolClass, "add", List.class), groovyDocTool, groovyDocSources);
} else {
Method add = findMethod(groovyDocToolClass, "add", String.class);
for (String groovyDocSource : groovyDocSources) {
invokeMethod(add, groovyDocTool, groovyDocSource);
}
}
invokeMethod(findMethod(groovyDocToolClass, "renderToOutput", outputToolClass, String.class), groovyDocTool, fileOutputTool, outputDirectory.getAbsolutePath());
}
public void generateStubs(GroovyStubConfiguration configuration) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
if (configuration.getStubSources() == null || configuration.getStubSources().isEmpty()) {
log.info("No sources specified for stub generation. Skipping.");
return;
}
if (!supportsStubGeneration()) {
log.error("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support stub generation. The minimum version of Groovy required is 1.8.2. Skipping stub generation.");
return;
}
if (!configuration.isSkipBytecodeCheck()) {
verifyGroovyVersionSupportsTargetBytecode(configuration.getTargetBytecode());
}
// get classes we need with reflection
Class<?> compilerConfigurationClass = classWrangler.getClass("org.codehaus.groovy.control.CompilerConfiguration");
Class<?> javaStubCompilationUnitClass = classWrangler.getClass("org.codehaus.groovy.tools.javac.JavaStubCompilationUnit");
Class<?> groovyClassLoaderClass = classWrangler.getClass("groovy.lang.GroovyClassLoader");
// setup stub generation options
Object compilerConfiguration = setupStubCompilerConfiguration(configuration, compilerConfigurationClass);
Object groovyClassLoader = invokeConstructor(findConstructor(groovyClassLoaderClass, ClassLoader.class, compilerConfigurationClass), classWrangler.getClassLoader(), compilerConfiguration);
Object javaStubCompilationUnit = invokeConstructor(findConstructor(javaStubCompilationUnitClass, compilerConfigurationClass, groovyClassLoaderClass, File.class), compilerConfiguration, groovyClassLoader, configuration.getOutputDirectory());
// add Groovy sources
addGroovySources(configuration.getStubSources(), compilerConfigurationClass, javaStubCompilationUnitClass, compilerConfiguration, javaStubCompilationUnit);
// generate the stubs
invokeMethod(findMethod(javaStubCompilationUnitClass, "compile"), javaStubCompilationUnit);
}
protected Object setupStubCompilerConfiguration(final GroovyStubConfiguration configuration, final Class<?> compilerConfigurationClass) throws InvocationTargetException, IllegalAccessException, InstantiationException {
Object compilerConfiguration = invokeConstructor(findConstructor(compilerConfigurationClass));
invokeMethod(findMethod(compilerConfigurationClass, "setDebug", boolean.class), compilerConfiguration, configuration.isDebug());
invokeMethod(findMethod(compilerConfigurationClass, "setVerbose", boolean.class), compilerConfiguration, configuration.isVerbose());
invokeMethod(findMethod(compilerConfigurationClass, "setWarningLevel", int.class), compilerConfiguration, configuration.getWarningLevel());
invokeMethod(findMethod(compilerConfigurationClass, "setTolerance", int.class), compilerConfiguration, configuration.getTolerance());
invokeMethod(findMethod(compilerConfigurationClass, "setTargetBytecode", String.class), compilerConfiguration, translateJavacTargetToTargetBytecode(configuration.getTargetBytecode()));
if (configuration.getSourceEncoding() != null) {
invokeMethod(findMethod(compilerConfigurationClass, "setSourceEncoding", String.class), compilerConfiguration, configuration.getSourceEncoding());
}
Map<String, Object> options = new HashMap<>();
options.put("stubDir", configuration.getOutputDirectory());
options.put("keepStubs", Boolean.TRUE);
invokeMethod(findMethod(compilerConfigurationClass, "setJointCompilationOptions", Map.class), compilerConfiguration, options);
return compilerConfiguration;
}
protected void addGroovySources(final Set<File> stubSources, final Class<?> compilerConfigurationClass, final Class<?> javaStubCompilationUnitClass, final Object compilerConfiguration, final Object javaStubCompilationUnit) throws InvocationTargetException, IllegalAccessException {
// Since we don't have FileUtils here easily without dependencies, we can just use simple extension check.
// Wait, FileUtils is in util/FileUtils.java in this project.
// I should check if I can assume it is available. It is in the same package or similar.
// It's org.codehaus.gmavenplus.util.FileUtils.
// I will use it.
Set<String> scriptExtensions = new java.util.HashSet<>();
for (File stubSource : stubSources) {
scriptExtensions.add(FileUtils.getFileExtension(stubSource));
}
log.debug("Detected Groovy file extensions: " + scriptExtensions + ".");
if (supportsSettingExtensions()) {
invokeMethod(findMethod(compilerConfigurationClass, "setScriptExtensions", Set.class), compilerConfiguration, scriptExtensions);
}
log.debug("Adding Groovy to generate stubs for:");
Method addSource = findMethod(javaStubCompilationUnitClass, "addSource", File.class);
for (File stubSource : stubSources) {
log.debug(" " + stubSource);
if (supportsSettingExtensions()) {
invokeMethod(addSource, javaStubCompilationUnit, stubSource);
} else {
// DotGroovyFile is in groovyworkarounds package.
// org.codehaus.gmavenplus.groovyworkarounds.DotGroovyFile
// I need to import it or use reflection? It is compiled code in this project, so I can import it.
org.codehaus.gmavenplus.groovyworkarounds.DotGroovyFile dotGroovyFile = new org.codehaus.gmavenplus.groovyworkarounds.DotGroovyFile(stubSource).setScriptExtensions(scriptExtensions);
invokeMethod(addSource, javaStubCompilationUnit, dotGroovyFile);
}
}
}
protected boolean supportsStubGeneration() {
return ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_8_2);
}
protected boolean supportsSettingExtensions() {
return ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_8_3) && (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_1_9_0_BETA1) || ClassWrangler.groovyNewerThan(classWrangler.getGroovyVersion(), GROOVY_1_9_0_BETA3));
}
protected Object setupCompilationUnit(final Set<File> sources, final Class<?> compilerConfigurationClass, final Class<?> compilationUnitClass, final Class<?> groovyClassLoaderClass, final Object compilerConfiguration, final Object groovyClassLoader, final Object transformLoader) throws InvocationTargetException, IllegalAccessException, InstantiationException {
Object compilationUnit;
if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_6_0)) {
compilationUnit = invokeConstructor(findConstructor(compilationUnitClass, compilerConfigurationClass, CodeSource.class, groovyClassLoaderClass, groovyClassLoaderClass), compilerConfiguration, null, groovyClassLoader, transformLoader);
} else {
compilationUnit = invokeConstructor(findConstructor(compilationUnitClass, compilerConfigurationClass, CodeSource.class, groovyClassLoaderClass), compilerConfiguration, null, groovyClassLoader);
}
log.debug("Adding Groovy to compile:");
Method addSourceMethod = findMethod(compilationUnitClass, "addSource", File.class);
for (File source : sources) {
log.debug(" " + source);
invokeMethod(addSourceMethod, compilationUnit, source);
}
return compilationUnit;
}
@SuppressWarnings({"rawtypes", "unchecked"})
protected Object setupCompilerConfiguration(final GroovyCompileConfiguration configuration, final Class<?> compilerConfigurationClass) throws InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException {
Object compilerConfiguration = invokeConstructor(findConstructor(compilerConfigurationClass));
if (configuration.getConfigScript() != null) {
if (!configuration.getConfigScript().exists()) {
log.warn("Configuration script file (" + configuration.getConfigScript().getAbsolutePath() + ") doesn't exist. Ignoring configScript parameter.");
} else if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_1_0_BETA1)) {
log.warn("Requested to use configScript, but your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support it (must be " + GROOVY_2_1_0_BETA1 + " or newer). Ignoring configScript parameter.");
} else {
Class<?> bindingClass = classWrangler.getClass("groovy.lang.Binding");
Class<?> importCustomizerClass = classWrangler.getClass("org.codehaus.groovy.control.customizers.ImportCustomizer");
Class<?> groovyShellClass = classWrangler.getClass("groovy.lang.GroovyShell");
Object binding = invokeConstructor(findConstructor(bindingClass));
invokeMethod(findMethod(bindingClass, "setVariable", String.class, Object.class), binding, "configuration", compilerConfiguration);
Object shellCompilerConfiguration = invokeConstructor(findConstructor(compilerConfigurationClass));
Object importCustomizer = invokeConstructor(findConstructor(importCustomizerClass));
invokeMethod(findMethod(importCustomizerClass, "addStaticStar", String.class), importCustomizer, "org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder");
List compilationCustomizers = (List) invokeMethod(findMethod(compilerConfigurationClass, "getCompilationCustomizers"), shellCompilerConfiguration);
compilationCustomizers.add(importCustomizer);
Object shell = invokeConstructor(findConstructor(groovyShellClass, ClassLoader.class, bindingClass, compilerConfigurationClass), classWrangler.getClassLoader(), binding, shellCompilerConfiguration);
log.debug("Using configuration script " + configuration.getConfigScript() + " for compilation.");
invokeMethod(findMethod(groovyShellClass, "evaluate", File.class), shell, configuration.getConfigScript());
}
}
invokeMethod(findMethod(compilerConfigurationClass, "setDebug", boolean.class), compilerConfiguration, configuration.isDebug());
invokeMethod(findMethod(compilerConfigurationClass, "setVerbose", boolean.class), compilerConfiguration, configuration.isVerbose());
invokeMethod(findMethod(compilerConfigurationClass, "setWarningLevel", int.class), compilerConfiguration, configuration.getWarningLevel());
invokeMethod(findMethod(compilerConfigurationClass, "setTolerance", int.class), compilerConfiguration, configuration.getTolerance());
invokeMethod(findMethod(compilerConfigurationClass, "setTargetBytecode", String.class), compilerConfiguration, translateJavacTargetToTargetBytecode(configuration.getTargetBytecode()));
if (configuration.isPreviewFeatures()) {
if (isJavaSupportPreviewFeatures()) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_5_7) || (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_2_6_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_0_BETA1))) {
log.warn("Requested to use preview features, but your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support it (must be " + GROOVY_2_5_7 + "/" + GROOVY_3_0_0_BETA1 + " or newer. No 2.6 version is supported. Ignoring previewFeatures parameter.");
} else {
invokeMethod(findMethod(compilerConfigurationClass, "setPreviewFeatures", boolean.class), compilerConfiguration, configuration.isPreviewFeatures());
}
} else {
log.warn("Requested to use to use preview features, but your Java version (" + getJavaVersionString() + ") doesn't support it. Ignoring previewFeatures parameter.");
}
}
if (configuration.getSourceEncoding() != null) {
invokeMethod(findMethod(compilerConfigurationClass, "setSourceEncoding", String.class), compilerConfiguration, configuration.getSourceEncoding());
}
invokeMethod(findMethod(compilerConfigurationClass, "setTargetDirectory", String.class), compilerConfiguration, configuration.getCompileOutputDirectory().getAbsolutePath());
if (configuration.isInvokeDynamic() || ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_4_0_0_ALPHA1)) {
if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_2_0_0_BETA3)) {
if (classWrangler.isGroovyIndy()) {
if (isJavaSupportIndy()) {
Map<String, Boolean> optimizationOptions = (Map<String, Boolean>) invokeMethod(findMethod(compilerConfigurationClass, "getOptimizationOptions"), compilerConfiguration);
optimizationOptions.put("indy", true);
optimizationOptions.put("int", false);
log.info("invokedynamic enabled.");
} else {
log.warn("Requested to use to use invokedynamic, but your Java version (" + getJavaVersionString() + ") doesn't support it. Ignoring invokeDynamic parameter.");
}
} else {
log.warn("Requested to use invokedynamic, but your Groovy version doesn't support it (must use have indy classifier). Ignoring invokeDynamic parameter.");
}
} else {
log.warn("Requested to use invokeDynamic, but your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support it (must be " + GROOVY_2_0_0_BETA3 + " or newer). Ignoring invokeDynamic parameter.");
}
}
if (configuration.isParameters()) {
if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_2_5_0_ALPHA1)) {
if (isJavaSupportParameters()) {
invokeMethod(findMethod(compilerConfigurationClass, "setParameters", boolean.class), compilerConfiguration, configuration.isParameters());
} else {
log.warn("Requested to use to use parameters, but your Java version (" + getJavaVersionString() + ") doesn't support it. Ignoring parameters parameter.");
}
} else {
log.warn("Requested to use parameters, but your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support it (must be " + GROOVY_2_5_0_ALPHA1 + " or newer). Ignoring parameters parameter.");
}
}
if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_3_0_5)) {
if ((configuration.getParallelParsing() == null && ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_4_0_0_ALPHA1)) || (configuration.getParallelParsing() != null && configuration.getParallelParsing())) {
Map<String, Boolean> optimizationOptions = (Map<String, Boolean>) invokeMethod(findMethod(compilerConfigurationClass, "getOptimizationOptions"), compilerConfiguration);
optimizationOptions.put("parallelParse", true);
log.info("Parallel parsing enabled.");
} else {
log.info("Parallel parsing disabled.");
}
}
return compilerConfiguration;
}
protected void verifyGroovyVersionSupportsTargetBytecode(String targetBytecode) {
if ("1.5".equals(targetBytecode) || "5".equals(targetBytecode) || "1.6".equals(targetBytecode) || "6".equals(targetBytecode) || "1.7".equals(targetBytecode) || "7".equals(targetBytecode) || "1.8".equals(targetBytecode) || "8".equals(targetBytecode) || "1.9".equals(targetBytecode) || "9".equals(targetBytecode) || "10".equals(targetBytecode)) {
if (ClassWrangler.groovyNewerThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA1)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " isn't accepted by Groovy " + GROOVY_5_0_0_ALPHA1 + " or newer.");
}
}
if ("25".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_27)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_27 + " or newer.");
}
if (ClassWrangler.groovyNewerThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA13)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_5_0_0_ALPHA13 + " or newer.");
}
} else if ("24".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_24)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_24 + " or newer.");
}
if (ClassWrangler.groovyNewerThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA11)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_5_0_0_ALPHA11 + " or newer.");
}
} else if ("23".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_21)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_21 + " or newer.");
}
if (ClassWrangler.groovyNewerThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA8)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_5_0_0_ALPHA8 + " or newer.");
}
} else if ("22".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_16)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_16 + " or newer.");
}
if (ClassWrangler.groovyNewerThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA3)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_5_0_0_ALPHA3 + " or newer.");
}
} else if ("21".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_11)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_11 + " or newer.");
}
} else if ("20".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_6)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_6 + " or newer.");
}
} else if ("19".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_2)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_2 + " or newer.");
}
} else if ("18".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_0_BETA1)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_0_BETA1 + " or newer.");
}
} else if ("17".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_8) || (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_4_0_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_0_ALPHA3))) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_8 + "/" + GROOVY_4_0_0_ALPHA3 + " or newer.");
}
} else if ("16".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_6)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_6 + " or newer.");
}
} else if ("15".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_3)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_3 + " or newer.");
}
} else if ("14".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_0_BETA2)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_0_BETA2 + " or newer.");
}
} else if ("13".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_5_7) || (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_2_6_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_0_BETA1))) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_5_7 + "/" + GROOVY_3_0_0_BETA1 + " or newer. No 2.6 version is supported.");
}
} else if ("12".equals(targetBytecode) || "11".equals(targetBytecode) || "10".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_5_3) || (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_2_6_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_0_ALPHA4))) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_5_3 + "/" + GROOVY_3_0_0_ALPHA4 + " or newer. No 2.6 version is supported.");
}
} else if ("9".equals(targetBytecode) || "1.9".equals(targetBytecode)) {
if (!classWrangler.isGroovyIndy() && (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_5_3)
|| (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_2_6_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_6_0_ALPHA4))
|| (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_3_0_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_0_ALPHA2)))) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_5_3 + "/" + GROOVY_2_6_0_ALPHA4 + "/" + GROOVY_3_0_0_ALPHA2 + " or newer.");
} else if (classWrangler.isGroovyIndy() && (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_5_3) || (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_2_6_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_0_ALPHA4)))) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_5_3 + "/" + GROOVY_3_0_0_ALPHA4 + " or newer. No 2.6 version is supported.");
}
} else if ("8".equals(targetBytecode) || "1.8".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_3_3)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_3_3 + " or newer.");
}
} else if ("7".equals(targetBytecode) || "1.7".equals(targetBytecode) || "6".equals(targetBytecode) || "1.6".equals(targetBytecode)) {
if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_1_3)) {
throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_1_3 + " or newer.");
}
} else if (!"5".equals(targetBytecode) && !"1.5".equals(targetBytecode) && !"4".equals(targetBytecode) && !"1.4".equals(targetBytecode)) {
throw new IllegalArgumentException("Unrecognized target bytecode: '" + targetBytecode + "'. This check can be skipped with 'skipBytecodeCheck', but this may result in a different target bytecode being used.");
}
}
public static String translateJavacTargetToTargetBytecode(String targetBytecode) {
Map<String, String> javacTargetToTargetBytecode = new HashMap<>();
javacTargetToTargetBytecode.put("5", "1.5");
javacTargetToTargetBytecode.put("6", "1.6");
javacTargetToTargetBytecode.put("7", "1.7");
javacTargetToTargetBytecode.put("8", "1.8");
javacTargetToTargetBytecode.put("1.9", "9");
return javacTargetToTargetBytecode.getOrDefault(targetBytecode, targetBytecode);
}
protected boolean isJavaSupportIndy() {
return getJavaVersion().compareTo(JAVA_1_7, false) >= 0;
}
protected boolean isJavaSupportPreviewFeatures() {
return getJavaVersion().compareTo(JAVA_12, false) >= 0;
}
protected boolean isJavaSupportParameters() {
return getJavaVersion().compareTo(JAVA_1_8, false) >= 0;
}
protected Version getJavaVersion() {
return Version.parseFromString(getJavaVersionString());
}
protected String getJavaVersionString() {
return System.getProperty("java.version");
}
}