All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.maven.plugin.clover.CloverInstrumentInternalMojo Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2001-2006 The Apache Software Foundation.
 *
 * 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.apache.maven.plugin.clover;

import com.cenqua.clover.CloverInstr;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.clover.internal.AbstractCloverMojo;
import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
import org.codehaus.plexus.util.FileUtils;

import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * Instrument source roots.
 *
 * 

Note 1: Do not call this MOJO directly. It is meant to be called in a custom forked lifecycle by the other * Clover plugin MOJOs.

*

Note 2: We bind this mojo to the "validate" phase so that it executes prior to any other mojos

* * @goal instrumentInternal * @phase validate * @requiresDependencyResolution test * * @author Vincent Massol * @version $Id: CloverInstrumentInternalMojo.java 525546 2007-04-04 16:01:35Z vmassol $ */ public class CloverInstrumentInternalMojo extends AbstractCloverMojo { /** * The directory where the Clover plugin will put all the files it generates during the build process. For * example the Clover plugin will put instrumented sources somewhere inside this directory. * * @parameter * @required */ private String cloverOutputDirectory; /** * List of all artifacts for this Clover plugin provided by Maven. This is used internally to get a handle on * the Clover JAR artifact. * *

Note: This is passed by Maven and must not be configured by the user.

* * @parameter expression="${plugin.artifacts}" * @required */ private List pluginArtifacts; /** * @parameter expression="${component.org.apache.maven.artifact.factory.ArtifactFactory}" * @required * @readonly */ private ArtifactFactory artifactFactory; /** * Artifact resolver used to find clovered artifacts (artifacts with a clover classifier). * * @component role="org.apache.maven.artifact.resolver.ArtifactResolver" * @required * @readonly */ private ArtifactResolver artifactResolver; /** * Local maven repository. * * @parameter expression="${localRepository}" * @required */ private ArtifactRepository localRepository; /** * The list of file to include in the instrumentation. * @parameter */ private Set includes = new HashSet(); /** * The list of file to exclude from the instrumentation. * @parameter */ private Set excludes = new HashSet(); /** * Whether the Clover plugin should instrument all source roots (ie even * generated sources) or whether it should only instrument the main source * root. * @parameter default-value="false" */ private boolean includesAllSourceRoots; private String cloverOutputSourceDirectory; /** * {@inheritDoc} * @see org.apache.maven.plugin.clover.internal.AbstractCloverMojo#execute() */ public void execute() throws MojoExecutionException { // Ensure output directories exist new File( this.cloverOutputDirectory ).mkdirs(); this.cloverOutputSourceDirectory = new File( this.cloverOutputDirectory, "src" ).getPath(); new File( getCloverDatabase() ).getParentFile().mkdirs(); super.execute(); logArtifacts( "before changes" ); if ( isJavaProject() ) { Map filesToInstrument = computeFilesToInstrument(); if ( filesToInstrument.isEmpty() ) { getLog().warn( "No Clover instrumentation done as no matching sources files found" ); } else { instrumentSources( filesToInstrument ); // We need to copy excluded files as otherwise they won't be in the new Clover source directory and // thus won't be compiled by the compile plugin. This will lead to compilation errors if any other // Java file depends on any of these excluded files. copyExcludedFiles(); } } swizzleCloverDependencies(); addCloverDependencyToCompileClasspath(); redirectSourceDirectories(); redirectOutputDirectories(); redirectArtifact(); logArtifacts( "after changes" ); } private boolean isJavaProject() { boolean isJavaProject; ArtifactHandler artifactHandler = getProject().getArtifact().getArtifactHandler(); if ( "java".equals( artifactHandler.getLanguage() ) ) { isJavaProject = true; } else { getLog().info( "Not instrumenting sources with Clover as this is not a Java project." ); isJavaProject = false; } return isJavaProject; } private void instrumentSources( Map filesToInstrument ) throws MojoExecutionException { int result = CloverInstr.mainImpl( createCliArgs( filesToInstrument ) ); if ( result != 0 ) { throw new MojoExecutionException( "Clover has failed to instrument the source files" ); } } /** * Copy all files that have been excluded by the user (using the excludes configuration property). This is required * as otherwise the excluded files won't be in the new Clover source directory and thus won't be compiled by the * compile plugin. This will lead to compilation errors if any other Java file depends on any of them. * * @throws MojoExecutionException if a failure happens during the copy */ private void copyExcludedFiles() throws MojoExecutionException { Map filesToCopy = computeExcludedFilesToCopy(); for ( Iterator sourceRoots = filesToCopy.keySet().iterator(); sourceRoots.hasNext(); ) { String sourceRoot = (String) sourceRoots.next(); Set filesInSourceRoot = (Set) filesToCopy.get( sourceRoot ); for ( Iterator files = filesInSourceRoot.iterator(); files.hasNext(); ) { File file = (File) files.next(); try { FileUtils.copyFile( file, new File( this.cloverOutputSourceDirectory, file.getPath().substring(sourceRoot.length() ) ) ); } catch (IOException e) { throw new MojoExecutionException( "Failed to copy excluded file [" + file + "] to [" + this.cloverOutputSourceDirectory + "]", e ); } } } } private void redirectOutputDirectories() { // Explicitely set the output directory to be the Clover one so that all other plugins executing // thereafter output files in the Clover output directory and not in the main output directory. getProject().getBuild().setDirectory( this.cloverOutputDirectory ); // TODO: Ugly hack below. Changing the directory should be enough for changing the values of all other // properties depending on it! getProject().getBuild().setOutputDirectory( new File( this.cloverOutputDirectory, "classes" ).getPath() ); // TODO: This is a hack. Remove this when http://jira.codehaus.org/browse/MINSTALL-18 is fixed. new File( getProject().getBuild().getOutputDirectory() ).mkdirs(); getProject().getBuild().setTestOutputDirectory( new File( this.cloverOutputDirectory, "test-classes" ).getPath() ); } private void redirectSourceDirectories() { String oldSourceDirectory = getProject().getBuild().getSourceDirectory(); if ( new File( oldSourceDirectory ).exists() ) { getProject().getBuild().setSourceDirectory( this.cloverOutputSourceDirectory ); } getLog().debug( "Clover source directories before change:" ); logSourceDirectories(); // Maven2 limitation: changing the source directory doesn't change the compile source roots // See http://jira.codehaus.org/browse/MNG-1945 List sourceRoots = new ArrayList( getProject().getCompileSourceRoots() ); // Clean all source roots to add them again in order to keep the same original order of source roots. getProject().getCompileSourceRoots().removeAll( sourceRoots ); for ( Iterator i = sourceRoots.iterator(); i.hasNext(); ) { String sourceRoot = (String) i.next(); if ( new File( oldSourceDirectory ).exists() && sourceRoot.equals( oldSourceDirectory ) ) { getProject().addCompileSourceRoot( getProject().getBuild().getSourceDirectory() ); } else { getProject().addCompileSourceRoot( sourceRoot ); } } getLog().debug( "Clover source directories after change:" ); logSourceDirectories(); } /** * Modify main artifact to add a "clover" classifier to it so that it's not mixed with the main artifact of * a normal build. */ private void redirectArtifact() { // Only redirect main artifact for non-pom projects if ( !getProject().getPackaging().equals( "pom" ) ) { Artifact oldArtifact = getProject().getArtifact(); Artifact newArtifact = this.artifactFactory.createArtifactWithClassifier( oldArtifact.getGroupId(), oldArtifact.getArtifactId(), oldArtifact.getVersion(), oldArtifact.getType(), "clover" ); getProject().setArtifact( newArtifact ); getProject().getBuild().setFinalName( getProject().getArtifactId() + "-" + getProject().getVersion() + "-clover" ); } } private void logSourceDirectories() { if ( getLog().isDebugEnabled() ) { for ( Iterator i = getProject().getCompileSourceRoots().iterator(); i.hasNext(); ) { String sourceRoot = (String) i.next(); getLog().debug( "[Clover] source root [" + sourceRoot + "]" ); } } } /** * Browse through all project dependencies and try to find a clovered version of the dependency. If found * replace the main depedencency by the clovered version. */ private void swizzleCloverDependencies() { getProject().setDependencyArtifacts( swizzleCloverDependencies( getProject().getDependencyArtifacts() ) ); getProject().setArtifacts( swizzleCloverDependencies( getProject().getArtifacts() ) ); } protected Set swizzleCloverDependencies( Set artifacts ) { Set resolvedArtifacts = new HashSet(); for ( Iterator i = artifacts.iterator(); i.hasNext(); ) { Artifact artifact = (Artifact) i.next(); // Do not try to find Clovered versions for artifacts with classifiers. This is because Maven2 only // supports a single classifier per artifact and thus if we replace the original classifier with // a Clover classifier the artifact will fail to perform properly as intended originally. This is a // limitation. if ( artifact.getClassifier() == null ) { Artifact cloveredArtifact = this.artifactFactory.createArtifactWithClassifier( artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), artifact.getType(), "clover" ); // Try to resolve the artifact with a clover classifier. If it doesn't exist, simply add the original // artifact. If found, use the clovered artifact. try { this.artifactResolver.resolve( cloveredArtifact, new ArrayList(), localRepository ); // Set the same scope as the main artifact as this is not set by createArtifactWithClassifier. cloveredArtifact.setScope( artifact.getScope() ); // Check the timestamp of the artifact. If the found clovered version is older than the // non-clovered one we need to use the non-clovered version. This is to handle use case such as: // - Say you have a module B that depends on a module A // - You run Clover on A // - You make modifications on A such that B would fail if not built with the latest version of A // - You try to run the Clover plugin on B. The build would fail if we didn't pick the latest // version between the original A version and the clovered version. if ( cloveredArtifact.getFile().lastModified() < artifact.getFile().lastModified() ) { getLog().warn( "Using [" + artifact.getId() + "] even though a Clovered version exists " + "but it's older and could fail the build. Please consider running Clover again on that " + "dependency's project." ); resolvedArtifacts.add( artifact ); } else { resolvedArtifacts.add( cloveredArtifact ); } } catch ( ArtifactResolutionException e ) { resolvedArtifacts.add( artifact ); } catch ( ArtifactNotFoundException e ) { resolvedArtifacts.add( artifact ); } } else { resolvedArtifacts.add( artifact ); } } return resolvedArtifacts; } protected Artifact findCloverArtifact( List pluginArtifacts ) { Artifact cloverArtifact = null; Iterator artifacts = pluginArtifacts.iterator(); while ( artifacts.hasNext() && cloverArtifact == null ) { Artifact artifact = (Artifact) artifacts.next(); // We identify the clover JAR by checking the groupId and artifactId. if ( "com.cenqua.clover".equals( artifact.getGroupId() ) && "clover".equals( artifact.getArtifactId() ) ) { cloverArtifact = artifact; } } return cloverArtifact; } private void addCloverDependencyToCompileClasspath() throws MojoExecutionException { Artifact cloverArtifact = findCloverArtifact( this.pluginArtifacts ); if ( cloverArtifact == null ) { throw new MojoExecutionException( "Couldn't find [com.cenqua.cover:clover] artifact in plugin dependencies" ); } cloverArtifact = artifactFactory.createArtifact( cloverArtifact.getGroupId(), cloverArtifact.getArtifactId(), cloverArtifact.getVersion(), Artifact.SCOPE_COMPILE, cloverArtifact.getType() ); // TODO: use addArtifacts when it's implemented, see http://jira.codehaus.org/browse/MNG-2197 Set set = new HashSet( getProject().getDependencyArtifacts() ); set.add( cloverArtifact ); getProject().setDependencyArtifacts( set ); } private void logArtifacts( String message ) { if ( getLog().isDebugEnabled() ) { getLog().debug( "[Clover] List of dependency artifacts " + message + ":" ); logArtifacts( getProject().getDependencyArtifacts() ); getLog().debug( "[Clover] List of artifacts " + message + ":" ); logArtifacts( getProject().getArtifacts() ); } } private void logArtifacts( Set artifacts ) { for ( Iterator i = artifacts.iterator(); i.hasNext(); ) { Artifact artifact = (Artifact) i.next(); getLog().debug( "[Clover] Artifact [" + artifact.getId() + "], scope = [" + artifact.getScope() + "]" ); } } /** * @return a Plexus scanner object that scans a source root and filters files according to inclusion and * exclusion patterns. In our case at hand we include only Java sources as these are the only files we want * to instrument. */ private SourceInclusionScanner getScanner() { SourceInclusionScanner scanner = null; if ( includes.isEmpty() && excludes.isEmpty() ) { includes = Collections.singleton( "**/*.java" ); scanner = new SimpleSourceInclusionScanner( includes, Collections.EMPTY_SET ); } else { if ( includes.isEmpty() ) { includes.add( "**/*.java" ); } scanner = new SimpleSourceInclusionScanner( includes, excludes ); } // Note: we shouldn't have to do this but this is a limitation of the Plexus SimpleSourceInclusionScanner scanner.addSourceMapping( new SuffixMapping( "dummy", "dummy" ) ); return scanner; } private SourceInclusionScanner getExcludesScanner() { SourceInclusionScanner scanner = null; if ( excludes.isEmpty() ) { scanner = new SimpleSourceInclusionScanner( Collections.EMPTY_SET, Collections.EMPTY_SET ); } else { scanner = new SimpleSourceInclusionScanner( excludes, Collections.EMPTY_SET ); } // Note: we shouldn't have to do this but this is a limitation of the Plexus SimpleSourceInclusionScanner scanner.addSourceMapping( new SuffixMapping( "dummy", "dummy" ) ); return scanner; } /** * @return the list of excluded files that we'll need to copy. See {@link #copyExcludedFiles()}. */ private Map computeExcludedFilesToCopy() { return computeFiles(getExcludesScanner()); } /** * @return the list of files to instrument taking into account the includes and excludes specified by the user */ private Map computeFilesToInstrument() { return computeFiles(getScanner()); } private Map computeFiles(SourceInclusionScanner scanner) { Map files = new HashMap(); // Decide whether to instrument all source roots or only the main source root. Iterator sourceRoots = getSourceRoots().iterator(); while ( sourceRoots.hasNext() ) { File sourceRoot = new File( (String) sourceRoots.next() ); if ( sourceRoot.exists() ) { try { files.put( sourceRoot.getPath(), scanner.getIncludedSources( sourceRoot, null ) ); } catch ( InclusionScanException e ) { getLog().warn( "Failed to add sources from [" + sourceRoot + "]", e ); } } } return files; } private List getSourceRoots() { List sourceRoots; if ( this.includesAllSourceRoots ) { sourceRoots = getProject().getCompileSourceRoots(); } else { sourceRoots = Collections.singletonList( getProject().getBuild().getSourceDirectory() ); } return sourceRoots; } /** * @return the CLI args to be passed to CloverInstr */ private String[] createCliArgs( Map filesToInstrument ) throws MojoExecutionException { List parameters = new ArrayList(); parameters.add( "-p" ); parameters.add( getFlushPolicy() ); parameters.add( "-f" ); parameters.add( "" + getFlushInterval() ); parameters.add( "-i" ); parameters.add( getCloverDatabase() ); parameters.add( "-d" ); parameters.add( this.cloverOutputSourceDirectory ); if ( getLog().isDebugEnabled() ) { parameters.add( "-v" ); } if ( getJdk() != null ) { if ( getJdk().equals( "1.4" ) ) { parameters.add( "-jdk14" ); } else if ( getJdk().equals( "1.5" ) ) { parameters.add( "-jdk15" ); } else { throw new MojoExecutionException( "Unsupported jdk version [" + getJdk() + "]. Valid values are [1.4] and [1.5]" ); } } for ( Iterator sourceRoots = filesToInstrument.keySet().iterator(); sourceRoots.hasNext(); ) { Set filesInSourceRoot = (Set) filesToInstrument.get( (String) sourceRoots.next() ); for ( Iterator files = filesInSourceRoot.iterator(); files.hasNext(); ) { File file = (File) files.next(); parameters.add( file.getPath() ); } } // Log parameters if ( getLog().isDebugEnabled() ) { getLog().debug( "Parameter list being passed to Clover CLI:" ); for ( Iterator it = parameters.iterator(); it.hasNext(); ) { String param = (String) it.next(); getLog().debug( " parameter = [" + param + "]" ); } } return (String[]) parameters.toArray( new String[0] ); } protected void setArtifactFactory( ArtifactFactory artifactFactory ) { this.artifactFactory = artifactFactory; } protected void setArtifactResolver( ArtifactResolver artifactResolver ) { this.artifactResolver = artifactResolver; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy