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