Skip to content

Linkage Checker Enforcer Rule

Tomo Suzuki edited this page Jun 4, 2021 · 35 revisions

The Linkage Checker Enforcer Rule verifies that a project's runtime classpath does not have any linkage errors. The rule assumes that the classpath is constructed from a project's pom.xml according to the usual Maven algorithm.

Usage

Add the following plugin configuration to your pom.xml:

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-enforcer-plugin</artifactId>
        <version>3.0.0-M3</version>
        <dependencies>
          <dependency>
            <groupId>com.google.cloud.tools</groupId>
            <artifactId>linkage-checker-enforcer-rules</artifactId>
            <version>1.5.7</version>
          </dependency>
        </dependencies>
        <executions>
          <execution>
            <id>enforce-linkage-checker</id>
            <!-- Important! Should run after compile -->
            <phase>verify</phase>
            <goals>
              <goal>enforce</goal>
            </goals>
            <configuration>
              <rules>
                <LinkageCheckerRule
                    implementation="com.google.cloud.tools.dependencies.enforcer.LinkageCheckerRule"/>
              </rules>
            </configuration>
          </execution>
        </executions>
      </plugin>
   ...

Configuration

For a BOM project, set dependencySection element to DEPENDENCY_MANAGEMENT.

  <LinkageCheckerRule
      implementation="com.google.cloud.tools.dependencies.enforcer.LinkageCheckerRule">
      <dependencySection>DEPENDENCY_MANAGEMENT</dependencySection>
  </LinkageCheckerRule>

To suppress linkage errors that are not reachable in the class reference graph from the classes in the direct dependencies of the project, set reportOnlyReachable element to true. (default: false).

  <LinkageCheckerRule
      implementation="com.google.cloud.tools.dependencies.enforcer.LinkageCheckerRule">
      <reportOnlyReachable>true</reportOnlyReachable>
  </LinkageCheckerRule>

To suppress linkage errors using Linkage Checker Exclusion File, set exclusionFile element to the exclusion file.

  <LinkageCheckerRule
      implementation="com.google.cloud.tools.dependencies.enforcer.LinkageCheckerRule">
      <exclusionFile>linkage-checker-exclusion-rules.xml</exclusionFile>
  </LinkageCheckerRule>

If you want violations not to fail the build, set the level element to WARN:

  <LinkageCheckerRule
      implementation="com.google.cloud.tools.dependencies.enforcer.LinkageCheckerRule">
      <level>WARN</level>
  </LinkageCheckerRule>

Run

The Linkage Checker Enforcer Rule is bound to the verify lifecycle:

$ mvn verify

Successful Result

Successful checks output no error.

[INFO] --- maven-enforcer-plugin:3.0.0-M3:enforce (enforce-linkage-checker) @ protobuf-java-util ---
[INFO] No error found

Failed Result

Failed checks output the missing classes, fields, or methods and the referencing classes.

[INFO] --- maven-enforcer-plugin:3.0.0-M3:enforce (enforce-linkage-checker) @ google-cloud-core-grpc ---
[ERROR] Linkage Checker rule found 21 reachable errors. Linkage error report:
Class org.eclipse.jetty.npn.NextProtoNego is not found;
  referenced by 1 class file
    io.grpc.netty.shaded.io.netty.handler.ssl.JettyNpnSslEngine (grpc-netty-shaded-1.23.0.jar)
...

Class path and dependencySection element

The dependencySection element determines whether the rule checks the dependencies in the dependencies section or the dependencyManagement section. The following values are accepted:

  • DEPENDENCIES (default value): the rule checks the class path calculated from the project's dependencies section.
  • DEPENDENCY_MANAGEMENT: the rule checks the class path calculated from the project's dependencyManagement section.

In both cases, the rule builds dependency graph that Maven would create. For example, in the dependency graph, the optional dependencies of transitive dependencies are not included in the graph.

Requirement

Maven and Java version

This enforcer rule requires Maven 3.6.0 or later and Java 8.

No concurrent execution with install task

This enforcer rule relies on the immutability of JAR files in the class path. Running this enforcer rule concurrently (the -T option in mvn) with install task causes PluginExecutionException with the following error:

Caused by: org.apache.maven.plugin.PluginExecutionException: Execution enforce-linkage-checker of goal
  org.apache.maven.plugins:maven-enforcer-plugin:3.0.0-M3:enforce failed: The source class in the reference
  is no longer available in the class path
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:148)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:208)

If you want concurrent execution of Maven, split the install lifecycle from verify:

$ mvn -T 1.5C verify
$ mvn -T 1.5C install -Denforcer.skip=true

No aether-util for Linkage Checker

If you use the Linkage Checker Enforcer Rule with other enforcer rule (such as extra-enforcer-rules), it sometimes causes the following "An API incompatibility" error: Execution enforce-linkage-checker of goal org.apache.maven.plugins:maven-enforcer-plugin:3.0.0-M3:enforce failed: An API incompatibility was encountered while executing org.apache.maven.plugins:maven-enforcer-plugin:3.0.0-M3:enforce: java.lang.IllegalAccessError: tried to access method org.eclipse.aether.util.ChecksumUtils.toHexString([B)Ljava/lang/String; from class org.eclipse.aether.connector.basic.ChecksumCalculator$Checksum. This error means the class in the old aether-util is not compatible with the one transitively used by Linkage Checker. To resolve this dependency conflict, add the following dependency to extra-enforcer-rules that explicitly excludes old aether-util artifact:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-enforcer-plugin</artifactId>
        <version>3.0.0-M3</version>
        <dependencies>
          <dependency>
            <groupId>com.google.cloud.tools</groupId>
            <artifactId>linkage-checker-enforcer-rules</artifactId>
            <version>1.5.7</version>
          </dependency>
          <!-- Add this maven-resolver-util dependency to avoid dependency conflicts
               with extra-enforcer-rules -->
          <dependency>
            <groupId>org.apache.maven.resolver</groupId>
            <artifactId>maven-resolver-util</artifactId>
            <version>1.6.1</version>
            <scope>compile</scope>
          </dependency>
        </dependencies>