1 package org.codehaus.plexus.compiler.javac;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 import javax.inject.Inject;
44 import javax.inject.Named;
45 import javax.inject.Singleton;
46
47 import java.io.BufferedReader;
48 import java.io.File;
49 import java.io.FileWriter;
50 import java.io.IOException;
51 import java.io.PrintWriter;
52 import java.io.StringReader;
53 import java.io.StringWriter;
54 import java.lang.reflect.InvocationTargetException;
55 import java.lang.reflect.Method;
56 import java.net.MalformedURLException;
57 import java.net.URL;
58 import java.net.URLClassLoader;
59 import java.util.ArrayList;
60 import java.util.Arrays;
61 import java.util.Deque;
62 import java.util.HashSet;
63 import java.util.List;
64 import java.util.Map;
65 import java.util.NoSuchElementException;
66 import java.util.Properties;
67 import java.util.Set;
68 import java.util.StringTokenizer;
69 import java.util.concurrent.ConcurrentHashMap;
70 import java.util.concurrent.ConcurrentLinkedDeque;
71 import java.util.regex.Matcher;
72 import java.util.regex.Pattern;
73
74 import org.codehaus.plexus.compiler.AbstractCompiler;
75 import org.codehaus.plexus.compiler.CompilerConfiguration;
76 import org.codehaus.plexus.compiler.CompilerException;
77 import org.codehaus.plexus.compiler.CompilerMessage;
78 import org.codehaus.plexus.compiler.CompilerOutputStyle;
79 import org.codehaus.plexus.compiler.CompilerResult;
80 import org.codehaus.plexus.util.FileUtils;
81 import org.codehaus.plexus.util.Os;
82 import org.codehaus.plexus.util.StringUtils;
83 import org.codehaus.plexus.util.cli.CommandLineException;
84 import org.codehaus.plexus.util.cli.CommandLineUtils;
85 import org.codehaus.plexus.util.cli.Commandline;
86
87
88
89
90
91
92
93
94 @Named("javac")
95 @Singleton
96 public class JavacCompiler extends AbstractCompiler {
97
98
99 private static final String[] WARNING_PREFIXES = {"warning: ", "\u8b66\u544a: ", "\u8b66\u544a\uff1a "};
100
101
102 private static final String[] NOTE_PREFIXES = {"Note: ", "\u6ce8: ", "\u6ce8\u610f\uff1a "};
103
104
105 private static final String[] MISC_PREFIXES = {"["};
106
107 private static final Object LOCK = new Object();
108
109 private static final String JAVAC_CLASSNAME = "com.sun.tools.javac.Main";
110
111 private volatile Class<?> javacClass;
112
113 private final Deque<Class<?>> javacClasses = new ConcurrentLinkedDeque<>();
114
115 private static final Pattern JAVA_MAJOR_AND_MINOR_VERSION_PATTERN = Pattern.compile("\\d+(\\.\\d+)?");
116
117
118 private static final Map<String, String> VERSION_PER_EXECUTABLE = new ConcurrentHashMap<>();
119
120 @Inject
121 private InProcessCompiler inProcessCompiler;
122
123
124
125
126
127 public JavacCompiler() {
128 super(CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE, ".java", ".class", null);
129 }
130
131
132
133
134
135 @Override
136 public String getCompilerId() {
137 return "javac";
138 }
139
140 private String getInProcessJavacVersion() throws CompilerException {
141 return System.getProperty("java.version");
142 }
143
144 private String getOutOfProcessJavacVersion(String executable) throws CompilerException {
145 String version = VERSION_PER_EXECUTABLE.get(executable);
146 if (version == null) {
147 Commandline cli = new Commandline();
148 cli.setExecutable(executable);
149
150
151
152
153 cli.addArguments(new String[] {"-version"});
154 CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
155 try {
156 int exitCode = CommandLineUtils.executeCommandLine(cli, out, out);
157 if (exitCode != 0) {
158 throw new CompilerException("Could not retrieve version from " + executable + ". Exit code "
159 + exitCode + ", Output: " + out.getOutput());
160 }
161 } catch (CommandLineException e) {
162 throw new CompilerException("Error while executing the external compiler " + executable, e);
163 }
164 version = extractMajorAndMinorVersion(out.getOutput());
165 VERSION_PER_EXECUTABLE.put(executable, version);
166 }
167 return version;
168 }
169
170 static String extractMajorAndMinorVersion(String text) {
171 Matcher matcher = JAVA_MAJOR_AND_MINOR_VERSION_PATTERN.matcher(text);
172 if (!matcher.find()) {
173 throw new IllegalArgumentException("Could not extract version from \"" + text + "\"");
174 }
175 return matcher.group();
176 }
177
178 @Override
179 public CompilerResult performCompile(CompilerConfiguration config) throws CompilerException {
180 File destinationDir = new File(config.getOutputLocation());
181
182 if (!destinationDir.exists()) {
183 destinationDir.mkdirs();
184 }
185
186 String[] sourceFiles = getSourceFiles(config);
187
188 if ((sourceFiles == null) || (sourceFiles.length == 0)) {
189 return new CompilerResult();
190 }
191
192 logCompiling(sourceFiles, config);
193
194 final String javacVersion;
195 final String executable;
196 if (config.isFork()) {
197 executable = getJavacExecutable(config);
198 javacVersion = getOutOfProcessJavacVersion(executable);
199 } else {
200 javacVersion = getInProcessJavacVersion();
201 executable = null;
202 }
203
204 String[] args = buildCompilerArguments(config, sourceFiles, javacVersion);
205
206 CompilerResult result;
207
208 if (config.isFork()) {
209
210 result = compileOutOfProcess(config, executable, args);
211 } else {
212 if (hasJavaxToolProvider() && !config.isForceJavacCompilerUse()) {
213
214 result = inProcessCompiler().compileInProcess(args, config, sourceFiles);
215 } else {
216 result = compileInProcess(args, config);
217 }
218 }
219
220 return result;
221 }
222
223 protected InProcessCompiler inProcessCompiler() {
224 return inProcessCompiler;
225 }
226
227
228
229
230
231 protected static boolean hasJavaxToolProvider() {
232 try {
233 Thread.currentThread().getContextClassLoader().loadClass("javax.tools.ToolProvider");
234 return true;
235 } catch (Exception e) {
236 return false;
237 }
238 }
239
240 public String[] createCommandLine(CompilerConfiguration config) throws CompilerException {
241 final String javacVersion;
242 if (config.isFork()) {
243 String executable = getJavacExecutable(config);
244 javacVersion = getOutOfProcessJavacVersion(executable);
245 } else {
246 javacVersion = getInProcessJavacVersion();
247 }
248 return buildCompilerArguments(config, getSourceFiles(config), javacVersion);
249 }
250
251 public static String[] buildCompilerArguments(
252 CompilerConfiguration config, String[] sourceFiles, String javacVersion) {
253 List<String> args = new ArrayList<>();
254
255
256
257
258
259 File destinationDir = new File(config.getOutputLocation());
260
261 args.add("-d");
262
263 args.add(destinationDir.getAbsolutePath());
264
265
266
267
268
269 List<String> classpathEntries = config.getClasspathEntries();
270 if (classpathEntries != null && !classpathEntries.isEmpty()) {
271 args.add("-classpath");
272
273 args.add(getPathString(classpathEntries));
274 }
275
276 List<String> modulepathEntries = config.getModulepathEntries();
277 if (modulepathEntries != null && !modulepathEntries.isEmpty()) {
278 args.add("--module-path");
279
280 args.add(getPathString(modulepathEntries));
281 }
282
283 List<String> sourceLocations = config.getSourceLocations();
284 if (sourceLocations != null && !sourceLocations.isEmpty()) {
285
286
287 args.add("-sourcepath");
288
289 args.add(getPathString(sourceLocations));
290 }
291 if (!hasJavaxToolProvider() || config.isForceJavacCompilerUse() || config.isFork()) {
292 args.addAll(Arrays.asList(sourceFiles));
293 }
294
295 if (JavaVersion.JAVA_1_6.isOlderOrEqualTo(javacVersion)) {
296
297
298 if (config.getGeneratedSourcesDirectory() != null) {
299 config.getGeneratedSourcesDirectory().mkdirs();
300
301 args.add("-s");
302 args.add(config.getGeneratedSourcesDirectory().getAbsolutePath());
303 }
304 if (config.getProc() != null) {
305 args.add("-proc:" + config.getProc());
306 }
307 if (config.getAnnotationProcessors() != null) {
308 args.add("-processor");
309 String[] procs = config.getAnnotationProcessors();
310 StringBuilder buffer = new StringBuilder();
311 for (int i = 0; i < procs.length; i++) {
312 if (i > 0) {
313 buffer.append(",");
314 }
315
316 buffer.append(procs[i]);
317 }
318 args.add(buffer.toString());
319 }
320 if (config.getProcessorPathEntries() != null
321 && !config.getProcessorPathEntries().isEmpty()) {
322 args.add("-processorpath");
323 args.add(getPathString(config.getProcessorPathEntries()));
324 }
325 if (config.getProcessorModulePathEntries() != null
326 && !config.getProcessorModulePathEntries().isEmpty()) {
327 args.add("--processor-module-path");
328 args.add(getPathString(config.getProcessorModulePathEntries()));
329 }
330 }
331
332 if (config.isOptimize()) {
333 args.add("-O");
334 }
335
336 if (config.isDebug()) {
337 if (StringUtils.isNotEmpty(config.getDebugLevel())) {
338 args.add("-g:" + config.getDebugLevel());
339 } else {
340 args.add("-g");
341 }
342 }
343
344 if (config.isVerbose()) {
345 args.add("-verbose");
346 }
347
348 if (JavaVersion.JAVA_1_8.isOlderOrEqualTo(javacVersion) && config.isParameters()) {
349 args.add("-parameters");
350 }
351
352 if (config.isEnablePreview()) {
353 args.add("--enable-preview");
354 }
355
356 if (config.getImplicitOption() != null) {
357 args.add("-implicit:" + config.getImplicitOption());
358 }
359
360 if (config.isShowDeprecation()) {
361 args.add("-deprecation");
362
363
364 config.setShowWarnings(true);
365 }
366
367 if (!config.isShowWarnings()) {
368 args.add("-nowarn");
369 } else {
370 String warnings = config.getWarnings();
371 if (config.isShowLint()) {
372 if (config.isShowWarnings() && StringUtils.isNotEmpty(warnings)) {
373 args.add("-Xlint:" + warnings);
374 } else {
375 args.add("-Xlint");
376 }
377 }
378 }
379
380 if (config.isFailOnWarning()) {
381 args.add("-Werror");
382 }
383
384 if (JavaVersion.JAVA_9.isOlderOrEqualTo(javacVersion) && !StringUtils.isEmpty(config.getReleaseVersion())) {
385 args.add("--release");
386 args.add(config.getReleaseVersion());
387 } else {
388
389 if (StringUtils.isEmpty(config.getTargetVersion())) {
390
391 args.add("-target");
392 args.add("1.1");
393 } else {
394 args.add("-target");
395 args.add(config.getTargetVersion());
396 }
397
398 if (JavaVersion.JAVA_1_4.isOlderOrEqualTo(javacVersion) && StringUtils.isEmpty(config.getSourceVersion())) {
399
400 args.add("-source");
401 args.add("1.3");
402 } else if (JavaVersion.JAVA_1_4.isOlderOrEqualTo(javacVersion)) {
403 args.add("-source");
404 args.add(config.getSourceVersion());
405 }
406 }
407
408 if (JavaVersion.JAVA_1_4.isOlderOrEqualTo(javacVersion) && !StringUtils.isEmpty(config.getSourceEncoding())) {
409 args.add("-encoding");
410 args.add(config.getSourceEncoding());
411 }
412
413 if (!StringUtils.isEmpty(config.getModuleVersion())) {
414 args.add("--module-version");
415 args.add(config.getModuleVersion());
416 }
417
418 for (Map.Entry<String, String> entry : config.getCustomCompilerArgumentsEntries()) {
419 String key = entry.getKey();
420
421 if (StringUtils.isEmpty(key) || key.startsWith("-J")) {
422 continue;
423 }
424
425 args.add(key);
426
427 String value = entry.getValue();
428
429 if (StringUtils.isEmpty(value)) {
430 continue;
431 }
432
433 args.add(value);
434 }
435
436 if (!config.isFork() && !args.contains("-XDuseUnsharedTable=false")) {
437 args.add("-XDuseUnsharedTable=true");
438 }
439
440 return args.toArray(new String[0]);
441 }
442
443
444
445
446 enum JavaVersion {
447 JAVA_1_3_OR_OLDER("1.3", "1.2", "1.1", "1.0"),
448 JAVA_1_4("1.4"),
449 JAVA_1_5("1.5"),
450 JAVA_1_6("1.6"),
451 JAVA_1_7("1.7"),
452 JAVA_1_8("1.8"),
453 JAVA_9("9");
454 final Set<String> versionPrefixes;
455
456 JavaVersion(String... versionPrefixes) {
457 this.versionPrefixes = new HashSet<>(Arrays.asList(versionPrefixes));
458 }
459
460
461
462
463
464
465
466 boolean isOlderOrEqualTo(String version) {
467
468 JavaVersion[] allJavaVersionPrefixes = JavaVersion.values();
469 for (int n = ordinal() - 1; n > -1; n--) {
470 if (allJavaVersionPrefixes[n].versionPrefixes.stream().anyMatch(version::startsWith)) {
471 return false;
472 }
473 }
474 return true;
475 }
476 }
477
478
479
480
481
482
483
484
485
486
487
488 protected CompilerResult compileOutOfProcess(CompilerConfiguration config, String executable, String[] args)
489 throws CompilerException {
490 Commandline cli = new Commandline();
491
492 cli.setWorkingDirectory(config.getWorkingDirectory().getAbsolutePath());
493
494 cli.setExecutable(executable);
495
496 try {
497 File argumentsFile =
498 createFileWithArguments(args, config.getBuildDirectory().getAbsolutePath());
499 cli.addArguments(
500 new String[] {"@" + argumentsFile.getCanonicalPath().replace(File.separatorChar, '/')});
501
502 if (!StringUtils.isEmpty(config.getMaxmem())) {
503 cli.addArguments(new String[] {"-J-Xmx" + config.getMaxmem()});
504 }
505
506 if (!StringUtils.isEmpty(config.getMeminitial())) {
507 cli.addArguments(new String[] {"-J-Xms" + config.getMeminitial()});
508 }
509
510 for (String key : config.getCustomCompilerArgumentsAsMap().keySet()) {
511 if (StringUtils.isNotEmpty(key) && key.startsWith("-J")) {
512 cli.addArguments(new String[] {key});
513 }
514 }
515 } catch (IOException e) {
516 throw new CompilerException("Error creating file with javac arguments", e);
517 }
518
519 CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
520
521 int returnCode;
522
523 List<CompilerMessage> messages;
524
525 if (getLog().isDebugEnabled()) {
526 String debugFileName = StringUtils.isEmpty(config.getDebugFileName()) ? "javac" : config.getDebugFileName();
527
528 File commandLineFile = new File(
529 config.getBuildDirectory(),
530 StringUtils.trim(debugFileName) + "." + (Os.isFamily(Os.FAMILY_WINDOWS) ? "bat" : "sh"));
531 try {
532 FileUtils.fileWrite(
533 commandLineFile.getAbsolutePath(), cli.toString().replaceAll("'", ""));
534
535 if (!Os.isFamily(Os.FAMILY_WINDOWS)) {
536 Runtime.getRuntime().exec(new String[] {"chmod", "a+x", commandLineFile.getAbsolutePath()});
537 }
538 } catch (IOException e) {
539 if (getLog().isWarnEnabled()) {
540 getLog().warn("Unable to write '" + commandLineFile.getName() + "' debug script file", e);
541 }
542 }
543 }
544
545 try {
546 returnCode = CommandLineUtils.executeCommandLine(cli, out, out);
547
548 messages = parseModernStream(returnCode, new BufferedReader(new StringReader(out.getOutput())));
549 } catch (CommandLineException | IOException e) {
550 throw new CompilerException("Error while executing the external compiler.", e);
551 }
552
553 boolean success = returnCode == 0;
554 return new CompilerResult(success, messages);
555 }
556
557
558
559
560
561
562
563
564
565
566 CompilerResult compileInProcess(String[] args, CompilerConfiguration config) throws CompilerException {
567 final Class<?> javacClass = getJavacClass(config);
568 final Thread thread = Thread.currentThread();
569 final ClassLoader contextClassLoader = thread.getContextClassLoader();
570 thread.setContextClassLoader(javacClass.getClassLoader());
571 if (getLog().isDebugEnabled()) {
572 getLog().debug("ttcl changed run compileInProcessWithProperClassloader");
573 }
574 try {
575 return compileInProcessWithProperClassloader(javacClass, args);
576 } finally {
577 releaseJavaccClass(javacClass, config);
578 thread.setContextClassLoader(contextClassLoader);
579 }
580 }
581
582 protected CompilerResult compileInProcessWithProperClassloader(Class<?> javacClass, String[] args)
583 throws CompilerException {
584 return compileInProcess0(javacClass, args);
585 }
586
587
588
589
590 private static CompilerResult compileInProcess0(Class<?> javacClass, String[] args) throws CompilerException {
591 StringWriter out = new StringWriter();
592
593 Integer ok;
594
595 List<CompilerMessage> messages;
596
597 try {
598 Method compile = javacClass.getMethod("compile", new Class[] {String[].class, PrintWriter.class});
599
600 ok = (Integer) compile.invoke(null, new Object[] {args, new PrintWriter(out)});
601
602 messages = parseModernStream(ok, new BufferedReader(new StringReader(out.toString())));
603 } catch (NoSuchMethodException | IOException | InvocationTargetException | IllegalAccessException e) {
604 throw new CompilerException("Error while executing the compiler.", e);
605 }
606
607 boolean success = ok == 0;
608 return new CompilerResult(success, messages);
609 }
610
611
612 private static final Pattern STACK_TRACE_FIRST_LINE = Pattern.compile("^(?:[\\w+.-]+\\.)[\\w$]*?(?:"
613 + "Exception|Error|Throwable|Failure|Result|Abort|Fault|ThreadDeath|Overflow|Warning|"
614 + "NotSupported|NotFound|BadArgs|BadClassFile|Illegal|Invalid|Unexpected|Unchecked|Unmatched\\w+"
615 + ").*$");
616
617
618 private static final Pattern STACK_TRACE_OTHER_LINE =
619 Pattern.compile("^(?:Caused by:\\s.*|\\s*at .*|\\s*\\.\\.\\.\\s\\d+\\smore)$");
620
621
622 private static final Pattern JAVAC_OR_JVM_ERROR =
623 Pattern.compile("^(?:javac:|Error occurred during initialization of (?:boot layer|VM)).*", Pattern.DOTALL);
624
625
626
627
628
629
630
631
632
633 static List<CompilerMessage> parseModernStream(int exitCode, BufferedReader input) throws IOException {
634 List<CompilerMessage> errors = new ArrayList<>();
635
636 String line;
637
638 StringBuilder buffer = new StringBuilder();
639
640 boolean hasPointer = false;
641 int stackTraceLineCount = 0;
642
643 while (true) {
644 line = input.readLine();
645
646 if (line == null) {
647
648
649 String bufferAsString = buffer.toString();
650 if (buffer.length() > 0) {
651 if (JAVAC_OR_JVM_ERROR.matcher(bufferAsString).matches()) {
652 errors.add(new CompilerMessage(bufferAsString, CompilerMessage.Kind.ERROR));
653 } else if (hasPointer) {
654
655 errors.add(parseModernError(exitCode, bufferAsString));
656 } else if (stackTraceLineCount > 0) {
657
658 String[] lines = bufferAsString.split("\\R");
659 int linesTotal = lines.length;
660 buffer = new StringBuilder();
661 int firstLine = linesTotal - stackTraceLineCount;
662
663
664
665 if (firstLine > 0) {
666 final String lineBeforeStackTrace = lines[firstLine - 1];
667
668
669 if (lineBeforeStackTrace.contains("java.sun.com/webapps/bugreport")
670 || lineBeforeStackTrace.contains("bugreport.java.com")) {
671 firstLine--;
672 }
673 }
674
675
676
677
678
679
680 for (int i = firstLine; i < linesTotal; i++) {
681 buffer.append(lines[i]).append(EOL);
682 }
683 errors.add(new CompilerMessage(buffer.toString(), CompilerMessage.Kind.ERROR));
684 }
685 }
686 return errors;
687 }
688
689 if (stackTraceLineCount == 0 && STACK_TRACE_FIRST_LINE.matcher(line).matches()
690 || STACK_TRACE_OTHER_LINE.matcher(line).matches()) {
691 stackTraceLineCount++;
692 } else {
693 stackTraceLineCount = 0;
694 }
695
696
697 if (!line.startsWith(" ") && hasPointer) {
698
699 errors.add(parseModernError(exitCode, buffer.toString()));
700
701
702 buffer = new StringBuilder();
703
704 hasPointer = false;
705 }
706
707
708 if ((buffer.length() == 0) && line.startsWith("error: ")) {
709 errors.add(new CompilerMessage(line, CompilerMessage.Kind.ERROR));
710 } else if ((buffer.length() == 0) && line.startsWith("warning: ")) {
711 errors.add(new CompilerMessage(line, CompilerMessage.Kind.WARNING));
712 } else if ((buffer.length() == 0) && isNote(line)) {
713
714 } else if ((buffer.length() == 0) && isMisc(line)) {
715
716 errors.add(new CompilerMessage(line, CompilerMessage.Kind.OTHER));
717 } else {
718 buffer.append(line);
719
720 buffer.append(EOL);
721 }
722
723 if (line.endsWith("^")) {
724 hasPointer = true;
725 }
726 }
727 }
728
729 private static boolean isMisc(String line) {
730 return startsWithPrefix(line, MISC_PREFIXES);
731 }
732
733 private static boolean isNote(String line) {
734 return startsWithPrefix(line, NOTE_PREFIXES);
735 }
736
737 private static boolean startsWithPrefix(String line, String[] prefixes) {
738 for (String prefix : prefixes) {
739 if (line.startsWith(prefix)) {
740 return true;
741 }
742 }
743 return false;
744 }
745
746
747
748
749
750
751
752
753 static CompilerMessage parseModernError(int exitCode, String error) {
754 final StringTokenizer tokens = new StringTokenizer(error, ":");
755
756 boolean isError = exitCode != 0;
757
758 try {
759
760
761
762
763 boolean tokenIsAnInteger;
764
765 StringBuilder file = null;
766
767 String currentToken = null;
768
769 do {
770 if (currentToken != null) {
771 if (file == null) {
772 file = new StringBuilder(currentToken);
773 } else {
774 file.append(':').append(currentToken);
775 }
776 }
777
778 currentToken = tokens.nextToken();
779
780
781
782 tokenIsAnInteger = true;
783
784 try {
785 Integer.parseInt(currentToken);
786 } catch (NumberFormatException e) {
787 tokenIsAnInteger = false;
788 }
789 } while (!tokenIsAnInteger);
790
791 final String lineIndicator = currentToken;
792
793 final int startOfFileName = file.toString().lastIndexOf(']');
794
795 if (startOfFileName > -1) {
796 file = new StringBuilder(file.substring(startOfFileName + 1 + EOL.length()));
797 }
798
799 final int line = Integer.parseInt(lineIndicator);
800
801 final StringBuilder msgBuffer = new StringBuilder();
802
803 String msg = tokens.nextToken(EOL).substring(2);
804
805
806 final String warnPrefix = getWarnPrefix(msg);
807 if (warnPrefix != null) {
808 isError = false;
809 msg = msg.substring(warnPrefix.length());
810 } else {
811 isError = exitCode != 0;
812 }
813
814 msgBuffer.append(msg);
815
816 msgBuffer.append(EOL);
817
818 String context = tokens.nextToken(EOL);
819
820 String pointer = null;
821
822 do {
823 final String msgLine = tokens.nextToken(EOL);
824
825 if (pointer != null) {
826 msgBuffer.append(msgLine);
827
828 msgBuffer.append(EOL);
829 } else if (msgLine.endsWith("^")) {
830 pointer = msgLine;
831 } else {
832 msgBuffer.append(context);
833
834 msgBuffer.append(EOL);
835
836 context = msgLine;
837 }
838 } while (tokens.hasMoreTokens());
839
840 msgBuffer.append(EOL);
841
842 final String message = msgBuffer.toString();
843
844 final int startcolumn = pointer.indexOf("^");
845
846 int endcolumn = (context == null) ? startcolumn : context.indexOf(" ", startcolumn);
847
848 if (endcolumn == -1) {
849 endcolumn = context.length();
850 }
851
852 return new CompilerMessage(file.toString(), isError, line, startcolumn, line, endcolumn, message.trim());
853 } catch (NoSuchElementException e) {
854 return new CompilerMessage("no more tokens - could not parse error message: " + error, isError);
855 } catch (Exception e) {
856 return new CompilerMessage("could not parse error message: " + error, isError);
857 }
858 }
859
860 private static String getWarnPrefix(String msg) {
861 for (String warningPrefix : WARNING_PREFIXES) {
862 if (msg.startsWith(warningPrefix)) {
863 return warningPrefix;
864 }
865 }
866 return null;
867 }
868
869
870
871
872
873
874
875
876 private File createFileWithArguments(String[] args, String outputDirectory) throws IOException {
877 PrintWriter writer = null;
878 try {
879 File tempFile;
880 if (getLog().isDebugEnabled()) {
881 tempFile = File.createTempFile(JavacCompiler.class.getName(), "arguments", new File(outputDirectory));
882 } else {
883 tempFile = File.createTempFile(JavacCompiler.class.getName(), "arguments");
884 tempFile.deleteOnExit();
885 }
886
887 writer = new PrintWriter(new FileWriter(tempFile));
888
889 for (String arg : args) {
890 String argValue = arg.replace(File.separatorChar, '/');
891
892 writer.write("\"" + argValue + "\"");
893
894 writer.println();
895 }
896
897 writer.flush();
898
899 return tempFile;
900
901 } finally {
902 if (writer != null) {
903 writer.close();
904 }
905 }
906 }
907
908
909
910
911
912
913
914 protected String getJavacExecutable(CompilerConfiguration config) {
915 String executable = config.getExecutable();
916
917 if (StringUtils.isEmpty(executable)) {
918 try {
919 executable = getJavacExecutable();
920 } catch (IOException e) {
921 if (getLog().isWarnEnabled()) {
922 getLog().warn("Unable to autodetect 'javac' path, using 'javac' from the environment.");
923 }
924 executable = "javac";
925 }
926 }
927 return executable;
928 }
929
930
931
932
933
934
935
936
937 private static String getJavacExecutable() throws IOException {
938 String javacCommand = "javac" + (Os.isFamily(Os.FAMILY_WINDOWS) ? ".exe" : "");
939
940 String javaHome = System.getProperty("java.home");
941 File javacExe;
942 if (Os.isName("AIX")) {
943 javacExe = new File(javaHome + File.separator + ".." + File.separator + "sh", javacCommand);
944 } else if (Os.isName("Mac OS X")) {
945 javacExe = new File(javaHome + File.separator + "bin", javacCommand);
946 } else {
947 javacExe = new File(javaHome + File.separator + ".." + File.separator + "bin", javacCommand);
948 }
949
950
951
952
953 if (!javacExe.isFile()) {
954 Properties env = CommandLineUtils.getSystemEnvVars();
955 javaHome = env.getProperty("JAVA_HOME");
956 if (StringUtils.isEmpty(javaHome)) {
957 throw new IOException("The environment variable JAVA_HOME is not correctly set.");
958 }
959 if (!new File(javaHome).isDirectory()) {
960 throw new IOException("The environment variable JAVA_HOME=" + javaHome
961 + " doesn't exist or is not a valid directory.");
962 }
963
964 javacExe = new File(env.getProperty("JAVA_HOME") + File.separator + "bin", javacCommand);
965 }
966
967 if (!javacExe.isFile()) {
968 throw new IOException("The javadoc executable '" + javacExe
969 + "' doesn't exist or is not a file. Verify the JAVA_HOME environment variable.");
970 }
971
972 return javacExe.getAbsolutePath();
973 }
974
975 private void releaseJavaccClass(Class<?> javaccClass, CompilerConfiguration compilerConfiguration) {
976 if (compilerConfiguration.getCompilerReuseStrategy()
977 == CompilerConfiguration.CompilerReuseStrategy.ReuseCreated) {
978 javacClasses.add(javaccClass);
979 }
980 }
981
982
983
984
985
986
987
988 private Class<?> getJavacClass(CompilerConfiguration compilerConfiguration) throws CompilerException {
989 Class<?> c;
990 switch (compilerConfiguration.getCompilerReuseStrategy()) {
991 case AlwaysNew:
992 return createJavacClass();
993 case ReuseCreated:
994 c = javacClasses.poll();
995 if (c == null) {
996 c = createJavacClass();
997 }
998 return c;
999 case ReuseSame:
1000 default:
1001 c = javacClass;
1002 if (c == null) {
1003 synchronized (this) {
1004 c = javacClass;
1005 if (c == null) {
1006 javacClass = c = createJavacClass();
1007 }
1008 }
1009 }
1010 return c;
1011 }
1012 }
1013
1014
1015
1016
1017 protected Class<?> createJavacClass() throws CompilerException {
1018 try {
1019
1020
1021 return JavacCompiler.class.getClassLoader().loadClass(JavacCompiler.JAVAC_CLASSNAME);
1022 } catch (ClassNotFoundException ex) {
1023
1024 }
1025
1026 final File toolsJar = new File(System.getProperty("java.home"), "../lib/tools.jar");
1027 if (!toolsJar.exists()) {
1028 throw new CompilerException("tools.jar not found: " + toolsJar);
1029 }
1030
1031 try {
1032
1033
1034 URL[] originalUrls = ((URLClassLoader) JavacCompiler.class.getClassLoader()).getURLs();
1035 URL[] urls = new URL[originalUrls.length + 1];
1036 urls[0] = toolsJar.toURI().toURL();
1037 System.arraycopy(originalUrls, 0, urls, 1, originalUrls.length);
1038 ClassLoader javacClassLoader = new URLClassLoader(urls);
1039
1040 final Thread thread = Thread.currentThread();
1041 final ClassLoader contextClassLoader = thread.getContextClassLoader();
1042 thread.setContextClassLoader(javacClassLoader);
1043 try {
1044
1045 return javacClassLoader.loadClass(JavacCompiler.JAVAC_CLASSNAME);
1046 } finally {
1047 thread.setContextClassLoader(contextClassLoader);
1048 }
1049 } catch (MalformedURLException ex) {
1050 throw new CompilerException(
1051 "Could not convert the file reference to tools.jar to a URL, path to tools.jar: '"
1052 + toolsJar.getAbsolutePath() + "'.",
1053 ex);
1054 } catch (ClassNotFoundException ex) {
1055 throw new CompilerException(
1056 "Unable to locate the Javac Compiler in:" + EOL + " " + toolsJar + EOL
1057 + "Please ensure you are using JDK 1.4 or above and" + EOL
1058 + "not a JRE (the com.sun.tools.javac.Main class is required)." + EOL
1059 + "In most cases you can change the location of your Java" + EOL
1060 + "installation by setting the JAVA_HOME environment variable.",
1061 ex);
1062 }
1063 }
1064 }