diff --git a/python/private/toolchains_repo.bzl b/python/private/toolchains_repo.bzl index c9ad35c6e5..6dad413274 100644 --- a/python/private/toolchains_repo.bzl +++ b/python/private/toolchains_repo.bzl @@ -29,27 +29,64 @@ PLATFORMS = { "@platforms//os:macos", "@platforms//cpu:aarch64", ], + # Matches the value returned from: + # repository_ctx.os.name.lower() + os_name = "mac os", + # Matches the value returned from: + # repository_ctx.execute(["uname", "-m"]).stdout.strip() + arch = "arm64", ), "x86_64-apple-darwin": struct( compatible_with = [ "@platforms//os:macos", "@platforms//cpu:x86_64", ], + # See comments above. + os_name = "mac os", + arch = "x86_64", ), "x86_64-pc-windows-msvc": struct( compatible_with = [ "@platforms//os:windows", "@platforms//cpu:x86_64", ], + # See comments above. + os_name = "windows", + arch = "x86_64", ), "x86_64-unknown-linux-gnu": struct( compatible_with = [ "@platforms//os:linux", "@platforms//cpu:x86_64", ], + # See comments above. + os_name = "linux", + arch = "x86_64", ), } +def host_platform(rctx): + """Infer the host platform from a repository context. + + Args: + rctx: Bazel's repository_ctx + Returns: + a key from the PLATFORMS dictionary + """ + os_name = rctx.os.name + + # We assume the arch for Windows is always x86_64. + if "windows" in os_name: + arch = "x86_64" + else: + # This is not ideal, but bazel doesn't directly expose arch. + arch = rctx.execute(["uname", "-m"]).stdout.strip() + + for platform, meta in PLATFORMS.items(): + if meta.os_name == os_name and meta.arch == arch: + return platform + fail("No platform declared for host OS {} on arch {}".format(os_name, arch)) + def _toolchains_repo_impl(repository_ctx): build_content = """\ # Generated by toolchains_repo.bzl diff --git a/python/repositories.bzl b/python/repositories.bzl index 53cf0651cb..b2c67231a0 100644 --- a/python/repositories.bzl +++ b/python/repositories.bzl @@ -17,11 +17,12 @@ For historic reasons, pip_repositories() is defined in //python:pip.bzl. """ -load("//python/private:toolchains_repo.bzl", "PLATFORMS", "toolchains_repo") -load("//python/private:versions.bzl", - "get_release_url", +load("//python/private:toolchains_repo.bzl", "PLATFORMS", "host_platform", "toolchains_repo") +load( + "//python/private:versions.bzl", "MINOR_MAPPING", "TOOL_VERSIONS", + "get_release_url", ) def py_repositories(): @@ -117,20 +118,16 @@ py_runtime_pair( rctx.file("BUILD.bazel", build_content) return { - "sha256": download_result.sha256, "name": rctx.attr.name, "platform": platform, "python_version": python_version, + "sha256": download_result.sha256, } python_repository = repository_rule( _python_repository_impl, doc = "Fetches the external tools needed for the Python toolchain.", attrs = { - "sha256": attr.string( - doc = "The SHA256 integrity hash for the Python interpreter tarball.", - mandatory = True, - ), "platform": attr.string( doc = "The platform name for the Python interpreter tarball.", mandatory = True, @@ -141,6 +138,48 @@ python_repository = repository_rule( mandatory = True, values = TOOL_VERSIONS.keys() + MINOR_MAPPING.keys(), ), + "sha256": attr.string( + doc = "The SHA256 integrity hash for the Python interpreter tarball.", + mandatory = True, + ), + }, +) + +def _host_os_alias_impl(repository_ctx): + # Base BUILD file for this repository. + repository_ctx.file("BUILD.bazel", """\ +# Generated by python/repositories.bzl +package(default_visibility = ["//visibility:public"]) +alias(name = "files", actual = "@{py_repository}_{host_platform}//:files") +alias(name = "py3_runtime", actual = "@{py_repository}_{host_platform}//:py3_runtime") +alias(name = "python_runtimes", actual = "@{py_repository}_{host_platform}//:python_runtimes") +alias(name = "python3", actual = "@{py_repository}_{host_platform}//:bin/python3") +""".format( + py_repository = repository_ctx.attr.user_repository_name, + host_platform = host_platform(repository_ctx), + )) + + # Expose a Starlark file so rules can know what host platform we used and where to find an interpreter + # when using repository_ctx.path, which doesn't understand aliases. + repository_ctx.file("defs.bzl", content = """\ +# Generated by python/repositories.bzl +host_platform="{host_platform}" +interpreter="@{py_repository}_{host_platform}//:bin/python3" +""".format( + py_repository = repository_ctx.attr.user_repository_name, + host_platform = host_platform(repository_ctx), + )) + +_host_os_alias = repository_rule( + _host_os_alias_impl, + doc = """Creates a repository with a shorter name meant for the host platform, which contains + a BUILD.bazel file declaring aliases to the host platform's targets. + """, + attrs = { + "user_repository_name": attr.string( + mandatory = True, + doc = "The base name for all created repositories, like 'python38'.", + ), }, ) @@ -150,8 +189,6 @@ def python_register_toolchains(name, python_version, **kwargs): - Create a repository for each built-in platform like "python_linux_amd64" - this repository is lazily fetched when Python is needed for that platform. - - TODO(f0rmiga): create a convenience repository for the host platform like - "python_host". - Create a repository exposing toolchains for each platform like "python_platforms". - Register a toolchain pointing at each platform. @@ -185,6 +222,10 @@ def python_register_toolchains(name, python_version, **kwargs): platform = platform, )) + _host_os_alias( + name = "{name}_host".format(name = name), + user_repository_name = name, + ) toolchains_repo( name = "{name}_toolchains".format(name = name), user_repository_name = name,