bazelbuild / bazel

a fast, scalable, multi-language and extensible build system
https://bazel.build
Apache License 2.0
22.95k stars 4.02k forks source link

Expose module version as a Starlark constant #17114

Closed fmeum closed 1 year ago

fmeum commented 1 year ago

Description of the feature request:

With Bzlmod, the version of a module is specified in its MODULE.bazel file. Since MODULE.bazel files can't be segmented more or less by design to make fetching module metadata from registries efficient, it isn't possible to load the version from a separate .bzl file that would also be available to the rest of the project. As that means that the version has to be specified explicitly in MODULE.bazel, it would be helpful if the version specified there would be available to the rest of the repository as a Starlark constant.

This is possible to achieve today, but very cumbersome:

  1. Extract the version into a Starlark constant defined in MODULE.bazel.
  2. Write a repository rule that exposes a string via a .bzl file.
  3. Write a module extension with a tag that instantiates this rule with a given string.
  4. Feed the Starlark constant into the module extension.

Sharing this module extension between modules isn't directly possible as each module will have to find its own version rather than that of the global root module. This can be worked around by also extracting the module name into a constant and passing it into the extension and having it generate Starlark constants named after the modules, but that entire process feels very convoluted. It also decreases the readability and tool accessibility of the MODULE.bazel file due to the need to extract the version and potentially name into constants.

What underlying problem are you trying to solve with this feature?

I am working on Bzlmod-ifying a project that deploys to Maven and thus needs to bake its version into release artifacts. It would be a great user experience if the MODULE.bazel file could be the single source of truth for the version for the rest of the repository as that would make it unnecessary to keep all the different version strings in sync via external tooling.

Which operating system are you running Bazel on?

Any

What is the output of bazel info release?

6.0.0

If bazel info release returns development version or (@non-git), tell us how you built Bazel.

No response

What's the output of git remote get-url origin; git rev-parse master; git rev-parse HEAD ?

No response

Have you found anything relevant by searching the web?

No response

Any other information, logs, or outputs that you want to share?

No response

fmeum commented 1 year ago

@sgowroji I think this should be team-External-Deps as inventing new Starlark features should not be needed to realize this feature request.

Wyverald commented 1 year ago

What kind of API are you looking for here? I'm guessing just module_ctx.modules[0].version isn't what you want?

fmeum commented 1 year ago

I didn't add any concrete API suggestions as I don't yet have an idea that I am fully convinced is good - only a use case.

My thoughts:

  1. I see value in not giving module_ctx access to version information for the same reason that target don't support listing all available providers: I am worried about brittle code that doesn't compose well with version resolution.
  2. I also think that non-root modules shouldn't necessarily be able to access the version of the root module, but only their own version.

One direction in which I have been thinking is to make something like load("//:MODULE.bazel", "VERSION", "NAME") (or //:MODULE.bazel.bzl) work: A magically generated .bzl file exporting the module arguments. This makes the intent very clear and requires the least effort by the end user (just a single load statement), but it is magic.

Wyverald commented 1 year ago

FWIW, module_ctx.modules[0].version is already a thing, in case you weren't aware (https://bazel.build/rules/lib/bazel_module#version)

fmeum commented 1 year ago

FWIW, module_ctx.modules[0].version is already a thing, in case you weren't aware (https://bazel.build/rules/lib/bazel_module#version)

Interesting, I indeed wasn't aware of this. That makes it much easier to create a BCR module that exposes this version as a constant. I will look into doing that and revisit this issue.

fmeum commented 1 year ago

I drafted a module that offers this functionality: https://github.com/fmeum/version

That brings the effort down to:

# MODULE.bazel
bazel_dep(name = "version", version = "0.1.0")
version_bzl = use_extension("@version//:extensions.bzl", "version_bzl")
use_repo(version_bzl, "version_bzl")

# BUILD.bazel
load("@version_bzl//:version.bzl", "MY_MODULE_VERSION")

@Wyverald What do you think, would that be a useful addition to the BCR?

Wyverald commented 1 year ago

Looks nice! Not sure about it being called version (makes it sound like a library handling version parsing etc), but I don't have a better name either.

Just one note: if a multiple-version override exists, only one of the versions will be reported (obviously, I guess).

fmeum commented 1 year ago

Looks nice! Not sure about it being called version (makes it sound like a library handling version parsing etc), but I don't have a better name either.

How about module_version? I agree that version is too generic.

Just one note: if a multiple-version override exists, only one of the versions will be reported (obviously, I guess).

Is it specified which one? The highest or the one that comes first in the version list?

Wyverald commented 1 year ago

Maybe just put it into skylib, actually?

It looks like Starlark dict literals with duplicate keys end up retaining the last one, so it would be the one that comes last in module_ctx.modules. This in turn means that you get the last one among the module versions that use the version_bzl extension.

Note also that module_ctx.modules only contains modules that use the version_bzl extension.

fmeum commented 1 year ago

Maybe just put it into skylib, actually?

Great idea, will turn this into a PR. It also solves the naming problem :-)

Note also that module_ctx.modules only contains modules that use the version_bzl extension.

That shouldn't affect us much here as users have to use_repo the generated repo anyway.

aiuto commented 1 year ago

It would be a great user experience if the MODULE.bazel file could be the single source of truth for the version for the rest of the repository as that would make it unnecessary to keep all the different version strings in sync via external tooling.

I don't find it that hard. Right now I prefer to maintain my own version.bzl file because I build both with and without bzlmod, and probably will as long as people are using bazel 5.x.

But that is sort of beside the point. FWIW, the tooling for SBOM generation and license compliance is going to do something that may provide the same benefit. For every repository downloaded, we'll be rewriting the top level BUILD file to add a package_info rule that includes metadata about the repository, such as version, canonical name and download url. That would be a target, rather than a loadable string from a bzl file, but that's just the current implementation. It could be implemented so that the strings were emitted to a .bzl file and loaded rather than places solely in the BUILD file.

fmeum commented 1 year ago

@aiuto It sounds to me as if the SBOM efforts would complement the feature this issue asks about and that is implemented by my skylib PR, but not replace it: Having package_info patched into downloaded repos provides you with the versions of your dependencies whereas the module extension I'm proposing (mostly) provides you with your own module's version. As the main workspace can't be patched, I don't see how the SBOM tooling could help achieve the latter.

aiuto commented 1 year ago

Yes. They do complement each other. I need solutions that work without bzlmod, and for projects that provide their own version and other metadata about the package.

Wyverald commented 1 year ago

Closing this as we're pursuing https://github.com/bazelbuild/bazel-skylib/pull/421

SanjayVas commented 2 months ago

For anyone else looking for the resolution, this was added in bcf309b88949fe1bbff1776d88fdaa5c3e1d2d37