| """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, | |
| ) |