From c0e85b6f5c6f51ce6ba596f8a929a7d6808b8632 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 24 Jul 2024 15:34:11 -0700 Subject: [PATCH 1/5] wip: making toolchain tests be in-build, not bazel-in-bazel tests --- MODULE.bazel | 9 + WORKSPACE | 5 +- python/private/python.bzl | 41 +++- tests/support/sh_py_run_test.bzl | 12 +- tests/toolchains/BUILD.bazel | 9 +- tests/toolchains/defs.bzl | 201 ++---------------- tests/toolchains/python_toolchain_test.py | 28 +++ tests/toolchains/run_acceptance_test.py.tmpl | 90 -------- tests/toolchains/versions_test.bzl | 51 ----- .../toolchains/workspace_template/BUILD.bazel | 6 - .../workspace_template/BUILD.bazel.tmpl | 9 - .../workspace_template/MODULE.bazel.tmpl | 19 -- tests/toolchains/workspace_template/README.md | 4 - .../workspace_template/WORKSPACE.tmpl | 41 ---- .../workspace_template/python_version_test.py | 26 --- 15 files changed, 109 insertions(+), 442 deletions(-) create mode 100644 tests/toolchains/python_toolchain_test.py delete mode 100644 tests/toolchains/run_acceptance_test.py.tmpl delete mode 100644 tests/toolchains/versions_test.bzl delete mode 100644 tests/toolchains/workspace_template/BUILD.bazel delete mode 100644 tests/toolchains/workspace_template/BUILD.bazel.tmpl delete mode 100644 tests/toolchains/workspace_template/MODULE.bazel.tmpl delete mode 100644 tests/toolchains/workspace_template/README.md delete mode 100644 tests/toolchains/workspace_template/WORKSPACE.tmpl delete mode 100644 tests/toolchains/workspace_template/python_version_test.py diff --git a/MODULE.bazel b/MODULE.bazel index 2e0d06dc5f..457d8cc0fa 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -76,6 +76,15 @@ bazel_dep(name = "rules_testing", version = "0.6.0", dev_dependency = True) bazel_dep(name = "rules_go", version = "0.41.0", dev_dependency = True, repo_name = "io_bazel_rules_go") bazel_dep(name = "gazelle", version = "0.33.0", dev_dependency = True, repo_name = "bazel_gazelle") +dev_python = use_extension( + "//python/extensions:python.bzl", + "python", + dev_dependency = True, +) +dev_python.rules_python_private_testing( + register_all_versions = True, +) + dev_pip = use_extension( "//python/private/pypi:pip.bzl", "pip_internal", diff --git a/WORKSPACE b/WORKSPACE index 90e9305684..6c1ab4f9af 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -42,12 +42,13 @@ load("//:internal_setup.bzl", "rules_python_internal_setup") rules_python_internal_setup() load("//python:repositories.bzl", "python_register_multi_toolchains") -load("//python:versions.bzl", "MINOR_MAPPING") +load("//python:versions.bzl", "MINOR_MAPPING", "TOOL_VERSIONS") python_register_multi_toolchains( name = "python", default_version = MINOR_MAPPING.values()[-2], - python_versions = MINOR_MAPPING.values(), + # Integration tests verify each version, so register all of them. + python_versions = TOOL_VERSIONS.keys(), ) load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file") diff --git a/python/private/python.bzl b/python/private/python.bzl index 2791ae9e38..49fd0084d7 100644 --- a/python/private/python.bzl +++ b/python/private/python.bzl @@ -16,6 +16,7 @@ load("@bazel_features//:features.bzl", "bazel_features") load("//python:repositories.bzl", "python_register_toolchains") +load("//python:versions.bzl", "TOOL_VERSIONS") load(":pythons_hub.bzl", "hub_repo") load(":text_util.bzl", "render") load(":toolchains_repo.bzl", "multi_toolchain_aliases") @@ -78,7 +79,10 @@ def _python_impl(module_ctx): for mod in module_ctx.modules: module_toolchain_versions = [] - for toolchain_attr in mod.tags.toolchain: + toolchain_tags = [_toolchain_struct_from(tag = tag) for tag in mod.tags.toolchain] + extra_toolchains = _compute_register_all_versions(module_ctx, toolchain_tags) + for toolchain_attr in mod.tags.toolchain + extra_toolchains: + toolchain_attr = _toolchain_struct_from(tag = toolchain_attr) toolchain_version = toolchain_attr.python_version toolchain_name = "python_" + toolchain_version.replace(".", "_") @@ -251,6 +255,36 @@ def _fail_multiple_default_toolchains(first, second): second = second, )) +def _compute_register_all_versions(mctx, existing_toolchains): + seen_versions = {v.python_version: None for v in existing_toolchains} + register_all = False + for mod in mctx.modules: + if not mod.is_root: + continue + for tag in mod.tags.rules_python_private_testing: + if tag.register_all_versions: + register_all = True + break + if not register_all: + return [] + + extra_toolchains = [ + _toolchain_struct_from(python_version = v) + for v in TOOL_VERSIONS.keys() + if v not in seen_versions + ] + return extra_toolchains + +def _toolchain_struct_from(*, tag = None, python_version = None): + if tag and python_version: + fail("Only one of tag and python version must be specified") + return struct( + is_default = getattr(tag, "is_default", False), + python_version = python_version if python_version else tag.python_version, + configure_coverage_tool = getattr(tag, "configure_coverage_tool", False), + ignore_root_user_error = getattr(tag, "ignore_root_user_error", False), + ) + def _get_bazel_version_specific_kwargs(): kwargs = {} @@ -264,6 +298,11 @@ python = module_extension( """, implementation = _python_impl, tag_classes = { + "rules_python_private_testing": tag_class( + attrs = { + "register_all_versions": attr.bool(default = False), + }, + ), "toolchain": tag_class( doc = """Tag class used to register Python toolchains. Use this tag class to register one or more Python toolchains. This class diff --git a/tests/support/sh_py_run_test.bzl b/tests/support/sh_py_run_test.bzl index 183122a6ba..b6ba596cfc 100644 --- a/tests/support/sh_py_run_test.bzl +++ b/tests/support/sh_py_run_test.bzl @@ -20,16 +20,18 @@ without the overhead of a bazel-in-bazel integration test. load("//python:py_binary.bzl", "py_binary") load("//python:py_test.bzl", "py_test") load("//python/private:toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE") # buildifier: disable=bzl-visibility +load("//tests/support:support.bzl", "VISIBLE_FOR_TESTING") def _perform_transition_impl(input_settings, attr): settings = dict(input_settings) + settings[VISIBLE_FOR_TESTING] = True settings["//command_line_option:build_python_zip"] = attr.build_python_zip if attr.bootstrap_impl: settings["//python/config_settings:bootstrap_impl"] = attr.bootstrap_impl if attr.extra_toolchains: settings["//command_line_option:extra_toolchains"] = attr.extra_toolchains - else: - settings["//command_line_option:extra_toolchains"] = input_settings["//command_line_option:extra_toolchains"] + if attr.python_version: + settings["//python/config_settings:python_version"] = attr.python_version return settings _perform_transition = transition( @@ -37,11 +39,14 @@ _perform_transition = transition( inputs = [ "//python/config_settings:bootstrap_impl", "//command_line_option:extra_toolchains", + "//python/config_settings:python_version", ], outputs = [ "//command_line_option:build_python_zip", "//command_line_option:extra_toolchains", "//python/config_settings:bootstrap_impl", + "//python/config_settings:python_version", + VISIBLE_FOR_TESTING, ], ) @@ -99,6 +104,7 @@ to make the RBE presubmits happy, which disable auto-detection of a CC toolchain. """, ), + "python_version": attr.string(), "target": attr.label(executable = True, cfg = "target"), "_allowlist_function_transition": attr.label( default = "@bazel_tools//tools/allowlists/function_transition_allowlist", @@ -125,6 +131,7 @@ def py_reconfig_test(*, name, **kwargs): reconfig_kwargs = {} reconfig_kwargs["bootstrap_impl"] = kwargs.pop("bootstrap_impl", None) reconfig_kwargs["extra_toolchains"] = kwargs.pop("extra_toolchains", None) + reconfig_kwargs["python_version"] = kwargs.pop("python_version", None) reconfig_kwargs["env"] = kwargs.get("env") inner_name = "_{}_inner" + name _py_reconfig_test( @@ -178,6 +185,7 @@ def _current_build_settings_impl(ctx): "short_path": runtime.interpreter.short_path if runtime.interpreter else None, }, "interpreter_path": runtime.interpreter_path, + "toolchain_label": str(getattr(toolchain, "toolchain_label", None)), }), ) return [DefaultInfo( diff --git a/tests/toolchains/BUILD.bazel b/tests/toolchains/BUILD.bazel index 2f804a4ca0..c55dc92a7d 100644 --- a/tests/toolchains/BUILD.bazel +++ b/tests/toolchains/BUILD.bazel @@ -12,9 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -load(":defs.bzl", "acceptance_tests") -load(":versions_test.bzl", "versions_test_suite") +load(":defs.bzl", "define_toolchain_tests") -versions_test_suite(name = "versions_test") - -acceptance_tests() +define_toolchain_tests( + name = "toolchain_tests", +) diff --git a/tests/toolchains/defs.bzl b/tests/toolchains/defs.bzl index 723272d212..70b18abe1d 100644 --- a/tests/toolchains/defs.bzl +++ b/tests/toolchains/defs.bzl @@ -12,192 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""This module contains the definition for the toolchains testing rules. -""" +"" -load("//python:versions.bzl", "PLATFORMS", "TOOL_VERSIONS") -load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED") # buildifier: disable=bzl-visibility -load("//python/private:toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE") # buildifier: disable=bzl-visibility +load("//python:versions.bzl", "TOOL_VERSIONS") +load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test") -_WINDOWS_RUNNER_TEMPLATE = """\ -@ECHO OFF -set PATHEXT=.COM;.EXE;.BAT -powershell.exe -c "& ./{interpreter_path} {run_acceptance_test_py}" -""" - -def _acceptance_test_impl(ctx): - files = [] - - if BZLMOD_ENABLED: - module_bazel = ctx.actions.declare_file("/".join([ctx.attr.python_version, "MODULE.bazel"])) - ctx.actions.expand_template( - template = ctx.file._module_bazel_tmpl, - output = module_bazel, - substitutions = {"%python_version%": ctx.attr.python_version}, - ) - files.append(module_bazel) - - workspace = ctx.actions.declare_file("/".join([ctx.attr.python_version, "WORKSPACE"])) - ctx.actions.write(workspace, "") - files.append(workspace) - else: - workspace = ctx.actions.declare_file("/".join([ctx.attr.python_version, "WORKSPACE"])) - ctx.actions.expand_template( - template = ctx.file._workspace_tmpl, - output = workspace, - substitutions = {"%python_version%": ctx.attr.python_version}, - ) - files.append(workspace) - - build_bazel = ctx.actions.declare_file("/".join([ctx.attr.python_version, "BUILD.bazel"])) - ctx.actions.expand_template( - template = ctx.file._build_bazel_tmpl, - output = build_bazel, - substitutions = {"%python_version%": ctx.attr.python_version}, - ) - files.append(build_bazel) - - python_version_test = ctx.actions.declare_file("/".join([ctx.attr.python_version, "python_version_test.py"])) - ctx.actions.symlink( - target_file = ctx.file._python_version_test, - output = python_version_test, - ) - files.append(python_version_test) - - run_acceptance_test_py = ctx.actions.declare_file("/".join([ctx.attr.python_version, "run_acceptance_test.py"])) - ctx.actions.expand_template( - template = ctx.file._run_acceptance_test_tmpl, - output = run_acceptance_test_py, - substitutions = { - "%is_bzlmod%": str(BZLMOD_ENABLED), - "%is_windows%": str(ctx.attr.is_windows), - "%python_version%": ctx.attr.python_version, - "%test_location%": "/".join([ctx.attr.test_location, ctx.attr.python_version]), - }, - ) - files.append(run_acceptance_test_py) - - toolchain = ctx.toolchains[TARGET_TOOLCHAIN_TYPE] - py3_runtime = toolchain.py3_runtime - interpreter_path = py3_runtime.interpreter_path - if not interpreter_path: - interpreter_path = py3_runtime.interpreter.short_path - - if ctx.attr.is_windows: - executable = ctx.actions.declare_file("run_test_{}.bat".format(ctx.attr.python_version)) - ctx.actions.write( - output = executable, - content = _WINDOWS_RUNNER_TEMPLATE.format( - interpreter_path = interpreter_path.replace("../", "external/"), - run_acceptance_test_py = run_acceptance_test_py.short_path, - ), - is_executable = True, - ) - else: - executable = ctx.actions.declare_file("run_test_{}.sh".format(ctx.attr.python_version)) - ctx.actions.write( - output = executable, - content = "exec '{interpreter_path}' '{run_acceptance_test_py}'".format( - interpreter_path = interpreter_path, - run_acceptance_test_py = run_acceptance_test_py.short_path, - ), - is_executable = True, - ) - files.append(executable) - files.extend(ctx.files._distribution) - - return [DefaultInfo( - executable = executable, - files = depset( - direct = files, - transitive = [py3_runtime.files], - ), - runfiles = ctx.runfiles( - files = files, - transitive_files = py3_runtime.files, - ), - )] - -_acceptance_test = rule( - implementation = _acceptance_test_impl, - doc = "A rule for the toolchain acceptance tests.", - attrs = { - "is_windows": attr.bool( - doc = "(Provided by the macro) Whether this is running under Windows or not.", - mandatory = True, - ), - "python_version": attr.string( - doc = "The Python version to be used when requesting the toolchain.", - mandatory = True, - ), - "test_location": attr.string( - doc = "(Provided by the macro) The value of native.package_name().", - mandatory = True, - ), - "_build_bazel_tmpl": attr.label( - doc = "The BUILD.bazel template.", - allow_single_file = True, - default = Label("//tests/toolchains/workspace_template:BUILD.bazel.tmpl"), - ), - "_distribution": attr.label( - doc = "The rules_python source distribution.", - default = Label("//:distribution"), - ), - "_module_bazel_tmpl": attr.label( - doc = "The MODULE.bazel template.", - allow_single_file = True, - default = Label("//tests/toolchains/workspace_template:MODULE.bazel.tmpl"), - ), - "_python_version_test": attr.label( - doc = "The python_version_test.py used to test the Python version.", - allow_single_file = True, - default = Label("//tests/toolchains/workspace_template:python_version_test.py"), - ), - "_run_acceptance_test_tmpl": attr.label( - doc = "The run_acceptance_test.py template.", - allow_single_file = True, - default = Label("//tests/toolchains:run_acceptance_test.py.tmpl"), - ), - "_workspace_tmpl": attr.label( - doc = "The WORKSPACE template.", - allow_single_file = True, - default = Label("//tests/toolchains/workspace_template:WORKSPACE.tmpl"), - ), - }, - test = True, - toolchains = [TARGET_TOOLCHAIN_TYPE], -) - -def acceptance_test(python_version, **kwargs): - _acceptance_test( - is_windows = select({ - "@bazel_tools//src/conditions:host_windows": True, - "//conditions:default": False, - }), - python_version = python_version, - test_location = native.package_name(), - **kwargs - ) - -# buildifier: disable=unnamed-macro -def acceptance_tests(): - """Creates a matrix of acceptance_test targets for all the toolchains. - """ +def define_toolchain_tests(name): for python_version in TOOL_VERSIONS.keys(): - for platform, meta in PLATFORMS.items(): - if platform not in TOOL_VERSIONS[python_version]["sha256"]: - continue - acceptance_test( - name = "python_{python_version}_{platform}_test".format( - python_version = python_version.replace(".", "_"), - platform = platform, - ), - python_version = python_version, - target_compatible_with = meta.compatible_with, - tags = [ - "acceptance-test", - # For some inexplicable reason, these fail locally with - # sandboxing enabled, but not on CI. - "no-sandbox", - ], - ) + py_reconfig_test( + name = "python_{}_test".format(python_version), + srcs = ["python_toolchain_test.py"], + main = "python_toolchain_test.py", + python_version = python_version, + env = { + "EXPECT_PYTHON_VERSION": python_version, + }, + deps = ["//python/runfiles"], + data = ["//tests/support:current_build_settings"], + ) diff --git a/tests/toolchains/python_toolchain_test.py b/tests/toolchains/python_toolchain_test.py new file mode 100644 index 0000000000..371b252a4a --- /dev/null +++ b/tests/toolchains/python_toolchain_test.py @@ -0,0 +1,28 @@ +import json +import os +import pathlib +import sys +import unittest + +from python.runfiles import runfiles + + +class PythonToolchainTest(unittest.TestCase): + def test_expected_toolchain_matches(self): + expect_version = os.environ["EXPECT_PYTHON_VERSION"] + + rf = runfiles.Create() + settings_path = rf.Rlocation( + "rules_python/tests/support/current_build_settings.json" + ) + settings = json.loads(pathlib.Path(settings_path).read_text()) + + expected = "python_{}".format(expect_version.replace(".", "_")) + self.assertIn(expected, settings["toolchain_label"], str(settings)) + + actual = "{v.major}.{v.minor}.{v.micro}".format(v=sys.version_info) + self.assertEqual(actual, expect_version) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/toolchains/run_acceptance_test.py.tmpl b/tests/toolchains/run_acceptance_test.py.tmpl deleted file mode 100644 index c52e078a32..0000000000 --- a/tests/toolchains/run_acceptance_test.py.tmpl +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright 2022 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import subprocess -import unittest -import pathlib - -class TestPythonVersion(unittest.TestCase): - @classmethod - def setUpClass(cls): - os.chdir("%test_location%") - test_srcdir = os.environ["TEST_SRCDIR"] - # When bzlmod is enabled, the name of the directory in runfiles changes - # to _main instead of rules_python - if os.path.exists(os.path.join(test_srcdir, "_main")): - rules_python_path = os.path.join(test_srcdir, "_main") - else: - rules_python_path = os.path.join(test_srcdir, "rules_python") - - test_tmpdir = os.environ["TEST_TMPDIR"] - if %is_windows%: - home = os.path.join(test_tmpdir, "HOME") - os.mkdir(home) - os.environ["HOME"] = home - - local_app_data = os.path.join(test_tmpdir, "LocalAppData") - os.mkdir(local_app_data) - os.environ["LocalAppData"] = local_app_data - - # Bazelisk requires a cache directory be set - os.environ["XDG_CACHE_HOME"] = os.path.join(test_tmpdir, "xdg-cache-home") - - # Unset this so this works when called by Bazel's latest Bazel build - # pipeline. It sets the following combination, which interfere with each other: - # * --sandbox_tmpfs_path=/tmp - # * --test_env=USE_BAZEL_VERSION - # * USE_BAZEL_VERSION=/tmp/ - os.environ.pop("USE_BAZEL_VERSION", None) - - bazelrc_lines = [ - "build --test_output=errors", - ] - - if %is_bzlmod%: - bazelrc_lines.extend( - [ - 'build --override_module rules_python="{}"'.format( - rules_python_path.replace("\\", "/") - ), - "common --enable_bzlmod", - ] - ) - else: - bazelrc_lines.extend( - [ - 'build --override_repository rules_python="{}"'.format( - rules_python_path.replace("\\", "/") - ), - "common --noexperimental_enable_bzlmod", - ] - ) - - bazelrc = pathlib.Path(".bazelrc") - bazelrc.write_text(os.linesep.join(bazelrc_lines)) - - def test_match_toolchain(self): - output = subprocess.check_output( - f"bazel run --announce_rc @python//:python3 -- --version", - shell = True, # Shell needed to look up via PATH - text=True, - ).strip() - self.assertEqual(output, "Python %python_version%") - - subprocess.run("bazel test --announce_rc //...", shell=True, check=True) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/toolchains/versions_test.bzl b/tests/toolchains/versions_test.bzl deleted file mode 100644 index b885d228a0..0000000000 --- a/tests/toolchains/versions_test.bzl +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2022 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Unit tests for starlark helpers -See https://docs.bazel.build/versions/main/skylark/testing.html#for-testing-starlark-utilities -""" - -load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest") -load("//python:versions.bzl", "MINOR_MAPPING", "TOOL_VERSIONS") - -required_platforms = [ - "x86_64-apple-darwin", - "x86_64-unknown-linux-gnu", -] - -def _smoke_test_impl(ctx): - env = unittest.begin(ctx) - for version in TOOL_VERSIONS.keys(): - platforms = TOOL_VERSIONS[version]["sha256"] - for required_platform in required_platforms: - asserts.true( - env, - required_platform in platforms.keys(), - "Missing platform {} for version {}".format(required_platform, version), - ) - for minor in MINOR_MAPPING: - version = MINOR_MAPPING[minor] - asserts.true( - env, - version in TOOL_VERSIONS.keys(), - "Missing version {} in TOOL_VERSIONS".format(version), - ) - return unittest.end(env) - -# The unittest library requires that we export the test cases as named test rules, -# but their names are arbitrary and don't appear anywhere. -_t0_test = unittest.make(_smoke_test_impl) - -def versions_test_suite(name): - unittest.suite(name, _t0_test) diff --git a/tests/toolchains/workspace_template/BUILD.bazel b/tests/toolchains/workspace_template/BUILD.bazel deleted file mode 100644 index 7f3e7b0370..0000000000 --- a/tests/toolchains/workspace_template/BUILD.bazel +++ /dev/null @@ -1,6 +0,0 @@ -exports_files([ - "BUILD.bazel.tmpl", - "MODULE.bazel.tmpl", - "WORKSPACE.tmpl", - "python_version_test.py", -]) diff --git a/tests/toolchains/workspace_template/BUILD.bazel.tmpl b/tests/toolchains/workspace_template/BUILD.bazel.tmpl deleted file mode 100644 index 4a452096a7..0000000000 --- a/tests/toolchains/workspace_template/BUILD.bazel.tmpl +++ /dev/null @@ -1,9 +0,0 @@ -load("@rules_python//python:defs.bzl", "py_test") - -py_test( - name = "python_version_test", - srcs = ["python_version_test.py"], - env = { - "PYTHON_VERSION": "%python_version%", - }, -) diff --git a/tests/toolchains/workspace_template/MODULE.bazel.tmpl b/tests/toolchains/workspace_template/MODULE.bazel.tmpl deleted file mode 100644 index 9e3a844fa6..0000000000 --- a/tests/toolchains/workspace_template/MODULE.bazel.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -module( - name = "module_test", - version = "0.0.0", - compatibility_level = 1, -) - -bazel_dep(name = "bazel_skylib", version = "1.3.0") -bazel_dep(name = "rules_python", version = "0.0.0") -local_path_override( - module_name = "rules_python", - path = "", -) - -python = use_extension("@rules_python//python/extensions:python.bzl", "python") -python.toolchain( - is_default = True, - python_version = "%python_version%", -) -use_repo(python, "python_versions", python = "python_%python_version%".replace(".", "_")) diff --git a/tests/toolchains/workspace_template/README.md b/tests/toolchains/workspace_template/README.md deleted file mode 100644 index b4d6e6ac41..0000000000 --- a/tests/toolchains/workspace_template/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Toolchains testing WORKSPACE template - -This directory contains templates for generating acceptance tests for the -toolchains. diff --git a/tests/toolchains/workspace_template/WORKSPACE.tmpl b/tests/toolchains/workspace_template/WORKSPACE.tmpl deleted file mode 100644 index 3335f4b063..0000000000 --- a/tests/toolchains/workspace_template/WORKSPACE.tmpl +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2022 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -workspace(name = "workspace_test") - -local_repository( - name = "rules_python", - path = "", -) - -load("@rules_python//python:repositories.bzl", "python_register_toolchains", "py_repositories") - -py_repositories() - -python_register_toolchains( - name = "python", - python_version = "%python_version%", -) - -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -http_archive( - name = "bazel_skylib", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz", - "https://github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz", - ], - sha256 = "c6966ec828da198c5d9adbaa94c05e3a1c7f21bd012a0b29ba8ddbccb2c93b0d", -) -load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") -bazel_skylib_workspace() diff --git a/tests/toolchains/workspace_template/python_version_test.py b/tests/toolchains/workspace_template/python_version_test.py deleted file mode 100644 index c82611cdab..0000000000 --- a/tests/toolchains/workspace_template/python_version_test.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2022 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import platform -import unittest - - -class TestPythonVersion(unittest.TestCase): - def test_match_toolchain(self): - self.assertEqual(platform.python_version(), os.getenv("PYTHON_VERSION")) - - -if __name__ == "__main__": - unittest.main() From bf00aaf114d950b45356a81254dfe85d048431c6 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Mon, 29 Jul 2024 10:27:19 -0700 Subject: [PATCH 2/5] fix extra calls being treated as default=true --- python/private/python.bzl | 56 +++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/python/private/python.bzl b/python/private/python.bzl index 49fd0084d7..68717e3bac 100644 --- a/python/private/python.bzl +++ b/python/private/python.bzl @@ -79,10 +79,9 @@ def _python_impl(module_ctx): for mod in module_ctx.modules: module_toolchain_versions = [] - toolchain_tags = [_toolchain_struct_from(tag = tag) for tag in mod.tags.toolchain] - extra_toolchains = _compute_register_all_versions(module_ctx, toolchain_tags) - for toolchain_attr in mod.tags.toolchain + extra_toolchains: - toolchain_attr = _toolchain_struct_from(tag = toolchain_attr) + toolchain_attr_structs = _create_toolchain_attr_structs(module_ctx, mod) + + for toolchain_attr in toolchain_attr_structs: toolchain_version = toolchain_attr.python_version toolchain_name = "python_" + toolchain_version.replace(".", "_") @@ -99,9 +98,7 @@ def _python_impl(module_ctx): # * rules_python needs to set a soft default in case the root module doesn't, # e.g. if the root module doesn't use Python itself. # * The root module is allowed to override the rules_python default. - - # A single toolchain is treated as the default because it's unambiguous. - is_default = toolchain_attr.is_default or len(mod.tags.toolchain) == 1 + is_default = toolchain_attr.is_default # Also only the root module should be able to decide ignore_root_user_error. # Modules being depended upon don't know the final environment, so they aren't @@ -255,31 +252,38 @@ def _fail_multiple_default_toolchains(first, second): second = second, )) -def _compute_register_all_versions(mctx, existing_toolchains): - seen_versions = {v.python_version: None for v in existing_toolchains} - register_all = False - for mod in mctx.modules: - if not mod.is_root: - continue +def _create_toolchain_attr_structs(mctx, mod): + arg_structs = [] + seen_versions = {} + for tag in mod.tags.toolchain: + arg_structs.append(_create_toolchain_attrs_struct(tag = tag, toolchain_tag_count = len(mod.tags.toolchain))) + seen_versions[tag.python_version] = True + + if mod.is_root: + register_all = False for tag in mod.tags.rules_python_private_testing: if tag.register_all_versions: register_all = True break - if not register_all: - return [] - - extra_toolchains = [ - _toolchain_struct_from(python_version = v) - for v in TOOL_VERSIONS.keys() - if v not in seen_versions - ] - return extra_toolchains - -def _toolchain_struct_from(*, tag = None, python_version = None): + if register_all: + arg_structs.extend([ + _create_toolchain_attrs_struct(python_version = v) + for v in TOOL_VERSIONS.keys() + if v not in seen_versions + ]) + return arg_structs + +def _create_toolchain_attrs_struct(*, tag = None, python_version = None, toolchain_tag_count = None): if tag and python_version: - fail("Only one of tag and python version must be specified") + fail("Only one of tag and python version can be specified") + if tag: + # A single toolchain is treated as the default because it's unambiguous. + is_default = tag.is_default or toolchain_tag_count == 1 + else: + is_default = False + return struct( - is_default = getattr(tag, "is_default", False), + is_default = is_default, python_version = python_version if python_version else tag.python_version, configure_coverage_tool = getattr(tag, "configure_coverage_tool", False), ignore_root_user_error = getattr(tag, "ignore_root_user_error", False), From 2edb20a938b6cc2d10cbe6bf4686585f31c87dd6 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Mon, 29 Jul 2024 10:29:59 -0700 Subject: [PATCH 3/5] fixup! fix extra calls being treated as default=true --- python/private/python.bzl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/private/python.bzl b/python/private/python.bzl index 68717e3bac..ce00a7bb74 100644 --- a/python/private/python.bzl +++ b/python/private/python.bzl @@ -79,7 +79,7 @@ def _python_impl(module_ctx): for mod in module_ctx.modules: module_toolchain_versions = [] - toolchain_attr_structs = _create_toolchain_attr_structs(module_ctx, mod) + toolchain_attr_structs = _create_toolchain_attr_structs(mod) for toolchain_attr in toolchain_attr_structs: toolchain_version = toolchain_attr.python_version @@ -252,7 +252,7 @@ def _fail_multiple_default_toolchains(first, second): second = second, )) -def _create_toolchain_attr_structs(mctx, mod): +def _create_toolchain_attr_structs(mod): arg_structs = [] seen_versions = {} for tag in mod.tags.toolchain: From 8e6ed8eb43405c66e3307da902f4697b77c4c5e9 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Mon, 29 Jul 2024 19:32:23 -0700 Subject: [PATCH 4/5] skip toolchains+platform combos that dont exist --- tests/support/sh_py_run_test.bzl | 2 ++ tests/toolchains/defs.bzl | 19 +++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/support/sh_py_run_test.bzl b/tests/support/sh_py_run_test.bzl index b6ba596cfc..455f64e49f 100644 --- a/tests/support/sh_py_run_test.bzl +++ b/tests/support/sh_py_run_test.bzl @@ -133,6 +133,8 @@ def py_reconfig_test(*, name, **kwargs): reconfig_kwargs["extra_toolchains"] = kwargs.pop("extra_toolchains", None) reconfig_kwargs["python_version"] = kwargs.pop("python_version", None) reconfig_kwargs["env"] = kwargs.get("env") + reconfig_kwargs["target_compatible_with"] = kwargs.get("target_compatible_with") + inner_name = "_{}_inner" + name _py_reconfig_test( name = name, diff --git a/tests/toolchains/defs.bzl b/tests/toolchains/defs.bzl index 70b18abe1d..dd2245c6a3 100644 --- a/tests/toolchains/defs.bzl +++ b/tests/toolchains/defs.bzl @@ -14,11 +14,25 @@ "" -load("//python:versions.bzl", "TOOL_VERSIONS") +load("@bazel_skylib//lib:selects.bzl", "selects") +load("//python:versions.bzl", "PLATFORMS", "TOOL_VERSIONS") load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test") def define_toolchain_tests(name): - for python_version in TOOL_VERSIONS.keys(): + for platform_key, platform_info in PLATFORMS.items(): + native.config_setting( + name = "_is_{}".format(platform_key), + constraint_values = platform_info.compatible_with, + ) + + for python_version, meta in TOOL_VERSIONS.items(): + target_compatible_with = { + "//conditions:default": ["@platforms//:incompatible"], + } + for platform_key in meta["sha256"].keys(): + is_platform = "_is_{}".format(platform_key) + target_compatible_with[is_platform] = [] + py_reconfig_test( name = "python_{}_test".format(python_version), srcs = ["python_toolchain_test.py"], @@ -29,4 +43,5 @@ def define_toolchain_tests(name): }, deps = ["//python/runfiles"], data = ["//tests/support:current_build_settings"], + target_compatible_with = select(target_compatible_with), ) From c51d0dfbf90118a42e2a53e60cffd7002008e9bf Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Mon, 29 Jul 2024 19:46:35 -0700 Subject: [PATCH 5/5] fixup! skip toolchains+platform combos that dont exist --- tests/toolchains/defs.bzl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/toolchains/defs.bzl b/tests/toolchains/defs.bzl index dd2245c6a3..076e6b42eb 100644 --- a/tests/toolchains/defs.bzl +++ b/tests/toolchains/defs.bzl @@ -14,11 +14,15 @@ "" -load("@bazel_skylib//lib:selects.bzl", "selects") load("//python:versions.bzl", "PLATFORMS", "TOOL_VERSIONS") load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test") def define_toolchain_tests(name): + """Define the toolchain tests. + + Args: + name: Only present to satisfy tooling. + """ for platform_key, platform_info in PLATFORMS.items(): native.config_setting( name = "_is_{}".format(platform_key),