rules_jvm_external icon indicating copy to clipboard operation
rules_jvm_external copied to clipboard

How to handle mixed lockfile presence in non-root modules

Open aaliddell opened this issue 4 months ago • 6 comments

Since https://github.com/protocolbuffers/protobuf/commit/1999135e8e1f209ed12bcdd984ab86ea3c5a87e4 (aka 32.0+), the protobuf dependency in BCR uses maven.install with a lockfile on the @maven name instead of @protobuf_maven: https://github.com/protocolbuffers/protobuf/blob/894039e7512e9940095a0bf04214adcba7a37adb/MODULE.bazel#L221-L228

Similarly, following feedback on the same logic, since 5.4.0 rules_proto_grpc pulls its 'user' dependencies into the same @maven repo, without a lockfile: https://github.com/rules-proto-grpc/rules_proto_grpc/blob/e40a70e7375f6dab68d402e78ff540c9d8177a09/modules/java/MODULE.bazel#L22-L25

A top level user may use rules_proto_grpc_java, which has a transitive dependency on protobuf and if they do not specify a lock file (or don't even use maven.install) then the protobuf lock file 'wins' and only its dependencies show up in the @maven repo and attempting to use a dep from outside the lockfile errors with a missing target.

See https://buildkite.com/bazel/rules-proto-grpc-rules-proto-grpc/builds/3744/steps/canvas?sid=0198eb95-044b-439a-9710-7bce4943ba79 for an example error

How is this meant to be handled when multiple non-root modules need to inject deps into the @maven name?

  • I cannot also add a lock file to rules_proto_grpc with the additional deps like protobuf has, as only a single non-root lock file is permitted (note also that this code below fails to print the error because lock_files is an array of labels not strings):

https://github.com/bazel-contrib/rules_jvm_external/blob/7d0015359dfac8532e408b6be67cee262c605c87/private/extensions/maven.bzl#L631-L636

  • Should the protobuf repo drop their lockfile?

  • Or is this additional functionality that needs adding to rules_jvm_external?

aaliddell avatar Aug 27 '25 12:08 aaliddell

That sounds like a bug.

I think the correct logic would be that if the workspace is defined in the root module, then only the root module's lock file should be used. If the workspace is not defined in the root module, then the one from the non-root module should be used, with multiple lock files being considered an error.

Does that sound right to you?

shs96c avatar Aug 27 '25 13:08 shs96c

As a work-around, specifying a lock file in the root module will work, and will also mean faster builds as you won't need to do the resolution every time the bazel daemon starts.

shs96c avatar Aug 27 '25 13:08 shs96c

Correction: adding just a dummy empty maven.install() in the root repo causes the build to pass, due to this section where the root module 'always wins' and wipes out all the lockfiles collected above:

https://github.com/bazel-contrib/rules_jvm_external/blob/7d0015359dfac8532e408b6be67cee262c605c87/private/extensions/maven.bzl#L606-L613

Since the above code effectively causes all transitive lockfiles to be ignored based on the mere presence of an extension tag in the root repo, I'm inclined to say that lockfiles from non-root modules should be ignored in all circumstances and perhaps a warning issued.

aaliddell avatar Aug 27 '25 13:08 aaliddell

That sounds like a bug.

I think the correct logic would be that if the workspace is defined in the root module, then only the root module's lock file should be used. If the workspace is not defined in the root module, then the one from the non-root module should be used, with multiple lock files being considered an error.

Does that sound right to you?

This is current behaviour, which is what is problematic I think, as the transitive dep tree can contain at most a single lockfile before an error is hit. Since protobuf in BCR now does this, no other module can and protobuf is widely pulled in transitively by language rules. Since transitive lockfiles are silently ignored in some circumstances anyway, the behaviour could instead be:

  • If the root module uses a lockfile, it is used
  • Lockfiles from transitive modules are always ignored

The alternative to this is that the lockfiles are somehow merged, which sounds like a world of pain when the inevitable conflicts are hit

Edit: this might be special-cased for the common @maven repo, as that is intended to be shared. I see what you mean about lockfiles being used for per-module deps that aren't being injected into the common repo

aaliddell avatar Aug 27 '25 13:08 aaliddell

To allow non-@maven lockfiles, the behaviour could actually be:

  • If the root module specifies a lockfile for a repo, it is used unconditionally (as it is today)
  • If only a single non-root module contributes to a repo (e.g. @rules_some_lang_maven), then its lockfile will be used if present
  • If multiple non-root modules contribute to a repo (i.e @maven), then transitive lockfiles are ignored

This allows modules to have their own local locked maven deps, whilst also allowing the @maven name to be shared.

aaliddell avatar Aug 27 '25 13:08 aaliddell

Would you accept a PR implementing the above? I now cannot escape this issue, as rules_java now transitively depends on the version of protobuf that trips this up, see here (which is a testing suite from a PR that only updates rules_java). I expect anyone similarly using the @maven name will start having problems from rules_java 8.16+ and the error message is particularly opaque as maven deps are just randomly missing

aaliddell avatar Sep 23 '25 15:09 aaliddell