Skip to content

Commit

Permalink
BEGIN_PUBLIC
Browse files Browse the repository at this point in the history
Implement cc_args.

Also change cc_flag_set / cc_flag_group to cc_args / cc_arg_group. This is to lean into the idea that this is roughly equivalent to ctx.actions.args()
END_PUBLIC

PiperOrigin-RevId: 608804069
Change-Id: I74ea883b14219f904aaafc4eab902b96a1fb3e3d
  • Loading branch information
Googler authored and copybara-github committed Feb 21, 2024
1 parent 2e780ce commit 35fe45e
Show file tree
Hide file tree
Showing 14 changed files with 375 additions and 58 deletions.
114 changes: 83 additions & 31 deletions cc/toolchains/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ sh_binary(
An action config is a mapping from action to:

* A list of tools, (the first one matching the execution requirements is used).
* A list of flags and features that are always enabled for the action
* A list of args and features that are always enabled for the action
* A set of additional files required for the action

Each action can only be specified once in the toolchain. Specifying multiple
Expand All @@ -62,7 +62,7 @@ cc_action_config(
name = "c_compile",
actions = ["@rules_cc//actions:all_c_compile"],
tools = ["@sysroot//:clang"],
flag_sets = [":my_flag_set"],
args = [":my_args"],
implies = [":my_feature"],
additional_files = ["@sysroot//:all_header_files"],
)
Expand All @@ -74,57 +74,109 @@ cc_additional_files_for_actions(
)
```

## Step 3: Define some flag sets
Flag sets are just sets of flags to be associated with actions. Most flag sets
are simple, so we provide the shorthand `flags`. However, sometimes you
need to do more complex things, for which we support `flag_groups` instead.
## Step 3: Define some arguments
Arguments are our replacement for `flag_set` and `env_set`. To add arguments to
our tools, we take heavy inspiration from bazel's
[`Args`](https://bazel.build/rules/lib/builtins/Args) type. We provide the same
API, with the following caveats:
* `actions` specifies which actions the arguments apply to (same as `flag_set`).
* `requires_any_of` is equivalent to `with_features` on the `flag_set`.
* `args` may be used instead of `add` if your command-line is only strings.
* `env` may be used to add environment variables to the arguments. Environment
variables set by later args take priority.
* By default, all inputs are automatically added to the corresponding actions.
`additional_files` specifies files that are required for an action when using
that argument.

Flag groups work exactly the same as the existing toolchain definition.

Flag sets are a combination of both `flag_set` and `env_set` from the existing
toolchain definition.
```
cc_args(
name = "inline",
actions = ["@rules_cc//actions:all_cpp_compile_actions"],
args = ["--foo"],
requires_any_of = [":feature"]
env = {"FOO": "bar"},
additional_files = [":file"],
)
```

`cc_flag_set_list` is simply a list of flag sets. This can be used to group
flag sets together, and preserves ordering.
For more complex use cases, we use the same API as `Args`. Values are either:
* A list of files (or a single file for `cc_add_args`).
* Something returning `CcVariableInfo`, which is equivalent to a list of strings.

```
cc_flag_set(
name = "simple",
actions = ["@rules_cc//actions:all_cpp_compile_actions"],
flags = ["--foo"],
envs = {"FOO": "bar"},
cc_variable(
name = "bar_baz",
values = ["bar", "baz"],
)
cc_flag_group(
name = "complex_flag_group",
# API TBD
# Expands to CcVariableInfo(values = ["x86_64-unknown-linux-gnu"])
custom_variable_rule(
name = "triple",
...
)
cc_flag_set(
# Taken from https://bazel.build/rules/lib/builtins/Args#add
cc_add_args(
name = "single",
arg_name = "--platform",
value = ":triple", # Either a single file or a cc_variable
format = "%s",
)
# Taken from https://bazel.build/rules/lib/builtins/Args#add_all
cc_add_args_all(
name = "multiple",
arg_name = "--foo",
values = [":file", ":file_set"], # Either files or cc_variable.
# map_each not supported. Write a custom rule if you want that.
format_each = "%s",
before_each = "--foo",
omit_if_empty = True,
uniquify = False,
# Expand_directories not yet supported.
terminate_with = "foo",
)
# Taken from https://bazel.build/rules/lib/builtins/Args#add_joined
cc_add_args_joined(
name = "joined",
arg_name = "--foo",
values = [":file", ":file_set"], # Either files or cc_variable.
join_with = ",",
# map_each not supported. Write a custom rule if you want that.
format_each = "%s",
format_joined = "--foo=%s",
omit_if_empty = True,
uniquify = False,
# Expand_directories not yet supported.
)
cc_args(
name = "complex",
actions = ["@rules_cc//actions:c_compile"],
flag_groups = [":complex_flag_group"],
add = [":single", ":multiple", ":joined"],
)
cc_flag_set_list(
cc_args_list(
name = "all_flags",
flag_sets = [":simple", ":complex"],
args = [":inline", ":complex"],
)
```

## Step 4: Define some features
A feature is a set of flags and configurations that can be enabled or disabled.
A feature is a set of args and configurations that can be enabled or disabled.

Although the existing toolchain recommends using features to avoid duplication
of definitions, we recommend avoiding using features unless you want the user to
be able to enable / disable the feature themselves. This is because we provide
alternatives such as `cc_flag_set_list` to allow combining flag sets and
alternatives such as `cc_args_list` to allow combining arguments and
specifying them on each action in the action config.

```
cc_feature(
name = "my_feature",
feature_name = "my_feature",
flag_sets = [":all_flags"],
args = [":all_args"],
implies = [":other_feature"],
)
```
Expand All @@ -143,7 +195,7 @@ The `cc_toolchain` macro:
cc_toolchain(
name = "toolchain",
features = [":my_feature"]
unconditional_flag_sets = [":all_warnings"],
unconditional_args = [":all_warnings"],
action_configs = [":c_compile"],
additional_files = [":all_action_files"],
)
Expand Down Expand Up @@ -187,7 +239,7 @@ def cc_legacy_features(name, features):
# Build file
FOO = feature(name = "foo", flag_sets=[flag_group(...)])
FOO = feature(name = "foo", args=[arg_group(...)])
FEATURES = [FOO]
cc_legacy_features(name = "legacy_features", features = FEATURES)
Expand Down Expand Up @@ -240,7 +292,7 @@ Feature requirements can come in two formats.
For example:

* Features can require some subset of features to be enabled.
* Flag sets can require some subset of features to be enabled, but others to be
* Arguments can require some subset of features to be enabled, but others to be
disabled.

This is very confusing for toolchain authors, so we will simplify things with
Expand All @@ -265,7 +317,7 @@ cc_feature_constraint(
none_of = [":my_other_feature"],
)
cc_flag_set(
cc_args(
name = "foo",
# All of these provide with_feature.
requires_any_of = [":my_feature", ":my_feature_set", ":my_feature_constraint"]
Expand Down
6 changes: 2 additions & 4 deletions cc/toolchains/actions.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.
"""Rules to turn action types into bazel labels."""

load("//cc/toolchains/impl:collect.bzl", "collect_action_types")
load(":cc_toolchain_info.bzl", "ActionTypeInfo", "ActionTypeSetInfo")

visibility("public")
Expand Down Expand Up @@ -51,10 +52,7 @@ cc_action_type(
def _cc_action_type_set_impl(ctx):
return [ActionTypeSetInfo(
label = ctx.label,
actions = depset(transitive = [
attr[ActionTypeSetInfo].actions
for attr in ctx.attr.actions
]),
actions = collect_action_types(ctx.attr.actions),
)]

cc_action_type_set = rule(
Expand Down
105 changes: 105 additions & 0 deletions cc/toolchains/args.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# 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.
"""All providers for rule-based bazel toolchain config."""

load(
"//cc/toolchains/impl:collect.bzl",
"collect_action_types",
"collect_files",
"collect_provider",
)
load(
":cc_toolchain_info.bzl",
"ActionTypeSetInfo",
"AddArgsInfo",
"ArgsInfo",
"ArgsListInfo",
"FeatureConstraintInfo",
)

visibility("public")

def _cc_args_impl(ctx):
add_args = [AddArgsInfo(
label = ctx.label,
args = tuple(ctx.attr.args),
files = depset([]),
)]

actions = collect_action_types(ctx.attr.actions)
files = collect_files(ctx.attr.additional_files)
requires = collect_provider(ctx.attr.requires_any_of, FeatureConstraintInfo)

args = ArgsInfo(
label = ctx.label,
actions = actions,
requires_any_of = tuple(requires),
files = files,
args = add_args,
env = ctx.attr.env,
)
return [
args,
ArgsListInfo(label = ctx.label, args = tuple([args])),
]

cc_args = rule(
implementation = _cc_args_impl,
attrs = {
"actions": attr.label_list(
providers = [ActionTypeSetInfo],
mandatory = True,
doc = """A list of action types that this flag set applies to.
See @rules_cc//cc/toolchains/actions:all for valid options.
""",
),
"additional_files": attr.label_list(
allow_files = True,
doc = """Files required to add this argument to the command-line.
For example, a flag that sets the header directory might add the headers in that
directory as additional files.
""",
),
"args": attr.string_list(
doc = """Arguments that should be added to the command-line.
These are evaluated in order, with earlier args appearing earlier in the
invocation of the underlying tool.
""",
),
"env": attr.string_dict(
doc = "Environment variables to be added to the command-line.",
),
"requires_any_of": attr.label_list(
providers = [FeatureConstraintInfo],
doc = """This will be enabled when any of the constraints are met.
If omitted, this flag set will be enabled unconditionally.
""",
),
},
provides = [ArgsInfo],
doc = """Declares a list of arguments bound to a set of actions.
Roughly equivalent to ctx.actions.args()
Examples:
cc_args(
name = "warnings_as_errors",
args = ["-Werror"],
)
""",
)
31 changes: 22 additions & 9 deletions cc/toolchains/cc_toolchain_info.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# decide to just require users to use the public user-facing rules.
visibility([
"//cc/toolchains/...",
"//tests/...",
"//tests/rule_based_toolchain/...",
])

# Note that throughout this file, we never use a list. This is because mutable
Expand All @@ -29,6 +29,7 @@ visibility([

ActionTypeInfo = provider(
doc = "A type of action (eg. c-compile, c++-link-executable)",
# @unsorted-dict-items
fields = {
"label": "(Label) The label defining this provider. Place in error messages to simplify debugging",
"name": "(str) The action name, as defined by action_names.bzl",
Expand All @@ -44,23 +45,34 @@ ActionTypeSetInfo = provider(
},
)

FlagGroupInfo = provider(
doc = "A group of flags",
AddArgsInfo = provider(
doc = "A provider representation of Args.add/add_all/add_joined parameters",
# @unsorted-dict-items
fields = {
"label": "(Label) The label defining this provider. Place in error messages to simplify debugging",
"flags": "(Sequence[str]) A list of flags to add to the command-line",
"args": "(Sequence[str]) The command-line arguments to add",
"files": "(depset[File]) The files required to use this variable",
},
)

FlagSetInfo = provider(
doc = "A set of flags to be expanded in the command line for specific actions",
ArgsInfo = provider(
doc = "A set of arguments to be added to the command line for specific actions",
# @unsorted-dict-items
fields = {
"label": "(Label) The label defining this provider. Place in error messages to simplify debugging",
"actions": "(depset[ActionTypeInfo]) The set of actions this is associated with",
"requires_any_of": "(Sequence[FeatureConstraintInfo]) This will be enabled if any of the listed predicates are met. Equivalent to with_features",
"flag_groups": "(Sequence[FlagGroupInfo]) Set of flag groups to include.",
"args": "(Sequence[AddArgsInfo]) The command-line arguments to add.",
"files": "(depset[File]) Files required for the args",
"env": "(dict[str, str]) Environment variables to apply",
},
)
ArgsListInfo = provider(
doc = "A ordered list of arguments",
# @unsorted-dict-items
fields = {
"label": "(Label) The label defining this provider. Place in error messages to simplify debugging",
"args": "(Sequence[ArgsInfo]) The flag sets contained within",
},
)

Expand All @@ -71,7 +83,7 @@ FeatureInfo = provider(
"label": "(Label) The label defining this provider. Place in error messages to simplify debugging",
"name": "(str) The name of the feature",
"enabled": "(bool) Whether this feature is enabled by default",
"flag_sets": "(depset[FlagSetInfo]) Flag sets enabled by this feature",
"args": "(Sequence[ArgsInfo]) Flag sets enabled by this feature",
"implies": "(depset[FeatureInfo]) Set of features implied by this feature",
"requires_any_of": "(Sequence[FeatureSetInfo]) A list of feature sets, at least one of which is required to enable this feature. This is semantically equivalent to the requires attribute of rules_cc's FeatureInfo",
"provides": "(Sequence[MutuallyExclusiveCategoryInfo]) Indicates that this feature is one of several mutually exclusive alternate features.",
Expand Down Expand Up @@ -100,6 +112,7 @@ FeatureConstraintInfo = provider(

MutuallyExclusiveCategoryInfo = provider(
doc = "Multiple features with the category will be mutally exclusive",
# @unsorted-dict-items
fields = {
"label": "(Label) The label defining this provider. Place in error messages to simplify debugging",
"name": "(str) The name of the category",
Expand All @@ -126,7 +139,7 @@ ActionConfigInfo = provider(
"action": "(ActionTypeInfo) The name of the action",
"enabled": "(bool) If True, this action is enabled unless a rule type explicitly marks it as unsupported",
"tools": "(Sequence[ToolInfo]) The tool applied to the action will be the first tool in the sequence with a feature set that matches the feature configuration",
"flag_sets": "(Sequence[FlagSetInfo]) Set of flag sets the action sets",
"args": "(Sequence[ArgsInfo]) Set of flag sets the action sets",
"implies": "(depset[FeatureInfo]) Set of features implied by this action config",
"files": "(depset[File]) The files required to run these actions",
},
Expand Down
Loading

0 comments on commit 35fe45e

Please sign in to comment.