Skip to content

Commit 8e4f5b9

Browse files
authored
Add better enforceBytecodeVersion rule based on mojohaus (#968)
This rule is very often used, and does not require anything special like a new dependency, hence it should be among "built in" rules. OTOH, it had issues, for example **on larger multi projects did recheck same (potentially huge) JAR over and over again**. It is now altered to not repeat same check (same options against same file) within same session. This rule is 100% drop-in replacement for old uses (ITs are unmodified and just copied over), moreover rule itself has set priority that if enforce plugin gets updated with this rule, but the extra rules are present, the new rule will kick in. All the options are same as before, and rule name is unchanged as well. Where it differs, and is reflected in ITs: is not so chatty as mojohaus one, it does not tell "skipped due scope", "skipped due regexp" etc, hence ITs are modified to simply assert for presence or absence of "[DEBUG] Analyzing GAV..." string instead.
1 parent fd4b148 commit 8e4f5b9

54 files changed

Lines changed: 2816 additions & 16 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

enforcer-rules/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,11 @@
154154
<artifactId>assertj-core</artifactId>
155155
<scope>test</scope>
156156
</dependency>
157+
<dependency>
158+
<groupId>org.slf4j</groupId>
159+
<artifactId>slf4j-simple</artifactId>
160+
<scope>test</scope>
161+
</dependency>
157162
</dependencies>
158163

159164
<build>

enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/EnforceBytecodeVersion.java

Lines changed: 548 additions & 0 deletions
Large diffs are not rendered by default.

enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/ResolverUtil.java

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import org.eclipse.aether.graph.Dependency;
4141
import org.eclipse.aether.graph.DependencyNode;
4242
import org.eclipse.aether.graph.DependencyVisitor;
43+
import org.eclipse.aether.resolution.DependencyRequest;
44+
import org.eclipse.aether.resolution.DependencyResolutionException;
4345
import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
4446
import org.eclipse.aether.util.graph.transformer.ConflictResolver;
4547
import org.eclipse.aether.util.graph.visitor.TreeDependencyVisitor;
@@ -82,7 +84,7 @@ class ResolverUtil {
8284
* @throws EnforcerRuleException thrown if the lookup fails
8385
*/
8486
DependencyNode resolveTransitiveDependenciesVerbose(List<String> excludedScopes) throws EnforcerRuleException {
85-
return resolveTransitiveDependencies(true, true, excludedScopes);
87+
return resolveTransitiveDependencies(true, false, true, excludedScopes);
8688
}
8789

8890
/**
@@ -93,7 +95,7 @@ DependencyNode resolveTransitiveDependenciesVerbose(List<String> excludedScopes)
9395
* @throws EnforcerRuleException thrown if the lookup fails
9496
*/
9597
DependencyNode resolveTransitiveDependencies() throws EnforcerRuleException {
96-
return resolveTransitiveDependencies(false, true, Arrays.asList(SCOPE_TEST, SCOPE_PROVIDED));
98+
return resolveTransitiveDependencies(false, false, true, Arrays.asList(SCOPE_TEST, SCOPE_PROVIDED));
9799
}
98100

99101
/**
@@ -107,11 +109,17 @@ DependencyNode resolveTransitiveDependencies() throws EnforcerRuleException {
107109
*/
108110
DependencyNode resolveTransitiveDependencies(boolean excludeOptional, List<String> excludedScopes)
109111
throws EnforcerRuleException {
110-
return resolveTransitiveDependencies(false, excludeOptional, excludedScopes);
112+
return resolveTransitiveDependencies(false, false, excludeOptional, excludedScopes);
111113
}
112114

113115
DependencyNode resolveTransitiveDependencies(boolean verbose, boolean excludeOptional, List<String> excludedScopes)
114116
throws EnforcerRuleException {
117+
return resolveTransitiveDependencies(verbose, false, excludeOptional, excludedScopes);
118+
}
119+
120+
DependencyNode resolveTransitiveDependencies(
121+
boolean verbose, boolean resolve, boolean excludeOptional, List<String> excludedScopes)
122+
throws EnforcerRuleException {
115123

116124
try {
117125
RepositorySystemSession repositorySystemSession = session.getRepositorySession();
@@ -145,11 +153,19 @@ DependencyNode resolveTransitiveDependencies(boolean verbose, boolean excludeOpt
145153
new CollectRequest(dependencies, managedDependencies, project.getRemoteProjectRepositories());
146154
collectRequest.setRootArtifact(RepositoryUtils.toArtifact(project.getArtifact()));
147155

148-
return repositorySystem
149-
.collectDependencies(repositorySystemSession, collectRequest)
150-
.getRoot();
151-
152-
} catch (DependencyCollectionException e) {
156+
if (resolve) {
157+
DependencyRequest dependencyRequest = new DependencyRequest();
158+
dependencyRequest.setCollectRequest(collectRequest);
159+
160+
return repositorySystem
161+
.resolveDependencies(repositorySystemSession, dependencyRequest)
162+
.getRoot();
163+
} else {
164+
return repositorySystem
165+
.collectDependencies(repositorySystemSession, collectRequest)
166+
.getRoot();
167+
}
168+
} catch (DependencyCollectionException | DependencyResolutionException e) {
153169
throw new EnforcerRuleException("Could not build dependency tree " + e.getLocalizedMessage(), e);
154170
}
155171
}

enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/utils/ArtifactUtils.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.maven.artifact.Artifact;
3030
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
3131
import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
32+
import org.eclipse.aether.graph.Dependency;
3233
import org.eclipse.aether.graph.DependencyNode;
3334

3435
import static java.util.Optional.ofNullable;
@@ -40,21 +41,26 @@
4041
*/
4142
public final class ArtifactUtils {
4243

44+
public static Artifact toArtifact(DependencyNode node) {
45+
if (node.getDependency() == null) {
46+
return RepositoryUtils.toArtifact(node.getArtifact());
47+
}
48+
return toArtifact(node.getDependency());
49+
}
50+
4351
/**
44-
* Converts {@link DependencyNode} to {@link Artifact}; in comparison
52+
* Converts {@link Dependency} to {@link Artifact}; in comparison
4553
* to {@link RepositoryUtils#toArtifact(org.eclipse.aether.artifact.Artifact)}, this method
4654
* assigns {@link Artifact#getScope()} and {@link Artifact#isOptional()} based on
4755
* the dependency information from the node.
4856
*
49-
* @param node {@link DependencyNode} to convert to {@link Artifact}
57+
* @param dependency {@link Dependency} to convert to {@link Artifact}
5058
* @return target artifact
5159
*/
52-
public static Artifact toArtifact(DependencyNode node) {
53-
Artifact artifact = RepositoryUtils.toArtifact(node.getArtifact());
54-
ofNullable(node.getDependency()).ifPresent(dependency -> {
55-
ofNullable(dependency.getScope()).ifPresent(artifact::setScope);
56-
artifact.setOptional(dependency.isOptional());
57-
});
60+
public static Artifact toArtifact(Dependency dependency) {
61+
Artifact artifact = RepositoryUtils.toArtifact(dependency.getArtifact());
62+
ofNullable(dependency.getScope()).ifPresent(artifact::setScope);
63+
artifact.setOptional(dependency.isOptional());
5864
return artifact;
5965
}
6066

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
~~ Licensed to the Apache Software Foundation (ASF) under one
2+
~~ or more contributor license agreements. See the NOTICE file
3+
~~ distributed with this work for additional information
4+
~~ regarding copyright ownership. The ASF licenses this file
5+
~~ to you under the Apache License, Version 2.0 (the
6+
~~ "License"); you may not use this file except in compliance
7+
~~ with the License. You may obtain a copy of the License at
8+
~~
9+
~~ http://www.apache.org/licenses/LICENSE-2.0
10+
~~
11+
~~ Unless required by applicable law or agreed to in writing,
12+
~~ software distributed under the License is distributed on an
13+
~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
~~ KIND, either express or implied. See the License for the
15+
~~ specific language governing permissions and limitations
16+
~~ under the License.
17+
18+
------
19+
Enforce Bytecode Version
20+
------
21+
Baptiste Mathus
22+
------
23+
Mars 2013
24+
------
25+
26+
Enforce Bytecode Version
27+
28+
This rule checks the dependencies transitively and fails if any class of any dependency is having its bytecode version higher than the one specified.
29+
30+
The following parameters are supported by this rule:
31+
32+
* <<maxJdkVersion>> - the maximum target jdk version (e.g. 8, 11, 17, 21...)
33+
34+
* <<maxJavaMajorVersionNumber>> - an integer indicating the maximum bytecode major version number (cannot be specified if maxJdkVersion is present)
35+
36+
* <<maxJavaMinorVersionNumber>> - an integer indicating the maximum bytecode minor version number (cannot be specified if maxJdkVersion is present)
37+
38+
* <<includes>>, <<excludes>> - optional lists of artifact patterns to include or exclude ([groupId]:[artifactId]:[type]:[version] with wildcards and optional segments)
39+
40+
* <<ignoreClasses>> - a list of classes to ignore bytecode version problems. Wildcards can be specified using the * character.
41+
42+
* <<scopes>> - a list of scopes (e.g. test, provided) to include when scanning artifacts
43+
44+
* <<ignoredScopes>> - a list of scopes (e.g. test, provided) to ignore when scanning artifacts
45+
46+
* <<ignoreOptionals>> - a boolean, if <<<true>>> all dependencies which have <<<<optional>true</optional>>>> are ignored.
47+
48+
* <<searchTransitive>> - a boolean, specify if transitive dependencies should be searched (default) or only look at direct dependencies.
49+
50+
* <<strict>> - a boolean, if <<<true>>> process module-info and Multi-Release JAR classes
51+
52+
* <<processOncePerSession>> - a boolean (by default <<true>>), optimize and process same JAR only once per session
53+
54+
[]
55+
56+
Note
57+
Sample Plugin Configuration:
58+
59+
+---+
60+
<project>
61+
[...]
62+
<build>
63+
<plugins>
64+
<plugin>
65+
<groupId>org.apache.maven.plugins</groupId>
66+
<artifactId>maven-enforcer-plugin</artifactId>
67+
<version>${project.version}</version>
68+
<executions>
69+
<execution>
70+
<id>enforce-bytecode-version</id>
71+
<goals>
72+
<goal>enforce</goal>
73+
</goals>
74+
<configuration>
75+
<rules>
76+
<enforceBytecodeVersion>
77+
<maxJdkVersion>8</maxJdkVersion>
78+
<excludes>
79+
<exclude>org.jline:jline</exclude>
80+
</excludes>
81+
</enforceBytecodeVersion>
82+
</rules>
83+
<fail>true</fail>
84+
</configuration>
85+
</execution>
86+
</executions>
87+
</plugin>
88+
</plugins>
89+
</build>
90+
[...]
91+
</project>
92+
+---+
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.enforcer.rules.dependency;
20+
21+
import java.util.stream.Stream;
22+
23+
import org.junit.jupiter.params.ParameterizedTest;
24+
import org.junit.jupiter.params.provider.Arguments;
25+
import org.junit.jupiter.params.provider.MethodSource;
26+
27+
import static org.junit.jupiter.api.Assertions.assertEquals;
28+
import static org.junit.jupiter.params.provider.Arguments.arguments;
29+
30+
class EnforceBytecodeVersionTest {
31+
32+
static Stream<Arguments> renderVersion() {
33+
return Stream.of(
34+
arguments("44.0", 44, 0),
35+
arguments("JDK 1.5", 49, 0),
36+
arguments("JDK 1.7", 51, 0),
37+
arguments("51.3", 51, 3),
38+
arguments("JDK 8", 52, 0),
39+
arguments("JDK 11", 55, 0),
40+
arguments("JDK 12", 56, 0),
41+
arguments("JDK 21", 65, 0),
42+
arguments("JDK 26", 70, 0),
43+
arguments("JDK 57", 101, 0));
44+
}
45+
46+
@ParameterizedTest
47+
@MethodSource
48+
void renderVersion(String expected, int major, int minor) {
49+
assertEquals(expected, EnforceBytecodeVersion.renderVersion(major, minor));
50+
}
51+
52+
public static Stream<Arguments> decodeMajorVersion() {
53+
return Stream.of(
54+
arguments("1.1", 45),
55+
arguments("1.2", 46),
56+
arguments("1.3", 47),
57+
arguments("1.4", 48),
58+
arguments("1.5", 49),
59+
arguments("1.6", 50),
60+
arguments("1.7", 51),
61+
arguments("1.8", 52),
62+
arguments("8", 52),
63+
arguments("1.9", 53),
64+
arguments("9", 53),
65+
arguments("11", 55),
66+
arguments("12", 56),
67+
arguments("21", 65),
68+
arguments("26", 70),
69+
arguments("57", 101));
70+
}
71+
72+
@ParameterizedTest
73+
@MethodSource
74+
void decodeMajorVersion(String version, int expectedMajor) {
75+
assertEquals(expectedMajor, EnforceBytecodeVersion.decodeMajorVersion(version));
76+
}
77+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one
4+
or more contributor license agreements. See the NOTICE file
5+
distributed with this work for additional information
6+
regarding copyright ownership. The ASF licenses this file
7+
to you under the Apache License, Version 2.0 (the
8+
"License"); you may not use this file except in compliance
9+
with the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing,
14+
software distributed under the License is distributed on an
15+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
KIND, either express or implied. See the License for the
17+
specific language governing permissions and limitations
18+
under the License.
19+
-->
20+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
21+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
22+
<modelVersion>4.0.0</modelVersion>
23+
<groupId>test</groupId>
24+
<artifactId>enforce-bytecode-version-direct-deps</artifactId>
25+
<version>1.0-SNAPSHOT</version>
26+
<description>Check only direct dependencies</description>
27+
28+
<build>
29+
<plugins>
30+
<plugin>
31+
<artifactId>maven-enforcer-plugin</artifactId>
32+
<version>@project.version@</version>
33+
<configuration>
34+
<rules>
35+
<enforceBytecodeVersion>
36+
<maxJdkVersion>1.5</maxJdkVersion>
37+
<searchTransitive>false</searchTransitive>
38+
</enforceBytecodeVersion>
39+
</rules>
40+
</configuration>
41+
<executions>
42+
<execution>
43+
<id>test</id>
44+
<goals>
45+
<goal>enforce</goal>
46+
</goals>
47+
<phase>validate</phase>
48+
</execution>
49+
</executions>
50+
</plugin>
51+
</plugins>
52+
</build>
53+
54+
<dependencies>
55+
<dependency>
56+
<groupId>junit</groupId>
57+
<artifactId>junit</artifactId>
58+
<version>4.13.2</version>
59+
<scope>test</scope>
60+
</dependency>
61+
<dependency>
62+
<groupId>org.slf4j</groupId>
63+
<artifactId>slf4j-simple</artifactId>
64+
<version>1.7.2</version>
65+
</dependency>
66+
</dependencies>
67+
</project>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
File file = new File(basedir, "build.log")
21+
assert file.exists()
22+
23+
String text = file.getText("utf-8")
24+
25+
// only direct dependency
26+
assert text.contains('[DEBUG] Analyzing artifact junit:junit:jar')
27+
assert text.contains('[DEBUG] Analyzing artifact org.slf4j:slf4j-simple:jar')
28+
29+
// no transitive dependencies
30+
assert !text.contains('[DEBUG] Analyzing artifact org.hamcrest:hamcrest-core:jar')
31+
assert !text.contains('[DEBUG] Analyzing artifact org.slf4j:slf4j-api:jar')

0 commit comments

Comments
 (0)