blob: 05bc5c1cbe0e62a9a5dbc2cf0de7b1f745d910a7 [file]
"""Repository rules and helpers for configuring rules_python's build tool
toolchains.
"""
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
load("//python/private:text_util.bzl", "render")
load("//python/private:toolchain_types.bzl", "EXE_ZIP_MAKER_TOOLCHAIN_TYPE")
_ARTIFACTS_MANIFEST_LABEL = Label("//python/private:artifacts_manifest.json")
_IS_RUST_EXE_ZIP_MAKER_ENABLED = (
"@rules_python//python/private/toolchains:is_rust_exe_zip_maker_enabled"
)
_FROM_SOURCE_EXE_ZIP_MAKER = (
"@rules_python//python/private/toolchains/from_source:exe_zip_maker"
)
_PREBUILT_EXE_ZIP_MAKER_TOOLCHAIN_IMPL_BUILD_TEMPLATE = """\
# Generated by @rules_python//python/private/toolchains:repo.bzl
load(
"@rules_python//python/private/zipapp:py_exe_zip_maker_toolchain.bzl",
"py_exe_zip_maker_toolchain",
)
py_exe_zip_maker_toolchain(
name = "toolchain",
exe_zip_maker = "{exe_zip_maker}",
visibility = ["//visibility:public"],
)
"""
def _prebuilt_exe_zip_maker_toolchain_impl_repo_impl(rctx):
rctx.file(
"BUILD.bazel",
_PREBUILT_EXE_ZIP_MAKER_TOOLCHAIN_IMPL_BUILD_TEMPLATE.format(
exe_zip_maker = rctx.attr.exe_zip_maker,
),
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_exe_zip_maker_toolchain_impl_repo = repository_rule(
implementation = _prebuilt_exe_zip_maker_toolchain_impl_repo_impl,
doc = """
Creates a repository containing a py_exe_zip_maker_toolchain target for a
prebuilt binary.
""",
attrs = {
"exe_zip_maker": attr.string(
doc = "Label string of the prebuilt exe_zip_maker 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_RUST_EXE_ZIP_MAKER_ENABLED],
toolchain = toolchain_impl,
toolchain_type = str(EXE_ZIP_MAKER_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(
mctx,
http_file = http_file,
prebuilt_exe_zip_maker_toolchain_impl_repo = (
prebuilt_exe_zip_maker_toolchain_impl_repo
),
tools_toolchains_repo = tools_toolchains_repo):
"""Configures prebuilt or from-source toolchains in `@rules_python_tools`.
Called by the `config` module extension (`//python/extensions:config.bzl`).
If `//python/private:artifacts_manifest.json` contains non-comment JSON
content (as in release archives), creates an `http_file` repo and a
`prebuilt_exe_zip_maker_toolchain_impl_repo` repo for each entry in the
manifest, and registers `toolchain()` declarations in
`@rules_python_tools//toolchains`.
Otherwise (when the manifest file contains only `#`-comments, as in git
checkouts and source builds), registers the from-source `toolchain()`
declaration in `@rules_python_tools//toolchains`.
Args:
mctx: The `module_ctx` object.
http_file: Repository rule callable for downloading prebuilt tool
binaries (injectable for testing).
prebuilt_exe_zip_maker_toolchain_impl_repo: Repository rule callable for
creating `py_exe_zip_maker_toolchain` repos (injectable for testing).
tools_toolchains_repo: Repository rule callable for creating
`@rules_python_tools` (injectable for testing).
"""
manifest_content = "\n".join([
line
for line in mctx.read(_ARTIFACTS_MANIFEST_LABEL).splitlines()
if not line.strip().startswith("#")
]).strip()
toolchain_names = []
toolchain_impls = []
exec_compatible_with = {}
if manifest_content:
manifest = json.decode(manifest_content)
entries = manifest.get("artifacts", {}).get("exe_zip_maker", [])
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_exe_zip_maker_toolchain_impl_repo(
name = toolchain_repo_name,
exe_zip_maker = "@{}//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("exe_zip_maker_from_source")
toolchain_impls.append(_FROM_SOURCE_EXE_ZIP_MAKER)
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,
)