1 package org.apache.maven.plugin;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.util.ArrayList;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.LinkedHashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30
31 import org.apache.maven.execution.MavenSession;
32 import org.apache.maven.toolchain.Toolchain;
33 import org.apache.maven.toolchain.ToolchainManager;
34 import org.codehaus.plexus.compiler.Compiler;
35 import org.codehaus.plexus.compiler.CompilerConfiguration;
36 import org.codehaus.plexus.compiler.CompilerError;
37 import org.codehaus.plexus.compiler.CompilerException;
38 import org.codehaus.plexus.compiler.CompilerOutputStyle;
39 import org.codehaus.plexus.compiler.manager.CompilerManager;
40 import org.codehaus.plexus.compiler.manager.NoSuchCompilerException;
41 import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
42 import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
43 import org.codehaus.plexus.compiler.util.scan.mapping.SingleTargetSourceMapping;
44 import org.codehaus.plexus.compiler.util.scan.mapping.SourceMapping;
45 import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
46 import org.codehaus.plexus.util.ReaderFactory;
47 import org.codehaus.plexus.util.StringUtils;
48
49
50
51
52
53
54
55
56
57
58
59
60 public abstract class AbstractCompilerMojo
61 extends AbstractMojo
62 {
63
64
65
66
67
68
69
70
71
72
73 private boolean failOnError = true;
74
75
76
77
78
79
80 private boolean debug = true;
81
82
83
84
85
86
87 private boolean verbose;
88
89
90
91
92
93
94 private boolean showDeprecation;
95
96
97
98
99
100
101 private boolean optimize;
102
103
104
105
106
107
108 private boolean showWarnings;
109
110
111
112
113
114
115 protected String source;
116
117
118
119
120
121
122 protected String target;
123
124
125
126
127
128
129 private String encoding;
130
131
132
133
134
135
136
137 private int staleMillis;
138
139
140
141
142
143
144
145 private String compilerId;
146
147
148
149
150
151
152 private String compilerVersion;
153
154
155
156
157
158
159
160 private boolean fork;
161
162
163
164
165
166
167
168
169 private String meminitial;
170
171
172
173
174
175
176
177
178 private String maxmem;
179
180
181
182
183
184
185 private String executable;
186
187
188
189
190
191
192
193
194
195
196
197
198
199 protected Map compilerArguments;
200
201
202
203
204
205
206
207
208
209
210
211
212 protected String compilerArgument;
213
214
215
216
217
218
219
220 private String outputFileName;
221
222
223
224
225
226
227
228
229 private String debuglevel;
230
231
232 private ToolchainManager toolchainManager;
233
234
235
236
237
238
239
240
241
242
243
244
245 private File basedir;
246
247
248
249
250
251
252
253
254 private File buildDirectory;
255
256
257
258
259
260
261 private CompilerManager compilerManager;
262
263
264
265
266
267
268
269
270
271 private MavenSession session;
272
273 protected abstract SourceInclusionScanner getSourceInclusionScanner( int staleMillis );
274
275 protected abstract SourceInclusionScanner getSourceInclusionScanner( String inputFileEnding );
276
277 protected abstract List getClasspathElements();
278
279 protected abstract List getCompileSourceRoots();
280
281 protected abstract File getOutputDirectory();
282
283 protected abstract String getSource();
284
285 protected abstract String getTarget();
286
287 protected abstract String getCompilerArgument();
288
289 protected abstract Map getCompilerArguments();
290
291 public void execute()
292 throws MojoExecutionException, CompilationFailureException
293 {
294
295
296
297
298
299
300 Compiler compiler;
301
302 getLog().debug( "Using compiler '" + compilerId + "'." );
303
304 try
305 {
306 compiler = compilerManager.getCompiler( compilerId );
307 }
308 catch ( NoSuchCompilerException e )
309 {
310 throw new MojoExecutionException( "No such compiler '" + e.getCompilerId() + "'." );
311 }
312
313
314
315 Toolchain tc = getToolchain();
316 if ( tc != null )
317 {
318 getLog().info( "Toolchain in compiler-plugin: " + tc );
319 if ( executable != null )
320 {
321 getLog().warn( "Toolchains are ignored, 'executable' parameter is set to " + executable );
322 }
323 else
324 {
325 fork = true;
326
327 executable = tc.findTool( compilerId );
328 }
329 }
330
331
332
333
334 List compileSourceRoots = removeEmptyCompileSourceRoots( getCompileSourceRoots() );
335
336 if ( compileSourceRoots.isEmpty() )
337 {
338 getLog().info( "No sources to compile" );
339
340 return;
341 }
342
343 if ( getLog().isDebugEnabled() )
344 {
345 getLog().debug( "Source directories: " + compileSourceRoots.toString().replace( ',', '\n' ) );
346 getLog().debug( "Classpath: " + getClasspathElements().toString().replace( ',', '\n' ) );
347 getLog().debug( "Output directory: " + getOutputDirectory() );
348 }
349
350
351
352
353
354 CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
355
356 compilerConfiguration.setOutputLocation( getOutputDirectory().getAbsolutePath() );
357
358 compilerConfiguration.setClasspathEntries( getClasspathElements() );
359
360 compilerConfiguration.setSourceLocations( compileSourceRoots );
361
362 compilerConfiguration.setOptimize( optimize );
363
364 compilerConfiguration.setDebug( debug );
365
366 if ( debug && StringUtils.isNotEmpty( debuglevel ) )
367 {
368 String[] split = StringUtils.split( debuglevel, "," );
369 for ( int i = 0; i < split.length; i++ )
370 {
371 if ( !( split[i].equalsIgnoreCase( "none" ) || split[i].equalsIgnoreCase( "lines" )
372 || split[i].equalsIgnoreCase( "vars" ) || split[i].equalsIgnoreCase( "source" ) ) )
373 {
374 throw new IllegalArgumentException( "The specified debug level: '" + split[i]
375 + "' is unsupported. " + "Legal values are 'none', 'lines', 'vars', and 'source'." );
376 }
377 }
378 compilerConfiguration.setDebugLevel( debuglevel );
379 }
380
381 compilerConfiguration.setVerbose( verbose );
382
383 compilerConfiguration.setShowWarnings( showWarnings );
384
385 compilerConfiguration.setShowDeprecation( showDeprecation );
386
387 compilerConfiguration.setSourceVersion( getSource() );
388
389 compilerConfiguration.setTargetVersion( getTarget() );
390
391 compilerConfiguration.setSourceEncoding( encoding );
392
393 Map effectiveCompilerArguments = getCompilerArguments();
394
395 String effectiveCompilerArgument = getCompilerArgument();
396
397 if ( ( effectiveCompilerArguments != null ) || ( effectiveCompilerArgument != null ) )
398 {
399 LinkedHashMap cplrArgsCopy = new LinkedHashMap();
400 if ( effectiveCompilerArguments != null )
401 {
402 for ( Iterator i = effectiveCompilerArguments.entrySet().iterator(); i.hasNext(); )
403 {
404 Map.Entry me = (Map.Entry) i.next();
405 String key = (String) me.getKey();
406 String value = (String) me.getValue();
407 if ( !key.startsWith( "-" ) )
408 {
409 key = "-" + key;
410 }
411 cplrArgsCopy.put( key, value );
412 }
413 }
414 if ( !StringUtils.isEmpty( effectiveCompilerArgument ) )
415 {
416 cplrArgsCopy.put( effectiveCompilerArgument, null );
417 }
418 compilerConfiguration.setCustomCompilerArguments( cplrArgsCopy );
419 }
420
421 compilerConfiguration.setFork( fork );
422
423 if ( fork )
424 {
425 if ( !StringUtils.isEmpty( meminitial ) )
426 {
427 String value = getMemoryValue( meminitial );
428
429 if ( value != null )
430 {
431 compilerConfiguration.setMeminitial( value );
432 }
433 else
434 {
435 getLog().info( "Invalid value for meminitial '" + meminitial + "'. Ignoring this option." );
436 }
437 }
438
439 if ( !StringUtils.isEmpty( maxmem ) )
440 {
441 String value = getMemoryValue( maxmem );
442
443 if ( value != null )
444 {
445 compilerConfiguration.setMaxmem( value );
446 }
447 else
448 {
449 getLog().info( "Invalid value for maxmem '" + maxmem + "'. Ignoring this option." );
450 }
451 }
452 }
453
454 compilerConfiguration.setExecutable( executable );
455
456 compilerConfiguration.setWorkingDirectory( basedir );
457
458 compilerConfiguration.setCompilerVersion( compilerVersion );
459
460 compilerConfiguration.setBuildDirectory( buildDirectory );
461
462 compilerConfiguration.setOutputFileName( outputFileName );
463
464
465 Set staleSources;
466
467 boolean canUpdateTarget;
468
469 try
470 {
471 staleSources =
472 computeStaleSources( compilerConfiguration, compiler, getSourceInclusionScanner( staleMillis ) );
473
474 canUpdateTarget = compiler.canUpdateTarget( compilerConfiguration );
475
476 if ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES ) &&
477 !canUpdateTarget )
478 {
479 getLog().info( "RESCANNING!" );
480
481 String inputFileEnding = compiler.getInputFileEnding( compilerConfiguration );
482
483 Set sources = computeStaleSources( compilerConfiguration, compiler,
484 getSourceInclusionScanner( inputFileEnding ) );
485
486 compilerConfiguration.setSourceFiles( sources );
487 }
488 else
489 {
490 compilerConfiguration.setSourceFiles( staleSources );
491 }
492 }
493 catch ( CompilerException e )
494 {
495 throw new MojoExecutionException( "Error while computing stale sources.", e );
496 }
497
498 if ( staleSources.isEmpty() )
499 {
500 getLog().info( "Nothing to compile - all classes are up to date" );
501
502 return;
503 }
504
505
506
507
508
509 if ( getLog().isDebugEnabled() )
510 {
511 getLog().debug( "Classpath:" );
512
513 for ( Iterator it = getClasspathElements().iterator(); it.hasNext(); )
514 {
515 String s = (String) it.next();
516
517 getLog().debug( " " + s );
518 }
519
520 getLog().debug( "Source roots:" );
521
522 for ( Iterator it = getCompileSourceRoots().iterator(); it.hasNext(); )
523 {
524 String root = (String) it.next();
525
526 getLog().debug( " " + root );
527 }
528
529 if ( fork )
530 {
531 try
532 {
533 if ( compilerConfiguration.getExecutable() != null )
534 {
535 getLog().debug( "Excutable: " );
536 getLog().debug( " " + compilerConfiguration.getExecutable() );
537 }
538
539 String[] cl = compiler.createCommandLine( compilerConfiguration );
540 if ( cl != null && cl.length > 0 )
541 {
542 StringBuffer sb = new StringBuffer();
543 sb.append( cl[0] );
544 for ( int i = 1; i < cl.length; i++ )
545 {
546 sb.append( " " );
547 sb.append( cl[i] );
548 }
549 getLog().debug( "Command line options:" );
550 getLog().debug( sb );
551 }
552 }
553 catch ( CompilerException ce )
554 {
555 getLog().debug( ce );
556 }
557 }
558 }
559
560
561
562
563
564 if ( StringUtils.isEmpty( compilerConfiguration.getSourceEncoding() ) )
565 {
566 getLog().warn(
567 "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
568 + ", i.e. build is platform dependent!" );
569 }
570
571 List messages;
572
573 try
574 {
575 messages = compiler.compile( compilerConfiguration );
576 }
577 catch ( Exception e )
578 {
579
580 throw new MojoExecutionException( "Fatal error compiling", e );
581 }
582
583 boolean compilationError = false;
584
585 for ( Iterator i = messages.iterator(); i.hasNext(); )
586 {
587 CompilerError message = (CompilerError) i.next();
588
589 if ( message.isError() )
590 {
591 compilationError = true;
592 break;
593 }
594 }
595
596 if ( compilationError && failOnError )
597 {
598 getLog().info( "-------------------------------------------------------------" );
599 getLog().error( "COMPILATION ERROR : " );
600 getLog().info( "-------------------------------------------------------------" );
601 if ( messages != null )
602 {
603 for ( Iterator i = messages.iterator(); i.hasNext(); )
604 {
605 CompilerError message = (CompilerError) i.next();
606
607 getLog().error( message.toString() );
608
609 }
610 getLog().info( messages.size() + ( ( messages.size() > 1 ) ? " errors " : "error" ) );
611 getLog().info( "-------------------------------------------------------------" );
612 }
613 throw new CompilationFailureException( messages );
614 }
615 else
616 {
617 for ( Iterator i = messages.iterator(); i.hasNext(); )
618 {
619 CompilerError message = (CompilerError) i.next();
620
621 getLog().warn( message.toString() );
622 }
623 }
624 }
625
626 private String getMemoryValue( String setting )
627 {
628 String value = null;
629
630
631 if ( isDigits( setting ) )
632 {
633 value = setting + "m";
634 }
635 else
636 {
637 if ( ( isDigits( setting.substring( 0, setting.length() - 1 ) ) ) &&
638 ( setting.toLowerCase().endsWith( "m" ) ) )
639 {
640 value = setting;
641 }
642 }
643 return value;
644 }
645
646
647
648 private Toolchain getToolchain()
649 {
650 Toolchain tc = null;
651 if ( toolchainManager != null )
652 {
653 tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
654 }
655 return tc;
656 }
657
658 private boolean isDigits( String string )
659 {
660 for ( int i = 0; i < string.length(); i++ )
661 {
662 if ( !Character.isDigit( string.charAt( i ) ) )
663 {
664 return false;
665 }
666 }
667 return true;
668 }
669
670 private Set computeStaleSources( CompilerConfiguration compilerConfiguration, Compiler compiler,
671 SourceInclusionScanner scanner )
672 throws MojoExecutionException, CompilerException
673 {
674 CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
675
676 SourceMapping mapping;
677
678 File outputDirectory;
679
680 if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE )
681 {
682 mapping = new SuffixMapping( compiler.getInputFileEnding( compilerConfiguration ), compiler
683 .getOutputFileEnding( compilerConfiguration ) );
684
685 outputDirectory = getOutputDirectory();
686 }
687 else if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
688 {
689 mapping = new SingleTargetSourceMapping( compiler.getInputFileEnding( compilerConfiguration ), compiler
690 .getOutputFile( compilerConfiguration ) );
691
692 outputDirectory = buildDirectory;
693 }
694 else
695 {
696 throw new MojoExecutionException( "Unknown compiler output style: '" + outputStyle + "'." );
697 }
698
699 scanner.addSourceMapping( mapping );
700
701 Set staleSources = new HashSet();
702
703 for ( Iterator it = getCompileSourceRoots().iterator(); it.hasNext(); )
704 {
705 String sourceRoot = (String) it.next();
706
707 File rootFile = new File( sourceRoot );
708
709 if ( !rootFile.isDirectory() )
710 {
711 continue;
712 }
713
714 try
715 {
716 staleSources.addAll( scanner.getIncludedSources( rootFile, outputDirectory ) );
717 }
718 catch ( InclusionScanException e )
719 {
720 throw new MojoExecutionException(
721 "Error scanning source root: \'" + sourceRoot + "\' " + "for stale files to recompile.", e );
722 }
723 }
724
725 return staleSources;
726 }
727
728
729
730
731
732 private static List removeEmptyCompileSourceRoots( List compileSourceRootsList )
733 {
734 List newCompileSourceRootsList = new ArrayList();
735 if ( compileSourceRootsList != null )
736 {
737
738 for ( Iterator i = compileSourceRootsList.iterator(); i.hasNext(); )
739 {
740 String srcDir = (String) i.next();
741 if ( !newCompileSourceRootsList.contains( srcDir ) && new File( srcDir ).exists() )
742 {
743 newCompileSourceRootsList.add( srcDir );
744 }
745 }
746 }
747 return newCompileSourceRootsList;
748 }
749 }