forked from bazelbuild/examples
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add examples of selectable copts and test wrapper (bazelbuild#158)
- Loading branch information
Showing
12 changed files
with
353 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
rules/starlark_configurations/cc_binary_selectable_copts/BUILD
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# This load statement replaces the native cc_binary (which has no "set_features" | ||
# attribute) with a macro that strings together the logic to make that work, | ||
# then passes everything else back to the native cc_binary. | ||
load(":defs.bzl", "transition_rule", "cc_binary") | ||
|
||
# To see what this does, try "$ bazel run //:app_with_feature1". | ||
cc_binary( | ||
name = "app_with_feature1", | ||
srcs = ["main.cc"], | ||
set_features = "feature1", | ||
deps = [":lib"], | ||
) | ||
|
||
# To see what this does, try "$ bazel run //:app_with_feature2". | ||
cc_binary( | ||
name = "app_with_feature2", | ||
srcs = ["main.cc"], | ||
set_features = "feature2", | ||
deps = [":lib"], | ||
) | ||
|
||
# This binary "forgets" to set any features, so we have it fail with | ||
# a descriptive message. | ||
cc_binary( | ||
name = "app_forgets_to_set_features", | ||
srcs = ["main.cc"], | ||
deps = [":lib"], | ||
) | ||
|
||
# The library only builds if some feature is requested. | ||
cc_library( | ||
name = "lib", | ||
srcs = ["lib.cc"], | ||
copts = select( | ||
{ | ||
"//starlark_configurations/cc_binary_selectable_copts/custom_settings:feature1": ["-Dfeature1"], | ||
"//starlark_configurations/cc_binary_selectable_copts/custom_settings:feature2": ["-Dfeature2"], | ||
}, | ||
no_match_error = "You must explicitly set which features you want!", | ||
), | ||
) |
66 changes: 66 additions & 0 deletions
66
rules/starlark_configurations/cc_binary_selectable_copts/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
### Example showing how to use [Starlark configuration](https://docs.bazel.build/versions/master/skylark/config.html) so a `cc_binary` can set custom "features" for `cc_library` dependencies to include. | ||
|
||
|
||
This example has five files: | ||
|
||
* [custom_settings/BUILD](custom_settings/BUILD) - This defines a [custom | ||
Starlark | ||
flag](https://docs.bazel.build/versions/master/skylark/config.html#user-defined-build-settings) | ||
called `//custom_settings:mycopts` which defines the set of possible features | ||
and stores whatever features the `cc_binary` sets. | ||
|
||
`cc_library` uses a `select` to read this flag and set its `copts` | ||
accordingly. This file also declares the `config_setting`s the `select` uses. | ||
|
||
* [defs.bzl](defs.bzl) - This defines a custom Starlark rule `transition_rule` | ||
which defines an attribute `set_features` that sets the desired feature and | ||
`actual_binary` which declares the `cc_binary` this should apply to. | ||
|
||
Because `cc_binary` is a native (not Starlark-defined) rule, we can't | ||
add `set_features` directly to it. For Starlark-defined rules, we could | ||
omit `transition_rule` and just add the functionality directly. | ||
|
||
`transition_rule` applies a [Starlark | ||
transition](https://docs.bazel.build/versions/master/skylark/config.html#user-defined-transitions) | ||
called `_copt_transition` that reads the value of `set_features` and sets | ||
`//custom-settings:mycopts` accordingly. | ||
|
||
Finally, this file declares a macro (also) called `cc_binary` that automates away all | ||
this extra abstraction: the new macro `cc_binary` simply instantiates a | ||
`transition_rule` with the desired `set_features` then passes all other | ||
attributes directly to the native `cc_binary`. To the end user this makes it | ||
look like `cc_binary` magically supports a new attribute. | ||
|
||
* [BUILD](BUILD) - This defines three versions of a `cc_binary` all depending on | ||
the same `cc_library`: one uses `feature`, the other uses `feature2`, and the | ||
third fails to build because it "forgets" to set any feature. | ||
|
||
* [main.cc](main.cc) and [lib.cc](lib.cc) for the actual C++ code. | ||
|
||
To see it in action, cd to this directory and try the following commands: | ||
|
||
```sh | ||
$ bazel run :app_with_feature1 | ||
... | ||
Running my app! | ||
Building lib with feature 1! | ||
``` | ||
|
||
```sh | ||
$ bazel run :app_with_feature2 | ||
... | ||
Running my app! | ||
Building lib with feature 2! | ||
``` | ||
|
||
```sh | ||
$ bazel run :app_forgets_to_set_features | ||
ERROR: $(MYWORKSPACE)/cc_binary_selectable_copts/BUILD:32:13: Configurable attribute "copts" | ||
doesn't match this configuration: You must explicitly set which features you want! | ||
``` | ||
This example relies on `select` and involves some duplication of values | ||
(e.g. `"feature1"` is defined in `//custom_settings:mycopts` and `//:lib` | ||
separately sets `"-Dfeature1"`). You could write more Starlark macros to make the | ||
`BUILD` API even simpler. For example: not requiring any changes to | ||
`cc_library` at all. |
52 changes: 52 additions & 0 deletions
52
rules/starlark_configurations/cc_binary_selectable_copts/custom_settings/BUILD
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# We don't actually need an external repo to define Starlark settings. But | ||
# Skylib provides convenience macros that reduce boilerplate, so we'll use that | ||
# here. | ||
# | ||
# See https://docs.bazel.build/versions/master/skylark/config.html and | ||
# https://github.com/bazelbuild/bazel-skylib) for more info. | ||
|
||
load("@bazel_skylib//rules:common_settings.bzl", "string_flag") | ||
|
||
# You can avoid the need for this entire file by using "--define" instead | ||
# ("--define" is a native flag, built into Bazel). But we recommend this | ||
# Starlark-based approach. This lets you model the setting you need much | ||
# more precisely, offers typing and validation, mixes more cleanly with | ||
# other flags, and doesn't risk namespace collisions on complicated projects | ||
# (e.g. imgagine unrelated package reading "--define mode=1" for some | ||
# overloaded concept of "mode"). | ||
|
||
string_flag( | ||
# You can set this at the command line with "$ bazel build | ||
# //starlark_configurations/cc_binary_selectable_copts:all | ||
# --//starlark_configurations/cc_binary_selectable_copts/custom_settings:mycopts". | ||
# It's also possible to disallow command line access (in this example | ||
# we don't need that because cc_binary sets this implicitly without the | ||
# end user even having to know this flag exists). See | ||
# https://docs.bazel.build/versions/master/skylark/config.html#the-build_setting-rule-parameter | ||
# for details. | ||
name = "mycopts", | ||
build_setting_default = "unset", | ||
# We could also omit this attribute to allow any value: | ||
values = [ | ||
"feature1", | ||
"feature2", | ||
# We'll use this to trigger an error if the binary forgets | ||
# to set the feature it wants: | ||
"unset", | ||
], | ||
) | ||
|
||
# Predefine config_settings matching each feature. The "flag_values" attribute | ||
# is used to match Starlark-defined flags. | ||
# | ||
# You could write more Starlark macros to further automate and simplify this. | ||
|
||
config_setting( | ||
name = "feature1", | ||
flag_values = {":mycopts": "feature1"}, | ||
) | ||
|
||
config_setting( | ||
name = "feature2", | ||
flag_values = {":mycopts": "feature2"}, | ||
) |
91 changes: 91 additions & 0 deletions
91
rules/starlark_configurations/cc_binary_selectable_copts/defs.bzl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
def _copt_transition_impl(settings, attr): | ||
_ignore = settings | ||
|
||
# settings provides read access to existing flags. But | ||
# this transition doesn't need to read any flags. | ||
return {"//starlark_configurations/cc_binary_selectable_copts/custom_settings:mycopts": attr.set_features} | ||
|
||
# This defines a Starlark transition and which flags it reads and | ||
# writes. In this case we don't need to read any flags - we | ||
# universally set --/custom_settings:mycopts according to whatever | ||
# is set in the owning rule's "set_features" attribute. | ||
_copt_transition = transition( | ||
implementation = _copt_transition_impl, | ||
inputs = [], | ||
outputs = ["//starlark_configurations/cc_binary_selectable_copts/custom_settings:mycopts"], | ||
) | ||
|
||
# The implementation of transition_rule: all this does is copy the | ||
# cc_binary's output to its own output and propagate its runfiles | ||
# and executable to use for "$ bazel run". | ||
# | ||
# This makes transition_rule as close to a pure wrapper of cc_binary | ||
# as possible. | ||
def _transition_rule_impl(ctx): | ||
actual_binary = ctx.attr.actual_binary[0] | ||
outfile = ctx.actions.declare_file(ctx.label.name) | ||
cc_binary_outfile = actual_binary[DefaultInfo].files.to_list()[0] | ||
|
||
ctx.actions.run_shell( | ||
inputs = [cc_binary_outfile], | ||
outputs = [outfile], | ||
command = "cp %s %s" % (cc_binary_outfile.path, outfile.path), | ||
) | ||
return [ | ||
DefaultInfo( | ||
executable = outfile, | ||
data_runfiles = actual_binary[DefaultInfo].data_runfiles, | ||
), | ||
] | ||
|
||
# The purpose of this rule is to take a "set_features" attribute, | ||
# invoke a transition that sets --//custom_settings:mycopts to the | ||
# desired feature, then depend on a cc_binary whose deps will now | ||
# be able to select() on that feature. | ||
# | ||
# You could add a transition_rule directly in your BUILD file. But for | ||
# convenience we also define a cc_binary macro below so the BUILD can look | ||
# as close to normal as possible. | ||
transition_rule = rule( | ||
implementation = _transition_rule_impl, | ||
attrs = { | ||
# This is where the user can set the feature they want. | ||
"set_features": attr.string(default = "unset"), | ||
# This is the cc_binary whose deps will select() on that feature. | ||
# Note specificaly how it's configured with _copt_transition, which | ||
# ensures that setting propagates down the graph. | ||
"actual_binary": attr.label(cfg = _copt_transition), | ||
# This is a stock Bazel requirement for any rule that uses Starlark | ||
# transitions. It's okay to copy the below verbatim for all such rules. | ||
# | ||
# The purpose of this requirement is to give the ability to restrict | ||
# which packages can invoke these rules, since Starlark transitions | ||
# make much larger graphs possible that can have memory and performance | ||
# consequences for your build. The whitelist defaults to "everything". | ||
# But you can redefine it more strictly if you feel that's prudent. | ||
"_whitelist_function_transition": attr.label( | ||
default = "@bazel_tools//tools/whitelists/function_transition_whitelist", | ||
), | ||
}, | ||
# Making this executable means it works with "$ bazel run". | ||
executable = True, | ||
) | ||
|
||
# Convenience macro: this instantiates a transition_rule with the given | ||
# desired features, instantiates a cc_binary as a dependency of that rule, | ||
# and fills out the cc_binary with all other parameters passed to this macro. | ||
# | ||
# The result is a wrapper over cc_binary that "magically" gives it a new | ||
# feature-setting attribute. The only difference for a BUILD user is they need | ||
# to load() this at the top of the BUILD file. | ||
def cc_binary(name, set_features = None, **kwargs): | ||
cc_binary_name = name + "_native_binary" | ||
transition_rule( | ||
name = name, | ||
actual_binary = ":%s" % cc_binary_name, | ||
set_features = set_features, | ||
) | ||
native.cc_binary( | ||
name = cc_binary_name, | ||
**kwargs | ||
) |
13 changes: 13 additions & 0 deletions
13
rules/starlark_configurations/cc_binary_selectable_copts/lib.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#include <iostream> | ||
|
||
void runlib() { | ||
#ifdef feature1 | ||
std::cout << "Building lib with feature 1!\n"; | ||
#elif feature2 | ||
std::cout << "Building lib with feature 2!\n"; | ||
#else | ||
std::cout << "Building lib without features. But the select() in the BUILD " | ||
<< "file should prevent this case from building in the first " | ||
<< "place.\n"; | ||
#endif | ||
} |
8 changes: 8 additions & 0 deletions
8
rules/starlark_configurations/cc_binary_selectable_copts/main.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#include <iostream> | ||
|
||
void runlib(); // Defined in lib.cc | ||
|
||
int main(int argc, char** argv) { | ||
std::cout << "Running my app!\n"; | ||
runlib(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
load(":defs.bzl", "test_arg_cc_test") | ||
|
||
test_arg_cc_test( | ||
name = "my-test", | ||
srcs = ["mytest.cc"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
### Example showing how to use [Starlark configuration](https://docs.bazel.build/versions/master/skylark/config.html) to write a | ||
`cc_test` wrapper with a starlark transition | ||
|
||
the `test_arg_cc_test` macro in `defs.bzl` defines a wrapper for basically a cc_test that has been transitioned. | ||
This allows, e.g., the test itself to select attribute values based on the value of that transition. There is some | ||
light magic in the `transition_rule` implementation that allows dependents of the `test_arg_cc_test` macros to | ||
treat the targets it creates the exact same as a regular cc test. |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# We can transition on native options using this | ||
# //command_line_option:<option-name> syntax | ||
_BUILD_SETTING = "//command_line_option:test_arg" | ||
|
||
def _test_arg_transition_impl(settings, attr): | ||
_ignore = (settings, attr) | ||
|
||
return {_BUILD_SETTING: ["new arg"]} | ||
|
||
_test_arg_transition = transition( | ||
implementation = _test_arg_transition_impl, | ||
inputs = [], | ||
outputs = [_BUILD_SETTING], | ||
) | ||
|
||
def _test_transition_rule_impl(ctx): | ||
# We need to copy the executable because starlark doesn't allow | ||
# providing an executable not created by the rule | ||
executable_src = ctx.executable.actual_test | ||
executable_dst = ctx.actions.declare_file(ctx.label.name) | ||
ctx.actions.run_shell( | ||
tools = [executable_src], | ||
outputs = [executable_dst], | ||
command = "cp %s %s" % (executable_src.path, executable_dst.path), | ||
) | ||
runfiles = ctx.attr.actual_test[0][DefaultInfo].default_runfiles | ||
return [DefaultInfo(runfiles = runfiles, executable = executable_dst)] | ||
|
||
transition_rule_test = rule( | ||
implementation = _test_transition_rule_impl, | ||
attrs = { | ||
"actual_test": attr.label(cfg = _test_arg_transition, executable = True), | ||
"_whitelist_function_transition": attr.label( | ||
default = "@bazel_tools//tools/whitelists/function_transition_whitelist", | ||
), | ||
}, | ||
test = True, | ||
) | ||
|
||
def test_arg_cc_test(name, **kwargs): | ||
cc_test_name = name + "_native_test" | ||
transition_rule_test( | ||
name = name, | ||
actual_test = ":%s" % cc_test_name, | ||
) | ||
native.cc_test(name = cc_test_name, **kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// a trivially passing test | ||
|
||
int main() { | ||
return 0; | ||
} |