• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2019 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"""Bazel Flags."""
16
17load("@rules_android//rules:utils.bzl", "utils")
18
19_BoolFlagInfo = provider(
20    doc = "Provides information about a boolean flag",
21    fields = dict(
22        name = "flag name",
23        value = "flag value",
24        explicit = "whether value was set explicitly",
25    ),
26)
27_BoolFlagGroupInfo = provider(
28    doc = "Provides information about a boolean flag group",
29    fields = dict(
30        name = "group name",
31        value = "group value",
32        flags = "flag names that belong to this group",
33    ),
34)
35_IntFlagInfo = provider(
36    doc = "Provides information about an integer flag",
37    fields = dict(
38        name = "flag name",
39        value = "flag value",
40    ),
41)
42_NativeBoolFlagInfo = provider(
43    doc = "Provides information about a native boolean flag",
44    fields = dict(
45        name = "flag name, the name of the native flag being accessed.",
46        value = "flag value, derived from config_setting targets that access the value",
47    ),
48)
49FlagsInfo = provider(
50    doc = "Provides all flags",
51)
52
53def _native_bool_impl(ctx):
54    return _NativeBoolFlagInfo(
55        name = ctx.label.name,
56        value = ctx.attr.value,
57    )
58
59native_bool_flag = rule(
60    implementation = _native_bool_impl,
61    attrs = dict(
62        value = attr.bool(mandatory = True),
63    ),
64    provides = [_NativeBoolFlagInfo],
65)
66
67def native_bool_flag_macro(name, description):
68    """Provides access to a native boolean flag from Starlark.
69
70    Args:
71      name: The name of the native flag to access.
72      description: The description of the flag.
73    """
74    native.config_setting(
75        name = name + "_on",
76        values = {name: "True"},
77    )
78    native.config_setting(
79        name = name + "_off",
80        values = {name: "False"},
81    )
82    native_bool_flag(
83        name = name,
84        value = select({
85            (":" + name + "_on"): True,
86            (":" + name + "_off"): False,
87        }),
88    )
89
90def _get_bool(v):
91    v = v.lower()
92    if v == "true":
93        return True
94    if v == "false":
95        return False
96    fail("Unknown bool: " + v)
97
98def _bool_impl(ctx):
99    if ctx.label.name in ctx.var:
100        value = _get_bool(ctx.var[ctx.label.name])
101        return _BoolFlagInfo(
102            name = ctx.label.name,
103            value = value,
104            explicit = True,
105        )
106    return _BoolFlagInfo(
107        name = ctx.label.name,
108        value = ctx.attr.default,
109        explicit = False,
110    )
111
112bool_flag = rule(
113    implementation = _bool_impl,
114    attrs = dict(
115        default = attr.bool(
116            mandatory = True,
117        ),
118        description = attr.string(
119            mandatory = True,
120        ),
121    ),
122    provides = [_BoolFlagInfo],
123)
124
125def _bool_group_impl(ctx):
126    if ctx.label.name in ctx.var:
127        value = _get_bool(ctx.var[ctx.label.name])
128        return _BoolFlagGroupInfo(
129            name = ctx.label.name,
130            value = value,
131            flags = [f[_BoolFlagInfo].name for f in ctx.attr.flags],
132        )
133    return _BoolFlagGroupInfo(
134        name = ctx.label.name,
135        value = ctx.attr.default,
136        flags = [f[_BoolFlagInfo].name for f in ctx.attr.flags],
137    )
138
139bool_flag_group = rule(
140    implementation = _bool_group_impl,
141    attrs = dict(
142        default = attr.bool(
143            mandatory = True,
144        ),
145        description = attr.string(
146            mandatory = True,
147        ),
148        flags = attr.label_list(
149            mandatory = True,
150            providers = [_BoolFlagInfo],
151        ),
152    ),
153    provides = [_BoolFlagGroupInfo],
154)
155
156def _int_impl(ctx):
157    if ctx.label.name in ctx.var:
158        value = int(ctx.var[ctx.label.name])
159    else:
160        value = ctx.attr.default
161    return _IntFlagInfo(
162        name = ctx.label.name,
163        value = value,
164    )
165
166int_flag = rule(
167    implementation = _int_impl,
168    attrs = dict(
169        default = attr.int(
170            mandatory = True,
171        ),
172        description = attr.string(
173            mandatory = True,
174        ),
175    ),
176    provides = [_IntFlagInfo],
177)
178
179def _flags_impl_internal(bool_flags, bool_flag_groups, int_flags, native_bool_flags):
180    flags = dict()
181
182    # For each group, set all flags to the group value
183    for fg in bool_flag_groups:
184        for f in fg.flags:
185            if f in flags:
186                fail("Flag '%s' referenced in multiple flag groups" % f)
187            flags[f] = fg.value
188
189    # Set booleans
190    for b in bool_flags:
191        # Always set explicitly specified flags
192        if b.explicit:
193            flags[b.name] = b.value
194            # If not explicit, only set when not set by a group
195
196        elif b.name not in flags:
197            flags[b.name] = b.value
198
199    # Set ints
200    for i in int_flags:
201        flags[i.name] = i.value
202
203    # Set native bool flags
204    for n in native_bool_flags:
205        if n.name in flags:
206            fail("Flag '%s' defined as both native and non-native flag type" % n.name)
207        flags[n.name] = n.value
208
209    return FlagsInfo(**flags)
210
211def _flags_impl(ctx):
212    return _flags_impl_internal(
213        utils.collect_providers(_BoolFlagInfo, ctx.attr.targets),
214        utils.collect_providers(_BoolFlagGroupInfo, ctx.attr.targets),
215        utils.collect_providers(_IntFlagInfo, ctx.attr.targets),
216        utils.collect_providers(_NativeBoolFlagInfo, ctx.attr.targets),
217    )
218
219flags_rule = rule(
220    implementation = _flags_impl,
221    attrs = dict(
222        targets = attr.label_list(),
223    ),
224)
225
226def _flags_macro():
227    flags_rule(
228        name = "flags",
229        targets = native.existing_rules().keys(),
230        visibility = ["//visibility:public"],
231    )
232
233def _get_flags(ctx):
234    return ctx.attr._flags[FlagsInfo]
235
236flags = struct(
237    DEFINE_bool = bool_flag,
238    DEFINE_bool_group = bool_flag_group,
239    DEFINE_int = int_flag,
240    EXPOSE_native_bool = native_bool_flag_macro,
241    FLAGS = _flags_macro,
242    FlagsInfo = FlagsInfo,
243    get = _get_flags,
244)
245
246exported_for_test = struct(
247    BoolFlagGroupInfo = _BoolFlagGroupInfo,
248    BoolFlagInfo = _BoolFlagInfo,
249    IntFlagInfo = _IntFlagInfo,
250    NativeBoolFlagInfo = _NativeBoolFlagInfo,
251    bool_impl = _bool_impl,
252    flags_impl_internal = _flags_impl_internal,
253    int_impl = _int_impl,
254    native_bool_flag_macro = native_bool_flag_macro,
255)
256