Filter test_only artifacts out of artifacts merged into root repos and print a warning when a root artifact version is overridden by a non_root bazel_dep (#1511)
diff --git a/examples/bzlmod/maven_install.json b/examples/bzlmod/maven_install.json index 23fc9e0..1357913 100644 --- a/examples/bzlmod/maven_install.json +++ b/examples/bzlmod/maven_install.json
@@ -1,10 +1,7 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": -200671778, - "__RESOLVED_ARTIFACTS_HASH": -1275115130, - "conflict_resolution": { - "com.google.errorprone:error_prone_annotations:2.23.0": "com.google.errorprone:error_prone_annotations:2.24.1" - }, + "__INPUT_ARTIFACTS_HASH": 1563368834, + "__RESOLVED_ARTIFACTS_HASH": -144767737, "artifacts": { "com.beust:jcommander": { "shasums": { @@ -27,13 +24,6 @@ }, "version": "1.0.1" }, - "com.google.auto.value:auto-value-annotations": { - "shasums": { - "jar": "e1c45e6beadaef9797cb0d9afd5a45621ad061cd8632012f85582853a3887825", - "sources": "61a433f015b12a6cf4ecff227c7748486ff8f294ffe9d39827b382ade0514d0a" - }, - "version": "1.10.4" - }, "com.google.auto:auto-common": { "shasums": { "jar": "326d674b411ea67505273f9ade5311c15bca50644b5211a6c309c9aee590a20a", @@ -57,10 +47,10 @@ }, "com.google.errorprone:error_prone_annotations": { "shasums": { - "jar": "19fe2f7155d20ea093168527999da98108103ee546d1e8b726bc4b27c31a3c30", - "sources": "d0d63b507b92b36d38d938e96daf260baa55e739fe9e0d642519b8fac823d288" + "jar": "ec6f39f068b6ff9ac323c68e28b9299f8c0a80ca512dccb1d4a70f40ac3ec054", + "sources": "5b4504609bb93d3c24b87cd839cf0bb7d878135d0a917a05081d0dc9b2a9973f" }, - "version": "2.24.1" + "version": "2.23.0" }, "com.google.guava:failureaccess": { "shasums": { @@ -103,13 +93,6 @@ }, "version": "4.27.2" }, - "com.google.truth:truth": { - "shasums": { - "jar": "235c28e96ee6701ab01cc852fb294cb0f34756f636a8154b9aef08fb1215bbc4", - "sources": "f2834e7abdd08f5ab31defe409f601e0863b31f3f47105ab3c5ae8df4a687318" - }, - "version": "1.4.0" - }, "com.sun.activation:jakarta.activation": { "shasums": { "jar": "02156773e4ae9d048d14a56ad35d644bee9f1052a791d072df3ded3c656e6e1a", @@ -313,26 +296,12 @@ }, "version": "2.0.0" }, - "junit:junit": { - "shasums": { - "jar": "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", - "sources": "34181df6482d40ea4c046b063cb53c7ffae94bdf1b1d62695bdf3adf9dea7e3a" - }, - "version": "4.13.2" - }, "net.bytebuddy:byte-buddy": { "shasums": { - "jar": "e99761a526df0fefbbd3fe14436b0f953000cdfa5151dc63c0b18d37d9c46f1c", - "sources": "143dd9fe73f0566cc703934b7fd15abbb97bfab045064c2f176067e70456a136" + "jar": "1a1ac9ce65eddcea54ead958387bb0b3863d02a2ffe856ab6a57ac79737c19cf", + "sources": "6d2fc2af6c71077dc9fb097c8eb06023545a04b6839bcea0d8d388b1c5e65b61" }, - "version": "1.14.5" - }, - "net.bytebuddy:byte-buddy-agent": { - "shasums": { - "jar": "55f19862b870f5d85890ba5386b1b45e9bbc88d5fe1f819abe0c788b4929fa6b", - "sources": "8844d4b130218aa87e0f6b99f9a9b40971b45a95f44a0dfd409038c46631cb5b" - }, - "version": "1.14.5" + "version": "1.12.10" }, "org.apache.commons:commons-exec": { "shasums": { @@ -357,38 +326,10 @@ }, "org.checkerframework:checker-qual": { "shasums": { - "jar": "ccaedd33af0b7894d9f2f3b644f4d19e43928e32902e61ac4d10777830f5aac7", - "sources": "efb65eb479f61f53c6dcafbd42ed59dad09b0a0d5a7f44b7bc68df25c2dcf8fd" + "jar": "2f9f245bf68e4259d610894f2406dc1f6363dc639302bd566e8272e4f4541172", + "sources": "8308220bbdd4e12b49fa06a91de685faf9cc1a376464478c80845be3e87b7d4f" }, - "version": "3.42.0" - }, - "org.hamcrest:hamcrest-core": { - "shasums": { - "jar": "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9", - "sources": "e223d2d8fbafd66057a8848cc94222d63c3cedd652cc48eddc0ab5c39c0f84df" - }, - "version": "1.3" - }, - "org.mockito:mockito-core": { - "shasums": { - "jar": "b1689b06617ea01fd777bfaedbdde512faf083d639a049f79b388d5a4e96d2e5", - "sources": "f21eb1cbb701477ba37c430b5a97ba78e3b3cf79a5737443e06b3f65bd7c9364" - }, - "version": "5.4.0" - }, - "org.objenesis:objenesis": { - "shasums": { - "jar": "02dfd0b0439a5591e35b708ed2f5474eb0948f53abf74637e959b8e4ef69bfeb", - "sources": "d06164f8ca002c8ef193cef2d682822014dd330505616af93a3fb64226fc131d" - }, - "version": "3.3" - }, - "org.ow2.asm:asm": { - "shasums": { - "jar": "3c6fac2424db3d4a853b669f4e3d1d9c3c552235e19a319673f887083c2303a1", - "sources": "2b6e12f0da3d065ba628a024a8851ab0d5b5d3501dacfcc18769243250f4f77e" - }, - "version": "9.6" + "version": "3.41.0" }, "org.reactivestreams:reactive-streams": { "shasums": { @@ -548,14 +489,6 @@ "com.google.j2objc:j2objc-annotations", "com.google.protobuf:protobuf-java" ], - "com.google.truth:truth": [ - "com.google.auto.value:auto-value-annotations", - "com.google.errorprone:error_prone_annotations", - "com.google.guava:guava", - "junit:junit", - "org.checkerframework:checker-qual", - "org.ow2.asm:asm" - ], "com.typesafe.netty:netty-reactive-streams": [ "io.netty:netty-handler", "org.reactivestreams:reactive-streams" @@ -682,9 +615,6 @@ "io.opentelemetry:opentelemetry-semconv": [ "io.opentelemetry:opentelemetry-api" ], - "junit:junit": [ - "org.hamcrest:hamcrest-core" - ], "org.asynchttpclient:async-http-client": [ "com.sun.activation:jakarta.activation", "com.typesafe.netty:netty-reactive-streams", @@ -701,11 +631,6 @@ "io.netty:netty-buffer", "org.slf4j:slf4j-api" ], - "org.mockito:mockito-core": [ - "net.bytebuddy:byte-buddy", - "net.bytebuddy:byte-buddy-agent", - "org.objenesis:objenesis" - ], "org.seleniumhq.selenium:selenium-chrome-driver": [ "com.google.auto.service:auto-service", "com.google.auto.service:auto-service-annotations", @@ -871,12 +796,6 @@ "com.google.auto.service:auto-service-annotations": [ "com.google.auto.service" ], - "com.google.auto.value:auto-value-annotations": [ - "com.google.auto.value", - "com.google.auto.value.extension.memoized", - "com.google.auto.value.extension.serializable", - "com.google.auto.value.extension.toprettystring" - ], "com.google.auto:auto-common": [ "com.google.auto.common" ], @@ -933,9 +852,6 @@ "com.google.protobuf:protobuf-java-util": [ "com.google.protobuf.util" ], - "com.google.truth:truth": [ - "com.google.common.truth" - ], "com.sun.activation:jakarta.activation": [ "com.sun.activation.registries", "com.sun.activation.viewers", @@ -1118,40 +1034,6 @@ "io.ous.jtoml", "io.ous.jtoml.impl" ], - "junit:junit": [ - "junit.extensions", - "junit.framework", - "junit.runner", - "junit.textui", - "org.junit", - "org.junit.experimental", - "org.junit.experimental.categories", - "org.junit.experimental.max", - "org.junit.experimental.results", - "org.junit.experimental.runners", - "org.junit.experimental.theories", - "org.junit.experimental.theories.internal", - "org.junit.experimental.theories.suppliers", - "org.junit.function", - "org.junit.internal", - "org.junit.internal.builders", - "org.junit.internal.management", - "org.junit.internal.matchers", - "org.junit.internal.requests", - "org.junit.internal.runners", - "org.junit.internal.runners.model", - "org.junit.internal.runners.rules", - "org.junit.internal.runners.statements", - "org.junit.matchers", - "org.junit.rules", - "org.junit.runner", - "org.junit.runner.manipulation", - "org.junit.runner.notification", - "org.junit.runners", - "org.junit.runners.model", - "org.junit.runners.parameterized", - "org.junit.validator" - ], "net.bytebuddy:byte-buddy": [ "net.bytebuddy", "net.bytebuddy.agent.builder", @@ -1192,10 +1074,6 @@ "net.bytebuddy.utility.privilege", "net.bytebuddy.utility.visitor" ], - "net.bytebuddy:byte-buddy-agent": [ - "net.bytebuddy.agent", - "net.bytebuddy.agent.utility.nullability" - ], "net.bytebuddy:byte-buddy:jar:sources": [ "net.bytebuddy.build" ], @@ -1268,98 +1146,11 @@ "org.checkerframework.common.reflection.qual", "org.checkerframework.common.returnsreceiver.qual", "org.checkerframework.common.subtyping.qual", - "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.util.report.qual", "org.checkerframework.common.value.qual", "org.checkerframework.dataflow.qual", "org.checkerframework.framework.qual" ], - "org.hamcrest:hamcrest-core": [ - "org.hamcrest", - "org.hamcrest.core", - "org.hamcrest.internal" - ], - "org.mockito:mockito-core": [ - "org.mockito", - "org.mockito.codegen", - "org.mockito.configuration", - "org.mockito.creation.instance", - "org.mockito.exceptions.base", - "org.mockito.exceptions.misusing", - "org.mockito.exceptions.stacktrace", - "org.mockito.exceptions.verification", - "org.mockito.exceptions.verification.junit", - "org.mockito.exceptions.verification.opentest4j", - "org.mockito.hamcrest", - "org.mockito.internal", - "org.mockito.internal.configuration", - "org.mockito.internal.configuration.injection", - "org.mockito.internal.configuration.injection.filter", - "org.mockito.internal.configuration.injection.scanner", - "org.mockito.internal.configuration.plugins", - "org.mockito.internal.creation", - "org.mockito.internal.creation.bytebuddy", - "org.mockito.internal.creation.instance", - "org.mockito.internal.creation.proxy", - "org.mockito.internal.creation.settings", - "org.mockito.internal.creation.util", - "org.mockito.internal.debugging", - "org.mockito.internal.exceptions", - "org.mockito.internal.exceptions.stacktrace", - "org.mockito.internal.exceptions.util", - "org.mockito.internal.framework", - "org.mockito.internal.hamcrest", - "org.mockito.internal.handler", - "org.mockito.internal.invocation", - "org.mockito.internal.invocation.finder", - "org.mockito.internal.invocation.mockref", - "org.mockito.internal.junit", - "org.mockito.internal.listeners", - "org.mockito.internal.matchers", - "org.mockito.internal.matchers.apachecommons", - "org.mockito.internal.matchers.text", - "org.mockito.internal.progress", - "org.mockito.internal.reporting", - "org.mockito.internal.runners", - "org.mockito.internal.runners.util", - "org.mockito.internal.session", - "org.mockito.internal.stubbing", - "org.mockito.internal.stubbing.answers", - "org.mockito.internal.stubbing.defaultanswers", - "org.mockito.internal.util", - "org.mockito.internal.util.collections", - "org.mockito.internal.util.concurrent", - "org.mockito.internal.util.io", - "org.mockito.internal.util.reflection", - "org.mockito.internal.verification", - "org.mockito.internal.verification.api", - "org.mockito.internal.verification.argumentmatching", - "org.mockito.internal.verification.checkers", - "org.mockito.invocation", - "org.mockito.junit", - "org.mockito.listeners", - "org.mockito.mock", - "org.mockito.plugins", - "org.mockito.quality", - "org.mockito.session", - "org.mockito.stubbing", - "org.mockito.verification" - ], - "org.objenesis:objenesis": [ - "org.objenesis", - "org.objenesis.instantiator", - "org.objenesis.instantiator.android", - "org.objenesis.instantiator.annotations", - "org.objenesis.instantiator.basic", - "org.objenesis.instantiator.gcj", - "org.objenesis.instantiator.perc", - "org.objenesis.instantiator.sun", - "org.objenesis.instantiator.util", - "org.objenesis.strategy" - ], - "org.ow2.asm:asm": [ - "org.objectweb.asm", - "org.objectweb.asm.signature" - ], "org.reactivestreams:reactive-streams": [ "org.reactivestreams" ], @@ -1825,8 +1616,6 @@ "com.google.auto.service:auto-service-annotations", "com.google.auto.service:auto-service-annotations:jar:sources", "com.google.auto.service:auto-service:jar:sources", - "com.google.auto.value:auto-value-annotations", - "com.google.auto.value:auto-value-annotations:jar:sources", "com.google.auto:auto-common", "com.google.auto:auto-common:jar:sources", "com.google.code.findbugs:jsr305", @@ -1846,8 +1635,6 @@ "com.google.protobuf:protobuf-java-util", "com.google.protobuf:protobuf-java-util:jar:sources", "com.google.protobuf:protobuf-java:jar:sources", - "com.google.truth:truth", - "com.google.truth:truth:jar:sources", "com.sun.activation:jakarta.activation", "com.sun.activation:jakarta.activation:jar:sources", "com.typesafe.netty:netty-reactive-streams", @@ -1906,11 +1693,7 @@ "io.opentelemetry:opentelemetry-semconv:jar:sources", "io.ous:jtoml", "io.ous:jtoml:jar:sources", - "junit:junit", - "junit:junit:jar:sources", "net.bytebuddy:byte-buddy", - "net.bytebuddy:byte-buddy-agent", - "net.bytebuddy:byte-buddy-agent:jar:sources", "net.bytebuddy:byte-buddy:jar:sources", "org.apache.commons:commons-exec", "org.apache.commons:commons-exec:jar:sources", @@ -1920,14 +1703,6 @@ "org.asynchttpclient:async-http-client:jar:sources", "org.checkerframework:checker-qual", "org.checkerframework:checker-qual:jar:sources", - "org.hamcrest:hamcrest-core", - "org.hamcrest:hamcrest-core:jar:sources", - "org.mockito:mockito-core", - "org.mockito:mockito-core:jar:sources", - "org.objenesis:objenesis", - "org.objenesis:objenesis:jar:sources", - "org.ow2.asm:asm", - "org.ow2.asm:asm:jar:sources", "org.reactivestreams:reactive-streams", "org.reactivestreams:reactive-streams:jar:sources", "org.seleniumhq.selenium:selenium-api", @@ -2120,8 +1895,5 @@ ] } }, - "skipped": [ - "com.google.guava:listenablefuture:jar:sources" - ], "version": "2" }
diff --git a/examples/java-export/maven_install.json b/examples/java-export/maven_install.json index 2baabca..548bb99 100644 --- a/examples/java-export/maven_install.json +++ b/examples/java-export/maven_install.json
@@ -1,20 +1,11 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": -1781511218, - "__RESOLVED_ARTIFACTS_HASH": 1370831359, + "__INPUT_ARTIFACTS_HASH": 908270220, + "__RESOLVED_ARTIFACTS_HASH": -8035072, "conflict_resolution": { - "com.google.errorprone:error_prone_annotations:2.23.0": "com.google.errorprone:error_prone_annotations:2.24.1", - "com.google.guava:guava:29.0-jre": "com.google.guava:guava:33.0.0-jre", - "com.google.protobuf:protobuf-java:4.27.2": "com.google.protobuf:protobuf-java:4.31.1" + "com.google.guava:guava:29.0-jre": "com.google.guava:guava:33.0.0-jre" }, "artifacts": { - "com.google.auto.value:auto-value-annotations": { - "shasums": { - "jar": "e1c45e6beadaef9797cb0d9afd5a45621ad061cd8632012f85582853a3887825", - "sources": "61a433f015b12a6cf4ecff227c7748486ff8f294ffe9d39827b382ade0514d0a" - }, - "version": "1.10.4" - }, "com.google.code.findbugs:jsr305": { "shasums": { "jar": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7", @@ -31,10 +22,10 @@ }, "com.google.errorprone:error_prone_annotations": { "shasums": { - "jar": "19fe2f7155d20ea093168527999da98108103ee546d1e8b726bc4b27c31a3c30", - "sources": "d0d63b507b92b36d38d938e96daf260baa55e739fe9e0d642519b8fac823d288" + "jar": "ec6f39f068b6ff9ac323c68e28b9299f8c0a80ca512dccb1d4a70f40ac3ec054", + "sources": "5b4504609bb93d3c24b87cd839cf0bb7d878135d0a917a05081d0dc9b2a9973f" }, - "version": "2.24.1" + "version": "2.23.0" }, "com.google.guava:failureaccess": { "shasums": { @@ -77,68 +68,12 @@ }, "version": "4.27.2" }, - "com.google.truth:truth": { - "shasums": { - "jar": "235c28e96ee6701ab01cc852fb294cb0f34756f636a8154b9aef08fb1215bbc4", - "sources": "f2834e7abdd08f5ab31defe409f601e0863b31f3f47105ab3c5ae8df4a687318" - }, - "version": "1.4.0" - }, - "junit:junit": { - "shasums": { - "jar": "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", - "sources": "34181df6482d40ea4c046b063cb53c7ffae94bdf1b1d62695bdf3adf9dea7e3a" - }, - "version": "4.13.2" - }, - "net.bytebuddy:byte-buddy": { - "shasums": { - "jar": "e99761a526df0fefbbd3fe14436b0f953000cdfa5151dc63c0b18d37d9c46f1c", - "sources": "143dd9fe73f0566cc703934b7fd15abbb97bfab045064c2f176067e70456a136" - }, - "version": "1.14.5" - }, - "net.bytebuddy:byte-buddy-agent": { - "shasums": { - "jar": "55f19862b870f5d85890ba5386b1b45e9bbc88d5fe1f819abe0c788b4929fa6b", - "sources": "8844d4b130218aa87e0f6b99f9a9b40971b45a95f44a0dfd409038c46631cb5b" - }, - "version": "1.14.5" - }, "org.checkerframework:checker-qual": { "shasums": { - "jar": "ccaedd33af0b7894d9f2f3b644f4d19e43928e32902e61ac4d10777830f5aac7", - "sources": "efb65eb479f61f53c6dcafbd42ed59dad09b0a0d5a7f44b7bc68df25c2dcf8fd" + "jar": "2f9f245bf68e4259d610894f2406dc1f6363dc639302bd566e8272e4f4541172", + "sources": "8308220bbdd4e12b49fa06a91de685faf9cc1a376464478c80845be3e87b7d4f" }, - "version": "3.42.0" - }, - "org.hamcrest:hamcrest-core": { - "shasums": { - "jar": "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9", - "sources": "e223d2d8fbafd66057a8848cc94222d63c3cedd652cc48eddc0ab5c39c0f84df" - }, - "version": "1.3" - }, - "org.mockito:mockito-core": { - "shasums": { - "jar": "b1689b06617ea01fd777bfaedbdde512faf083d639a049f79b388d5a4e96d2e5", - "sources": "f21eb1cbb701477ba37c430b5a97ba78e3b3cf79a5737443e06b3f65bd7c9364" - }, - "version": "5.4.0" - }, - "org.objenesis:objenesis": { - "shasums": { - "jar": "02dfd0b0439a5591e35b708ed2f5474eb0948f53abf74637e959b8e4ef69bfeb", - "sources": "d06164f8ca002c8ef193cef2d682822014dd330505616af93a3fb64226fc131d" - }, - "version": "3.3" - }, - "org.ow2.asm:asm": { - "shasums": { - "jar": "3c6fac2424db3d4a853b669f4e3d1d9c3c552235e19a319673f887083c2303a1", - "sources": "2b6e12f0da3d065ba628a024a8851ab0d5b5d3501dacfcc18769243250f4f77e" - }, - "version": "9.6" + "version": "3.41.0" } }, "dependencies": { @@ -157,31 +92,9 @@ "com.google.guava:guava", "com.google.j2objc:j2objc-annotations", "com.google.protobuf:protobuf-java" - ], - "com.google.truth:truth": [ - "com.google.auto.value:auto-value-annotations", - "com.google.errorprone:error_prone_annotations", - "com.google.guava:guava", - "junit:junit", - "org.checkerframework:checker-qual", - "org.ow2.asm:asm" - ], - "junit:junit": [ - "org.hamcrest:hamcrest-core" - ], - "org.mockito:mockito-core": [ - "net.bytebuddy:byte-buddy", - "net.bytebuddy:byte-buddy-agent", - "org.objenesis:objenesis" ] }, "packages": { - "com.google.auto.value:auto-value-annotations": [ - "com.google.auto.value", - "com.google.auto.value.extension.memoized", - "com.google.auto.value.extension.serializable", - "com.google.auto.value.extension.toprettystring" - ], "com.google.code.findbugs:jsr305": [ "javax.annotation", "javax.annotation.concurrent", @@ -235,90 +148,6 @@ "com.google.protobuf:protobuf-java-util": [ "com.google.protobuf.util" ], - "com.google.truth:truth": [ - "com.google.common.truth" - ], - "junit:junit": [ - "junit.extensions", - "junit.framework", - "junit.runner", - "junit.textui", - "org.junit", - "org.junit.experimental", - "org.junit.experimental.categories", - "org.junit.experimental.max", - "org.junit.experimental.results", - "org.junit.experimental.runners", - "org.junit.experimental.theories", - "org.junit.experimental.theories.internal", - "org.junit.experimental.theories.suppliers", - "org.junit.function", - "org.junit.internal", - "org.junit.internal.builders", - "org.junit.internal.management", - "org.junit.internal.matchers", - "org.junit.internal.requests", - "org.junit.internal.runners", - "org.junit.internal.runners.model", - "org.junit.internal.runners.rules", - "org.junit.internal.runners.statements", - "org.junit.matchers", - "org.junit.rules", - "org.junit.runner", - "org.junit.runner.manipulation", - "org.junit.runner.notification", - "org.junit.runners", - "org.junit.runners.model", - "org.junit.runners.parameterized", - "org.junit.validator" - ], - "net.bytebuddy:byte-buddy": [ - "net.bytebuddy", - "net.bytebuddy.agent.builder", - "net.bytebuddy.asm", - "net.bytebuddy.build", - "net.bytebuddy.description", - "net.bytebuddy.description.annotation", - "net.bytebuddy.description.enumeration", - "net.bytebuddy.description.field", - "net.bytebuddy.description.method", - "net.bytebuddy.description.modifier", - "net.bytebuddy.description.type", - "net.bytebuddy.dynamic", - "net.bytebuddy.dynamic.loading", - "net.bytebuddy.dynamic.scaffold", - "net.bytebuddy.dynamic.scaffold.inline", - "net.bytebuddy.dynamic.scaffold.subclass", - "net.bytebuddy.implementation", - "net.bytebuddy.implementation.attribute", - "net.bytebuddy.implementation.auxiliary", - "net.bytebuddy.implementation.bind", - "net.bytebuddy.implementation.bind.annotation", - "net.bytebuddy.implementation.bytecode", - "net.bytebuddy.implementation.bytecode.assign", - "net.bytebuddy.implementation.bytecode.assign.primitive", - "net.bytebuddy.implementation.bytecode.assign.reference", - "net.bytebuddy.implementation.bytecode.collection", - "net.bytebuddy.implementation.bytecode.constant", - "net.bytebuddy.implementation.bytecode.member", - "net.bytebuddy.jar.asm", - "net.bytebuddy.jar.asm.commons", - "net.bytebuddy.jar.asm.signature", - "net.bytebuddy.matcher", - "net.bytebuddy.pool", - "net.bytebuddy.utility", - "net.bytebuddy.utility.dispatcher", - "net.bytebuddy.utility.nullability", - "net.bytebuddy.utility.privilege", - "net.bytebuddy.utility.visitor" - ], - "net.bytebuddy:byte-buddy-agent": [ - "net.bytebuddy.agent", - "net.bytebuddy.agent.utility.nullability" - ], - "net.bytebuddy:byte-buddy:jar:sources": [ - "net.bytebuddy.build" - ], "org.checkerframework:checker-qual": [ "org.checkerframework.checker.builder.qual", "org.checkerframework.checker.calledmethods.qual", @@ -346,103 +175,14 @@ "org.checkerframework.common.reflection.qual", "org.checkerframework.common.returnsreceiver.qual", "org.checkerframework.common.subtyping.qual", - "org.checkerframework.common.util.count.report.qual", + "org.checkerframework.common.util.report.qual", "org.checkerframework.common.value.qual", "org.checkerframework.dataflow.qual", "org.checkerframework.framework.qual" - ], - "org.hamcrest:hamcrest-core": [ - "org.hamcrest", - "org.hamcrest.core", - "org.hamcrest.internal" - ], - "org.mockito:mockito-core": [ - "org.mockito", - "org.mockito.codegen", - "org.mockito.configuration", - "org.mockito.creation.instance", - "org.mockito.exceptions.base", - "org.mockito.exceptions.misusing", - "org.mockito.exceptions.stacktrace", - "org.mockito.exceptions.verification", - "org.mockito.exceptions.verification.junit", - "org.mockito.exceptions.verification.opentest4j", - "org.mockito.hamcrest", - "org.mockito.internal", - "org.mockito.internal.configuration", - "org.mockito.internal.configuration.injection", - "org.mockito.internal.configuration.injection.filter", - "org.mockito.internal.configuration.injection.scanner", - "org.mockito.internal.configuration.plugins", - "org.mockito.internal.creation", - "org.mockito.internal.creation.bytebuddy", - "org.mockito.internal.creation.instance", - "org.mockito.internal.creation.proxy", - "org.mockito.internal.creation.settings", - "org.mockito.internal.creation.util", - "org.mockito.internal.debugging", - "org.mockito.internal.exceptions", - "org.mockito.internal.exceptions.stacktrace", - "org.mockito.internal.exceptions.util", - "org.mockito.internal.framework", - "org.mockito.internal.hamcrest", - "org.mockito.internal.handler", - "org.mockito.internal.invocation", - "org.mockito.internal.invocation.finder", - "org.mockito.internal.invocation.mockref", - "org.mockito.internal.junit", - "org.mockito.internal.listeners", - "org.mockito.internal.matchers", - "org.mockito.internal.matchers.apachecommons", - "org.mockito.internal.matchers.text", - "org.mockito.internal.progress", - "org.mockito.internal.reporting", - "org.mockito.internal.runners", - "org.mockito.internal.runners.util", - "org.mockito.internal.session", - "org.mockito.internal.stubbing", - "org.mockito.internal.stubbing.answers", - "org.mockito.internal.stubbing.defaultanswers", - "org.mockito.internal.util", - "org.mockito.internal.util.collections", - "org.mockito.internal.util.concurrent", - "org.mockito.internal.util.io", - "org.mockito.internal.util.reflection", - "org.mockito.internal.verification", - "org.mockito.internal.verification.api", - "org.mockito.internal.verification.argumentmatching", - "org.mockito.internal.verification.checkers", - "org.mockito.invocation", - "org.mockito.junit", - "org.mockito.listeners", - "org.mockito.mock", - "org.mockito.plugins", - "org.mockito.quality", - "org.mockito.session", - "org.mockito.stubbing", - "org.mockito.verification" - ], - "org.objenesis:objenesis": [ - "org.objenesis", - "org.objenesis.instantiator", - "org.objenesis.instantiator.android", - "org.objenesis.instantiator.annotations", - "org.objenesis.instantiator.basic", - "org.objenesis.instantiator.gcj", - "org.objenesis.instantiator.perc", - "org.objenesis.instantiator.sun", - "org.objenesis.instantiator.util", - "org.objenesis.strategy" - ], - "org.ow2.asm:asm": [ - "org.objectweb.asm", - "org.objectweb.asm.signature" ] }, "repositories": { "https://repo1.maven.org/maven2/": [ - "com.google.auto.value:auto-value-annotations", - "com.google.auto.value:auto-value-annotations:jar:sources", "com.google.code.findbugs:jsr305", "com.google.code.findbugs:jsr305:jar:sources", "com.google.code.gson:gson", @@ -460,29 +200,10 @@ "com.google.protobuf:protobuf-java-util", "com.google.protobuf:protobuf-java-util:jar:sources", "com.google.protobuf:protobuf-java:jar:sources", - "com.google.truth:truth", - "com.google.truth:truth:jar:sources", - "junit:junit", - "junit:junit:jar:sources", - "net.bytebuddy:byte-buddy", - "net.bytebuddy:byte-buddy-agent", - "net.bytebuddy:byte-buddy-agent:jar:sources", - "net.bytebuddy:byte-buddy:jar:sources", "org.checkerframework:checker-qual", - "org.checkerframework:checker-qual:jar:sources", - "org.hamcrest:hamcrest-core", - "org.hamcrest:hamcrest-core:jar:sources", - "org.mockito:mockito-core", - "org.mockito:mockito-core:jar:sources", - "org.objenesis:objenesis", - "org.objenesis:objenesis:jar:sources", - "org.ow2.asm:asm", - "org.ow2.asm:asm:jar:sources" + "org.checkerframework:checker-qual:jar:sources" ] }, "services": {}, - "skipped": [ - "com.google.guava:listenablefuture:jar:sources" - ], "version": "2" }
diff --git a/maven_install.json b/maven_install.json index 07c3b81..8042b3c 100644 --- a/maven_install.json +++ b/maven_install.json
@@ -1,19 +1,8 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": 1994476565, - "__RESOLVED_ARTIFACTS_HASH": -274973469, - "conflict_resolution": { - "com.google.errorprone:error_prone_annotations:2.23.0": "com.google.errorprone:error_prone_annotations:2.41.0", - "com.google.guava:guava:33.0.0-jre": "com.google.guava:guava:33.5.0-jre", - "com.google.protobuf:protobuf-java:4.27.2": "com.google.protobuf:protobuf-java:4.31.0" - }, + "__INPUT_ARTIFACTS_HASH": -647997795, + "__RESOLVED_ARTIFACTS_HASH": 1828317327, "artifacts": { - "com.google.auto.value:auto-value-annotations": { - "shasums": { - "jar": "e1c45e6beadaef9797cb0d9afd5a45621ad061cd8632012f85582853a3887825" - }, - "version": "1.10.4" - }, "com.google.code.findbugs:jsr305": { "shasums": { "jar": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" @@ -28,9 +17,9 @@ }, "com.google.errorprone:error_prone_annotations": { "shasums": { - "jar": "a56e782b5b50811ac204073a355a21d915a2107fce13ec711331ad036f660fcc" + "jar": "ec6f39f068b6ff9ac323c68e28b9299f8c0a80ca512dccb1d4a70f40ac3ec054" }, - "version": "2.41.0" + "version": "2.23.0" }, "com.google.guava:failureaccess": { "shasums": { @@ -52,9 +41,9 @@ }, "com.google.j2objc:j2objc-annotations": { "shasums": { - "jar": "84d3a150518485f8140ea99b8a985656749629f6433c92b80c75b36aba3b099b" + "jar": "f02a95fa1a5e95edb3ed859fd0fb7df709d121a35290eff8b74dce2ab7f4d6ed" }, - "version": "3.1" + "version": "2.8" }, "com.google.protobuf:protobuf-java": { "shasums": { @@ -68,12 +57,6 @@ }, "version": "4.27.2" }, - "com.google.truth:truth": { - "shasums": { - "jar": "235c28e96ee6701ab01cc852fb294cb0f34756f636a8154b9aef08fb1215bbc4" - }, - "version": "1.4.0" - }, "io.netty:netty-tcnative-boringssl-static": { "shasums": { "jar": "b6f974972c44cd6f9cecabc255290286faac40b6393c66c3c3c0db7f421cc28e", @@ -91,30 +74,6 @@ }, "version": "2.0.61.Final" }, - "junit:junit": { - "shasums": { - "jar": "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3" - }, - "version": "4.13.2" - }, - "net.bytebuddy:byte-buddy": { - "shasums": { - "jar": "e99761a526df0fefbbd3fe14436b0f953000cdfa5151dc63c0b18d37d9c46f1c" - }, - "version": "1.14.5" - }, - "net.bytebuddy:byte-buddy-agent": { - "shasums": { - "jar": "55f19862b870f5d85890ba5386b1b45e9bbc88d5fe1f819abe0c788b4929fa6b" - }, - "version": "1.14.5" - }, - "org.checkerframework:checker-qual": { - "shasums": { - "jar": "ccaedd33af0b7894d9f2f3b644f4d19e43928e32902e61ac4d10777830f5aac7" - }, - "version": "3.42.0" - }, "org.hamcrest:hamcrest": { "shasums": { "jar": "ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050" @@ -132,24 +91,6 @@ "jar": "1fad6e6be7557781e4d33729d49ae1cdc8fdda6fe477bb0cc68ce351eafdfbab" }, "version": "1.0.0" - }, - "org.mockito:mockito-core": { - "shasums": { - "jar": "b1689b06617ea01fd777bfaedbdde512faf083d639a049f79b388d5a4e96d2e5" - }, - "version": "5.4.0" - }, - "org.objenesis:objenesis": { - "shasums": { - "jar": "02dfd0b0439a5591e35b708ed2f5474eb0948f53abf74637e959b8e4ef69bfeb" - }, - "version": "3.3" - }, - "org.ow2.asm:asm": { - "shasums": { - "jar": "3c6fac2424db3d4a853b669f4e3d1d9c3c552235e19a319673f887083c2303a1" - }, - "version": "9.6" } }, "dependencies": { @@ -168,14 +109,6 @@ "com.google.j2objc:j2objc-annotations", "com.google.protobuf:protobuf-java" ], - "com.google.truth:truth": [ - "com.google.auto.value:auto-value-annotations", - "com.google.errorprone:error_prone_annotations", - "com.google.guava:guava", - "junit:junit", - "org.checkerframework:checker-qual", - "org.ow2.asm:asm" - ], "io.netty:netty-tcnative-boringssl-static": [ "io.netty:netty-tcnative-boringssl-static:jar:linux-aarch_64", "io.netty:netty-tcnative-boringssl-static:jar:linux-x86_64", @@ -219,25 +152,11 @@ "io.netty:netty-tcnative-boringssl-static:jar:osx-x86_64", "io.netty:netty-tcnative-classes" ], - "junit:junit": [ - "org.hamcrest:hamcrest-core" - ], "org.hamcrest:hamcrest-core": [ "org.hamcrest:hamcrest" - ], - "org.mockito:mockito-core": [ - "net.bytebuddy:byte-buddy", - "net.bytebuddy:byte-buddy-agent", - "org.objenesis:objenesis" ] }, "packages": { - "com.google.auto.value:auto-value-annotations": [ - "com.google.auto.value", - "com.google.auto.value.extension.memoized", - "com.google.auto.value.extension.serializable", - "com.google.auto.value.extension.toprettystring" - ], "com.google.code.findbugs:jsr305": [ "javax.annotation", "javax.annotation.concurrent", @@ -291,122 +210,9 @@ "com.google.protobuf:protobuf-java-util": [ "com.google.protobuf.util" ], - "com.google.truth:truth": [ - "com.google.common.truth" - ], "io.netty:netty-tcnative-classes": [ "io.netty.internal.tcnative" ], - "junit:junit": [ - "junit.extensions", - "junit.framework", - "junit.runner", - "junit.textui", - "org.junit", - "org.junit.experimental", - "org.junit.experimental.categories", - "org.junit.experimental.max", - "org.junit.experimental.results", - "org.junit.experimental.runners", - "org.junit.experimental.theories", - "org.junit.experimental.theories.internal", - "org.junit.experimental.theories.suppliers", - "org.junit.function", - "org.junit.internal", - "org.junit.internal.builders", - "org.junit.internal.management", - "org.junit.internal.matchers", - "org.junit.internal.requests", - "org.junit.internal.runners", - "org.junit.internal.runners.model", - "org.junit.internal.runners.rules", - "org.junit.internal.runners.statements", - "org.junit.matchers", - "org.junit.rules", - "org.junit.runner", - "org.junit.runner.manipulation", - "org.junit.runner.notification", - "org.junit.runners", - "org.junit.runners.model", - "org.junit.runners.parameterized", - "org.junit.validator" - ], - "net.bytebuddy:byte-buddy": [ - "net.bytebuddy", - "net.bytebuddy.agent.builder", - "net.bytebuddy.asm", - "net.bytebuddy.build", - "net.bytebuddy.description", - "net.bytebuddy.description.annotation", - "net.bytebuddy.description.enumeration", - "net.bytebuddy.description.field", - "net.bytebuddy.description.method", - "net.bytebuddy.description.modifier", - "net.bytebuddy.description.type", - "net.bytebuddy.dynamic", - "net.bytebuddy.dynamic.loading", - "net.bytebuddy.dynamic.scaffold", - "net.bytebuddy.dynamic.scaffold.inline", - "net.bytebuddy.dynamic.scaffold.subclass", - "net.bytebuddy.implementation", - "net.bytebuddy.implementation.attribute", - "net.bytebuddy.implementation.auxiliary", - "net.bytebuddy.implementation.bind", - "net.bytebuddy.implementation.bind.annotation", - "net.bytebuddy.implementation.bytecode", - "net.bytebuddy.implementation.bytecode.assign", - "net.bytebuddy.implementation.bytecode.assign.primitive", - "net.bytebuddy.implementation.bytecode.assign.reference", - "net.bytebuddy.implementation.bytecode.collection", - "net.bytebuddy.implementation.bytecode.constant", - "net.bytebuddy.implementation.bytecode.member", - "net.bytebuddy.jar.asm", - "net.bytebuddy.jar.asm.commons", - "net.bytebuddy.jar.asm.signature", - "net.bytebuddy.matcher", - "net.bytebuddy.pool", - "net.bytebuddy.utility", - "net.bytebuddy.utility.dispatcher", - "net.bytebuddy.utility.nullability", - "net.bytebuddy.utility.privilege", - "net.bytebuddy.utility.visitor" - ], - "net.bytebuddy:byte-buddy-agent": [ - "net.bytebuddy.agent", - "net.bytebuddy.agent.utility.nullability" - ], - "org.checkerframework:checker-qual": [ - "org.checkerframework.checker.builder.qual", - "org.checkerframework.checker.calledmethods.qual", - "org.checkerframework.checker.compilermsgs.qual", - "org.checkerframework.checker.fenum.qual", - "org.checkerframework.checker.formatter.qual", - "org.checkerframework.checker.guieffect.qual", - "org.checkerframework.checker.i18n.qual", - "org.checkerframework.checker.i18nformatter.qual", - "org.checkerframework.checker.index.qual", - "org.checkerframework.checker.initialization.qual", - "org.checkerframework.checker.interning.qual", - "org.checkerframework.checker.lock.qual", - "org.checkerframework.checker.mustcall.qual", - "org.checkerframework.checker.nullness.qual", - "org.checkerframework.checker.optional.qual", - "org.checkerframework.checker.propkey.qual", - "org.checkerframework.checker.regex.qual", - "org.checkerframework.checker.signature.qual", - "org.checkerframework.checker.signedness.qual", - "org.checkerframework.checker.tainting.qual", - "org.checkerframework.checker.units.qual", - "org.checkerframework.common.aliasing.qual", - "org.checkerframework.common.initializedfields.qual", - "org.checkerframework.common.reflection.qual", - "org.checkerframework.common.returnsreceiver.qual", - "org.checkerframework.common.subtyping.qual", - "org.checkerframework.common.util.count.report.qual", - "org.checkerframework.common.value.qual", - "org.checkerframework.dataflow.qual", - "org.checkerframework.framework.qual" - ], "org.hamcrest:hamcrest": [ "org.hamcrest", "org.hamcrest.beans", @@ -425,93 +231,10 @@ ], "org.jspecify:jspecify": [ "org.jspecify.annotations" - ], - "org.mockito:mockito-core": [ - "org.mockito", - "org.mockito.codegen", - "org.mockito.configuration", - "org.mockito.creation.instance", - "org.mockito.exceptions.base", - "org.mockito.exceptions.misusing", - "org.mockito.exceptions.stacktrace", - "org.mockito.exceptions.verification", - "org.mockito.exceptions.verification.junit", - "org.mockito.exceptions.verification.opentest4j", - "org.mockito.hamcrest", - "org.mockito.internal", - "org.mockito.internal.configuration", - "org.mockito.internal.configuration.injection", - "org.mockito.internal.configuration.injection.filter", - "org.mockito.internal.configuration.injection.scanner", - "org.mockito.internal.configuration.plugins", - "org.mockito.internal.creation", - "org.mockito.internal.creation.bytebuddy", - "org.mockito.internal.creation.instance", - "org.mockito.internal.creation.proxy", - "org.mockito.internal.creation.settings", - "org.mockito.internal.creation.util", - "org.mockito.internal.debugging", - "org.mockito.internal.exceptions", - "org.mockito.internal.exceptions.stacktrace", - "org.mockito.internal.exceptions.util", - "org.mockito.internal.framework", - "org.mockito.internal.hamcrest", - "org.mockito.internal.handler", - "org.mockito.internal.invocation", - "org.mockito.internal.invocation.finder", - "org.mockito.internal.invocation.mockref", - "org.mockito.internal.junit", - "org.mockito.internal.listeners", - "org.mockito.internal.matchers", - "org.mockito.internal.matchers.apachecommons", - "org.mockito.internal.matchers.text", - "org.mockito.internal.progress", - "org.mockito.internal.reporting", - "org.mockito.internal.runners", - "org.mockito.internal.runners.util", - "org.mockito.internal.session", - "org.mockito.internal.stubbing", - "org.mockito.internal.stubbing.answers", - "org.mockito.internal.stubbing.defaultanswers", - "org.mockito.internal.util", - "org.mockito.internal.util.collections", - "org.mockito.internal.util.concurrent", - "org.mockito.internal.util.io", - "org.mockito.internal.util.reflection", - "org.mockito.internal.verification", - "org.mockito.internal.verification.api", - "org.mockito.internal.verification.argumentmatching", - "org.mockito.internal.verification.checkers", - "org.mockito.invocation", - "org.mockito.junit", - "org.mockito.listeners", - "org.mockito.mock", - "org.mockito.plugins", - "org.mockito.quality", - "org.mockito.session", - "org.mockito.stubbing", - "org.mockito.verification" - ], - "org.objenesis:objenesis": [ - "org.objenesis", - "org.objenesis.instantiator", - "org.objenesis.instantiator.android", - "org.objenesis.instantiator.annotations", - "org.objenesis.instantiator.basic", - "org.objenesis.instantiator.gcj", - "org.objenesis.instantiator.perc", - "org.objenesis.instantiator.sun", - "org.objenesis.instantiator.util", - "org.objenesis.strategy" - ], - "org.ow2.asm:asm": [ - "org.objectweb.asm", - "org.objectweb.asm.signature" ] }, "repositories": { "https://repo1.maven.org/maven2/": [ - "com.google.auto.value:auto-value-annotations", "com.google.code.findbugs:jsr305", "com.google.code.gson:gson", "com.google.errorprone:error_prone_annotations", @@ -521,7 +244,6 @@ "com.google.j2objc:j2objc-annotations", "com.google.protobuf:protobuf-java", "com.google.protobuf:protobuf-java-util", - "com.google.truth:truth", "io.netty:netty-tcnative-boringssl-static", "io.netty:netty-tcnative-boringssl-static:jar:linux-aarch_64", "io.netty:netty-tcnative-boringssl-static:jar:linux-x86_64", @@ -529,16 +251,9 @@ "io.netty:netty-tcnative-boringssl-static:jar:osx-x86_64", "io.netty:netty-tcnative-boringssl-static:jar:windows-x86_64", "io.netty:netty-tcnative-classes", - "junit:junit", - "net.bytebuddy:byte-buddy", - "net.bytebuddy:byte-buddy-agent", - "org.checkerframework:checker-qual", "org.hamcrest:hamcrest", "org.hamcrest:hamcrest-core", - "org.jspecify:jspecify", - "org.mockito:mockito-core", - "org.objenesis:objenesis", - "org.ow2.asm:asm" + "org.jspecify:jspecify" ] }, "services": {},
diff --git a/private/extensions/maven.bzl b/private/extensions/maven.bzl index c42a051..3236888 100644 --- a/private/extensions/maven.bzl +++ b/private/extensions/maven.bzl
@@ -8,9 +8,10 @@ "escape", "strip_packaging_and_classifier_and_version", ) -load("//private/lib:coordinates.bzl", "to_external_form", "unpack_coordinates") +load("//private/lib:coordinates.bzl", "to_external_form", "to_key", "unpack_coordinates") load("//private/lib:toml_parser.bzl", "parse_toml") load("//private/rules:coursier.bzl", "DEFAULT_AAR_IMPORT_LABEL", "coursier_fetch", "pinned_coursier_fetch") +load("//private/rules:maven_version.bzl", "compare_maven_versions") load("//private/rules:unpinned_maven_pin_command_alias.bzl", "unpinned_maven_pin_command_alias") load("//private/rules:v1_lock_file.bzl", "v1_lock_file") load("//private/rules:v2_lock_file.bzl", "v2_lock_file") @@ -178,27 +179,11 @@ to_return.append(exclusion) return to_return -# Each bzlmod module may contribute jars to different rules_jvm_external maven repo namespaces. -# We record this mapping of repo_name to the list of modules that contributed to it, and emit a warning -# to the user if there are more than one module that contributed to the same repo name. -# -# This can be typical for the default @maven namespace, if a bzlmod dependency -# wishes to contribute to the users' jars. -def _check_repo_name(repo_name_2_module_name, repo_name, module_name): - contributing_module_names = repo_name_2_module_name.get(repo_name, []) - if module_name in contributing_module_names: - return - contributing_module_names.append(module_name) - repo_name_2_module_name[repo_name] = contributing_module_names - -def _warn_if_multiple_contributing_modules(repo_name_2_module_name, repos): - for (repo_name, contributing_module_names) in repo_name_2_module_name.items(): - if len(contributing_module_names) == 1: - continue - known_contributing_modules = repos[repo_name].get("known_contributing_modules", sets.make()) - new_contributing_modules = sets.difference(sets.make(contributing_module_names), known_contributing_modules) - if sets.length(new_contributing_modules) == 0: - continue +def _warn_if_multiple_contributing_modules(repo, repo_name, non_root_bazel_dep_to_artifacts): + known_contributing_modules = repo.get("known_contributing_modules", sets.make()) + contributing_module_names = non_root_bazel_dep_to_artifacts.keys() + new_contributing_modules = sets.difference(sets.make(contributing_module_names), known_contributing_modules) + if sets.length(new_contributing_modules) > 0: print("The maven repository '%s' has contributions from multiple bzlmod modules, and will be resolved together: %s." % ( repo_name, sorted(contributing_module_names), @@ -230,85 +215,67 @@ return seen -def _get_maven_module(artifact_spec): - """Extract group:artifact (maven module) coordinates from an artifact spec.""" - return "%s:%s" % (artifact_spec.group, artifact_spec.artifact) +def _deduplicate_non_root_artifacts(bazel_dep_to_non_root_artifacts, return_only_artifacts = False): + coordinate_to_artifact = {} + for bazel_dep_name in bazel_dep_to_non_root_artifacts: + for artifact in bazel_dep_to_non_root_artifacts.get(bazel_dep_name, []): + if not getattr(artifact, "testonly", False): + artifact_key = to_key(artifact) -def _find_duplicate_artifacts_across_submodules(non_root_artifacts, root_maven_modules): - """Find artifacts that appear in multiple sub-modules with detailed version info.""" + # prioritize highest version + if artifact_key in coordinate_to_artifact: + _bazel_dep_name, current_artifact = coordinate_to_artifact[artifact_key] + if compare_maven_versions(current_artifact.version, artifact.version) == -1: + coordinate_to_artifact[artifact_key] = (bazel_dep_name, artifact) + else: + coordinate_to_artifact[artifact_key] = (bazel_dep_name, artifact) - # Group artifacts by maven module (group:artifact) - maven_module_to_versions = {} + if return_only_artifacts: + return [v[1] for v in coordinate_to_artifact.values()] + else: + return coordinate_to_artifact - for artifact in non_root_artifacts: - maven_module = _get_maven_module(artifact) - if maven_module not in maven_module_to_versions: - maven_module_to_versions[maven_module] = [] - - # Extract version information - version = getattr(artifact, "version", "unspecified") - maven_module_to_versions[maven_module].append(version) - - # Find duplicates that aren't overridden by root - duplicates = {} - for maven_module, versions in maven_module_to_versions.items(): - # Only warn if: - # 1. There are multiple artifacts with the same group:artifact - # 2. This maven module is NOT overridden by the root module - if len(versions) > 1 and maven_module not in root_maven_modules: - # Deduplicate versions for cleaner output - unique_versions = {v: True for v in versions}.keys() - duplicates[maven_module] = sorted(unique_versions) - - return duplicates - -def _deduplicate_artifacts_with_root_priority(name, root_artifacts, non_root_artifacts): +# Each bzlmod module may contribute jars to different rules_jvm_external maven repo namespaces. +# We emit a warning to the user if a module overrides an artifact version in the root maven repo. +# +# This can be typical for the default @maven namespace, if a bzlmod dependency +# wishes to contribute to the users' jars. +def _deduplicate_artifacts_with_root_priority(name, root_artifacts, bazel_dep_to_non_root_artifacts, repin_env_var, rje_verbose_env_var): """Deduplicate artifacts, giving priority to root module artifacts with force_version set.""" + non_root_coordinate_to_artifact = _deduplicate_non_root_artifacts(bazel_dep_to_non_root_artifacts) - # Collect maven modules from root artifacts that have force_version = True - root_maven_modules_with_force_version = [] - for artifact in root_artifacts: - if getattr(artifact, "force_version", False): - maven_module = _get_maven_module(artifact) - if maven_module not in root_maven_modules_with_force_version: - root_maven_modules_with_force_version.append(maven_module) + duplicate_artifact_warning = "" + filtered_non_root_artifacts = [] + for root_artifact in root_artifacts: + artifact_key = to_key(root_artifact) + if artifact_key in non_root_coordinate_to_artifact: + bazel_dep_name, non_root_artifact = non_root_coordinate_to_artifact.pop(artifact_key) + if not getattr(root_artifact, "force_version", False): + # prioritize highest version + if compare_maven_versions(root_artifact.version, non_root_artifact.version) == -1: + filtered_non_root_artifacts.append(non_root_artifact) + duplicate_artifact_warning = duplicate_artifact_warning + ( + "\nWARNING: For dependency '%s:%s' the root @%s repo wants version %s, " % (root_artifact.group, root_artifact.artifact, name, root_artifact.version) + + "but got %s from the %s bazel dep. " % (non_root_artifact.version, bazel_dep_name) + + "Please update the version in your MODULE.bazel or set `force_version = True`." + ) - # Collect all maven modules from root artifacts (for duplicate detection) - all_root_maven_modules = [] - for artifact in root_artifacts: - maven_module = _get_maven_module(artifact) - if maven_module not in all_root_maven_modules: - all_root_maven_modules.append(maven_module) + # Add any remaining non root artifacts that weren't found in the root artifact list + addtional_artifact_message = "" + for bazel_dep_name, non_root_artifact in non_root_coordinate_to_artifact.values(): + addtional_artifact_message = addtional_artifact_message + ( + "\nINFO: The @%s repo is getting the additional artifact %s:%s:%s from the %s bazel dep." % (name, non_root_artifact.group, non_root_artifact.artifact, non_root_artifact.version, bazel_dep_name) + ) + filtered_non_root_artifacts.append(non_root_artifact) - # Find duplicates across sub-modules that aren't overridden by root - duplicate_submodule_artifacts = _find_duplicate_artifacts_across_submodules( - non_root_artifacts, - all_root_maven_modules, - ) + if repin_env_var: + if duplicate_artifact_warning != "": + print(duplicate_artifact_warning) + if rje_verbose_env_var: + if addtional_artifact_message != "": + print(addtional_artifact_message) - # Filter non-root artifacts that conflict with root artifacts that have force_version = True - filtered_non_root = [] - for artifact in non_root_artifacts: - maven_module = _get_maven_module(artifact) - - # Only exclude if root module has force_version = True for this coordinate - if not maven_module in root_maven_modules_with_force_version: - filtered_non_root.append(artifact) - - # Log detailed warning for duplicate sub-module artifacts - if len(duplicate_submodule_artifacts): - warning_parts = [] - for maven_module, versions in duplicate_submodule_artifacts.items(): - if len(versions) > 1: - warning_parts.append("%s (versions: %s)" % (maven_module, ", ".join(versions))) - else: - warning_parts.append(maven_module) - - print("WARNING: The following coordinates from `%s` appear in multiple sub-modules with potentially different versions. " % name + - "Consider adding one of these to your root module to ensure consistent versions and setting `force_version = True` on that artifact:\n\t%s" % - "\n\t".join(sorted(warning_parts))) - - return root_artifacts + filtered_non_root + return root_artifacts + filtered_non_root_artifacts def _amend_artifact(original_artifact, amend): """Apply amendments to an artifact struct, returning a new amended struct.""" @@ -391,137 +358,155 @@ return artifacts, boms -def _process_module_tags(mctx, mod, target_repos, repo_name_2_module_name): - """Process artifact and install tags for a single module.""" - - # Process from_toml tags - for from_toml_tag in mod.tags.from_toml: - _check_repo_name(repo_name_2_module_name, from_toml_tag.name, mod.name) - - repo = target_repos.get(from_toml_tag.name, {}) - - content = mctx.read(mctx.path(from_toml_tag.libs_versions_toml)) - parsed = parse_toml(content) - - (new_artifacts, new_boms) = process_gradle_versions_file(parsed, from_toml_tag.bom_modules) - - repo["artifacts"] = repo.get("artifacts", []) + new_artifacts - repo["boms"] = repo.get("boms", []) + new_boms - target_repos[from_toml_tag.name] = repo - - for artifact in mod.tags.artifact: - _check_repo_name(repo_name_2_module_name, artifact.name, mod.name) - - repo = target_repos.get(artifact.name, {}) - existing_artifacts = repo.get("artifacts", []) - existing_artifacts.append(struct( - group = artifact.group, - artifact = artifact.artifact, - version = artifact.version, - packaging = artifact.packaging, - classifier = artifact.classifier, - force_version = artifact.force_version, - neverlink = artifact.neverlink, - testonly = artifact.testonly, - exclusions = _add_exclusions(artifact.exclusions), - )) - - repo["artifacts"] = existing_artifacts - target_repos[artifact.name] = repo - - for install in mod.tags.install: - _check_repo_name(repo_name_2_module_name, install.name, mod.name) - - repo = target_repos.get(install.name, {}) - - repo["resolver"] = install.resolver - +def _add_artifacts_to_repo(repo, mod, new_artifacts): + if mod.is_root: artifacts = repo.get("artifacts", []) - repo["artifacts"] = artifacts + [unpack_coordinates(a) for a in install.artifacts] + repo["artifacts"] = artifacts + new_artifacts + else: + if not "bazel_dep_to_artifacts" in repo: + repo["bazel_dep_to_artifacts"] = {} + artifacts = repo["bazel_dep_to_artifacts"].get(mod.name, []) + repo["bazel_dep_to_artifacts"][mod.name] = artifacts + new_artifacts +def _add_boms_to_repo(repo, mod, new_boms): + if mod.is_root: boms = repo.get("boms", []) - repo["boms"] = boms + [unpack_coordinates(b) for b in install.boms] + repo["boms"] = boms + new_boms + else: + if not "bazel_dep_to_boms" in repo: + repo["bazel_dep_to_boms"] = {} + boms = repo["bazel_dep_to_boms"].get(mod.name, []) + repo["bazel_dep_to_boms"][mod.name] = boms + new_boms - existing_repos = repo.get("repositories", []) - for repository in parse.parse_repository_spec_list(install.repositories): - repo_string = _json.write_repository_spec(repository) - if repo_string not in existing_repos: - existing_repos.append(repo_string) - repo["repositories"] = existing_repos +def _process_module_tags(mctx): + """Process artifact and install tags for a single module.""" + root_module_repos = {} + non_root_module_repos = {} - repo["excluded_artifacts"] = repo.get("excluded_artifacts", []) + install.excluded_artifacts + for mod in mctx.modules: + target_repos = root_module_repos if mod.is_root else non_root_module_repos - _logical_or(repo, "fetch_sources", False, install.fetch_sources) - _logical_or(repo, "generate_compat_repositories", False, install.generate_compat_repositories) - _logical_or(repo, "use_starlark_android_rules", False, install.use_starlark_android_rules) - _logical_or(repo, "ignore_empty_files", False, install.ignore_empty_files) - _logical_or(repo, "use_credentials_from_home_netrc_file", False, install.use_credentials_from_home_netrc_file) + # Process from_toml tags + for from_toml_tag in mod.tags.from_toml: + repo = target_repos.get(from_toml_tag.name, {}) - repo["version_conflict_policy"] = _fail_if_different( - "version_conflict_policy", - repo.get("version_conflict_policy"), - install.version_conflict_policy, - [None, "default"], - ) + content = mctx.read(mctx.path(from_toml_tag.libs_versions_toml)) + parsed = parse_toml(content) - repo["strict_visibility_value"] = _fail_if_different( - "strict_visibility_value", - repo.get("strict_visibility_value", []), - install.strict_visibility_value, - [None, []], - ) + (new_artifacts, new_boms) = process_gradle_versions_file(parsed, from_toml_tag.bom_modules) - additional_netrc_lines = repo.get("additional_netrc_lines", []) + getattr(install, "additional_netrc_lines", []) - repo["additional_netrc_lines"] = additional_netrc_lines + _add_artifacts_to_repo(repo, mod, new_artifacts) + _add_boms_to_repo(repo, mod, new_boms) - repo["aar_import_bzl_label"] = _fail_if_different( - "aar_import_bzl_label", - repo.get("aar_import_bzl_label"), - install.aar_import_bzl_label, - [DEFAULT_AAR_IMPORT_LABEL, None], - ) + target_repos[from_toml_tag.name] = repo - repo["duplicate_version_warning"] = _fail_if_different( - "duplicate_version_warning", - repo.get("duplicate_version_warning"), - install.duplicate_version_warning, - [None, "warn"], - ) + for artifact in mod.tags.artifact: + repo = target_repos.get(artifact.name, {}) + new_artifact = struct( + group = artifact.group, + artifact = artifact.artifact, + version = artifact.version, + packaging = artifact.packaging, + classifier = artifact.classifier, + force_version = artifact.force_version, + neverlink = artifact.neverlink, + testonly = artifact.testonly, + exclusions = _add_exclusions(artifact.exclusions), + ) + _add_artifacts_to_repo(repo, mod, [new_artifact]) - # Get the longest timeout - timeout = repo.get("resolve_timeout", install.resolve_timeout) - if install.resolve_timeout > timeout: - timeout = install.resolve_timeout - repo["resolve_timeout"] = timeout + target_repos[artifact.name] = repo - if mod.is_root: - repo["repin_instructions"] = install.repin_instructions - repo["known_contributing_modules"] = sets.make(install.known_contributing_modules) + for install in mod.tags.install: + repo = target_repos.get(install.name, {}) - repo["additional_coursier_options"] = repo.get("additional_coursier_options", []) + getattr(install, "additional_coursier_options", []) + repo["resolver"] = install.resolver - target_repos[install.name] = repo + _add_artifacts_to_repo(repo, mod, [unpack_coordinates(a) for a in install.artifacts]) + _add_boms_to_repo(repo, mod, [unpack_coordinates(b) for b in install.boms]) - # Process amend_artifact tags - for amend in mod.tags.amend_artifact: - _check_repo_name(repo_name_2_module_name, amend.name, mod.name) + existing_repos = repo.get("repositories", []) + for repository in parse.parse_repository_spec_list(install.repositories): + repo_string = _json.write_repository_spec(repository) + if repo_string not in existing_repos: + existing_repos.append(repo_string) + repo["repositories"] = existing_repos - repo = target_repos.get(amend.name, {}) - artifacts = repo.get("artifacts", []) + repo["excluded_artifacts"] = repo.get("excluded_artifacts", []) + install.excluded_artifacts - # Find matching artifacts and amend them - amended = False - for i, artifact in enumerate(artifacts): - if _coordinates_match(artifact, amend.coordinates): - artifacts[i] = _amend_artifact(artifact, amend) - amended = True + _logical_or(repo, "fetch_sources", False, install.fetch_sources) + _logical_or(repo, "generate_compat_repositories", False, install.generate_compat_repositories) + _logical_or(repo, "use_starlark_android_rules", False, install.use_starlark_android_rules) + _logical_or(repo, "ignore_empty_files", False, install.ignore_empty_files) + _logical_or(repo, "use_credentials_from_home_netrc_file", False, install.use_credentials_from_home_netrc_file) - if not amended: - # If no matching artifact found, this might be an error or we could create a placeholder - fail("No artifact found matching coordinates '%s' for amendment" % amend.coordinates) + repo["version_conflict_policy"] = _fail_if_different( + "version_conflict_policy", + repo.get("version_conflict_policy"), + install.version_conflict_policy, + [None, "default"], + ) - repo["artifacts"] = artifacts - target_repos[amend.name] = repo + repo["strict_visibility_value"] = _fail_if_different( + "strict_visibility_value", + repo.get("strict_visibility_value", []), + install.strict_visibility_value, + [None, []], + ) + + additional_netrc_lines = repo.get("additional_netrc_lines", []) + getattr(install, "additional_netrc_lines", []) + repo["additional_netrc_lines"] = additional_netrc_lines + + repo["aar_import_bzl_label"] = _fail_if_different( + "aar_import_bzl_label", + repo.get("aar_import_bzl_label"), + install.aar_import_bzl_label, + [DEFAULT_AAR_IMPORT_LABEL, None], + ) + + repo["duplicate_version_warning"] = _fail_if_different( + "duplicate_version_warning", + repo.get("duplicate_version_warning"), + install.duplicate_version_warning, + [None, "warn"], + ) + + # Get the longest timeout + timeout = repo.get("resolve_timeout", install.resolve_timeout) + if install.resolve_timeout > timeout: + timeout = install.resolve_timeout + repo["resolve_timeout"] = timeout + + if mod.is_root: + repo["repin_instructions"] = install.repin_instructions + repo["known_contributing_modules"] = sets.make(install.known_contributing_modules) + + repo["additional_coursier_options"] = repo.get("additional_coursier_options", []) + getattr(install, "additional_coursier_options", []) + + target_repos[install.name] = repo + + # Process amend_artifact tags + for amend in mod.tags.amend_artifact: + repo = target_repos.get(amend.name, {}) + if mod.is_root: + artifacts = repo.get("artifacts", []) + else: + if not "bazel_dep_to_artifacts" in repo: + repo["bazel_dep_to_artifacts"] = {} + artifacts = repo["bazel_dep_to_artifacts"].get(mod.name, []) + + # Find matching artifacts and amend them + amended = False + for i, artifact in enumerate(artifacts): + if _coordinates_match(artifact, amend.coordinates): + artifacts[i] = _amend_artifact(artifact, amend) + amended = True + + if not amended: + # If no matching artifact found, this might be an error or we could create a placeholder + fail("No artifact found matching coordinates '%s' for amendment" % amend.coordinates) + + return root_module_repos, non_root_module_repos def _merge_repo_lists(root_list, non_root_list): """Merge two lists, removing duplicates while preserving order, root items first.""" @@ -548,11 +533,6 @@ http_files = [] compat_repos = [] - # Separate tracking for root vs non-root artifacts - root_module_repos = {} - non_root_module_repos = {} - repo_name_2_module_name = {} - # Process overrides first (they don't need deduplication) # The order of the transitive overrides do not matter, but the root # overrides take precedence over all transitive ones. @@ -582,14 +562,16 @@ overrides[override.name].update({override.coordinates: to_use}) override_visibilities[override.name].update({override.coordinates: visibility_to_use}) - # First pass: process the module tags, but keep root and non-root modules separately - for mod in mctx.modules: - collection = root_module_repos if mod.is_root else non_root_module_repos - _process_module_tags(mctx, mod, collection, repo_name_2_module_name) + # First pass: process the module tags, separating root and non-root modules + root_module_repos, non_root_module_repos = _process_module_tags(mctx) # Second pass: merge and deduplicate repositories all_repo_names = {name: True for name in root_module_repos.keys() + non_root_module_repos.keys()}.keys() + os_env = mctx.os.environ + repin_env_var = os_env.get("REPIN") + rje_verbose_env_var = os_env.get("RJE_VERBOSE") + for repo_name in all_repo_names: root_repo = root_module_repos.get(repo_name, {}) non_root_repo = non_root_module_repos.get(repo_name, {}) @@ -601,25 +583,32 @@ # Special handling for artifacts and boms - deduplicate with root priority root_artifacts = root_repo.get("artifacts", []) - non_root_artifacts = non_root_repo.get("artifacts", []) + bazel_dep_to_non_root_artifacts = non_root_repo.get("bazel_dep_to_artifacts", {}) root_boms = root_repo.get("boms", []) - non_root_boms = non_root_repo.get("boms", []) + non_root_bazel_dep_to_boms = non_root_repo.get("bazel_dep_to_boms", []) if repo_name in root_module_repos.keys(): + # Warn users if multiple modules contribute to the same maven `name` + _warn_if_multiple_contributing_modules(root_repo, repo_name, bazel_dep_to_non_root_artifacts) + merged_repo["artifacts"] = _deduplicate_artifacts_with_root_priority( repo_name, root_artifacts, - non_root_artifacts, + bazel_dep_to_non_root_artifacts, + repin_env_var, + rje_verbose_env_var, ) merged_repo["boms"] = _deduplicate_artifacts_with_root_priority( repo_name, root_boms, - non_root_boms, + bazel_dep_to_non_root_artifacts, + repin_env_var, + rje_verbose_env_var, ) else: - merged_repo["artifacts"] = non_root_artifacts - merged_repo["boms"] = non_root_boms + merged_repo["artifacts"] = _deduplicate_non_root_artifacts(bazel_dep_to_non_root_artifacts, True) + merged_repo["boms"] = _deduplicate_non_root_artifacts(bazel_dep_to_non_root_artifacts, True) # For list attributes, concatenate but avoid duplicates (root items first) for list_attr in ["repositories", "excluded_artifacts", "additional_netrc_lines", "additional_coursier_options"]: @@ -629,9 +618,6 @@ repos[repo_name] = merged_repo - # Warn users if multiple modules contribute to the same maven `name` - _warn_if_multiple_contributing_modules(repo_name_2_module_name, repos) - # Breaking out the logic for picking lock files, because it's not terribly simple repo_to_lock_file = {} for mod in mctx.modules:
diff --git a/private/rules/maven_version.bzl b/private/rules/maven_version.bzl new file mode 100644 index 0000000..e031d21 --- /dev/null +++ b/private/rules/maven_version.bzl
@@ -0,0 +1,664 @@ +"""Maven ComparableVersion implementation in Starlark. + +This implements the version comparison algorithm from Apache Maven's +ComparableVersion class. See: +https://maven.apache.org/pom.html#version-order-specification + +Features: +- Mixing of '-' (hyphen) and '.' (dot) separators +- Transition between characters and digits constitutes a separator +- Unlimited number of version components +- Version components can be digits or strings +- Well-known qualifiers have special ordering (alpha < beta < milestone < rc < snapshot < "" < sp) +- Unknown qualifiers are sorted lexically after known qualifiers +""" + +# Item type constants +_TYPE_INT = "int" +_TYPE_STR = "str" +_TYPE_LIST = "list" +_TYPE_COMBO = "combo" + +# Qualifier ordering (from least to greatest) +# Index 5 ("") represents a release version +_QUALIFIERS = ["alpha", "beta", "milestone", "rc", "snapshot", "", "sp"] + +# These qualifiers are equivalent to "" (release) +_RELEASE_QUALIFIERS = ["ga", "final", "release"] + +# Aliases for qualifiers +_ALIASES = {"cr": "rc"} + +def _strip_leading_zeroes(s): + """Strip leading zeroes from a numeric string.""" + if not s: + return "0" + for i in range(len(s)): + if s[i] != "0": + return s[i:] + return "0" + +def _comparable_qualifier(qualifier): + """ + Convert qualifier to a comparable string. + + Known qualifiers get their index, unknown qualifiers get "7-" prefix + followed by the qualifier for lexical sorting after known ones. + """ + if qualifier in _RELEASE_QUALIFIERS: + return str(_QUALIFIERS.index("")) + + for i, q in enumerate(_QUALIFIERS): + if q == qualifier: + return str(i) + + # Unknown qualifier: sort after known ones, lexically + return str(len(_QUALIFIERS)) + "-" + qualifier + +def _new_int_item(value): + """Create an integer item.""" + return {"type": _TYPE_INT, "value": int(value) if value else 0} + +def _new_string_item(value, followed_by_digit): + """Create a string item, handling single-char aliases.""" + if followed_by_digit and len(value) == 1: + if value == "a": + value = "alpha" + elif value == "b": + value = "beta" + elif value == "m": + value = "milestone" + + # Apply aliases (cr -> rc) + value = _ALIASES.get(value, value) + return {"type": _TYPE_STR, "value": value} + +def _new_list_item(items): + """Create a list item.""" + return {"type": _TYPE_LIST, "value": items} + +def _new_combo_item(string_part, digit_part): + """Create a combination item (string followed by digits, like 'alpha1').""" + return {"type": _TYPE_COMBO, "string_part": string_part, "digit_part": digit_part} + +def _parse_combination(buf): + """Parse a combination token like 'alpha1' or 'rc2'.""" + buf = buf.replace("-", "") + + # Find where digits start + index = len(buf) + for i in range(len(buf)): + if buf[i].isdigit(): + index = i + break + + string_part = _new_string_item(buf[:index], True) + digit_str = _strip_leading_zeroes(buf[index:]) if index < len(buf) else "0" + digit_part = _new_int_item(digit_str) + + return _new_combo_item(string_part, digit_part) + +def _item_is_null(item): + """Check if an item is null (equivalent to empty/zero).""" + t = item["type"] + if t == _TYPE_INT: + return item["value"] == 0 + elif t == _TYPE_STR: + return not item["value"] + elif t == _TYPE_LIST: + return len(item["value"]) == 0 + elif t == _TYPE_COMBO: + return False + elif item["value"] == None: + return True + return False + +def _normalize_list(items): + """ + Normalize a list by removing trailing null items. + + This follows Maven's normalization rules: + - Remove trailing null items (0 for numbers, "" for strings, empty lists) + - But only if followed by a string or at the end + """ + result = list(items) + + # Iterate backwards using range + for idx in range(len(result) - 1, -1, -1): + item = result[idx] + if _item_is_null(item): + next_idx = idx + 1 + should_remove = False + + if next_idx >= len(result): + # At the end + should_remove = True + elif result[next_idx]["type"] == _TYPE_STR: + # Followed by string + should_remove = True + elif result[next_idx]["type"] == _TYPE_LIST: + # Followed by list - check first item in that list + next_list = result[next_idx]["value"] + if next_list: + first_type = next_list[0]["type"] + if first_type == _TYPE_COMBO or first_type == _TYPE_STR: + should_remove = True + else: + should_remove = True + + if should_remove: + result.pop(idx) + + return result + +def _compare_strings(s1, s2): + """Compare two strings lexicographically.""" + if s1 < s2: + return -1 + elif s1 > s2: + return 1 + return 0 + +def _compare_ints(i1, i2): + """Compare two integers.""" + if i1 < i2: + return -1 + elif i1 > i2: + return 1 + return 0 + +def _compare_to_null_simple(item): + """Compare a simple (non-list) item to null.""" + t = item["type"] + + if t == _TYPE_INT: + return 0 if item["value"] == 0 else 1 + + elif t == _TYPE_STR: + release_idx = _comparable_qualifier("") + return _compare_strings(_comparable_qualifier(item["value"]), release_idx) + + elif t == _TYPE_COMBO: + # Combination's null comparison is based on its string part + release_idx = _comparable_qualifier("") + return _compare_strings(_comparable_qualifier(item["string_part"]["value"]), release_idx) + + return 0 + +def _compare_simple_items(left, right): + """ + Compare two simple (non-list) items. + + Returns: + -1 if left < right + 0 if left == right + 1 if left > right + """ + lt = left["type"] + rt = right["type"] + + # INT comparisons + if lt == _TYPE_INT: + if rt == _TYPE_INT: + return _compare_ints(left["value"], right["value"]) + elif rt == _TYPE_STR: + return 1 # number > string + elif rt == _TYPE_COMBO: + return 1 # number > combination + elif rt == _TYPE_LIST: + return 1 # number > list + + # STRING comparisons + elif lt == _TYPE_STR: + if rt == _TYPE_INT: + return -1 # string < number + elif rt == _TYPE_STR: + q1 = _comparable_qualifier(left["value"]) + q2 = _comparable_qualifier(right["value"]) + return _compare_strings(q1, q2) + elif rt == _TYPE_COMBO: + # Compare string to combination's string part + q1 = _comparable_qualifier(left["value"]) + q2 = _comparable_qualifier(right["string_part"]["value"]) + result = _compare_strings(q1, q2) + if result == 0: + return -1 # X < X1 + return result + elif rt == _TYPE_LIST: + return -1 # string < list + + # COMBINATION comparisons + elif lt == _TYPE_COMBO: + if rt == _TYPE_INT: + return -1 # combination < number + elif rt == _TYPE_STR: + q1 = _comparable_qualifier(left["string_part"]["value"]) + q2 = _comparable_qualifier(right["value"]) + result = _compare_strings(q1, q2) + if result == 0: + return 1 # X1 > X + return result + elif rt == _TYPE_COMBO: + q1 = _comparable_qualifier(left["string_part"]["value"]) + q2 = _comparable_qualifier(right["string_part"]["value"]) + result = _compare_strings(q1, q2) + if result == 0: + return _compare_ints(left["digit_part"]["value"], right["digit_part"]["value"]) + return result + elif rt == _TYPE_LIST: + return -1 # combination < list + + # LIST comparisons (comparing list to non-list) + elif lt == _TYPE_LIST: + if rt == _TYPE_INT: + return -1 # list < number + elif rt == _TYPE_STR: + return 1 # list > string + elif rt == _TYPE_COMBO: + return 1 # list > combination + + return 0 + +def _compare_items(left, right): + """ + Compare two items iteratively using a stack. + + Returns: + -1 if left < right + 0 if left == right + 1 if left > right + """ + + # Stack entries: (left_item, right_item, list_index) + # list_index is used when both items are lists to track position + stack = [(left, right, 0)] + + # Process stack iteratively + for _ in range(1000): # Safety limit to prevent infinite loops + if not stack: + return 0 + + current_left, current_right, idx = stack.pop() + + # Handle null comparisons + if current_right == None: + if current_left == None: + continue + + t = current_left["type"] + if t == _TYPE_LIST: + items = current_left["value"] + if not items: + continue + + # Push all items to compare to null (in reverse order so first is processed first) + for i in range(len(items) - 1, -1, -1): + stack.append((items[i], None, 0)) + continue + else: + result = _compare_to_null_simple(current_left) + if result != 0: + return result + continue + + if current_left == None: + # Invert comparison + t = current_right["type"] + if t == _TYPE_LIST: + items = current_right["value"] + if not items: + continue + + for i in range(len(items) - 1, -1, -1): + stack.append((None, items[i], 0)) + continue + else: + result = _compare_to_null_simple(current_right) + if result != 0: + return -1 * result + continue + + lt = current_left["type"] + rt = current_right["type"] + + # Both are lists - compare element by element + if lt == _TYPE_LIST and rt == _TYPE_LIST: + left_items = current_left["value"] + right_items = current_right["value"] + max_len = max(len(left_items), len(right_items)) + + # Push remaining comparisons in reverse order + for i in range(max_len - 1, -1, -1): + l = left_items[i] if i < len(left_items) else None + r = right_items[i] if i < len(right_items) else None + stack.append((l, r, 0)) + continue + + # At least one is not a list - compare directly + result = _compare_simple_items(current_left, current_right) + if result != 0: + return result + + return 0 + +def _parse_version(version): + """ + Parse a version string into a structured list. + + Args: + version: The version string to parse + + Returns: + A list item containing the parsed version structure + """ + version = version.lower() + + items = [] + stack = [items] + current_list = [items] # Use list to allow mutation in nested function scope + + is_digit = [False] # Use list to allow mutation + is_combination = [False] + start_index = [0] + + def add_token(end_index): + """Add a token from start_index to end_index.""" + if end_index == start_index[0]: + current_list[0].append(_new_int_item("0")) + else: + token = version[start_index[0]:end_index] + if is_combination[0]: + current_list[0].append(_parse_combination(token)) + elif is_digit[0]: + current_list[0].append(_new_int_item(_strip_leading_zeroes(token))) + else: + current_list[0].append(_new_string_item(token, False)) + + def start_new_sublist(): + """Start a new sub-list.""" + if current_list[0]: + new_list = [] + current_list[0].append(_new_list_item(new_list)) + stack.append(new_list) + current_list[0] = new_list + + # Process each character + skip_next = [False] + for i in range(len(version)): + if skip_next[0]: + skip_next[0] = False + continue + + c = version[i] + + if c == ".": + add_token(i) + is_combination[0] = False + start_index[0] = i + 1 + is_digit[0] = False + + elif c == "-": + if i == start_index[0]: + current_list[0].append(_new_int_item("0")) + else: + # Check for X-1 pattern (should be treated as X1 combination) + if not is_digit[0] and i < len(version) - 1: + next_c = version[i + 1] + if next_c.isdigit(): + is_combination[0] = True + skip_next[0] = False # Don't skip, continue processing + continue + + token = version[start_index[0]:i] + if is_combination[0]: + current_list[0].append(_parse_combination(token)) + elif is_digit[0]: + current_list[0].append(_new_int_item(_strip_leading_zeroes(token))) + else: + current_list[0].append(_new_string_item(token, False)) + + start_index[0] = i + 1 + + # Hyphen creates a new sub-list + start_new_sublist() + + is_combination[0] = False + is_digit[0] = False + + elif c.isdigit(): + if not is_digit[0] and i > start_index[0]: + # Transition from string to digit: X1 + is_combination[0] = True + start_new_sublist() + is_digit[0] = True + + else: + # Non-digit character + if is_digit[0] and i > start_index[0]: + # Transition from digit to string + token = version[start_index[0]:i] + if is_combination[0]: + current_list[0].append(_parse_combination(token)) + else: + current_list[0].append(_new_int_item(_strip_leading_zeroes(token))) + + start_index[0] = i + start_new_sublist() + is_combination[0] = False + + is_digit[0] = False + + # Handle remaining token + if len(version) > start_index[0]: + # Treat .X as -X for any string qualifier X + if not is_digit[0] and current_list[0]: + start_new_sublist() + + token = version[start_index[0]:] + if is_combination[0]: + current_list[0].append(_parse_combination(token)) + elif is_digit[0]: + current_list[0].append(_new_int_item(_strip_leading_zeroes(token))) + else: + current_list[0].append(_new_string_item(token, False)) + + # Normalize all lists in reverse order (deepest first) + for j in range(len(stack) - 1, -1, -1): + lst = stack[j] + normalized = _normalize_list(lst) + + # Clear and repopulate the list + for _ in range(len(lst)): + lst.pop() + for item in normalized: + lst.append(item) + + return _new_list_item(items) + +def _simple_item_to_string(item): + """Convert a simple (non-list) item to string.""" + t = item["type"] + if t == _TYPE_INT: + return str(item["value"]) + elif t == _TYPE_STR: + return item["value"] + elif t == _TYPE_COMBO: + return item["string_part"]["value"] + str(item["digit_part"]["value"]) + return "" + +def _item_to_canonical(item): + """ + Convert an item to its canonical string representation (iterative). + + Args: + item: The parsed item + + Returns: + The canonical string representation + """ + if item["type"] != _TYPE_LIST: + return _simple_item_to_string(item) + + # For lists, we need to flatten and build the string iteratively + # Use a work queue approach: process list items and build result + result_parts = [] + + # Stack of (items_list, current_index, is_first_in_parent) + stack = [(item["value"], 0, True)] + + for _ in range(1000): # Safety limit + if not stack: + break + + current_items, current_idx, is_first = stack[-1] + + if current_idx >= len(current_items): + stack.pop() + continue + + sub_item = current_items[current_idx] + stack[-1] = (current_items, current_idx + 1, False) + + # Add separator if not first item + if not is_first: + if sub_item["type"] == _TYPE_LIST: + result_parts.append("-") + else: + result_parts.append(".") + + if sub_item["type"] == _TYPE_LIST: + # Push the sub-list onto the stack + if sub_item["value"]: + stack.append((sub_item["value"], 0, True)) + else: + result_parts.append(_simple_item_to_string(sub_item)) + + return "".join(result_parts) + +def get_canonical(version): + """ + Get the canonical (normalized) form of a version string. + + The canonical form normalizes the version by: + - Removing trailing zero segments (1.0.0 -> 1) + - Normalizing qualifiers (1-ga -> 1, 1a1 -> 1.alpha.1) + - Lowercasing everything + + Args: + version: The version string + + Returns: + The canonical form of the version + """ + parsed = _parse_version(version) + return _item_to_canonical(parsed) + +def compare_maven_versions(v1, v2): + """ + Compare two Maven version strings. + + This implements Maven's ComparableVersion comparison algorithm. + + Args: + v1: First version string + v2: Second version string + + Returns: + -1 if v1 < v2 + 0 if v1 == v2 + 1 if v1 > v2 + """ + parsed1 = _parse_version(v1) + parsed2 = _parse_version(v2) + return _compare_items(parsed1, parsed2) + +def is_version_greater(v1, v2): + """Check if v1 is greater than v2.""" + return compare_maven_versions(v1, v2) > 0 + +def is_version_less(v1, v2): + """Check if v1 is less than v2.""" + return compare_maven_versions(v1, v2) < 0 + +def is_version_equal(v1, v2): + return compare_maven_versions(v1, v2) == 0 + +def max_version(versions): + """ + Return the maximum version from a list of version strings. + + Args: + versions: List of version strings + + Returns: + The maximum version string, or None if the list is empty + """ + if not versions: + return None + + result = versions[0] + for v in versions[1:]: + if compare_maven_versions(v, result) > 0: + result = v + return result + +def min_version(versions): + """ + Return the minimum version from a list of version strings. + + Args: + versions: List of version strings + + Returns: + The minimum version string, or None if the list is empty + """ + if not versions: + return None + + result = versions[0] + for v in versions[1:]: + if compare_maven_versions(v, result) < 0: + result = v + return result + +def sort_versions(versions, reverse = False): + """ + Sort a list of version strings. + + This uses a simple insertion sort since Starlark doesn't have a built-in + sort with custom comparator. + + Args: + versions: List of version strings + reverse: If True, sort in descending order + + Returns: + A new sorted list of version strings + """ + result = list(versions) + + # Insertion sort using for loops + for i in range(1, len(result)): + key = result[i] + + # Find insertion point + insert_pos = 0 + found = False + for k in range(i - 1, -1, -1): + cmp = compare_maven_versions(result[k], key) + should_move = cmp > 0 if not reverse else cmp < 0 + if not should_move: + insert_pos = k + 1 + found = True + break + + if not found: + insert_pos = 0 + + # Shift elements and insert + if insert_pos < i: + for m in range(i, insert_pos, -1): + result[m] = result[m - 1] + result[insert_pos] = key + + return result
diff --git a/tests/custom_maven_install/multiple_lock_files_install.json b/tests/custom_maven_install/multiple_lock_files_install.json index 3a698d6..6825e7e 100644 --- a/tests/custom_maven_install/multiple_lock_files_install.json +++ b/tests/custom_maven_install/multiple_lock_files_install.json
@@ -1,6 +1,6 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": -965689370, + "__INPUT_ARTIFACTS_HASH": -575359670, "__RESOLVED_ARTIFACTS_HASH": -746459257, "artifacts": { "com.google.code.gson:gson": {
diff --git a/tests/custom_maven_install/regression_testing_coursier_install.json b/tests/custom_maven_install/regression_testing_coursier_install.json index 037c9e1..d78bd9b 100644 --- a/tests/custom_maven_install/regression_testing_coursier_install.json +++ b/tests/custom_maven_install/regression_testing_coursier_install.json
@@ -1,7 +1,7 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", "__INPUT_ARTIFACTS_HASH": 899526900, - "__RESOLVED_ARTIFACTS_HASH": -1058194221, + "__RESOLVED_ARTIFACTS_HASH": -1034505854, "artifacts": { "android.arch.core:common": { "shasums": { @@ -636,9 +636,9 @@ }, "com.nimbusds:nimbus-jose-jwt": { "shasums": { - "jar": "104ed6c348d35f68ab16d85550ed59239bcb7846bc2bd885ef3851a0ab2e73cf" + "jar": "899791e2fc5512279348617bd30c64f22d9a1456d7c54619e1f8ff359dcb2cc5" }, - "version": "10.5" + "version": "10.7" }, "com.nimbusds:oauth2-oidc-sdk": { "shasums": {
diff --git a/tests/custom_maven_install/root_wins_install.json b/tests/custom_maven_install/root_wins_install.json index a5997f4..1d8b8ae 100644 --- a/tests/custom_maven_install/root_wins_install.json +++ b/tests/custom_maven_install/root_wins_install.json
@@ -1,6 +1,6 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": -935842665, + "__INPUT_ARTIFACTS_HASH": 1721865497, "__RESOLVED_ARTIFACTS_HASH": -1272363274, "artifacts": { "io.netty:netty-buffer": {
diff --git a/tests/unit/BUILD b/tests/unit/BUILD index 194f77c..4144fce 100644 --- a/tests/unit/BUILD +++ b/tests/unit/BUILD
@@ -3,6 +3,7 @@ load(":coursier_test.bzl", "coursier_test_suite") load(":coursier_utilities_test.bzl", "coursier_utilities_test_suite") load(":java_utilities_test.bzl", "java_utilities_test_suite") +load(":maven_version_test.bzl", "maven_version_test_suite") load(":proxy_test.bzl", "proxy_test_suite") load(":specs_test.bzl", "artifact_specs_test_suite") load(":version_catalogs_test.bzl", "version_catalogs_test_suite") @@ -19,6 +20,8 @@ java_utilities_test_suite() +maven_version_test_suite() + proxy_test_suite() version_catalogs_test_suite()
diff --git a/tests/unit/maven_version_test.bzl b/tests/unit/maven_version_test.bzl new file mode 100644 index 0000000..ce43958 --- /dev/null +++ b/tests/unit/maven_version_test.bzl
@@ -0,0 +1,493 @@ +"""Tests for Maven version comparison.""" + +load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest") +load("//private/rules:maven_version.bzl", "compare_maven_versions", "get_canonical", "is_version_equal", "is_version_greater", "is_version_less", "max_version", "min_version", "sort_versions") + +def _check_versions_order(v1, v2): + """Check that v1 < v2.""" + cmp = compare_maven_versions(v1, v2) + if cmp >= 0: + fail("Expected {} < {}, but got cmp = {}".format(v1, v2, cmp)) + + cmp_rev = compare_maven_versions(v2, v1) + if cmp_rev <= 0: + fail("Expected {} > {}, but got cmp = {}".format(v2, v1, cmp_rev)) + +def _check_versions_equal(v1, v2): + """Check that v1 == v2.""" + cmp = compare_maven_versions(v1, v2) + if cmp != 0: + fail("Expected {} == {}, but got cmp = {}".format(v1, v2, cmp)) + + cmp_rev = compare_maven_versions(v2, v1) + if cmp_rev != 0: + fail("Expected {} == {}, but got cmp = {}".format(v2, v1, cmp_rev)) + +def _check_versions_array_order(versions): + """Check that versions are in ascending order.""" + for i in range(1, len(versions)): + for j in range(i, len(versions)): + _check_versions_order(versions[i - 1], versions[j]) + +# Test version arrays from Java tests +_VERSIONS_QUALIFIER = [ + "1-alpha2snapshot", + "1-alpha2", + "1-alpha-123", + "1-beta-2", + "1-beta123", + "1-m2", + "1-m11", + "1-rc", + "1-cr2", + "1-rc123", + "1-SNAPSHOT", + "1", + "1-sp", + "1-sp2", + "1-sp123", + "1-abc", + "1-def", + "1-pom-1", + "1-1-snapshot", + "1-1", + "1-2", + "1-123", +] + +_VERSIONS_NUMBER = [ + "2.0", + "2.0.a", + "2-1", + "2.0.2", + "2.0.123", + "2.1.0", + "2.1-a", + "2.1b", + "2.1-c", + "2.1-1", + "2.1.0.1", + "2.2", + "2.123", + "11.a2", + "11.a11", + "11.b2", + "11.b11", + "11.m2", + "11.m11", + "11", + "11.a", + "11b", + "11c", + "11m", +] + +def _versions_qualifier_impl(ctx): + env = unittest.begin(ctx) + _check_versions_array_order(_VERSIONS_QUALIFIER) + return unittest.end(env) + +versions_qualifier_test = unittest.make(_versions_qualifier_impl) + +def _versions_number_impl(ctx): + env = unittest.begin(ctx) + _check_versions_array_order(_VERSIONS_NUMBER) + return unittest.end(env) + +versions_number_test = unittest.make(_versions_number_impl) + +def _versions_equal_impl(ctx): + env = unittest.begin(ctx) + + _check_versions_equal("1", "1") + _check_versions_equal("1", "1.0") + _check_versions_equal("1", "1.0.0") + _check_versions_equal("1.0", "1.0.0") + _check_versions_equal("1", "1-0") + _check_versions_equal("1", "1.0-0") + _check_versions_equal("1.0", "1.0-0") + + # no separator between number and character + _check_versions_equal("1a", "1-a") + _check_versions_equal("1a", "1.0-a") + _check_versions_equal("1a", "1.0.0-a") + _check_versions_equal("1.0a", "1-a") + _check_versions_equal("1.0.0a", "1-a") + _check_versions_equal("1x", "1-x") + _check_versions_equal("1x", "1.0-x") + _check_versions_equal("1x", "1.0.0-x") + _check_versions_equal("1.0x", "1-x") + _check_versions_equal("1.0.0x", "1-x") + + # cr = rc + _check_versions_equal("1cr", "1rc") + + # special "aliases" a, b and m for alpha, beta and milestone + _check_versions_equal("1a1", "1-alpha-1") + _check_versions_equal("1b2", "1-beta-2") + _check_versions_equal("1m3", "1-milestone-3") + + # case insensitive + _check_versions_equal("1X", "1x") + _check_versions_equal("1A", "1a") + _check_versions_equal("1B", "1b") + _check_versions_equal("1M", "1m") + _check_versions_equal("1Cr", "1Rc") + _check_versions_equal("1cR", "1rC") + _check_versions_equal("1m3", "1Milestone3") + _check_versions_equal("1m3", "1MileStone3") + _check_versions_equal("1m3", "1MILESTONE3") + + return unittest.end(env) + +versions_equal_test = unittest.make(_versions_equal_impl) + +def _versions_have_same_order_impl(ctx): + env = unittest.begin(ctx) + + # These have same order (compare to 0) but may not be equal + asserts.equals(env, 0, compare_maven_versions("1ga", "1")) + asserts.equals(env, 0, compare_maven_versions("1release", "1")) + asserts.equals(env, 0, compare_maven_versions("1final", "1")) + asserts.equals(env, 0, compare_maven_versions("1Ga", "1")) + asserts.equals(env, 0, compare_maven_versions("1GA", "1")) + asserts.equals(env, 0, compare_maven_versions("1RELEASE", "1")) + asserts.equals(env, 0, compare_maven_versions("1release", "1")) + asserts.equals(env, 0, compare_maven_versions("1RELeaSE", "1")) + asserts.equals(env, 0, compare_maven_versions("1Final", "1")) + asserts.equals(env, 0, compare_maven_versions("1FinaL", "1")) + asserts.equals(env, 0, compare_maven_versions("1FINAL", "1")) + + return unittest.end(env) + +versions_have_same_order_test = unittest.make(_versions_have_same_order_impl) + +def _version_comparing_impl(ctx): + env = unittest.begin(ctx) + + _check_versions_order("1", "2") + _check_versions_order("1.5", "2") + _check_versions_order("1", "2.5") + _check_versions_order("1.0", "1.1") + _check_versions_order("1.1", "1.2") + _check_versions_order("1.0.0", "1.1") + _check_versions_order("1.0.1", "1.1") + _check_versions_order("1.1", "1.2.0") + + _check_versions_order("1.0-alpha-1", "1.0") + _check_versions_order("1.0-alpha-1", "1.0-alpha-2") + _check_versions_order("1.0-alpha-1", "1.0-beta-1") + + _check_versions_order("1.0-beta-1", "1.0-SNAPSHOT") + _check_versions_order("1.0-SNAPSHOT", "1.0") + _check_versions_order("1.0-alpha-1-SNAPSHOT", "1.0-alpha-1") + + _check_versions_order("1.0", "1.0-1") + _check_versions_order("1.0-1", "1.0-2") + _check_versions_order("1.0.0", "1.0-1") + + _check_versions_order("2.0-1", "2.0.1") + _check_versions_order("2.0.1-klm", "2.0.1-lmn") + _check_versions_order("2.0.1", "2.0.1-xyz") + _check_versions_order("2.0.1", "2.0.1-123") + _check_versions_order("2.0.1-xyz", "2.0.1-123") + + return unittest.end(env) + +version_comparing_test = unittest.make(_version_comparing_impl) + +def _leading_zeroes_impl(ctx): + env = unittest.begin(ctx) + _check_versions_order("0.7", "2") + _check_versions_order("0.2", "1.0.7") + return unittest.end(env) + +leading_zeroes_test = unittest.make(_leading_zeroes_impl) + +def _mng5568_impl(ctx): + """Test MNG-5568 edge case for transitive consistency.""" + env = unittest.begin(ctx) + + a = "6.1.0" + b = "6.1.0rc3" + c = "6.1H.5-beta" # unusual version string, with 'H' in the middle + + _check_versions_order(b, a) # classical + _check_versions_order(b, c) # now b < c + _check_versions_order(a, c) + + return unittest.end(env) + +mng5568_test = unittest.make(_mng5568_impl) + +def _mng6572_impl(ctx): + """Test MNG-6572 optimization with large numbers.""" + env = unittest.begin(ctx) + + a = "20190126.230843" # resembles a SNAPSHOT + b = "1234567890.12345" # 10 digit number + c = "123456789012345.1H.5-beta" # 15 digit number + d = "12345678901234567890.1H.5-beta" # 20 digit number + + _check_versions_order(a, b) + _check_versions_order(b, c) + _check_versions_order(a, c) + _check_versions_order(c, d) + _check_versions_order(b, d) + _check_versions_order(a, d) + + return unittest.end(env) + +mng6572_test = unittest.make(_mng6572_impl) + +def _version_equal_with_leading_zeroes_impl(ctx): + """Test all versions are equal when starting with leading zeroes.""" + env = unittest.begin(ctx) + + versions = [ + "0000000000000000001", + "000000000000000001", + "00000000000000001", + "0000000000000001", + "000000000000001", + "00000000000001", + "0000000000001", + "000000000001", + "00000000001", + "0000000001", + "000000001", + "00000001", + "0000001", + "000001", + "00001", + "0001", + "001", + "01", + "1", + ] + + for i in range(len(versions)): + for j in range(i, len(versions)): + _check_versions_equal(versions[i], versions[j]) + + return unittest.end(env) + +version_equal_with_leading_zeroes_test = unittest.make(_version_equal_with_leading_zeroes_impl) + +def _version_zero_equal_with_leading_zeroes_impl(ctx): + """Test all '0' versions are equal with leading zeroes.""" + env = unittest.begin(ctx) + + versions = [ + "0000000000000000000", + "000000000000000000", + "00000000000000000", + "0000000000000000", + "000000000000000", + "00000000000000", + "0000000000000", + "000000000000", + "00000000000", + "0000000000", + "000000000", + "00000000", + "0000000", + "000000", + "00000", + "0000", + "000", + "00", + "0", + ] + + for i in range(len(versions)): + for j in range(i, len(versions)): + _check_versions_equal(versions[i], versions[j]) + + return unittest.end(env) + +version_zero_equal_with_leading_zeroes_test = unittest.make(_version_zero_equal_with_leading_zeroes_impl) + +def _mng6964_impl(ctx): + """Test MNG-6964 edge cases for qualifiers starting with '-0.'.""" + env = unittest.begin(ctx) + + a = "1-0.alpha" + b = "1-0.beta" + c = "1" + + _check_versions_order(a, c) # a < c + _check_versions_order(b, c) # b < c + _check_versions_order(a, b) # a < b + + return unittest.end(env) + +mng6964_test = unittest.make(_mng6964_impl) + +def _locale_independent_impl(ctx): + """Test case insensitivity works for all letters.""" + env = unittest.begin(ctx) + _check_versions_equal("1-abcdefghijklmnopqrstuvwxyz", "1-ABCDEFGHIJKLMNOPQRSTUVWXYZ") + return unittest.end(env) + +locale_independent_test = unittest.make(_locale_independent_impl) + +def _mng7644_impl(ctx): + """Test MNG-7644: 1.0.0.X1 < 1.0.0-X2 for any string X.""" + env = unittest.begin(ctx) + + for x in ["abc", "alpha", "a", "beta", "b", "def", "milestone", "m", "RC"]: + # 1.0.0.X1 < 1.0.0-X2 for any string x + _check_versions_order("1.0.0." + x + "1", "1.0.0-" + x + "2") + + # 2.0.X == 2-X == 2.0.0.X for any string x + _check_versions_equal("2-" + x, "2.0." + x) + _check_versions_equal("2-" + x, "2.0.0." + x) + _check_versions_equal("2.0." + x, "2.0.0." + x) + + return unittest.end(env) + +mng7644_test = unittest.make(_mng7644_impl) + +def _mng7714_impl(ctx): + """Test MNG-7714: sp qualifier ordering with redhat suffix.""" + env = unittest.begin(ctx) + + f = "1.0.final-redhat" + sp1 = "1.0-sp1-redhat" + sp2 = "1.0-sp-1-redhat" + sp3 = "1.0-sp.1-redhat" + + _check_versions_order(f, sp1) + _check_versions_order(f, sp2) + _check_versions_order(f, sp3) + + return unittest.end(env) + +mng7714_test = unittest.make(_mng7714_impl) + +def _helper_functions_impl(ctx): + """Test helper functions like max_version, min_version, sort_versions.""" + env = unittest.begin(ctx) + + versions = ["1.0", "2.0", "1.5", "1.0-alpha", "2.0-SNAPSHOT"] + + asserts.equals(env, "2.0", max_version(versions)) + asserts.equals(env, "1.0-alpha", min_version(versions)) + + sorted_versions = sort_versions(versions) + asserts.equals(env, ["1.0-alpha", "1.0", "1.5", "2.0-SNAPSHOT", "2.0"], sorted_versions) + + sorted_desc = sort_versions(versions, reverse = True) + asserts.equals(env, ["2.0", "2.0-SNAPSHOT", "1.5", "1.0", "1.0-alpha"], sorted_desc) + + asserts.true(env, is_version_greater("2.0", "1.0")) + asserts.true(env, is_version_less("1.0", "2.0")) + asserts.true(env, is_version_equal("1.0", "1.0.0")) + + return unittest.end(env) + +helper_functions_test = unittest.make(_helper_functions_impl) + +def _compare_digit_to_letter_impl(ctx): + """Test that digits are greater than letters.""" + env = unittest.begin(ctx) + + asserts.true(env, compare_maven_versions("7", "J") > 0) + asserts.true(env, compare_maven_versions("J", "7") < 0) + asserts.true(env, compare_maven_versions("7", "c") > 0) + asserts.true(env, compare_maven_versions("c", "7") < 0) + + return unittest.end(env) + +compare_digit_to_letter_test = unittest.make(_compare_digit_to_letter_impl) + +def _lexicographic_order_impl(ctx): + """Test lexicographic ordering of unknown qualifiers.""" + env = unittest.begin(ctx) + + asserts.true(env, compare_maven_versions("zebra", "aardvark") > 0) + asserts.true(env, compare_maven_versions("aardvark", "zebra") < 0) + + return unittest.end(env) + +lexicographic_order_test = unittest.make(_lexicographic_order_impl) + +def _case_insensitive_impl(ctx): + """Test case insensitivity.""" + env = unittest.begin(ctx) + + asserts.equals(env, 0, compare_maven_versions("1.0.0-ALPHA1", "1.0.0-alpha1")) + asserts.equals(env, 0, compare_maven_versions("1.0.0-alpha1", "1.0.0-ALPHA1")) + asserts.equals(env, 0, compare_maven_versions("1.A", "1.a")) + asserts.equals(env, 0, compare_maven_versions("1.a", "1.A")) + + return unittest.end(env) + +case_insensitive_test = unittest.make(_case_insensitive_impl) + +def _get_canonical_impl(ctx): + """Test canonical form generation.""" + env = unittest.begin(ctx) + + # Basic normalization - trailing zeros removed + asserts.equals(env, "1", get_canonical("1.0.0")) + asserts.equals(env, "1", get_canonical("1.0")) + asserts.equals(env, "1", get_canonical("1")) + + # Qualifiers normalized - note: a/b/m only expand to alpha/beta/milestone + # when followed by a digit (like "a1" -> "alpha1") + asserts.equals(env, "1-alpha", get_canonical("1.0-alpha")) + asserts.equals(env, "1-a", get_canonical("1-a")) # standalone 'a' stays as 'a' + asserts.equals(env, "1-b", get_canonical("1-b")) # standalone 'b' stays as 'b' + asserts.equals(env, "1-m", get_canonical("1-m")) # standalone 'm' stays as 'm' + asserts.equals(env, "1-rc", get_canonical("1-cr")) # cr -> rc alias always applies + + # Aliases expand when followed by digit + asserts.equals(env, "1-alpha1", get_canonical("1-a1")) + asserts.equals(env, "1-beta2", get_canonical("1-b2")) + asserts.equals(env, "1-milestone3", get_canonical("1-m3")) + + # Case normalized to lowercase + asserts.equals(env, "1-alpha", get_canonical("1-ALPHA")) + asserts.equals(env, "1-beta", get_canonical("1-BETA")) + + # MNG-7700 test cases + asserts.equals(env, "x", get_canonical("0.x")) + asserts.equals(env, "x", get_canonical("0-x")) + asserts.equals(env, "rc", get_canonical("0.rc")) + asserts.equals(env, "0-1", get_canonical("0-1")) + asserts.equals(env, "0.2", get_canonical("0.2")) + + # Canonical of canonical should equal itself + for v in ["1.0-alpha", "2.3.4", "1-SNAPSHOT", "1.0.0-RC1"]: + canonical = get_canonical(v) + asserts.equals(env, canonical, get_canonical(canonical)) + + return unittest.end(env) + +get_canonical_test = unittest.make(_get_canonical_impl) + +def maven_version_test_suite(): + unittest.suite( + "maven_version_tests", + versions_qualifier_test, + versions_number_test, + versions_equal_test, + versions_have_same_order_test, + version_comparing_test, + leading_zeroes_test, + mng5568_test, + mng6572_test, + version_equal_with_leading_zeroes_test, + version_zero_equal_with_leading_zeroes_test, + mng6964_test, + locale_independent_test, + mng7644_test, + mng7714_test, + helper_functions_test, + compare_digit_to_letter_test, + lexicographic_order_test, + case_insensitive_test, + get_canonical_test, + )