Search code examples
bazel

Bazel WORKSPACE conditionally define exactly one of two `git_repository`s


I'm maintaining two python libraries A and B, each partially using Bazel for building non-python codes. Library B depends on A in terms of Bazel, so B needs a remote repository of A.

  • For the released version of B, I'd like to have remote repository of A in canonical form, for example git_repository with commit hash.
git_repository(
  name = "A",
  commit = "...",
  remote = "https://github.com/foo/A",
)
  • During development, I'd like to have remote repository of A in symbolic link form, for example, git_repository with master branch.
git_repository(
  name = "A",
  branch = "master",
  remote = "https://github.com/foo/B",
)

And I'd like to use exactly one of them. After some research I found there is no "conditional branch" method (fed from command line flags or environment variable) I can use at the WORKSPACE level. I'm asking for any options I couldn't have found.


Followings are the alternatives that I've searched for, but not 100% happy.

  • Using local_repository during development is not an attractive solution, as in real there are 8+ libraries with chained dependencies, and I don't think it is realistic to manually clone and sometimes pull them.
  • Using alias() with select() at a BUILD level is not also very attractive solution, because it turns out there are tens of A's blaze targets that are used in B. Defining aliases for all of them is not maintainable at scale. (or is there a way to define alias at a package level?).
# WORKSPACE
git_repository(name = "A", ...)
git_repository(name = "A_master", ...)

# BUILD
config_setting(name = "use_master", ...)
alias(
  name = "A_pkg_label",  # There are too many targets to declare
  actual = select({
    ":use_master": "@A_master/pkg:label",
    "//conditions:default": "@A/pkg:label",
  })
)
  • Using two WORKSPACE files seems feasible, but I couldn't find a clean way to select WORKSPACE file other than manually renaming them.
  • Defining custom repository_rule, branching by the repository_ctx.os.environ value, seemed promising until I figured out that I cannot reuse other repository rule inside implementation.

Solution

  • While you can't reuse other repository rules in general, in practice many of them are written in Starlark and are easy to reuse. For example, git_repository's implementation looks like this:

    def _git_repository_implementation(ctx):
        update = _clone_or_update(ctx)
        patch(ctx)
        ctx.delete(ctx.path(".git"))
        return _update_git_attrs(ctx.attr, _common_attrs.keys(), update)
    

    Most of those utility functions are either NOPs if you're only using the basic features or possible to load from your own starlark code. You could do a barebones replacement with just this:

    load("@bazel_tools//tools/build_defs/repo:git_worker.bzl", "git_repo")
    def _my_git_repository_implementation(ctx):
        directory = str(ctx.path("."))
        git_repo(ctx, directory)
        ctx.delete(ctx.path(".git"))