diff --git a/MODULE.bazel b/MODULE.bazel index 22e50a94d..0c9502e20 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1076,6 +1076,18 @@ dev_maven.install( resolver = "coursier", ) +dev_maven.install( + name = "coursier_pinned_layering", + artifacts = [ + "com.google.code.findbugs:jsr305:3.0.1", + ], + known_contributing_modules = [ + "coursier_higher_layer", + ], + resolver = "coursier", + version_conflict_policy = "pinned", +) + bazel_dep(name = "coursier_higher_layer", version = "0.0.0", dev_dependency = True) local_path_override( module_name = "coursier_higher_layer", @@ -1117,6 +1129,7 @@ use_repo( "amend_artifacts", "bom_only_pinning", "coursier_layering", + "coursier_pinned_layering", "duplicate_version_error", "duplicate_version_warning", "duplicate_version_warning_same_version", diff --git a/docs/bzlmod-api.md b/docs/bzlmod-api.md index 38e747474..0a29f542f 100644 --- a/docs/bzlmod-api.md +++ b/docs/bzlmod-api.md @@ -114,7 +114,7 @@ Combines artifact and bom declarations with setting the location of lock files t | strict_visibility_value | - | List of labels | optional | `["@rules_jvm_external//visibility:private"]` | | use_credentials_from_home_netrc_file | Whether to pass machine login credentials from the ~/.netrc file to coursier. | Boolean | optional | `False` | | use_starlark_android_rules | Whether to use the native or Starlark version of the Android rules. | Boolean | optional | `False` | -| version_conflict_policy | Policy for user-defined vs. transitive dependency version conflicts

If "pinned", choose the user-specified version in maven_install unconditionally. With the Gradle and Maven resolvers, this only applies to artifacts contributed by the root module. If "default", follow the selected resolver's default policy. | String | optional | `"default"` | +| version_conflict_policy | Policy for user-defined vs. transitive dependency version conflicts

If "pinned", choose the user-specified version in maven_install unconditionally. In layered installs, artifacts contributed by the root module take precedence over versions contributed by other modules. If "default", follow the selected resolver's default policy. | String | optional | `"default"` | diff --git a/docs/bzlmod.md b/docs/bzlmod.md index edce4bcee..e1b2d51e6 100644 --- a/docs/bzlmod.md +++ b/docs/bzlmod.md @@ -216,16 +216,16 @@ plain versioned coordinate. This may lead to some surprises when resolution is c "Highest" uses the Maven `ComparableVersion` ordering implemented by `private/rules/maven_version.bzl`, not lexical string ordering. -`version_conflict_policy = "pinned"` changes this interaction. For the Gradle and Maven resolvers, -root artifacts are marked as `force_version` before layering. The duplicate-force check applies to -declared forces before this policy is applied. Maven then marks every versioned root declaration. -Gradle first selects one version for each root `group:artifact`: an unclassified declaration takes -precedence over classified declarations, and Maven `ComparableVersion` order selects among -declarations with the same classification status. Every root declaration for that module at the -selected version is then marked forced, including classified declarations. The root consequently -wins because it now forces the coordinate. For Coursier, layering is unchanged and the one -surviving direct version is later passed as a `--force-version` argument. A higher non-root -version can therefore displace the root under Coursier and then be pinned. +`version_conflict_policy = "pinned"` changes this interaction. Root artifacts are marked as +`force_version` before layering, whatever the resolver. The duplicate-force check applies to +declared forces before this policy is applied. Maven and Coursier mark every versioned root +declaration. Gradle first selects one version for each root `group:artifact`: an unclassified +declaration takes precedence over classified declarations, and Maven `ComparableVersion` order +selects among declarations with the same classification status. Every root declaration for that +module at the selected version is then marked forced, including classified declarations. The +root consequently wins because it now forces the coordinate. Coursier additionally passes every +direct artifact's version as a `--force-version` argument, so transitive dependencies cannot +displace the pinned version either. The `force_version` flag can be set by an `artifact` tag, an `amend_artifact` tag, or a regular artifact read by `from_toml`. Coordinates in `install.artifacts` cannot carry the flag. BOMs use the diff --git a/examples/android_local_test/maven_install.json b/examples/android_local_test/maven_install.json index 2734089ce..0115c75bd 100644 --- a/examples/android_local_test/maven_install.json +++ b/examples/android_local_test/maven_install.json @@ -1,24 +1,24 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", "__INPUT_ARTIFACTS_HASH": { - "androidx.annotation:annotation": 588494489, - "androidx.appcompat:appcompat": -864916242, - "androidx.emoji2:emoji2": -667316066, - "androidx.emoji2:emoji2-views-helper": -799295894, - "androidx.lifecycle:lifecycle-common": 965287242, - "androidx.lifecycle:lifecycle-livedata-core": -1534752455, - "androidx.lifecycle:lifecycle-runtime": -1133978085, - "androidx.lifecycle:lifecycle-viewmodel-savedstate": -1098900718, - "androidx.savedstate:savedstate": -833725607, - "androidx.test.ext:junit": 923457984, + "androidx.annotation:annotation": 1998854284, + "androidx.appcompat:appcompat": 526874845, + "androidx.emoji2:emoji2": 1270171825, + "androidx.emoji2:emoji2-views-helper": -985278619, + "androidx.lifecycle:lifecycle-common": -84119015, + "androidx.lifecycle:lifecycle-livedata-core": 993085898, + "androidx.lifecycle:lifecycle-runtime": 1906619816, + "androidx.lifecycle:lifecycle-viewmodel-savedstate": -577646767, + "androidx.savedstate:savedstate": -671028048, + "androidx.test.ext:junit": 199011195, "com.google.code.findbugs:jsr305": 495355163, - "com.google.code.gson:gson": 2063056812, - "com.google.errorprone:error_prone_annotations": 1790047777, - "com.google.guava:guava": -249069712, - "com.google.j2objc:j2objc-annotations": -26400317, - "junit:junit": -744267592, - "org.assertj:assertj-core": -1145412507, - "org.robolectric:robolectric": -1784339344, + "com.google.code.gson:gson": 579810017, + "com.google.errorprone:error_prone_annotations": 1828684942, + "com.google.guava:guava": 1001533771, + "com.google.j2objc:j2objc-annotations": -57684360, + "junit:junit": 1633899997, + "org.assertj:assertj-core": -88843862, + "org.robolectric:robolectric": 1761992425, "repositories": 1670674627 }, "__RESOLVED_ARTIFACTS_HASH": { @@ -1140,13 +1140,40 @@ ] }, "packages": { + "androidx.activity:activity:aar": [ + "androidx.activity", + "androidx.activity.contextaware", + "androidx.activity.result", + "androidx.activity.result.contract" + ], + "androidx.annotation:annotation-experimental:aar": [ + "androidx.annotation", + "androidx.annotation.experimental" + ], "androidx.annotation:annotation-jvm": [ "androidx.annotation" ], + "androidx.appcompat:appcompat-resources:aar": [ + "androidx.appcompat.content.res", + "androidx.appcompat.graphics.drawable", + "androidx.appcompat.resources", + "androidx.appcompat.widget" + ], + "androidx.appcompat:appcompat:aar": [ + "androidx.appcompat.app", + "androidx.appcompat.graphics.drawable", + "androidx.appcompat.text", + "androidx.appcompat.view", + "androidx.appcompat.view.menu", + "androidx.appcompat.widget" + ], "androidx.arch.core:core-common": [ "androidx.arch.core.internal", "androidx.arch.core.util" ], + "androidx.arch.core:core-runtime:aar": [ + "androidx.arch.core.executor" + ], "androidx.collection:collection-jvm": [ "androidx.collection", "androidx.collection.internal" @@ -1157,12 +1184,196 @@ "androidx.concurrent:concurrent-futures-ktx": [ "androidx.concurrent.futures" ], + "androidx.core:core-ktx:aar": [ + "androidx.core.animation", + "androidx.core.content", + "androidx.core.content.res", + "androidx.core.database", + "androidx.core.database.sqlite", + "androidx.core.graphics", + "androidx.core.graphics.drawable", + "androidx.core.location", + "androidx.core.net", + "androidx.core.os", + "androidx.core.text", + "androidx.core.transition", + "androidx.core.util", + "androidx.core.view", + "androidx.core.widget" + ], + "androidx.core:core-viewtree:aar": [ + "androidx.core.viewtree" + ], + "androidx.core:core:aar": [ + "android.support.v4.app", + "android.support.v4.graphics.drawable", + "android.support.v4.os", + "androidx.core.accessibilityservice", + "androidx.core.app", + "androidx.core.app.unusedapprestrictions", + "androidx.core.content", + "androidx.core.content.pm", + "androidx.core.content.res", + "androidx.core.database", + "androidx.core.database.sqlite", + "androidx.core.graphics", + "androidx.core.graphics.drawable", + "androidx.core.hardware.display", + "androidx.core.hardware.fingerprint", + "androidx.core.internal.view", + "androidx.core.location", + "androidx.core.math", + "androidx.core.net", + "androidx.core.os", + "androidx.core.provider", + "androidx.core.service.quicksettings", + "androidx.core.telephony", + "androidx.core.telephony.mbms", + "androidx.core.text", + "androidx.core.text.method", + "androidx.core.text.util", + "androidx.core.util", + "androidx.core.view", + "androidx.core.view.accessibility", + "androidx.core.view.animation", + "androidx.core.view.autofill", + "androidx.core.view.contentcapture", + "androidx.core.view.inputmethod", + "androidx.core.widget" + ], + "androidx.cursoradapter:cursoradapter:aar": [ + "androidx.cursoradapter.widget" + ], + "androidx.customview:customview:aar": [ + "androidx.customview.view", + "androidx.customview.widget" + ], + "androidx.drawerlayout:drawerlayout:aar": [ + "androidx.drawerlayout.widget" + ], + "androidx.emoji2:emoji2-views-helper:aar": [ + "androidx.emoji2.viewsintegration" + ], + "androidx.emoji2:emoji2:aar": [ + "androidx.emoji2.text", + "androidx.emoji2.text.flatbuffer" + ], + "androidx.fragment:fragment:aar": [ + "androidx.fragment.app", + "androidx.fragment.app.strictmode" + ], + "androidx.interpolator:interpolator:aar": [ + "androidx.interpolator.view.animation" + ], "androidx.lifecycle:lifecycle-common-jvm": [ "androidx.lifecycle" ], + "androidx.lifecycle:lifecycle-livedata-core:aar": [ + "androidx.lifecycle" + ], + "androidx.lifecycle:lifecycle-livedata:aar": [ + "androidx.lifecycle" + ], + "androidx.lifecycle:lifecycle-process:aar": [ + "androidx.lifecycle" + ], + "androidx.lifecycle:lifecycle-runtime-android:aar": [ + "androidx.lifecycle" + ], + "androidx.lifecycle:lifecycle-viewmodel-android:aar": [ + "androidx.lifecycle", + "androidx.lifecycle.viewmodel", + "androidx.lifecycle.viewmodel.internal" + ], + "androidx.lifecycle:lifecycle-viewmodel-savedstate-android:aar": [ + "androidx.lifecycle", + "androidx.lifecycle.internal", + "androidx.lifecycle.serialization" + ], + "androidx.loader:loader:aar": [ + "androidx.loader.app", + "androidx.loader.content" + ], + "androidx.profileinstaller:profileinstaller:aar": [ + "androidx.profileinstaller" + ], "androidx.resourceinspection:resourceinspection-annotation": [ "androidx.resourceinspection.annotation" ], + "androidx.savedstate:savedstate-android:aar": [ + "androidx.savedstate", + "androidx.savedstate.internal", + "androidx.savedstate.serialization", + "androidx.savedstate.serialization.serializers" + ], + "androidx.startup:startup-runtime:aar": [ + "androidx.startup" + ], + "androidx.test.espresso:espresso-idling-resource:aar": [ + "androidx.test.espresso", + "androidx.test.espresso.idling" + ], + "androidx.test.ext:junit:aar": [ + "androidx.test.ext.junit.rules", + "androidx.test.ext.junit.runners" + ], + "androidx.test.services:storage:aar": [ + "androidx.test.services.storage", + "androidx.test.services.storage.file", + "androidx.test.services.storage.internal" + ], + "androidx.test:core:aar": [ + "androidx.test.core.app", + "androidx.test.core.content.pm", + "androidx.test.core.graphics", + "androidx.test.core.internal.os", + "androidx.test.core.os", + "androidx.test.core.view" + ], + "androidx.test:monitor:aar": [ + "androidx.test", + "androidx.test.annotation", + "androidx.test.internal.platform", + "androidx.test.internal.platform.app", + "androidx.test.internal.platform.content", + "androidx.test.internal.platform.os", + "androidx.test.internal.platform.reflect", + "androidx.test.internal.platform.util", + "androidx.test.internal.runner", + "androidx.test.internal.runner.hidden", + "androidx.test.internal.runner.intent", + "androidx.test.internal.runner.intercepting", + "androidx.test.internal.runner.lifecycle", + "androidx.test.internal.util", + "androidx.test.platform", + "androidx.test.platform.app", + "androidx.test.platform.concurrent", + "androidx.test.platform.device", + "androidx.test.platform.graphics", + "androidx.test.platform.io", + "androidx.test.platform.tracing", + "androidx.test.platform.ui", + "androidx.test.platform.view.inspector", + "androidx.test.runner", + "androidx.test.runner.intent", + "androidx.test.runner.intercepting", + "androidx.test.runner.lifecycle" + ], + "androidx.tracing:tracing:aar": [ + "androidx.tracing" + ], + "androidx.vectordrawable:vectordrawable-animated:aar": [ + "androidx.vectordrawable.graphics.drawable" + ], + "androidx.vectordrawable:vectordrawable:aar": [ + "androidx.vectordrawable.graphics.drawable" + ], + "androidx.versionedparcelable:versionedparcelable:aar": [ + "androidx.versionedparcelable" + ], + "androidx.viewpager:viewpager:aar": [ + "androidx.viewpager.widget" + ], "com.almworks.sqlite4java:sqlite4java": [ "com.almworks.sqlite4java", "javolution.util.stripped" diff --git a/examples/kt_android_local_test/maven_install.json b/examples/kt_android_local_test/maven_install.json index 2734089ce..0115c75bd 100644 --- a/examples/kt_android_local_test/maven_install.json +++ b/examples/kt_android_local_test/maven_install.json @@ -1,24 +1,24 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", "__INPUT_ARTIFACTS_HASH": { - "androidx.annotation:annotation": 588494489, - "androidx.appcompat:appcompat": -864916242, - "androidx.emoji2:emoji2": -667316066, - "androidx.emoji2:emoji2-views-helper": -799295894, - "androidx.lifecycle:lifecycle-common": 965287242, - "androidx.lifecycle:lifecycle-livedata-core": -1534752455, - "androidx.lifecycle:lifecycle-runtime": -1133978085, - "androidx.lifecycle:lifecycle-viewmodel-savedstate": -1098900718, - "androidx.savedstate:savedstate": -833725607, - "androidx.test.ext:junit": 923457984, + "androidx.annotation:annotation": 1998854284, + "androidx.appcompat:appcompat": 526874845, + "androidx.emoji2:emoji2": 1270171825, + "androidx.emoji2:emoji2-views-helper": -985278619, + "androidx.lifecycle:lifecycle-common": -84119015, + "androidx.lifecycle:lifecycle-livedata-core": 993085898, + "androidx.lifecycle:lifecycle-runtime": 1906619816, + "androidx.lifecycle:lifecycle-viewmodel-savedstate": -577646767, + "androidx.savedstate:savedstate": -671028048, + "androidx.test.ext:junit": 199011195, "com.google.code.findbugs:jsr305": 495355163, - "com.google.code.gson:gson": 2063056812, - "com.google.errorprone:error_prone_annotations": 1790047777, - "com.google.guava:guava": -249069712, - "com.google.j2objc:j2objc-annotations": -26400317, - "junit:junit": -744267592, - "org.assertj:assertj-core": -1145412507, - "org.robolectric:robolectric": -1784339344, + "com.google.code.gson:gson": 579810017, + "com.google.errorprone:error_prone_annotations": 1828684942, + "com.google.guava:guava": 1001533771, + "com.google.j2objc:j2objc-annotations": -57684360, + "junit:junit": 1633899997, + "org.assertj:assertj-core": -88843862, + "org.robolectric:robolectric": 1761992425, "repositories": 1670674627 }, "__RESOLVED_ARTIFACTS_HASH": { @@ -1140,13 +1140,40 @@ ] }, "packages": { + "androidx.activity:activity:aar": [ + "androidx.activity", + "androidx.activity.contextaware", + "androidx.activity.result", + "androidx.activity.result.contract" + ], + "androidx.annotation:annotation-experimental:aar": [ + "androidx.annotation", + "androidx.annotation.experimental" + ], "androidx.annotation:annotation-jvm": [ "androidx.annotation" ], + "androidx.appcompat:appcompat-resources:aar": [ + "androidx.appcompat.content.res", + "androidx.appcompat.graphics.drawable", + "androidx.appcompat.resources", + "androidx.appcompat.widget" + ], + "androidx.appcompat:appcompat:aar": [ + "androidx.appcompat.app", + "androidx.appcompat.graphics.drawable", + "androidx.appcompat.text", + "androidx.appcompat.view", + "androidx.appcompat.view.menu", + "androidx.appcompat.widget" + ], "androidx.arch.core:core-common": [ "androidx.arch.core.internal", "androidx.arch.core.util" ], + "androidx.arch.core:core-runtime:aar": [ + "androidx.arch.core.executor" + ], "androidx.collection:collection-jvm": [ "androidx.collection", "androidx.collection.internal" @@ -1157,12 +1184,196 @@ "androidx.concurrent:concurrent-futures-ktx": [ "androidx.concurrent.futures" ], + "androidx.core:core-ktx:aar": [ + "androidx.core.animation", + "androidx.core.content", + "androidx.core.content.res", + "androidx.core.database", + "androidx.core.database.sqlite", + "androidx.core.graphics", + "androidx.core.graphics.drawable", + "androidx.core.location", + "androidx.core.net", + "androidx.core.os", + "androidx.core.text", + "androidx.core.transition", + "androidx.core.util", + "androidx.core.view", + "androidx.core.widget" + ], + "androidx.core:core-viewtree:aar": [ + "androidx.core.viewtree" + ], + "androidx.core:core:aar": [ + "android.support.v4.app", + "android.support.v4.graphics.drawable", + "android.support.v4.os", + "androidx.core.accessibilityservice", + "androidx.core.app", + "androidx.core.app.unusedapprestrictions", + "androidx.core.content", + "androidx.core.content.pm", + "androidx.core.content.res", + "androidx.core.database", + "androidx.core.database.sqlite", + "androidx.core.graphics", + "androidx.core.graphics.drawable", + "androidx.core.hardware.display", + "androidx.core.hardware.fingerprint", + "androidx.core.internal.view", + "androidx.core.location", + "androidx.core.math", + "androidx.core.net", + "androidx.core.os", + "androidx.core.provider", + "androidx.core.service.quicksettings", + "androidx.core.telephony", + "androidx.core.telephony.mbms", + "androidx.core.text", + "androidx.core.text.method", + "androidx.core.text.util", + "androidx.core.util", + "androidx.core.view", + "androidx.core.view.accessibility", + "androidx.core.view.animation", + "androidx.core.view.autofill", + "androidx.core.view.contentcapture", + "androidx.core.view.inputmethod", + "androidx.core.widget" + ], + "androidx.cursoradapter:cursoradapter:aar": [ + "androidx.cursoradapter.widget" + ], + "androidx.customview:customview:aar": [ + "androidx.customview.view", + "androidx.customview.widget" + ], + "androidx.drawerlayout:drawerlayout:aar": [ + "androidx.drawerlayout.widget" + ], + "androidx.emoji2:emoji2-views-helper:aar": [ + "androidx.emoji2.viewsintegration" + ], + "androidx.emoji2:emoji2:aar": [ + "androidx.emoji2.text", + "androidx.emoji2.text.flatbuffer" + ], + "androidx.fragment:fragment:aar": [ + "androidx.fragment.app", + "androidx.fragment.app.strictmode" + ], + "androidx.interpolator:interpolator:aar": [ + "androidx.interpolator.view.animation" + ], "androidx.lifecycle:lifecycle-common-jvm": [ "androidx.lifecycle" ], + "androidx.lifecycle:lifecycle-livedata-core:aar": [ + "androidx.lifecycle" + ], + "androidx.lifecycle:lifecycle-livedata:aar": [ + "androidx.lifecycle" + ], + "androidx.lifecycle:lifecycle-process:aar": [ + "androidx.lifecycle" + ], + "androidx.lifecycle:lifecycle-runtime-android:aar": [ + "androidx.lifecycle" + ], + "androidx.lifecycle:lifecycle-viewmodel-android:aar": [ + "androidx.lifecycle", + "androidx.lifecycle.viewmodel", + "androidx.lifecycle.viewmodel.internal" + ], + "androidx.lifecycle:lifecycle-viewmodel-savedstate-android:aar": [ + "androidx.lifecycle", + "androidx.lifecycle.internal", + "androidx.lifecycle.serialization" + ], + "androidx.loader:loader:aar": [ + "androidx.loader.app", + "androidx.loader.content" + ], + "androidx.profileinstaller:profileinstaller:aar": [ + "androidx.profileinstaller" + ], "androidx.resourceinspection:resourceinspection-annotation": [ "androidx.resourceinspection.annotation" ], + "androidx.savedstate:savedstate-android:aar": [ + "androidx.savedstate", + "androidx.savedstate.internal", + "androidx.savedstate.serialization", + "androidx.savedstate.serialization.serializers" + ], + "androidx.startup:startup-runtime:aar": [ + "androidx.startup" + ], + "androidx.test.espresso:espresso-idling-resource:aar": [ + "androidx.test.espresso", + "androidx.test.espresso.idling" + ], + "androidx.test.ext:junit:aar": [ + "androidx.test.ext.junit.rules", + "androidx.test.ext.junit.runners" + ], + "androidx.test.services:storage:aar": [ + "androidx.test.services.storage", + "androidx.test.services.storage.file", + "androidx.test.services.storage.internal" + ], + "androidx.test:core:aar": [ + "androidx.test.core.app", + "androidx.test.core.content.pm", + "androidx.test.core.graphics", + "androidx.test.core.internal.os", + "androidx.test.core.os", + "androidx.test.core.view" + ], + "androidx.test:monitor:aar": [ + "androidx.test", + "androidx.test.annotation", + "androidx.test.internal.platform", + "androidx.test.internal.platform.app", + "androidx.test.internal.platform.content", + "androidx.test.internal.platform.os", + "androidx.test.internal.platform.reflect", + "androidx.test.internal.platform.util", + "androidx.test.internal.runner", + "androidx.test.internal.runner.hidden", + "androidx.test.internal.runner.intent", + "androidx.test.internal.runner.intercepting", + "androidx.test.internal.runner.lifecycle", + "androidx.test.internal.util", + "androidx.test.platform", + "androidx.test.platform.app", + "androidx.test.platform.concurrent", + "androidx.test.platform.device", + "androidx.test.platform.graphics", + "androidx.test.platform.io", + "androidx.test.platform.tracing", + "androidx.test.platform.ui", + "androidx.test.platform.view.inspector", + "androidx.test.runner", + "androidx.test.runner.intent", + "androidx.test.runner.intercepting", + "androidx.test.runner.lifecycle" + ], + "androidx.tracing:tracing:aar": [ + "androidx.tracing" + ], + "androidx.vectordrawable:vectordrawable-animated:aar": [ + "androidx.vectordrawable.graphics.drawable" + ], + "androidx.vectordrawable:vectordrawable:aar": [ + "androidx.vectordrawable.graphics.drawable" + ], + "androidx.versionedparcelable:versionedparcelable:aar": [ + "androidx.versionedparcelable" + ], + "androidx.viewpager:viewpager:aar": [ + "androidx.viewpager.widget" + ], "com.almworks.sqlite4java:sqlite4java": [ "com.almworks.sqlite4java", "javolution.util.stripped" diff --git a/private/extensions/maven.bzl b/private/extensions/maven.bzl index 503e6c2cc..0c3a5ea8f 100644 --- a/private/extensions/maven.bzl +++ b/private/extensions/maven.bzl @@ -114,7 +114,8 @@ install = tag_class( doc = """Policy for user-defined vs. transitive dependency version conflicts If "pinned", choose the user-specified version in maven_install unconditionally. - With the Gradle and Maven resolvers, this only applies to artifacts contributed by the root module. + In layered installs, artifacts contributed by the root module take precedence + over versions contributed by other modules. If "default", follow the selected resolver's default policy. """, default = "default", diff --git a/private/lib/layering.bzl b/private/lib/layering.bzl index f1c51fb86..5d9b196ef 100644 --- a/private/lib/layering.bzl +++ b/private/lib/layering.bzl @@ -319,7 +319,7 @@ def _forces_gradle_module_version(artifact, forced_versions): def apply_root_version_conflict_policy(artifacts, resolver, version_conflict_policy): """Applies the install-level conflict policy to root module artifacts.""" - if resolver not in ["gradle", "maven"] or version_conflict_policy != "pinned": + if version_conflict_policy != "pinned": return artifacts if resolver == "gradle": diff --git a/tests/custom_maven_install/coursier_resolved_install.json b/tests/custom_maven_install/coursier_resolved_install.json index 5ce89d44b..7954d0843 100644 --- a/tests/custom_maven_install/coursier_resolved_install.json +++ b/tests/custom_maven_install/coursier_resolved_install.json @@ -2,7 +2,7 @@ "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", "__INPUT_ARTIFACTS_HASH": { "com.google.auth:google-auth-library-oauth2-http": 991267597, - "com.google.auto:auto-common": -832702775, + "com.google.auto:auto-common": 605821486, "com.google.cloud:google-cloud-bigquery": 1661989687, "com.google.cloud:libraries-bom": 710163787, "repositories": -1949687017 @@ -882,20 +882,12 @@ "autovalue.shaded.com.google.auto.service", "autovalue.shaded.com.google.common.annotations", "autovalue.shaded.com.google.common.base", - "autovalue.shaded.com.google.common.cache", "autovalue.shaded.com.google.common.collect", - "autovalue.shaded.com.google.common.escape", - "autovalue.shaded.com.google.common.eventbus", - "autovalue.shaded.com.google.common.graph", "autovalue.shaded.com.google.common.hash", - "autovalue.shaded.com.google.common.html", "autovalue.shaded.com.google.common.io", "autovalue.shaded.com.google.common.math", - "autovalue.shaded.com.google.common.net", "autovalue.shaded.com.google.common.primitives", "autovalue.shaded.com.google.common.reflect", - "autovalue.shaded.com.google.common.util.concurrent", - "autovalue.shaded.com.google.common.xml", "autovalue.shaded.com.google.errorprone.annotations", "autovalue.shaded.com.google.errorprone.annotations.concurrent", "autovalue.shaded.com.google.escapevelocity", @@ -1162,8 +1154,6 @@ "io.netty.util.concurrent", "io.netty.util.internal", "io.netty.util.internal.logging", - "io.netty.util.internal.shaded.org.jctools.counters", - "io.netty.util.internal.shaded.org.jctools.maps", "io.netty.util.internal.shaded.org.jctools.queues", "io.netty.util.internal.shaded.org.jctools.queues.atomic", "io.netty.util.internal.shaded.org.jctools.queues.atomic.unpadded", diff --git a/tests/custom_maven_install/policy_pinned_testing_install.json b/tests/custom_maven_install/policy_pinned_testing_install.json index 85cf17fb7..f3a1b139b 100644 --- a/tests/custom_maven_install/policy_pinned_testing_install.json +++ b/tests/custom_maven_install/policy_pinned_testing_install.json @@ -1,7 +1,48 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": 316608203, - "__RESOLVED_ARTIFACTS_HASH": -968606658, + "__INPUT_ARTIFACTS_HASH": { + "com.google.cloud:google-cloud-storage": -1491579406, + "com.google.guava:guava": -1853738824, + "repositories": -1949687017 + }, + "__RESOLVED_ARTIFACTS_HASH": { + "com.fasterxml.jackson.core:jackson-core": -262972278, + "com.google.api-client:google-api-client": -793854737, + "com.google.api.grpc:proto-google-common-protos": -1885550498, + "com.google.api.grpc:proto-google-iam-v1": -453830571, + "com.google.api:api-common": -549936361, + "com.google.api:gax": 805574868, + "com.google.api:gax-httpjson": -1168617842, + "com.google.apis:google-api-services-storage": 158985735, + "com.google.auth:google-auth-library-credentials": 1709815480, + "com.google.auth:google-auth-library-oauth2-http": 735705443, + "com.google.cloud:google-cloud-core": -2024787513, + "com.google.cloud:google-cloud-core-http": -327232662, + "com.google.cloud:google-cloud-storage": -2032350017, + "com.google.code.findbugs:jsr305": 870839855, + "com.google.code.gson:gson": 1862043778, + "com.google.errorprone:error_prone_annotations": 875547987, + "com.google.guava:guava": -164902174, + "com.google.http-client:google-http-client": -294460505, + "com.google.http-client:google-http-client-apache": -921946789, + "com.google.http-client:google-http-client-appengine": -849655450, + "com.google.http-client:google-http-client-jackson2": -1187124445, + "com.google.j2objc:j2objc-annotations": 1702790440, + "com.google.oauth-client:google-oauth-client": 1855107119, + "com.google.protobuf:protobuf-java": 1939119124, + "com.google.protobuf:protobuf-java-util": -944010332, + "commons-codec:commons-codec": -1216058892, + "commons-logging:commons-logging": 1248790901, + "io.grpc:grpc-context": -1228139597, + "io.opencensus:opencensus-api": 1930972101, + "io.opencensus:opencensus-contrib-http-util": -528120454, + "javax.annotation:javax.annotation-api": -1009230154, + "org.apache.httpcomponents:httpclient": 483019193, + "org.apache.httpcomponents:httpcore": 283803218, + "org.checkerframework:checker-compat-qual": -1467964223, + "org.codehaus.mojo:animal-sniffer-annotations": -349140135, + "org.threeten:threetenbp": 659773227 + }, "artifacts": { "com.fasterxml.jackson.core:jackson-core": { "shasums": { @@ -672,5 +713,5 @@ ] } }, - "version": "2" + "version": "3" } diff --git a/tests/integration/BUILD b/tests/integration/BUILD index 71654c7c0..1eac56aa8 100644 --- a/tests/integration/BUILD +++ b/tests/integration/BUILD @@ -179,6 +179,33 @@ diff_test( tags = [] if is_bzlmod_enabled() else ["manual"], ) +genquery( + name = "coursier-pinned-layering-deps", + expression = "deps(@coursier_pinned_layering//:com_google_code_findbugs_jsr305)", + opts = [ + "--nohost_deps", + "--noimplicit_deps", + ], + scope = ["@coursier_pinned_layering//:com_google_code_findbugs_jsr305"], +) + +genrule( + name = "coursier-pinned-layering-deps-sorted", + srcs = [":coursier-pinned-layering-deps"], + outs = ["coursier-pinned-layering-deps-sorted.txt"], + cmd = "cat $< | grep coursier_pinned_layering | sed -e 's|^@@|@|g; s|\\r||g' | sed -e 's|^@[^/]*[+~]|@|g; s|\\r||g' | sort > $@", +) + +# The root pins jsr305 at 3.0.1 while the contributing module offers 3.0.2. +# Without root precedence under `version_conflict_policy = "pinned"` the +# resolved version would be 3.0.2. +diff_test( + name = "coursier-pinned-layering-deps-test", + file1 = "coursier-pinned-layering-deps.golden.unix", + file2 = ":coursier-pinned-layering-deps-sorted.txt", + tags = [] if is_bzlmod_enabled() else ["manual"], +) + # This target will fail to build if we're not handling merging of maven.install # tags properly, and if we don't handle multiple lock files properly. java_library( diff --git a/tests/integration/coursier-pinned-layering-deps.golden.unix b/tests/integration/coursier-pinned-layering-deps.golden.unix new file mode 100644 index 000000000..652243d7b --- /dev/null +++ b/tests/integration/coursier-pinned-layering-deps.golden.unix @@ -0,0 +1,2 @@ +@coursier_pinned_layering//:com_google_code_findbugs_jsr305 +@coursier_pinned_layering//:v1/https/repo1.maven.org/maven2/com/google/code/findbugs/jsr305/3.0.1/jsr305-3.0.1.jar diff --git a/tests/integration/coursier_higher_layer/MODULE.bazel b/tests/integration/coursier_higher_layer/MODULE.bazel index 16f3d137f..0e7bfba74 100644 --- a/tests/integration/coursier_higher_layer/MODULE.bazel +++ b/tests/integration/coursier_higher_layer/MODULE.bazel @@ -14,3 +14,14 @@ maven.install( ], ) use_repo(maven, "coursier_layering") + +# Contributes a higher version to the `coursier_pinned_layering` repo, whose +# root pins jsr305 at 3.0.1. The root's `version_conflict_policy = "pinned"` +# must win over this higher version. +maven.install( + name = "coursier_pinned_layering", + artifacts = [ + "com.google.code.findbugs:jsr305:3.0.2", + ], +) +use_repo(maven, "coursier_pinned_layering") diff --git a/tests/unit/layering_test.bzl b/tests/unit/layering_test.bzl index 1abb46159..ece310b65 100644 --- a/tests/unit/layering_test.bzl +++ b/tests/unit/layering_test.bzl @@ -673,6 +673,63 @@ def _pinned_gradle_root_beats_higher_nonroot_force_impl(ctx): pinned_gradle_root_beats_higher_nonroot_force_test = unittest.make(_pinned_gradle_root_beats_higher_nonroot_force_impl) +def _pinned_coursier_root_beats_higher_nonroot_impl(ctx): + env = unittest.begin(ctx) + root = _artifact("1.0") + + result = _layer( + root_artifacts = [root], + resolver = "coursier", + version_conflict_policy = "pinned", + non_root_artifacts = {"dep": [_artifact("2.0")]}, + ) + + asserts.equals(env, ["1.0"], [artifact.version for artifact in result.artifacts]) + asserts.true(env, result.artifacts[0].force_version) + + return unittest.end(env) + +pinned_coursier_root_beats_higher_nonroot_test = unittest.make(_pinned_coursier_root_beats_higher_nonroot_impl) + +def _pinned_coursier_root_beats_higher_nonroot_force_impl(ctx): + env = unittest.begin(ctx) + root = _artifact("1.0") + + result = _layer( + root_artifacts = [root], + resolver = "coursier", + version_conflict_policy = "pinned", + non_root_artifacts = {"dep": [_artifact("2.0", force_version = True)]}, + ) + + asserts.equals(env, ["1.0"], [artifact.version for artifact in result.artifacts]) + asserts.true(env, result.artifacts[0].force_version) + + return unittest.end(env) + +pinned_coursier_root_beats_higher_nonroot_force_test = unittest.make(_pinned_coursier_root_beats_higher_nonroot_force_impl) + +def _pinned_coursier_root_wins_without_override_diagnostics_impl(ctx): + env = unittest.begin(ctx) + + # `duplicate_version_warning = "error"` must not fail: `pinned` resolves + # the displacement deterministically in favour of the root. + result = _layer( + root_artifacts = [_artifact("1.0")], + resolver = "coursier", + version_conflict_policy = "pinned", + duplicate_version_warning = "error", + known_contributing_modules = sets.make(["dep"]), + non_root_artifacts = {"dep": [_artifact("2.0")]}, + ) + + asserts.equals(env, ["1.0"], [artifact.version for artifact in result.artifacts]) + asserts.equals(env, [], result.diagnostics) + + return unittest.end(env) + +pinned_coursier_root_wins_without_override_diagnostics_test = unittest.make(_pinned_coursier_root_wins_without_override_diagnostics_impl) + def _namespaces_are_layered_independently_impl(ctx): env = unittest.begin(ctx) @@ -871,6 +928,9 @@ def layering_test_suite(): partial.make(boms_merge_with_root_priority_test, size = "small"), partial.make(classifier_and_packaging_layer_independently_test, size = "small"), partial.make(pinned_gradle_root_beats_higher_nonroot_force_test, size = "small"), + partial.make(pinned_coursier_root_beats_higher_nonroot_test, size = "small"), + partial.make(pinned_coursier_root_beats_higher_nonroot_force_test, size = "small"), + partial.make(pinned_coursier_root_wins_without_override_diagnostics_test, size = "small"), partial.make(namespaces_are_layered_independently_test, size = "small"), partial.make(diagnostics_preserve_text_gates_and_order_test, size = "small"), partial.make(default_namespace_contribution_warning_is_preserved_test, size = "small"), diff --git a/tests/unit/version_conflict_policy_test.bzl b/tests/unit/version_conflict_policy_test.bzl index 891a434ba..1e87a00d9 100644 --- a/tests/unit/version_conflict_policy_test.bzl +++ b/tests/unit/version_conflict_policy_test.bzl @@ -8,7 +8,7 @@ load("//private/lib:layering.bzl", "apply_root_version_conflict_policy") def _pinned_policy_forces_versioned_root_artifacts_impl(ctx): env = unittest.begin(ctx) - for resolver in ["gradle", "maven"]: + for resolver in ["gradle", "maven", "coursier"]: versioned = unpack_coordinates("com.example:root:1.0") versionless = unpack_coordinates("com.example:managed-by-bom") @@ -30,11 +30,9 @@ def _other_policies_leave_root_artifacts_unchanged_impl(ctx): artifact = unpack_coordinates("com.example:root:1.0") - default_artifacts = apply_root_version_conflict_policy([artifact], "gradle", "default") - coursier_artifacts = apply_root_version_conflict_policy([artifact], "coursier", "pinned") - - asserts.false(env, hasattr(default_artifacts[0], "force_version")) - asserts.false(env, hasattr(coursier_artifacts[0], "force_version")) + for resolver in ["gradle", "maven", "coursier"]: + default_artifacts = apply_root_version_conflict_policy([artifact], resolver, "default") + asserts.false(env, hasattr(default_artifacts[0], "force_version")) return unittest.end(env)