如何将用户定义的构建设置传递给现有的 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 文件规则字段中的构建设置值,或者它们是否只能由 Starlark 配置访问?
- 有没有一种方法可以在不访问已发布的实现的情况下有效地“子类化”加载的规则?如果
_impl
是 public,那么我似乎可以用我自己的实现来包装它,并将标志传递给它。
我对 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_file
和 tag_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 会很棘手,因为您必须深入并更改一些操作。
我想从 --define
标志迁移并根据以下内容构建设置:https://docs.bazel.build/versions/5.0.0/skylark/config.html
Here's the rule 我想向其传递命令行值。
- 在使用加载规则时可以在实践中做到这一点吗?
- 是否可以访问 .bazel 文件规则字段中的构建设置值,或者它们是否只能由 Starlark 配置访问?
- 有没有一种方法可以在不访问已发布的实现的情况下有效地“子类化”加载的规则?如果
_impl
是 public,那么我似乎可以用我自己的实现来包装它,并将标志传递给它。
我对 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_file
和 tag_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 会很棘手,因为您必须深入并更改一些操作。