Kotlin / binary-compatibility-validator

Public API management tool
Apache License 2.0
758 stars 55 forks source link

Support Multi-Release JAR Files #235

Open Sineaggi opened 1 month ago

Sineaggi commented 1 month ago

Multi-release jars https://openjdk.org/jeps/238 should have identical api surfaces, but if we create an mr-jar with a new or altered function signature, that's not currently checked by the plugin.

fzhinkin commented 1 month ago

Jar tool validates that public API does not diverge between classes for different release versions and prints something like:

entry: META-INF/versions/11/Specific.class, contains a new public class not found in base entries
entry: META-INF/versions/11/Common.class, contains a class with different api from earlier version
invalid multi-release jar file mrj.jar deleted

@Sineaggi, do you experience issues with invalid multi-release jars? Could you please elaborate on how these jars were created (manually, via gradle/maven/etc. plugin)?

Sineaggi commented 1 month ago

I'm using gradle to create the jars.

tasks.jar {
    from(compileJava9Java.flatMap { it.destinationDirectory }) {
        into("META-INF/versions/9")
    }
    manifest {
        attributes["Multi-Release"] = "true"
    }
}

I'm guessing Gradle doesn't use the jar tool to create jars.

Sineaggi commented 1 month ago

Ok so building a new tar task that calls the java jar tool does properly warn.

entry: META-INF/versions/9/org/example/Common.class, contains a class with different api from earlier version
invalid multi-release jar file /Users/cwalker/git/mrjar-compat-check/build/libs/mrjar-compat-check-jar.jar deleted

FAILURE: Build failed with an exception.

Maybe this is better suited to an issue upstream in Gradle.

Sineaggi commented 1 month ago

Was able to get a gradle task to validate the jar produced by gradle

tasks.register<JavaExec>("validateJar") {
    mainModule = "jdk.jartool/sun.tools.jar.Main"
    val jarFile = tasks.jar.flatMap { it.archiveFile }
    inputs.file(jarFile)
    argumentProviders.plusAssign(CommandLineArgumentProvider {
        listOf(
            "--validate",
            "--file",
            jarFile.get().toString(),
        )
    })
}
JakeWharton commented 1 month ago

The jar tool is relatively slow and doesn't have enough options which is why almost every build tool uses something else. A jar is, after all, just a zip. They optimize for performance.

Sineaggi commented 1 month ago

I wonder if the speed of jar --validate as a check task would work be a blocker for general builds?

JakeWharton commented 1 month ago

Don't think so. Checks are (generally) slow!

Sineaggi commented 1 month ago

I can't find any documentation, but it looks like jar --validate was added in java 17. EDIT: here JDK-8266835

Sineaggi commented 3 weeks ago

It's probably fine to close this issue.