package org.apache.maven.plugin.nar;
/*
* 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.
*/
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import net.sf.antcontrib.cpptasks.CCTask;
import net.sf.antcontrib.cpptasks.CUtil;
import net.sf.antcontrib.cpptasks.CompilerDef;
import net.sf.antcontrib.cpptasks.LinkerDef;
import net.sf.antcontrib.cpptasks.OutputTypeEnum;
import net.sf.antcontrib.cpptasks.RuntimeType;
import net.sf.antcontrib.cpptasks.SubsystemEnum;
import net.sf.antcontrib.cpptasks.types.LibrarySet;
import net.sf.antcontrib.cpptasks.types.LinkerArgument;
import net.sf.antcontrib.cpptasks.types.SystemLibrarySet;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;
/**
* Compiles native source files.
*
* @goal nar-compile
* @phase compile
* @requiresSession
* @requiresProject
* @requiresDependencyResolution compile
* @author Mark Donszelmann
*/
public class NarCompileMojo
extends AbstractCompileMojo
{
/**
* The current build session instance.
*
* @parameter expression="${session}"
* @required
* @readonly
*/
protected MavenSession session;
public final void narExecute()
throws MojoExecutionException, MojoFailureException
{
// make sure destination is there
getTargetDirectory().mkdirs();
// check for source files
int noOfSources = 0;
noOfSources += getSourcesFor(getCpp()).size();
noOfSources += getSourcesFor(getC()).size();
noOfSources += getSourcesFor(getFortran()).size();
if ( noOfSources > 0 )
{
getLog().info( "Compiling " + noOfSources + " native files" );
for ( Iterator i = getLibraries().iterator(); i.hasNext(); )
{
createLibrary(getAntProject(), (Library) i.next());
}
}
else
{
getLog().info( "Nothing to compile" );
}
try
{
// FIXME, should the include paths be defined at a higher level ?
getCpp().copyIncludeFiles(
getMavenProject(),
getLayout().getIncludeDirectory( getTargetDirectory(),
getMavenProject().getArtifactId(),
getMavenProject().getVersion() ),
getLog());
}
catch ( IOException e )
{
throw new MojoExecutionException( "NAR: could not copy include files", e );
}
}
private List getSourcesFor( Compiler compiler )
throws MojoFailureException, MojoExecutionException
{
try
{
List files = new ArrayList();
List srcDirs = compiler.getSourceDirectories();
for ( Iterator i = srcDirs.iterator(); i.hasNext(); )
{
File dir = (File) i.next();
if ( dir.exists() )
{
files.addAll( FileUtils.getFiles( dir, StringUtils.join( compiler.getIncludes().iterator(), "," ),
null ) );
}
}
return files;
}
catch ( IOException e )
{
return Collections.EMPTY_LIST;
}
}
private void createLibrary(Project antProject, Library library)
throws MojoExecutionException, MojoFailureException
{
getLog().debug( "Creating Library " + library );
// configure task
CCTask task = new CCTask();
task.setProject(antProject);
// subsystem
SubsystemEnum subSystem = new SubsystemEnum();
subSystem.setValue( library.getSubSystem() );
task.setSubsystem( subSystem );
// set max cores
task.setMaxCores(getMaxCores(getAOL()));
// outtype
OutputTypeEnum outTypeEnum = new OutputTypeEnum();
String type = library.getType();
outTypeEnum.setValue(type);
task.setOuttype(outTypeEnum);
// stdc++
task.setLinkCPP(library.linkCPP());
// fortran
task.setLinkFortran(library.linkFortran());
task.setLinkFortranMain( library.linkFortranMain() );
// outDir
File outDir;
if ( type.equals( Library.EXECUTABLE ) )
{
outDir =
getLayout().getBinDirectory( getTargetDirectory(), getMavenProject().getArtifactId(),
getMavenProject().getVersion(), getAOL().toString() );
}
else
{
outDir =
getLayout().getLibDirectory( getTargetDirectory(), getMavenProject().getArtifactId(),
getMavenProject().getVersion(), getAOL().toString(), type );
}
outDir.mkdirs();
// outFile
// FIXME NAR-90 we could get the final name from layout
File outFile;
if ( type.equals( Library.EXECUTABLE ) )
{
// executable has no version number
outFile = new File(outDir, getMavenProject().getArtifactId());
}
else
{
outFile = new File(outDir, getOutput(getAOL()));
}
getLog().debug("NAR - output: '" + outFile + "'");
task.setOutfile(outFile);
// object directory
File objDir = new File(getTargetDirectory(), "obj");
objDir = new File(objDir, getAOL().toString());
objDir.mkdirs();
task.setObjdir(objDir);
// failOnError, libtool
task.setFailonerror(failOnError(getAOL()));
task.setLibtool(useLibtool(getAOL()));
// runtime
RuntimeType runtimeType = new RuntimeType();
runtimeType.setValue(getRuntime(getAOL()));
task.setRuntime(runtimeType);
// Darren Sargent Feb 11 2010: Use Compiler.MAIN for "type"...appears the wrong "type" variable was being used
// since getCompiler() expects "main" or "test", whereas the "type" variable here is "executable", "shared" etc.
// add C++ compiler
CompilerDef cpp = getCpp().getCompiler( Compiler.MAIN, getOutput( getAOL() ) );
if ( cpp != null )
{
task.addConfiguredCompiler( cpp );
}
// add C compiler
CompilerDef c = getC().getCompiler( Compiler.MAIN, getOutput( getAOL() ) );
if ( c != null )
{
task.addConfiguredCompiler( c );
}
// add Fortran compiler
CompilerDef fortran = getFortran().getCompiler( Compiler.MAIN, getOutput( getAOL() ) );
if ( fortran != null )
{
task.addConfiguredCompiler( fortran );
}
// end Darren
// add javah include path
File jniDirectory = getJavah().getJniDirectory();
if (jniDirectory.exists())
{
task.createIncludePath().setPath(jniDirectory.getPath());
}
// add java include paths
getJava().addIncludePaths(task, type);
// add dependency include paths
for ( Iterator i = getNarManager().getNarDependencies( "compile" ).iterator(); i.hasNext(); )
{
// FIXME, handle multiple includes from one NAR
NarArtifact narDependency = (NarArtifact) i.next();
String binding = narDependency.getNarInfo().getBinding(getAOL(), Library.STATIC);
getLog().debug( "Looking for " + narDependency + " found binding " + binding);
if ( !binding.equals(Library.JNI ) )
{
File unpackDirectory = getUnpackDirectory();
File include =
getLayout().getIncludeDirectory( unpackDirectory, narDependency.getArtifactId(),
narDependency.getVersion() );
getLog().debug( "Looking for include directory: " + include );
if ( include.exists() )
{
task.createIncludePath().setPath(include.getPath());
} else {
throw new MojoExecutionException(
"NAR: unable to locate include path: " + include);
}
}
}
// add linker
LinkerDef linkerDefinition =
getLinker().getLinker( this, antProject, getOS(), getAOL().getKey() + ".linker.", type );
task.addConfiguredLinker(linkerDefinition);
// add dependency libraries
// FIXME: what about PLUGIN and STATIC, depending on STATIC, should we
// not add all libraries, see NARPLUGIN-96
if ( type.equals( Library.SHARED ) || type.equals( Library.JNI ) || type.equals( Library.EXECUTABLE ) )
{
List depLibOrder = getDependencyLibOrder();
List depLibs = getNarManager().getNarDependencies("compile");
// reorder the libraries that come from the nar dependencies
// to comply with the order specified by the user
if ( ( depLibOrder != null ) && !depLibOrder.isEmpty() )
{
List tmp = new LinkedList();
for ( Iterator i = depLibOrder.iterator(); i.hasNext(); )
{
String depToOrderName = (String) i.next();
for ( Iterator j = depLibs.iterator(); j.hasNext(); )
{
NarArtifact dep = (NarArtifact) j.next();
String depName = dep.getGroupId() + ":" + dep.getArtifactId();
if (depName.equals(depToOrderName))
{
tmp.add(dep);
j.remove();
}
}
}
tmp.addAll(depLibs);
depLibs = tmp;
}
for ( Iterator i = depLibs.iterator(); i.hasNext(); )
{
NarArtifact dependency = (NarArtifact) i.next();
// FIXME no handling of "local"
// FIXME, no way to override this at this stage
String binding = dependency.getNarInfo().getBinding( getAOL(), Library.NONE );
getLog().debug("Using Binding: " + binding);
AOL aol = getAOL();
aol = dependency.getNarInfo().getAOL(getAOL());
getLog().debug("Using Library AOL: " + aol.toString());
if ( !binding.equals( Library.JNI ) && !binding.equals( Library.NONE ) && !binding.equals( Library.EXECUTABLE) )
{
File unpackDirectory = getUnpackDirectory();
File dir =
getLayout().getLibDirectory( unpackDirectory, dependency.getArtifactId(),
dependency.getVersion(), aol.toString(), binding );
getLog().debug("Looking for Library Directory: " + dir);
if ( dir.exists() )
{
LibrarySet libSet = new LibrarySet();
libSet.setProject(antProject);
// FIXME, no way to override
String libs = dependency.getNarInfo().getLibs(getAOL());
if ( ( libs != null ) && !libs.equals( "" ) )
{
getLog().debug("Using LIBS = " + libs);
libSet.setLibs(new CUtil.StringArrayBuilder(libs));
libSet.setDir(dir);
task.addLibset(libSet);
}
}
else
{
getLog().debug( "Library Directory " + dir + " does NOT exist." );
}
// FIXME, look again at this, for multiple dependencies we may need to remove duplicates
String options = dependency.getNarInfo().getOptions( getAOL() );
if ( ( options != null ) && !options.equals( "" ) )
{
getLog().debug("Using OPTIONS = " + options);
LinkerArgument arg = new LinkerArgument();
arg.setValue(options);
linkerDefinition.addConfiguredLinkerArg(arg);
}
String sysLibs = dependency.getNarInfo().getSysLibs( getAOL() );
if ( ( sysLibs != null ) && !sysLibs.equals( "" ) )
{
getLog().debug("Using SYSLIBS = " + sysLibs);
SystemLibrarySet sysLibSet = new SystemLibrarySet();
sysLibSet.setProject(antProject);
sysLibSet.setLibs( new CUtil.StringArrayBuilder( sysLibs ) );
task.addSyslibset(sysLibSet);
}
}
}
}
// Add JVM to linker
getJava().addRuntime( task, getJavaHome( getAOL() ), getOS(), getAOL().getKey() + ".java." );
// execute
try
{
task.execute();
}
catch ( BuildException e )
{
throw new MojoExecutionException("NAR: Compile failed", e);
}
// FIXME, this should be done in CPPTasks at some point
if ( getRuntime( getAOL() ).equals( "dynamic" ) && getOS().equals( OS.WINDOWS )
&& getLinker().getName( null, null ).equals( "msvc" ) && !getLinker().getVersion().startsWith( "6." ) )
{
String libType = library.getType();
if ( libType.equals( Library.JNI ) || libType.equals( Library.SHARED ) )
{
String dll = outFile.getPath() + ".dll";
String manifest = dll + ".manifest";
int result =
NarUtil.runCommand( "mt.exe", new String[] { "/manifest", manifest,
"/outputresource:" + dll + ";#2" }, null, null, getLog() );
if (result != 0)
{
throw new MojoFailureException("MT.EXE failed with exit code: " + result);
}
} else if (libType.equals(Library.EXECUTABLE)) {
String exe = outFile.getPath() + ".exe";
String manifest = exe + ".manifest";
int result = NarUtil.runCommand("mt.exe",
new String[] { "/manifest", manifest,
"/outputresource:" + exe + ";#1" }, null, null, getLog());
if (result != 0)
throw new MojoFailureException(
"MT.EXE failed with exit code: " + result);
}
}
}
}