Please wait. This can take some minutes ...
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.
org.scoverage.plugin.SCoveragePreCompileMojo Maven / Gradle / Ivy
/*
* Copyright 2014-2019 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 org.scoverage.plugin;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
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.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.StringUtils;
/**
* Configures project for compilation with SCoverage instrumentation.
*
*
* Supported compiler plugins:
*
*
* This is internal mojo, executed in forked {@code scoverage} life cycle.
*
*
* @author Grzegorz Slowikowski
* @since 1.0.0
*/
@Mojo( name = "pre-compile", defaultPhase = LifecyclePhase.GENERATE_RESOURCES )
public class SCoveragePreCompileMojo
extends AbstractMojo
{
/**
* Allows SCoverage to be skipped.
*
*
* @since 1.0.0
*/
@Parameter( property = "scoverage.skip", defaultValue = "false" )
private boolean skip;
/**
* Scala version used for compiler plugin artifact resolution.
*
* if specified, and equals {@code 2.10} or starts with {@code 2.10.} - {@code scalac-scoverage-plugin_2.10} will be used
* if specified, and equals {@code 2.11} or starts with {@code 2.11.} - {@code scalac-scoverage-plugin_2.11} will be used
* if specified, and equals {@code 2.12} or starts with {@code 2.12.} - {@code scalac-scoverage-plugin_2.12} will be used
* if specified, and equals {@code 2.13.0-M5} - {@code scalac-scoverage-plugin_2.13.0-M5} will be used
* if specified, but does not meet any of the above conditions or if not specified - plugin execution will be skipped
*
*
* @since 1.0.0
*/
@Parameter( property = "scala.version" )
private String scalaVersion;
/**
* Directory where the coverage files should be written.
*
*
* @since 1.0.0
*/
@Parameter( property = "scoverage.dataDirectory", defaultValue = "${project.build.directory}/scoverage-data", required = true, readonly = true )
private File dataDirectory;
/**
* Semicolon-separated list of regular expressions for packages to exclude, "(empty)" for default package.
*
*
* Example:
*
* {@code (empty);Reverse.*;.*AuthService.*;models\.data\..*}
*
*
* See https://github.com/scoverage/sbt-scoverage#exclude-classes-and-packages for additional documentation.
*
*
* @since 1.0.0
*/
@Parameter( property = "scoverage.excludedPackages", defaultValue = "" )
private String excludedPackages;
/**
* Semicolon-separated list of regular expressions for source paths to exclude.
*
*
* @since 1.0.0
*/
@Parameter( property = "scoverage.excludedFiles", defaultValue = "" )
private String excludedFiles;
/**
* See https://github.com/scoverage/sbt-scoverage#highlighting .
*
*
* @since 1.0.0
*/
@Parameter( property = "scoverage.highlighting", defaultValue = "false" )
private boolean highlighting;
/**
* Force scalac-scoverage-plugin version used.
*
*
* @since 1.0.0
*/
@Parameter( property = "scoverage.scalacPluginVersion", defaultValue = "" )
private String scalacPluginVersion;
/**
* Semicolon-separated list of project properties set in forked {@code scoverage} life cycle.
*
*
* Example:
*
* {@code prop1=val1;prop2=val2;prop3=val3}
*
*
* @since 1.4.0
*/
@Parameter( property = "scoverage.additionalForkedProjectProperties", defaultValue = "" )
private String additionalForkedProjectProperties;
/**
* Maven project to interact with.
*/
@Parameter( defaultValue = "${project}", readonly = true, required = true )
private MavenProject project;
/**
* All Maven projects in the reactor.
*/
@Parameter( defaultValue = "${reactorProjects}", required = true, readonly = true )
private List reactorProjects;
/**
* Artifact factory used to look up artifacts in the remote repository.
*/
@Component
private ArtifactFactory factory;
/**
* Artifact resolver used to resolve artifacts.
*/
@Component
private ArtifactResolver resolver;
/**
* Location of the local repository.
*/
@Parameter( property = "localRepository", readonly = true, required = true )
private ArtifactRepository localRepo;
/**
* Remote repositories used by the resolver
*/
@Parameter( property = "project.remoteArtifactRepositories", readonly = true, required = true )
private List remoteRepos;
/**
* List of artifacts this plugin depends on.
*/
@Parameter( property = "plugin.artifacts", readonly = true, required = true )
private List pluginArtifacts;
/**
* Configures project for compilation with SCoverage instrumentation.
*
* @throws MojoExecutionException if unexpected problem occurs
*/
@Override
public void execute() throws MojoExecutionException
{
if ( "pom".equals( project.getPackaging() ) )
{
getLog().info( "Skipping SCoverage execution for project with packaging type 'pom'" );
//for aggragetor mojo - list of submodules: List modules = project.getCollectedProjects();
return;
}
if ( skip )
{
getLog().info( "Skipping Scoverage execution" );
Properties projectProperties = project.getProperties();
// for maven-resources-plugin (testResources), maven-compiler-plugin (testCompile),
// sbt-compiler-maven-plugin (testCompile), scala-maven-plugin (testCompile),
// maven-surefire-plugin and scalatest-maven-plugin
setProperty( projectProperties, "maven.test.skip", "true" );
// for scalatest-maven-plugin and specs2-maven-plugin
setProperty( projectProperties, "skipTests", "true" );
return;
}
long ts = System.currentTimeMillis();
String scalaBinaryVersion = null;
String resolvedScalaVersion = resolveScalaVersion();
if ( resolvedScalaVersion != null )
{
if ( "2.10".equals( resolvedScalaVersion ) || resolvedScalaVersion.startsWith( "2.10." ) )
{
scalaBinaryVersion = "2.10";
}
else if ( "2.11".equals( resolvedScalaVersion ) || resolvedScalaVersion.startsWith( "2.11." ) )
{
scalaBinaryVersion = "2.11";
}
else if ( "2.12".equals( resolvedScalaVersion ) || resolvedScalaVersion.startsWith( "2.12." ) )
{
scalaBinaryVersion = "2.12";
}
else if ( "2.13.0-M5".equals( resolvedScalaVersion ) )
{
scalaBinaryVersion = "2.13.0-M5";
}
else
{
getLog().warn( String.format( "Skipping SCoverage execution - unsupported Scala version \"%s\"",
resolvedScalaVersion ) );
return;
}
}
else
{
getLog().warn( "Skipping SCoverage execution - Scala version not set" );
return;
}
Map additionalProjectPropertiesMap = null;
if ( additionalForkedProjectProperties != null && !additionalForkedProjectProperties.isEmpty() )
{
String[] props = additionalForkedProjectProperties.split( ";" );
additionalProjectPropertiesMap = new HashMap( props.length );
for ( String propVal: props )
{
String[] tmp = propVal.split( "=", 2 );
if ( tmp.length == 2 )
{
String propName = tmp[ 0 ].trim();
String propValue = tmp[ 1 ].trim();
additionalProjectPropertiesMap.put( propName, propValue );
}
else
{
getLog().warn( String.format( "Skipping invalid additional forked project property \"%s\", must be in \"key=value\" format",
propVal ) );
}
}
}
SCoverageForkedLifecycleConfigurator.afterForkedLifecycleEnter( project, reactorProjects, additionalProjectPropertiesMap );
try
{
Artifact pluginArtifact = getScalaScoveragePluginArtifact( scalaBinaryVersion );
Artifact runtimeArtifact = getScalaScoverageRuntimeArtifact( scalaBinaryVersion );
if ( pluginArtifact == null )
{
return; // scoverage plugin will not be configured
}
addScoverageDependenciesToClasspath( runtimeArtifact );
String arg = DATA_DIR_OPTION + dataDirectory.getAbsolutePath();
String _scalacOptions = quoteArgument( arg );
String addScalacArgs = arg;
if ( !StringUtils.isEmpty( excludedPackages ) )
{
arg = EXCLUDED_PACKAGES_OPTION + excludedPackages.replace( "(empty)", "" );
_scalacOptions = _scalacOptions + SPACE + quoteArgument( arg );
addScalacArgs = addScalacArgs + PIPE + arg;
}
if ( !StringUtils.isEmpty( excludedFiles ) )
{
arg = EXCLUDED_FILES_OPTION + excludedFiles;
_scalacOptions = _scalacOptions + SPACE + quoteArgument( arg );
addScalacArgs = addScalacArgs + PIPE + arg;
}
if ( highlighting )
{
_scalacOptions = _scalacOptions + SPACE + "-Yrangepos";
addScalacArgs = addScalacArgs + PIPE + "-Yrangepos";
}
String _scalacPlugins =
String.format( "%s:%s:%s", pluginArtifact.getGroupId(), pluginArtifact.getArtifactId(),
pluginArtifact.getVersion() );
arg = PLUGIN_OPTION + pluginArtifact.getFile().getAbsolutePath();
addScalacArgs = addScalacArgs + PIPE + arg;
Properties projectProperties = project.getProperties();
// for sbt-compiler-maven-plugin (version 1.0.0-beta5+)
setProperty( projectProperties, "sbt._scalacOptions", _scalacOptions );
// for sbt-compiler-maven-plugin (version 1.0.0-beta5+)
setProperty( projectProperties, "sbt._scalacPlugins", _scalacPlugins );
// for scala-maven-plugin (version 3.0.0+)
setProperty( projectProperties, "addScalacArgs", addScalacArgs );
// for scala-maven-plugin (version 3.1.0+)
setProperty( projectProperties, "analysisCacheFile",
"${project.build.directory}/scoverage-analysis/compile" );
// for maven-surefire-plugin and scalatest-maven-plugin
setProperty( projectProperties, "maven.test.failure.ignore", "true" );
// for maven-jar-plugin
// VERY IMPORTANT! Prevents from overwriting regular project artifact file
// with instrumented one during "integration-check" or "integration-report" execution.
project.getBuild().setFinalName( "scoverage-" + project.getBuild().getFinalName() );
saveSourceRootsToFile();
}
catch ( ArtifactNotFoundException e )
{
throw new MojoExecutionException( "SCoverage preparation failed", e );
}
catch ( ArtifactResolutionException e )
{
throw new MojoExecutionException( "SCoverage preparation failed", e );
}
catch ( IOException e )
{
throw new MojoExecutionException( "SCoverage preparation failed", e );
}
long te = System.currentTimeMillis();
getLog().debug( String.format( "Mojo execution time: %d ms", te - ts ) );
}
// Private utility methods
private static final String SCALA_LIBRARY_GROUP_ID = "org.scala-lang";
private static final String SCALA_LIBRARY_ARTIFACT_ID = "scala-library";
private static final String DATA_DIR_OPTION = "-P:scoverage:dataDir:";
private static final String EXCLUDED_PACKAGES_OPTION = "-P:scoverage:excludedPackages:";
private static final String EXCLUDED_FILES_OPTION = "-P:scoverage:excludedFiles:";
private static final String PLUGIN_OPTION = "-Xplugin:";
private static final char DOUBLE_QUOTE = '\"';
private static final char SPACE = ' ';
private static final char PIPE = '|';
private String quoteArgument( String arg )
{
return arg.indexOf( SPACE ) >= 0 ? DOUBLE_QUOTE + arg + DOUBLE_QUOTE : arg;
}
private String resolveScalaVersion()
{
String result = scalaVersion;
if ( result == null )
{
// check project direct dependencies (transitive dependencies cannot be checked in this Maven lifecycle phase)
@SuppressWarnings( "unchecked" )
List dependencies = project.getDependencies();
for ( Dependency dependency: dependencies )
{
if ( SCALA_LIBRARY_GROUP_ID.equals( dependency.getGroupId() )
&& SCALA_LIBRARY_ARTIFACT_ID.equals( dependency.getArtifactId() ) )
{
result = dependency.getVersion();
break;
}
}
}
return result;
}
private void setProperty( Properties projectProperties, String propertyName, String newValue )
{
if ( projectProperties.containsKey( propertyName ) )
{
String oldValue = projectProperties.getProperty( propertyName );
projectProperties.put( "scoverage.backup." + propertyName, oldValue );
}
else
{
projectProperties.remove( "scoverage.backup." + propertyName );
}
if ( newValue != null )
{
projectProperties.put( propertyName, newValue );
}
else
{
projectProperties.remove( propertyName );
}
}
private Artifact getScalaScoveragePluginArtifact( String scalaMainVersion )
throws ArtifactNotFoundException, ArtifactResolutionException
{
Artifact result = null;
String resolvedScalacPluginVersion = scalacPluginVersion;
if ( resolvedScalacPluginVersion == null || "".equals( resolvedScalacPluginVersion ) )
{
for ( Artifact artifact : pluginArtifacts )
{
if ( "org.scoverage".equals( artifact.getGroupId() )
&& "scalac-scoverage-plugin_2.12".equals( artifact.getArtifactId() ) )
{
if ( "2.12".equals( scalaMainVersion ) )
{
return artifact; // shortcut, use the same artifact plugin uses
}
resolvedScalacPluginVersion = artifact.getVersion();
break;
}
}
}
result =
getResolvedArtifact( "org.scoverage", "scalac-scoverage-plugin_" + scalaMainVersion,
resolvedScalacPluginVersion );
return result;
}
private Artifact getScalaScoverageRuntimeArtifact( String scalaMainVersion )
throws ArtifactNotFoundException, ArtifactResolutionException
{
Artifact result = null;
String resolvedScalacRuntimeVersion = scalacPluginVersion;
if ( resolvedScalacRuntimeVersion == null || "".equals( resolvedScalacRuntimeVersion ) )
{
for ( Artifact artifact : pluginArtifacts )
{
if ( "org.scoverage".equals( artifact.getGroupId() )
&& "scalac-scoverage-plugin_2.12".equals( artifact.getArtifactId() ) )
{
resolvedScalacRuntimeVersion = artifact.getVersion();
break;
}
}
}
result =
getResolvedArtifact( "org.scoverage", "scalac-scoverage-runtime_" + scalaMainVersion,
resolvedScalacRuntimeVersion );
return result;
}
/**
* We need to tweak our test classpath for Scoverage.
*
* @throws MojoExecutionException
*/
private void addScoverageDependenciesToClasspath( Artifact scalaScoveragePluginArtifact )
throws MojoExecutionException
{
@SuppressWarnings( "unchecked" )
Set set = new LinkedHashSet( project.getDependencyArtifacts() );
set.add( scalaScoveragePluginArtifact );
project.setDependencyArtifacts( set );
}
private Artifact getResolvedArtifact( String groupId, String artifactId, String version )
throws ArtifactNotFoundException, ArtifactResolutionException
{
Artifact artifact = factory.createArtifact( groupId, artifactId, version, Artifact.SCOPE_COMPILE, "jar" );
resolver.resolve( artifact, remoteRepos, localRepo );
return artifact;
}
private void saveSourceRootsToFile() throws IOException
{
List sourceRoots = project.getCompileSourceRoots();
if ( !sourceRoots.isEmpty() )
{
if ( !dataDirectory.exists() && !dataDirectory.mkdirs() )
{
throw new IOException( String.format( "Cannot create \"%s\" directory ",
dataDirectory.getAbsolutePath() ) );
}
File sourceRootsFile = new File( dataDirectory, "source.roots" );
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter( new FileOutputStream( sourceRootsFile ), "UTF-8" ) );
try
{
for ( String sourceRoot: sourceRoots )
{
writer.write( sourceRoot );
writer.newLine();
}
}
finally
{
writer.close();
}
}
}
}