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