blob: 0743ca8eaa56f897ac1e7782b2c52b1fe2a3403e [file] [log] [blame]
# Copyright 2018 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.
import("//build/compiled_action.gni")
import("//build/config/clang/clang.gni")
import("//build/fidl/toolchain.gni")
import("//build/sdk/sdk_atom_alias.gni")
# Define the include patterns for various binding flavors.
# Includes will be of the form <my/library/{output-stem}.h>
_hlcpp_output_stem = "cpp/fidl"
template("_fidl_cpp_codegen_impl") {
generation_target_name = "${target_name}_generate"
fidl_target_name = invoker.fidl_target_name
fidl_target_gen_dir =
get_label_info(":${fidl_target_name}($fidl_toolchain)", "target_gen_dir")
root = "$fidl_target_gen_dir/$fidl_target_name/${invoker.bindings_flavor}"
json_representation = "$fidl_target_gen_dir/$fidl_target_name.fidl.json"
generation_visibility = [
":$target_name",
"${invoker.fidlgen_tool}:*",
]
if (defined(invoker.additional_visibility)) {
generation_visibility += invoker.additional_visibility
}
if (defined(invoker.fuzzers)) {
foreach(fuzzer, invoker.fuzzers) {
assert(
defined(fuzzer.protocol),
"FIDL protocol fuzzers must set protocol: the fully-qualified name of the protocol to be fuzzed.")
protocol_suffix = "_" + string_replace(fuzzer.protocol, ".", "_")
if (defined(fuzzer.methods)) {
foreach(method, fuzzer.methods) {
protocol_suffix = "${protocol_suffix}_${method}"
}
}
generation_visibility += [ ":${target_name}${protocol_suffix}" ]
}
}
if (defined(invoker.golden_fuzzer) && invoker.golden_fuzzer) {
generation_visibility += [ ":${target_name}__golden" ]
}
compiled_action(generation_target_name) {
# supply defaults for various options
forward_variables_from(invoker, [ "testonly" ])
visibility = generation_visibility
tool = invoker.fidlgen_tool
inputs = [
json_representation,
"${clang_prefix}/clang-format",
]
outputs = []
if (defined(invoker.generated_sources)) {
foreach(output, invoker.generated_sources) {
outputs += [ "$root/$output" ]
}
}
if (defined(invoker.generated_headers)) {
foreach(output, invoker.generated_headers) {
outputs += [ "$root/$output" ]
}
}
args = [
"--json",
rebase_path(json_representation, root_build_dir),
"--clang-format-path",
"${rebased_clang_prefix}/clang-format",
"--root",
rebase_path(root, root_build_dir),
]
if (defined(invoker.experiments)) {
foreach(experiment, invoker.experiments) {
args += [
"--experiment",
experiment,
]
}
}
deps = [ ":$fidl_target_name($fidl_toolchain)" ]
metadata = {
generated_sources = rebase_path(outputs, root_build_dir)
}
}
}
# Generates C++ code for a given FIDL library: low-level C++ bindings,
# high-level C++ bindings, natural domain objects, unified C++ bindings,
# or the fuzzer header library.
#
# Parameters
#
# fidl_target_name (required)
# Name of the GN target corresponding to the FIDL library.
#
# bindings_flavor (required)
# Identifies which kind of bindings (hlcpp, llcpp, ...).
# This should be the same flavor as the one used when instantiating
# the fidl_cpp_library template.
#
# generated_sources (optional)
# A list of source files that will be generated for this binding, relative
# to the target-specific generation directory.
#
# generated_headers (optional)
# A list of header files that will be generated for this binding, relative
# to the target-specific generation directory.
#
# fidlgen_tool (required)
# The code generation tool to use, as an absolute GN label.
#
# fuzzers (optional)
# A list of { protocol = ..., methods = ... } scopes declaring which
# methods will be fuzzed by a fuzzer.
#
# golden_fuzzer (optional)
# Boolean flag to generate a LibFuzzer fuzz target for all protocols, used
# to ensure fuzzers for golden libraries compile successfully.
#
# additional_visibility (optional)
# A list of labels which are allowed to depend on the generated code.
template("fidl_cpp_codegen") {
if (current_toolchain == fidl_toolchain) {
_fidl_cpp_codegen_impl(target_name) {
forward_variables_from(invoker, "*")
}
} else {
# Code generation only happens under the FIDL toolchain.
not_needed([ "target_name" ])
not_needed(invoker, "*")
}
}
template("_fidl_cpp_library_impl") {
generation_target_name =
"${invoker.fidl_target_name}_${invoker.bindings_flavor}_generate"
not_needed(invoker, [ "library_name" ])
fidl_target_name = invoker.fidl_target_name
fidl_target_gen_dir =
get_label_info(":$target_name($fidl_toolchain)", "target_gen_dir")
root = "$fidl_target_gen_dir/$fidl_target_name/${invoker.bindings_flavor}"
if (defined(invoker.generated_headers)) {
config_target_name = "${target_name}__config"
config(config_target_name) {
include_dirs = [ root ]
# TODO(fxbug.dev/92670): The .cc.o files from generated sources
# contain a redundant output-dir.
# This needs to be propagated through public_deps to all
# Rust targets that link these.
configs = [ "//build/config/rust:output_dir_sensitive" ]
}
}
source_set(target_name) {
forward_variables_from(invoker,
[
"defines",
"testonly",
"visibility",
])
sources = []
public = []
if (defined(invoker.generated_sources)) {
foreach(source, invoker.generated_sources) {
sources += [ "$root/$source" ]
}
}
if (defined(invoker.generated_headers)) {
foreach(header, invoker.generated_headers) {
sources += [ "$root/$header" ]
public += [ "$root/$header" ]
}
}
# TODO(fxbug.dev/56257): Remove this line when `-Wextra-semi` is on
# for all of Fuchsia by default.
cflags_cc = [ "-Wextra-semi" ]
# TODO(fxbug.dev/69585): We suppress deprecated raw channel usage
# in LLCPP generated code itself. Delete this line after everything
# migrates to typed channels.
configs += [ "//build/cpp:fidl-llcpp-deprecated-raw-channels-reserved-for-llcpp-generated-code-only" ]
if (defined(invoker.additional_configs)) {
configs += invoker.additional_configs
}
if (defined(invoker.public_configs)) {
public_configs = invoker.public_configs
} else {
public_configs = []
}
if (defined(invoker.generated_headers)) {
# Let dependencies use `#include "$file_stem.h"`.
public_configs += [ ":$config_target_name" ]
}
public_deps = [
":$generation_target_name($fidl_toolchain)",
":${invoker.fidl_target_name}($fidl_toolchain)",
":${invoker.fidl_target_name}_tables",
]
# Map FIDL library dependencies to generated library dependencies
# of the same type (identified by bindings_flavor or bindings_layer).
not_needed(invoker, [ "bindings_flavor" ])
not_needed(invoker, [ "bindings_layer" ])
if (defined(invoker.public_deps)) {
if (invoker.public_deps != []) {
if (defined(invoker.bindings_layer)) {
bindings_layer = invoker.bindings_layer
} else {
bindings_layer = invoker.bindings_flavor
}
foreach(dep, invoker.public_deps) {
label = get_label_info(dep, "label_no_toolchain")
if (label == "//zircon/vdso/zx:zx") {
# For the zx library, only link in the coding tables.
public_deps += [ "${label}_tables" ]
not_needed([ "bindings_layer" ])
} else {
public_deps += [ "${label}_${bindings_layer}" ]
}
}
}
}
public_deps += invoker.additional_public_deps
if (defined(invoker.deps)) {
public_deps += invoker.deps
}
}
}
# Defines a C++ library target (source_set) generated from a FIDL library.
#
# FIDL library dependencies under `public_deps` will manifest as corresponding
# library target dependencies.
#
# Parameters
#
# library_name (required)
# Name of the FIDL library.
#
# fidl_target_name (required)
# Name of the GN target corresponding to the FIDL library.
#
# bindings_flavor (required)
# Identifies which kind of bindings (hlcpp, llcpp, ...).
# This should be the same suffix as the one used when instantiating
# the fidl_cpp_codegen template.
#
# bindings_layer (optional)
# A particular binding may be decomposed into multiple layers. In that case,
# this identifies the particular layer that makes up this library (e.g.
# domain objects layer, messaging layer, ...). When adding dependencies,
# a particular layer for a FIDL library depends on the corresponding
# layers of the dependencies of that FIDL library.
#
# Some examples of valid layers are:
# * llcpp: Wire messaging over channel transport
# * wire_types: Wire domain objects
# * driver: Unified messaging over driver transport
#
# If missing, defaults to bindings_flavor.
#
# generated_sources (optional)
# A list of source files that will be generated for this binding, relative
# to the target-specific generation directory.
#
# generated_headers (optional)
# A list of header files that will be generated for this binding, relative
# to the target-specific generation directory.
#
# header_only (optional)
# If true, the generated library only has a header.
#
# source_only (optional)
# If true, the generated library only has a source file.
#
template("fidl_cpp_library") {
if (current_toolchain != fidl_toolchain) {
if (invoker.library_name == "zx") {
# The zx FIDL library isn't generated with C++ fidlgens.
not_needed(invoker, "*")
group(target_name) {
}
} else {
_fidl_cpp_library_impl(target_name) {
forward_variables_from(invoker, "*")
}
}
} else {
# No-op under FIDL toolchain
not_needed([ "target_name" ])
not_needed(invoker, "*")
}
}
# Generates various C++ FIDL bindings: HLCPP, LLCPP, Unified C++, and fuzzers.
#
# Note:
# - Under the FIDL toolchain, we would create action targets that
# generate the C++ bindings.
# - Under other toolchains, we would create library targets that
# reference the generated code.
#
template("fidl_cpp_family") {
# Allow users to override the library name by specifying a
# "name" variable in the `fidl("my_lib")` template invocation.
# Otherwise, default to the `target_name` of the FIDL library.
#
# Note that library names will have implication on the generated
# include paths, hence should be separate from `target_name`.
library_name = target_name
if (defined(invoker.name)) {
library_name = invoker.name
}
library_name_slashes = string_replace(library_name, ".", "/")
# `target_name` becomes clobbered in template invocation scopes;
# back it up here.
fidl_target_name = target_name
# `sources` is only required to invoke `fidlc`;
# the bindings code generation consumes the JSON IR instead.
not_needed(invoker, [ "sources" ])
# Determine the C++ bindings dependencies based on Fuchsia/host.
if (is_fuchsia) {
hlcpp_public_deps = [ "//sdk/lib/fidl/cpp" ]
llcpp_public_deps = [
"//sdk/lib/fidl/llcpp",
"//sdk/lib/fit",
"//zircon/system/ulib/fidl",
]
} else {
# On host.
hlcpp_public_deps = [ "//sdk/lib/fidl/cpp:cpp_base" ]
llcpp_public_deps = [
"//sdk/lib/fidl/llcpp",
"//sdk/lib/fit",
]
}
common_options = {
fidl_target_name = fidl_target_name
library_name = library_name
not_needed([ "library_name" ])
forward_variables_from(invoker, [ "testonly" ])
}
#
# HLCPP Bindings
#
# Define HLCPP target.
hlcpp_options = {
bindings_flavor = "hlcpp"
generated_headers = [
"$library_name_slashes/cpp/fidl.h",
"$library_name_slashes/cpp/fidl_test_base.h",
]
generated_sources = [ "$library_name_slashes/cpp/fidl.cc" ]
}
fidl_cpp_codegen("${target_name}_hlcpp") {
forward_variables_from(common_options, "*")
forward_variables_from(hlcpp_options, "*")
fidlgen_tool = "//tools/fidl/fidlgen_hlcpp"
}
fidl_cpp_library("${target_name}_hlcpp") {
forward_variables_from(invoker,
[
"public_deps",
"visibility",
])
forward_variables_from(common_options, "*")
forward_variables_from(hlcpp_options, "*")
additional_public_deps = hlcpp_public_deps
}
if (current_toolchain != fidl_toolchain) {
# Set up an alias from ":my_lib_hlcpp" to ":my_lib"
group(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
public_deps = [ ":${target_name}_hlcpp" ]
}
# Set up an HLCPP SDK item for this library
if (defined(invoker.sdk_category) && invoker.sdk_category != "excluded") {
# Instead of depending on the generated bindings, set up a dependency on
# the original library.
sdk_target_name = "${target_name}_sdk"
sdk_atom_alias(sdk_target_name) {
atom = ":$sdk_target_name($fidl_toolchain)"
}
}
}
#
# LLCPP Bindings
#
# These bindings are defined as a set of layers that build upon each other.
# All of the layers are generated in a single fidlgen invocation but are
# exposed as separate source_sets to user code so that it's easy to include
# just the parts of the bindings that you need.
llcpp_layers = [
{
layer = "common"
headers = [
"fidl/$library_name/cpp/common_types.h",
"fidl/$library_name/cpp/markers.h",
]
sources = [ "fidl/$library_name/cpp/common_types.cc" ]
public_deps = llcpp_public_deps
},
{
layer = "wire_types"
headers = [ "fidl/$library_name/cpp/wire_types.h" ]
sources = [ "fidl/$library_name/cpp/wire_types.cc" ]
layer_deps = [ "common" ]
if (defined(invoker.contains_drivers) && invoker.contains_drivers) {
public_deps = [ "//sdk/lib/fidl_driver" ]
}
},
{
layer = "llcpp" # AKA: wire_channel_messaging
headers = [
"fidl/$library_name/cpp/wire.h",
"fidl/$library_name/cpp/wire_messaging.h",
]
sources = [ "fidl/$library_name/cpp/wire_messaging.cc" ]
layer_deps = [ "wire_types" ]
},
{
layer = "llcpp_testing"
headers = [ "fidl/$library_name/cpp/wire_test_base.h" ]
layer_deps = [ "llcpp" ]
testonly = true
},
{
layer = "natural_types"
headers = [ "fidl/$library_name/cpp/natural_types.h" ]
sources = [ "fidl/$library_name/cpp/natural_types.cc" ]
public_deps = [ "//src/lib/fidl/cpp:cpp_base" ]
layer_deps = [ "common" ]
if (defined(invoker.contains_drivers) && invoker.contains_drivers) {
public_deps += [ "//sdk/lib/fidl_driver:fidl_driver_natural" ]
}
},
{
layer = "type_conversions"
headers = [ "fidl/$library_name/cpp/type_conversions.h" ]
sources = [ "fidl/$library_name/cpp/type_conversions.cc" ]
# TODO(fxbug.dev/101525): Type conversion should not depend on messaging layer.
# We should only need `cpp_base` here.
if (is_fuchsia) {
public_deps = [ "//src/lib/fidl/cpp" ]
} else {
public_deps = [ "//src/lib/fidl/cpp:cpp_base" ]
}
layer_deps = [
# TODO(fxbug.dev/101525): Replace with "wire_types".
"llcpp",
"natural_types",
]
},
{
layer = "cpp" # AKA: unified_channel_messaging
headers = [
"fidl/$library_name/cpp/fidl.h",
"fidl/$library_name/cpp/natural_messaging.h",
]
sources = [ "fidl/$library_name/cpp/natural_messaging.cc" ]
layer_deps = [
"llcpp",
"natural_types",
"type_conversions",
]
public_deps = [ "//src/lib/fidl/cpp" ]
},
{
layer = "driver_wire" # AKA: wire_driver_messaging
headers = [
"fidl/$library_name/cpp/driver/wire.h",
"fidl/$library_name/cpp/driver/wire_messaging.h",
]
sources = [ "fidl/$library_name/cpp/driver/wire_messaging.cc" ]
public_deps = [ "//sdk/lib/fidl_driver" ]
layer_deps = [ "wire_types" ]
},
{
layer = "driver" # AKA: driver_messaging
headers = [ "fidl/$library_name/cpp/driver/natural_messaging.h" ]
sources = [ "fidl/$library_name/cpp/driver/natural_messaging.cc" ]
public_deps = [ "//sdk/lib/fidl_driver:fidl_driver_natural" ]
layer_deps = [
"driver_wire",
"natural_types",
"type_conversions",
]
},
{
layer = "hlcpp_conversion"
headers = [ "fidl/$library_name/cpp/hlcpp_conversion.h" ]
public_deps = [
":${target_name}",
"//src/lib/fidl/cpp:hlcpp_conversion",
]
layer_deps = [ "natural_types" ]
},
]
not_needed([ "llcpp_layers" ])
# Define LLCPP targets, if requested.
layer_target_prefix = target_name + "_"
foreach(layer, llcpp_layers) {
layer_target_name = layer_target_prefix + layer.layer
fidl_cpp_library(layer_target_name) {
forward_variables_from(invoker,
[
"public_configs",
"public_deps",
"visibility",
])
forward_variables_from(common_options, "*")
bindings_flavor = "llcpp"
bindings_layer = layer.layer
if (defined(layer.headers)) {
generated_headers = layer.headers
}
if (defined(layer.sources)) {
generated_sources = layer.sources
}
if (defined(layer.testonly)) {
testonly = layer.testonly
}
additional_public_deps = []
if (defined(layer.public_deps)) {
additional_public_deps += layer.public_deps
}
if (defined(layer.layer_deps)) {
foreach(layer_dep, layer.layer_deps) {
additional_public_deps += [ ":${layer_target_prefix}${layer_dep}" ]
}
}
}
if (current_toolchain != fidl_toolchain) {
# Set up an LLCPP SDK item for this library
if (defined(invoker.sdk_category) && invoker.sdk_category != "excluded") {
# Instead of depending on the generated bindings, set up a dependency on
# the original library.
sdk_atom_alias("${layer_target_name}_sdk") {
atom = ":$sdk_target_name($fidl_toolchain)"
}
}
}
}
fidl_cpp_codegen("${target_name}_llcpp") {
forward_variables_from(common_options, "*")
bindings_flavor = "llcpp"
fidlgen_tool = "//tools/fidl/fidlgen_llcpp"
generated_headers = []
generated_sources = []
additional_visibility = []
foreach(layer, llcpp_layers) {
additional_visibility += [ ":${layer_target_prefix}${layer.layer}" ]
if (defined(layer.headers)) {
generated_headers += layer.headers
}
if (defined(layer.sources)) {
generated_sources += layer.sources
}
}
}
#
# Fuzzers (also dependent on HLCPP and LLCPP)
#
libfuzzer_options = {
testonly = true
bindings_flavor = "libfuzzer"
}
libfuzzer_headers = [
"$library_name_slashes/cpp/libfuzzer.h",
"$library_name_slashes/cpp/libfuzzer_decode_encode.h",
]
libfuzzer_sources = [
"$library_name_slashes/cpp/libfuzzer.cc",
"$library_name_slashes/cpp/libfuzzer_decode_encode.cc",
]
# Define fuzzer targets.
fidl_cpp_codegen("${target_name}_libfuzzer") {
forward_variables_from(invoker,
[
"fuzzers",
"golden_fuzzer",
])
forward_variables_from(common_options, "*")
forward_variables_from(libfuzzer_options, "*")
generated_headers = libfuzzer_headers
generated_sources = libfuzzer_sources
fidlgen_tool = "//tools/fidl/fidlgen_libfuzzer"
}
# Define fuzzer header library.
fidl_cpp_library("${target_name}_libfuzzer") {
forward_variables_from(invoker,
[
"public_deps",
"visibility",
])
forward_variables_from(common_options, "*")
forward_variables_from(libfuzzer_options, "*")
testonly = true
additional_public_deps = [
"//sdk/lib/fidl/cpp/fuzzing",
"//zircon/system/ulib/async-default",
"//zircon/system/ulib/async-loop:async-loop-cpp",
"//zircon/system/ulib/async-loop:async-loop-default",
# The generated headers `#include` the hlcpp and llcpp bindings headers
# generated by the clauses above, so the generated target needs
# that bindings library target in its public_deps.
":${fidl_target_name}_hlcpp",
":${fidl_target_name}_llcpp",
]
# Note: `.../libfuzzer.cc` is linked with different build-time parameters below in loop
# over `invoker.fuzzers`. The library itself contains headers only.
generated_headers = libfuzzer_headers
}
# Define fuzzer implementation libraries (one per fuzzed protocol).
# The source file is the same - different macro definitions
# are used to customize the fuzzing configuration.
if (defined(invoker.fuzzers)) {
foreach(fuzzer, invoker.fuzzers) {
assert(
defined(fuzzer.protocol),
"FIDL protocol fuzzers must set protocol: the fully-qualified name " +
"of the protocol to be fuzzed.")
protocol_suffix = "_" + string_replace(fuzzer.protocol, ".", "_")
library_defines = []
library_defines += [ "PROTOCOL${protocol_suffix}" ]
if (defined(invoker.defines)) {
library_defines += invoker.defines
}
if (defined(fuzzer.methods)) {
foreach(method, fuzzer.methods) {
library_defines += [ "METHOD_${method}" ]
}
} else {
library_defines += [ "ALL_METHODS" ]
}
bindings_flavor = "libfuzzer"
fuzzer_lib_name = "${target_name}_${bindings_flavor}${protocol_suffix}"
fidl_cpp_library(fuzzer_lib_name) {
forward_variables_from(invoker,
[
"visibility",
"public_deps",
"library_name",
])
forward_variables_from(common_options, "*")
forward_variables_from(libfuzzer_options, "*")
testonly = true
defines = library_defines
additional_public_deps = [ ":${fidl_target_name}_libfuzzer" ]
generated_sources = libfuzzer_sources
}
}
}
# If requested, generate a fuzz target that includes all protocols and methods
# so we can make sure the fuzzers for golden libraries compile successfully.
if (defined(invoker.golden_fuzzer) && invoker.golden_fuzzer) {
# Note: double underscore to prevent clashes with the fuzzer targets below.
fidl_cpp_library("${target_name}_libfuzzer__golden") {
forward_variables_from(invoker,
[
"visibility",
"public_deps",
"library_name",
])
forward_variables_from(common_options, "*")
forward_variables_from(libfuzzer_options, "*")
testonly = true
defines = [ "GOLDEN_FUZZER" ]
additional_public_deps = [ ":${fidl_target_name}_libfuzzer" ]
generated_sources = libfuzzer_sources
}
}
}