blob: eff5ec607f941681898e98f30182ed81ebeedb9b [file] [log] [blame]
# Copyright 2022 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(":providers.bzl", "FuchsiaFidlLibraryInfo")
_CodegenInfo = provider("Carries generated information across FIDL bindings code generation ", fields = ["files"])
def _typed_deps(deps, binding_type):
result = []
for dep in deps:
if ":" in dep: # appends the binding_type suffix
dep_with_binding = get_cc_lib_name(dep, binding_type)
else:
# assumption: this is a path with an implicit target name
# TODO: THIS WILL FAIL eventually with local targets without ":" for example.
# A better implementation is needed! We can't use Label() here because this is evaluated
# in the generator context, not the SDK context.
# One possible better implementation is to make fuchsia_fidl carry all the transitive dependencies per
# cc binding type, and provide them as a Provider, and then just collect them in _codegen and make them
# _codegen's own dependencies, which would probably cause native.cc_library to do the right thing.
dep_label = dep.rpartition("/")[2]
dep_label = get_cc_lib_name(dep_label, binding_type)
dep_with_binding = "%s:%s" % (dep, dep_label)
result.append(dep_with_binding)
return result
def get_cc_lib_name(fidl_target_name, binding_type):
return "{fidl}_{binding_type}".format(fidl = fidl_target_name, binding_type = binding_type)
def fuchsia_fidl_cc_library(name, library, binding_type = "cpp_wire", sdk_for_default_deps = None, deps = [], tags = [], **kwargs):
"""Generates cc_library() for the given fidl_library.
Args:
name: Target name. Required.
library: fidl_library() target to generate the language bindings for. Required.
binding_type: the FIDL binding type, for example "cpp_wire", "cpp_driver" or "cpp_natural_types". Defaults to "cpp_wire"
sdk_for_default_deps: Name of the Bazel workspace where default FIDL library dependencies are defined. If empty or not defined, defaults to the current repository.
deps: Additional dependencies.
tags: Optional tags.
**kwargs: Remaining args.
"""
gen_name = "%s_codegen" % name
impl_name = "%s_impl" % name
_codegen(
name = gen_name,
library = library,
binding_type = binding_type,
sdk_for_default_deps = sdk_for_default_deps,
)
_impl_wrapper(
name = impl_name,
codegen = ":%s" % gen_name,
)
native.cc_library(
name = name,
hdrs = [
":%s" % gen_name,
],
srcs = [
":%s" % impl_name,
# For the coding tables.
library,
],
# This is necessary in order to locate generated headers.
strip_include_prefix = gen_name + "." + binding_type,
deps = _typed_deps(deps, binding_type) + _get_binding_info(sdk_for_default_deps, binding_type)["deps"],
tags = tags,
**kwargs
)
def _codegen_impl(context):
sdk = context.toolchains["@rules_fuchsia//fuchsia:toolchain"]
ir = context.attr.library[FuchsiaFidlLibraryInfo].ir
name = context.attr.library[FuchsiaFidlLibraryInfo].name
base_path = context.attr.name + "." + context.attr.binding_type
# This declaration is needed in order to get access to the full path.
root = context.actions.declare_directory(base_path)
b = _get_binding_info(context.attr.sdk_for_default_deps, context.attr.binding_type)
header_files = []
header_files.extend(b["headers"])
source_files = []
source_files.extend(b["sources"])
for layer_dep in b["layer_deps"]:
layer_dep_b = _get_binding_info(context.attr.sdk_for_default_deps, layer_dep)
header_files.extend(layer_dep_b["headers"])
source_files.extend(layer_dep_b["sources"])
# TODO(fxbug.dev/108680): Better workaround for skipping codegen for zx.
if name == "zx":
source_files = ["markers.h"]
dir = base_path + "/fidl/" + name + "/cpp"
headers = []
sources = []
for header in header_files:
headers.append(context.actions.declare_file(dir + "/" + header))
for source in source_files:
sources.append(context.actions.declare_file(dir + "/" + source))
outputs = [root] + headers + sources
context.actions.run(
executable = sdk.fidlgen_cpp,
arguments = [
"--json",
ir.path,
"--root",
root.path,
],
inputs = [
ir,
],
outputs = outputs,
mnemonic = "FidlGenCc",
)
return [
_CodegenInfo(files = depset(sources)),
DefaultInfo(files = depset(headers)),
]
# Runs fidlgen to produce both the header file and the implementation file.
# Only exposes the header as a source, as the two files need to be consumed by
# the cc_library as two separate rules.
_codegen = rule(
implementation = _codegen_impl,
toolchains = ["@rules_fuchsia//fuchsia:toolchain"],
# Files must be generated in genfiles in order for the header to be included
# anywhere.
output_to_genfiles = True,
attrs = {
"library": attr.label(
doc = "The FIDL library to generate code for",
mandatory = True,
allow_files = False,
providers = [FuchsiaFidlLibraryInfo],
),
"binding_type": attr.string(
doc = "Type of bindings to expose",
mandatory = True,
),
"sdk_for_default_deps": attr.string(
doc = "Name of the Bazel workspace where default FIDL library dependencies are defined. If empty or not defined, defaults to the current repository.",
mandatory = False,
),
},
)
def _impl_wrapper_impl(context):
files = context.attr.codegen[_CodegenInfo].files
return [DefaultInfo(files = files)]
# Simply declares the implementation file generated by the codegen target as an
# output.
# This allows the implementation file to be exposed as a source in its own rule.
_impl_wrapper = rule(
implementation = _impl_wrapper_impl,
output_to_genfiles = True,
attrs = {
"codegen": attr.label(
doc = "The codegen rules generating the implementation file",
mandatory = True,
allow_files = False,
providers = [_CodegenInfo],
),
},
)
def _get_binding_info(sdk_for_default_deps, binding_type):
# Note: deps needs to be flattened, since Starlark does not support
# recursivity or unbounded loops.
if not sdk_for_default_deps:
sdk_for_default_deps = ""
wire_dep = sdk_for_default_deps + "//pkg/fidl_cpp_wire"
natural_dep = sdk_for_default_deps + "//pkg/fidl_cpp_v2"
driver_dep = sdk_for_default_deps + "//pkg/fidl_driver"
driver_natural_dep = sdk_for_default_deps + "//pkg/fidl_driver_natural"
bindings = {
"cpp_common": {
"headers": ["common_types.h", "markers.h"],
"sources": ["common_types.cc"],
"layer_deps": [],
"deps": [wire_dep],
},
"cpp_wire_types": {
"headers": ["wire_types.h"],
"sources": ["wire_types.cc"],
"layer_deps": ["cpp_common"],
"deps": [],
},
"cpp_wire": {
"headers": ["wire.h", "wire_messaging.h"],
"sources": ["wire_messaging.cc"],
"layer_deps": ["cpp_wire_types", "cpp_common"],
"deps": [wire_dep],
},
"cpp_wire_testing": {
"headers": ["wire_test_base.h"],
"sources": [""],
"layer_deps": ["cpp_wire", "cpp_wire_types", "cpp_common"],
"deps": [],
},
"cpp_natural_types": {
"headers": ["natural_types.h"],
"sources": ["natural_types.cc"],
"layer_deps": ["cpp_common"],
"deps": [],
},
"cpp_natural_ostream": {
"headers": ["natural_ostream.h"],
"sources": ["natural_ostream.cc"],
"layer_deps": ["cpp_natural_types", "cpp_common"],
"deps": [],
},
"cpp_type_conversions": {
"headers": ["type_conversions.h"],
"sources": ["type_conversions.cc"],
"layer_deps": ["cpp_natural_types", "cpp_wire", "cpp_common", "cpp_wire_types"],
"deps": [],
},
"cpp": {
"headers": ["fidl.h", "natural_messaging.h"],
"sources": ["natural_messaging.cc"],
"layer_deps": ["cpp_natural_types", "cpp_type_conversions", "cpp_wire", "cpp_common", "cpp_wire_types"],
"deps": [wire_dep, natural_dep],
},
"cpp_driver_wire": {
"headers": ["driver/wire.h", "driver/wire_messaging.h"],
"sources": ["driver/wire_messaging.cc"],
"layer_deps": ["cpp_wire_types", "cpp_common"],
"deps": [wire_dep, driver_dep],
},
"cpp_driver": {
"headers": ["driver/wire.h", "driver/wire_messaging.h", "natural_messaging.h", "driver/natural_messaging.h"],
"sources": ["driver/wire_messaging.cc", "natural_messaging.cc", "driver/natural_messaging.cc"],
"layer_deps": ["cpp_wire_types", "cpp_common", "cpp_natural_types", "cpp_type_conversions", "cpp_wire"],
"deps": [wire_dep, natural_dep, driver_dep, driver_natural_dep],
},
"cpp_hlcpp_conversion": {
"headers": ["hlcpp_conversion.h"],
"sources": [""],
"layer_deps": ["cpp_natural_types", "cpp_common"],
"deps": [],
},
}
if binding_type not in bindings:
fail("Unsupported binding type: %s" % binding_type)
return bindings[binding_type]