@@ -64,8 +64,10 @@ public EclipseJavaCompiler()
6464 public CompilerResult performCompile (CompilerConfiguration config )
6565 throws CompilerException
6666 {
67- // Build settings from configuration
6867 List <String > args = new ArrayList <>();
68+ args .add ("-noExit" ); // Make sure ecj does not System.exit on us 8-/
69+
70+ // Build settings from configuration
6971 if ( config .isDebug () )
7072 {
7173 args .add ("-preserveAllLocals" );
@@ -146,13 +148,44 @@ else if(extras.containsKey("-errorsAsWarnings"))
146148
147149 for (Entry <String , String > entry : extras .entrySet ())
148150 {
151+ /*
152+ * The compiler mojo makes quite a mess of passing arguments, depending on exactly WHICH
153+ * way is used to pass them. The method method using <compilerArguments> uses the tag names
154+ * of its contents to denote option names, and so the compiler mojo happily adds a '-' to
155+ * all of the names there and adds them to the "custom compiler arguments" map as a
156+ * name, value pair where the name always contains a single '-'. The Eclipse compiler (and
157+ * javac too, btw) has options with two dashes (like --add-modules for java 9). These cannot
158+ * be passed using a <compilerArguments> tag.
159+ *
160+ * The other method is to use <compilerArgs>, where each SINGLE argument needs to be passed
161+ * using an <arg>xxxx</arg> tag. In there the xxx is not manipulated by the compiler mojo, so
162+ * if it starts with a dash or more dashes these are perfectly preserved. But of course these
163+ * single <arg> entries are not a pair. So the compiler mojo adds them as pairs of (xxxx, null).
164+ *
165+ * We use that knowledge here: if a pair has a null value then do not mess up the key but
166+ * render it as a single value. This should ensure that something like:
167+ * <compilerArgs>
168+ * <arg>--add-modules</arg>
169+ * <arg>java.se.ee</arg>
170+ * </compilerArgs>
171+ *
172+ * is actually added to the command like as such.
173+ *
174+ * (btw: the above example will still give an error when using ecj <= 4.8M6:
175+ * invalid module name: java.se.ee
176+ * but that seems to be a bug in ecj).
177+ */
149178 String opt = entry .getKey ();
150- if (! opt .startsWith ("-" ))
151- opt = "-" + opt ; // compiler mojo apparently messes with this; make sure we are safe
152- args .add (opt );
153- String value = entry .getValue ();
154- if (null != value && ! value .isEmpty ())
155- args .add (value );
179+ String optionValue = entry .getValue ();
180+ if (null == optionValue ) {
181+ //-- We have an option from compilerArgs: use the key as-is as a single option value
182+ args .add (opt );
183+ } else {
184+ if (!opt .startsWith ("-" ))
185+ opt = "-" + opt ;
186+ args .add (opt );
187+ args .add (optionValue );
188+ }
156189 }
157190
158191 // Output path
@@ -209,65 +242,102 @@ else if(extras.containsKey("-errorsAsWarnings"))
209242
210243 // Compile! Send all errors to xml temp file.
211244 File errorF = null ;
212- try {
245+ try
246+ {
213247 errorF = File .createTempFile ("ecjerr-" , ".xml" );
214248
215249 args .add ("-log" );
216250 args .add (errorF .toString ());
217251
218252 // Add all sources.
219253 int argCount = args .size ();
220- for (String source : config .getSourceLocations ()) {
254+ for (String source : config .getSourceLocations ())
255+ {
221256 File srcFile = new File (source );
222- if (srcFile .exists ()) {
223- Set <String > ss = getSourceFilesForSourceRoot ( config , source );
257+ if (srcFile .exists ())
258+ {
259+ Set <String > ss = getSourceFilesForSourceRoot (config , source );
224260 args .addAll (ss );
225261 }
226262 }
227263 args .addAll (extraSourceDirs );
228- if (args .size () == argCount ) {
264+ if (args .size () == argCount )
265+ {
229266 //-- Nothing to do -> bail out
230267 return new CompilerResult (true , Collections .EMPTY_LIST );
231268 }
232269
270+ getLogger ().debug ("ecj command line: " + args );
271+
233272 StringWriter sw = new StringWriter ();
234273 PrintWriter devNull = new PrintWriter (sw );
235274
236275 //BatchCompiler.compile(args.toArray(new String[args.size()]), new PrintWriter(System.err), new PrintWriter(System.out), new CompilationProgress() {
237- BatchCompiler .compile (args .toArray (new String [args .size ()]), devNull , devNull , new CompilationProgress () {
238- @ Override public void begin (int i ) {
276+ boolean success = BatchCompiler .compile (args .toArray (new String [args .size ()]), devNull , devNull , new CompilationProgress () {
277+ @ Override
278+ public void begin (int i )
279+ {
239280 }
240281
241- @ Override public void done () {
282+ @ Override
283+ public void done ()
284+ {
242285 }
243286
244- @ Override public boolean isCanceled () {
287+ @ Override
288+ public boolean isCanceled ()
289+ {
245290 return false ;
246291 }
247292
248- @ Override public void setTaskName (String s ) {
293+ @ Override
294+ public void setTaskName (String s )
295+ {
249296 }
250297
251- @ Override public void worked (int i , int i1 ) {
298+ @ Override
299+ public void worked (int i , int i1 )
300+ {
252301 }
253302 });
254303 getLogger ().debug (sw .toString ());
255304
256305 List <CompilerMessage > messageList ;
257306 boolean hasError = false ;
258- if (errorF .length () < 80 ) {
259- throw new IOException ("Failed to run the ECJ compiler:\n " + sw .toString ());
307+ if (errorF .length () < 80 )
308+ {
309+ throw new EcjFailureException (sw .toString ());
260310 }
261311 messageList = new EcjResponseParser ().parse (errorF , errorsAsWarnings );
262312
263- for (CompilerMessage compilerMessage : messageList ) {
264- if (compilerMessage .isError ()) {
313+ for (CompilerMessage compilerMessage : messageList )
314+ {
315+ if (compilerMessage .isError ())
316+ {
265317 hasError = true ;
266318 break ;
267319 }
268320 }
269- return new CompilerResult (! hasError , messageList );
270-
321+ if (!hasError && !success && !errorsAsWarnings )
322+ {
323+ CompilerMessage .Kind kind = errorsAsWarnings ? CompilerMessage .Kind .WARNING : CompilerMessage .Kind .ERROR ;
324+
325+ //-- Compiler reported failure but we do not seem to have one -> probable exception
326+ CompilerMessage cm = new CompilerMessage ("[ecj] The compiler reported an error but has not written it to its logging" , kind );
327+ messageList .add (cm );
328+ hasError = true ;
329+
330+ //-- Try to find the actual message by reporting the last 5 lines as a message
331+ String stdout = getLastLines (sw .toString (), 5 );
332+ if (stdout .length () > 0 )
333+ {
334+ cm = new CompilerMessage ("[ecj] The following line(s) might indicate the issue:\n " + stdout , kind );
335+ messageList .add (cm );
336+ }
337+ }
338+ return new CompilerResult (!hasError || errorsAsWarnings , messageList );
339+ } catch (EcjFailureException x ) {
340+ throw x ;
271341 } catch (Exception x ) {
272342 throw new RuntimeException (x ); // sigh
273343 } finally {
@@ -279,6 +349,36 @@ else if(extras.containsKey("-errorsAsWarnings"))
279349 }
280350 }
281351
352+ private String getLastLines (String text , int lines )
353+ {
354+ List <String > lineList = new ArrayList <>();
355+ text = text .replace ("\r \n " , "\n " );
356+ text = text .replace ("\r " , "\n " ); // make sure eoln is \n
357+
358+ int index = text .length ();
359+ while (index > 0 ) {
360+ int before = text .lastIndexOf ('\n' , index - 1 );
361+
362+ if (before + 1 < index ) { // Non empty line?
363+ lineList .add (text .substring (before + 1 , index ));
364+ lines --;
365+ if (lines <= 0 )
366+ break ;
367+ }
368+
369+ index = before ;
370+ }
371+
372+ StringBuilder sb = new StringBuilder ();
373+ for (int i = lineList .size () - 1 ; i >= 0 ; i --)
374+ {
375+ String s = lineList .get (i );
376+ sb .append (s );
377+ sb .append (System .getProperty ("line.separator" )); // 8-/
378+ }
379+ return sb .toString ();
380+ }
381+
282382 static private void append (StringBuilder warns , String s ) {
283383 if (warns .length () > 0 )
284384 warns .append (',' );
0 commit comments