blob: 2ac0d152cd28cb4e21fc419d68f15bd01558af2a [file]
"""Repository rules and helpers for configuring rules_python's build tool
toolchains.
"""
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
# Loaded rather than read by Label: reading a file of @rules_python by Label
# makes --experimental_remote_repo_contents_cache materialize the whole repo.
load("//python/private:artifacts_manifest.bzl", "ARTIFACTS_MANIFEST_JSON")
load("//python/private:text_util.bzl", "render")
load("//python/private:toolchain_types.bzl", "SHRUBBERY_TOOLCHAIN_TYPE")
_IS_NEED_SHRUBBERY_ENABLED = (
"@rules_python//python/private/toolchains:is_need_shrubbery_enabled"
)
_FROM_SOURCE_SHRUBBERY = (
"@rules_python//python/private/toolchains/from_source:shrubbery"
)
_PREBUILT_SHRUBBERY_TOOLCHAIN_IMPL_BUILD_TEMPLATE = """\
# Generated by @rules_python//python/private/toolchains:repo.bzl
load(
"@rules_python//python/private/toolchains:shrubbery_toolchain.bzl",
"shrubbery_toolchain",
)
shrubbery_toolchain(
name = "toolchain",
shrubbery = "{shrubbery}",
visibility = ["//visibility:public"],
)
"""
def _prebuilt_shrubbery_toolchain_impl_repo_impl(rctx):
rctx.file(
"BUILD.bazel",
_PREBUILT_SHRUBBERY_TOOLCHAIN_IMPL_BUILD_TEMPLATE.format(
shrubbery = rctx.attr.shrubbery,
),
executable = False,
)
# Bazel <8.3.0 lacks repository_ctx.repo_metadata.
if not hasattr(rctx, "repo_metadata"):
return None
return rctx.repo_metadata(reproducible = True)
prebuilt_shrubbery_toolchain_impl_repo = repository_rule(
implementation = _prebuilt_shrubbery_toolchain_impl_repo_impl,
doc = """
Creates a repository containing a shrubbery_toolchain target for a
prebuilt binary.
""",
attrs = {
"shrubbery": attr.string(
doc = "Label string of the prebuilt shrubbery binary file.",
mandatory = True,
),
},
)
def _tools_toolchains_repo_impl(rctx):
blocks = ["# Generated by @rules_python//python/private/toolchains:repo.bzl"]
for i, toolchain_name in enumerate(rctx.attr.toolchain_names):
toolchain_impl = rctx.attr.toolchain_impls[i]
exec_compatible_with = rctx.attr.exec_compatible_with[str(i)]
blocks.append(
render.toolchain(
name = toolchain_name,
exec_compatible_with = exec_compatible_with,
target_settings = [_IS_NEED_SHRUBBERY_ENABLED],
toolchain = toolchain_impl,
toolchain_type = str(SHRUBBERY_TOOLCHAIN_TYPE),
),
)
rctx.file(
"toolchains/BUILD.bazel",
"\n\n".join(blocks) + "\n",
executable = False,
)
# Bazel <8.3.0 lacks repository_ctx.repo_metadata.
if not hasattr(rctx, "repo_metadata"):
return None
return rctx.repo_metadata(reproducible = True)
tools_toolchains_repo = repository_rule(
implementation = _tools_toolchains_repo_impl,
doc = """
Creates the @rules_python_tools repository containing toolchain()
declarations.
""",
attrs = {
# Starlark repository rules do not have a list-of-string-lists
# attribute type, so string indices ("0", "1", ...) map each entry in
# toolchain_names to its exec_compatible_with constraint list.
"exec_compatible_with": attr.string_list_dict(
doc = """
Mapping from string index to exec_compatible_with constraint labels.
""",
),
"toolchain_impls": attr.string_list(
doc = "List of toolchain implementation target labels.",
mandatory = True,
),
"toolchain_names": attr.string_list(
doc = "List of toolchain target names.",
mandatory = True,
),
},
)
# Called by _config_impl in //python/extensions:config.bzl.
def configure_tool_toolchains(
http_file = http_file,
prebuilt_shrubbery_toolchain_impl_repo = (
prebuilt_shrubbery_toolchain_impl_repo
),
tools_toolchains_repo = tools_toolchains_repo,
artifacts_manifest_json = ARTIFACTS_MANIFEST_JSON):
"""Configures prebuilt or from-source toolchains in `@rules_python_tools`.
Called by the `config` module extension (`//python/extensions:config.bzl`).
If `ARTIFACTS_MANIFEST_JSON` from `//python/private:artifacts_manifest.bzl`
is non-empty (as in release archives), creates an `http_file` repo and a
`prebuilt_shrubbery_toolchain_impl_repo` repo for each entry in the
manifest, and registers `toolchain()` declarations in
`@rules_python_tools//toolchains`.
Otherwise (as in git checkouts and source builds), registers the
from-source `toolchain()` declaration in `@rules_python_tools//toolchains`.
Args:
http_file: Repository rule callable for downloading prebuilt tool
binaries (injectable for testing).
prebuilt_shrubbery_toolchain_impl_repo: Repository rule
callable for creating `shrubbery_toolchain` repos
(injectable for testing).
tools_toolchains_repo: Repository rule callable for creating
`@rules_python_tools` (injectable for testing).
artifacts_manifest_json: The JSON manifest of prebuilt tool binaries,
or empty to build from source (injectable for testing).
"""
manifest_content = artifacts_manifest_json.strip()
toolchain_names = []
toolchain_impls = []
exec_compatible_with = {}
if manifest_content:
manifest = json.decode(manifest_content)
entries = manifest.get("artifacts", {}).get("shrubbery", [])
for i, entry in enumerate(entries):
name = entry["name"]
file_repo_name = "rules_python_" + name + "_file"
toolchain_repo_name = "rules_python_" + name + "_toolchain"
http_file(
name = file_repo_name,
executable = True,
sha256 = entry["sha256"],
urls = entry["urls"],
)
prebuilt_shrubbery_toolchain_impl_repo(
name = toolchain_repo_name,
shrubbery = "@{}//file".format(file_repo_name),
)
toolchain_names.append(name)
toolchain_impls.append(
"@{}//:toolchain".format(toolchain_repo_name),
)
exec_compatible_with[str(i)] = entry["exec_compatible_with"]
else:
toolchain_names.append("shrubbery_from_source")
toolchain_impls.append(_FROM_SOURCE_SHRUBBERY)
exec_compatible_with["0"] = []
tools_toolchains_repo(
name = "rules_python_tools",
exec_compatible_with = exec_compatible_with,
toolchain_impls = toolchain_impls,
toolchain_names = toolchain_names,
)