blob: 10b4d686a759563a61bc55a62442764cd90e79ad [file] [log] [blame]
Ignas Anikeviciusc9c27682024-03-18 16:16:26 +09001# Copyright 2024 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""This module is used to construct the config settings in the BUILD file in this same package.
16"""
17
18load("@bazel_skylib//lib:selects.bzl", "selects")
Richard Levasseur54c9fab2024-08-31 07:19:47 -070019load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
Ignas Anikevicius654b4d52024-09-17 13:12:48 +090020load(":semver.bzl", "semver")
Ignas Anikeviciusc9c27682024-03-18 16:16:26 +090021
Ignas Anikevicius654b4d52024-09-17 13:12:48 +090022_PYTHON_VERSION_FLAG = Label("//python/config_settings:python_version")
Ignas Anikevicius30fc3f92024-10-07 12:22:19 +090023_PYTHON_VERSION_MAJOR_MINOR_FLAG = Label("//python/config_settings:python_version_major_minor")
Ignas Anikeviciusc9c27682024-03-18 16:16:26 +090024
Ignas Anikevicius33fa8452024-10-07 11:07:27 +090025def construct_config_settings(*, name, default_version, versions, minor_mapping): # buildifier: disable=function-docstring
Ignas Anikeviciusc9c27682024-03-18 16:16:26 +090026 """Create a 'python_version' config flag and construct all config settings used in rules_python.
27
28 This mainly includes the targets that are used in the toolchain and pip hub
29 repositories that only match on the 'python_version' flag values.
30
31 Args:
Ignas Anikevicius654b4d52024-09-17 13:12:48 +090032 name: {type}`str` A dummy name value that is no-op for now.
Ignas Anikevicius33fa8452024-10-07 11:07:27 +090033 default_version: {type}`str` the default value for the `python_version` flag.
Ignas Anikevicius654b4d52024-09-17 13:12:48 +090034 versions: {type}`list[str]` A list of versions to build constraint settings for.
35 minor_mapping: {type}`dict[str, str]` A mapping from `X.Y` to `X.Y.Z` python versions.
Ignas Anikeviciusc9c27682024-03-18 16:16:26 +090036 """
Ignas Anikevicius654b4d52024-09-17 13:12:48 +090037 _ = name # @unused
Richard Levasseur54c9fab2024-08-31 07:19:47 -070038 _python_version_flag(
Ignas Anikevicius654b4d52024-09-17 13:12:48 +090039 name = _PYTHON_VERSION_FLAG.name,
Ignas Anikevicius33fa8452024-10-07 11:07:27 +090040 build_setting_default = default_version,
Ignas Anikevicius654b4d52024-09-17 13:12:48 +090041 visibility = ["//visibility:public"],
42 )
43
44 _python_version_major_minor_flag(
45 name = _PYTHON_VERSION_MAJOR_MINOR_FLAG.name,
46 build_setting_default = "",
Ignas Anikeviciusc9c27682024-03-18 16:16:26 +090047 visibility = ["//visibility:public"],
48 )
49
Ignas Anikeviciusfce73542024-06-17 22:05:28 +090050 native.config_setting(
51 name = "is_python_version_unset",
Ignas Anikevicius654b4d52024-09-17 13:12:48 +090052 flag_values = {_PYTHON_VERSION_FLAG: ""},
Ignas Anikeviciusfce73542024-06-17 22:05:28 +090053 visibility = ["//visibility:public"],
54 )
55
Ignas Anikevicius654b4d52024-09-17 13:12:48 +090056 _reverse_minor_mapping = {full: minor for minor, full in minor_mapping.items()}
57 for version in versions:
58 minor_version = _reverse_minor_mapping.get(version)
59 if not minor_version:
60 native.config_setting(
61 name = "is_python_{}".format(version),
62 flag_values = {":python_version": version},
63 visibility = ["//visibility:public"],
64 )
65 continue
66
67 # Also need to match the minor version when using
68 name = "is_python_{}".format(version)
69 native.config_setting(
70 name = "_" + name,
71 flag_values = {":python_version": version},
72 visibility = ["//visibility:public"],
73 )
74
75 # An alias pointing to an underscore-prefixed config_setting_group
76 # is used because config_setting_group creates
77 # `is_{version}_N` targets, which are easily confused with the
78 # `is_{minor}.{micro}` (dot) targets.
79 selects.config_setting_group(
80 name = "_{}_group".format(name),
81 match_any = [
82 ":_is_python_{}".format(version),
83 ":is_python_{}".format(minor_version),
84 ],
85 visibility = ["//visibility:private"],
86 )
87 native.alias(
88 name = name,
89 actual = "_{}_group".format(name),
90 visibility = ["//visibility:public"],
91 )
92
93 # This matches the raw flag value, e.g. --//python/config_settings:python_version=3.8
94 # It's private because matching the concept of e.g. "3.8" value is done
95 # using the `is_python_X.Y` config setting group, which is aware of the
96 # minor versions that could match instead.
97 for minor in minor_mapping.keys():
98 native.config_setting(
99 name = "is_python_{}".format(minor),
100 flag_values = {_PYTHON_VERSION_MAJOR_MINOR_FLAG: minor},
Ignas Anikeviciusc9c27682024-03-18 16:16:26 +0900101 visibility = ["//visibility:public"],
102 )
Richard Levasseur54c9fab2024-08-31 07:19:47 -0700103
104def _python_version_flag_impl(ctx):
105 value = ctx.build_setting_value
Richard Levasseur54c9fab2024-08-31 07:19:47 -0700106 return [
107 # BuildSettingInfo is the original provider returned, so continue to
108 # return it for compatibility
109 BuildSettingInfo(value = value),
110 # FeatureFlagInfo is returned so that config_setting respects the value
111 # as returned by this rule instead of as originally seen on the command
112 # line.
113 # It is also for Google compatibility, which expects the FeatureFlagInfo
114 # provider.
115 config_common.FeatureFlagInfo(value = value),
116 ]
117
118_python_version_flag = rule(
119 implementation = _python_version_flag_impl,
120 build_setting = config.string(flag = True),
Ignas Anikevicius654b4d52024-09-17 13:12:48 +0900121 attrs = {},
122)
123
124def _python_version_major_minor_flag_impl(ctx):
125 input = ctx.attr._python_version_flag[config_common.FeatureFlagInfo].value
126 if input:
127 version = semver(input)
128 value = "{}.{}".format(version.major, version.minor)
129 else:
130 value = ""
131
132 return [config_common.FeatureFlagInfo(value = value)]
133
134_python_version_major_minor_flag = rule(
135 implementation = _python_version_major_minor_flag_impl,
136 build_setting = config.string(flag = False),
Richard Levasseur54c9fab2024-08-31 07:19:47 -0700137 attrs = {
Ignas Anikevicius654b4d52024-09-17 13:12:48 +0900138 "_python_version_flag": attr.label(
139 default = _PYTHON_VERSION_FLAG,
Richard Levasseur54c9fab2024-08-31 07:19:47 -0700140 ),
141 },
142)