xref: /aosp_15_r20/external/bazelbuild-rules_cc/cc/toolchains/README.md (revision eed53cd41c5909d05eedc7ad9720bb158fd93452)
1*eed53cd4SHONG Yifan# Writing a custom rule_based C++ toolchain with rule-based definition.
2*eed53cd4SHONG Yifan
3*eed53cd4SHONG YifanWork in progress!
4*eed53cd4SHONG Yifan
5*eed53cd4SHONG YifanThis document serves two purposes:
6*eed53cd4SHONG Yifan* Until complete, this serves as an agreement for the final user-facing API.
7*eed53cd4SHONG Yifan* Once complete, this will serve as onboarding documentation.
8*eed53cd4SHONG Yifan
9*eed53cd4SHONG YifanThis section will be removed once complete.
10*eed53cd4SHONG Yifan
11*eed53cd4SHONG Yifan## Step 1: Define tools
12*eed53cd4SHONG YifanA tool is simply a binary. Just like any other bazel binary, a tool can specify
13*eed53cd4SHONG Yifanadditional files required to run.
14*eed53cd4SHONG Yifan
15*eed53cd4SHONG YifanWe can use any bazel binary as an input to anything that requires tools. In the
16*eed53cd4SHONG Yifanexample below, you could use both clang and ld as tools.
17*eed53cd4SHONG Yifan
18*eed53cd4SHONG Yifan```
19*eed53cd4SHONG Yifan# @sysroot//:BUILD
20*eed53cd4SHONG Yifancc_tool(
21*eed53cd4SHONG Yifan    name = "clang",
22*eed53cd4SHONG Yifan    exe = ":bin/clang",
23*eed53cd4SHONG Yifan    execution_requirements = ["requires-mem:24g"],
24*eed53cd4SHONG Yifan    data = [...],
25*eed53cd4SHONG Yifan)
26*eed53cd4SHONG Yifan
27*eed53cd4SHONG Yifansh_binary(
28*eed53cd4SHONG Yifan    name = "ld",
29*eed53cd4SHONG Yifan    srcs = ["ld_wrapper.sh"],
30*eed53cd4SHONG Yifan    data = [":bin/ld"],
31*eed53cd4SHONG Yifan)
32*eed53cd4SHONG Yifan
33*eed53cd4SHONG Yifan```
34*eed53cd4SHONG Yifan
35*eed53cd4SHONG Yifan## Step 2: Generate action configs from those tools
36*eed53cd4SHONG YifanAn action config is a mapping from action to:
37*eed53cd4SHONG Yifan
38*eed53cd4SHONG Yifan* A list of tools, (the first one matching the execution requirements is used).
39*eed53cd4SHONG Yifan* A list of args and features that are always enabled for the action
40*eed53cd4SHONG Yifan* A set of additional files required for the action
41*eed53cd4SHONG Yifan
42*eed53cd4SHONG YifanEach action can only be specified once in the toolchain. Specifying multiple
43*eed53cd4SHONG Yifanactions in a single `cc_action_type_config` is just a shorthand for specifying the
44*eed53cd4SHONG Yifansame config for every one of those actions.
45*eed53cd4SHONG Yifan
46*eed53cd4SHONG YifanIf you're already familiar with how to define toolchains, the additional files
47*eed53cd4SHONG Yifanis a replacement for `compile_files`, `link_files`, etc.
48*eed53cd4SHONG Yifan
49*eed53cd4SHONG YifanAdditionally, to replace `all_files`, we add `cc_additional_files_for_actions`.
50*eed53cd4SHONG YifanThis allows you to specify that particular files are required for particular
51*eed53cd4SHONG Yifanactions.
52*eed53cd4SHONG Yifan
53*eed53cd4SHONG YifanWe provide `additional_files` on the `cc_action_type_config` as a shorthand for
54*eed53cd4SHONG Yifanspecifying `cc_additional_files_for_actions`
55*eed53cd4SHONG Yifan
56*eed53cd4SHONG YifanWarning: Implying a feature that is not listed directly in the toolchain will throw
57*eed53cd4SHONG Yifanan error. This is to ensure you don't accidentally add a feature to the
58*eed53cd4SHONG Yifantoolchain.
59*eed53cd4SHONG Yifan
60*eed53cd4SHONG Yifan```
61*eed53cd4SHONG Yifancc_action_type_config(
62*eed53cd4SHONG Yifan    name  = "c_compile",
63*eed53cd4SHONG Yifan    actions = ["@rules_cc//actions:all_c_compile"],
64*eed53cd4SHONG Yifan    tools = ["@sysroot//:clang"],
65*eed53cd4SHONG Yifan    args = [":my_args"],
66*eed53cd4SHONG Yifan    implies = [":my_feature"],
67*eed53cd4SHONG Yifan    additional_files = ["@sysroot//:all_header_files"],
68*eed53cd4SHONG Yifan)
69*eed53cd4SHONG Yifan
70*eed53cd4SHONG Yifancc_additional_files_for_actions(
71*eed53cd4SHONG Yifan    name = "all_action_files",
72*eed53cd4SHONG Yifan    actions = ["@rules_cc//actions:all_actions"],
73*eed53cd4SHONG Yifan    additional_files = ["@sysroot//:always_needed_files"]
74*eed53cd4SHONG Yifan)
75*eed53cd4SHONG Yifan```
76*eed53cd4SHONG Yifan
77*eed53cd4SHONG Yifan## Step 3: Define some arguments
78*eed53cd4SHONG YifanArguments are our replacement for `flag_set` and `env_set`. To add arguments to
79*eed53cd4SHONG Yifanour tools, we take heavy inspiration from bazel's
80*eed53cd4SHONG Yifan[`Args`](https://bazel.build/rules/lib/builtins/Args) type. We provide the same
81*eed53cd4SHONG YifanAPI, with the following caveats:
82*eed53cd4SHONG Yifan* `actions` specifies which actions the arguments apply to (same as `flag_set`).
83*eed53cd4SHONG Yifan* `requires_any_of` is equivalent to `with_features` on the `flag_set`.
84*eed53cd4SHONG Yifan* `args` may be used instead of `add` if your command-line is only strings.
85*eed53cd4SHONG Yifan* `env` may be used to add environment variables to the arguments. Environment
86*eed53cd4SHONG Yifan  variables set by later args take priority.
87*eed53cd4SHONG Yifan* By default, all inputs are automatically added to the corresponding actions.
88*eed53cd4SHONG Yifan  `additional_files` specifies files that are required for an action when using
89*eed53cd4SHONG Yifan  that argument.
90*eed53cd4SHONG Yifan
91*eed53cd4SHONG Yifan```
92*eed53cd4SHONG Yifancc_args(
93*eed53cd4SHONG Yifan    name = "inline",
94*eed53cd4SHONG Yifan    actions = ["@rules_cc//actions:all_cpp_compile_actions"],
95*eed53cd4SHONG Yifan    args = ["--foo"],
96*eed53cd4SHONG Yifan    requires_any_of = [":feature"]
97*eed53cd4SHONG Yifan    env = {"FOO": "bar"},
98*eed53cd4SHONG Yifan    additional_files = [":file"],
99*eed53cd4SHONG Yifan)
100*eed53cd4SHONG Yifan```
101*eed53cd4SHONG Yifan
102*eed53cd4SHONG YifanFor more complex use cases, we use the same API as `Args`. Values are either:
103*eed53cd4SHONG Yifan* A list of files (or a single file for `cc_add_args`).
104*eed53cd4SHONG Yifan* Something returning `CcVariableInfo`, which is equivalent to a list of strings.
105*eed53cd4SHONG Yifan
106*eed53cd4SHONG Yifan```
107*eed53cd4SHONG Yifancc_variable(
108*eed53cd4SHONG Yifan  name = "bar_baz",
109*eed53cd4SHONG Yifan  values = ["bar", "baz"],
110*eed53cd4SHONG Yifan)
111*eed53cd4SHONG Yifan
112*eed53cd4SHONG Yifan# Expands to CcVariableInfo(values = ["x86_64-unknown-linux-gnu"])
113*eed53cd4SHONG Yifancustom_variable_rule(
114*eed53cd4SHONG Yifan  name = "triple",
115*eed53cd4SHONG Yifan  ...
116*eed53cd4SHONG Yifan)
117*eed53cd4SHONG Yifan
118*eed53cd4SHONG Yifan# Taken from https://bazel.build/rules/lib/builtins/Args#add
119*eed53cd4SHONG Yifancc_add_args(
120*eed53cd4SHONG Yifan    name = "single",
121*eed53cd4SHONG Yifan    arg_name = "--platform",
122*eed53cd4SHONG Yifan    value = ":triple", # Either a single file or a cc_variable
123*eed53cd4SHONG Yifan    format = "%s",
124*eed53cd4SHONG Yifan)
125*eed53cd4SHONG Yifan
126*eed53cd4SHONG Yifan# Taken from https://bazel.build/rules/lib/builtins/Args#add_all
127*eed53cd4SHONG Yifancc_add_args_all(
128*eed53cd4SHONG Yifan    name = "multiple",
129*eed53cd4SHONG Yifan    arg_name = "--foo",
130*eed53cd4SHONG Yifan    values = [":file", ":file_set"], # Either files or cc_variable.
131*eed53cd4SHONG Yifan    # map_each not supported. Write a custom rule if you want that.
132*eed53cd4SHONG Yifan    format_each = "%s",
133*eed53cd4SHONG Yifan    before_each = "--foo",
134*eed53cd4SHONG Yifan    omit_if_empty = True,
135*eed53cd4SHONG Yifan    uniquify = False,
136*eed53cd4SHONG Yifan    # Expand_directories not yet supported.
137*eed53cd4SHONG Yifan    terminate_with = "foo",
138*eed53cd4SHONG Yifan)
139*eed53cd4SHONG Yifan
140*eed53cd4SHONG Yifan# Taken from https://bazel.build/rules/lib/builtins/Args#add_joined
141*eed53cd4SHONG Yifancc_add_args_joined(
142*eed53cd4SHONG Yifan    name = "joined",
143*eed53cd4SHONG Yifan    arg_name = "--foo",
144*eed53cd4SHONG Yifan    values = [":file", ":file_set"], # Either files or cc_variable.
145*eed53cd4SHONG Yifan    join_with = ",",
146*eed53cd4SHONG Yifan    # map_each not supported. Write a custom rule if you want that.
147*eed53cd4SHONG Yifan    format_each = "%s",
148*eed53cd4SHONG Yifan    format_joined = "--foo=%s",
149*eed53cd4SHONG Yifan    omit_if_empty = True,
150*eed53cd4SHONG Yifan    uniquify = False,
151*eed53cd4SHONG Yifan    # Expand_directories not yet supported.
152*eed53cd4SHONG Yifan)
153*eed53cd4SHONG Yifan
154*eed53cd4SHONG Yifancc_args(
155*eed53cd4SHONG Yifan    name = "complex",
156*eed53cd4SHONG Yifan    actions = ["@rules_cc//actions:c_compile"],
157*eed53cd4SHONG Yifan    add = [":single", ":multiple", ":joined"],
158*eed53cd4SHONG Yifan)
159*eed53cd4SHONG Yifan
160*eed53cd4SHONG Yifancc_args_list(
161*eed53cd4SHONG Yifan    name = "all_flags",
162*eed53cd4SHONG Yifan    args = [":inline", ":complex"],
163*eed53cd4SHONG Yifan)
164*eed53cd4SHONG Yifan```
165*eed53cd4SHONG Yifan
166*eed53cd4SHONG Yifan## Step 4: Define some features
167*eed53cd4SHONG YifanA feature is a set of args and configurations that can be enabled or disabled.
168*eed53cd4SHONG Yifan
169*eed53cd4SHONG YifanAlthough the existing toolchain recommends using features to avoid duplication
170*eed53cd4SHONG Yifanof definitions, we recommend avoiding using features unless you want the user to
171*eed53cd4SHONG Yifanbe able to enable / disable the feature themselves. This is because we provide
172*eed53cd4SHONG Yifanalternatives such as `cc_args_list` to allow combining arguments and
173*eed53cd4SHONG Yifanspecifying them on each action in the action config.
174*eed53cd4SHONG Yifan
175*eed53cd4SHONG Yifan```
176*eed53cd4SHONG Yifancc_feature(
177*eed53cd4SHONG Yifan    name = "my_feature",
178*eed53cd4SHONG Yifan    feature_name = "my_feature",
179*eed53cd4SHONG Yifan    args = [":all_args"],
180*eed53cd4SHONG Yifan    implies = [":other_feature"],
181*eed53cd4SHONG Yifan)
182*eed53cd4SHONG Yifan```
183*eed53cd4SHONG Yifan
184*eed53cd4SHONG Yifan## Step 5: Generate the toolchain
185*eed53cd4SHONG YifanThe `cc_toolchain` macro:
186*eed53cd4SHONG Yifan
187*eed53cd4SHONG Yifan* Performs validation on the inputs (eg. no two action configs for a single
188*eed53cd4SHONG Yifan  action)
189*eed53cd4SHONG Yifan* Converts the type-safe providers to the unsafe ones in
190*eed53cd4SHONG Yifan  `cc_toolchain_config_lib.bzl`
191*eed53cd4SHONG Yifan* Generates a set of providers for each of the filegroups respectively
192*eed53cd4SHONG Yifan* Generates the appropriate `native.cc_toolchain` invocation.
193*eed53cd4SHONG Yifan
194*eed53cd4SHONG Yifan```
195*eed53cd4SHONG Yifancc_toolchain(
196*eed53cd4SHONG Yifan    name = "toolchain",
197*eed53cd4SHONG Yifan    features = [":my_feature"]
198*eed53cd4SHONG Yifan    unconditional_args = [":all_warnings"],
199*eed53cd4SHONG Yifan    action_type_configs = [":c_compile"],
200*eed53cd4SHONG Yifan    additional_files = [":all_action_files"],
201*eed53cd4SHONG Yifan)
202*eed53cd4SHONG Yifan```
203*eed53cd4SHONG Yifan
204*eed53cd4SHONG Yifan# Ancillary components for type-safe toolchains.
205*eed53cd4SHONG Yifan## Well-known features
206*eed53cd4SHONG YifanWell-known features will be defined in `@rules_cc//features/well_known:*`.
207*eed53cd4SHONG YifanAny feature with `feature_name` in the well known features will have to specify
208*eed53cd4SHONG Yifanoverrides.
209*eed53cd4SHONG Yifan
210*eed53cd4SHONG Yifan`cc_toolchain` is aware of the builtin / well-known features. In order to
211*eed53cd4SHONG Yifanensure that a user understands that this overrides the builtin opt feature (I
212*eed53cd4SHONG Yifanoriginally thought that it added extra flags to opt, but you still got the
213*eed53cd4SHONG Yifandefault ones, so that can definitely happen), and to ensure that they don't
214*eed53cd4SHONG Yifanaccidentally do so, we will force them to explicitly specify that it overrides
215*eed53cd4SHONG Yifanthe builtin one. This is essentially just an acknowledgement of "I know what
216*eed53cd4SHONG YifanI'm doing".
217*eed53cd4SHONG Yifan
218*eed53cd4SHONG YifanWarning: Specifying two features with the same name is an error, unless one
219*eed53cd4SHONG Yifanoverrides the other.
220*eed53cd4SHONG Yifan
221*eed53cd4SHONG Yifan```
222*eed53cd4SHONG Yifancc_feature(
223*eed53cd4SHONG Yifan    name = "opt",
224*eed53cd4SHONG Yifan    ...,
225*eed53cd4SHONG Yifan    overrides = "@rules_cc//features/well_known:opt",
226*eed53cd4SHONG Yifan)
227*eed53cd4SHONG Yifan```
228*eed53cd4SHONG Yifan
229*eed53cd4SHONG YifanIn addition to well-known features, we could also consider in future iterations
230*eed53cd4SHONG Yifanto also use known features for partial migrations, where you still imply a
231*eed53cd4SHONG Yifanfeature that's still defined by the legacy API:
232*eed53cd4SHONG Yifan
233*eed53cd4SHONG Yifan```
234*eed53cd4SHONG Yifan# Implementation
235*eed53cd4SHONG Yifandef cc_legacy_features(name, features):
236*eed53cd4SHONG Yifan  for feature in features:
237*eed53cd4SHONG Yifan    cc_known_feature(name = name + "_" + feature.name)
238*eed53cd4SHONG Yifan  cc_legacy_features(name = name, features = FEATURES)
239*eed53cd4SHONG Yifan
240*eed53cd4SHONG Yifan
241*eed53cd4SHONG Yifan# Build file
242*eed53cd4SHONG YifanFOO = feature(name = "foo", args=[arg_group(...)])
243*eed53cd4SHONG YifanFEATURES = [FOO]
244*eed53cd4SHONG Yifancc_legacy_features(name = "legacy_features", features = FEATURES)
245*eed53cd4SHONG Yifan
246*eed53cd4SHONG Yifancc_feature(name = "bar", implies = [":legacy_features_foo"])
247*eed53cd4SHONG Yifan
248*eed53cd4SHONG Yifancc_toolchain(
249*eed53cd4SHONG Yifan  name = "toolchain",
250*eed53cd4SHONG Yifan  legacy_features = ":legacy_features",
251*eed53cd4SHONG Yifan  features = [":bar"],
252*eed53cd4SHONG Yifan)
253*eed53cd4SHONG Yifan```
254*eed53cd4SHONG Yifan
255*eed53cd4SHONG Yifan## Mutual exclusion
256*eed53cd4SHONG YifanFeatures can be mutually exclusive.
257*eed53cd4SHONG Yifan
258*eed53cd4SHONG YifanWe allow two approaches to mutual exclusion - via features or via categories.
259*eed53cd4SHONG Yifan
260*eed53cd4SHONG YifanThe existing toolchain uses `provides` for both of these. We rename it so that
261*eed53cd4SHONG Yifanit makes more sense semantically.
262*eed53cd4SHONG Yifan
263*eed53cd4SHONG Yifan```
264*eed53cd4SHONG Yifancc_feature(
265*eed53cd4SHONG Yifan   name = "incompatible_with_my_feature",
266*eed53cd4SHONG Yifan   feature_name = "bar",
267*eed53cd4SHONG Yifan   mutually_exclusive = [":my_feature"],
268*eed53cd4SHONG Yifan)
269*eed53cd4SHONG Yifan
270*eed53cd4SHONG Yifan
271*eed53cd4SHONG Yifan# This is an example of how we would define compilation mode.
272*eed53cd4SHONG Yifan# Since it already exists, this wouldn't work.
273*eed53cd4SHONG Yifancc_mutual_exclusion_category(
274*eed53cd4SHONG Yifan    name = "compilation_mode",
275*eed53cd4SHONG Yifan)
276*eed53cd4SHONG Yifan
277*eed53cd4SHONG Yifancc_feature(
278*eed53cd4SHONG Yifan    name = "opt",
279*eed53cd4SHONG Yifan    ...
280*eed53cd4SHONG Yifan    mutually_exclusive = [":compilation_mode"],
281*eed53cd4SHONG Yifan)
282*eed53cd4SHONG Yifancc_feature(
283*eed53cd4SHONG Yifan    name = "dbg",
284*eed53cd4SHONG Yifan    ...
285*eed53cd4SHONG Yifan    mutually_exclusive = [":compilation_mode"],
286*eed53cd4SHONG Yifan)
287*eed53cd4SHONG Yifan```
288*eed53cd4SHONG Yifan
289*eed53cd4SHONG Yifan## Feature requirements
290*eed53cd4SHONG YifanFeature requirements can come in two formats.
291*eed53cd4SHONG Yifan
292*eed53cd4SHONG YifanFor example:
293*eed53cd4SHONG Yifan
294*eed53cd4SHONG Yifan* Features can require some subset of features to be enabled.
295*eed53cd4SHONG Yifan* Arguments can require some subset of features to be enabled, but others to be
296*eed53cd4SHONG Yifan  disabled.
297*eed53cd4SHONG Yifan
298*eed53cd4SHONG YifanThis is very confusing for toolchain authors, so we will simplify things with
299*eed53cd4SHONG Yifanthe use of providers:
300*eed53cd4SHONG Yifan
301*eed53cd4SHONG Yifan* `cc_feature` will provide `feature`, `feature_set`, and `with_feature`
302*eed53cd4SHONG Yifan* `cc_feature_set` will provide `feature_set` and `with_feature`.
303*eed53cd4SHONG Yifan* `cc_feature_constraint` will provide `with_features` only.
304*eed53cd4SHONG Yifan
305*eed53cd4SHONG YifanWe will rename all `with_features` and `requires` to `requires_any_of`, to make
306*eed53cd4SHONG Yifanit very clear that only one of the requirements needs to be met.
307*eed53cd4SHONG Yifan
308*eed53cd4SHONG Yifan```
309*eed53cd4SHONG Yifancc_feature_set(
310*eed53cd4SHONG Yifan    name = "my_feature_set",
311*eed53cd4SHONG Yifan    all_of = [":my_feature"],
312*eed53cd4SHONG Yifan)
313*eed53cd4SHONG Yifan
314*eed53cd4SHONG Yifancc_feature_constraint(
315*eed53cd4SHONG Yifan    name = "my_feature_constraint",
316*eed53cd4SHONG Yifan    all_of = [":my_feature"],
317*eed53cd4SHONG Yifan    none_of = [":my_other_feature"],
318*eed53cd4SHONG Yifan)
319*eed53cd4SHONG Yifan
320*eed53cd4SHONG Yifancc_args(
321*eed53cd4SHONG Yifan   name = "foo",
322*eed53cd4SHONG Yifan   # All of these provide with_feature.
323*eed53cd4SHONG Yifan   requires_any_of = [":my_feature", ":my_feature_set", ":my_feature_constraint"]
324*eed53cd4SHONG Yifan)
325*eed53cd4SHONG Yifan
326*eed53cd4SHONG Yifan# my_feature_constraint would be an error here.
327*eed53cd4SHONG Yifancc_feature(
328*eed53cd4SHONG Yifan   name = "foo",
329*eed53cd4SHONG Yifan   # Both of these provide feature_set.
330*eed53cd4SHONG Yifan   requires_any_of = [":my_feature", ":my_feature_set"]
331*eed53cd4SHONG Yifan   implies = [":my_other_feature", :my_other_feature_set"],
332*eed53cd4SHONG Yifan)
333*eed53cd4SHONG Yifan```
334