package org.jetbrains.kotlin.maven.kapt; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DefaultArtifact; import org.apache.maven.artifact.handler.ArtifactHandler; import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; import org.apache.maven.artifact.resolver.ArtifactResolutionResult; import org.apache.maven.artifact.resolver.ResolutionErrorHandler; import org.apache.maven.artifact.versioning.VersionRange; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.compiler.DependencyCoordinate; import org.apache.maven.project.MavenProject; import org.apache.maven.repository.RepositorySystem; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.*; public class AnnotationProcessingManager { private final ArtifactHandlerManager artifactHandlerManager; private final MavenSession session; private final MavenProject project; private final RepositorySystem repositorySystem; private final ResolutionErrorHandler resolutionErrorHandler; AnnotationProcessingManager(@NotNull ArtifactHandlerManager artifactHandlerManager, @NotNull MavenSession session, @NotNull MavenProject project, @NotNull RepositorySystem repositorySystem, @NotNull ResolutionErrorHandler resolutionErrorHandler) { this.artifactHandlerManager = artifactHandlerManager; this.session = session; this.project = project; this.repositorySystem = repositorySystem; this.resolutionErrorHandler = resolutionErrorHandler; } private final static DependencyCoordinate KAPT_DEPENDENCY = new DependencyCoordinate(); public final static String COMPILE_SOURCE_SET_NAME = "compile"; public final static String TEST_SOURCE_SET_NAME = "test"; static { KAPT_DEPENDENCY.setGroupId("org.jetbrains.kotlin"); KAPT_DEPENDENCY.setArtifactId("kotlin-annotation-processing-maven"); try { KAPT_DEPENDENCY.setVersion(getMavenPluginVersion()); } catch (MojoExecutionException e) { throw new RuntimeException(e); } } @NotNull public static File getGeneratedSourcesDirectory(@NotNull MavenProject project, @NotNull String sourceSetName) { return new File(getTargetDirectory(project), "generated-sources/kapt/" + sourceSetName); } @NotNull static File getStubsDirectory(@NotNull MavenProject project, @NotNull String sourceSetName) { return new File(getTargetDirectory(project), "kaptStubs/" + sourceSetName); } @NotNull static File getGeneratedClassesDirectory(@NotNull MavenProject project, @NotNull String sourceSetName) { if (sourceSetName.equals(COMPILE_SOURCE_SET_NAME)) { return new File(project.getModel().getBuild().getOutputDirectory()); } else if (sourceSetName.equals(TEST_SOURCE_SET_NAME)) { return new File(project.getModel().getBuild().getTestOutputDirectory()); } else { throw new IllegalArgumentException("'" + COMPILE_SOURCE_SET_NAME + "' or '" + TEST_SOURCE_SET_NAME + "' expected"); } } @NotNull private static File getTargetDirectory(@NotNull MavenProject project) { return new File(project.getModel().getBuild().getDirectory()); } @NotNull ResolvedArtifacts resolveAnnotationProcessors(@Nullable List<DependencyCoordinate> aptDependencies) throws Exception { if (aptDependencies == null) { aptDependencies = Collections.emptyList(); } Set<Artifact> requiredArtifacts = new LinkedHashSet<Artifact>(); requiredArtifacts.add(getArtifact(artifactHandlerManager, KAPT_DEPENDENCY)); for (DependencyCoordinate dependency : aptDependencies) { requiredArtifacts.add(getArtifact(artifactHandlerManager, dependency)); } ArtifactResolutionRequest request = new ArtifactResolutionRequest() .setArtifact(requiredArtifacts.iterator().next()) .setResolveRoot(true) .setResolveTransitively(true) .setArtifactDependencies(requiredArtifacts) .setLocalRepository(session.getLocalRepository()) .setRemoteRepositories(project.getRemoteArtifactRepositories()); ArtifactResolutionResult resolutionResult = repositorySystem.resolve(request); resolutionErrorHandler.throwErrors(request, resolutionResult); if (resolutionResult == null || resolutionResult.getArtifacts() == null) { throw new IllegalStateException("Annotation processing artifacts were not resolved."); } List<String> apClasspath = new ArrayList<String>(resolutionResult.getArtifacts().size()); String kaptCompilerPluginArtifact = null; for (Artifact artifact : resolutionResult.getArtifacts()) { if (artifact == null) { continue; } if (artifact.getGroupId().equals(KAPT_DEPENDENCY.getGroupId()) && artifact.getArtifactId().equals(KAPT_DEPENDENCY.getArtifactId())) { kaptCompilerPluginArtifact = artifact.getFile().getAbsolutePath(); } else { apClasspath.add(artifact.getFile().getAbsolutePath()); } } if (kaptCompilerPluginArtifact == null) { throw new IllegalStateException("Kapt compiler plugin artifact was not resolved."); } return new ResolvedArtifacts(apClasspath, kaptCompilerPluginArtifact); } @NotNull private Artifact getArtifact( @NotNull ArtifactHandlerManager artifactHandlerManager, @NotNull DependencyCoordinate dependency) throws Exception { ArtifactHandler handler = artifactHandlerManager.getArtifactHandler(dependency.getType()); return new DefaultArtifact( dependency.getGroupId(), dependency.getArtifactId(), VersionRange.createFromVersionSpec(dependency.getVersion()), Artifact.SCOPE_RUNTIME, dependency.getType(), dependency.getClassifier(), handler, false); } @NotNull private static String getMavenPluginVersion() throws MojoExecutionException { ClassLoader classLoader = AnnotationProcessingManager.class.getClassLoader(); InputStream pomPropertiesIs = classLoader.getResourceAsStream( "META-INF/maven/org.jetbrains.kotlin/kotlin-maven-plugin/pom.properties"); if (pomPropertiesIs == null) { throw new MojoExecutionException("Can't resolve the version of kotlin-maven-plugin"); } Properties properties = new Properties(); try { properties.load(pomPropertiesIs); } catch (IOException e) { throw new MojoExecutionException("Error while reading kotlin-maven-plugin/pom.properties", e); } return properties.getProperty("version"); } class ResolvedArtifacts { @NotNull final List<String> annotationProcessingClasspath; @NotNull final String kaptCompilerPluginArtifact; ResolvedArtifacts(@NotNull List<String> annotationProcessingClasspath, @NotNull String kaptCompilerPluginArtifact) { this.annotationProcessingClasspath = annotationProcessingClasspath; this.kaptCompilerPluginArtifact = kaptCompilerPluginArtifact; } } }