From 86b9f5972bcb005305f8abb8fb1f3c0d89df2726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0irok=C3=BD?= Date: Mon, 13 Feb 2023 10:28:21 +0100 Subject: [PATCH] [MCOMPILER-395] Allow dependency exclusions for 'annotationProcessorPaths' (#173) Co-authored-by: Guillaume Nodet --- .../annotation-processor-dep/pom.xml | 34 +++++++ .../AnnotationProcessorDependency.java | 21 +++++ .../annotation-processor/pom.xml | 41 +++++++++ .../SimpleAnnotationProcessor.java | 85 +++++++++++++++++ .../annotation-user/pom.xml | 77 ++++++++++++++++ .../java/mcompiler395/SimpleAnnotation.java | 28 ++++++ .../main/java/mcompiler395/SimpleObject.java | 22 +++++ .../java/mcompiler395/SimpleTestObject.java | 22 +++++ .../invoker.properties | 18 ++++ .../pom.xml | 48 ++++++++++ .../plugin/compiler/AbstractCompilerMojo.java | 31 ++++++- .../plugin/compiler/DependencyCoordinate.java | 64 ++++--------- .../plugin/compiler/DependencyExclusion.java | 92 +++++++++++++++++++ 13 files changed, 537 insertions(+), 46 deletions(-) create mode 100644 src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor-dep/pom.xml create mode 100644 src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor-dep/src/main/java/mcompiler395/AnnotationProcessorDependency.java create mode 100644 src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor/pom.xml create mode 100644 src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor/src/main/java/mcompiler395/SimpleAnnotationProcessor.java create mode 100644 src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/pom.xml create mode 100644 src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/src/main/java/mcompiler395/SimpleAnnotation.java create mode 100644 src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/src/main/java/mcompiler395/SimpleObject.java create mode 100644 src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/src/test/java/mcompiler395/SimpleTestObject.java create mode 100644 src/it/MCOMPILER-395-processorpath-exclude-deps/invoker.properties create mode 100644 src/it/MCOMPILER-395-processorpath-exclude-deps/pom.xml create mode 100644 src/main/java/org/apache/maven/plugin/compiler/DependencyExclusion.java diff --git a/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor-dep/pom.xml b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor-dep/pom.xml new file mode 100644 index 00000000..0c6da32d --- /dev/null +++ b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor-dep/pom.xml @@ -0,0 +1,34 @@ + + + + + 4.0.0 + + + org.apache.maven.plugins.compiler.it + mcompiler395-test + 1.0.0-SNAPSHOT + + + mcompiler395-annotation-processor-dep + + diff --git a/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor-dep/src/main/java/mcompiler395/AnnotationProcessorDependency.java b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor-dep/src/main/java/mcompiler395/AnnotationProcessorDependency.java new file mode 100644 index 00000000..001845b8 --- /dev/null +++ b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor-dep/src/main/java/mcompiler395/AnnotationProcessorDependency.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package mcompiler395; + +public class AnnotationProcessorDependency {} diff --git a/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor/pom.xml b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor/pom.xml new file mode 100644 index 00000000..b180db0c --- /dev/null +++ b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor/pom.xml @@ -0,0 +1,41 @@ + + + + + 4.0.0 + + + org.apache.maven.plugins.compiler.it + mcompiler395-test + 1.0.0-SNAPSHOT + + + mcompiler395-annotation-processor + + + + org.apache.maven.plugins.compiler.it + mcompiler395-annotation-processor-dep + 1.0.0-SNAPSHOT + + + diff --git a/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor/src/main/java/mcompiler395/SimpleAnnotationProcessor.java b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor/src/main/java/mcompiler395/SimpleAnnotationProcessor.java new file mode 100644 index 00000000..9ee6229e --- /dev/null +++ b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-processor/src/main/java/mcompiler395/SimpleAnnotationProcessor.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package mcompiler395; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Filer; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.Name; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.Elements; +import javax.tools.FileObject; +import javax.tools.StandardLocation; + +import java.io.IOException; +import java.io.Writer; +import java.util.Set; + +@SupportedSourceVersion(SourceVersion.RELEASE_6) +@SupportedAnnotationTypes("mcompiler395.SimpleAnnotation") +public class SimpleAnnotationProcessor extends AbstractProcessor { + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (annotations.isEmpty()) { + return true; + } + + // assert that mcompiler395-annotation-processor-dep is NOT on the processorpath, since it is excluded + // in the plugin configuration + try { + getClass().getClassLoader().loadClass("mcompiler395.AnnotationProcessorDependency"); + throw new RuntimeException("Expected a ClassNotFoundException, because " + + "mcompiler395.AnnotationProcessorDependency is not supposed to be on the processorpath."); + } catch (ClassNotFoundException expected) { + // expected + } + + Filer filer = processingEnv.getFiler(); + + Elements elementUtils = processingEnv.getElementUtils(); + + Set elements = + roundEnv.getElementsAnnotatedWith(annotations.iterator().next()); + + for (Element element : elements) { + Name name = element.getSimpleName(); + + PackageElement packageElement = elementUtils.getPackageOf(element); + + try { + Name packageName = packageElement.getQualifiedName(); + FileObject resource = + filer.createResource(StandardLocation.SOURCE_OUTPUT, packageName, name + ".txt", element); + + Writer writer = resource.openWriter(); + writer.write(name.toString()); + writer.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return !elements.isEmpty(); + } +} diff --git a/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/pom.xml b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/pom.xml new file mode 100644 index 00000000..b5e40524 --- /dev/null +++ b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/pom.xml @@ -0,0 +1,77 @@ + + + + + 4.0.0 + + + org.apache.maven.plugins.compiler.it + mcompiler395-test + 1.0.0-SNAPSHOT + + + mcompiler395-annotation-user + + + + + maven-compiler-plugin + + + mcompiler395.SimpleAnnotationProcessor + + + + org.apache.maven.plugins.compiler.it + mcompiler395-annotation-processor + 1.0.0-SNAPSHOT + + + org.apache.maven.plugins.compiler.it + mcompiler395-annotation-processor-dep + + + + + + + + org.apache.maven.plugins.compiler.it + annotation-verify-plugin + 1.0.0-SNAPSHOT + + + verify-annotations + process-test-classes + + read-source + + + mcompiler395.SimpleObject + mcompiler395.SimpleTestObject + + + + + + + diff --git a/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/src/main/java/mcompiler395/SimpleAnnotation.java b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/src/main/java/mcompiler395/SimpleAnnotation.java new file mode 100644 index 00000000..361cfefa --- /dev/null +++ b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/src/main/java/mcompiler395/SimpleAnnotation.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package mcompiler395; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.SOURCE) +public @interface SimpleAnnotation {} diff --git a/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/src/main/java/mcompiler395/SimpleObject.java b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/src/main/java/mcompiler395/SimpleObject.java new file mode 100644 index 00000000..60cee1ef --- /dev/null +++ b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/src/main/java/mcompiler395/SimpleObject.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package mcompiler395; + +@SimpleAnnotation +public class SimpleObject {} diff --git a/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/src/test/java/mcompiler395/SimpleTestObject.java b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/src/test/java/mcompiler395/SimpleTestObject.java new file mode 100644 index 00000000..15d51903 --- /dev/null +++ b/src/it/MCOMPILER-395-processorpath-exclude-deps/annotation-user/src/test/java/mcompiler395/SimpleTestObject.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package mcompiler395; + +@SimpleAnnotation +public class SimpleTestObject {} diff --git a/src/it/MCOMPILER-395-processorpath-exclude-deps/invoker.properties b/src/it/MCOMPILER-395-processorpath-exclude-deps/invoker.properties new file mode 100644 index 00000000..a0a3964f --- /dev/null +++ b/src/it/MCOMPILER-395-processorpath-exclude-deps/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals=process-test-classes diff --git a/src/it/MCOMPILER-395-processorpath-exclude-deps/pom.xml b/src/it/MCOMPILER-395-processorpath-exclude-deps/pom.xml new file mode 100644 index 00000000..cf9dec7b --- /dev/null +++ b/src/it/MCOMPILER-395-processorpath-exclude-deps/pom.xml @@ -0,0 +1,48 @@ + + + + + 4.0.0 + + org.apache.maven.plugins.compiler.it + mcompiler395-test + 1.0.0-SNAPSHOT + pom + + + annotation-processor-dep + annotation-processor + annotation-user + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + @project.version@ + + + + + diff --git a/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java b/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java index b199e24d..aff9059b 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java +++ b/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java @@ -80,6 +80,7 @@ import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.collection.CollectRequest; import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.graph.Exclusion; import org.eclipse.aether.resolution.ArtifactResult; import org.eclipse.aether.resolution.DependencyRequest; import org.eclipse.aether.resolution.DependencyResult; @@ -299,7 +300,7 @@ public abstract class AbstractCompilerMojo extends AbstractMojo { *

*

* Each classpath element is specified using their Maven coordinates (groupId, artifactId, version, classifier, - * type). Transitive dependencies are added automatically. Example: + * type). Transitive dependencies are added automatically. Exclusions are supported as well. Example: *

* *
@@ -309,12 +310,21 @@ public abstract class AbstractCompilerMojo extends AbstractMojo {
      *       <groupId>org.sample</groupId>
      *       <artifactId>sample-annotation-processor</artifactId>
      *       <version>1.2.3</version>
+     *       <!-- Optionally exclude transitive dependencies -->
+     *       <exclusions>
+     *         <exclusion>
+     *           <groupId>org.sample</groupId>
+     *           <artifactId>sample-dependency</artifactId>
+     *         </exclusion>
+     *       </exclusions>
      *     </path>
      *     <!-- ... more ... -->
      *   </annotationProcessorPaths>
      * </configuration>
      * 
* + * Note: Exclusions are supported from version 3.11.0. + * * @since 3.5 */ @Parameter @@ -1604,11 +1614,28 @@ private List convertToDependencies(List annota annotationProcessorPath.getClassifier(), handler.getExtension(), annotationProcessorPath.getVersion()); - dependencies.add(new Dependency(artifact, JavaScopes.RUNTIME)); + Set exclusions = convertToAetherExclusions(annotationProcessorPath.getExclusions()); + dependencies.add(new Dependency(artifact, JavaScopes.RUNTIME, false, exclusions)); } return dependencies; } + private Set convertToAetherExclusions(Set exclusions) { + if (exclusions == null || exclusions.isEmpty()) { + return Collections.emptySet(); + } + Set aetherExclusions = new HashSet<>(); + for (DependencyExclusion exclusion : exclusions) { + Exclusion aetherExclusion = new Exclusion( + exclusion.getGroupId(), + exclusion.getArtifactId(), + exclusion.getClassifier(), + exclusion.getExtension()); + aetherExclusions.add(aetherExclusion); + } + return aetherExclusions; + } + private void writePlugin(MessageBuilder mb) { mb.a(" ").newline(); mb.a(" org.apache.maven.plugins").newline(); diff --git a/src/main/java/org/apache/maven/plugin/compiler/DependencyCoordinate.java b/src/main/java/org/apache/maven/plugin/compiler/DependencyCoordinate.java index 593ca5ef..19609499 100644 --- a/src/main/java/org/apache/maven/plugin/compiler/DependencyCoordinate.java +++ b/src/main/java/org/apache/maven/plugin/compiler/DependencyCoordinate.java @@ -18,6 +18,9 @@ */ package org.apache.maven.plugin.compiler; +import java.util.Objects; +import java.util.Set; + /** * Simple representation of Maven-coordinates of a dependency. * @@ -35,6 +38,8 @@ public class DependencyCoordinate { private String type = "jar"; + private Set exclusions; + public String getGroupId() { return groupId; } @@ -75,16 +80,17 @@ public void setType(String type) { this.type = type; } + public Set getExclusions() { + return exclusions; + } + + public void setExclusions(Set exclusions) { + this.exclusions = exclusions; + } + @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((artifactId == null) ? 0 : artifactId.hashCode()); - result = prime * result + ((classifier == null) ? 0 : classifier.hashCode()); - result = prime * result + ((groupId == null) ? 0 : groupId.hashCode()); - result = prime * result + ((type == null) ? 0 : type.hashCode()); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; + return Objects.hash(groupId, artifactId, version, classifier, type, exclusions); } @Override @@ -99,42 +105,12 @@ public boolean equals(Object obj) { return false; } DependencyCoordinate other = (DependencyCoordinate) obj; - if (artifactId == null) { - if (other.artifactId != null) { - return false; - } - } else if (!artifactId.equals(other.artifactId)) { - return false; - } - if (classifier == null) { - if (other.classifier != null) { - return false; - } - } else if (!classifier.equals(other.classifier)) { - return false; - } - if (groupId == null) { - if (other.groupId != null) { - return false; - } - } else if (!groupId.equals(other.groupId)) { - return false; - } - if (type == null) { - if (other.type != null) { - return false; - } - } else if (!type.equals(other.type)) { - return false; - } - if (version == null) { - if (other.version != null) { - return false; - } - } else if (!version.equals(other.version)) { - return false; - } - return true; + return Objects.equals(groupId, other.groupId) + && Objects.equals(artifactId, other.artifactId) + && Objects.equals(version, other.version) + && Objects.equals(classifier, other.classifier) + && Objects.equals(type, other.type) + && Objects.equals(exclusions, other.exclusions); } @Override diff --git a/src/main/java/org/apache/maven/plugin/compiler/DependencyExclusion.java b/src/main/java/org/apache/maven/plugin/compiler/DependencyExclusion.java new file mode 100644 index 00000000..352f0e89 --- /dev/null +++ b/src/main/java/org/apache/maven/plugin/compiler/DependencyExclusion.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.plugin.compiler; + +import java.util.Objects; + +/** + * Simple representation of a Maven dependency exclusion. + */ +public class DependencyExclusion { + private String groupId; + + private String artifactId; + + private String classifier; + + private String extension = "jar"; + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public String getClassifier() { + return classifier; + } + + public void setClassifier(String classifier) { + this.classifier = classifier; + } + + public String getExtension() { + return extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + DependencyExclusion other = (DependencyExclusion) obj; + return Objects.equals(groupId, other.groupId) + && Objects.equals(artifactId, other.artifactId) + && Objects.equals(classifier, other.classifier) + && Objects.equals(extension, other.extension); + } + + @Override + public int hashCode() { + return Objects.hash(groupId, artifactId, classifier, extension); + } + + @Override + public String toString() { + return groupId + ":" + artifactId + (classifier != null ? ":" + classifier : "") + + (extension != null ? "." + extension : ""); + } +}