com.mysema.maven.apt.AbstractProcessorMojo Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2009 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.maven.apt;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.commons.io.FileUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.Scanner;
import org.codehaus.plexus.util.StringUtils;
import org.sonatype.plexus.build.incremental.BuildContext;
/**
* Base class for AnnotationProcessorMojo implementations
*
* @author tiwe
*
*/
public abstract class AbstractProcessorMojo extends AbstractMojo {
private static final String JAVA_FILE_FILTER = "/*.java";
private static final String[] ALL_JAVA_FILES_FILTER = new String[] { "**" + JAVA_FILE_FILTER };
/**
* @component
*/
private BuildContext buildContext;
/**
* @parameter expression="${project}" readonly=true required=true
*/
private MavenProject project;
/**
* @parameter
*/
private String[] processors;
/**
* @parameter
*/
private String processor;
/**
* @parameter expression="${project.build.sourceEncoding}" required=true
*/
private String sourceEncoding;
/**
* @parameter
*/
private Map options;
/**
* @parameter
*/
private Map compilerOptions;
/**
* A list of inclusion package filters for the apt processor.
*
* If not specified all sources will be used for apt processor
*
*
* e.g.:
* <includes>
* <include>com.mypackge.**.bo.**</include>
* </includes>
*
*
* will include all files which match com/mypackge/ ** /bo/ ** / *.java
*
* @parameter
*/
private Set includes = new HashSet();
/**
* @parameter
*/
private boolean showWarnings = false;
/**
* @parameter
*/
private boolean logOnlyOnError = false;
/**
* @parameter expression="${plugin.artifacts}" readonly=true required=true
*/
private List pluginArtifacts;
/**
* @parameter
*/
private boolean ignoreDelta = true;
@SuppressWarnings("unchecked")
private String buildCompileClasspath() {
List pathElements = null;
try {
if (isForTest()) {
pathElements = project.getTestClasspathElements();
} else {
pathElements = project.getCompileClasspathElements();
}
} catch (DependencyResolutionRequiredException e) {
super.getLog().warn("exception calling getCompileClasspathElements", e);
return null;
}
if (pluginArtifacts != null) {
for (Artifact a : pluginArtifacts) {
if (a.getFile() != null) {
pathElements.add(a.getFile().getAbsolutePath());
}
}
}
if (pathElements.isEmpty()) {
return null;
}
StringBuilder result = new StringBuilder();
int i = 0;
for (i = 0; i < pathElements.size() - 1; ++i) {
result.append(pathElements.get(i)).append(File.pathSeparatorChar);
}
result.append(pathElements.get(i));
return result.toString();
}
private String buildProcessor() {
if (processors != null) {
StringBuilder result = new StringBuilder();
for (String processor : processors) {
if (result.length() > 0) {
result.append(",");
}
result.append(processor);
}
return result.toString();
} else if (processor != null) {
return processor;
} else {
String error = "Either processor or processors need to be given";
getLog().error(error);
throw new IllegalArgumentException(error);
}
}
private List buildCompilerOptions(String processor, String compileClassPath,
String outputDirectory) throws IOException {
Map compilerOpts = new LinkedHashMap();
// Default options
compilerOpts.put("cp", compileClassPath);
if (sourceEncoding != null) {
compilerOpts.put("encoding", sourceEncoding);
}
compilerOpts.put("proc:only", null);
compilerOpts.put("processor", processor);
if (options != null) {
for (Map.Entry entry : options.entrySet()) {
if (entry.getValue() != null) {
compilerOpts.put("A" + entry.getKey() + "=" + entry.getValue(), null);
} else {
compilerOpts.put("A" + entry.getKey() + "=", null);
}
}
}
if (outputDirectory != null) {
compilerOpts.put("s", outputDirectory);
}
if (!showWarnings) {
compilerOpts.put("nowarn", null);
}
StringBuilder builder = new StringBuilder();
for (File file : getSourceDirectories()) {
if (builder.length() > 0) {
builder.append(";");
}
builder.append(file.getCanonicalPath());
}
compilerOpts.put("sourcepath", builder.toString());
// User options override default options
if (compilerOptions != null) {
compilerOpts.putAll(compilerOptions);
}
List opts = new ArrayList(compilerOpts.size() * 2);
for (Map.Entry compilerOption : compilerOpts.entrySet()) {
opts.add("-" + compilerOption.getKey());
String value = compilerOption.getValue();
if (StringUtils.isNotBlank(value)) {
opts.add(value);
}
}
return opts;
}
/**
* Filter files for apt processing based on the {@link #includes} filter and
* also taking into account m2e {@link BuildContext} to filter-out unchanged
* files when invoked as incremental build
*
* @param directories
* source directories in which files are located for apt processing
*
* @return files for apt processing. Returns empty set when there is no
* files to process
*/
private Set filterFiles(Set directories) {
String[] filters = ALL_JAVA_FILES_FILTER;
if (includes != null && !includes.isEmpty()) {
filters = includes.toArray(new String[includes.size()]);
for (int i = 0; i < filters.length; i++) {
filters[i] = filters[i].replace('.', '/') + JAVA_FILE_FILTER;
}
}
Set files = new HashSet();
for (File directory : directories) {
// support for incremental build in m2e context
Scanner scanner = buildContext.newScanner(directory, false);
scanner.setIncludes(filters);
scanner.scan();
String[] includedFiles = scanner.getIncludedFiles();
// check also for possible deletions
if (buildContext.isIncremental() && (includedFiles == null || includedFiles.length == 0)) {
scanner = buildContext.newDeleteScanner(directory);
scanner.setIncludes(filters);
scanner.scan();
includedFiles = scanner.getIncludedFiles();
}
// get all sources if ignoreDelta and at least one source file has changed
if (ignoreDelta && buildContext.isIncremental() && includedFiles != null && includedFiles.length > 0) {
scanner = buildContext.newScanner(directory, true);
scanner.setIncludes(filters);
scanner.scan();
includedFiles = scanner.getIncludedFiles();
}
if (includedFiles != null) {
for (String includedFile : includedFiles) {
files.add(new File(scanner.getBasedir(), includedFile));
}
}
}
return files;
}
public void execute() throws MojoExecutionException {
if (getOutputDirectory() == null) {
return;
}
if ("true".equals(System.getProperty("maven.apt.skip"))) {
return;
}
if (!getOutputDirectory().exists()) {
getOutputDirectory().mkdirs();
}
// make sure to add compileSourceRoots also during configuration build in m2e context
if (isForTest()) {
project.addTestCompileSourceRoot(getOutputDirectory().getAbsolutePath());
} else {
project.addCompileSourceRoot(getOutputDirectory().getAbsolutePath());
}
Set sourceDirectories = getSourceDirectories();
getLog().debug("Using build context: " + buildContext);
StandardJavaFileManager fileManager = null;
try {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if (compiler == null) {
throw new MojoExecutionException("You need to run build with JDK or have tools.jar on the classpath."
+ "If this occures during eclipse build make sure you run eclipse under JDK as well");
}
Set files = filterFiles(sourceDirectories);
if (files.isEmpty()) {
getLog().debug("No Java sources found (skipping)");
return;
}
fileManager = compiler.getStandardFileManager(null, null, null);
Iterable extends JavaFileObject> compilationUnits1 = fileManager.getJavaFileObjectsFromFiles(files);
String compileClassPath = buildCompileClasspath();
String processor = buildProcessor();
String outputDirectory = getOutputDirectory().getPath();
File tempDirectory = null;
if (buildContext.isIncremental()) {
tempDirectory = new File(project.getBuild().getDirectory(), "apt"+System.currentTimeMillis());
tempDirectory.mkdirs();
outputDirectory = tempDirectory.getAbsolutePath();
}
List compilerOptions = buildCompilerOptions(processor, compileClassPath, outputDirectory);
Writer out = null;
if (logOnlyOnError) {
out = new StringWriter();
}
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
CompilationTask task = compiler.getTask(out, fileManager, null, compilerOptions, null, compilationUnits1);
Future future = executor.submit(task);
Boolean rv = future.get();
if (Boolean.FALSE.equals(rv) && logOnlyOnError) {
getLog().error(out.toString());
}
} finally {
executor.shutdown();
if (tempDirectory != null) {
FileSync.syncFiles(tempDirectory, getOutputDirectory());
FileUtils.deleteDirectory(tempDirectory);
}
}
buildContext.refresh(getOutputDirectory());
} catch (Exception e1) {
getLog().error("execute error", e1);
throw new MojoExecutionException(e1.getMessage(), e1);
} finally {
if (fileManager != null) {
try {
fileManager.close();
} catch (Exception e) {
getLog().warn("Unable to close fileManager", e);
}
}
}
}
protected abstract File getOutputDirectory();
@SuppressWarnings("unchecked")
protected Set getSourceDirectories() {
File outputDirectory = getOutputDirectory();
String outputPath = outputDirectory.getAbsolutePath();
Set directories = new HashSet();
List directoryNames = isForTest() ? project.getTestCompileSourceRoots()
: project.getCompileSourceRoots();
for (String name : directoryNames) {
File file = new File(name);
if (!file.getAbsolutePath().equals(outputPath) && file.exists()) {
directories.add(file);
}
}
return directories;
}
protected boolean isForTest() {
return false;
}
public void setBuildContext(BuildContext buildContext) {
this.buildContext = buildContext;
}
public void setProject(MavenProject project) {
this.project = project;
}
public void setProcessors(String[] processors) {
this.processors = processors;
}
public void setProcessor(String processor) {
this.processor = processor;
}
public void setSourceEncoding(String sourceEncoding) {
this.sourceEncoding = sourceEncoding;
}
public void setOptions(Map options) {
this.options = options;
}
public void setCompilerOptions(Map compilerOptions) {
this.compilerOptions = compilerOptions;
}
public void setIncludes(Set includes) {
this.includes = includes;
}
public void setShowWarnings(boolean showWarnings) {
this.showWarnings = showWarnings;
}
public void setLogOnlyOnError(boolean logOnlyOnError) {
this.logOnlyOnError = logOnlyOnError;
}
public void setPluginArtifacts(List pluginArtifacts) {
this.pluginArtifacts = pluginArtifacts;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy