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