Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(toolchain): support specifying x.y versions in transitions #1720

Merged
merged 23 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4357386
refactor: move the toolchain_def to starlark as opposed to templating
aignas Nov 8, 2023
1f98be3
refactor: move the version setting as well
aignas Nov 8, 2023
1177c45
Match on X.Y versions
aignas Nov 8, 2023
cdb602b
add an example of how we could use the transition files directly
aignas Jan 24, 2024
baadb17
changelog
aignas Jan 24, 2024
7dbcec4
chore: add a missing dep to be able to build docs
aignas Jan 24, 2024
65a65e1
chore: move the newly added code to a separate file to avoid dependen…
aignas Jan 25, 2024
bb2334d
rename examples
aignas Jan 26, 2024
493fb98
s/toolchain_defs/toolchain_suite
aignas Jan 26, 2024
cb0ae15
Revert "s/toolchain_defs/toolchain_suite"
aignas Jan 26, 2024
0b8906f
s/toolchain_defs/py_toolchain_suite
aignas Jan 26, 2024
f963deb
comment: move the import
aignas Jan 26, 2024
cfa949e
Merge branch 'main' into feat/x.y-transition
aignas Jan 26, 2024
366c55e
Merge branch 'main' into feat/x.y-transition
aignas Jan 28, 2024
438ea0e
fix: match select of x.y.z with config of x.y if it is in MINOR_MAPPING
aignas Jan 28, 2024
d644dc1
fixup tests
aignas Jan 29, 2024
9cca70f
use no_match_error instead of a comment in code
aignas Jan 29, 2024
844705b
Update CHANGELOG.md
aignas Jan 31, 2024
6b73356
Update python/config_settings/config_settings.bzl
aignas Jan 31, 2024
38378a2
comment: make the lookup mandatory
aignas Jan 31, 2024
b739ed2
comment: move the bzl_library
aignas Jan 31, 2024
3ca03ec
Fix the select statements
aignas Jan 31, 2024
f061f02
lint: pre-commit
aignas Jan 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ A brief description of the categories of changes:
* New Python versions available: `3.11.7`, `3.12.1` using
https://github.com/indygreg/python-build-standalone/releases/tag/20240107.

* (toolchain) Allow setting `x.y` as the `python_version` parameter in
`py_binary` and `py_test` with transitions. This allows the users to
use the same rule import for testing with specific Python versions and
rely on toolchain configuration and how the latest version takes precedence
if e.g. `3.8` is selected. That also simplifies `.bazelrc` for any users
that set the default `python_version` string flag in that way.

aignas marked this conversation as resolved.
Show resolved Hide resolved
## 0.29.0 - 2024-01-22

[0.29.0]: https://github.com/bazelbuild/rules_python/releases/tag/0.29.0
Expand Down
52 changes: 52 additions & 0 deletions examples/bzlmod/tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ load("@python_versions//3.10:defs.bzl", py_binary_3_10 = "py_binary", py_test_3_
load("@python_versions//3.11:defs.bzl", py_binary_3_11 = "py_binary", py_test_3_11 = "py_test")
load("@python_versions//3.9:defs.bzl", py_binary_3_9 = "py_binary", py_test_3_9 = "py_test")
load("@rules_python//python:defs.bzl", "py_binary", "py_test")
load("@rules_python//python:versions.bzl", "MINOR_MAPPING")
load("@rules_python//python/config_settings:transition.bzl", py_versioned_binary = "py_binary", py_versioned_test = "py_test")

py_binary(
name = "version_default",
Expand All @@ -27,6 +29,13 @@ py_binary_3_11(
main = "version.py",
)

py_versioned_binary(
name = "version_3_10_versioned",
srcs = ["version.py"],
main = "version.py",
python_version = "3.10",
)

# This is a work in progress and the commented
# tests will not work until we can support
# multiple pips with bzlmod.
Expand All @@ -52,6 +61,28 @@ py_test_3_10(
deps = ["//libs/my_lib"],
)

py_versioned_test(
name = "my_lib_versioned_test",
srcs = ["my_lib_test.py"],
main = "my_lib_test.py",
python_version = "3.10",
deps = select(
{
"@rules_python//python/config_settings:is_python_" + MINOR_MAPPING["3.10"]: ["//libs/my_lib"],
},
no_match_error = """\
This test is failing to find dependencies and it seems that the is_python_{version}
does not match the transitioned configuration of python-version 3.10. Please
look at the

@rules_python//python/config_settings:config_settings.bzl

to fix any bugs.""".format(
version = MINOR_MAPPING["3.10"],
),
),
)

py_test(
name = "version_default_test",
srcs = ["version_test.py"],
Expand All @@ -73,6 +104,14 @@ py_test_3_10(
main = "version_test.py",
)

py_versioned_test(
name = "version_versioned_test",
srcs = ["version_test.py"],
env = {"VERSION_CHECK": "3.10"},
main = "version_test.py",
python_version = "3.10",
)

py_test_3_11(
name = "version_3_11_test",
srcs = ["version_test.py"],
Expand Down Expand Up @@ -104,6 +143,19 @@ py_test_3_10(
main = "cross_version_test.py",
)

py_versioned_test(
name = "version_3_10_takes_3_9_subprocess_test_2",
aignas marked this conversation as resolved.
Show resolved Hide resolved
srcs = ["cross_version_test.py"],
data = [":version_3_9"],
env = {
"SUBPROCESS_VERSION_CHECK": "3.9",
"SUBPROCESS_VERSION_PY_BINARY": "$(rootpath :version_3_9)",
"VERSION_CHECK": "3.10",
},
main = "cross_version_test.py",
python_version = "3.10",
)

sh_test(
name = "version_test_binary_default",
srcs = ["version_test.sh"],
Expand Down
32 changes: 30 additions & 2 deletions python/config_settings/config_settings.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

load("@bazel_skylib//lib:selects.bzl", "selects")
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
load("//python:versions.bzl", "MINOR_MAPPING")

def construct_config_settings(name, python_versions):
"""Constructs a set of configs for all Python versions.
Expand All @@ -36,6 +37,8 @@ def construct_config_settings(name, python_versions):
minor_to_micro_versions.setdefault(minor, []).append(micro_version)
allowed_flag_values.append(micro_version)

allowed_flag_values.extend(list(minor_to_micro_versions))

string_flag(
name = "python_version",
# TODO: The default here should somehow match the MODULE config. Until
Expand All @@ -59,14 +62,39 @@ def construct_config_settings(name, python_versions):
)
matches_minor_version_names = [equals_minor_version_name]

default_micro_version = MINOR_MAPPING.get(minor_version)

for micro_version in micro_versions:
is_micro_version_name = "is_python_" + micro_version
if not default_micro_version:
aignas marked this conversation as resolved.
Show resolved Hide resolved
native.config_setting(
name = is_micro_version_name,
flag_values = {":python_version": micro_version},
visibility = ["//visibility:public"],
)
matches_minor_version_names.append(is_micro_version_name)
continue

# Ensure that is_python_3.9.8 is matched if python_version is set
# to 3.9 if MINOR_MAPPING points to 3.9.8
equals_micro_name = "_python_version_flag_equals_" + micro_version
native.config_setting(
name = is_micro_version_name,
name = equals_micro_name,
flag_values = {":python_version": micro_version},
)
selects.config_setting_group(
aignas marked this conversation as resolved.
Show resolved Hide resolved
name = "_" + is_micro_version_name,
match_any = [
equals_micro_name,
equals_minor_version_name,
],
)
native.alias(
name = is_micro_version_name,
actual = "_is_python_" + minor_version,
visibility = ["//visibility:public"],
)
matches_minor_version_names.append(is_micro_version_name)
matches_minor_version_names.append(equals_micro_name)

# This is prefixed with an underscore to prevent confusion due to how
# config_setting_group is implemented and how our micro-version targets
Expand Down
8 changes: 8 additions & 0 deletions python/private/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,14 @@ bzl_library(
srcs = ["text_util.bzl"],
)

bzl_library(
name = "py_toolchain_suite_bzl",
aignas marked this conversation as resolved.
Show resolved Hide resolved
srcs = ["py_toolchain_suite.bzl"],
deps = [
"@bazel_skylib//lib:selects",
],
)

bzl_library(
name = "toolchains_repo_bzl",
srcs = ["toolchains_repo.bzl"],
Expand Down
2 changes: 1 addition & 1 deletion python/private/bzlmod/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,6 @@ bzl_library(
deps = [
"//python:versions_bzl",
"//python/private:full_version_bzl",
"//python/private:toolchains_repo_bzl",
"//python/private:py_toolchain_suite_bzl",
],
)
29 changes: 17 additions & 12 deletions python/private/bzlmod/pythons_hub.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ load(
"//python/private:toolchains_repo.bzl",
"get_host_os_arch",
"get_host_platform",
"get_repository_name",
"python_toolchain_build_file_content",
)

Expand All @@ -31,6 +30,7 @@ def _have_same_length(*lists):

_HUB_BUILD_FILE_TEMPLATE = """\
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("@@{rules_python}//python/private:py_toolchain_suite.bzl", "py_toolchain_suite")

bzl_library(
name = "interpreters_bzl",
Expand All @@ -56,19 +56,24 @@ def _hub_build_file_content(
if not _have_same_length(python_versions, set_python_version_constraints, user_repository_names):
fail("all lists must have the same length")

rules_python = get_repository_name(workspace_location)

# Iterate over the length of python_versions and call
# build the toolchain content by calling python_toolchain_build_file_content
toolchains = "\n".join([python_toolchain_build_file_content(
prefix = prefixes[i],
python_version = full_version(python_versions[i]),
set_python_version_constraint = set_python_version_constraints[i],
user_repository_name = user_repository_names[i],
rules_python = rules_python,
) for i in range(len(python_versions))])

return _HUB_BUILD_FILE_TEMPLATE.format(toolchains = toolchains)
toolchains = "\n".join(
[
python_toolchain_build_file_content(
prefix = prefixes[i],
python_version = full_version(python_versions[i]),
set_python_version_constraint = set_python_version_constraints[i],
user_repository_name = user_repository_names[i],
)
for i in range(len(python_versions))
],
)

return _HUB_BUILD_FILE_TEMPLATE.format(
toolchains = toolchains,
rules_python = workspace_location.workspace_name,
)

_interpreters_bzl_template = """
INTERPRETER_LABELS = {{
Expand Down
81 changes: 81 additions & 0 deletions python/private/py_toolchain_suite.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# 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.

"""Create the toolchain defs in a BUILD.bazel file."""

load("@bazel_skylib//lib:selects.bzl", "selects")

_py_toolchain_type = Label("@bazel_tools//tools/python:toolchain_type")
_py_cc_toolchain_type = Label("//python/cc:toolchain_type")

def py_toolchain_suite(*, prefix, user_repository_name, python_version, set_python_version_constraint, **kwargs):
"""For internal use only.

Args:
prefix: Prefix for toolchain target names.
user_repository_name: The name of the user repository.
python_version: The full (X.Y.Z) version of the interpreter.
set_python_version_constraint: True or False as a string.
**kwargs: extra args passed to the `toolchain` calls.

"""

# We have to use a String value here because bzlmod is passing in a
# string as we cannot have list of bools in build rule attribues.
# This if statement does not appear to work unless it is in the
# toolchain file.
if set_python_version_constraint == "True":
major_minor, _, _ = python_version.rpartition(".")

selects.config_setting_group(
name = prefix + "_version_setting",
match_any = [
Label("//python/config_settings:is_python_%s" % v)
for v in [
major_minor,
python_version,
]
],
visibility = ["//visibility:private"],
)
target_settings = [prefix + "_version_setting"]
elif set_python_version_constraint == "False":
target_settings = []
else:
fail(("Invalid set_python_version_constraint value: got {} {}, wanted " +
"either the string 'True' or the string 'False'; " +
"(did you convert bool to string?)").format(
type(set_python_version_constraint),
repr(set_python_version_constraint),
))

native.toolchain(
name = "{prefix}_toolchain".format(prefix = prefix),
toolchain = "@{user_repository_name}//:python_runtimes".format(
user_repository_name = user_repository_name,
),
toolchain_type = _py_toolchain_type,
target_settings = target_settings,
**kwargs
)

native.toolchain(
name = "{prefix}_py_cc_toolchain".format(prefix = prefix),
toolchain = "@{user_repository_name}//:py_cc_toolchain".format(
user_repository_name = user_repository_name,
),
toolchain_type = _py_cc_toolchain_type,
target_settings = target_settings,
**kwargs
)
Loading
Loading