1 package org.codehaus.gmavenplus.mojo;
2
3 import org.apache.maven.plugins.annotations.Parameter;
4 import org.codehaus.gmavenplus.model.IncludeClasspath;
5
6 import java.io.File;
7
8 import org.apache.maven.plugins.annotations.Component;
9 import org.apache.maven.toolchain.Toolchain;
10 import org.apache.maven.toolchain.ToolchainManager;
11 import org.codehaus.gmavenplus.model.GroovyCompileConfiguration;
12 import org.codehaus.gmavenplus.util.ForkedGroovyCompiler;
13 import org.codehaus.gmavenplus.util.GroovyCompiler;
14
15 import java.io.*;
16 import java.lang.reflect.InvocationTargetException;
17 import java.net.MalformedURLException;
18 import java.nio.file.Files;
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.Set;
22
23
24
25
26
27
28
29 public abstract class AbstractCompileMojo extends AbstractGroovySourcesMojo {
30
31
32
33
34 @Parameter(defaultValue = "${project.build.sourceEncoding}")
35 protected String sourceEncoding;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 @Parameter(property = "maven.compiler.target", defaultValue = "1.8")
83 protected String targetBytecode;
84
85
86
87
88
89
90 @Parameter(property = "skipBytecodeCheck", defaultValue = "false")
91 protected boolean skipBytecodeCheck;
92
93
94
95
96 @Parameter(defaultValue = "false")
97 protected boolean debug;
98
99
100
101
102 @Parameter(defaultValue = "false")
103 protected boolean verbose;
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 @Parameter(defaultValue = "1")
119 protected int warningLevel;
120
121
122
123
124 @Parameter(defaultValue = "0")
125 protected int tolerance;
126
127
128
129
130
131 @Parameter(defaultValue = "false")
132 protected boolean invokeDynamic;
133
134
135
136
137
138
139
140 @Parameter
141 protected Boolean parallelParsing = null;
142
143
144
145
146
147 @Parameter
148 protected File configScript;
149
150
151
152
153
154 @Parameter(defaultValue = "false")
155 protected boolean parameters;
156
157
158
159
160
161
162
163
164
165
166
167
168 @Parameter(defaultValue = "PROJECT_ONLY")
169 protected IncludeClasspath includeClasspath;
170
171
172
173
174
175
176
177 @Parameter(defaultValue = "false")
178 protected boolean previewFeatures;
179
180
181
182
183 @Component
184 protected ToolchainManager toolchainManager;
185
186
187
188
189 @Parameter(defaultValue = "false")
190 protected boolean fork;
191
192
193
194
195
196
197
198
199
200
201
202
203
204 @SuppressWarnings({"rawtypes"})
205 protected synchronized void doCompile(final Set<File> sources, final List classpath, final File compileOutputDirectory)
206 throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, MalformedURLException {
207 if (sources == null || sources.isEmpty()) {
208 getLog().info("No sources specified for compilation. Skipping.");
209 return;
210 }
211
212 GroovyCompileConfiguration configuration = new GroovyCompileConfiguration(sources, classpath, compileOutputDirectory);
213 configuration.setIncludeClasspath(includeClasspath);
214 configuration.setSkipBytecodeCheck(skipBytecodeCheck);
215 configuration.setDebug(debug);
216 configuration.setVerbose(verbose);
217 configuration.setWarningLevel(warningLevel);
218 configuration.setTolerance(tolerance);
219 configuration.setInvokeDynamic(invokeDynamic);
220 configuration.setParallelParsing(parallelParsing);
221 configuration.setConfigScript(configScript);
222 configuration.setParameters(parameters);
223 configuration.setPreviewFeatures(previewFeatures);
224 configuration.setSourceEncoding(sourceEncoding);
225 configuration.setTargetBytecode(targetBytecode);
226
227 Toolchain toolchain = toolchainManager.getToolchainFromBuildContext("jdk", session);
228 if (toolchain != null) {
229 getLog().info("Toolchain in gmavenplus-plugin: " + toolchain);
230 performForkedCompilation(configuration, toolchain.findTool("java"));
231 } else if (fork) {
232 String javaExecutable = getJavaExecutable();
233 getLog().info("Forking compilation using " + javaExecutable);
234 performForkedCompilation(configuration, javaExecutable);
235 } else {
236 getLog().info("Performing in-process compilation");
237 performInProcessCompilation(configuration, classpath);
238 }
239 }
240
241 protected void performInProcessCompilation(GroovyCompileConfiguration configuration, List<?> classpath) throws MalformedURLException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {
242 setupClassWrangler(classpath, includeClasspath);
243 logPluginClasspath();
244 classWrangler.logGroovyVersion(mojoExecution.getMojoDescriptor().getGoal());
245
246 if (!groovyVersionSupportsAction()) {
247 getLog().error("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support compilation. The minimum version of Groovy required is " + minGroovyVersion + ". Skipping compiling.");
248 return;
249 }
250
251 GroovyCompiler compiler = new GroovyCompiler(classWrangler, getLog());
252 compiler.compile(configuration);
253 }
254
255 protected void performForkedCompilation(GroovyCompileConfiguration configuration, String javaExecutable) {
256 if (javaExecutable == null) {
257 getLog().warn("Unable to find 'java' executable for toolchain. Falling back to in-process compilation.");
258 try {
259 performInProcessCompilation(configuration, configuration.getClasspath());
260 } catch (Exception e) {
261 throw new RuntimeException("Compilation failed", e);
262 }
263 return;
264 }
265
266 try {
267 File configFile = File.createTempFile("gmavenplus-compile-config", ".ser");
268 configFile.deleteOnExit();
269 try (ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(configFile.toPath()))) {
270 oos.writeObject(configuration);
271 }
272
273 List<String> command = new ArrayList<>();
274 command.add(javaExecutable);
275 command.add("-cp");
276 command.add(buildForkClasspath());
277 command.add(ForkedGroovyCompiler.class.getName());
278 command.add(configFile.getAbsolutePath());
279
280 getLog().info("Forking compilation using " + javaExecutable);
281 getLog().debug("Command: " + command);
282
283 ProcessBuilder pb = new ProcessBuilder(command);
284 pb.inheritIO();
285 Process process = pb.start();
286 int exitCode = process.waitFor();
287 if (exitCode != 0) {
288 throw new RuntimeException("Groovy compilation failed with exit code " + exitCode);
289 }
290 } catch (IOException | InterruptedException e) {
291 throw new RuntimeException("Unable to fork compilation", e);
292 }
293 }
294
295 protected String buildForkClasspath() {
296 StringBuilder cp = new StringBuilder();
297
298 cp.append(pluginDescriptor.getPluginArtifact().getFile().getAbsolutePath());
299
300
301 for (org.apache.maven.artifact.Artifact artifact : pluginDescriptor.getArtifacts()) {
302 cp.append(File.pathSeparator);
303 cp.append(artifact.getFile().getAbsolutePath());
304 }
305
306
307 try {
308 Class<?> logClass = org.apache.maven.plugin.logging.Log.class;
309 java.security.CodeSource codeSource = logClass.getProtectionDomain().getCodeSource();
310 if (codeSource != null) {
311 String logJar = new File(codeSource.getLocation().toURI()).getAbsolutePath();
312 cp.append(File.pathSeparator).append(logJar);
313 }
314 } catch (Exception e) {
315 getLog().warn("Could not find maven-plugin-api jar to add to fork classpath", e);
316 }
317
318 return cp.toString();
319 }
320
321 }