1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package com.google.code.sbt;
19
20 import java.io.IOException;
21 import java.io.File;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30
31 import org.apache.maven.artifact.Artifact;
32 import org.apache.maven.artifact.factory.ArtifactFactory;
33 import org.apache.maven.artifact.repository.ArtifactRepository;
34 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
35 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
36 import org.apache.maven.artifact.resolver.ArtifactResolver;
37 import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
38 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
39 import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
40 import org.apache.maven.plugin.AbstractMojo;
41 import org.apache.maven.plugin.MojoExecutionException;
42 import org.apache.maven.plugin.MojoFailureException;
43 import org.apache.maven.plugins.annotations.Component;
44 import org.apache.maven.plugins.annotations.Parameter;
45 import org.apache.maven.project.MavenProject;
46 import org.apache.maven.project.MavenProjectBuilder;
47 import org.apache.maven.project.ProjectBuildingException;
48 import org.apache.maven.project.artifact.InvalidDependencyVersionException;
49
50 import org.codehaus.plexus.util.DirectoryScanner;
51
52 import scala.Option;
53
54 import com.typesafe.zinc.Compiler;
55 import com.typesafe.zinc.IncOptions;
56 import com.typesafe.zinc.Inputs;
57 import com.typesafe.zinc.Setup;
58
59
60
61
62
63
64 public abstract class AbstractSBTCompileMojo
65 extends AbstractMojo
66 {
67
68
69
70
71
72 public static final String DEFAULT_SCALA_VERSION = "2.10.2";
73
74
75
76
77 private static final String SCALA_GROUPID = "org.scala-lang";
78
79
80
81
82 private static final String SCALA_LIBRARY_ARTIFACTID = "scala-library";
83
84
85
86
87 private static final String SCALA_COMPILER_ARTIFACTID = "scala-compiler";
88
89
90
91
92 private static final String SBT_GROUP_ID = "com.typesafe.sbt";
93
94
95
96
97 private static final String COMPILER_INTERFACE_ARTIFACT_ID = "compiler-interface";
98
99
100
101
102 private static final String COMPILER_INTERFACE_CLASSIFIER = "sources";
103
104
105
106
107 private static final String XSBTI_ARTIFACT_ID = "sbt-interface";
108
109
110
111
112 private static final String COMPILE_ORDER = "mixed";
113
114
115
116
117 private static final boolean FORK_JAVA = false;
118
119
120
121
122
123
124
125
126
127
128 @Parameter( property = "scala.version" )
129 private String scalaVersion;
130
131
132
133
134
135
136 @Parameter( property = "sbt.version", defaultValue = "0.13.0" )
137 private String sbtVersion;
138
139
140
141
142
143
144 @Parameter( property = "project.build.sourceEncoding" )
145 protected String sourceEncoding;
146
147
148
149
150
151
152 @Parameter( property = "sbt.javacOptions", defaultValue = "-g" )
153 protected String javacOptions;
154
155
156
157
158
159
160 @Parameter( property = "sbt.scalacOptions", defaultValue = "-deprecation -unchecked" )
161 protected String scalacOptions;
162
163
164
165
166 @Component
167 protected MavenProject project;
168
169
170
171
172 @Component
173 protected MavenProjectBuilder mavenProjectBuilder;
174
175
176
177
178 @Parameter( defaultValue = "${reactorProjects}", required = true, readonly = true )
179 protected List<MavenProject> reactorProjects;
180
181
182
183
184 @Component
185 protected ArtifactFactory factory;
186
187
188
189
190 @Component
191 protected ArtifactResolver resolver;
192
193
194
195
196 @Parameter( property = "localRepository", readonly = true, required = true )
197 protected ArtifactRepository localRepo;
198
199
200
201
202 @Parameter( property = "project.remoteArtifactRepositories", readonly = true, required = true )
203 protected List<?> remoteRepos;
204
205
206
207
208
209
210
211
212
213 public void execute()
214 throws MojoExecutionException, MojoFailureException
215 {
216 if ( "pom".equals( project.getPackaging() ) )
217 {
218 return;
219 }
220
221 try
222 {
223 long ts = System.currentTimeMillis();
224 internalExecute();
225 long te = System.currentTimeMillis();
226 getLog().debug( String.format( "Mojo execution time: %d ms", te - ts ) );
227 }
228 catch ( IOException e )
229 {
230 throw new MojoExecutionException( "Scala compilation failed", e );
231 }
232 }
233
234
235
236
237
238
239
240
241 protected void internalExecute()
242 throws MojoExecutionException, MojoFailureException, IOException
243 {
244 List<String> compileSourceRoots = getCompileSourceRoots();
245
246 if ( compileSourceRoots.isEmpty() )
247 {
248 getLog().info( "No sources to compile" );
249
250 return;
251 }
252
253 List<File> sourceRootDirs = new ArrayList<File>( compileSourceRoots.size() );
254 for ( String compileSourceRoot : compileSourceRoots )
255 {
256 sourceRootDirs.add( new File( compileSourceRoot ) );
257 }
258
259 List<File> sourceFiles = getSourceFiles( sourceRootDirs );
260 if ( sourceFiles.isEmpty() )
261 {
262 getLog().info( "No sources to compile" );
263
264 return;
265 }
266
267 try
268 {
269 String resolvedScalaVersion = getScalaVersion();
270
271 Artifact scalaLibraryArtifact =
272 getResolvedArtifact( SCALA_GROUPID, SCALA_LIBRARY_ARTIFACTID, resolvedScalaVersion );
273 if ( scalaLibraryArtifact == null )
274 {
275 throw new MojoExecutionException(
276 String.format( "Required %s:%s:%s:jar artifact not found",
277 SCALA_GROUPID, SCALA_LIBRARY_ARTIFACTID,
278 resolvedScalaVersion ) );
279 }
280
281 Artifact scalaCompilerArtifact =
282 getResolvedArtifact( SCALA_GROUPID, SCALA_COMPILER_ARTIFACTID, resolvedScalaVersion );
283 if ( scalaCompilerArtifact == null )
284 {
285 throw new MojoExecutionException(
286 String.format( "Required %s:%s:%s:jar artifact not found",
287 SCALA_GROUPID, SCALA_COMPILER_ARTIFACTID,
288 resolvedScalaVersion ) );
289 }
290
291 List<File> scalaExtraJars = getCompilerDependencies( scalaCompilerArtifact );
292 scalaExtraJars.remove( scalaLibraryArtifact.getFile() );
293
294 Artifact xsbtiArtifact = getResolvedArtifact( SBT_GROUP_ID, XSBTI_ARTIFACT_ID, sbtVersion );
295 if ( xsbtiArtifact == null )
296 {
297 throw new MojoExecutionException( String.format( "Required %s:%s:%s:jar dependency not found",
298 SBT_GROUP_ID, XSBTI_ARTIFACT_ID, sbtVersion ) );
299 }
300
301 Artifact compilerInterfaceSrc =
302 getResolvedArtifact( SBT_GROUP_ID, COMPILER_INTERFACE_ARTIFACT_ID, sbtVersion,
303 COMPILER_INTERFACE_CLASSIFIER );
304 if ( compilerInterfaceSrc == null )
305 {
306 throw new MojoExecutionException( String.format( "Required %s:%s:%s:%s:jar dependency not found",
307 SBT_GROUP_ID, COMPILER_INTERFACE_ARTIFACT_ID,
308 sbtVersion, COMPILER_INTERFACE_CLASSIFIER ) );
309 }
310
311 List<String> classpathElements = getClasspathElements();
312 classpathElements.remove( getOutputDirectory().getAbsolutePath() );
313 List<File> classpathFiles = new ArrayList<File>( classpathElements.size() );
314 for ( String path : classpathElements )
315 {
316 classpathFiles.add( new File( path ) );
317 }
318
319 SBTLogger sbtLogger = new SBTLogger( getLog() );
320 Setup setup =
321 Setup.create( scalaCompilerArtifact.getFile(), scalaLibraryArtifact.getFile(), scalaExtraJars,
322 xsbtiArtifact.getFile(), compilerInterfaceSrc.getFile(), null, FORK_JAVA );
323 if ( getLog().isDebugEnabled() )
324 {
325 Setup.debug( setup, sbtLogger );
326 }
327 Compiler compiler = Compiler.create( setup, sbtLogger );
328
329 Inputs inputs =
330 Inputs.create( classpathFiles, sourceFiles, getOutputDirectory(), getScalacOptions(),
331 getJavacOptions(), getAnalysisCacheFile(), getAnalysisCacheMap(), COMPILE_ORDER,
332 getIncOptions(), getLog().isDebugEnabled()
333 if ( getLog().isDebugEnabled() )
334 {
335 Inputs.debug( inputs, sbtLogger );
336 }
337
338 compiler.compile( inputs, sbtLogger );
339 }
340 catch ( xsbti.CompileFailed e )
341 {
342 throw new MojoFailureException( "Scala compilation failed", e );
343 }
344 catch ( ArtifactNotFoundException e )
345 {
346 throw new MojoFailureException( "Scala compilation failed", e );
347 }
348 catch ( ArtifactResolutionException e )
349 {
350 throw new MojoFailureException( "Scala compilation failed", e );
351 }
352 catch ( InvalidDependencyVersionException e )
353 {
354 throw new MojoFailureException( "Scala compilation failed", e );
355 }
356 catch ( ProjectBuildingException e )
357 {
358 throw new MojoFailureException( "Scala compilation failed", e );
359 }
360 }
361
362
363
364
365
366
367 protected abstract List<String> getClasspathElements();
368
369
370
371
372
373
374 protected abstract List<String> getCompileSourceRoots();
375
376
377
378
379
380
381 protected abstract Set<String> getSourceIncludes();
382
383
384
385
386
387
388 protected abstract Set<String> getSourceExcludes();
389
390
391
392
393
394
395 protected abstract File getOutputDirectory();
396
397
398
399
400
401
402 protected abstract File getAnalysisCacheFile();
403
404
405
406
407
408
409 protected abstract Map<File, File> getAnalysisCacheMap();
410
411 private Artifact getDependencyArtifact( Collection<?> classPathArtifacts, String groupId, String artifactId,
412 String type )
413 {
414 Artifact result = null;
415 for ( Iterator<?> iter = classPathArtifacts.iterator(); iter.hasNext(); )
416 {
417 Artifact artifact = (Artifact) iter.next();
418 if ( groupId.equals( artifact.getGroupId() ) && artifactId.equals( artifact.getArtifactId() )
419 && type.equals( artifact.getType() ) )
420 {
421 result = artifact;
422 break;
423 }
424 }
425 return result;
426 }
427
428 private List<File> getSourceFiles( List<File> sourceRootDirs )
429 {
430 List<File> sourceFiles = new ArrayList<File>();
431
432 Set<String> sourceIncludes = getSourceIncludes();
433 if ( sourceIncludes.isEmpty() )
434 {
435 sourceIncludes.add( "**/*.java" );
436 sourceIncludes.add( "**/*.scala" );
437 }
438 Set<String> sourceExcludes = getSourceExcludes();
439
440 DirectoryScanner scanner = new DirectoryScanner();
441 scanner.setIncludes( sourceIncludes.toArray( new String[sourceIncludes.size()] ) );
442 if ( !sourceExcludes.isEmpty() )
443 {
444 scanner.setExcludes( sourceExcludes.toArray( new String[sourceExcludes.size()] ) );
445 }
446 scanner.addDefaultExcludes();
447
448 for ( File dir : sourceRootDirs )
449 {
450 if ( dir.isDirectory() )
451 {
452 scanner.setBasedir( dir );
453 scanner.scan();
454 String[] includedFileNames = scanner.getIncludedFiles();
455 for ( String includedFileName : includedFileNames )
456 {
457 File tmpAbsFile = new File( dir, includedFileName ).getAbsoluteFile();
458 sourceFiles.add( tmpAbsFile );
459 }
460 }
461 }
462
463
464
465
466 return sourceFiles;
467 }
468
469 private String getScalaVersion()
470 {
471 String result = scalaVersion;
472 if ( result == null || result.length() == 0 )
473 {
474 result = DEFAULT_SCALA_VERSION;
475 Artifact scalaLibraryArtifact =
476 getDependencyArtifact( project.getArtifacts(), SCALA_GROUPID, SCALA_LIBRARY_ARTIFACTID, "jar" );
477 if ( scalaLibraryArtifact != null )
478 {
479 result = scalaLibraryArtifact.getVersion();
480 }
481 }
482 return result;
483 }
484
485 private List<String> getScalacOptions()
486 {
487 List<String> result = new ArrayList<String>( Arrays.asList( scalacOptions.split( " " ) ) );
488 if ( !result.contains( "-encoding" ) && sourceEncoding != null && sourceEncoding.length() > 0 )
489 {
490 result.add( "-encoding" );
491 result.add( sourceEncoding );
492 }
493 return result;
494 }
495
496 private List<String> getJavacOptions()
497 {
498 List<String> result = new ArrayList<String>( Arrays.asList( javacOptions.split( " " ) ) );
499 if ( !result.contains( "-encoding" ) && sourceEncoding != null && sourceEncoding.length() > 0 )
500 {
501 result.add( "-encoding" );
502 result.add( sourceEncoding );
503 }
504 return result;
505 }
506
507 private File defaultAnalysisDirectory( MavenProject p )
508 {
509 return new File( p.getBuild().getDirectory(), "cache" );
510 }
511
512
513
514
515
516
517
518 protected File defaultAnalysisCacheFile( MavenProject p )
519 {
520 return new File( defaultAnalysisDirectory( p ), "compile" );
521 }
522
523
524
525
526
527
528
529 protected File defaultTestAnalysisCacheFile( MavenProject p )
530 {
531 return new File( defaultAnalysisDirectory( p ), "test-compile" );
532 }
533
534 private IncOptions getIncOptions()
535 {
536
537
538
539
540
541 int transitiveStep = 3;
542
543
544
545
546
547
548
549
550
551
552 double recompileAllFraction = 0.5d;
553
554
555
556
557
558
559 boolean relationsDebug = false;
560
561
562
563
564
565
566
567
568
569
570 boolean apiDebug = false;
571
572
573
574
575
576
577
578
579
580 int apiDiffContextSize = 5;
581
582
583
584
585
586
587
588
589 Option<File> apiDumpDirectory = Option.empty();
590
591
592
593 boolean transactional = false;
594
595
596
597 Option<File> backup = Option.empty();
598
599 return new IncOptions( transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, transactional, backup );
600 }
601
602
603
604 private Artifact getResolvedArtifact( String groupId, String artifactId, String version )
605 throws ArtifactNotFoundException, ArtifactResolutionException
606 {
607 Artifact artifact = factory.createArtifact( groupId, artifactId, version, Artifact.SCOPE_RUNTIME, "jar" );
608 resolver.resolve( artifact, remoteRepos, localRepo );
609 return artifact;
610 }
611
612 private Artifact getResolvedArtifact( String groupId, String artifactId, String version, String classifier )
613 throws ArtifactNotFoundException, ArtifactResolutionException
614 {
615 Artifact artifact = factory.createArtifactWithClassifier( groupId, artifactId, version, "jar", classifier );
616 resolver.resolve( artifact, remoteRepos, localRepo );
617 return artifact;
618 }
619
620 private List<File> getCompilerDependencies( Artifact scalaCompilerArtifact )
621 throws ArtifactNotFoundException, ArtifactResolutionException, InvalidDependencyVersionException,
622 ProjectBuildingException
623 {
624 List<File> d = new ArrayList<File>();
625 for ( Artifact artifact : getAllDependencies( scalaCompilerArtifact ) )
626 {
627 d.add( artifact.getFile() );
628 }
629 return d;
630 }
631
632 private Set<Artifact> getAllDependencies( Artifact artifact )
633 throws ArtifactNotFoundException, ArtifactResolutionException, InvalidDependencyVersionException,
634 ProjectBuildingException
635 {
636 Set<Artifact> result = new HashSet<Artifact>();
637 MavenProject p = mavenProjectBuilder.buildFromRepository( artifact, remoteRepos, localRepo );
638 Set<Artifact> d = resolveDependencyArtifacts( p );
639 result.addAll( d );
640 for ( Artifact dependency : d )
641 {
642 Set<Artifact> transitive = getAllDependencies( dependency );
643 result.addAll( transitive );
644 }
645 return result;
646 }
647
648
649
650
651
652
653
654
655
656
657 private Set<Artifact> resolveDependencyArtifacts( MavenProject theProject )
658 throws ArtifactNotFoundException, ArtifactResolutionException, InvalidDependencyVersionException
659 {
660 AndArtifactFilter filter = new AndArtifactFilter();
661 filter.add( new ScopeArtifactFilter( Artifact.SCOPE_TEST ) );
662 filter.add( new ArtifactFilter()
663 {
664 public boolean include( Artifact artifact )
665 {
666 return !artifact.isOptional();
667 }
668 } );
669
670 Set<Artifact> artifacts = theProject.createArtifacts( factory, Artifact.SCOPE_RUNTIME, filter );
671 for ( Artifact artifact : artifacts )
672 {
673 resolver.resolve( artifact, remoteRepos, localRepo );
674 }
675 return artifacts;
676 }
677
678 }