Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2013-2014 Grzegorz Slowikowski (gslowikowski at gmail dot com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.google.code.sbt.compiler.plugin;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.artifact.InvalidDependencyVersionException;
import org.codehaus.plexus.util.DirectoryScanner;
import com.google.code.sbt.compiler.api.Compiler;
import com.google.code.sbt.compiler.api.CompilerConfiguration;
import com.google.code.sbt.compiler.api.CompilerException;
import com.google.code.sbt.compiler.api.Compilers;
/**
* Abstract base class for SBT compilation mojos.
*
* @author Grzegorz Slowikowski
*/
public abstract class AbstractSBTCompileMojo
extends AbstractMojo
{
/**
* Scala artifacts "groupId".
*/
private static final String SCALA_GROUPID = "org.scala-lang";
/**
* Scala library "artifactId".
*/
private static final String SCALA_LIBRARY_ARTIFACTID = "scala-library";
/**
* Scala compiler "artifactId".
*/
private static final String SCALA_COMPILER_ARTIFACTID = "scala-compiler";
/**
* SBT artifacts "groupId".
*/
private static final String SBT_GROUP_ID = "com.typesafe.sbt";
/**
* SBT compile interface "artifactId".
*/
private static final String COMPILER_INTERFACE_ARTIFACT_ID = "compiler-interface";
/**
* SBT compile interface sources "classifier".
*/
private static final String COMPILER_INTERFACE_CLASSIFIER = "sources";
/**
* SBT interface "artifactId".
*/
private static final String XSBTI_ARTIFACT_ID = "sbt-interface";
/**
* Forced Scala version.
*
* If specified, this version of Scala compiler is used for compilation.
* If not specified, version of project's {@code org.scala-lang:scala-library} dependency is used.
* If there is no {@code org.scala-lang:scala-library} dependency in the project (in Java only projects),
* selected compiler's {@link Compiler#getDefaultScalaVersion()} is used.
*
* @since 1.0.0
*/
@Parameter( property = "scala.version" )
protected String scalaVersion;
/**
* Forced SBT version.
*
* Used to automatically select one of the "well known" SBT compilers if no compiler added explicitly as plugin's dependency.
* There are three cases possible:
*
*
* If {@link #sbtVersion} is specified, compatible {@link Compiler} implementation
* is selected and configured to use {@link #sbtVersion} SBT version for compilation.
*
*
* If {@link #sbtVersion} is not specified, and {@link #playVersion} is specified
* {@link #playVersion} parameter value is used to indirectly select compatible {@link Compiler} implementation
* and it's {@link Compiler#getDefaultSbtVersion()} SBT version used for compilation.
*
*
* If both {@link #sbtVersion} and {@link #playVersion} are not specified
* the most recent {@link Compiler} implementation is selected
* and it's {@link Compiler#getDefaultSbtVersion()} SBT version used for compilation.
*
*
*
* @since 1.0.0
*/
@Parameter( property = "sbt.version" )
protected String sbtVersion;
/**
* Forced Play! Framework version.
*
*
* Used to select SBT version in Play! Framework projects based on framework version used.
* See {@link #sbtVersion} for more details.
*
* @since 1.0.0
*/
@Parameter( property = "play2.version" )
protected String playVersion;
/**
* Scala and Java source files encoding.
*
* @since 1.0.0
*/
@Parameter( property = "project.build.sourceEncoding" )
protected String sourceEncoding;
/**
* Additional parameters for Java compiler.
*
* @since 1.0.0
*/
@Parameter( property = "sbt.javacOptions", defaultValue = "-g" )
protected String javacOptions;
/**
* Additional parameters for Scala compiler.
*
* @since 1.0.0
*/
@Parameter( property = "sbt.scalacOptions", defaultValue = "-deprecation -unchecked" )
protected String scalacOptions;
/**
* Maven project to interact with.
*/
@Parameter( defaultValue = "${project}", readonly = true )
protected MavenProject project;
/**
* Maven project builder used to resolve artifacts.
*/
@Component
protected MavenProjectBuilder mavenProjectBuilder;
/**
* All Maven projects in the reactor.
*/
@Parameter( defaultValue = "${reactorProjects}", required = true, readonly = true )
protected List reactorProjects;
/**
* Artifact factory used to look up artifacts in the remote repository.
*/
@Component
protected ArtifactFactory factory;
/**
* Artifact resolver used to resolve artifacts.
*/
@Component
protected ArtifactResolver resolver;
/**
* Location of the local repository.
*/
@Parameter( property = "localRepository", readonly = true, required = true )
protected ArtifactRepository localRepo;
/**
* Remote repositories used by the resolver
*/
@Parameter( property = "project.remoteArtifactRepositories", readonly = true, required = true )
protected List remoteRepos;
/**
* Plugin descriptor used to retrieve this plugin's properties.
*/
@Parameter( defaultValue = "${plugin}", readonly = true, required = true )
private PluginDescriptor plugin;
/**
* Map of compiler implementations. For now only zero or one allowed.
*/
@Component( role = Compiler.class )
private Map compilers;
/**
* Performs compilation.
*
* @throws MojoExecutionException if unexpected problem occurs
* @throws MojoFailureException if expected problem (such as compilation failure) occurs
*/
@Override
public void execute()
throws MojoExecutionException, MojoFailureException
{
if ( !"pom".equals( project.getPackaging() ) )
{
long ts = System.currentTimeMillis();
internalExecute();
long te = System.currentTimeMillis();
getLog().debug( String.format( "Mojo execution time: %d ms", te - ts ) );
}
}
/**
* Performs compilation.
*
* @throws MojoExecutionException if unexpected problem occurs
* @throws MojoFailureException if expected problem (such as compilation failure) occurs
*/
protected void internalExecute()
throws MojoExecutionException, MojoFailureException
{
List compileSourceRoots = getCompileSourceRoots();
if ( compileSourceRoots.isEmpty() )// ?
{
getLog().info( "No sources to compile" );
return;
}
List sourceRootDirs = new ArrayList( compileSourceRoots.size() );
for ( String compileSourceRoot : compileSourceRoots )
{
sourceRootDirs.add( new File( compileSourceRoot ) );
}
List sourceFiles = getSourceFiles( sourceRootDirs );
if ( sourceFiles.isEmpty() )
{
getLog().info( "No sources to compile" );
return;
}
try
{
Compiler sbtCompiler = getSbtCompiler();
String resolvedScalaVersion = getScalaVersion( sbtCompiler );
Artifact scalaLibraryArtifact =
getResolvedArtifact( SCALA_GROUPID, SCALA_LIBRARY_ARTIFACTID, resolvedScalaVersion );
if ( scalaLibraryArtifact == null )
{
throw new MojoExecutionException( String.format( "Required %s:%s:%s:jar artifact not found",
SCALA_GROUPID, SCALA_LIBRARY_ARTIFACTID,
resolvedScalaVersion ) );
}
Artifact scalaCompilerArtifact =
getResolvedArtifact( SCALA_GROUPID, SCALA_COMPILER_ARTIFACTID, resolvedScalaVersion );
if ( scalaCompilerArtifact == null )
{
throw new MojoExecutionException( String.format( "Required %s:%s:%s:jar artifact not found",
SCALA_GROUPID, SCALA_COMPILER_ARTIFACTID,
resolvedScalaVersion ) );
}
List scalaExtraJars = getCompilerDependencies( scalaCompilerArtifact );
scalaExtraJars.remove( scalaLibraryArtifact.getFile() );
String resolvedSbtVersion = getSbtVersion( sbtCompiler );
Artifact xsbtiArtifact = getResolvedArtifact( SBT_GROUP_ID, XSBTI_ARTIFACT_ID, resolvedSbtVersion );
if ( xsbtiArtifact == null )
{
throw new MojoExecutionException( String.format( "Required %s:%s:%s:jar dependency not found",
SBT_GROUP_ID, XSBTI_ARTIFACT_ID, resolvedSbtVersion ) );
}
Artifact compilerInterfaceSrc =
getResolvedArtifact( SBT_GROUP_ID, COMPILER_INTERFACE_ARTIFACT_ID, resolvedSbtVersion,
COMPILER_INTERFACE_CLASSIFIER );
if ( compilerInterfaceSrc == null )
{
throw new MojoExecutionException( String.format( "Required %s:%s:%s:%s:jar dependency not found",
SBT_GROUP_ID, COMPILER_INTERFACE_ARTIFACT_ID,
resolvedSbtVersion, COMPILER_INTERFACE_CLASSIFIER ) );
}
List classpathElements = getClasspathElements();
classpathElements.remove( getOutputDirectory().getAbsolutePath() );
List classpathFiles = new ArrayList( classpathElements.size() );
for ( String path : classpathElements )
{
classpathFiles.add( new File( path ) );
}
CompilerConfiguration configuration = new CompilerConfiguration();
configuration.setSourceFiles( sourceFiles );
configuration.setScalaLibraryFile( scalaLibraryArtifact.getFile() );
configuration.setScalaCompilerFile( scalaCompilerArtifact.getFile() );
configuration.setScalaExtraJarFiles( scalaExtraJars );
configuration.setXsbtiFile( xsbtiArtifact.getFile() );
configuration.setCompilerInterfaceSrcFile( compilerInterfaceSrc.getFile() );
configuration.setClasspathFiles( classpathFiles );
configuration.setLogger( new MavenCompilerLogger( getLog() ) );
configuration.setOutputDirectory( getOutputDirectory() );
configuration.setSourceEncoding( sourceEncoding );
configuration.setJavacOptions( javacOptions );
configuration.setScalacOptions( scalacOptions );
configuration.setAnalysisCacheFile( getAnalysisCacheFile() );
configuration.setAnalysisCacheMap( getAnalysisCacheMap() );
sbtCompiler.performCompile( configuration );
}
catch ( CompilerException e )
{
throw new MojoFailureException( "Scala compilation failed", e );
}
catch ( ArtifactNotFoundException e )
{
throw new MojoExecutionException( "Scala compilation failed", e );
}
catch ( ArtifactResolutionException e )
{
throw new MojoExecutionException( "Scala compilation failed", e );
}
catch ( InvalidDependencyVersionException e )
{
throw new MojoExecutionException( "Scala compilation failed", e );
}
catch ( ProjectBuildingException e )
{
throw new MojoExecutionException( "Scala compilation failed", e );
}
}
/**
* Returns compilation classpath elements.
*
* @return classpath elements
*/
protected abstract List getClasspathElements();
/**
* Returns compilation source roots.
*
* @return source roots
*/
protected abstract List getCompileSourceRoots();
/**
* Returns source inclusion filters for the compiler.
*
* @return source inclusion filters
*/
protected abstract Set getSourceIncludes();
/**
* Returns source exclusion filters for the compiler.
*
* @return source exclusion filters
*/
protected abstract Set getSourceExcludes();
/**
* Returns output directory.
*
* @return output directory
*/
protected abstract File getOutputDirectory();
/**
* Returns SBT incremental compilation analysis cache file location for this project.
*
* @return analysis cache file
*/
protected abstract File getAnalysisCacheFile();
/**
* Returns SBT incremental compilation analysis cache file locations map for all reactor projects.
*
* @return analysis cache files map
*/
protected abstract Map getAnalysisCacheMap();
private Artifact getDependencyArtifact( Collection> classPathArtifacts, String groupId, String artifactId,
String type )
{
Artifact result = null;
for ( Iterator> iter = classPathArtifacts.iterator(); iter.hasNext(); )
{
Artifact artifact = (Artifact) iter.next();
if ( groupId.equals( artifact.getGroupId() ) && artifactId.equals( artifact.getArtifactId() )
&& type.equals( artifact.getType() ) )
{
result = artifact;
break;
}
}
return result;
}
private List getSourceFiles( List sourceRootDirs )
{
List sourceFiles = new ArrayList();
Set sourceIncludes = getSourceIncludes();
if ( sourceIncludes.isEmpty() )
{
sourceIncludes.add( "**/*.java" );
sourceIncludes.add( "**/*.scala" );
}
Set sourceExcludes = getSourceExcludes();
DirectoryScanner scanner = new DirectoryScanner();
scanner.setIncludes( sourceIncludes.toArray( new String[sourceIncludes.size()] ) );
if ( !sourceExcludes.isEmpty() )
{
scanner.setExcludes( sourceExcludes.toArray( new String[sourceExcludes.size()] ) );
}
scanner.addDefaultExcludes();
for ( File dir : sourceRootDirs )
{
if ( dir.isDirectory() )
{
scanner.setBasedir( dir );
scanner.scan();
String[] includedFileNames = scanner.getIncludedFiles();
for ( String includedFileName : includedFileNames )
{
File tmpAbsFile = new File( dir, includedFileName ).getAbsoluteFile(); // ?
sourceFiles.add( tmpAbsFile );
}
}
}
// scalac is sensible to scala file order, file system can't guarantee file order => unreproductible build error
// across platform
// to guarantee reproductible command line we order file by path (os dependend).
// Collections.sort( sourceFiles );
return sourceFiles;
}
private String getScalaVersion( Compiler sbtCompiler )
{
String result = scalaVersion;
if ( result == null || result.length() == 0 )
{
Artifact scalaLibraryArtifact =
getDependencyArtifact( project.getArtifacts(), SCALA_GROUPID, SCALA_LIBRARY_ARTIFACTID, "jar" );
if ( scalaLibraryArtifact != null )
{
result = scalaLibraryArtifact.getVersion();
}
else
{
result = sbtCompiler.getDefaultScalaVersion();
}
}
return result;
}
private String getSbtVersion( Compiler sbtCompiler )
{
String result = sbtVersion;
if ( result == null || result.length() == 0 )
{
result = sbtCompiler.getDefaultSbtVersion();
}
return result;
}
private File defaultAnalysisDirectory( MavenProject p )
{
return new File( p.getBuild().getDirectory(), "cache" );
}
/**
* Returns SBT incremental main compilation analysis cache file location for a project.
*
* @param p Maven project
* @return analysis cache file location
*/
protected File defaultAnalysisCacheFile( MavenProject p )
{
return new File( defaultAnalysisDirectory( p ), "compile" );
}
/**
* Returns SBT incremental test compilation analysis cache file location for a project.
*
* @param p Maven project
* @return analysis cache file location
*/
protected File defaultTestAnalysisCacheFile( MavenProject p )
{
return new File( defaultAnalysisDirectory( p ), "test-compile" );
}
// Private utility methods
private Artifact getResolvedArtifact( String groupId, String artifactId, String version )
throws ArtifactNotFoundException, ArtifactResolutionException
{
Artifact artifact = factory.createArtifact( groupId, artifactId, version, Artifact.SCOPE_RUNTIME, "jar" );
resolver.resolve( artifact, remoteRepos, localRepo );
return artifact;
}
private Artifact getResolvedArtifact( String groupId, String artifactId, String version, String classifier )
throws ArtifactNotFoundException, ArtifactResolutionException
{
Artifact artifact = factory.createArtifactWithClassifier( groupId, artifactId, version, "jar", classifier );
resolver.resolve( artifact, remoteRepos, localRepo );
return artifact;
}
private List getCompilerDependencies( Artifact scalaCompilerArtifact )
throws ArtifactNotFoundException, ArtifactResolutionException, InvalidDependencyVersionException,
ProjectBuildingException
{
List d = new ArrayList();
for ( Artifact artifact : getAllDependencies( scalaCompilerArtifact ) )
{
d.add( artifact.getFile() );
}
return d;
}
private Set getAllDependencies( Artifact artifact )
throws ArtifactNotFoundException, ArtifactResolutionException, InvalidDependencyVersionException,
ProjectBuildingException
{
Set result = new HashSet();
MavenProject p = mavenProjectBuilder.buildFromRepository( artifact, remoteRepos, localRepo );
Set d = resolveDependencyArtifacts( p );
result.addAll( d );
for ( Artifact dependency : d )
{
Set transitive = getAllDependencies( dependency );
result.addAll( transitive );
}
return result;
}
/**
* This method resolves the dependency artifacts from the project.
*
* @param theProject The POM.
* @return resolved set of dependency artifacts.
* @throws ArtifactResolutionException
* @throws ArtifactNotFoundException
* @throws InvalidDependencyVersionException
*/
private Set resolveDependencyArtifacts( MavenProject theProject )
throws ArtifactNotFoundException, ArtifactResolutionException, InvalidDependencyVersionException
{
AndArtifactFilter filter = new AndArtifactFilter();
filter.add( new ScopeArtifactFilter( Artifact.SCOPE_TEST ) );
filter.add( new NonOptionalArtifactFilter() );
// TODO follow the dependenciesManagement and override rules
Set artifacts = theProject.createArtifacts( factory, Artifact.SCOPE_RUNTIME, filter );
for ( Artifact artifact : artifacts )
{
resolver.resolve( artifact, remoteRepos, localRepo );
}
return artifacts;
}
// Cached classloaders
private static final ConcurrentHashMap cachedClassLoaders = new ConcurrentHashMap( 2 );
private static ClassLoader getCachedClassLoader( String compilerId )
{
return cachedClassLoaders.get( compilerId );
}
private static void setCachedClassLoader( String compilerId, ClassLoader classLoader )
{
cachedClassLoaders.put( compilerId, classLoader );
}
private Compiler getSbtCompiler()
throws MojoExecutionException
{
Compiler sbtCompiler = null;
if ( !compilers.isEmpty() )
{
sbtCompiler = getDeclaredSbtCompiler();
}
else
{
sbtCompiler = getWellKnownSbtCompiler();
}
return sbtCompiler;
}
private Compiler getDeclaredSbtCompiler()
throws MojoExecutionException
{
if ( compilers.size() > 1 )
{
throw new MojoExecutionException( "Too many compiles defined. A maximum of one allowed." );
}
Map.Entry compilerEntry = compilers.entrySet().iterator().next();
String compilerId = compilerEntry.getKey();
Compiler sbtCompiler = compilerEntry.getValue();
getLog().debug( String.format( "Using declared compiler \"%s\".", compilerId ) );
return sbtCompiler;
}
private Compiler getWellKnownSbtCompiler()
throws MojoExecutionException
{
try
{
String compilerId = Compilers.getDefaultCompilerId( sbtVersion, playVersion );
ClassLoader compilerClassLoader = getCachedClassLoader( compilerId );
if ( compilerClassLoader == null )
{
getLog().debug( String.format( "Cached classloader for compiler \"%s\" not available.", compilerId ) );
}
else
{
if ( compilerClassLoader.getParent() == Thread.currentThread().getContextClassLoader() )
{
getLog().debug( String.format( "Using cached classloader for compiler \"%s\".", compilerId ) );
}
else
{
getLog().debug( String.format( "Invalidated cached classloader for compiler \"%s\". Parent classloader changed from %d to %d.",
compilerId,
Integer.valueOf( compilerClassLoader.getParent().hashCode() ),
Integer.valueOf( Thread.currentThread().getContextClassLoader().hashCode() ) ) );
compilerClassLoader = null;
}
}
if ( compilerClassLoader == null )
{
Artifact compilerArtifact =
getResolvedArtifact( plugin.getGroupId(), "sbt-compiler-" + compilerId, plugin.getVersion() );
Set compilerDependencies = getAllDependencies( compilerArtifact );
List classPathFiles = new ArrayList( compilerDependencies.size() + 2 );
classPathFiles.add( compilerArtifact.getFile() );
for ( Artifact dependencyArtifact : compilerDependencies )
{
classPathFiles.add( dependencyArtifact.getFile() );
}
String javaHome = System.getProperty( "java.home" );
classPathFiles.add( new File( javaHome, "../lib/tools.jar" ) );
List classPathUrls = new ArrayList( classPathFiles.size() );
for ( File classPathFile : classPathFiles )
{
classPathUrls.add( new URL( classPathFile.toURI().toASCIIString() ) );
}
compilerClassLoader =
new URLClassLoader( classPathUrls.toArray( new URL[classPathUrls.size()] ),
Thread.currentThread().getContextClassLoader() );
getLog().debug( String.format( "Setting cached classloader for compiler \"%s\" with parent classloader %d",
compilerId, Integer.valueOf( compilerClassLoader.getParent().hashCode() ) ) );
setCachedClassLoader( compilerId, compilerClassLoader );
}
String compilerClassName =
String.format( "com.google.code.sbt.compiler.%s.%sCompiler", compilerId,
compilerId.toUpperCase( Locale.ROOT ) );
Compiler sbtCompiler = (Compiler) compilerClassLoader.loadClass( compilerClassName ).newInstance();
getLog().debug( String.format( "Using autodetected compiler \"%s\".", compilerId ) );
return sbtCompiler;
}
catch ( ArtifactNotFoundException e )
{
throw new MojoExecutionException( "Compiler autodetection failed", e );
}
catch ( ArtifactResolutionException e )
{
throw new MojoExecutionException( "Compiler autodetection failed", e );
}
catch ( ClassNotFoundException e )
{
throw new MojoExecutionException( "Compiler autodetection failed", e );
}
catch ( IllegalAccessException e )
{
throw new MojoExecutionException( "Compiler autodetection failed", e );
}
catch ( InstantiationException e )
{
throw new MojoExecutionException( "Compiler autodetection failed", e );
}
catch ( InvalidDependencyVersionException e )
{
throw new MojoExecutionException( "Compiler autodetection failed", e );
}
catch ( MalformedURLException e )
{
throw new MojoExecutionException( "Compiler autodetection failed", e );
}
catch ( ProjectBuildingException e )
{
throw new MojoExecutionException( "Compiler autodetection failed", e );
}
}
private static class NonOptionalArtifactFilter
implements ArtifactFilter
{
public boolean include( Artifact artifact )
{
return !artifact.isOptional();
}
}
}