blob: de4c3951f67401e08666cfd74276cb5f443c45b4 [file]
# Copyright 2026 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A cc_library backed by a FIDL library."""
load("@bazel_skylib//lib:paths.bzl", "paths")
load("@rules_cc//cc:cc_library.bzl", "cc_library")
visibility("private")
_FidlCcCodegenInfo = provider(
"Carries lists of `File`s generated by the FIDL bindings code generation.",
fields = {
"headers": "A depset of generated header files.",
"sources": "A depset of generated source files.",
"testing_headers": "A depset of generated testing header files.",
"conversion_headers": "A depset of generated HLCPP conversion header files.",
},
)
def _get_deps_with_suffix(deps, dep_suffix):
"""Converts a list of FIDL library labels to cc_library() labels with a suffix appended.
Args:
deps: List of `Label`s, each representing a FIDL library target.
dep_suffix: String suffix to append to the FIDL library label.
Returns:
List of `Label`s, where each original FIDL library label is transformed
into the corresponding cc_library() label with the suffix appended.
"""
result = []
for dep in deps:
# Skip deps on the "zx" library, for which bindings are not generated
# with C++ fidlgens. Normalize the dep when comparing.
if Label(dep) == Label("//zircon/vdso/zx"):
continue
dep_label = _get_name_with_suffix(dep.name, dep_suffix)
dep_with_binding = "//%s:%s" % (dep.package, dep_label)
result.append(dep_with_binding)
return result
def _get_fidl_cc_lib_name(fidl_target_name, bindings_flavor):
"""Returns the cc_library() name for a FIDL library and binding flavor.
Args:
fidl_target_name: String name of the FIDL library target.
bindings_flavor: String indicating the binding flavor (e.g., "cpp").
Returns:
String name of the cc_library() target.
"""
return _get_name_with_suffix(fidl_target_name, bindings_flavor)
def _get_name_with_suffix(name, suffix):
"""Returns a name with a suffix in target name format.
Args:
name: String name.
suffix: String suffix to append to the target name.
Returns:
String name with the suffix appended.
"""
return "{name}_{suffix}".format(name = name, suffix = suffix)
def _get_root_path(bindings_flavor):
"""Returns the root path for the generated FIDL bindings files.
Args:
bindings_flavor: String indicating the binding flavor (e.g., "cpp").
Returns:
String root path for the generated FIDL bindings files.
"""
return "bindings/" + bindings_flavor
def fidl_cpp_family(
*,
name,
fidl_library_name,
fidl_ir_json,
deps,
contains_drivers,
enable_cpp,
enable_hlcpp,
hlcpp_lib_deps,
define_idk_atom_aliases,
testonly,
visibility):
"""Generates instances of `cc_library()` providing the generated C++ bindings for a given FIDL library.
Args:
name: String base name of the `cc_library()` targets.
fidl_library_name: String name of the FIDL library for which bindings are generated.
fidl_ir_json: `Label` pointing to a single file containing the FIDL IR
representation of the `fidl_library_name` library.
deps: List of `Label`s for FIDL libraries that the `fidl_library_name` library depends on.
contains_drivers: Boolean indicating whether the `fidl_library_name`
library supports drivers.
enable_cpp: Boolean indicating whether to generate C++ bindings.
enable_hlcpp: Boolean indicating whether to generate HLCPP bindings.
testonly: Usual meaning.
visibility: Usual meaning.
"""
if not enable_cpp and not enable_hlcpp:
fail("At least one of `enable_cpp` or `enable_hlcpp` must be true.")
hlcpp_bindings_flavor = "hlcpp"
hlcpp_lib_name = _get_fidl_cc_lib_name(name, hlcpp_bindings_flavor)
if enable_cpp:
cpp_bindings_flavor = "cpp"
cpp_lib_name = _get_fidl_cc_lib_name(name, cpp_bindings_flavor)
cpp_codegen_name = "%s_generated_fidl_cc_bindings" % cpp_lib_name
cpp_sources_name = "%s_sources" % cpp_lib_name
cpp_testing_headers_name = "%s_testing_headers" % cpp_lib_name
generated_header_files = [
"common_types.h",
"common_types_format.h",
"fidl.h",
"markers.h",
"natural_messaging.h",
"natural_ostream.h",
"natural_types.h",
"type_conversions.h",
"wire_messaging.h",
"wire_types.h",
"wire.h",
]
generated_source_files = [
"common_types.cc",
"natural_ostream.cc",
"natural_types.cc",
"type_conversions.cc",
"wire_types.cc",
] + select({
"@platforms//os:fuchsia": [
"natural_messaging.cc",
"wire_messaging.cc",
],
"//conditions:default": [],
})
generated_testing_header_files = [
"test_base.h",
"wire_test_base.h",
]
additional_deps = [
"//sdk/lib/fidl/cpp:cpp_base",
"//sdk/lib/fidl/cpp:natural_ostream",
"//sdk/lib/fidl/cpp/wire",
"//sdk/lib/fit",
] + select({
"@platforms//os:fuchsia": [
"//sdk/lib/fidl",
"//sdk/lib/fidl/cpp",
],
"//conditions:default": [],
})
if contains_drivers:
# TODO(https://fxbug.dev/503359085): Implement driver transport
# support and uncomment these lines.
# generated_header_files += ["driver/wire.h"] + select({
# "@platforms//os:fuchsia": [
# "driver/fidl.h",
# "driver/natural_messaging.h",
# "driver/wire_messaging.h",
# ],
# "//conditions:default": [],
# })
# generated_source_files += select({
# "@platforms//os:fuchsia": [
# "driver/natural_messaging.cc",
# "driver/wire_messaging.cc",
# ],
# "//conditions:default": [],
# })
# generated_testing_header_files += [
# "driver/test_base.h",
# "driver/wire_test_base.h",
# ]
# additional_deps += [
# "//sdk/lib/fidl_driver",
# "//sdk/lib/fidl_driver:fidl_driver_natural",
# ]
pass
generated_hlcpp_conversion_header_files = []
if enable_hlcpp:
generated_hlcpp_conversion_header_files = ["hlcpp_conversion.h"]
_generate_fidl_cc_bindings(
name = cpp_codegen_name,
fidl_library_name = fidl_library_name,
fidl_ir_json = fidl_ir_json,
bindings_flavor = cpp_bindings_flavor,
contains_drivers = contains_drivers,
generated_files_base = "fidl/" + fidl_library_name + "/cpp",
generated_header_files = generated_header_files,
generated_source_files = generated_source_files,
generated_testing_header_files = generated_testing_header_files,
generated_conversion_header_files = generated_hlcpp_conversion_header_files,
fidlgen_tool = "@//tools/fidl/fidlgen_cpp:fidlgen_cpp",
testonly = testonly,
)
_sources_wrapper(
name = cpp_sources_name,
generated_fidl_cc_bindings = ":%s" % cpp_codegen_name,
testonly = testonly,
)
_fidl_cc_library(
name = cpp_lib_name,
fidl_library_name = fidl_library_name,
fidl_deps = deps,
bindings_flavor = cpp_bindings_flavor,
hdrs = [":%s" % cpp_codegen_name],
srcs = [":%s" % cpp_sources_name],
additional_deps = additional_deps,
testonly = testonly,
visibility = visibility,
)
if define_idk_atom_aliases:
native.alias(
name = cpp_lib_name + "_idk",
actual = name + "_idk",
visibility = visibility,
)
_testing_headers_wrapper(
name = cpp_testing_headers_name,
generated_fidl_cc_bindings = ":%s" % cpp_codegen_name,
testonly = testonly,
)
testing_suffix = "testing"
cpp_testing_lib_name = _get_name_with_suffix(cpp_lib_name, testing_suffix)
_fidl_cc_library(
name = cpp_testing_lib_name,
fidl_library_name = fidl_library_name,
fidl_deps = deps,
bindings_flavor = cpp_bindings_flavor,
hdrs = [":%s" % cpp_testing_headers_name],
srcs = [],
additional_deps = [":%s" % cpp_lib_name],
extra_dep_suffix = testing_suffix,
target_compatible_with = ["@platforms//os:fuchsia"],
testonly = testonly,
visibility = visibility,
)
if define_idk_atom_aliases:
native.alias(
name = cpp_testing_lib_name + "_idk",
actual = name + "_idk",
visibility = visibility,
)
if enable_hlcpp:
cpp_hlcpp_conversion_headers_name = "%s_hlcpp_conversion_headers" % cpp_lib_name
_conversion_headers_wrapper(
name = cpp_hlcpp_conversion_headers_name,
generated_fidl_cc_bindings = ":%s" % cpp_codegen_name,
testonly = testonly,
)
cpp_hlcpp_conversion_suffix = "hlcpp_conversion"
hlcpp_conversion_lib_name = _get_name_with_suffix(cpp_lib_name, cpp_hlcpp_conversion_suffix)
_fidl_cc_library(
name = hlcpp_conversion_lib_name,
fidl_library_name = fidl_library_name,
fidl_deps = deps,
bindings_flavor = cpp_bindings_flavor,
hdrs = [":%s" % cpp_hlcpp_conversion_headers_name],
srcs = [],
additional_deps = [
":%s" % cpp_lib_name,
":%s" % hlcpp_lib_name,
"//sdk/lib/fidl/cpp:hlcpp_conversion",
],
extra_dep_suffix = cpp_hlcpp_conversion_suffix,
testonly = testonly,
visibility = visibility,
)
if define_idk_atom_aliases:
native.alias(
name = hlcpp_conversion_lib_name + "_idk",
actual = name + "_idk",
visibility = visibility,
)
if enable_hlcpp:
hlcpp_codegen_name = "%s_generated_fidl_cc_bindings" % hlcpp_lib_name
hlcpp_sources_name = "%s_sources" % hlcpp_lib_name
library_name_slashes = fidl_library_name.replace(".", "/")
generated_header_files = [
"fidl.h",
"fidl_test_base.h",
]
generated_source_files = [
"fidl.cc",
"tables.c",
]
additional_deps = ["//sdk/lib/fidl_base"] + hlcpp_lib_deps
_generate_fidl_cc_bindings(
name = hlcpp_codegen_name,
fidl_library_name = fidl_library_name,
fidl_ir_json = fidl_ir_json,
bindings_flavor = hlcpp_bindings_flavor,
contains_drivers = contains_drivers,
generated_files_base = library_name_slashes + "/cpp",
generated_header_files = generated_header_files,
generated_source_files = generated_source_files,
fidlgen_tool = "@//tools/fidl/fidlgen_hlcpp:fidlgen_hlcpp",
testonly = testonly,
)
_sources_wrapper(
name = hlcpp_sources_name,
generated_fidl_cc_bindings = ":%s" % hlcpp_codegen_name,
testonly = testonly,
)
# TODO(https://fxbug.dev/454977301): Implement `hlcpp_visibility`.
hlcpp_visibility = visibility
_fidl_cc_library(
name = hlcpp_lib_name,
fidl_library_name = fidl_library_name,
fidl_deps = deps,
bindings_flavor = hlcpp_bindings_flavor,
hdrs = [":%s" % hlcpp_codegen_name],
srcs = [":%s" % hlcpp_sources_name],
additional_deps = additional_deps,
testonly = testonly,
visibility = hlcpp_visibility,
)
if define_idk_atom_aliases:
native.alias(
name = hlcpp_lib_name + "_idk",
actual = name + "_idk",
visibility = hlcpp_visibility,
)
def _fidl_cc_library(
name,
fidl_library_name,
fidl_deps,
bindings_flavor,
hdrs,
srcs,
additional_deps,
testonly,
visibility,
extra_dep_suffix = "",
target_compatible_with = []):
"""Generate a `cc_library()` providing the generated C++ bindings for a given FIDL library.
Args:
name: String name of the `cc_library()` target.
fidl_library_name: String name of the FIDL library for which bindings are generated.
fidl_deps: List of `Label`s for FIDL libraries that the
`fidl_library_name` library depends on.
bindings_flavor: String name of the FIDL bindings flavor (e.g., "cpp").
hdrs: List of `Label`s for the generated header files.
srcs: List of `Label`s for the generated source files.
additional_deps: List of `Label`s for additional dependencies of the generated files.
testonly: Usual meaning.
visibility: Usual meaning.
extra_dep_suffix: Additional string suffix to append to the
`bindings_flavor` to form the dependency suffix. Optional.
target_compatible_with: Usual meaning. Optional.
"""
if fidl_library_name == "zx":
# The zx FIDL library isn't generated with C++ fidlgens.
# Create an empty library.
if fidl_deps != []:
fail("zx FIDL library should not have any dependencies.")
cc_library(
name = name,
testonly = testonly,
visibility = visibility,
)
else:
if extra_dep_suffix:
dep_suffix = _get_name_with_suffix(bindings_flavor, extra_dep_suffix)
else:
dep_suffix = bindings_flavor
cc_library(
name = name,
hdrs = hdrs,
srcs = srcs,
deps = _get_deps_with_suffix(fidl_deps, dep_suffix) + additional_deps,
# This is necessary in order to locate generated headers.
strip_include_prefix = _get_root_path(bindings_flavor),
target_compatible_with = target_compatible_with,
testonly = testonly,
visibility = visibility,
)
def _generate_fidl_cc_bindings_impl(ctx):
ir = ctx.file.fidl_ir_json
root_path = _get_root_path(ctx.attr.bindings_flavor)
dir = root_path + "/" + ctx.attr.generated_files_base
headers = []
sources = []
testing_headers = []
conversion_headers = []
for header in ctx.attr.generated_header_files:
headers.append(ctx.actions.declare_file(dir + "/" + header))
for source in ctx.attr.generated_source_files:
sources.append(ctx.actions.declare_file(dir + "/" + source))
for testing_header in ctx.attr.generated_testing_header_files:
testing_headers.append(ctx.actions.declare_file(dir + "/" + testing_header))
for conversion_header in ctx.attr.generated_conversion_header_files:
conversion_headers.append(ctx.actions.declare_file(dir + "/" + conversion_header))
ctx.actions.run(
executable = ctx.executable.fidlgen_tool,
arguments = [
"--json",
ir.path,
"--clang-format-path",
ctx.executable._clang_format.path,
"--root",
paths.join(ctx.bin_dir.path, ctx.label.workspace_root, ctx.label.package, root_path),
# TODO(https://fxbug.dev/454977301): Support experiments.
# if (defined(invoker.experiments)) {
# foreach(experiment, invoker.experiments) {
# args += [
# "--experiment",
# experiment,
# ]
# }
# }
],
inputs = [ir, ctx.executable._clang_format],
outputs = headers + sources + testing_headers + conversion_headers,
mnemonic = "FidlGenCc",
)
return [
_FidlCcCodegenInfo(
headers = depset(headers),
sources = depset(sources),
testing_headers = depset(testing_headers),
conversion_headers = depset(conversion_headers),
),
DefaultInfo(files = depset(headers)),
]
_generate_fidl_cc_bindings = rule(
doc = "Runs fidlgen to produce both the header files and source files. " +
"Only exposes the header files in `DefaultInfo`, as the two files need to be " +
"consumed by `cc_library()` as two separate rules.",
implementation = _generate_fidl_cc_bindings_impl,
attrs = {
"fidl_library_name": attr.string(
doc = "Name of the FIDL library.",
mandatory = True,
),
"fidl_ir_json": attr.label(
doc = "The FIDL IR for which to generate code.",
allow_single_file = True,
mandatory = True,
),
"bindings_flavor": attr.string(
doc = "The flavor of bindings to generate.",
mandatory = True,
values = ["cpp", "hlcpp"],
),
"contains_drivers": attr.bool(
doc = "Indicates if any of the FIDL files contain the driver transport or " +
"references to the driver transport.",
mandatory = True,
),
"generated_files_base": attr.string(
doc = "Base path for generated files. The files will be generated" +
" at this path under the root path for the bindings flavor",
mandatory = True,
),
"generated_header_files": attr.string_list(
doc = "List of header files that will be generated.",
mandatory = True,
),
"generated_source_files": attr.string_list(
doc = "List of source files that will be generated.",
mandatory = True,
),
"generated_testing_header_files": attr.string_list(
doc = "List of header files for testing that will be generated.",
default = [],
),
"generated_conversion_header_files": attr.string_list(
doc = "List of header files for HLCPP conversion that will be generated.",
default = [],
),
"fidlgen_tool": attr.label(
doc = "fidlgen tool.",
mandatory = True,
executable = True,
cfg = "exec",
),
"_clang_format": attr.label(
doc = "clang-format tool.",
executable = True,
allow_single_file = True,
cfg = "exec",
default = "@fuchsia_clang//:bin/clang-format",
),
},
)
def _sources_wrapper_impl(ctx):
return [DefaultInfo(files = ctx.attr.generated_fidl_cc_bindings[_FidlCcCodegenInfo].sources)]
_sources_wrapper = rule(
doc = "Simply declares the source files generated by the " +
"`_generate_fidl_cc_bindings` target as outputs. This allows the " +
"source files to be exposed as sources in their own rule.",
implementation = _sources_wrapper_impl,
attrs = {
"generated_fidl_cc_bindings": attr.label(
doc = "The `_generate_fidl_cc_bindings` target generating the source files",
mandatory = True,
allow_files = False,
providers = [_FidlCcCodegenInfo],
),
},
)
def _testing_headers_wrapper_impl(ctx):
return [DefaultInfo(
files = ctx.attr.generated_fidl_cc_bindings[_FidlCcCodegenInfo].testing_headers,
)]
_testing_headers_wrapper = rule(
doc = "Simply declares the testing header files generated by the " +
"`_generate_fidl_cc_bindings` target as outputs. This allows the " +
"testing header files to be exposed as sources in their own rule.",
implementation = _testing_headers_wrapper_impl,
attrs = {
"generated_fidl_cc_bindings": attr.label(
doc = "The `_generate_fidl_cc_bindings` target generating the testing header files.",
mandatory = True,
allow_files = False,
providers = [_FidlCcCodegenInfo],
),
},
)
def _conversion_headers_wrapper_impl(ctx):
return [DefaultInfo(
files = ctx.attr.generated_fidl_cc_bindings[_FidlCcCodegenInfo].conversion_headers,
)]
_conversion_headers_wrapper = rule(
doc = "Simply declares the HLCPP conversion header files generated by the " +
"`_generate_fidl_cc_bindings` target as outputs. This allows the " +
"HLCPP conversion header files to be exposed as sources in their own rule.",
implementation = _conversion_headers_wrapper_impl,
attrs = {
"generated_fidl_cc_bindings": attr.label(
doc = "The `_generate_fidl_cc_bindings` target generating the HLCPP conversion header files.",
mandatory = True,
allow_files = False,
providers = [_FidlCcCodegenInfo],
),
},
)