1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.codehaus.gmavenplus.mojo;
18
19 import org.apache.maven.plugins.annotations.Parameter;
20 import org.codehaus.gmavenplus.model.IncludeClasspath;
21 import org.codehaus.gmavenplus.model.internal.Version;
22
23 import java.io.File;
24 import java.lang.reflect.InvocationTargetException;
25 import java.lang.reflect.Method;
26 import java.net.MalformedURLException;
27 import java.security.CodeSource;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32
33 import static org.codehaus.gmavenplus.util.ReflectionUtils.findConstructor;
34 import static org.codehaus.gmavenplus.util.ReflectionUtils.findMethod;
35 import static org.codehaus.gmavenplus.util.ReflectionUtils.invokeConstructor;
36 import static org.codehaus.gmavenplus.util.ReflectionUtils.invokeMethod;
37
38
39
40
41
42
43
44
45 public abstract class AbstractCompileMojo extends AbstractGroovySourcesMojo {
46
47
48
49
50 protected static final Version GROOVY_5_0_0_ALPHA1 = new Version(5, 0, 0, "alpha-1");
51
52
53
54
55 protected static final Version GROOVY_4_0_21 = new Version(4, 0, 21);
56
57
58
59
60 protected static final Version GROOVY_4_0_16 = new Version(4, 0, 16);
61
62
63
64
65 protected static final Version GROOVY_4_0_11 = new Version(4, 0, 11);
66
67
68
69
70 protected static final Version GROOVY_4_0_6 = new Version(4, 0, 6);
71
72
73
74
75 protected static final Version GROOVY_4_0_2 = new Version(4, 0, 2);
76
77
78
79
80 protected static final Version GROOVY_4_0_0_BETA1 = new Version(4, 0, 0, "beta-1");
81
82
83
84
85 protected static final Version GROOVY_4_0_0_ALPHA3 = new Version(4, 0, 0, "alpha-3");
86
87
88
89
90 protected static final Version GROOVY_4_0_0_ALPHA1 = new Version(4, 0, 0, "alpha-1");
91
92
93
94
95 protected static final Version GROOVY_3_0_8 = new Version(3, 0, 8);
96
97
98
99
100 protected static final Version GROOVY_3_0_6 = new Version(3, 0, 6);
101
102
103
104
105 protected static final Version GROOVY_3_0_5 = new Version(3, 0, 5);
106
107
108
109
110 protected static final Version GROOVY_3_0_3 = new Version(3, 0, 3);
111
112
113
114
115 protected static final Version GROOVY_3_0_0_BETA2 = new Version(3, 0, 0, "beta-2");
116
117
118
119
120 protected static final Version GROOVY_3_0_0_BETA1 = new Version(3, 0, 0, "beta-1");
121
122
123
124
125 protected static final Version GROOVY_3_0_0_ALPHA4 = new Version(3, 0, 0, "alpha-4");
126
127
128
129
130 protected static final Version GROOVY_3_0_0_ALPHA2 = new Version(3, 0, 0, "alpha-2");
131
132
133
134
135 protected static final Version GROOVY_3_0_0_ALPHA1 = new Version(3, 0, 0, "alpha-1");
136
137
138
139
140 protected static final Version GROOVY_2_6_0_ALPHA4 = new Version(2, 6, 0, "alpha-4");
141
142
143
144
145 protected static final Version GROOVY_2_6_0_ALPHA1 = new Version(2, 6, 0, "alpha-1");
146
147
148
149
150 protected static final Version GROOVY_2_5_7 = new Version(2, 5, 7);
151
152
153
154
155 protected static final Version GROOVY_2_5_3 = new Version(2, 5, 3);
156
157
158
159
160 protected static final Version GROOVY_2_5_0_ALPHA1 = new Version(2, 5, 0, "alpha-1");
161
162
163
164
165 protected static final Version GROOVY_2_3_3 = new Version(2, 3, 3);
166
167
168
169
170 protected static final Version GROOVY_2_1_3 = new Version(2, 1, 3);
171
172
173
174
175 protected static final Version GROOVY_2_1_0_BETA1 = new Version(2, 1, 0, "beta-1");
176
177
178
179
180 protected static final Version GROOVY_2_0_0_BETA3 = new Version(2, 0, 0, "beta-3");
181
182
183
184
185 protected static final Version GROOVY_1_6_0 = new Version(1, 6, 0);
186
187
188
189
190 @Parameter(defaultValue = "${project.build.sourceEncoding}")
191 protected String sourceEncoding;
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234 @Parameter(property = "maven.compiler.target", defaultValue = "1.8")
235 protected String targetBytecode;
236
237
238
239
240
241
242 @Parameter(property = "skipBytecodeCheck", defaultValue = "false")
243 protected boolean skipBytecodeCheck;
244
245
246
247
248 @Parameter(defaultValue = "false")
249 protected boolean debug;
250
251
252
253
254 @Parameter(defaultValue = "false")
255 protected boolean verbose;
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270 @Parameter(defaultValue = "1")
271 protected int warningLevel;
272
273
274
275
276 @Parameter(defaultValue = "0")
277 protected int tolerance;
278
279
280
281
282
283 @Parameter(defaultValue = "false")
284 protected boolean invokeDynamic;
285
286
287
288
289
290
291
292 @Parameter
293 protected Boolean parallelParsing = null;
294
295
296
297
298
299 @Parameter
300 protected File configScript;
301
302
303
304
305
306 @Parameter(defaultValue = "false")
307 protected boolean parameters;
308
309
310
311
312
313
314
315
316
317
318
319
320 @Parameter(defaultValue = "PROJECT_ONLY")
321 protected IncludeClasspath includeClasspath;
322
323
324
325
326
327
328
329 @Parameter(defaultValue = "false")
330 protected boolean previewFeatures;
331
332
333
334
335
336
337
338
339
340
341
342
343
344 @SuppressWarnings({"rawtypes"})
345 protected synchronized void doCompile(final Set<File> sources, final List classpath, final File compileOutputDirectory)
346 throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, MalformedURLException {
347 if (sources == null || sources.isEmpty()) {
348 getLog().info("No sources specified for compilation. Skipping.");
349 return;
350 }
351
352 setupClassWrangler(classpath, includeClasspath);
353
354 logPluginClasspath();
355 classWrangler.logGroovyVersion(mojoExecution.getMojoDescriptor().getGoal());
356
357 if (!groovyVersionSupportsAction()) {
358 getLog().error("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support compilation. The minimum version of Groovy required is " + minGroovyVersion + ". Skipping compiling.");
359 return;
360 }
361
362 if (!skipBytecodeCheck) {
363 verifyGroovyVersionSupportsTargetBytecode();
364 }
365
366
367 Class<?> compilerConfigurationClass = classWrangler.getClass("org.codehaus.groovy.control.CompilerConfiguration");
368 Class<?> compilationUnitClass = classWrangler.getClass("org.codehaus.groovy.control.CompilationUnit");
369 Class<?> groovyClassLoaderClass = classWrangler.getClass("groovy.lang.GroovyClassLoader");
370
371
372 Object compilerConfiguration = setupCompilerConfiguration(compileOutputDirectory, compilerConfigurationClass);
373 Object groovyClassLoader = invokeConstructor(findConstructor(groovyClassLoaderClass, ClassLoader.class, compilerConfigurationClass), classWrangler.getClassLoader(), compilerConfiguration);
374 Object transformLoader = invokeConstructor(findConstructor(groovyClassLoaderClass, ClassLoader.class), classWrangler.getClassLoader());
375
376
377 Object compilationUnit = setupCompilationUnit(sources, compilerConfigurationClass, compilationUnitClass, groovyClassLoaderClass, compilerConfiguration, groovyClassLoader, transformLoader);
378
379
380 invokeMethod(findMethod(compilationUnitClass, "compile"), compilationUnit);
381
382
383 List classes = (List) invokeMethod(findMethod(compilationUnitClass, "getClasses"), compilationUnit);
384 getLog().info("Compiled " + classes.size() + " file" + (classes.size() != 1 ? "s" : "") + ".");
385 }
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402 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 {
403 Object compilationUnit;
404 if (groovyAtLeast(GROOVY_1_6_0)) {
405 compilationUnit = invokeConstructor(findConstructor(compilationUnitClass, compilerConfigurationClass, CodeSource.class, groovyClassLoaderClass, groovyClassLoaderClass), compilerConfiguration, null, groovyClassLoader, transformLoader);
406 } else {
407 compilationUnit = invokeConstructor(findConstructor(compilationUnitClass, compilerConfigurationClass, CodeSource.class, groovyClassLoaderClass), compilerConfiguration, null, groovyClassLoader);
408 }
409 getLog().debug("Adding Groovy to compile:");
410 Method addSourceMethod = findMethod(compilationUnitClass, "addSource", File.class);
411 for (File source : sources) {
412 getLog().debug(" " + source);
413 invokeMethod(addSourceMethod, compilationUnit, source);
414 }
415
416 return compilationUnit;
417 }
418
419
420
421
422
423
424
425
426
427
428
429
430 @SuppressWarnings({"rawtypes", "unchecked"})
431 protected Object setupCompilerConfiguration(final File compileOutputDirectory, final Class<?> compilerConfigurationClass) throws InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException {
432 Object compilerConfiguration = invokeConstructor(findConstructor(compilerConfigurationClass));
433 if (configScript != null) {
434 if (!configScript.exists()) {
435 getLog().warn("Configuration script file (" + configScript.getAbsolutePath() + ") doesn't exist. Ignoring configScript parameter.");
436 } else if (groovyOlderThan(GROOVY_2_1_0_BETA1)) {
437 getLog().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.");
438 } else {
439 Class<?> bindingClass = classWrangler.getClass("groovy.lang.Binding");
440 Class<?> importCustomizerClass = classWrangler.getClass("org.codehaus.groovy.control.customizers.ImportCustomizer");
441 Class<?> groovyShellClass = classWrangler.getClass("groovy.lang.GroovyShell");
442
443 Object binding = invokeConstructor(findConstructor(bindingClass));
444 invokeMethod(findMethod(bindingClass, "setVariable", String.class, Object.class), binding, "configuration", compilerConfiguration);
445 Object shellCompilerConfiguration = invokeConstructor(findConstructor(compilerConfigurationClass));
446 Object importCustomizer = invokeConstructor(findConstructor(importCustomizerClass));
447 invokeMethod(findMethod(importCustomizerClass, "addStaticStar", String.class), importCustomizer, "org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder");
448 List compilationCustomizers = (List) invokeMethod(findMethod(compilerConfigurationClass, "getCompilationCustomizers"), shellCompilerConfiguration);
449 compilationCustomizers.add(importCustomizer);
450 Object shell = invokeConstructor(findConstructor(groovyShellClass, ClassLoader.class, bindingClass, compilerConfigurationClass), classWrangler.getClassLoader(), binding, shellCompilerConfiguration);
451 getLog().debug("Using configuration script " + configScript + " for compilation.");
452 invokeMethod(findMethod(groovyShellClass, "evaluate", File.class), shell, configScript);
453 }
454 }
455 invokeMethod(findMethod(compilerConfigurationClass, "setDebug", boolean.class), compilerConfiguration, debug);
456 invokeMethod(findMethod(compilerConfigurationClass, "setVerbose", boolean.class), compilerConfiguration, verbose);
457 invokeMethod(findMethod(compilerConfigurationClass, "setWarningLevel", int.class), compilerConfiguration, warningLevel);
458 invokeMethod(findMethod(compilerConfigurationClass, "setTolerance", int.class), compilerConfiguration, tolerance);
459 invokeMethod(findMethod(compilerConfigurationClass, "setTargetBytecode", String.class), compilerConfiguration, translateJavacTargetToTargetBytecode(targetBytecode));
460 if (previewFeatures) {
461 if (isJavaSupportPreviewFeatures()) {
462 if (groovyOlderThan(GROOVY_2_5_7) || (groovyAtLeast(GROOVY_2_6_0_ALPHA1) && groovyOlderThan(GROOVY_3_0_0_BETA1))) {
463 getLog().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.");
464 } else {
465 invokeMethod(findMethod(compilerConfigurationClass, "setPreviewFeatures", boolean.class), compilerConfiguration, previewFeatures);
466 }
467 } else {
468 getLog().warn("Requested to use to use preview features, but your Java version (" + getJavaVersionString() + ") doesn't support it. Ignoring previewFeatures parameter.");
469 }
470 }
471 if (sourceEncoding != null) {
472 invokeMethod(findMethod(compilerConfigurationClass, "setSourceEncoding", String.class), compilerConfiguration, sourceEncoding);
473 }
474 invokeMethod(findMethod(compilerConfigurationClass, "setTargetDirectory", String.class), compilerConfiguration, compileOutputDirectory.getAbsolutePath());
475 if (invokeDynamic || groovyAtLeast(GROOVY_4_0_0_ALPHA1)) {
476 if (groovyAtLeast(GROOVY_2_0_0_BETA3)) {
477 if (classWrangler.isGroovyIndy()) {
478 if (isJavaSupportIndy()) {
479 Map<String, Boolean> optimizationOptions = (Map<String, Boolean>) invokeMethod(findMethod(compilerConfigurationClass, "getOptimizationOptions"), compilerConfiguration);
480 optimizationOptions.put("indy", true);
481 optimizationOptions.put("int", false);
482 getLog().info("invokedynamic enabled.");
483 } else {
484 getLog().warn("Requested to use to use invokedynamic, but your Java version (" + getJavaVersionString() + ") doesn't support it. Ignoring invokeDynamic parameter.");
485 }
486 } else {
487 getLog().warn("Requested to use invokedynamic, but your Groovy version doesn't support it (must use have indy classifier). Ignoring invokeDynamic parameter.");
488 }
489 } else {
490 getLog().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.");
491 }
492 }
493 if (parameters) {
494 if (groovyAtLeast(GROOVY_2_5_0_ALPHA1)) {
495 if (isJavaSupportParameters()) {
496 invokeMethod(findMethod(compilerConfigurationClass, "setParameters", boolean.class), compilerConfiguration, parameters);
497 } else {
498 getLog().warn("Requested to use to use parameters, but your Java version (" + getJavaVersionString() + ") doesn't support it. Ignoring parameters parameter.");
499 }
500 } else {
501 getLog().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.");
502 }
503 }
504 if (groovyAtLeast(GROOVY_3_0_5)) {
505 if ((parallelParsing == null && groovyAtLeast(GROOVY_4_0_0_ALPHA1)) || (parallelParsing != null && parallelParsing)) {
506 Map<String, Boolean> optimizationOptions = (Map<String, Boolean>) invokeMethod(findMethod(compilerConfigurationClass, "getOptimizationOptions"), compilerConfiguration);
507 optimizationOptions.put("parallelParse", true);
508 getLog().info("Parallel parsing enabled.");
509 } else {
510 getLog().info("Parallel parsing disabled.");
511 }
512 }
513
514 return compilerConfiguration;
515 }
516
517
518
519
520
521
522 protected void verifyGroovyVersionSupportsTargetBytecode() {
523 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)) {
524 if (groovyNewerThan(GROOVY_5_0_0_ALPHA1)) {
525 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " isn't accepted by Groovy " + GROOVY_5_0_0_ALPHA1 + " or newer.");
526 }
527 }
528
529 if ("23".equals(targetBytecode)) {
530 if (groovyOlderThan(GROOVY_4_0_21)) {
531 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_21 + " or newer.");
532 }
533 } else if ("22".equals(targetBytecode)) {
534 if (groovyOlderThan(GROOVY_4_0_16)) {
535 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_16 + " or newer.");
536 }
537 } else if ("21".equals(targetBytecode)) {
538 if (groovyOlderThan(GROOVY_4_0_11)) {
539 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_11 + " or newer.");
540 }
541 } else if ("20".equals(targetBytecode)) {
542 if (groovyOlderThan(GROOVY_4_0_6)) {
543 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_6 + " or newer.");
544 }
545 } else if ("19".equals(targetBytecode)) {
546 if (groovyOlderThan(GROOVY_4_0_2)) {
547 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_2 + " or newer.");
548 }
549 } else if ("18".equals(targetBytecode)) {
550 if (groovyOlderThan(GROOVY_4_0_0_BETA1)) {
551 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_0_BETA1 + " or newer.");
552 }
553 } else if ("17".equals(targetBytecode)) {
554 if (groovyOlderThan(GROOVY_3_0_8) || (groovyAtLeast(GROOVY_4_0_0_ALPHA1) && groovyOlderThan(GROOVY_4_0_0_ALPHA3))) {
555 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_8 + "/" + GROOVY_4_0_0_ALPHA3 + " or newer.");
556 }
557 } else if ("16".equals(targetBytecode)) {
558 if (groovyOlderThan(GROOVY_3_0_6)) {
559 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_6 + " or newer.");
560 }
561 } else if ("15".equals(targetBytecode)) {
562 if (groovyOlderThan(GROOVY_3_0_3)) {
563 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_3 + " or newer.");
564 }
565 } else if ("14".equals(targetBytecode)) {
566 if (groovyOlderThan(GROOVY_3_0_0_BETA2)) {
567 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_0_BETA2 + " or newer.");
568 }
569 } else if ("13".equals(targetBytecode)) {
570 if (groovyOlderThan(GROOVY_2_5_7) || (groovyAtLeast(GROOVY_2_6_0_ALPHA1) && groovyOlderThan(GROOVY_3_0_0_BETA1))) {
571 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.");
572 }
573 } else if ("12".equals(targetBytecode) || "11".equals(targetBytecode) || "10".equals(targetBytecode)) {
574 if (groovyOlderThan(GROOVY_2_5_3) || (groovyAtLeast(GROOVY_2_6_0_ALPHA1) && groovyOlderThan(GROOVY_3_0_0_ALPHA4))) {
575 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.");
576 }
577 } else if ("9".equals(targetBytecode) || "1.9".equals(targetBytecode)) {
578 if (!isGroovyIndy() && (groovyOlderThan(GROOVY_2_5_3)
579 || (groovyAtLeast(GROOVY_2_6_0_ALPHA1) && groovyOlderThan(GROOVY_2_6_0_ALPHA4))
580 || (groovyAtLeast(GROOVY_3_0_0_ALPHA1) && groovyOlderThan(GROOVY_3_0_0_ALPHA2)))) {
581 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_5_3 + "/" + GROOVY_2_6_0_ALPHA4 + "/" + GROOVY_3_0_0_ALPHA2 + " or newer.");
582 } else if (isGroovyIndy() && (groovyOlderThan(GROOVY_2_5_3) || (groovyAtLeast(GROOVY_2_6_0_ALPHA1) && groovyOlderThan(GROOVY_3_0_0_ALPHA4)))) {
583 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.");
584 }
585 } else if ("8".equals(targetBytecode) || "1.8".equals(targetBytecode)) {
586 if (groovyOlderThan(GROOVY_2_3_3)) {
587 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_3_3 + " or newer.");
588 }
589 } else if ("7".equals(targetBytecode) || "1.7".equals(targetBytecode) || "6".equals(targetBytecode) || "1.6".equals(targetBytecode)) {
590 if (groovyOlderThan(GROOVY_2_1_3)) {
591 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_1_3 + " or newer.");
592 }
593 } else if (!"5".equals(targetBytecode) && !"1.5".equals(targetBytecode) && !"4".equals(targetBytecode) && !"1.4".equals(targetBytecode)) {
594 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.");
595 }
596 }
597
598 protected static String translateJavacTargetToTargetBytecode(String targetBytecode) {
599 Map<String, String> javacTargetToTargetBytecode = new HashMap<>();
600 javacTargetToTargetBytecode.put("5", "1.5");
601 javacTargetToTargetBytecode.put("6", "1.6");
602 javacTargetToTargetBytecode.put("7", "1.7");
603 javacTargetToTargetBytecode.put("8", "1.8");
604 javacTargetToTargetBytecode.put("1.9", "9");
605 return javacTargetToTargetBytecode.getOrDefault(targetBytecode, targetBytecode);
606 }
607
608 }