-
-
Notifications
You must be signed in to change notification settings - Fork 594
internal(config_settings): make config_setting creation reusable #1750
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
Merged
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
dbc080a
internal(config_settings): make config_setting creation reusable
aignas 787281d
update tests
aignas 5cf63b5
buildifier
aignas 373bde4
fixup
aignas ba7b5cb
buildifier
aignas 321ffc3
move to private
aignas 48df917
appease buildifier
aignas 3f4829a
add a bzl_library for consistency
aignas f9e0dcb
buildifier
aignas c316bca
comment: construct config settings via a macro
aignas bd89952
have a python version flag
aignas 6c2920c
comment: add a comment on the contents of the VERSION_FLAG_VALUES
aignas d82b9ab
change the VERSION_FLAG_VALUES
aignas 6abf0c0
comment: use match_any instead of match_extra
aignas e91ebab
clarify the fail message
aignas ab9b139
Update python/private/config_settings.bzl
aignas d6912e2
clarify the doc
aignas b367461
Self review
aignas 52e3202
Address most PR comments
aignas 24ab9f3
remove no-op match_any checks
aignas 6bd0de0
pass visibilty implicitly
aignas 85bbb84
wip cleanup
aignas 67a3e8a
clarify code and use different concepts for label reusing
aignas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
# Copyright 2024 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. | ||
|
||
"""This module is used to construct the config settings in the BUILD file in this same package. | ||
""" | ||
|
||
load("@bazel_skylib//lib:selects.bzl", "selects") | ||
load("@bazel_skylib//rules:common_settings.bzl", "string_flag") | ||
load("//python:versions.bzl", "MINOR_MAPPING", "TOOL_VERSIONS") | ||
|
||
_PYTHON_VERSION_FLAG = str(Label("//python/config_settings:python_version")) | ||
|
||
def _ver_key(s): | ||
rickeylev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
major, _, s = s.partition(".") | ||
minor, _, s = s.partition(".") | ||
micro, _, s = s.partition(".") | ||
return (int(major), int(minor), int(micro)) | ||
|
||
def _flag_values(python_versions): | ||
"""Construct a map of python_version to a list of toolchain values. | ||
|
||
This mapping maps the concept of a config setting to a list of compatible toolchain versions. | ||
For using this in the code, the VERSION_FLAG_VALUES should be used instead. | ||
|
||
Args: | ||
python_versions: list of strings; all X.Y.Z python versions | ||
|
||
Returns: | ||
A `map[str, list[str]]`. Each key is a python_version flag value. Each value | ||
is a list of the python_version flag values that should match when for the | ||
`key`. For example: | ||
``` | ||
"3.8" -> ["3.8", "3.8.1", "3.8.2", ..., "3.8.19"] # All 3.8 versions | ||
"3.8.2" -> ["3.8.2"] # Only 3.8.2 | ||
"3.8.19" -> ["3.8.19", "3.8"] # The latest version should also match 3.8 so | ||
as when the `3.8` toolchain is used we just use the latest `3.8` toolchain. | ||
this makes the `select("is_python_3.8.19")` work no matter how the user | ||
specifies the latest python version to use. | ||
``` | ||
""" | ||
ret = {} | ||
|
||
for micro_version in sorted(python_versions, key = _ver_key): | ||
minor_version, _, _ = micro_version.rpartition(".") | ||
|
||
# This matches the raw flag value, e.g. --//python/config_settings:python_version=3.8 | ||
# It's private because matching the concept of e.g. "3.8" value is done | ||
# using the `is_python_X.Y` config setting group, which is aware of the | ||
# minor versions that could match instead. | ||
ret.setdefault(minor_version, [minor_version]).append(micro_version) | ||
|
||
# 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 | ||
default_micro_version = MINOR_MAPPING[minor_version] | ||
ret[micro_version] = [micro_version, minor_version] if default_micro_version == micro_version else [micro_version] | ||
|
||
return ret | ||
|
||
VERSION_FLAG_VALUES = _flag_values(TOOL_VERSIONS.keys()) | ||
|
||
def is_python_config_setting(name, *, python_version, reuse_conditions = None, **kwargs): | ||
"""Create a config setting for matching 'python_version' configuration flag. | ||
|
||
This function is mainly intended for internal use within the `whl_library` and `pip_parse` | ||
machinery. | ||
|
||
The matching of the 'python_version' flag depends on the value passed in | ||
`python_version` and here is the example for `3.8` (but the same applies | ||
to other python versions present in @//python:versions.bzl#TOOL_VERSIONS): | ||
* "3.8" -> ["3.8", "3.8.1", "3.8.2", ..., "3.8.19"] # All 3.8 versions | ||
* "3.8.2" -> ["3.8.2"] # Only 3.8.2 | ||
* "3.8.19" -> ["3.8.19", "3.8"] # The latest version should also match 3.8 so | ||
as when the `3.8` toolchain is used we just use the latest `3.8` toolchain. | ||
this makes the `select("is_python_3.8.19")` work no matter how the user | ||
specifies the latest python version to use. | ||
|
||
Args: | ||
name: name for the target that will be created to be used in select statements. | ||
python_version: The python_version to be passed in the `flag_values` in the | ||
`config_setting`. Depending on the version, the matching python version list | ||
can be as described above. | ||
reuse_conditions: A dict of version to version label for which we should | ||
reuse config_setting targets instead of creating them from scratch. This | ||
is useful when using is_python_config_setting multiple times in the | ||
same package with the same `major.minor` python versions. | ||
**kwargs: extra kwargs passed to the `config_setting`. | ||
""" | ||
if python_version not in name: | ||
aignas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
fail("The name '{}' must have the python version '{}' in it".format(name, python_version)) | ||
|
||
if python_version not in VERSION_FLAG_VALUES: | ||
aignas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
fail("The 'python_version' must be known to 'rules_python', choose from the values: {}".format(VERSION_FLAG_VALUES.keys())) | ||
|
||
python_versions = VERSION_FLAG_VALUES[python_version] | ||
rickeylev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if len(python_versions) == 1: | ||
native.config_setting( | ||
name = name, | ||
flag_values = { | ||
_PYTHON_VERSION_FLAG: python_version, | ||
}, | ||
**kwargs | ||
) | ||
return | ||
|
||
reuse_conditions = reuse_conditions or {} | ||
create_config_settings = { | ||
"_{}".format(name).replace(python_version, version): {_PYTHON_VERSION_FLAG: version} | ||
for version in python_versions | ||
if not reuse_conditions or version not in reuse_conditions | ||
} | ||
match_any = list(create_config_settings.keys()) | ||
for version, condition in reuse_conditions.items(): | ||
if len(VERSION_FLAG_VALUES[version]) == 1: | ||
match_any.append(condition) | ||
continue | ||
|
||
# Convert the name to an internal label that this function would create, | ||
# so that we are hitting the config_setting and not the config_setting_group. | ||
condition = Label(condition) | ||
if hasattr(condition, "same_package_label"): | ||
condition = condition.same_package_label("_" + condition.name) | ||
else: | ||
condition = condition.relative("_" + condition.name) | ||
|
||
match_any.append(condition) | ||
|
||
for name_, flag_values_ in create_config_settings.items(): | ||
native.config_setting( | ||
name = name_, | ||
flag_values = flag_values_, | ||
**kwargs | ||
) | ||
|
||
# An alias pointing to an underscore-prefixed config_setting_group | ||
# is used because config_setting_group creates | ||
# `is_{version}_N` targets, which are easily confused with the | ||
# `is_{minor}.{micro}` (dot) targets. | ||
selects.config_setting_group( | ||
name = "_{}_group".format(name), | ||
match_any = match_any, | ||
visibility = ["//visibility:private"], | ||
) | ||
native.alias( | ||
name = name, | ||
actual = "_{}_group".format(name), | ||
visibility = kwargs.get("visibility", []), | ||
) | ||
|
||
def construct_config_settings(name = None): # buildifier: disable=function-docstring | ||
"""Create a 'python_version' config flag and construct all config settings used in rules_python. | ||
|
||
This mainly includes the targets that are used in the toolchain and pip hub | ||
repositories that only match on the 'python_version' flag values. | ||
|
||
Args: | ||
name(str): A dummy name value that is no-op for now. | ||
""" | ||
string_flag( | ||
name = "python_version", | ||
# TODO: The default here should somehow match the MODULE config. Until | ||
# then, use the empty string to indicate an unknown version. This | ||
# also prevents version-unaware targets from inadvertently matching | ||
# a select condition when they shouldn't. | ||
build_setting_default = "", | ||
values = [""] + VERSION_FLAG_VALUES.keys(), | ||
visibility = ["//visibility:public"], | ||
) | ||
|
||
for version, matching_versions in VERSION_FLAG_VALUES.items(): | ||
is_python_config_setting( | ||
name = "is_python_{}".format(version), | ||
python_version = version, | ||
reuse_conditions = { | ||
v: native.package_relative_label("is_python_{}".format(v)) | ||
for v in matching_versions | ||
if v != version | ||
}, | ||
visibility = ["//visibility:public"], | ||
) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.