// Copyright FreeHEP, 2006-2007.
package org.freehep.maven.swig;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DefaultArtifact;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.versioning.VersionRange;
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.codehaus.plexus.archiver.manager.ArchiverManager;
import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner;
import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
import org.codehaus.plexus.util.FileUtils;
import org.freehep.maven.nar.Linker;
import org.freehep.maven.nar.NarArtifact;
import org.freehep.maven.nar.NarInfo;
import org.freehep.maven.nar.NarManager;
import org.freehep.maven.nar.NarUtil;
/**
* Compiles swg files using the swig compiler.
*
* @goal generate
* @description Compiles swg files using the swig compiler.
* @phase generate-sources
* @requiresDependencyResolution compile
* @author <a href="Mark.Donszelmann@slac.stanford.edu">Mark Donszelmann</a>
* @version $Id: SwigMojo.java 13291 2007-09-06 18:28:46Z duns $
*/
public class SwigMojo extends AbstractMojo {
/**
* Skip the running of SWIG
*
* @parameter expression="${swig.skip}" default-value="false"
*/
private boolean skip;
/**
* Force the running of SWIG
*
* @parameter expression="${swig.force}" default-value="false"
*/
private boolean force;
/**
* Define symbol for conditional compilation, same as -D option for swig.
*
* @parameter
*/
private List defines;
/**
* Sets a fake version number, same as -fakeversion for swig.
*
* @parameter
*/
private String fakeVersion;
/**
* Enable C++ processing, same as -c++ option for swig.
*
* @parameter expression="${swig.cpp}" default-value="false"
*/
private boolean cpp;
/**
* Add include paths. By default the current directory is scanned.
*
* @parameter
*/
private List includePaths;
/**
* List of warning numbers to be suppressed, same as -w option for swig.
*
* @parameter expression="${swig.noWarn}"
*/
private String noWarn;
/**
* Enable all warnings, same as -Wall
*
* @parameter expression="${swig.warnAll}" default-value="false"
*/
private boolean warnAll;
/**
* Treat warnings as errors, same as -Werror
*
* @parameter expression="${swig.warnError}" default-value="false"
*/
private boolean warnError;
/**
* The target directory into which to generate the output.
*
* @parameter expression="${project.build.directory}/swig"
*/
private File targetDirectory;
/**
* The package name for the generated java files (fully qualified ex:
* org.freehep.jni).
*
* @parameter expression="${swig.packageName}"
*/
private String packageName;
/**
* The output filename. Defaults to ${source}.cpp or .c depending on cpp option.
*
* @parameter
*/
private String outFile;
/**
* The target directory into which to generate the java output, becomes
* -outdir option for swig.
*
* @parameter expression="${project.build.directory}/swig/java"
*/
private String javaTargetDirectory;
/**
* Remove all *.java files from the output directory. The output directory
* is formed by ${javaTargetDirectory}/${packageName}. This
* setting is ignored (false) if no packageName is supplied.
* All *.java are deleted from the output directory just before
* the swig command is run. This allows the user to configure to
* have the java files of the swig command in the src directory tree.
*
* @parameter expression="false"
*/
private boolean cleanOutputDirectory;
/**
* The directory to look for swig files and swig include files. Also added
* to -I flag when swig is run.
*
* @parameter expression="${basedir}/src/main/swig"
* @required
*/
private String sourceDirectory;
/**
* The swig file to process, normally in source directory set by
* swigDirectory.
*
* @parameter
* @required
*/
private String source;
/**
* The Architecture for picking up swig, Some choices are: "x86", "i386",
* "amd64", "ppc", "sparc", ... Defaults to a derived value from ${os.arch}
*
* @parameter expression="${os.arch}"
* @required
*/
private String architecture;
/**
* The Operating System for picking up swig. Some choices are: "Windows",
* "Linux", "MacOSX", "SunOS", ... Defaults to a derived value from
* ${os.name}
*
* @parameter expression=""
*/
private String os;
/**
* The granularity in milliseconds of the last modification date for testing
* whether a source needs recompilation
*
* @parameter expression="${idlj.staleMillis}" default-value="0"
* @required
*/
private int staleMillis;
/**
* Swig Executable (overrides built-in or user configured reference to NAR)
*
* @parameter expression="${swig.exec}"
*/
private String exec;
/**
* GroupId for the swig NAR
*
* @parameter expression="${swig.groupId}" default-value="org.swig"
*/
private String groupId;
/**
* ArtifactId for the swig NAR
*
* @parameter expression="${swig.artifactId}" default-value="swig"
*/
private String artifactId;
/**
* Version for the swig NAR
*
* @parameter expression="${swig.version}" default-value="1.3.31-1"
*/
private String version;
/**
* @parameter expression="${project}"
* @required
* @readonly
*/
private MavenProject project;
/**
* @parameter expression="${localRepository}"
* @required
* @readonly
*/
private ArtifactRepository localRepository;
/**
* Artifact handler
*
* @component role="org.apache.maven.artifact.handler.ArtifactHandler"
* @required
* @readonly
*/
private ArtifactHandler artifactHandler;
/**
* Artifact resolver, needed to download source jars for inclusion in
* classpath.
*
* @component role="org.apache.maven.artifact.resolver.ArtifactResolver"
* @required
* @readonly
*/
private ArtifactResolver artifactResolver;
/**
* Remote repositories which will be searched for source attachments.
*
* @parameter expression="${project.remoteArtifactRepositories}"
* @required
* @readonly
*/
private List remoteArtifactRepositories;
/**
* To look up Archiver/UnArchiver implementations
*
* @parameter expression="${component.org.codehaus.plexus.archiver.manager.ArchiverManager}"
* @required
*/
private ArchiverManager archiverManager;
private NarManager narManager;
public void execute() throws MojoExecutionException, MojoFailureException {
if (skip) {
getLog()
.warn("SKIPPED Running SWIG compiler on " + source + " ...");
return;
}
os = NarUtil.getOS(os);
// FIXME, should have some function in NarUtil
Linker linker = new Linker("g++");
narManager = new NarManager(getLog(), localRepository,
project, architecture, os, linker);
targetDirectory = new File(targetDirectory, cpp ? "c++" : "c");
if (!targetDirectory.exists()) {
targetDirectory.mkdirs();
}
if (project != null) {
project.addCompileSourceRoot(javaTargetDirectory);
project.addCompileSourceRoot(targetDirectory.getPath());
}
if (packageName != null) {
if (!javaTargetDirectory.endsWith("/")) {
javaTargetDirectory = javaTargetDirectory + "/";
}
javaTargetDirectory += packageName.replace('.', File.separatorChar);
}
if (!FileUtils.fileExists(javaTargetDirectory)) {
FileUtils.mkdir(javaTargetDirectory);
}
if (!sourceDirectory.endsWith("/")) {
sourceDirectory = sourceDirectory + "/";
}
// make sure all NAR dependencies are downloaded and unpacked
// even if packaging is NOT nar
// in nar packaging, downloading happens in generate-sources phase and
// thus may be too late
// unpacking happens in process-sources which is definitely too late
// so we need to handle this here ourselves.
List narArtifacts = narManager.getNarDependencies("compile");
narManager.downloadAttachedNars(narArtifacts,
remoteArtifactRepositories, artifactResolver, null);
narManager.unpackAttachedNars(narArtifacts, archiverManager, null, os);
File swig, swigInclude, swigJavaInclude;
if (exec == null) {
// NOTE, since a project will just load this as a plugin, there is
// no way to look up
// the org.swig:swig dependency, so we hardcode that in here, but it
// is configurable
// in the configuration part of this plugin.
Artifact swigJar = new DefaultArtifact(groupId, artifactId,
VersionRange.createFromVersion(version), "compile", "jar",
"", artifactHandler);
// download jar file
try {
getLog().debug("freehep-swig-plugin: Resolving " + swigJar);
artifactResolver.resolve(swigJar, remoteArtifactRepositories,
localRepository);
} catch (ArtifactNotFoundException e) {
String message = "Jar not found " + swigJar;
throw new MojoExecutionException(message, e);
} catch (ArtifactResolutionException e) {
String message = "Jar cannot be resolved " + swigJar;
throw new MojoExecutionException(message, e);
}
NarInfo info = narManager.getNarInfo(swigJar);
getLog().debug(info.toString());
NarArtifact swigNar = new NarArtifact(swigJar, info);
// download attached nars
List swigNarArtifacts = new ArrayList();
swigNarArtifacts.add(swigNar);
narManager.downloadAttachedNars(swigNarArtifacts,
remoteArtifactRepositories, artifactResolver, null);
narManager.unpackAttachedNars(swigNarArtifacts, archiverManager,
null, os);
swig = new File(narManager.getNarFile(swigNar).getParentFile(),
"nar");
swigInclude = new File(swig, "include");
swigJavaInclude = new File(swigInclude, "java");
swig = new File(swig, "bin");
swig = new File(swig, NarUtil
.getAOL(architecture, os, linker, null));
swig = new File(swig, "swig");
} else {
swig = new File(exec);
swigInclude = null;
swigJavaInclude = null;
}
File sourceFile = new File(sourceDirectory);
File targetFile = targetDirectory;
SourceInclusionScanner scanner = new StaleSourceScanner(staleMillis,
Collections.singleton(source), Collections.EMPTY_SET);
SuffixMapping mapping = new SuffixMapping(".swg", ".flag");
scanner.addSourceMapping(mapping);
try {
Set files = scanner.getIncludedSources(sourceFile, targetFile);
if (!files.isEmpty() || force) {
if (cleanOutputDirectory && (packageName != null) && FileUtils.fileExists(javaTargetDirectory)) {
getLog().info("Cleaning "+javaTargetDirectory);
String[] filesToRemove = FileUtils.getFilesFromExtension(javaTargetDirectory, new String[] { "java" });
for (int i=0; i<filesToRemove.length; i++) {
File f = new File(filesToRemove[i]);
f.delete();
}
}
getLog().info(
(force ? "FORCE " : "") + "Running SWIG compiler on "
+ source + " ...");
int error = runCommand(generateCommandLine(swig, swigInclude,
swigJavaInclude));
if (error != 0) {
throw new MojoFailureException("SWIG returned error code "
+ error);
}
File flagFile = new File(targetDirectory, FileUtils.basename(
source, ".swg")
+ ".flag");
FileUtils.fileDelete(flagFile.getPath());
FileUtils.fileWrite(flagFile.getPath(), "");
} else {
getLog().info("Nothing to swig - all classes are up to date");
}
} catch (InclusionScanException e) {
throw new MojoExecutionException("IDLJ: Source scanning failed", e);
} catch (IOException e) {
throw new MojoExecutionException(
"SWIG: Creation of timestamp flag file failed", e);
}
}
private String[] generateCommandLine(File swig, File swigInclude,
File swigJavaInclude) throws MojoExecutionException,
MojoFailureException {
List cmdLine = new ArrayList();
cmdLine.add(swig.toString());
if (fakeVersion != null) {
cmdLine.add("-fakeversion");
cmdLine.add(fakeVersion);
}
if (getLog().isDebugEnabled()) {
cmdLine.add("-v");
}
// FIXME hardcoded
cmdLine.add("-java");
if (cpp) {
cmdLine.add("-c++");
}
// defines
if (defines != null) {
for (Iterator i = defines.iterator(); i.hasNext();) {
cmdLine.add("-D" + i.next());
}
}
// warnings
if (noWarn != null) {
String noWarns[] = noWarn.split(",| ");
for (int i = 0; i < noWarns.length; i++) {
cmdLine.add("-w" + noWarns[i]);
}
}
if (warnAll) {
cmdLine.add("-Wall");
}
if (warnError) {
cmdLine.add("-Werror");
}
// output file
String baseName = FileUtils.basename(source);
cmdLine.add("-o");
File outputFile = outFile != null ? new File(targetDirectory, outFile) : new File(targetDirectory, baseName + (cpp ? "cxx" : "c"));
cmdLine.add(outputFile.toString());
// package for java code
if (packageName != null) {
cmdLine.add("-package");
cmdLine.add(packageName);
}
// output dir for java code
cmdLine.add("-outdir");
cmdLine.add(javaTargetDirectory);
// user added include dirs
if (includePaths != null) {
for (Iterator i = includePaths.iterator(); i.hasNext();) {
cmdLine.add("-I" + i.next());
}
}
// default include dirs
cmdLine.add("-I" + "src/main/include");
cmdLine.add("-I" + sourceDirectory);
// NAR dependency include dirs
List narIncludes = narManager.getNarDependencies("compile");
for (Iterator i = narIncludes.iterator(); i.hasNext();) {
Artifact narInclude = (Artifact) i.next();
File narIncludeDir = new File(narManager.getNarFile(narInclude)
.getParentFile(), "nar");
narIncludeDir = new File(narIncludeDir, "include");
if (narIncludeDir.isDirectory()) {
cmdLine.add("-I" + narIncludeDir);
}
}
// system swig include dirs
if (swigJavaInclude != null)
cmdLine.add("-I" + swigJavaInclude.toString());
if (swigInclude != null)
cmdLine.add("-I" + swigInclude.toString());
// swig file
cmdLine.add(sourceDirectory + source);
StringBuffer b = new StringBuffer();
for (Iterator i = cmdLine.iterator(); i.hasNext();) {
b.append((String) i.next());
if (i.hasNext())
b.append(' ');
}
getLog().info(b.toString());
return (String[]) cmdLine.toArray(new String[cmdLine.size()]);
}
private int runCommand(String[] cmdLine) throws MojoExecutionException {
try {
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(cmdLine);
StreamGobbler errorGobbler = new StreamGobbler(process
.getErrorStream(), true);
StreamGobbler outputGobbler = new StreamGobbler(process
.getInputStream(), false);
errorGobbler.start();
outputGobbler.start();
return process.waitFor();
} catch (Throwable e) {
throw new MojoExecutionException("Could not launch " + cmdLine[0],
e);
}
}
class StreamGobbler extends Thread {
InputStream is;
boolean error;
StreamGobbler(InputStream is, boolean error) {
this.is = is;
this.error = error;
}
public void run() {
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(is));
String line = null;
while ((line = reader.readLine()) != null) {
if (error) {
getLog().error(line);
} else {
getLog().debug(line);
}
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}