David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 1 | # Rules for distributable C++ libraries |
| 2 | |
| 3 | load("@rules_cc//cc:action_names.bzl", cc_action_names = "ACTION_NAMES") |
| 4 | load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain") |
| 5 | |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 6 | ################################################################################ |
| 7 | # Archive/linking support |
| 8 | ################################################################################ |
| 9 | |
| 10 | def _collect_linker_input_objects(dep_label, cc_info, objs, pic_objs): |
| 11 | """Accumulate .o and .pic.o files into `objs` and `pic_objs`.""" |
| 12 | link_ctx = cc_info.linking_context |
| 13 | if link_ctx == None: |
| 14 | return |
| 15 | |
| 16 | linker_inputs = link_ctx.linker_inputs.to_list() |
| 17 | for link_input in linker_inputs: |
| 18 | if link_input.owner != dep_label: |
| 19 | # This is a transitive dep: skip it. |
| 20 | continue |
| 21 | |
| 22 | for lib in link_input.libraries: |
| 23 | objs.extend(lib.objects or []) |
| 24 | pic_objs.extend(lib.pic_objects or []) |
| 25 | |
David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 26 | # Creates an action to build the `output_file` static library (archive) |
| 27 | # using `object_files`. |
| 28 | def _create_archive_action( |
| 29 | ctx, |
| 30 | feature_configuration, |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 31 | cc_toolchain_info, |
David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 32 | output_file, |
| 33 | object_files): |
| 34 | # Based on Bazel's src/main/starlark/builtins_bzl/common/cc/cc_import.bzl: |
| 35 | |
| 36 | # Build the command line and add args for all of the input files: |
| 37 | archiver_variables = cc_common.create_link_variables( |
| 38 | feature_configuration = feature_configuration, |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 39 | cc_toolchain = cc_toolchain_info, |
David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 40 | output_file = output_file.path, |
| 41 | is_using_linker = False, |
| 42 | ) |
| 43 | command_line = cc_common.get_memory_inefficient_command_line( |
| 44 | feature_configuration = feature_configuration, |
| 45 | action_name = cc_action_names.cpp_link_static_library, |
| 46 | variables = archiver_variables, |
| 47 | ) |
| 48 | args = ctx.actions.args() |
| 49 | args.add_all(command_line) |
| 50 | args.add_all(object_files) |
| 51 | args.use_param_file("@%s", use_always = True) |
| 52 | |
| 53 | archiver_path = cc_common.get_tool_for_action( |
| 54 | feature_configuration = feature_configuration, |
| 55 | action_name = cc_action_names.cpp_link_static_library, |
| 56 | ) |
| 57 | |
| 58 | env = cc_common.get_environment_variables( |
| 59 | feature_configuration = feature_configuration, |
| 60 | action_name = cc_action_names.cpp_link_static_library, |
| 61 | variables = archiver_variables, |
| 62 | ) |
| 63 | |
| 64 | ctx.actions.run( |
| 65 | executable = archiver_path, |
| 66 | arguments = [args], |
| 67 | env = env, |
| 68 | inputs = depset( |
| 69 | direct = object_files, |
| 70 | transitive = [ |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 71 | cc_toolchain_info.all_files, |
David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 72 | ], |
| 73 | ), |
David L. Jones | 4e5b920 | 2022-05-16 16:11:09 -0700 | [diff] [blame] | 74 | use_default_shell_env = False, |
David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 75 | outputs = [output_file], |
| 76 | mnemonic = "CppArchiveDist", |
| 77 | ) |
| 78 | |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 79 | def _create_dso_link_action( |
| 80 | ctx, |
| 81 | feature_configuration, |
| 82 | cc_toolchain_info, |
| 83 | object_files, |
| 84 | pic_object_files): |
David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 85 | compilation_outputs = cc_common.create_compilation_outputs( |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 86 | objects = depset(object_files), |
| 87 | pic_objects = depset(pic_object_files), |
David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 88 | ) |
| 89 | link_output = cc_common.link( |
| 90 | actions = ctx.actions, |
| 91 | feature_configuration = feature_configuration, |
| 92 | cc_toolchain = cc_toolchain_info, |
| 93 | compilation_outputs = compilation_outputs, |
| 94 | name = ctx.label.name, |
| 95 | output_type = "dynamic_library", |
| 96 | user_link_flags = ctx.attr.linkopts, |
| 97 | ) |
| 98 | library_to_link = link_output.library_to_link |
| 99 | |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 100 | outputs = [] |
| 101 | |
David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 102 | # Note: library_to_link.dynamic_library and interface_library are often |
| 103 | # symlinks in the solib directory. For DefaultInfo, prefer reporting |
| 104 | # the resolved artifact paths. |
| 105 | if library_to_link.resolved_symlink_dynamic_library != None: |
| 106 | outputs.append(library_to_link.resolved_symlink_dynamic_library) |
| 107 | elif library_to_link.dynamic_library != None: |
| 108 | outputs.append(library_to_link.dynamic_library) |
| 109 | |
| 110 | if library_to_link.resolved_symlink_interface_library != None: |
| 111 | outputs.append(library_to_link.resolved_symlink_interface_library) |
| 112 | elif library_to_link.interface_library != None: |
| 113 | outputs.append(library_to_link.interface_library) |
| 114 | |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 115 | return outputs |
| 116 | |
| 117 | ################################################################################ |
| 118 | # Source file/header support |
| 119 | ################################################################################ |
| 120 | |
| 121 | CcFileList = provider( |
| 122 | doc = "List of files to be built into a library.", |
| 123 | fields = { |
| 124 | # As a rule of thumb, `hdrs` and `textual_hdrs` are the files that |
| 125 | # would be installed along with a prebuilt library. |
| 126 | "hdrs": "public header files, including those used by generated code", |
| 127 | "textual_hdrs": "files which are included but are not self-contained", |
| 128 | |
| 129 | # The `internal_hdrs` are header files which appear in `srcs`. |
| 130 | # These are only used when compiling the library. |
| 131 | "internal_hdrs": "internal header files (only used to build .cc files)", |
| 132 | "srcs": "source files", |
| 133 | }, |
| 134 | ) |
| 135 | |
| 136 | def _flatten_target_files(targets): |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 137 | return depset(transitive = [ |
| 138 | target.files |
| 139 | for target in targets |
Adam Cozzette | 5ca8400 | 2023-11-08 15:36:33 -0800 | [diff] [blame] | 140 | # Filter out targets from external workspaces. We also filter out |
| 141 | # utf8_range since that has a separate CMake build for now. |
| 142 | if (target.label.workspace_name == "" or |
| 143 | target.label.workspace_name == "com_google_protobuf") and |
| 144 | not target.label.package.startswith("third_party/utf8_range") |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 145 | ]) |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 146 | |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 147 | def _get_transitive_sources(targets, attr, deps): |
| 148 | return depset(targets, transitive = [getattr(dep[CcFileList], attr) for dep in deps if CcFileList in dep]) |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 149 | |
| 150 | def _cc_file_list_aspect_impl(target, ctx): |
| 151 | # Extract sources from a `cc_library` (or similar): |
| 152 | if CcInfo not in target: |
| 153 | return [] |
| 154 | |
| 155 | # We're going to reach directly into the attrs on the traversed rule. |
| 156 | rule_attr = ctx.rule.attr |
| 157 | |
| 158 | # CcInfo is a proxy for what we expect this rule to look like. |
| 159 | # However, some deps may expose `CcInfo` without having `srcs`, |
| 160 | # `hdrs`, etc., so we use `getattr` to handle that gracefully. |
| 161 | |
| 162 | internal_hdrs = [] |
| 163 | srcs = [] |
| 164 | |
| 165 | # Filter `srcs` so it only contains source files. Headers will go |
| 166 | # into `internal_headers`. |
| 167 | for src in _flatten_target_files(getattr(rule_attr, "srcs", [])).to_list(): |
| 168 | if src.extension.lower() in ["c", "cc", "cpp", "cxx"]: |
| 169 | srcs.append(src) |
| 170 | else: |
| 171 | internal_hdrs.append(src) |
| 172 | |
| 173 | return [CcFileList( |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 174 | hdrs = _get_transitive_sources( |
Adam Cozzette | 77aa913 | 2023-10-21 09:37:33 -0700 | [diff] [blame] | 175 | _flatten_target_files(getattr(rule_attr, "hdrs", [])).to_list(), |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 176 | "hdrs", |
| 177 | rule_attr.deps, |
| 178 | ), |
| 179 | textual_hdrs = _get_transitive_sources( |
Adam Cozzette | 77aa913 | 2023-10-21 09:37:33 -0700 | [diff] [blame] | 180 | _flatten_target_files(getattr(rule_attr, "textual_hdrs", [])).to_list(), |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 181 | "textual_hdrs", |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 182 | rule_attr.deps, |
| 183 | ), |
| 184 | internal_hdrs = _get_transitive_sources( |
| 185 | internal_hdrs, |
| 186 | "internal_hdrs", |
| 187 | rule_attr.deps, |
| 188 | ), |
| 189 | srcs = _get_transitive_sources(srcs, "srcs", rule_attr.deps), |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 190 | )] |
| 191 | |
| 192 | cc_file_list_aspect = aspect( |
| 193 | doc = """ |
| 194 | Aspect to provide the list of sources and headers from a rule. |
| 195 | |
| 196 | Output is CcFileList. Example: |
| 197 | |
| 198 | cc_library( |
| 199 | name = "foo", |
| 200 | srcs = [ |
| 201 | "foo.cc", |
| 202 | "foo_internal.h", |
| 203 | ], |
| 204 | hdrs = ["foo.h"], |
| 205 | textual_hdrs = ["foo_inl.inc"], |
| 206 | ) |
| 207 | # produces: |
| 208 | # CcFileList( |
| 209 | # hdrs = depset([File("foo.h")]), |
| 210 | # textual_hdrs = depset([File("foo_inl.inc")]), |
| 211 | # internal_hdrs = depset([File("foo_internal.h")]), |
| 212 | # srcs = depset([File("foo.cc")]), |
| 213 | # ) |
| 214 | """, |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 215 | required_providers = [CcInfo], |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 216 | implementation = _cc_file_list_aspect_impl, |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 217 | attr_aspects = ["deps"], |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 218 | ) |
| 219 | |
| 220 | ################################################################################ |
| 221 | # Rule impl |
| 222 | ################################################################################ |
| 223 | |
| 224 | def _collect_inputs(deps): |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 225 | """Collects files from a list of deps. |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 226 | |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 227 | This rule collects source files and linker inputs transitively for C++ |
| 228 | deps. |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 229 | |
| 230 | The return value is a struct with object files (linker inputs), |
| 231 | partitioned by PIC and non-pic, and the rules' source and header files: |
| 232 | |
| 233 | struct( |
| 234 | objects = ..., # non-PIC object files |
| 235 | pic_objects = ..., # PIC objects |
| 236 | cc_file_list = ..., # a CcFileList |
| 237 | ) |
| 238 | |
| 239 | Args: |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 240 | deps: Iterable of immediate deps, which will be treated as roots to |
| 241 | recurse transitively. |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 242 | Returns: |
| 243 | A struct with linker inputs, source files, and header files. |
| 244 | """ |
| 245 | |
| 246 | objs = [] |
| 247 | pic_objs = [] |
| 248 | |
| 249 | # The returned CcFileList will contain depsets of the deps' file lists. |
| 250 | # These lists hold `depset()`s from each of `deps`. |
| 251 | srcs = [] |
| 252 | hdrs = [] |
| 253 | internal_hdrs = [] |
| 254 | textual_hdrs = [] |
| 255 | |
| 256 | for dep in deps: |
| 257 | if CcInfo in dep: |
| 258 | _collect_linker_input_objects( |
| 259 | dep.label, |
| 260 | dep[CcInfo], |
| 261 | objs, |
| 262 | pic_objs, |
| 263 | ) |
| 264 | |
| 265 | if CcFileList in dep: |
| 266 | cfl = dep[CcFileList] |
| 267 | srcs.append(cfl.srcs) |
| 268 | hdrs.append(cfl.hdrs) |
| 269 | internal_hdrs.append(cfl.internal_hdrs) |
| 270 | textual_hdrs.append(cfl.textual_hdrs) |
| 271 | |
| 272 | return struct( |
| 273 | objects = objs, |
| 274 | pic_objects = pic_objs, |
| 275 | cc_file_list = CcFileList( |
| 276 | srcs = depset(transitive = srcs), |
| 277 | hdrs = depset(transitive = hdrs), |
| 278 | internal_hdrs = depset(transitive = internal_hdrs), |
| 279 | textual_hdrs = depset(transitive = textual_hdrs), |
| 280 | ), |
| 281 | ) |
| 282 | |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 283 | # Given structs a and b returned from _collect_inputs(), returns a copy of a |
| 284 | # but with all files from b subtracted out. |
| 285 | def _subtract_files(a, b): |
| 286 | result_args = {} |
| 287 | |
| 288 | top_level_fields = ["objects", "pic_objects"] |
| 289 | for field in top_level_fields: |
| 290 | to_remove = {e: None for e in getattr(b, field)} |
| 291 | result_args[field] = [e for e in getattr(a, field) if not e in to_remove] |
| 292 | |
| 293 | cc_file_list_args = {} |
| 294 | file_list_fields = ["srcs", "hdrs", "internal_hdrs", "textual_hdrs"] |
| 295 | for field in file_list_fields: |
| 296 | to_remove = {e: None for e in getattr(b.cc_file_list, field).to_list()} |
| 297 | cc_file_list_args[field] = depset( |
| 298 | [e for e in getattr(a.cc_file_list, field).to_list() if not e in to_remove], |
| 299 | ) |
| 300 | result_args["cc_file_list"] = CcFileList(**cc_file_list_args) |
| 301 | |
| 302 | return struct(**result_args) |
| 303 | |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 304 | # Implementation for cc_dist_library rule. |
| 305 | def _cc_dist_library_impl(ctx): |
| 306 | cc_toolchain_info = find_cc_toolchain(ctx) |
| 307 | |
| 308 | feature_configuration = cc_common.configure_features( |
| 309 | ctx = ctx, |
| 310 | cc_toolchain = cc_toolchain_info, |
| 311 | ) |
| 312 | |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 313 | inputs = _subtract_files(_collect_inputs(ctx.attr.deps), _collect_inputs(ctx.attr.dist_deps)) |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 314 | |
| 315 | # For static libraries, build separately with and without pic. |
| 316 | |
| 317 | stemname = "lib" + ctx.label.name |
| 318 | outputs = [] |
| 319 | |
| 320 | if len(inputs.objects) > 0: |
| 321 | archive_out = ctx.actions.declare_file(stemname + ".a") |
| 322 | _create_archive_action( |
| 323 | ctx, |
| 324 | feature_configuration, |
| 325 | cc_toolchain_info, |
| 326 | archive_out, |
| 327 | inputs.objects, |
| 328 | ) |
| 329 | outputs.append(archive_out) |
| 330 | |
| 331 | if len(inputs.pic_objects) > 0: |
| 332 | pic_archive_out = ctx.actions.declare_file(stemname + ".pic.a") |
| 333 | _create_archive_action( |
| 334 | ctx, |
| 335 | feature_configuration, |
| 336 | cc_toolchain_info, |
| 337 | pic_archive_out, |
| 338 | inputs.pic_objects, |
| 339 | ) |
| 340 | outputs.append(pic_archive_out) |
| 341 | |
| 342 | # For dynamic libraries, use the `cc_common.link` command to ensure |
| 343 | # everything gets built correctly according to toolchain definitions. |
| 344 | outputs.extend(_create_dso_link_action( |
| 345 | ctx, |
| 346 | feature_configuration, |
| 347 | cc_toolchain_info, |
| 348 | inputs.objects, |
| 349 | inputs.pic_objects, |
| 350 | )) |
| 351 | |
David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 352 | # We could expose the libraries for use from cc rules: |
| 353 | # |
| 354 | # linking_context = cc_common.create_linking_context( |
| 355 | # linker_inputs = depset([ |
| 356 | # cc_common.create_linker_input( |
| 357 | # owner = ctx.label, |
| 358 | # libraries = depset([library_to_link]), |
| 359 | # ), |
| 360 | # ]), |
| 361 | # ) |
| 362 | # cc_info = CcInfo(linking_context = linking_context) # and return this |
| 363 | # |
| 364 | # However, if the goal is to force a protobuf dependency to use the |
| 365 | # DSO, then `cc_import` is a better-supported way to do so. |
| 366 | # |
| 367 | # If we wanted to expose CcInfo from this rule (and make it usable as a |
| 368 | # C++ dependency), then we would probably want to include the static |
| 369 | # archive and headers as well. exposing headers would probably require |
| 370 | # an additional aspect to extract CcInfos with just the deps' headers. |
| 371 | |
| 372 | return [ |
| 373 | DefaultInfo(files = depset(outputs)), |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 374 | inputs.cc_file_list, |
David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 375 | ] |
| 376 | |
| 377 | cc_dist_library = rule( |
| 378 | implementation = _cc_dist_library_impl, |
| 379 | doc = """ |
| 380 | Create libraries suitable for distribution. |
| 381 | |
| 382 | This rule creates static and dynamic libraries from the libraries listed in |
| 383 | 'deps'. The resulting libraries are suitable for distributing all of 'deps' |
| 384 | in a single logical library, for example, in an installable binary package. |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 385 | The result includes all transitive dependencies, excluding those reachable |
| 386 | from 'dist_deps' or defined in a separate repository (e.g. Abseil). |
David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 387 | |
| 388 | The outputs of this rule are a dynamic library and a static library. (If |
| 389 | the build produces both PIC and non-PIC object files, then there is also a |
| 390 | second static library.) The example below illustrates additional details. |
| 391 | |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 392 | This rule is different from Bazel's experimental `shared_cc_library` in two |
| 393 | ways. First, this rule produces a static archive library in addition to the |
| 394 | dynamic shared library. Second, this rule is not directly usable as a C++ |
| 395 | dependency (although the outputs could be used, e.g., by `cc_import`). |
David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 396 | |
| 397 | Example: |
| 398 | |
| 399 | cc_library(name = "a", srcs = ["a.cc"], hdrs = ["a.h"]) |
| 400 | cc_library(name = "b", srcs = ["b.cc"], hdrs = ["b.h"], deps = [":a"]) |
| 401 | cc_library(name = "c", srcs = ["c.cc"], hdrs = ["c.h"], deps = [":b"]) |
| 402 | |
| 403 | # Creates libdist.so and (typically) libdist.pic.a: |
| 404 | # (This may also produce libdist.a if the build produces non-PIC objects.) |
| 405 | cc_dist_library( |
| 406 | name = "dist", |
| 407 | linkopts = ["-la"], # libdist.so dynamically links against liba.so. |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 408 | deps = [":b", ":c"], # Output contains a.o, b.o, and c.o. |
David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 409 | ) |
| 410 | """, |
| 411 | attrs = { |
| 412 | "deps": attr.label_list( |
Adam Cozzette | b146519 | 2022-10-25 14:40:38 +0000 | [diff] [blame] | 413 | doc = ("The list of libraries to be included in the outputs, " + |
| 414 | "along with their transitive dependencies."), |
| 415 | aspects = [cc_file_list_aspect], |
| 416 | ), |
| 417 | "dist_deps": attr.label_list( |
| 418 | doc = ("The list of cc_dist_library dependencies that " + |
| 419 | "should be excluded."), |
David L. Jones | 586b72c | 2022-05-24 12:31:56 -0700 | [diff] [blame] | 420 | aspects = [cc_file_list_aspect], |
David L. Jones | 46710ca | 2022-04-20 16:26:44 -0700 | [diff] [blame] | 421 | ), |
| 422 | "linkopts": attr.string_list( |
| 423 | doc = ("Add these flags to the C++ linker command when creating " + |
| 424 | "the dynamic library."), |
| 425 | ), |
| 426 | # C++ toolchain before https://github.com/bazelbuild/bazel/issues/7260: |
| 427 | "_cc_toolchain": attr.label( |
| 428 | default = Label("@rules_cc//cc:current_cc_toolchain"), |
| 429 | ), |
| 430 | }, |
| 431 | toolchains = [ |
| 432 | # C++ toolchain after https://github.com/bazelbuild/bazel/issues/7260: |
| 433 | "@bazel_tools//tools/cpp:toolchain_type", |
| 434 | ], |
| 435 | fragments = ["cpp"], |
| 436 | ) |