diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/CompilationProvider.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/CompilationProvider.java index c25b0eb591028..9aced27b49bf6 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/CompilationProvider.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/CompilationProvider.java @@ -52,6 +52,7 @@ class Context { private final List compilerPluginOptions; private final boolean ignoreModuleInfo; private final File generatedSourcesDirectory; + private final Set annotationProcessorPaths; public Context( String name, @@ -68,6 +69,7 @@ public Context( List compilePluginArtifacts, List compilerPluginOptions, File generatedSourcesDirectory, + Set annotationProcessorPaths, String ignoreModuleInfo) { this.name = name; this.classpath = classpath; @@ -84,6 +86,7 @@ public Context( this.compilerPluginOptions = compilerPluginOptions; this.ignoreModuleInfo = Boolean.parseBoolean(ignoreModuleInfo); this.generatedSourcesDirectory = generatedSourcesDirectory; + this.annotationProcessorPaths = annotationProcessorPaths; } public String getName() { @@ -98,6 +101,10 @@ public Set getReloadableClasspath() { return reloadableClasspath; } + public Set getAnnotationProcessorPaths() { + return annotationProcessorPaths; + } + public File getProjectDirectory() { return projectDirectory; } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/DevModeContext.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/DevModeContext.java index a896364a07b85..4ffc65bc847c0 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/DevModeContext.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/DevModeContext.java @@ -64,6 +64,8 @@ public class DevModeContext implements Serializable { private String baseName; private final Set localArtifacts = new HashSet<>(); + private Set processorPaths; + public boolean isLocalProjectDiscovery() { return localProjectDiscovery; } @@ -239,6 +241,14 @@ public Set getLocalArtifacts() { return localArtifacts; } + public void setAnnotationProcessorPaths(Set processorPaths) { + this.processorPaths = processorPaths; + } + + public Set getAnnotationProcessorPaths() { + return processorPaths; + } + public static class ModuleInfo implements Serializable { private static final long serialVersionUID = -1376678003747618410L; diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/JavaCompilationProvider.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/JavaCompilationProvider.java index 32edf76f1f915..c828c485db482 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/JavaCompilationProvider.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/JavaCompilationProvider.java @@ -75,6 +75,7 @@ public void compile(Set filesToCompile, CompilationProvider.Context contex final QuarkusFileManager.Context sourcesContext = new QuarkusFileManager.Context( context.getClasspath(), context.getReloadableClasspath(), context.getOutputDirectory(), context.getGeneratedSourcesDirectory(), + context.getAnnotationProcessorPaths(), context.getSourceEncoding(), context.ignoreModuleInfo()); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusCompiler.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusCompiler.java index 8661de11c54d1..94b79a900e24e 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusCompiler.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusCompiler.java @@ -212,6 +212,7 @@ public void setupSourceCompilationContext(DevModeContext context, context.getCompilerPluginsOptions(), compilationUnit.getGeneratedSourcesPath() == null ? null : new File(compilationUnit.getGeneratedSourcesPath()), + context.getAnnotationProcessorPaths(), context.getBuildSystemProperties().getOrDefault("quarkus.live-reload.ignore-module-info", "true"))); } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusDevModeLauncher.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusDevModeLauncher.java index 674320c9bd34b..ff5a562a71fa7 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusDevModeLauncher.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusDevModeLauncher.java @@ -181,6 +181,12 @@ public B compilerPluginOptions(List options) { return (B) this; } + @SuppressWarnings("unchecked") + public B annotationProcessorPaths(Set processorPaths) { + QuarkusDevModeLauncher.this.processorPaths = processorPaths; + return (B) this; + } + @SuppressWarnings("unchecked") public B releaseJavaVersion(String releaseJavaVersion) { QuarkusDevModeLauncher.this.releaseJavaVersion = releaseJavaVersion; @@ -317,6 +323,7 @@ public R build() throws Exception { private ModuleInfo main; private List dependencies = new ArrayList<>(0); private LinkedHashMap classpath = new LinkedHashMap<>(); + private Set processorPaths; protected QuarkusDevModeLauncher() { } @@ -442,6 +449,9 @@ protected void prepare() throws Exception { if (compilerPluginOptions != null) { devModeContext.setCompilerPluginsOptions(compilerPluginOptions); } + if (processorPaths != null) { + devModeContext.setAnnotationProcessorPaths(processorPaths); + } devModeContext.setReleaseJavaVersion(releaseJavaVersion); devModeContext.setSourceJavaVersion(sourceJavaVersion); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/QuarkusFileManager.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/QuarkusFileManager.java index fdef0129a1bf0..75b624df295a0 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/QuarkusFileManager.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/QuarkusFileManager.java @@ -21,6 +21,9 @@ protected QuarkusFileManager(StandardJavaFileManager fileManager, Context contex if (context.getGeneratedSourcesDirectory() != null) { this.fileManager.setLocation(StandardLocation.SOURCE_OUTPUT, List.of(context.getGeneratedSourcesDirectory())); } + if (context.getAnnotationProcessorPaths() != null) { + this.fileManager.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, context.getAnnotationProcessorPaths()); + } } catch (IOException e) { throw new RuntimeException("Cannot initialize file manager", e); } @@ -35,6 +38,9 @@ public void reset(Context context) { if (context.getGeneratedSourcesDirectory() != null) { this.fileManager.setLocation(StandardLocation.SOURCE_OUTPUT, List.of(context.getGeneratedSourcesDirectory())); } + if (context.getAnnotationProcessorPaths() != null) { + this.fileManager.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, context.getAnnotationProcessorPaths()); + } } catch (IOException e) { throw new RuntimeException("Cannot reset file manager", e); } @@ -52,15 +58,22 @@ public static class Context { private final Charset sourceEncoding; private final boolean ignoreModuleInfo; private final File generatedSourcesDirectory; + private final Set annotationProcessorPaths; public Context(Set classPath, Set reloadableClassPath, - File outputDirectory, File generatedSourcesDirectory, Charset sourceEncoding, boolean ignoreModuleInfo) { + File outputDirectory, File generatedSourcesDirectory, Set annotationProcessorPaths, + Charset sourceEncoding, boolean ignoreModuleInfo) { this.classPath = classPath; this.reloadableClassPath = reloadableClassPath; this.outputDirectory = outputDirectory; this.sourceEncoding = sourceEncoding; this.ignoreModuleInfo = ignoreModuleInfo; this.generatedSourcesDirectory = generatedSourcesDirectory; + this.annotationProcessorPaths = annotationProcessorPaths; + } + + public Set getAnnotationProcessorPaths() { + return annotationProcessorPaths; } public Set getClassPath() { diff --git a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java index 6994d3395daa5..0c74b488219fe 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; @@ -43,6 +44,8 @@ import org.aesh.terminal.utils.ANSI; import org.apache.commons.lang3.StringUtils; import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.BuildBase; import org.apache.maven.model.Dependency; @@ -267,6 +270,9 @@ public class DevMojo extends AbstractMojo { @Component private MavenVersionEnforcer mavenVersionEnforcer; + @Component + private ArtifactHandlerManager artifactHandlerManager; + @Component private RepositorySystem repoSystem; @@ -708,6 +714,73 @@ private void executeGoal(PluginExec pluginExec, String goal, Map pluginManager)); } + private Set readAnnotationProcessorPaths(Xpp3Dom pluginConfig) throws MojoExecutionException { + Xpp3Dom annotationProcessorPaths = pluginConfig.getChild("annotationProcessorPaths"); + if (annotationProcessorPaths == null) { + return Collections.emptySet(); + } + Xpp3Dom[] paths = annotationProcessorPaths.getChildren("path"); + Set elements = new LinkedHashSet<>(); + try { + List dependencies = convertToDependencies(paths); + CollectRequest collectRequest = new CollectRequest(dependencies, Collections.emptyList(), + project.getRemoteProjectRepositories()); + DependencyRequest dependencyRequest = new DependencyRequest(); + dependencyRequest.setCollectRequest(collectRequest); + DependencyResult dependencyResult = repoSystem.resolveDependencies(session.getRepositorySession(), + dependencyRequest); + + for (ArtifactResult resolved : dependencyResult.getArtifactResults()) { + elements.add(resolved.getArtifact().getFile()); + } + return elements; + } catch (Exception e) { + throw new MojoExecutionException( + "Resolution of annotationProcessorPath dependencies failed: " + e.getLocalizedMessage(), e); + } + } + + private List convertToDependencies(Xpp3Dom[] paths) { + List dependencies = new ArrayList<>(); + for (Xpp3Dom path : paths) { + String type = getValue(path, "type", "jar"); + ArtifactHandler handler = artifactHandlerManager.getArtifactHandler(type); + org.eclipse.aether.artifact.Artifact artifact = new DefaultArtifact( + getValue(path, "groupId", null), + getValue(path, "artifactId", null), + getValue(path, "classifier", null), + handler.getExtension(), + getValue(path, "version", null)); + Set exclusions = convertToAetherExclusions(path.getChild("exclusions")); + dependencies.add(new org.eclipse.aether.graph.Dependency(artifact, JavaScopes.RUNTIME, false, exclusions)); + } + return dependencies; + } + + private String getValue(Xpp3Dom path, String element, String defaultValue) { + Xpp3Dom child = path.getChild(element); + if (child == null) { + return defaultValue; + } + return child.getValue(); + } + + private Set convertToAetherExclusions(Xpp3Dom exclusions) { + if (exclusions == null) { + return Collections.emptySet(); + } + Set aetherExclusions = new HashSet<>(); + for (Xpp3Dom exclusion : exclusions.getChildren("exclusion")) { + Exclusion aetherExclusion = new Exclusion( + getValue(exclusion, "groupId", null), + getValue(exclusion, "artifactId", null), + getValue(exclusion, "classifier", null), + getValue(exclusion, "extension", "jar")); + aetherExclusions.add(aetherExclusion); + } + return aetherExclusions; + } + private boolean isGoalConfigured(Plugin plugin, String goal) { if (plugin == null) { return false; @@ -1156,6 +1229,7 @@ private QuarkusDevModeLauncher newLauncher(Boolean debugPortOk, String bootstrap } setKotlinSpecificFlags(builder); + setAnnotationProcessorFlags(builder); // path to the serialized application model final Path appModelLocation = resolveSerializedModelLocation(); @@ -1406,6 +1480,36 @@ private void setKotlinSpecificFlags(MavenDevModeLauncher.Builder builder) { builder.compilerPluginOptions(options); } + private void setAnnotationProcessorFlags(MavenDevModeLauncher.Builder builder) { + Plugin compilerMavenPlugin = null; + for (Plugin plugin : project.getBuildPlugins()) { + if (plugin.getArtifactId().equals("maven-compiler-plugin") + && plugin.getGroupId().equals("org.apache.maven.plugins")) { + compilerMavenPlugin = plugin; + break; + } + } + + if (compilerMavenPlugin == null) { + return; + } + + getLog().debug("Maven compiler plugin found, looking for annotation processors"); + + List options = new ArrayList<>(); + Xpp3Dom compilerPluginConfiguration = (Xpp3Dom) compilerMavenPlugin.getConfiguration(); + try { + Set processorPaths = this.readAnnotationProcessorPaths(compilerPluginConfiguration); + getLog().debug("Found processor paths: " + processorPaths); + if (!processorPaths.isEmpty()) { + builder.annotationProcessorPaths(processorPaths); + } + } catch (MojoExecutionException e) { + throw new RuntimeException(e); + } + builder.compilerPluginOptions(options); + } + protected void modifyDevModeContext(MavenDevModeLauncher.Builder builder) { }