如何将用户定义的构建设置传递给现有的 Bazel 规则?

How do I pass user-defined build settings to existing Bazel rules?

我想从 --define 标志迁移并根据以下内容构建设置:https://docs.bazel.build/versions/5.0.0/skylark/config.html

Here's the rule 我想向其传递命令行值。

我对 Bazel 有点陌生,仍在寻找正确的方法来概念化这些东西。任何指导表示赞赏!

当前方法

backend/BUILD.bazel:

load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_push")

# container_image :run_server definition

container_push(
    name = "push_server",
    format = "Docker",
    image = ":run_server",
    registry = "gcr.io",
    repository = "$(PROJECT_ID)/chat/server",
    tag = "$(CONTAINER_TAG)",
)

那我运行:

bazel run \
  --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64 \
  --define PROJECT_ID=$(gcloud config get-value project) \
  --define CONTAINER_TAG=some_feature_branch \
  -- //backend:push_server

我试过的

一些变体:

load("//backend:rules.bzl", "gcr_container_push")
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
load("@io_bazel_rules_docker//container:container.bzl", "container_image")

string_flag(
    name = "container_tag",
    build_setting_default = "latest",
    visibility = ["//visibility:public"],
)

string_flag(
    name = "project_id",
    build_setting_default = "",
    visibility = ["//visibility:public"],
)

# container_image :run_server definition

gcr_container_push(
    name = "push_server",
    image = ":run_server", 
    path = "chat/server",
)

backend/rules.bzl:

load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load("@bazel_skylib//lib:paths.bzl", "paths")
load("@io_bazel_rules_docker//container:container.bzl", "container_push")

def _gcr_container_push_impl(ctx):
    project_id = ctx.attr._project_id[BuildSettingInfo].value
    if len(project_id) == 0:
        fail("Please provide a GCP project ID via --//backend:project_id=<PROJECT ID>.")
    container_push(
        name = ctx.label.name,
        format = "Docker",
        image = ctx.attr.image,
        registry = "gcr.io",
        repository = paths.join(project_id, ctx.attr.path),
        tag = ctx.attr._container_tag[BuildSettingInfo].value,
    )

_gcr_container_push_attrs = {
    "image": attr.label(
        allow_single_file = [".tar"],
        mandatory = True,
        doc = "The label of the image to push.",
    ),
    "path": attr.string(
        mandatory = True,
        doc = "The name of the image within the repository. Ex. gcr.io/project_id/<PATH>:tag.",
    ),
    "_container_tag": attr.label(default = Label("//backend:container_tag")),
    "_project_id": attr.label(default = Label("//backend:project_id")),
}

gcr_container_push = rule(
    implementation = _gcr_container_push_impl,
    attrs = _gcr_container_push_attrs,
    executable = True,
)

那我运行:

bazel run \
  --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64 \
  --//backend:project_id=ggx-prototype \
  -- //backend:push_server  

哪个returns:

Error in container_push_: 'container_push_' can only be called during the loading phase

rules_docker 具有 repository_filetag_file 之类的属性,正好适合这种情况。您可以根据需要生成这些文件,包括使用 user-defined 标志的自定义规则。我会这样做:

def gcr_container_push(name, image, path, **kwargs):
  if 'tag' in kwargs or 'repository' in kwargs:
    fail('Not allowed to set these')
  _gcr_container_repository(
    name = name + '_repository',
    visibility = ['//visibility:private'],
    path = path,
  )
  _gcr_container_tag(
    name = name + '_tag',
    visibility = ['//visibility:private'],
    path = path,
  )
  container_push(
    name = name,
    format = 'Docker',
    image = image,
    registry = 'gcr.io',
    repository = '',
    repository_file = ':%s_repository' % name,
    tag_file = ':%s_tag' % name,
    **kwargs
  )

def _gcr_container_repository_impl(ctx):
    project_id = ctx.attr._project_id[BuildSettingInfo].value
    if len(project_id) == 0:
        fail("Please provide a GCP project ID via --//backend:project_id=<PROJECT ID>.")
    output = ctx.actions.declare_file(ctx.label.name + '_file')
    ctx.actions.write(output, paths.join(project_id, ctx.attr.path))
    return [DefaultInfo(files = depset([output]))]

_gcr_container_repository = rule(
  impl = _gcr_container_repository_impl,
  attrs = {
    "path": attr.string(mandatory = True),
    "_project_id": attr.label(default = Label("//backend:project_id")),
  },
)

def _gcr_container_tag_impl(ctx):
    output = ctx.actions.declare_file(ctx.label.name + '_file')
    ctx.actions.write(output, ctx.attr._container_tag[BuildSettingInfo].value)
    return [DefaultInfo(files = depset([output]))]

_gcr_container_tag = rule(
  impl = _gcr_container_tag_impl,
  attrs = {
    "path": attr.string(mandatory = True),
    "_container_tag": attr.label(default = Label("//backend:container_tag")),
  },
)

您正在尝试混合 rule and a macro。规则有属性和 _impl vs 宏可以使用其他规则。我的方法使用自定义规则生成文件,并使用宏将这些规则绑定到 container_push.

您问题的一般答案是,这需要修改规则以根据 user-defined 标志执行新类型的替换。我可以看到某种配置有 allow_multiple = True--@rules_docker//flags:docker_info=MY_PROJECT=foo 会被替换,但它肯定需要修改规则。包装 _impl 会很棘手,因为您必须深入并更改一些操作。