// Copyright FreeHEP, 2005-2007.
package org.freehep.maven.nar;
import java.io.File;
import java.io.IOException;
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.LinkerDef;
import net.sf.antcontrib.cpptasks.OutputTypeEnum;
import net.sf.antcontrib.cpptasks.RuntimeType;
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.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
* @requiresDependencyResolution compile
* @author <a href="Mark.Donszelmann@slac.stanford.edu">Mark Donszelmann</a>
* @version $Id: NarCompileMojo.java 13350 2007-09-20 18:42:29Z duns $
*/
public class NarCompileMojo extends AbstractCompileMojo {
public void execute() throws MojoExecutionException, MojoFailureException {
if (shouldSkip())
return;
// 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) {
for (Iterator i = getLibraries().iterator(); i.hasNext();) {
createLibrary(getAntProject(), (Library) i.next());
}
}
try {
// FIXME, should the include paths be defined at a higher level ?
getCpp().copyIncludeFiles(getMavenProject(),
new File(getTargetDirectory(), "include"));
} catch (IOException e) {
throw new MojoExecutionException(
"NAR: could not copy include files", e);
}
}
private List getSourcesFor(Compiler compiler) throws MojoFailureException {
try {
File srcDir = compiler.getSourceDirectory();
return srcDir.exists() ? FileUtils.getFiles(srcDir, StringUtils
.join(compiler.getIncludes().iterator(), ","), null)
: Collections.EMPTY_LIST;
} catch (IOException e) {
return Collections.EMPTY_LIST;
}
}
private void createLibrary(Project antProject, Library library)
throws MojoExecutionException, MojoFailureException {
// configure task
CCTask task = new CCTask();
task.setProject(antProject);
// 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());
// outDir
File outDir = new File(getTargetDirectory(), type
.equals(Library.EXECUTABLE) ? "bin" : "lib");
outDir = new File(outDir, getAOL().toString());
if (!type.equals(Library.EXECUTABLE))
outDir = new File(outDir, type);
outDir.mkdirs();
// outFile
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
task.addConfiguredCompiler(getCpp().getCompiler(type, getOutput(getAOL())));
// add C compiler
task.addConfiguredCompiler(getC().getCompiler(type, getOutput(getAOL())));
// add Fortran compiler
task.addConfiguredCompiler(getFortran().getCompiler(type, getOutput(getAOL())));
// 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 include = new File(getNarManager().getNarFile(
narDependency).getParentFile(), "nar/include");
getLog().debug("Looking for for 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.STATIC);
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)) {
File dir = new File(getNarManager().getNarFile(dependency)
.getParentFile(), "nar/lib/" + 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") &&
NarUtil.getEnv("MSVCVer", "MSVCVer", "6.0").startsWith("8.")) {
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, getLog());
if (result != 0)
throw new MojoFailureException("MT.EXE failed with exit code: " + result);
}
}
}
}