View Javadoc
1   /*
2    * Copyright (C) 2011 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.codehaus.gmavenplus.mojo;
18  
19  import org.apache.maven.plugins.annotations.Parameter;
20  import org.codehaus.gmavenplus.groovyworkarounds.DotGroovyFile;
21  import org.codehaus.gmavenplus.model.IncludeClasspath;
22  import org.codehaus.gmavenplus.model.internal.Version;
23  import org.codehaus.gmavenplus.util.FileUtils;
24  
25  import java.io.File;
26  import java.lang.reflect.InvocationTargetException;
27  import java.lang.reflect.Method;
28  import java.net.MalformedURLException;
29  import java.util.HashMap;
30  import java.util.HashSet;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Set;
34  
35  import static org.codehaus.gmavenplus.util.ReflectionUtils.findConstructor;
36  import static org.codehaus.gmavenplus.util.ReflectionUtils.findMethod;
37  import static org.codehaus.gmavenplus.util.ReflectionUtils.invokeConstructor;
38  import static org.codehaus.gmavenplus.util.ReflectionUtils.invokeMethod;
39  
40  
41  /**
42   * The base generate stubs mojo, which all generate stubs mojos extend.
43   *
44   * @author Keegan Witt
45   * @since 1.0-beta-1
46   */
47  public abstract class AbstractGenerateStubsMojo extends AbstractGroovyStubSourcesMojo {
48  
49      /**
50       * Groovy 5.0.0-alpha-1 version.
51       */
52      protected static final Version GROOVY_5_0_0_ALPHA1 = new Version(5, 0, 0, "alpha-1");
53  
54      /**
55       * Groovy 4.0.11 version.
56       */
57      protected static final Version GROOVY_4_0_21 = new Version(4, 0, 21);
58  
59      /**
60       * Groovy 4.0.11 version.
61       */
62      protected static final Version GROOVY_4_0_16 = new Version(4, 0, 16);
63  
64      /**
65       * Groovy 4.0.11 version.
66       */
67      protected static final Version GROOVY_4_0_11 = new Version(4, 0, 11);
68  
69      /**
70       * Groovy 4.0.6 version.
71       */
72      protected static final Version GROOVY_4_0_6 = new Version(4, 0, 6);
73  
74      /**
75       * Groovy 4.0.2 version.
76       */
77      protected static final Version GROOVY_4_0_2 = new Version(4, 0, 2);
78  
79      /**
80       * Groovy 4.0.0 beta-1 version.
81       */
82      protected static final Version GROOVY_4_0_0_BETA1 = new Version(4, 0, 0, "beta-1");
83  
84      /**
85       * Groovy 4.0.0 alpha-3 version.
86       */
87      protected static final Version GROOVY_4_0_0_ALPHA3 = new Version(4, 0, 0, "alpha-3");
88  
89      /**
90       * Groovy 4.0.0 alpha-1 version.
91       */
92      protected static final Version GROOVY_4_0_0_ALPHA1 = new Version(4, 0, 0, "alpha-1");
93  
94      /**
95       * Groovy 3.0.8 version.
96       */
97      protected static final Version GROOVY_3_0_8 = new Version(3, 0, 8);
98  
99      /**
100      * Groovy 3.0.6 version.
101      */
102     protected static final Version GROOVY_3_0_6 = new Version(3, 0, 6);
103 
104     /**
105      * Groovy 3.0.3 version.
106      */
107     protected static final Version GROOVY_3_0_3 = new Version(3, 0, 3);
108 
109     /**
110      * Groovy 3.0.0 beta-2 version.
111      */
112     protected static final Version GROOVY_3_0_0_BETA2 = new Version(3, 0, 0, "beta-2");
113 
114     /**
115      * Groovy 3.0.0 beta-1 version.
116      */
117     protected static final Version GROOVY_3_0_0_BETA1 = new Version(3, 0, 0, "beta-1");
118 
119     /**
120      * Groovy 3.0.0 alpha-4 version.
121      */
122     protected static final Version GROOVY_3_0_0_ALPHA4 = new Version(3, 0, 0, "alpha-4");
123 
124     /**
125      * Groovy 3.0.0 alpha-2 version.
126      */
127     protected static final Version GROOVY_3_0_0_ALPHA2 = new Version(3, 0, 0, "alpha-2");
128 
129     /**
130      * Groovy 3.0.0 alpha-1 version.
131      */
132     protected static final Version GROOVY_3_0_0_ALPHA1 = new Version(3, 0, 0, "alpha-1");
133 
134     /**
135      * Groovy 2.6.0 alpha-4 version.
136      */
137     protected static final Version GROOVY_2_6_0_ALPHA4 = new Version(2, 6, 0, "alpha-4");
138 
139     /**
140      * Groovy 2.6.0 alpha-1 version.
141      */
142     protected static final Version GROOVY_2_6_0_ALPHA1 = new Version(2, 6, 0, "alpha-1");
143 
144     /**
145      * Groovy 2.5.7 version.
146      */
147     protected static final Version GROOVY_2_5_7 = new Version(2, 5, 7);
148 
149     /**
150      * Groovy 2.5.3 version.
151      */
152     protected static final Version GROOVY_2_5_3 = new Version(2, 5, 3);
153 
154     /**
155      * Groovy 2.3.3 version.
156      */
157     protected static final Version GROOVY_2_3_3 = new Version(2, 3, 3);
158 
159     /**
160      * Groovy 2.1.3 version.
161      */
162     protected static final Version GROOVY_2_1_3 = new Version(2, 1, 3);
163 
164     /**
165      * Groovy 2.9.0 beta-1 version.
166      */
167     protected static final Version GROOVY_1_9_0_BETA1 = new Version(1, 9, 0, "beta-1");
168 
169     /**
170      * Groovy 1.9.0 beta-3 version.
171      */
172     protected static final Version GROOVY_1_9_0_BETA3 = new Version(1, 9, 0, "beta-3");
173 
174     /**
175      * Groovy 1.8.2 version.
176      */
177     protected static final Version GROOVY_1_8_2 = new Version(1, 8, 2);
178 
179     /**
180      * Groovy 1.8.3 version.
181      */
182     protected static final Version GROOVY_1_8_3 = new Version(1, 8, 3);
183 
184     /**
185      * The encoding of source files.
186      */
187     @Parameter(defaultValue = "${project.build.sourceEncoding}")
188     protected String sourceEncoding;
189 
190     /**
191      * The Groovy compiler bytecode compatibility. One of
192      * <ul>
193      *   <li>1.4 (or 4)</li>
194      *   <li>1.5 (or 5)</li>
195      *   <li>1.6 (or 6)</li>
196      *   <li>1.7 (or 7)</li>
197      *   <li>1.8 (or 8)</li>
198      *   <li>9 (or 1.9)</li>
199      *   <li>10</li>
200      *   <li>11</li>
201      *   <li>12</li>
202      *   <li>13</li>
203      *   <li>14</li>
204      *   <li>15</li>
205      *   <li>16</li>
206      *   <li>17</li>
207      *   <li>18</li>
208      *   <li>19</li>
209      *   <li>20</li>
210      *   <li>21</li>
211      *   <li>22</li>
212      *   <li>23</li>
213      * </ul>
214      * Using 1.6 (or 6) or 1.7 (or 7) requires Groovy &gt;= 2.1.3.
215      * Using 1.8 (or 8) requires Groovy &gt;= 2.3.3.
216      * Using 9 (or 1.9) requires Groovy &gt;= 2.5.3, or Groovy &gt;= 2.6.0 alpha 4, or Groovy &gt;= 3.0.0 alpha 2.
217      * Using 9 (or 1.9) with invokedynamic requires Groovy &gt;= 2.5.3, or Groovy &gt;= 3.0.0 alpha 2, but not any 2.6 versions.
218      * Using 10, 11, or 12 requires Groovy &gt;= 2.5.3, or Groovy &gt;= 3.0.0 alpha 4, but not any 2.6 versions.
219      * Using 13 requires Groovy &gt;= 2.5.7, or Groovy &gt;= 3.0.0-beta-1, but not any 2.6 versions.
220      * Using 14 requires Groovy &gt;= 3.0.0 beta-2.
221      * Using 15 requires Groovy &gt;= 3.0.3.
222      * Using 16 requires Groovy &gt;= 3.0.6.
223      * Using 17 requires Groovy &gt;= 3.0.8 or Groovy &gt; 4.0.0-alpha-3.
224      * Using 18 requires Groovy &gt; 4.0.0-beta-1.
225      * Using 19 requires Groovy &gt; 4.0.2.
226      * Using 20 requires Groovy &gt; 4.0.6.
227      * Using 21 requires Groovy &gt; 4.0.11.
228      * Using 22 requires Groovy &gt; 4.0.16.
229      * Using 23 requires Groovy &gt; 4.0.21.
230      *
231      * @since 1.0-beta-3
232      */
233     @Parameter(property = "maven.compiler.target", defaultValue = "1.8")
234     protected String targetBytecode;
235 
236     /**
237      * Whether to check that the version of Groovy used is able to use the requested <code>targetBytecode</code>.
238      *
239      * @since 1.9.0
240      */
241     @Parameter(property = "skipBytecodeCheck", defaultValue = "false")
242     protected boolean skipBytecodeCheck;
243 
244     /**
245      * Whether Groovy compiler should be set to debug.
246      */
247     @Parameter(defaultValue = "false")
248     protected boolean debug;
249 
250     /**
251      * Whether Groovy compiler should be set to verbose.
252      */
253     @Parameter(defaultValue = "false")
254     protected boolean verbose;
255 
256     /**
257      * Groovy compiler warning level. Should be one of:
258      * <dl>
259      *   <dt>0</dt>
260      *     <dd>None</dd>
261      *   <dt>1</dt>
262      *     <dd>Likely Errors</dd>
263      *   <dt>2</dt>
264      *     <dd>Possible Errors</dd>
265      *   <dt>3</dt>
266      *     <dd>Paranoia</dd>
267      * </dl>
268      */
269     @Parameter(defaultValue = "1")
270     protected int warningLevel;
271 
272     /**
273      * Groovy compiler error tolerance (the number of non-fatal errors (per unit) that should be tolerated before compilation is aborted).
274      */
275     @Parameter(defaultValue = "0")
276     protected int tolerance;
277 
278     /**
279      * What classpath to include. One of
280      * <ul>
281      *   <li>PROJECT_ONLY</li>
282      *   <li>PROJECT_AND_PLUGIN</li>
283      *   <li>PLUGIN_ONLY</li>
284      * </ul>
285      * Uses the same scope as the required dependency resolution of this mojo. Use only if you know what you're doing.
286      *
287      * @since 1.8.0
288      */
289     @Parameter(defaultValue = "PROJECT_ONLY")
290     protected IncludeClasspath includeClasspath;
291 
292     /**
293      * Performs the stub generation on the specified source files.
294      *
295      * @param stubSources     the sources to perform stub generation on
296      * @param classpath       The classpath to use for compilation
297      * @param outputDirectory the directory to write the stub files to
298      * @throws ClassNotFoundException    when a class needed for stub generation cannot be found
299      * @throws InstantiationException    when a class needed for stub generation cannot be instantiated
300      * @throws IllegalAccessException    when a method needed for stub generation cannot be accessed
301      * @throws InvocationTargetException when a reflection invocation needed for stub generation cannot be completed
302      * @throws MalformedURLException     when a classpath element provides a malformed URL
303      */
304     protected synchronized void doStubGeneration(final Set<File> stubSources, final List<?> classpath, final File outputDirectory) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException, MalformedURLException {
305         if (stubSources == null || stubSources.isEmpty()) {
306             getLog().info("No sources specified for stub generation. Skipping.");
307             return;
308         }
309 
310         setupClassWrangler(classpath, includeClasspath);
311 
312         logPluginClasspath();
313         classWrangler.logGroovyVersion(mojoExecution.getMojoDescriptor().getGoal());
314 
315         if (!groovyVersionSupportsAction()) {
316             getLog().error("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support stub generation. The minimum version of Groovy required is " + minGroovyVersion + ". Skipping stub generation.");
317             return;
318         }
319 
320         if (!skipBytecodeCheck) {
321             verifyGroovyVersionSupportsTargetBytecode();
322         }
323 
324         // get classes we need with reflection
325         Class<?> compilerConfigurationClass = classWrangler.getClass("org.codehaus.groovy.control.CompilerConfiguration");
326         Class<?> javaStubCompilationUnitClass = classWrangler.getClass("org.codehaus.groovy.tools.javac.JavaStubCompilationUnit");
327         Class<?> groovyClassLoaderClass = classWrangler.getClass("groovy.lang.GroovyClassLoader");
328 
329         // setup stub generation options
330         Object compilerConfiguration = setupCompilerConfiguration(outputDirectory, compilerConfigurationClass);
331         Object groovyClassLoader = invokeConstructor(findConstructor(groovyClassLoaderClass, ClassLoader.class, compilerConfigurationClass), classWrangler.getClassLoader(), compilerConfiguration);
332         Object javaStubCompilationUnit = invokeConstructor(findConstructor(javaStubCompilationUnitClass, compilerConfigurationClass, groovyClassLoaderClass, File.class), compilerConfiguration, groovyClassLoader, outputDirectory);
333 
334         // add Groovy sources
335         addGroovySources(stubSources, compilerConfigurationClass, javaStubCompilationUnitClass, compilerConfiguration, javaStubCompilationUnit);
336 
337         // generate the stubs
338         invokeMethod(findMethod(javaStubCompilationUnitClass, "compile"), javaStubCompilationUnit);
339     }
340 
341     /**
342      * Sets up the CompilerConfiguration to use for stub generation.
343      *
344      * @param outputDirectory            the directory to write the stub files to
345      * @param compilerConfigurationClass the CompilerConfiguration class
346      * @return the CompilerConfiguration to use for stub generation
347      * @throws InstantiationException    when a class needed for stub generation cannot be instantiated
348      * @throws IllegalAccessException    when a method needed for stub generation cannot be accessed
349      * @throws InvocationTargetException when a reflection invocation needed for stub generation cannot be completed
350      */
351     protected Object setupCompilerConfiguration(final File outputDirectory, final Class<?> compilerConfigurationClass) throws InvocationTargetException, IllegalAccessException, InstantiationException {
352         Object compilerConfiguration = invokeConstructor(findConstructor(compilerConfigurationClass));
353         invokeMethod(findMethod(compilerConfigurationClass, "setDebug", boolean.class), compilerConfiguration, debug);
354         invokeMethod(findMethod(compilerConfigurationClass, "setVerbose", boolean.class), compilerConfiguration, verbose);
355         invokeMethod(findMethod(compilerConfigurationClass, "setWarningLevel", int.class), compilerConfiguration, warningLevel);
356         invokeMethod(findMethod(compilerConfigurationClass, "setTolerance", int.class), compilerConfiguration, tolerance);
357         invokeMethod(findMethod(compilerConfigurationClass, "setTargetBytecode", String.class), compilerConfiguration, translateJavacTargetToTargetBytecode(targetBytecode));
358         if (sourceEncoding != null) {
359             invokeMethod(findMethod(compilerConfigurationClass, "setSourceEncoding", String.class), compilerConfiguration, sourceEncoding);
360         }
361         Map<String, Object> options = new HashMap<>();
362         options.put("stubDir", outputDirectory);
363         options.put("keepStubs", Boolean.TRUE);
364         invokeMethod(findMethod(compilerConfigurationClass, "setJointCompilationOptions", Map.class), compilerConfiguration, options);
365 
366         return compilerConfiguration;
367     }
368 
369     /**
370      * Adds the Groovy sources to the CompilationUnit.
371      *
372      * @param stubSources                  the sources to perform stub generation on
373      * @param compilerConfigurationClass   the CompilerConfiguration class
374      * @param javaStubCompilationUnitClass the JavaStubCompilationUnit class
375      * @param compilerConfiguration        the CompilerConfiguration to use for stub generation
376      * @param javaStubCompilationUnit      the JavaStubCompilationUnit to use for stub generation
377      * @throws IllegalAccessException    when a method needed for stub generation cannot be accessed
378      * @throws InvocationTargetException when a reflection invocation needed for stub generation cannot be completed
379      */
380     protected void addGroovySources(final Set<File> stubSources, final Class<?> compilerConfigurationClass, final Class<?> javaStubCompilationUnitClass, final Object compilerConfiguration, final Object javaStubCompilationUnit) throws InvocationTargetException, IllegalAccessException {
381         Set<String> scriptExtensions = new HashSet<>();
382         for (File stubSource : stubSources) {
383             scriptExtensions.add(FileUtils.getFileExtension(stubSource));
384         }
385         getLog().debug("Detected Groovy file extensions: " + scriptExtensions + ".");
386         if (supportsSettingExtensions()) {
387             invokeMethod(findMethod(compilerConfigurationClass, "setScriptExtensions", Set.class), compilerConfiguration, scriptExtensions);
388         }
389         getLog().debug("Adding Groovy to generate stubs for:");
390         Method addSource = findMethod(javaStubCompilationUnitClass, "addSource", File.class);
391         for (File stubSource : stubSources) {
392             getLog().debug("    " + stubSource);
393             if (supportsSettingExtensions()) {
394                 invokeMethod(addSource, javaStubCompilationUnit, stubSource);
395             } else {
396                 DotGroovyFile dotGroovyFile = new DotGroovyFile(stubSource).setScriptExtensions(scriptExtensions);
397                 invokeMethod(addSource, javaStubCompilationUnit, dotGroovyFile);
398             }
399         }
400     }
401 
402     /**
403      * Determines whether the version of Groovy supports stub generation.
404      *
405      * @return <code>true</code> if the version of Groovy supports stub generation, <code>false</code> otherwise
406      */
407     protected boolean supportsSettingExtensions() {
408         return groovyAtLeast(GROOVY_1_8_3) && (groovyOlderThan(GROOVY_1_9_0_BETA1) || groovyNewerThan(GROOVY_1_9_0_BETA3));
409     }
410 
411     /**
412      * Logs the stubs that have been generated.
413      *
414      * @param outputDirectory the output directory for the stubs
415      */
416     protected void logGeneratedStubs(File outputDirectory) {
417         Set<File> stubs = getStubs(outputDirectory);
418         getLog().info("Generated " + stubs.size() + " stub" + (stubs.size() != 1 ? "s" : "") + ".");
419     }
420 
421     /**
422      * This is a fix for <a href="http://jira.codehaus.org/browse/MGROOVY-187">...</a>
423      * It modifies the dates of the created stubs to 1/1/1970, ensuring that the Java compiler will not overwrite perfectly
424      * good compiled Groovy just because it has a newer source stub. Basically, this prevents the stubs from causing a
425      * side effect with the Java compiler, but still allows stubs to work with JavaDoc.
426      *
427      * @param stubs the files on which to reset the modified date
428      */
429     protected void resetStubModifiedDates(final Set<File> stubs) {
430         for (File stub : stubs) {
431             boolean success = stub.setLastModified(0L);
432             if (!success) {
433                 getLog().warn("Unable to set modified time on stub " + stub.getAbsolutePath() + ".");
434             }
435         }
436     }
437 
438     /**
439      * Throws an exception if targetBytecode is not supported with this version of Groovy. That is, when Groovy added
440      * the option to org.codehaus.groovy.control.CompilerConfiguration and used it in
441      * org.codehaus.groovy.classgen.asm.WriterController.
442      */
443     protected void verifyGroovyVersionSupportsTargetBytecode() {
444         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)) {
445             if (groovyNewerThan(GROOVY_5_0_0_ALPHA1)) {
446                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " isn't accepted by Groovy " + GROOVY_5_0_0_ALPHA1 + " or newer.");
447             }
448         }
449 
450         if ("23".equals(targetBytecode)) {
451             if (groovyOlderThan(GROOVY_4_0_21)) {
452                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_21 + " or newer.");
453             }
454         } else if ("22".equals(targetBytecode)) {
455             if (groovyOlderThan(GROOVY_4_0_16)) {
456                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_16 + " or newer.");
457             }
458         } else if ("21".equals(targetBytecode)) {
459             if (groovyOlderThan(GROOVY_4_0_11)) {
460                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_11 + " or newer.");
461             }
462         } else if ("20".equals(targetBytecode)) {
463             if (groovyOlderThan(GROOVY_4_0_6)) {
464                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_6 + " or newer.");
465             }
466         } else if ("19".equals(targetBytecode)) {
467             if (groovyOlderThan(GROOVY_4_0_2)) {
468                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_2 + " or newer.");
469             }
470         } else if ("18".equals(targetBytecode)) {
471             if (groovyOlderThan(GROOVY_4_0_0_BETA1)) {
472                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_0_BETA1 + " or newer.");
473             }
474         } else if ("17".equals(targetBytecode)) {
475             if (groovyOlderThan(GROOVY_3_0_8) || (groovyAtLeast(GROOVY_4_0_0_ALPHA1) && groovyOlderThan(GROOVY_4_0_0_ALPHA3))) {
476                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_8 + "/" + GROOVY_4_0_0_ALPHA3 + " or newer.");
477             }
478         } else if ("16".equals(targetBytecode)) {
479             if (groovyOlderThan(GROOVY_3_0_6)) {
480                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_6 + " or newer.");
481             }
482         } else if ("15".equals(targetBytecode)) {
483             if (groovyOlderThan(GROOVY_3_0_3)) {
484                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_3 + " or newer.");
485             }
486         } else if ("14".equals(targetBytecode)) {
487             if (groovyOlderThan(GROOVY_3_0_0_BETA2)) {
488                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_0_BETA2 + " or newer.");
489             }
490         } else if ("13".equals(targetBytecode)) {
491             if (groovyOlderThan(GROOVY_2_5_7) || (groovyAtLeast(GROOVY_2_6_0_ALPHA1) && groovyOlderThan(GROOVY_3_0_0_BETA1))) {
492                 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.");
493             }
494         } else if ("12".equals(targetBytecode) || "11".equals(targetBytecode) || "10".equals(targetBytecode)) {
495             if (groovyOlderThan(GROOVY_2_5_3) || (groovyAtLeast(GROOVY_2_6_0_ALPHA1) && groovyOlderThan(GROOVY_3_0_0_ALPHA4))) {
496                 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.");
497             }
498         } else if ("9".equals(targetBytecode) || "1.9".equals(targetBytecode)) {
499             if (!isGroovyIndy() && (groovyOlderThan(GROOVY_2_5_3)
500                     || (groovyAtLeast(GROOVY_2_6_0_ALPHA1) && groovyOlderThan(GROOVY_2_6_0_ALPHA4))
501                     || (groovyAtLeast(GROOVY_3_0_0_ALPHA1) && groovyOlderThan(GROOVY_3_0_0_ALPHA2)))) {
502                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_5_3 + "/" + GROOVY_2_6_0_ALPHA4 + "/" + GROOVY_3_0_0_ALPHA2 + " or newer.");
503             } else if (isGroovyIndy() && (groovyOlderThan(GROOVY_2_5_3) || (groovyAtLeast(GROOVY_2_6_0_ALPHA1) && groovyOlderThan(GROOVY_3_0_0_ALPHA4)))) {
504                 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.");
505             }
506         } else if ("8".equals(targetBytecode) || "1.8".equals(targetBytecode)) {
507             if (groovyOlderThan(GROOVY_2_3_3)) {
508                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_3_3 + " or newer.");
509             }
510         } else if ("7".equals(targetBytecode) || "1.7".equals(targetBytecode) || "6".equals(targetBytecode) || "1.6".equals(targetBytecode)) {
511             if (groovyOlderThan(GROOVY_2_1_3)) {
512                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_1_3 + " or newer.");
513             }
514         } else if (!"5".equals(targetBytecode) && !"1.5".equals(targetBytecode) && !"4".equals(targetBytecode) && !"1.4".equals(targetBytecode)) {
515             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.");
516         }
517     }
518 
519     protected static String translateJavacTargetToTargetBytecode(String targetBytecode) {
520         Map<String, String> javacTargetToTargetBytecode = new HashMap<>();
521         javacTargetToTargetBytecode.put("5", "1.5");
522         javacTargetToTargetBytecode.put("6", "1.6");
523         javacTargetToTargetBytecode.put("7", "1.7");
524         javacTargetToTargetBytecode.put("8", "1.8");
525         javacTargetToTargetBytecode.put("1.9", "9");
526         return javacTargetToTargetBytecode.getOrDefault(targetBytecode, targetBytecode);
527     }
528 
529 }