package org.jetbrains.kotlin.maven.kapt;
import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
import org.apache.maven.artifact.resolver.ResolutionErrorHandler;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.compiler.DependencyCoordinate;
import org.apache.maven.plugins.annotations.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments;
import org.jetbrains.kotlin.maven.K2JVMCompileMojo;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import static org.jetbrains.kotlin.maven.kapt.AnnotationProcessingManager.getGeneratedClassesDirectory;
import static org.jetbrains.kotlin.maven.kapt.AnnotationProcessingManager.getGeneratedSourcesDirectory;
import static org.jetbrains.kotlin.maven.kapt.AnnotationProcessingManager.getStubsDirectory;
/** @noinspection UnusedDeclaration */
@Mojo(name = "kapt", defaultPhase = LifecyclePhase.PROCESS_SOURCES, requiresDependencyResolution = ResolutionScope.COMPILE, threadSafe = false)
public class KaptJVMCompilerMojo extends K2JVMCompileMojo {
@Parameter
private String[] annotationProcessors;
@Parameter
private List<DependencyCoordinate> annotationProcessorPaths;
@Parameter
private boolean useLightAnalysis = true;
@Parameter
private boolean correctErrorTypes = false;
// Components for AnnotationProcessingManager
@Component
private ArtifactHandlerManager artifactHandlerManager;
@Parameter( defaultValue = "${session}", readonly = true, required = true )
private MavenSession session;
@Component
private ResolutionErrorHandler resolutionErrorHandler;
private AnnotationProcessingManager cachedAnnotationProcessingManager;
private AnnotationProcessingManager getAnnotationProcessingManager() {
if (cachedAnnotationProcessingManager != null) {
return cachedAnnotationProcessingManager;
}
cachedAnnotationProcessingManager = new AnnotationProcessingManager(
artifactHandlerManager, session, project, system, resolutionErrorHandler);
return cachedAnnotationProcessingManager;
}
@NotNull
private List<KaptOption> getKaptOptions(
@NotNull K2JVMCompilerArguments arguments,
@NotNull AnnotationProcessingManager.ResolvedArtifacts resolvedArtifacts
) {
List<KaptOption> options = new ArrayList<KaptOption>();
options.add(new KaptOption("aptOnly", true));
options.add(new KaptOption("useLightAnalysis", useLightAnalysis));
options.add(new KaptOption("correctErrorTypes", correctErrorTypes));
options.add(new KaptOption("processors", annotationProcessors));
if (arguments.verbose) {
options.add(new KaptOption("verbose", true));
}
for (String entry : resolvedArtifacts.annotationProcessingClasspath) {
options.add(new KaptOption("apclasspath", entry));
}
String sourceSetName = getSourceSetName();
File sourcesDirectory = getGeneratedSourcesDirectory(project, sourceSetName);
File classesDirectory = getGeneratedClassesDirectory(project, sourceSetName);
File stubsDirectory = getStubsDirectory(project, sourceSetName);
addKaptSourcesDirectory(sourcesDirectory.getPath());
mkdirsSafe(sourcesDirectory);
mkdirsSafe(classesDirectory);
mkdirsSafe(stubsDirectory);
options.add(new KaptOption("sources", sourcesDirectory.getAbsolutePath()));
options.add(new KaptOption("classes", classesDirectory.getAbsolutePath()));
options.add(new KaptOption("stubs", stubsDirectory.getAbsolutePath()));
return options;
}
protected void addKaptSourcesDirectory(@NotNull String path) {
project.addCompileSourceRoot(path);
}
private void mkdirsSafe(@NotNull File directory) {
if (!directory.mkdirs()) {
getLog().warn("Unable to create directory " + directory);
}
}
@Override
protected void configureSpecificCompilerArguments(@NotNull K2JVMCompilerArguments arguments) throws MojoExecutionException {
super.configureSpecificCompilerArguments(arguments);
AnnotationProcessingManager.ResolvedArtifacts resolvedArtifacts;
try {
resolvedArtifacts = getAnnotationProcessingManager().resolveAnnotationProcessors(annotationProcessorPaths);
}
catch (Exception e) {
throw new MojoExecutionException("Error while processing kapt options", e);
}
String[] kaptOptions = renderKaptOptions(getKaptOptions(arguments, resolvedArtifacts));
arguments.pluginOptions = joinArrays(arguments.pluginOptions, kaptOptions);
String jdkToolsJarPath = getJdkToolsJarPath();
arguments.pluginClasspaths = joinArrays(arguments.pluginClasspaths,
(jdkToolsJarPath == null)
? new String[] { resolvedArtifacts.kaptCompilerPluginArtifact }
: new String[] { jdkToolsJarPath, resolvedArtifacts.kaptCompilerPluginArtifact });
}
@Nullable
private String getJdkToolsJarPath() {
String javaHomePath = System.getProperty("java.home");
if (javaHomePath == null || javaHomePath.isEmpty()) {
getLog().warn("Can't determine Java home, 'java.home' property does not exist");
return null;
}
File javaHome = new File(javaHomePath);
File toolsJar = new File(javaHome, "lib/tools.jar");
if (toolsJar.exists()) {
return toolsJar.getAbsolutePath();
}
// We might be inside jre.
if (javaHome.getName().equals("jre")) {
toolsJar = new File(javaHome.getParent(), "lib/tools.jar");
if (toolsJar.exists()) {
return toolsJar.getAbsolutePath();
}
}
getLog().debug(toolsJar.getAbsolutePath() + " does not exist");
getLog().warn("'tools.jar' was not found, kapt may work unreliably");
return null;
}
@NotNull
private String[] renderKaptOptions(@NotNull List<KaptOption> options) {
String[] result = new String[options.size()];
int i = 0;
for (KaptOption option : options) {
result[i++] = option.toString();
}
return result;
}
@NotNull
private String[] joinArrays(@Nullable String[] first, @Nullable String[] second) {
if (first == null) {
first = new String[0];
}
if (second == null) {
second = new String[0];
}
String[] result = new String[first.length + second.length];
System.arraycopy(first, 0, result, 0, first.length);
System.arraycopy(second, 0, result, first.length, second.length);
return result;
}
@Override
protected boolean isIncremental() {
return false;
}
}