blob: 10b4d686a759563a61bc55a62442764cd90e79ad [file] [log] [blame]
# 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", "BuildSettingInfo")
load(":semver.bzl", "semver")
_PYTHON_VERSION_FLAG = Label("//python/config_settings:python_version")
_PYTHON_VERSION_MAJOR_MINOR_FLAG = Label("//python/config_settings:python_version_major_minor")
def construct_config_settings(*, name, default_version, versions, minor_mapping): # 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: {type}`str` A dummy name value that is no-op for now.
default_version: {type}`str` the default value for the `python_version` flag.
versions: {type}`list[str]` A list of versions to build constraint settings for.
minor_mapping: {type}`dict[str, str]` A mapping from `X.Y` to `X.Y.Z` python versions.
"""
_ = name # @unused
_python_version_flag(
name = _PYTHON_VERSION_FLAG.name,
build_setting_default = default_version,
visibility = ["//visibility:public"],
)
_python_version_major_minor_flag(
name = _PYTHON_VERSION_MAJOR_MINOR_FLAG.name,
build_setting_default = "",
visibility = ["//visibility:public"],
)
native.config_setting(
name = "is_python_version_unset",
flag_values = {_PYTHON_VERSION_FLAG: ""},
visibility = ["//visibility:public"],
)
_reverse_minor_mapping = {full: minor for minor, full in minor_mapping.items()}
for version in versions:
minor_version = _reverse_minor_mapping.get(version)
if not minor_version:
native.config_setting(
name = "is_python_{}".format(version),
flag_values = {":python_version": version},
visibility = ["//visibility:public"],
)
continue
# Also need to match the minor version when using
name = "is_python_{}".format(version)
native.config_setting(
name = "_" + name,
flag_values = {":python_version": version},
visibility = ["//visibility:public"],
)
# 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 = [
":_is_python_{}".format(version),
":is_python_{}".format(minor_version),
],
visibility = ["//visibility:private"],
)
native.alias(
name = name,
actual = "_{}_group".format(name),
visibility = ["//visibility:public"],
)
# 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.
for minor in minor_mapping.keys():
native.config_setting(
name = "is_python_{}".format(minor),
flag_values = {_PYTHON_VERSION_MAJOR_MINOR_FLAG: minor},
visibility = ["//visibility:public"],
)
def _python_version_flag_impl(ctx):
value = ctx.build_setting_value
return [
# BuildSettingInfo is the original provider returned, so continue to
# return it for compatibility
BuildSettingInfo(value = value),
# FeatureFlagInfo is returned so that config_setting respects the value
# as returned by this rule instead of as originally seen on the command
# line.
# It is also for Google compatibility, which expects the FeatureFlagInfo
# provider.
config_common.FeatureFlagInfo(value = value),
]
_python_version_flag = rule(
implementation = _python_version_flag_impl,
build_setting = config.string(flag = True),
attrs = {},
)
def _python_version_major_minor_flag_impl(ctx):
input = ctx.attr._python_version_flag[config_common.FeatureFlagInfo].value
if input:
version = semver(input)
value = "{}.{}".format(version.major, version.minor)
else:
value = ""
return [config_common.FeatureFlagInfo(value = value)]
_python_version_major_minor_flag = rule(
implementation = _python_version_major_minor_flag_impl,
build_setting = config.string(flag = False),
attrs = {
"_python_version_flag": attr.label(
default = _PYTHON_VERSION_FLAG,
),
},
)