package mandelbrot.ocamljava_maven_plugin;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import mandelbrot.dependency.data.DependencyGraph;
import mandelbrot.dependency.data.ModuleDescriptor;
import mandelbrot.ocamljava_maven_plugin.util.ArtifactDescriptor;
import mandelbrot.ocamljava_maven_plugin.util.ClassPathGatherer;
import mandelbrot.ocamljava_maven_plugin.util.FileExtensions;
import mandelbrot.ocamljava_maven_plugin.util.FileMappings;
import ocaml.compilers.ocamljavaMain;
import org.apache.commons.io.FileUtils;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import org.codehaus.plexus.util.StringUtils;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
public abstract class OcamlJavaCompileAbstractMojo extends OcamlJavaAbstractMojo {
private static final Boolean FORK_BY_DEFAULT = Boolean.FALSE;
/**
* Record debugging information.
*
*/
@Parameter(defaultValue="false")
protected boolean recordDebugInfo = false;
/**
* Optimize code for size rather than speed.
*
*/
@Parameter(defaultValue="false")
protected boolean compact = false;
@Override
public final void execute() throws MojoExecutionException, MojoFailureException {
if (!ensureTargetDirectoryExists()) {
getLog().error("Could not create target directory");
return;
}
if (!ocamlSourceDirectory.exists()) {
getLog().error(
"Source directory \"" + ocamlSourceDirectory
+ "\" is not valid.");
return;
}
new OcamlRuntimeContainer.Builder()
.setMojo(this)
.setRunningArtifact(project.getArtifactMap().get(
"org.ocamljava:ocamljava"))
.setOcamlRuntime(
project.getArtifactMap().get(
"org.ocamljava:ocamlrun"))
.setStagingFolder(outputDirectory)
.build();
final Object object = System.getProperty(FORK_PROPERTY_NAME);
if (Boolean.parseBoolean(Optional.fromNullable(object).or(FORK_BY_DEFAULT)
.toString())) {
getLog().info("forking process");
final boolean forkAgain = false;
invokePlugin(fullyQualifiedGoal(), forkAgain);
return;
}
getLog().info(
"ocaml source directory: "
+ ocamlSourceDirectory.getPath());
try {
final Multimap<String, String> ocamlSourceFiles = gatherOcamlSourceFiles(chooseOcamlSourcesDirectory());
final File dependencyGraphTarget = chooseDependencyGraphTargetFullPath();
if (getLog().isInfoEnabled())
getLog().info("full path for dependency target: " + dependencyGraphTarget.getPath());
final boolean madeDirs = dependencyGraphTarget.getParentFile().mkdirs();
if (getLog().isDebugEnabled()) {
getLog().debug("made directory \"" + dependencyGraphTarget + "\"? " + madeDirs);
}
final DependencyGraph dependencyGraph = DependencyGraph.read(dependencyGraphTarget);
if (getLog().isInfoEnabled())
getLog().info("ordered modules: " + dependencyGraph);
final Set<Entry<String, Collection<ModuleDescriptor>>> entrySet = dependencyGraph.getDependencies().entrySet();
final ImmutableSet.Builder<String> includeDirectoryBuilder = ImmutableSet.builder();
for (final Entry<String, Collection<ModuleDescriptor>> entry : entrySet) {
compileSources(includeDirectoryBuilder.build(), entry.getValue());
includeDirectoryBuilder.addAll(Collections2.transform(entry.getValue(), new Function<ModuleDescriptor, String>() {
@Override public String apply(final ModuleDescriptor moduleDescriptor) {
return moduleDescriptor.getModuleFile().get().getParent();
}
}));
}
moveCompiledFiles(ocamlSourceFiles.get(OcamlJavaConstants.IMPL_SOURCE_EXTENSION), chooseOcamlCompiledSourcesTarget(),
chooseOcamlSourcesDirectory().getPath(),
ImmutableSet.of(OcamlJavaConstants.COMPILED_IMPL_JAVA_EXTENSION,
OcamlJavaConstants.OBJECT_BINARY_EXTENSION, OcamlJavaConstants.COMPILED_INTERFACE_EXTENSION));
} catch (final Exception e) {
throw new MojoExecutionException("ocamljava threw an error", e);
}
}
protected abstract File chooseOcamlSourcesDirectory();
private Collection<String> compileSources(final Collection<String> includeDirs,
final Collection<ModuleDescriptor> moduleDescriptors) throws MojoExecutionException, IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
final Collection<String> sourceFiles = Collections2.transform(moduleDescriptors, ModuleDescriptor.toFileTransform());
final Multimap<String, String> byPathMapping = FileMappings
.buildPathMap(sourceFiles);
final Set<String> pathMappings = byPathMapping.keySet();
final ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for (final String path : pathMappings) {
if (!sourceFiles.isEmpty()) {
final String[] sourceArgs = generateCommandLineArguments(ImmutableSet.<String>builder()
.addAll(Collections2.filter(includeDirs,
new Predicate<String>() {
@Override public boolean apply(final String input) { return new File(input).exists(); }}))
.addAll(pathMappings)
.build(),
toPackage(ocamlSourceDirectory, path), sourceFiles).toArray(new String[] {});
if (getLog().isInfoEnabled())
getLog().info("ocamljava compile args: " + Joiner.on(" ").join(ImmutableList.copyOf(sourceArgs)));
final ocamljavaMain main = ocamljavaMain.mainWithReturn(sourceArgs);
checkForErrors("ocamljava compiler error while processing path: " + path, main);
}
}
return builder.build();
}
private Set<String> moveCompiledFiles(final Collection<String> ocamlSourceFiles,
final String outputDirectoryQualifier, final String toFilter, final Set<String> extensions) {
final ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for (final String extension : extensions)
builder.addAll(moveCompiledSourceFilesToTargetDirectory(ocamlSourceFiles,
extension, outputDirectoryQualifier, toFilter));
return builder.build();
}
private Set<String> moveCompiledSourceFilesToTargetDirectory(
final Collection<String> ocamlSourceFiles,
final String compiledExtension,
final String outputDirectoryQualifier, final String toFilter) {
final Collection<String> transformed = Collections2.transform(
ocamlSourceFiles, new Function<String, String>() {
@Override
public String apply(final String path) {
final File srcFile = new File(path);
final String compiledSourceName = FileExtensions.changeExtension(
srcFile, compiledExtension);
final File compiledSrcFile = new File(srcFile
.getParent()
+ File.separator
+ compiledSourceName);
final File qualifiedOutputDirectory = new File(
outputDirectory.getPath()
+ File.separator
+ outputDirectoryQualifier
+ File.separator
+ compiledSrcFile.getParent().replace(
toFilter, ""));
qualifiedOutputDirectory.mkdirs();
try {
if (compiledSrcFile.exists()) {
if (getLog().isInfoEnabled())
getLog().info(
"moving src " + compiledSrcFile
+ " to output directory: "
+ qualifiedOutputDirectory);
FileUtils.copyFileToDirectory(compiledSrcFile,
qualifiedOutputDirectory, true);
FileUtils.deleteQuietly(compiledSrcFile);
} else
getLog().warn(
"skipping transfer of file "
+ compiledSrcFile
+ " which doesn't exist.");
} catch (final IOException e) {
throw new RuntimeException(
"error moving compiled sources", e);
}
return outputDirectory.getPath()
+ File.separator + compiledSourceName;
}
});
return ImmutableSet.copyOf(transformed);
}
private List<String> generateCommandLineArguments(
final Collection<String> includePaths, final String packageName,
final Collection<String> ocamlSourceFiles)
throws MojoExecutionException {
final ImmutableList.Builder<String> builder = ImmutableList
.<String> builder();
if (javaExtensions)
builder.add("javalib.cmja");
if (recordDebugInfo) {
builder.add(OcamlJavaConstants.RECORD_DEBUGGING_INFO_OPTION);
}
if (javaExtensions) {
builder.add(OcamlJavaConstants.JAVA_EXTENSIONS_OPTION);
}
if (compact) {
builder.add(OcamlJavaConstants.COMPACT_OPTION);
}
if (!StringUtils.isBlank(packageName)) {
builder.add(OcamlJavaConstants.JAVA_PACKAGE_OPTION)
.add(packageName);
}
addIncludePaths(includePaths, builder);
final ImmutableSet<String> classPathElements = ImmutableSet.<String>builder()
.addAll(new ClassPathGatherer(this).getClassPath(project, false)).build();;
for (final String classPath : classPathElements) {
builder
.add(OcamlJavaConstants.CLASSPATH_OPTION)
.add(classPath);
}
builder
.add(OcamlJavaConstants.COMPILE_SOURCES_OPTION)
.addAll(ocamlSourceFiles);
return builder.build();
}
private boolean ensureTargetDirectoryExists() {
if (outputDirectory.exists()) {
return true;
}
return outputDirectory.mkdirs();
}
protected abstract String chooseOcamlCompiledSourcesTarget();
public abstract String fullyQualifiedGoal();
}