blob: 009b030b4466acde62ccb9907ae35393f98535db [file] [log] [blame] [view]
gregcec44a87b2018-06-08 12:07:03 -07001---
2layout: documentation
3title: Configurable Build Attributes
4---
5
6# Configurable Build Attributes
7
8### Contents
9* [Example](#example)
10* [Configuration Conditions](#configuration-conditions)
11* [Defaults](#defaults)
12* [Custom Keys](#custom-keys)
13* [Platforms](#platforms)
14* [Short Keys](#short-keys)
15* [Multiple Selects](#multiple-selects)
16* [OR Chaining](#or-chaining)
17* [Custom Error Messages](#custom-error-messages)
18* [Skylark Compatibility](#skylark)
19* [Bazel Query and Cquery](#query)
20* [FAQ](#faq)
21 * [Why doesn't select() work in Skylark](#skylark-macros-select)
22 * [Why does select() always return true in Skylark?](#boolean-select)
23 * [Can I read select() like a dict in Skylark?](#inspectable-select)
24
25 
26
27**_Configurable attributes_**, commonly known as [`select()`]
28(be/functions.html#select), is a Bazel feature that lets users toggle the values
29of BUILD rule attributes at the command line.
30
31This can be used, for example, to write multiplatform libraries that
32automatically choose the right implementation or feature-configurable binaries
33that can be custom assembled at build time.
34
35## Example
36
37```sh
38//myapp/BUILD:
39cc_binary(
40 name = "mybinary",
41 srcs = ["main.cc"],
42 deps = select({
43 ":arm_build": [":arm_lib"],
44 ":x86_debug_build": [":x86_dev_lib"],
45 "//conditions:default": [":generic_lib"]
46 })
47)
48
49config_setting(
50 name = "arm_build",
51 values = { "cpu": "arm" }
52)
53
54config_setting(
55 name = "x86_debug_build",
56 values = {
57 "cpu": "x86",
58 "compilation_mode": "dbg"
59 }
60)
61```
62
63This declares a `cc_binary` that "chooses" its deps based on the flags at the
64command line. Specficially, `deps` becomes:
65
66<table>
67 <tr style="background: #E9E9E9; font-weight: bold">
68 <td>Command</td>
69 <td>deps =</td>
70 </tr>
71 <tr>
72 <td><code>bazel build //myapp:mybinary --cpu=arm</code></td>
73 <td><code>[":arm_lib"]</code></td>
74 </tr>
75 <tr>
76 <td><code>bazel build //myapp:mybinary --c dbg --cpu=x86</code></td>
77 <td><code>[":x86_dev_lib"]</code></td>
78 </tr>
79 <tr>
80 <td><code>bazel build //myapp:mybinary --cpu=ppc</code></td>
81 <td><code>[":generic_lib"]</code></td>
82 </tr>
83 <tr>
84 <td><code>bazel build //myapp:mybinary -c dbg --cpu=ppc</code></td>
85 <td><code>[":generic_lib"]</code></td>
86 </tr>
87</table>
88&nbsp;
89
90`select()` turns any attribute into a dictionary that maps _configuration
91conditions_ to desired values. Configuration conditions are build labels that
92reference [`config_setting`](be/general.html#config_setting) rules. Values are
93any value the attribute can normally take.
94
95Matches must be unambiguous: either exactly one condition must match or, if
96multiple conditions match, one's `values` must be a strict superset of all
97others' (for example, `values = {"cpu": "x86", "compilation_mode": "dbg"}` is an
98unambiguous specialization of `values = {"cpu": "x86"}`). The built-in condition
99[`//conditions:default`](#defaults) automatically matches when nothing else
100does.
101
102This example uses `deps`. But `select()` works just as well on `srcs`,
103`resources`, `cmd`, or practically any other attribute. Only a small number of
104attributes are *non-configurable*, and those are clearly [annotated]
105(be/general.html#config_setting.values).
106
107## Configuration Conditions
108
109Each key in a configurable attribute is a label reference to a
110[`config_setting`](be/general.html#config_setting) rule. This is just a
111collection of expected command line flag settings. By encapsulating these in a
112rule, it's easy to maintain "standard" conditions that can be referenced across
113rules and BUILD files.
114
115The core `config_setting` syntax is:
116
117```sh
118config_setting(
119 name = "meaningful_condition_name",
120 values = {
121 "flag1": "expected_value1",
122 "flag2": "expected_value2",
123 ...
124 }
125)
126```
127
128`flagN` is an arbitrary Bazel command line flag. `value` is the expected value
129for that flag. A `config_setting` matches when *all* of its flags match.
130
131`values` entries use the same parsing logic as at the actual command line. This
132means:
133
134* `values = { "compilation_mode": "opt" }` matches `bazel build -c opt ...`
135* `values = { "java_header_compilation": "true" }` matches `bazel build
136--java_header_compilation=1 ...`
137* `values = { "java_header_compilation": "0" }` matches `bazel build
138--nojava_header_compilation ...`
139
140`config_setting` only works with flags that affect build rule output. For
141example, [`--show_progress`](user-manual.html#flag--show_progress) isn't allowed
142because this only affects how Bazel reports progress to the user.
143
144`config_setting` semantics are intentionally simple. For example, there's no
145direct support for `OR` chaining (although a
146[Skylark convenience function](#or-chaining) provides this). Consider writing
147Skylark macros for complicated flag logic.
148
149## Defaults
150
151The built-in condition `//conditions:default` matches when no other condition
152matches.
153
154Because of the "exactly one match" rule, a configurable attribute with no match
155and no default condition triggers a `"no matching conditions"` error. This can
156protect against silent failures from unexpected build flags:
157
158```sh
159//foo:
160config_setting(
161 name = "foobar",
162 values = { "define": "foo=bar" }
163)
164
165cc_library(
166 name = "my_lib",
167 srcs = select({
168 ":foobar": ["foobar_lib.cc"],
169 })
170)
171```
172
173```sh
174$ bazel build //foo:my_lib --define foo=baz
175ERROR: Configurable attribute "srcs" doesn't match this configuration (would
176a default condition help?).
177Conditions checked:
178 //foo:foobar
179```
180
181`select()` can include a [`no_match_error`](#custom-error-messages) for custom
182failure messages.
183
184## Custom Keys
185
186Since `config_setting` currently only supports built-in Bazel flags, the level
187of custom conditioning it can support is limited. For example, there's no Bazel
188flag for `IncludeSpecialProjectFeatureX`.
189
190Plans for [truly custom flags]
191(https://docs.google.com/document/d/1vc8v-kXjvgZOdQdnxPTaV0rrLxtP2XwnD2tAZlYJOqw/edit?usp=sharing)
192are underway. In the meantime, [`--define`](user-manual.html#flag--define) is
193the best approach for these purposes.
194`--define` is a bit awkward to use and wasn't originally designed for this
195purpose. We recommend using it sparingly until true custom flags are available.
196For example, don't use `--define` to specify multiple variants of top-level
197binary. Just use multiple rules instead.
198
199To trigger an arbitrary condition with `--define`, write
200
201```sh
202config_setting(
203 name = "bar",
204 values = { "define": "foo=bar" }
205)
206
207config_setting(
208 name = "baz",
209 values = { "define": "foo=baz" }
210)
211```
212
213and run `$ bazel build //my:target --define foo=baz`.
214
215The `values` attribute can't contain multiple `define`s. This is
216because each instance has the same dictionary key. To solve this, use
217`define_values`:
218
219```sh
220config_setting(
221 name = "bar_and_baz",
222 define_values = {
223 "foo": "bar", # matches --define foo=bar
224 "baz": "bat", # matches --define baz=bat
225 }
226)
227```
228
229When `define`s appear in both `values` and `define_values`, all must match for
230the `config_setting` to match.
231
232## Platforms
233
234While the ability to specify multiple flags on the command line provides
235flexibility, it can also be burdensome to individually set each `--cpu`,
236`-crosstool_top`, etc. flag every time you want to build a target. [Platforms]
237(https://docs.bazel.build/versions/master/platforms.html) allow you to
238consolidate these into simple bundles.
239
240```sh
241sh_binary(
242 name = "my_rocks_rule",
243 srcs = select({
244 ":basalt" : ["pyroxene.sh"],
245 ":marble" : ["calcite.sh"],
246 "//conditions:default": ["feldspar.sh"]
247 })
248)
249
250config_setting(
251 name = "basalt",
252 constraint_values = [
253 ":igneous",
254 ":black"
255 ]
256)
257
258config_setting(
259 name = "marble",
260 constraint_values = [
261 ":white",
262 ":metamorphic"
263 ":smooth"
264 ]
265)
266
267constraint_setting(name = "color")
268constraint_value(name = "black", constraint_setting = "color")
269constraint_value(name = "white", constraint_setting = "color")
270constraint_setting(name = "texture")
271constraint_value(name = "smooth", constraint_setting = "texture")
272constraint_setting(name = "type")
273constraint_value(name = "igneous", constraint_setting = "type")
274constraint_value(name = "metamorphic", constraint_setting = "type")
275
276platform(
277 name = "basalt_platform",
278 constraint_values = [
279 ":black",
280 ":igneous"]
281)
282
283platform(
284 name = "marble_platform",
285 constraint_values = [
286 ":white",
287 ":smooth"
288 ":metamorphic"
289 ]
290)
291```
292
293The `platform` specified on the command line matches a `config_setting` that
294contains the same set (or a superset) of `constraint_values` and triggers
295that `config_setting` as a match in the `select()` statement.
296
297For example, in order to set the `srcs` attribute of `my_rocks_rule` to
298`calcite.sh`, simply run
299
300```sh
301bazel build my_app:my_rocks_rule --platforms=marble_platform
302```
303
304Without platforms, this might look something like
305
306```sh
307bazel build my_app:my_rocks_rule --define color=light --define texture=smooth --define type=metamorphic
308```
309Platforms are still under development. See the [documentation]
310(https://docs.bazel.build/versions/master/platforms.html) and [roadmap]
311(https://docs.google.com/document/d/1_clxJHyUylwYjmQ9jQfWr-eZeLLTUZL6onfvPV7CMoI/edit?usp=sharing)
312for details.
313
314## Short Keys
315
316Since configuration keys are rule labels, their names can get long and unwieldy.
317This can be mitigated with local variable definitions:
318
319Before:
320
321```sh
322sh_binary(
323 name = "my_rule",
324 srcs = select({
325 "//my/project/my/team/configs:config1": ["my_rule_1.sh"],
326 "//my/project/my/team/configs:config2": ["my_rule_2.sh"],
327 })
328)
329```
330
331After:
332
333```sh
334CONFIG1="//my/project/my/team/configs:config1"
335CONFIG2="//my/project/my/team/configs:config2"
336
337sh_binary(
338 name = "my_rule",
339 srcs = select({
340 CONFIG1: ["my_rule_1.sh"],
341 CONFIG2: ["my_rule_2.sh"],
342 })
343)
344```
345
346
347For more complex expressions, use [Skylark macros](skylark/macros.md):
348
349Before:
350
351```sh
352//foo/BUILD
353genrule(
354 name = "my_rule",
355 srcs = [],
356 outs = ["my_rule.out"],
357 cmd = select({
358 "//my/project/my/team/configs/config1": "echo custom val: this > $@",
359 "//my/project/my/team/configs/config2": "echo custom val: that > $@",
360 "//conditions:default": "echo default output > $@"
361 })
362)
363```
364
365After:
366
367```sh
368//foo/genrule_select.bzl:
369def select_echo(input_dict):
370 echo_cmd = "echo %s > $@"
371 out_dict = {"//conditions:default": echo_cmd % "default output" }
372 for (key, val) in input_dict.items():
373 cmd = echo_cmd % ("custom val: " + val)
374 out_dict["//my/project/my/team/configs/config" + key] = cmd
375 return select(out_dict)
376```
377
378```sh
379//foo/BUILD:
380load("//foo:genrule_select.bzl", "select_echo")
381genrule(
382 name = "my_rule",
383 srcs = [],
384 outs = ["my_rule.out"],
385 cmd = select_echo({
386 "1": "this",
387 "2": "that",
388 })
389)
390```
391
392## Multiple Selects
393
394`select` can appear multiple times in the same attribute:
395
396```sh
397sh_binary(
398 name = "my_rule",
399 srcs = ["always_include.sh"]
400 + select({
401 ":armeabi_mode": ["armeabi_src.sh"],
402 ":x86_mode": ["x86_src.sh"],
403 })
404 + select({
405 ":opt_mode": ["opt_extras.sh"],
406 ":dbg_mode": ["dbg_extras.sh"],
407 })
408)
409```
410
411`select` cannot appear inside another `select` (i.e. *`AND` chaining*). If you
412need to `AND` selects together, either use an intermediate rule:
413
414```sh
415sh_binary
416 name = "my_rule",
417 srcs = ["always_include.sh"],
418 deps = select({
419 ":armeabi_mode": [":armeabi_lib"],
420 ...
421 })
422)
423
424sh_library(
425 name = "armeabi_lib",
426 srcs = select({
427 ":opt_mode": ["armeabi_with_opt.sh"],
428 ...
429 })
430)
431```
432
433or write a [Skylark macro](skylark/macros.md) to do the same thing
434automatically.
435
436This approach doesn't work for non-deps attributes (like
437[genrule:cmd](be/general.html#genrule.cmd)). For these, extra `config_settings`
438may be necessary:
439
440```sh
441config_setting(
442 name = "armeabi_and_opt",
443 values = {
444 "cpu": "armeabi",
445 "compilation_mode": "opt"
446 }
447)
448```
449
450
451## OR Chaining
452
453Consider the following:
454
455```sh
456sh_binary
457 name = "my_rule",
458 srcs = ["always_include.sh"],
459 deps = select({
460 ":config1": [":standard_lib"],
461 ":config2": [":standard_lib"],
462 ":config3": [":standard_lib"],
463 ":config4": [":special_lib"],
464 })
465)
466```
467
468Most conditions evaluate to the same dep. But this syntax is verbose, hard to
469maintain, and refactoring-unfriendly. It would be nice to not have to repeat
470`[":standard_lib"]` over and over.
471
472One option is to predefine the declaration as a BUILD variable:
473
474```python
475STANDARD_DEP = [":standard_lib"]
476
477sh_binary
478 name = "my_rule",
479 srcs = ["always_include.sh"],
480 deps = select({
481 ":config1": STANDARD_DEP,
482 ":config2": STANDARD_DEP,
483 ":config3": STANDARD_DEP,
484 ":config4": [":special_lib"],
485 })
486)
487```
488
489This makes it easier to manage the dependency. But it still adds unnecessary
490duplication.
491
492`select()` doesn't support native syntax for `OR`ed conditions. For this, use
493the [Skylib](https://github.com/bazelbuild/bazel-skylib) utility [`selects`]
494(https://github.com/bazelbuild/bazel-skylib/blob/master/lib/selects.bzl).
495
496```sh
497load("@bazel_skylib//:lib.bzl", "selects")
498```
499
500```sh
501sh_binary
502 name = "my_rule",
503 srcs = ["always_include.sh"],
504 deps = selects.with_or({
505 (":config1", ":config2", ":config3"): [":standard_lib"],
506 ":config4": [":special_lib"],
507 })
508)
509```
510
511This automatically expands the `select` to the original syntax above.
512
513For `AND` chaining, see [here](#multiple-selects).
514
515## Custom Error Messages
516
517By default, when no condition matches, the owning rule fails with the error:
518
519```sh
520ERROR: Configurable attribute "deps" doesn't match this configuration (would
521a default condition help?).
522Conditions checked:
523 //tools/cc_target_os:darwin
524 //tools/cc_target_os:android
525```
526
527This can be customized with [`no_match_error`](be/functions.html#select):
528
529```sh
530cc_library(
531 name = "my_lib",
532 deps = select({
533 "//tools/cc_target_os:android": [":android_deps"],
534 "//tools/cc_target_os:windows": [":windows_deps"],
535 }, no_match_error = "Please build with an Android or Windows toolchain"
536 )
537)
538```
539
540```sh
541$ bazel build //foo:my_lib
542ERROR: Configurable attribute "deps" doesn't match this configuration: Please
543build with an Android or Windows toolchain
544```
545
546## <a name="skylark"></a>Skylark Compatibility
547Skylark is compatible with configurable attributes in limited form.
548
549Skylark rule implementations receive the *resolved values* of configurable
550attributes. For example, given:
551
552```sh
553//myproject/BUILD:
554some_skylark_rule(
555 name = "my_rule",
556 some_attr = select({
557 ":foo_mode": [":foo"],
558 ":bar_mode": [":bar"],
559 })
560)
561```
562
563```sh
564$ bazel build //myproject/my_rule --define mode=foo
565```
566
567Skylark rule implementation code sees `ctx.attr.some_attr` as `[":foo"]`.
568
569Skylark macros can accept `select()` clauses and pass them through to native
570rules. But *they cannot directly manipulate them*. For example, there's no way
571for a Skylark macro to convert
572
573```sh
574`select({"foo": "val"}, ...)`
575```
576
577to
578
579```sh
580`select({"foo": "val_with_suffix"}, ...)`.
581```
582
583This is for two reasons.
584
585First, macros that need to know which path a `select` will choose *cannot work*
586because macros are evaluated in Bazel's [loading phase]
587(user-manual.html#loading-phase), which occurs before flag values are known.
588This is a core Bazel design restriction that's unlikely to change any time soon.
589
590Second, macros that just need to iterate over *all* `select` paths, while
591technically feasible, lack a coherent UI. Further design is necessary to change
592this.
593
594## <a name="query"></a>Bazel Query and Cquery
595Bazel `query` operates over Bazel's [loading phase]
596(user-manual.html#loading-phase). This means it doesn't know what command line
597flags will be applied to a rule since those flags aren't evaluated until later
598in the build (during the [analysis phase](user-manual.html#analysis-phase)). So
599the [`query`](query.html) command can't accurately determine which path a
600configurable attribute will follow.
601
602[Bazel `cquery`](cquery.html) has the advantage of being able to parse build
603flags and operating post-analysis phase so it correctly resolves configurable
604attributes. It doesn't have full feature parity with query but supports most
605major functionality and is actively being worked on.
606Querying the following build file...
607
608```sh
609//myproject/BUILD:
610cc_library(
611 name = "my_lib",
612 deps = select({
613 ":long": [":foo_dep"],
614 ":short": [":bar_dep"],
615 })
616)
617config_setting(
618 name = 'long',
619 values = { "define": "dog=dachshund" }
620)
621config_setting(
622 name = 'short',
623 values = { "define": "dog=pug" }
624)
625```
626...would return the following results.
627
628```sh
629$ bazel query 'deps(//myproject:my_lib)'
630//myproject:my_lib
631//myproject:foo_dep
632//myproject:bar_dep
633
634$ bazel cquery 'deps(//myproject:my_lib)' --define dog=pug
635//myproject:my_lib
636//myproject:bar_dep
637```
638
639## FAQ
640
641## <a name="skylark-macros-select"></a>Why doesn't select() work in Skylark?
642select() *does* work in Skylark! See [Skylark compatibility](#skylark) for
643details.
644
645The key issue this question usually means is that select() doesn't work in
646Skylark *macros*. These are different than Skylark *rules*. See the Skylark
647documentation on [rules](skylark/rules.html) and [macros](skylark/macros.html)
648to understand the difference.
649Here's an end-to-end example:
650
651Define a Skylark rule and macro:
652
653```sh
654# myproject/defs.bzl:
655
656# Rule implementation: when an attribute is read, all select()s have already
657# been resolved. So it looks like a plain old attribute just like any other.
658def _impl(ctx):
659 name = ctx.attr.name
660 allcaps = ctx.attr.my_config_string.upper() # This works fine on all values.
661 print("My name is " + name + " with custom message: " + allcaps)
662
663# Skylark rule declaration:
664my_custom_bazel_rule = rule(
665 implementation = _impl,
666 attrs = {"my_config_string": attr.string()}
667)
668
669# Skylark macro declaration:
670def my_custom_bazel_macro(name, my_config_string):
671 allcaps = my_config_string.upper() # This line won't work with select(s).
672 print("My name is " + name + " with custom message: " + allcaps)
673```
674
675Instantiate the rule and macro:
676
677```sh
678# myproject/BUILD:
679load("//myproject:defx.bzl", "my_custom_bazel_rule")
680load("//myproject:defs.bzl", "my_custom_bazel_macro")
681
682my_custom_bazel_rule(
683 name = "happy_rule",
684 my_config_string = select({
685 "//tools/target_cpu:x86": "first string",
686 "//tools/target_cpu:ppc": "second string",
687 }),
688)
689
690my_custom_bazel_macro(
691 name = "happy_macro",
692 my_config_string = "fixed string",
693)
694
695my_custom_bazel_macro(
696 name = "sad_macro",
697 my_config_string = select({
698 "//tools/target_cpu:x86": "first string",
699 "//tools/target_cpu:ppc": "other string",
700 }),
701)
702```
703
704Building fails because `sad_macro` can't process the `select()`:
705
706```sh
707$ bazel build //myproject:all
708ERROR: /myworkspace/myproject/BUILD:17:1: Traceback
709 (most recent call last):
710File "/myworkspace/myproject/BUILD", line 17
711my_custom_bazel_macro(name = "sad_macro", my_config_stri..."}))
712File "/myworkspace/myproject/defs.bzl", line 4, in
713 my_custom_bazel_macro
714my_config_string.upper()
715type 'select' has no method upper().
716ERROR: error loading package 'myproject': Package 'myproject' contains errors.
717```
718
719Building succeeds when we comment out `sad_macro`:
720
721```sh
722# Comment out sad_macro so it doesn't mess up the build.
723$ bazel build //myproject:all
724DEBUG: /myworkspace/myproject/defs.bzl:5:3: My name is happy_macro with custom message: FIXED STRING.
725DEBUG: /myworkspace/myproject/hi.bzl:15:3: My name is happy_rule with custom message: FIRST STRING.
726```
727
728This is impossible to change because *by definition* macros are evaluated before
729Bazel reads the build's command line flags. That means there isn't enough
730information to evaluate select()s.
731
732Macros can, however, pass `select()`s as opaque blobs to rules:
733
734```sh
735# myproject/defs.bzl:
736def my_custom_bazel_macro(name, my_config_string):
737 print("Invoking macro " + name)
738 my_custom_bazel_rule(
739 name = name + "_as_rule",
740 my_config_string = my_config_string)
741```
742
743```sh
744$ bazel build //myproject:sad_macro_less_sad
745DEBUG: /myworkspace/myproject/defs.bzl:23:3: Invoking macro sad_macro_less_sad.
746DEBUG: /myworkspace/myproject/defs.bzl:15:3: My name is sad_macro_less_sad with custom message: FIRST STRING.
747```
748
749## <a name="boolean-select"></a>Why does select() always return true in Skylark?
750Because Skylark *macros* (but not rules) by definition
751[can't evaluate select(s)](#skylark-macros-select), any attempt to do so
752usually produces a an error:
753
754```sh
755ERROR: /myworkspace/myproject/BUILD:17:1: Traceback
756 (most recent call last):
757File "/myworkspace/myproject/BUILD", line 17
758my_custom_bazel_macro(name = "sad_macro", my_config_stri..."}))
759File "/myworkspace/myproject/defs.bzl", line 4, in
760 my_custom_bazel_macro
761my_config_string.upper()
762type 'select' has no method upper().
763```
764
765Booleans are a special case that fail silently, so you should be particularly
766vigilant with them:
767
768```sh
769$ cat myproject/defs.bzl:
770def my_boolean_macro(boolval):
771 print("TRUE" if boolval else "FALSE")
772
773$ cat myproject/BUILD:
774load("//myproject:defx.bzl", "my_boolean_macro")
775my_boolean_macro(
776 boolval = select({
777 "//tools/target_cpu:x86": True,
778 "//tools/target_cpu:ppc": False,
779 }),
780)
781
782$ bazel build //myproject:all --cpu=x86
783DEBUG: /myworkspace/myproject/defs.bzl:4:3: TRUE.
784$ bazel build //myproject:all --cpu=ppc
785DEBUG: /myworkspace/myproject/defs.bzl:4:3: TRUE.
786```
787
788This happens because Skylark macros don't understand the contents of `select()`.
789So what they're really evaluting is the `select()` object itself. According to
790[Pythonic](https://docs.python.org/release/2.5.2/lib/truth.html) design
791standards, all objects aside from a very small number of exceptions
792automatically return true.
793## <a name="inspectable-select"></a>Can I read select() like a dict in Skylark?
794Fine. Skylark macros [can't](#skylark-macros-select) evaluate select(s) because
795macros are evaluated before Bazel knows what the command line flags are.
796
797Can macros at least read the `select()`'s dictionary, say, to add an extra
798suffix to each branch?
799
800Conceptually this is possible. But this isn't yet implemented and is not
801currently prioritized.
802What you *can* do today is prepare a straight dictionary, then feed it into a
803`select()`:
804
805```sh
806$ cat myproject/defs.bzl
807def selecty_genrule(name, select_cmd):
808 for key in select_cmd.keys():
809 select_cmd[key] += " WITH SUFFIX"
810 native.genrule(
811 name = name,
812 outs = [name + ".out"],
813 srcs = [],
814 cmd = "echo " + select(select_cmd + {"//conditions:default": "default"})
815 + " > $@"
816 )
817
818$ cat myproject/BUILD
819selecty_genrule(
820 name = "selecty",
821 select_cmd = {
822 "//tools/target_cpu:x86": "x86 mode",
823 },
824)
825
826$ bazel build //testapp:selecty --cpu=x86 && cat bazel-genfiles/testapp/selecty.out
827x86 mode WITH SUFFIX
828```
829
830If you'd like to support both `select()` and native types, you can do this:
831
832```sh
833$ cat myproject/defs.bzl
834def selecty_genrule(name, select_cmd):
835 cmd_suffix = ""
836 if type(select_cmd) == "string":
837 cmd_suffix = select_cmd + " WITH SUFFIX"
838 elif type(select_cmd) == "dict":
839 for key in select_cmd.keys():
840 select_cmd[key] += " WITH SUFFIX"
841 cmd_suffix = select(select_cmd + {"//conditions:default": "default"})
842
843 native.genrule(
844 name = name,
845 outs = [name + ".out"],
846 srcs = [],
847 cmd = "echo " + cmd_suffix + "> $@")
848```