|
16 | 16 | """
|
17 | 17 |
|
18 | 18 | load("@bazel_skylib//lib:selects.bzl", "selects")
|
19 |
| -load("@bazel_skylib//rules:common_settings.bzl", "string_flag") |
20 |
| -load("//python:versions.bzl", "MINOR_MAPPING") |
| 19 | +load("//python:versions.bzl", "MINOR_MAPPING", "TOOL_VERSIONS") |
21 | 20 |
|
22 |
| -def construct_config_settings(name, python_versions): |
23 |
| - """Constructs a set of configs for all Python versions. |
| 21 | +def _ver_key(s): |
| 22 | + _, _, s = s.partition(".") # All are 3 |
| 23 | + minor, _, s = s.partition(".") |
| 24 | + micro, _, s = s.partition(".") |
| 25 | + return 100 * int(minor) + int(micro) |
| 26 | + |
| 27 | +def _flag_values(python_versions): |
| 28 | + """Construct a map of python_version to a list of toolchain values. |
| 29 | +
|
| 30 | + This mapping maps the concept of a config setting to a list of compatible toolchain versions. |
| 31 | + For using this in the code, the VERSION_FLAG_VALUES should be used instead. |
24 | 32 |
|
25 | 33 | Args:
|
26 |
| - name: str, unused; only specified to satisfy buildifier lint checks |
27 |
| - and allow programatic modification of the target. |
28 |
| - python_versions: list of all (x.y.z) Python versions supported by rules_python. |
| 34 | + python_versions: A list of all versions. |
| 35 | +
|
| 36 | + Returns: |
| 37 | + A map with config settings as keys and values as extra flag values to be included in |
| 38 | + the config_setting_group if they should be also matched, which is used for generating |
| 39 | + correct entries for matching the latest 3.8 version, etc. |
29 | 40 | """
|
30 | 41 |
|
31 | 42 | # Maps e.g. "3.8" -> ["3.8.1", "3.8.2", etc]
|
32 |
| - minor_to_micro_versions = {} |
33 |
| - |
34 |
| - allowed_flag_values = [] |
35 |
| - for micro_version in python_versions: |
36 |
| - minor, _, _ = micro_version.rpartition(".") |
37 |
| - minor_to_micro_versions.setdefault(minor, []).append(micro_version) |
38 |
| - allowed_flag_values.append(micro_version) |
39 |
| - |
40 |
| - allowed_flag_values.extend(list(minor_to_micro_versions)) |
41 |
| - |
42 |
| - string_flag( |
43 |
| - name = "python_version", |
44 |
| - # TODO: The default here should somehow match the MODULE config. Until |
45 |
| - # then, use the empty string to indicate an unknown version. This |
46 |
| - # also prevents version-unaware targets from inadvertently matching |
47 |
| - # a select condition when they shouldn't. |
48 |
| - build_setting_default = "", |
49 |
| - values = [""] + sorted(allowed_flag_values), |
50 |
| - visibility = ["//visibility:public"], |
51 |
| - ) |
| 43 | + ret = {} |
| 44 | + |
| 45 | + for micro_version in sorted(python_versions, key = _ver_key): |
| 46 | + minor_version, _, _ = micro_version.rpartition(".") |
52 | 47 |
|
53 |
| - for minor_version, micro_versions in minor_to_micro_versions.items(): |
54 | 48 | # This matches the raw flag value, e.g. --//python/config_settings:python_version=3.8
|
55 | 49 | # It's private because matching the concept of e.g. "3.8" value is done
|
56 | 50 | # using the `is_python_X.Y` config setting group, which is aware of the
|
57 | 51 | # minor versions that could match instead.
|
58 |
| - equals_minor_version_name = "_python_version_flag_equals_" + minor_version |
59 |
| - native.config_setting( |
60 |
| - name = equals_minor_version_name, |
61 |
| - flag_values = {":python_version": minor_version}, |
62 |
| - ) |
63 |
| - matches_minor_version_names = [equals_minor_version_name] |
| 52 | + ret.setdefault(minor_version, []).append(micro_version) |
64 | 53 |
|
| 54 | + # Ensure that is_python_3.9.8 is matched if python_version is set |
| 55 | + # to 3.9 if MINOR_MAPPING points to 3.9.8 |
65 | 56 | default_micro_version = MINOR_MAPPING[minor_version]
|
| 57 | + ret[micro_version] = [minor_version] if default_micro_version == micro_version else [] |
| 58 | + |
| 59 | + return ret |
| 60 | + |
| 61 | +VERSION_FLAG_VALUES = _flag_values(TOOL_VERSIONS.keys()) |
| 62 | + |
| 63 | +def is_python_config_setting(name, flag_values, match_extra, **kwargs): |
| 64 | + """Create a config setting for matching 'python_version' configuration flag. |
66 | 65 |
|
67 |
| - for micro_version in micro_versions: |
68 |
| - is_micro_version_name = "is_python_" + micro_version |
69 |
| - if default_micro_version != micro_version: |
70 |
| - native.config_setting( |
71 |
| - name = is_micro_version_name, |
72 |
| - flag_values = {":python_version": micro_version}, |
73 |
| - visibility = ["//visibility:public"], |
74 |
| - ) |
75 |
| - matches_minor_version_names.append(is_micro_version_name) |
76 |
| - continue |
77 |
| - |
78 |
| - # Ensure that is_python_3.9.8 is matched if python_version is set |
79 |
| - # to 3.9 if MINOR_MAPPING points to 3.9.8 |
80 |
| - equals_micro_name = "_python_version_flag_equals_" + micro_version |
81 |
| - native.config_setting( |
82 |
| - name = equals_micro_name, |
83 |
| - flag_values = {":python_version": micro_version}, |
84 |
| - ) |
85 |
| - |
86 |
| - # An alias pointing to an underscore-prefixed config_setting_group |
87 |
| - # is used because config_setting_group creates |
88 |
| - # `is_{minor}_N` targets, which are easily confused with the |
89 |
| - # `is_{minor}.{micro}` (dot) targets. |
90 |
| - selects.config_setting_group( |
91 |
| - name = "_" + is_micro_version_name, |
92 |
| - match_any = [ |
93 |
| - equals_micro_name, |
94 |
| - equals_minor_version_name, |
95 |
| - ], |
96 |
| - ) |
97 |
| - native.alias( |
98 |
| - name = is_micro_version_name, |
99 |
| - actual = "_" + is_micro_version_name, |
100 |
| - visibility = ["//visibility:public"], |
101 |
| - ) |
102 |
| - matches_minor_version_names.append(equals_micro_name) |
103 |
| - |
104 |
| - # This is prefixed with an underscore to prevent confusion due to how |
105 |
| - # config_setting_group is implemented and how our micro-version targets |
106 |
| - # are named. config_setting_group will generate targets like |
107 |
| - # "is_python_3.10_1" (where the `_N` suffix is len(match_any). |
108 |
| - # Meanwhile, the micro-version tarets are named "is_python_3.10.1" -- |
109 |
| - # just a single dot vs underscore character difference. |
110 |
| - selects.config_setting_group( |
111 |
| - name = "_is_python_" + minor_version, |
112 |
| - match_any = matches_minor_version_names, |
| 66 | + This function is mainly intended for internal use within the `whl_library` and `pip_parse` |
| 67 | + machinery. |
| 68 | +
|
| 69 | + Args: |
| 70 | + name: name for the target that will be created to be used in select statements. |
| 71 | + flag_values: The flag_values in the `config_setting`. |
| 72 | + match_extra: The extra flag values that we should matched when the `name` is used |
| 73 | + in the config setting. You can either pass a list of labels that will be included |
| 74 | + in the bazel-skylib selects.config_setting_group match_any clause or it can be a |
| 75 | + dict[str, dic], where the keys are the names of the extra config_setting targets |
| 76 | + to be created and the value is the `flag_values` attribute. |
| 77 | + **kwargs: extra kwargs passed to the `config_setting` |
| 78 | + """ |
| 79 | + visibility = kwargs.pop("visibility", []) |
| 80 | + if not match_extra: |
| 81 | + native.config_setting( |
| 82 | + name = name, |
| 83 | + flag_values = flag_values, |
| 84 | + visibility = visibility, |
| 85 | + **kwargs |
113 | 86 | )
|
| 87 | + return |
| 88 | + |
| 89 | + create_config_settings = {"_" + name: flag_values} |
| 90 | + match_any = ["_" + name] |
| 91 | + if type(match_extra) == type([]): |
| 92 | + match_any.extend(match_extra) |
| 93 | + elif type(match_extra) == type({}): |
| 94 | + match_any.extend(match_extra.keys()) |
| 95 | + create_config_settings.update(match_extra) |
| 96 | + else: |
| 97 | + fail("unsupported match_extra type, can be either a list or a dict of dicts") |
114 | 98 |
|
115 |
| - native.alias( |
116 |
| - name = "is_python_" + minor_version, |
117 |
| - actual = "_is_python_" + minor_version, |
118 |
| - visibility = ["//visibility:public"], |
| 99 | + # Create all of the necessary config setting values for the config_setting_group |
| 100 | + for name_, flag_values_ in create_config_settings.items(): |
| 101 | + native.config_setting( |
| 102 | + name = name_, |
| 103 | + flag_values = flag_values_, |
| 104 | + # We need to pass the visibility here because of how `config_setting_group` is |
| 105 | + # implemented, it is using the internal aliases here, hence the need for making |
| 106 | + # them with the same visibility as the `alias` itself. |
| 107 | + visibility = visibility, |
| 108 | + **kwargs |
119 | 109 | )
|
| 110 | + |
| 111 | + # An alias pointing to an underscore-prefixed config_setting_group |
| 112 | + # is used because config_setting_group creates |
| 113 | + # `is_{version}_N` targets, which are easily confused with the |
| 114 | + # `is_{minor}.{micro}` (dot) targets. |
| 115 | + selects.config_setting_group( |
| 116 | + name = "_{}_group".format(name), |
| 117 | + match_any = match_any, |
| 118 | + visibility = ["//visibility:private"], |
| 119 | + ) |
| 120 | + native.alias( |
| 121 | + name = name, |
| 122 | + actual = "_{}_group".format(name), |
| 123 | + visibility = visibility, |
| 124 | + ) |
0 commit comments