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.artifact.Artifact; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.project.MavenProject; 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 */ private 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() ) ); } 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 ); // add C++ compiler CompilerDef cpp = getCpp().getCompiler( type, getOutput( getAOL() ) ); if ( cpp != null ) { task.addConfiguredCompiler( cpp ); } // add C compiler CompilerDef c = getC().getCompiler( type, getOutput( getAOL() ) ); if ( c != null ) { task.addConfiguredCompiler( c ); } // add Fortran compiler CompilerDef fortran = getFortran().getCompiler( type, getOutput( getAOL() ) ); if ( fortran != null ) { task.addConfiguredCompiler( fortran ); } // 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() ); } } } // 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 ) ) { 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 ); } } } } }