blob: 48b2944ec320350c90c83a5e1fe97bbc3e23c12d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 npanday.plugin.ilmerge;
import npanday.ArtifactType;
import npanday.LocalRepositoryUtil;
import npanday.PlatformUnsupportedException;
import npanday.executable.ExecutableRequirement;
import npanday.executable.ExecutionException;
import npanday.executable.compiler.CompilerConfig;
import npanday.executable.compiler.CompilerExecutable;
import npanday.executable.compiler.CompilerRequirement;
import npanday.executable.compiler.KeyInfo;
import npanday.registry.RepositoryRegistry;
import npanday.resolver.NPandayDependencyResolution;
import npanday.vendor.SettingsUtil;
import npanday.vendor.Vendor;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.codehaus.plexus.util.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* Merges assemblies into unified assembly.
*
* @phase package
* @goal merge-assemblies
*/
public class AssemblyMerger extends AbstractMojo
{
/**
* @parameter expression="${npanday.settings}" default-value="${user.home}/.m2"
*/
private String settingsPath;
/**
* @component
*/
private RepositoryRegistry repositoryRegistry;
/**
* The maven project.
*
* @parameter expression="${project}"
* @required
*/
private MavenProject project;
/**
* The Vendor for the executable. Supports MONO and MICROSOFT: the default value is <code>MICROSOFT</code>. Not
* case or white-space sensitive.
*
* @parameter expression="${vendor}"
*/
private String vendor;
/**
* @component
*/
private npanday.executable.NetExecutableFactory netExecutableFactory;
/**
* @component
*/
private MavenProjectHelper projectHelper;
/**
* @parameter expression = "${frameworkVersion}"
*/
private String frameworkVersion;
/**
* Specify a strong name key file.
*
* @parameter expression = "${keyfile}"
*/
private File keyfile;
/**
* Specifies a strong name key container. (not currently supported)
*
* @parameter expression = "${keycontainer}"
*/
private String keycontainer;
/**
* Copy assembly attributes into the merged assembly. Only the primary assembly attributes are copied.
*
* @parameter default-value = "false"
*/
private boolean copyAttributes;
/**
* The profile that the compiler should use to compile classes: FULL, COMPACT, (or a custom one specified in a
* compiler-plugins.xml).
*
* @parameter expression = "${profile}" default-value = "FULL"
*/
private String profile;
/**
* .NET Language. The default value is <code>C_SHARP</code>. Not case or white-space sensitive.
*
* @parameter expression="${language}" default-value = "C_SHARP"
* @required
*/
private String language;
/**
* @parameter expression = "${vendorVersion}"
*/
private String vendorVersion;
/**
* The home directory of your .NET SDK.
*
* @parameter expression="${netHome}"
*/
private File netHome;
/**
* The ILMerge command name. Supports either ILMerge (default) or ILRepack.
*
* @parameter expression = "${ilmergeCommand}" default-value="ILMerge"
*/
private String executable;
/**
* This overrides the defaultAssemblyPath for the compiler.
*
* @parameter expression = "${profileAssemblyPath}
*/
private File profileAssemblyPath;
/**
* The directory for the compilated web application
*
* @parameter expression = "${project.build.directory}/merged"
*/
private File outputDirectory;
/**
* The location of the local Maven repository.
*
* @parameter expression="${settings.localRepository}"
*/
private File localRepository;
/**
* @parameter
*/
private ArtifactSet artifactSet;
/**
* @parameter
*/
private ArtifactSet internalizeSet;
/**
* @parameter default-value="true"
*/
private boolean mergeDebugSymbols;
/**
* @parameter default-value="true"
*/
private boolean mergeDocumentation;
/**
* Defines whether the merged artifact should be attached as classifier to
* the original artifact. If false, the merged assembly will be the main artifact
* of the project
*
* @parameter expression="${mergedArtifactAttached}" default-value="false"
*/
private boolean mergedArtifactAttached;
/**
* The name of the classifier used in case the merged artifact is attached.
*
* @parameter expression="${mergedClassifierName}" default-value="merged"
*/
private String mergedClassifierName;
/**
* If specified, this will include only artifacts which have groupIds which
* start with this.
*
* @parameter expression="${mergedGroupFilter}"
*/
private String mergedGroupFilter;
/**
*
* @parameter default-value="true"
*/
private boolean mergedArtifactReplacesProjectArtifact;
/**
* @component
*/
private NPandayDependencyResolution dependencyResolution;
/**
* The scope up to which dependencies should be resolved.
*
* @parameter default-value="runtime"
*/
private String requiredScope;
/**
* Merges the specified assemblies into a primary assembly with classifier "merged".
*/
public void execute()
throws MojoExecutionException, MojoFailureException
{
SettingsUtil.applyCustomSettings( getLog(), repositoryRegistry, settingsPath );
resolveDependencies();
try
{
// ilmerge.exe
// determine how to set /lib:[assemblyPath]
CompilerExecutable compilerExecutable = netExecutableFactory.getCompilerExecutable(
getCompilerRequirement(), getCompilerConfig(), project
);
File assemblyPath = compilerExecutable.getAssemblyPath();
if ( assemblyPath == null )
{
throw new MojoExecutionException( "NPANDAY-1501-007: Unable to determine assembly path, perhaps missing profileAssemblyPath?" );
}
Set artifacts = new LinkedHashSet();
ArtifactSelector artifactSelector =
new ArtifactSelector( artifactSet, mergedGroupFilter );
Artifact projectArtifact = project.getArtifact();
if ( artifactSelector.isSelected( projectArtifact ) && !"pom".equals( projectArtifact.getType() ) )
{
if ( projectArtifact.getFile() == null )
{
getLog().error( "The project main artifact does not exist. This could have the following" );
getLog().error( "reasons:" );
getLog().error( "- You have invoked the goal directly from the command line. This is not" );
getLog().error( " supported. Please add the goal to the default lifecycle via an" );
getLog().error( " <execution> element in your POM and use \"mvn package\" to have it run." );
getLog().error( "- You have bound the goal to a lifecycle phase before \"package\". Please" );
getLog().error( " remove this binding from your POM such that the goal will be run in" );
getLog().error( " the proper phase." );
throw new MojoExecutionException( "Failed to create shaded artifact, "
+ "project main artifact does not exist." );
}
getLog().info( "Including " + projectArtifact.getId() + " in the merged assembly." );
artifacts.add( projectArtifact.getFile() );
}
else
{
getLog().info( "Excluding " + projectArtifact.getId() + " from the merged assembly." );
}
ArtifactType packagingType = ArtifactType.getArtifactTypeForPackagingName(project.getPackaging());
File mergedArtifactFile = new File(outputDirectory, project.getArtifactId() + "." + packagingType.getExtension());
// TODO: /target defaults to same kind as first assembly
Set candidateArtifacts = new HashSet();
candidateArtifacts.addAll(project.getArtifacts());
candidateArtifacts.addAll(project.getAttachedArtifacts());
ArtifactSelector internalizeArtifactSelector = new ArtifactSelector( internalizeSet, null );
Set internalizeArtifacts = new HashSet();
for ( Iterator it = candidateArtifacts.iterator(); it.hasNext(); )
{
Artifact artifact = (Artifact) it.next();
if ( !artifactSelector.isSelected( artifact ) )
{
getLog().info( "Excluding " + artifact.getId() + " from the merged assembly." );
continue;
}
if ( "pom".equals( artifact.getType() ) )
{
getLog().info( "Skipping pom dependency " + artifact.getId() + " in the merged assembly." );
continue;
}
boolean internalize = internalizeArtifactSelector.isSelected( artifact );
getLog().info( "Including " + artifact.getId() + " in the merged assembly." + (internalize ? " (internalize)" : ""));
artifacts.add( artifact.getFile() );
if ( internalize )
{
internalizeArtifacts.add( artifact.getFile() );
}
}
// TODO: support multple non-internalized artifacts by executing ilmerge twice
// * first merge all the public assemblies together
// * then internalizing the rest using the previous result as the primary assembly
if ( artifacts.removeAll( internalizeArtifacts ) )
{
if ( !internalizeArtifacts.isEmpty() && artifacts.size() > 1 )
{
throw new MojoExecutionException( "NPANDAY-1501-011: Multiple non-internalized assemblies after applying internalizeSet filter to artifactSet" );
}
}
// ILRepack on non-Windows appears to need a /lib: referring to the target directory
// to avoid a problem during the merge process where it is unable to locate the primary assembly
File artifactFile = (File) artifacts.iterator().next();
Collection<String> searchDirectoryPaths = Arrays.asList( artifactFile.getParent() );
// ILMerge cannot tolerate overwriting an input assembly with the output assembly
File mergedArtifactTempDirectory = null;
if ( artifacts.contains( mergedArtifactFile ) || internalizeArtifacts.contains( mergedArtifactFile ) )
{
mergedArtifactTempDirectory = new File( mergedArtifactFile.getParentFile(), "temp" );
mergedArtifactTempDirectory.mkdirs();
}
List commands = new ArrayList();
commands.add("/lib:" + assemblyPath.toString());
for ( String searchDirectoryPath : searchDirectoryPaths )
{
commands.add("/lib:" + searchDirectoryPath);
}
if ( mergedArtifactTempDirectory != null )
{
File mergedArtifactTempFile = new File( mergedArtifactTempDirectory, mergedArtifactFile.getName() );
commands.add("/out:" + mergedArtifactTempFile );
}
else
{
commands.add("/out:" + mergedArtifactFile);
}
if ( copyAttributes )
{
commands.add("/copyattrs");
}
if (!mergeDebugSymbols)
{
commands.add("/ndebug");
}
if (mergeDocumentation)
{
commands.add("/xmldocs");
}
for ( Iterator it = artifacts.iterator(); it.hasNext(); )
{
File artifact = (File)it.next();
commands.add( artifact.getAbsolutePath() );
}
for ( Iterator it = internalizeArtifacts.iterator(); it.hasNext(); )
{
File internalizeArtifact = (File)it.next();
commands.add( internalizeArtifact.getAbsolutePath() );
}
outputDirectory.mkdirs();
netExecutableFactory.getExecutable(
new ExecutableRequirement( vendor, null, frameworkVersion, executable ), commands, netHome
).execute();
if ( mergedArtifactTempDirectory != null )
{
File mergedArtifactTempFile = new File( mergedArtifactTempDirectory, mergedArtifactFile.getName() );
FileUtils.rename( mergedArtifactTempFile, mergedArtifactFile );
if ( mergeDebugSymbols )
{
Vendor vendor = compilerExecutable.getVendor();
String debugSymbolsExtension = ( vendor == Vendor.MONO ) ? ".mdb" : ".pdb";
String mergedArtifactSymbolFileName = mergedArtifactFile.getName().replace( ".dll", debugSymbolsExtension );
File mergedArtifactSymbolFile = new File( mergedArtifactFile.getParentFile(), mergedArtifactSymbolFileName );
if ( mergedArtifactSymbolFile.exists() )
{
File mergedArtifactTempSymbolFile = new File( mergedArtifactTempDirectory, mergedArtifactSymbolFileName );
FileUtils.rename( mergedArtifactTempSymbolFile, mergedArtifactSymbolFile );
}
}
FileUtils.deleteDirectory( mergedArtifactTempDirectory );
}
if ( mergedArtifactAttached )
{
getLog().info( "Attaching merged artifact." );
projectHelper.attachArtifact(project, projectArtifact.getType(), mergedClassifierName, mergedArtifactFile);
}
else if ( mergedArtifactReplacesProjectArtifact )
{
getLog().info( "Replacing project artifact with merged artifact." );
File projectArtifactFile = projectArtifact.getFile();
FileUtils.rename( mergedArtifactFile, projectArtifactFile );
}
}
catch ( ExecutionException e )
{
throw new MojoExecutionException( "NPANDAY-1501-002: Unable to execute " + executable + ": Vendor = " + vendor +
", frameworkVersion = " + frameworkVersion, e );
}
catch ( PlatformUnsupportedException e )
{
throw new MojoExecutionException( "NPANDAY-1501-003: Platform Unsupported", e );
}
catch ( IOException e )
{
throw new MojoExecutionException( "NPANDAY-1501-004: Unable to overwrite default artifact file", e );
}
}
private void resolveDependencies() throws MojoExecutionException
{
try
{
dependencyResolution.require( project, LocalRepositoryUtil.create( localRepository ), requiredScope );
}
catch ( ArtifactResolutionException e )
{
throw new MojoExecutionException(
"NPANDAY-1501-012: Could not satisfy required dependencies of scope " + requiredScope, e
);
}
}
private String getFileNameMinusExtension(File file)
{
if (file==null) return null;
String name = file.getName();
int lastIndex = name.lastIndexOf( '.' );
return name.substring( 0, lastIndex );
}
protected void initializeDefaults() throws MojoExecutionException
{
if ( profileAssemblyPath != null && !profileAssemblyPath.exists() )
{
throw new MojoExecutionException( "NPANDAY-900-000: Profile Assembly Path does not exist: Path = " +
profileAssemblyPath.getAbsolutePath() );
}
}
protected CompilerRequirement getCompilerRequirement() throws MojoExecutionException
{
return new CompilerRequirement(vendor, vendorVersion, frameworkVersion, profile, language);
}
protected CompilerConfig getCompilerConfig() throws MojoExecutionException
{
//Config
CompilerConfig compilerConfig = new CompilerConfig();
compilerConfig.setLocalRepository( localRepository );
if ( keyfile != null )
{
KeyInfo keyInfo = KeyInfo.Factory.createDefaultKeyInfo();
keyInfo.setKeyFileUri( keyfile.getAbsolutePath() );
compilerConfig.setKeyInfo( keyInfo );
}
if(outputDirectory != null)
{
if(!outputDirectory.exists())
{
outputDirectory.mkdirs();
}
compilerConfig.setOutputDirectory(outputDirectory);
}
String artifactTypeName = project.getArtifact().getType();
ArtifactType artifactType = ArtifactType.getArtifactTypeForPackagingName( artifactTypeName );
if ( artifactType.equals( ArtifactType.NULL ) )
{
throw new MojoExecutionException( "NPANDAY-900-002: Unrecognized artifact type: Language = " + language +
", Vendor = " + vendor + ", ArtifactType = " + artifactTypeName );
}
compilerConfig.setArtifactType( artifactType );
if (profileAssemblyPath != null){
compilerConfig.setAssemblyPath( profileAssemblyPath );
}
return compilerConfig;
}
}