You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

256 lines
6.7 KiB

# Copyright 2019 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.
"""Bazel Flags."""
load("@rules_android//rules:utils.bzl", "utils")
_BoolFlagInfo = provider(
doc = "Provides information about a boolean flag",
fields = dict(
name = "flag name",
value = "flag value",
explicit = "whether value was set explicitly",
),
)
_BoolFlagGroupInfo = provider(
doc = "Provides information about a boolean flag group",
fields = dict(
name = "group name",
value = "group value",
flags = "flag names that belong to this group",
),
)
_IntFlagInfo = provider(
doc = "Provides information about an integer flag",
fields = dict(
name = "flag name",
value = "flag value",
),
)
_NativeBoolFlagInfo = provider(
doc = "Provides information about a native boolean flag",
fields = dict(
name = "flag name, the name of the native flag being accessed.",
value = "flag value, derived from config_setting targets that access the value",
),
)
FlagsInfo = provider(
doc = "Provides all flags",
)
def _native_bool_impl(ctx):
return _NativeBoolFlagInfo(
name = ctx.label.name,
value = ctx.attr.value,
)
native_bool_flag = rule(
implementation = _native_bool_impl,
attrs = dict(
value = attr.bool(mandatory = True),
),
provides = [_NativeBoolFlagInfo],
)
def native_bool_flag_macro(name, description):
"""Provides access to a native boolean flag from Starlark.
Args:
name: The name of the native flag to access.
description: The description of the flag.
"""
native.config_setting(
name = name + "_on",
values = {name: "True"},
)
native.config_setting(
name = name + "_off",
values = {name: "False"},
)
native_bool_flag(
name = name,
value = select({
(":" + name + "_on"): True,
(":" + name + "_off"): False,
}),
)
def _get_bool(v):
v = v.lower()
if v == "true":
return True
if v == "false":
return False
fail("Unknown bool: " + v)
def _bool_impl(ctx):
if ctx.label.name in ctx.var:
value = _get_bool(ctx.var[ctx.label.name])
return _BoolFlagInfo(
name = ctx.label.name,
value = value,
explicit = True,
)
return _BoolFlagInfo(
name = ctx.label.name,
value = ctx.attr.default,
explicit = False,
)
bool_flag = rule(
implementation = _bool_impl,
attrs = dict(
default = attr.bool(
mandatory = True,
),
description = attr.string(
mandatory = True,
),
),
provides = [_BoolFlagInfo],
)
def _bool_group_impl(ctx):
if ctx.label.name in ctx.var:
value = _get_bool(ctx.var[ctx.label.name])
return _BoolFlagGroupInfo(
name = ctx.label.name,
value = value,
flags = [f[_BoolFlagInfo].name for f in ctx.attr.flags],
)
return _BoolFlagGroupInfo(
name = ctx.label.name,
value = ctx.attr.default,
flags = [f[_BoolFlagInfo].name for f in ctx.attr.flags],
)
bool_flag_group = rule(
implementation = _bool_group_impl,
attrs = dict(
default = attr.bool(
mandatory = True,
),
description = attr.string(
mandatory = True,
),
flags = attr.label_list(
mandatory = True,
providers = [_BoolFlagInfo],
),
),
provides = [_BoolFlagGroupInfo],
)
def _int_impl(ctx):
if ctx.label.name in ctx.var:
value = int(ctx.var[ctx.label.name])
else:
value = ctx.attr.default
return _IntFlagInfo(
name = ctx.label.name,
value = value,
)
int_flag = rule(
implementation = _int_impl,
attrs = dict(
default = attr.int(
mandatory = True,
),
description = attr.string(
mandatory = True,
),
),
provides = [_IntFlagInfo],
)
def _flags_impl_internal(bool_flags, bool_flag_groups, int_flags, native_bool_flags):
flags = dict()
# For each group, set all flags to the group value
for fg in bool_flag_groups:
for f in fg.flags:
if f in flags:
fail("Flag '%s' referenced in multiple flag groups" % f)
flags[f] = fg.value
# Set booleans
for b in bool_flags:
# Always set explicitly specified flags
if b.explicit:
flags[b.name] = b.value
# If not explicit, only set when not set by a group
elif b.name not in flags:
flags[b.name] = b.value
# Set ints
for i in int_flags:
flags[i.name] = i.value
# Set native bool flags
for n in native_bool_flags:
if n.name in flags:
fail("Flag '%s' defined as both native and non-native flag type" % n.name)
flags[n.name] = n.value
return FlagsInfo(**flags)
def _flags_impl(ctx):
return _flags_impl_internal(
utils.collect_providers(_BoolFlagInfo, ctx.attr.targets),
utils.collect_providers(_BoolFlagGroupInfo, ctx.attr.targets),
utils.collect_providers(_IntFlagInfo, ctx.attr.targets),
utils.collect_providers(_NativeBoolFlagInfo, ctx.attr.targets),
)
flags_rule = rule(
implementation = _flags_impl,
attrs = dict(
targets = attr.label_list(),
),
)
def _flags_macro():
flags_rule(
name = "flags",
targets = native.existing_rules().keys(),
visibility = ["//visibility:public"],
)
def _get_flags(ctx):
return ctx.attr._flags[FlagsInfo]
flags = struct(
DEFINE_bool = bool_flag,
DEFINE_bool_group = bool_flag_group,
DEFINE_int = int_flag,
EXPOSE_native_bool = native_bool_flag_macro,
FLAGS = _flags_macro,
FlagsInfo = FlagsInfo,
get = _get_flags,
)
exported_for_test = struct(
BoolFlagGroupInfo = _BoolFlagGroupInfo,
BoolFlagInfo = _BoolFlagInfo,
IntFlagInfo = _IntFlagInfo,
NativeBoolFlagInfo = _NativeBoolFlagInfo,
bool_impl = _bool_impl,
flags_impl_internal = _flags_impl_internal,
int_impl = _int_impl,
native_bool_flag_macro = native_bool_flag_macro,
)