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