blob: 0031df8641e87bb3eca28249a7d7b0398f0ad29f [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>
_llcpp_output_stem = "llcpp/fidl"
_hlcpp_output_stem = "cpp/fidl"
_natural_types_output_stem = "cpp/natural_types"
_unified_cpp_output_stem = "cpp/fidl_v2"
_libfuzzer_output_stem = "cpp/libfuzzer"
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")
library_name = invoker.library_name
include_base =
"$fidl_target_gen_dir/$fidl_target_name/${invoker.bindings_flavor}"
include_stem =
string_replace(library_name, ".", "/") + "/" + invoker.output_stem
file_stem = "$include_base/$include_stem"
json_representation = "$fidl_target_gen_dir/$fidl_target_name.fidl.json"
generation_visibility = [
":$target_name",
"${invoker.fidlgen_tool}:*",
]
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}" ]
}
}
compiled_action(generation_target_name) {
# supply defaults for various options
forward_variables_from(invoker,
[
"testonly",
"experimental_split_generation_domain_objects",
"generates_test_base_header",
"generates_decoder_encoders",
"additional_includes_natural_types",
"additional_includes_hlcpp_bindings",
"additional_includes_wire_bindings",
])
if (!defined(experimental_split_generation_domain_objects)) {
experimental_split_generation_domain_objects = false
}
if (!defined(generates_decoder_encoders)) {
generates_decoder_encoders = false
}
if (!defined(additional_includes_natural_types)) {
additional_includes_natural_types = false
}
if (!defined(additional_includes_hlcpp_bindings)) {
additional_includes_hlcpp_bindings = false
}
if (!defined(additional_includes_wire_bindings)) {
additional_includes_wire_bindings = false
}
visibility = generation_visibility
tool = invoker.fidlgen_tool
inputs = [ json_representation ]
args = [
"--json",
rebase_path(json_representation, root_build_dir),
"--include-base",
rebase_path(include_base, root_build_dir),
"--include-stem",
invoker.output_stem,
"--clang-format-path",
rebase_path("${clang_prefix}/clang-format", "", root_build_dir),
]
header_file = "${file_stem}.h"
source_file = "${file_stem}.cc"
outputs = [
header_file,
source_file,
]
args += [
"--header",
rebase_path(header_file, root_build_dir),
"--source",
rebase_path(source_file, root_build_dir),
]
if (generates_test_base_header) {
test_base_file = "${file_stem}_test_base.h"
outputs += [ test_base_file ]
args += [
"--test-base",
rebase_path(test_base_file, root_build_dir),
]
}
if (generates_decoder_encoders) {
decoder_encoder_header_file = "${file_stem}_decode_encode.h"
decoder_encoder_source_file = "${file_stem}_decode_encode.cc"
outputs += [
decoder_encoder_header_file,
decoder_encoder_source_file,
]
args += [
"--decoder-encoder-header",
rebase_path(decoder_encoder_header_file, root_build_dir),
"--decoder-encoder-source",
rebase_path(decoder_encoder_source_file, root_build_dir),
]
}
# test base header is omitted when only generating domain objects.
if (experimental_split_generation_domain_objects) {
assert(!generates_test_base_header)
args += [ "--experimental-split-generation-domain-objects" ]
}
if (additional_includes_natural_types) {
args += [
"--natural-domain-objects-include-stem",
_natural_types_output_stem,
]
}
if (additional_includes_hlcpp_bindings) {
args += [
"--hlcpp-bindings-include-stem",
_hlcpp_output_stem,
]
}
if (additional_includes_wire_bindings) {
args += [
"--wire-bindings-include-stem",
_llcpp_output_stem,
]
}
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
#
# library_name (required)
# Name of the FIDL library.
#
# fidl_target_name (required)
# Name of the GN target corresponding to the FIDL library.
#
# output_stem (required)
# A suffix to name generated files, after library path, and before
# any file extension.
#
# fidlgen_tool (required)
# The code generation tool to use, as an absolute GN label.
#
# bindings_flavor (required)
# Identifies which kind of library (hlcpp, llcpp, ...).
# This should be the same suffix as the one used when instantiating
# the fidl_cpp_library template.
#
# fuzzers (optional)
# A list of { protocol = ..., methods = ... } scopes declaring which
# methods will be fuzzed by a fuzzer.
#
# experimental_split_generation_domain_objects (optional)
# If true, only generate the domain objects (structs, tables, ...)
# for this library.
#
# generates_test_base_header (required)
# Specifies if the tool will generate a test base header.
# If experimental_split_generation_domain_objects is true, then this
# parameter must be false, as only protocols require a test base.
# This is required to support hermetic actions.
#
# generates_decoder_encoders (optional)
# Specifies if the tool will generate fuzzer decoder encoder
# wrapper implementations.
#
# additional_includes_natural_types (optional)
# If true, the generated code depends on the natural domain object types
# in addition to its own headers. Informs the tool the natural types
# include stem.
#
# additional_includes_hlcpp_bindings (optional)
# If true, the generated code depends on the HLCPP bindings in addition to
# its own headers. Informs the tool the HLCPP bindings include stem.
#
# additional_includes_wire_bindings (optional)
# If true, the generated code depends on the wire bindings in addition to
# its own headers. Informs the tool the wire bindings include stem.
#
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") {
config_target_name = "${target_name}__config"
generation_target_name =
"${invoker.fidl_target_name}_${invoker.bindings_flavor}_generate"
library_name = invoker.library_name
fidl_target_name = invoker.fidl_target_name
fidl_target_gen_dir =
get_label_info(":$target_name($fidl_toolchain)", "target_gen_dir")
include_base =
"$fidl_target_gen_dir/$fidl_target_name/${invoker.bindings_flavor}"
include_stem =
string_replace(library_name, ".", "/") + "/" + invoker.output_stem
file_stem = "$include_base/$include_stem"
config(config_target_name) {
include_dirs = [ include_base ]
}
source_set(target_name) {
forward_variables_from(invoker,
[
"defines",
"testonly",
"visibility",
])
if (defined(invoker.header_only) && invoker.header_only) {
sources = [ "$file_stem.h" ]
} else if (defined(invoker.source_only) && invoker.source_only) {
sources = [ "$file_stem.cc" ]
} else {
sources = [
"$file_stem.cc",
"$file_stem.h",
]
}
# Note: decoder/encoders are completely enabled/disabled by `invoker.includes_decoder_encoders`.
# Its sources and headers are do not honour `invoker.(header|source)_only`.
if (defined(invoker.includes_decoder_encoders) &&
invoker.includes_decoder_encoders) {
sources += [
"${file_stem}_decode_encode.cc",
"${file_stem}_decode_encode.h",
]
}
# 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" ]
# 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).
not_needed(invoker, [ "bindings_flavor" ])
if (defined(invoker.public_deps)) {
foreach(dep, invoker.public_deps) {
label = get_label_info(dep, "label_no_toolchain")
public_deps += [ "${label}_${invoker.bindings_flavor}" ]
}
}
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 library (hlcpp, llcpp, ...).
# This should be the same suffix as the one used when instantiating
# the fidl_cpp_codegen template.
#
# 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) {
_fidl_cpp_library_impl(target_name) {
forward_variables_from(invoker, "*")
}
} else {
# No-op under FIDL toolchain
not_needed([ "target_name" ])
not_needed(invoker, "*")
}
}
template("_fidl_cpp_fuzzer") {
fuzzer = invoker.fuzzer
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 = [ "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) {
defines = library_defines
library_name = invoker.library_name
forward_variables_from(invoker,
[
"testonly",
"visibility",
"public_deps",
"fidl_target_name",
])
output_stem = _libfuzzer_output_stem
bindings_flavor = bindings_flavor
additional_public_deps = [ ":${invoker.fidl_target_name}_libfuzzer" ]
source_only = true
}
}
# 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
}
# `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.
# Additionally, an empty llcpp_public_deps indicates that the LLCPP
# bindings for this library is not needed.
llcpp_public_deps = []
if (is_fuchsia) {
not_needed(invoker, [ "host_llcpp" ])
hlcpp_public_deps = [ "//sdk/lib/fidl/cpp" ]
llcpp_public_deps = [
"//sdk/lib/fit",
"//zircon/public/lib/fidl",
"//zircon/public/lib/fidl-llcpp",
]
} else {
# On host.
hlcpp_public_deps = [ "//sdk/lib/fidl/cpp:cpp_base" ]
if (defined(invoker.host_llcpp) && invoker.host_llcpp) {
llcpp_public_deps = [
"//sdk/lib/fit",
"//zircon/public/lib/fidl-llcpp",
]
} else {
# Do not generate LLCPP if not requested.
llcpp_public_deps = []
}
}
common_options = {
fidl_target_name = fidl_target_name
library_name = library_name
forward_variables_from(invoker, [ "testonly" ])
}
natural_types_public_deps = [ "//sdk/lib/fidl/cpp:cpp_base" ]
#
# HLCPP Bindings
#
# Define C++ natural types target.
# They are generated using the HLCPP backend.
fidl_cpp_codegen("${target_name}_cpp_natural_types") {
forward_variables_from(common_options, "*")
output_stem = _natural_types_output_stem
bindings_flavor = "cpp_natural_types"
fidlgen_tool = "//tools/fidl/fidlgen_hlcpp"
experimental_split_generation_domain_objects = true
generates_test_base_header = false
generates_decoder_encoders = false
}
fidl_cpp_library("${target_name}_cpp_natural_types") {
forward_variables_from(invoker,
[
"public_deps",
"visibility",
])
forward_variables_from(common_options, "*")
output_stem = _natural_types_output_stem
bindings_flavor = "cpp_natural_types"
additional_public_deps = natural_types_public_deps
}
# Define HLCPP target.
# TODO(fxbug.dev/64093): Currently the HLCPP generated code embeds the
# natural types inline. Change this to depend on the dedicated natural types
# target when the unified bindings and HLCPP are used in one program.
fidl_cpp_codegen("${target_name}_hlcpp") {
forward_variables_from(common_options, "*")
output_stem = _hlcpp_output_stem
bindings_flavor = "hlcpp"
fidlgen_tool = "//tools/fidl/fidlgen_hlcpp"
generates_test_base_header = true
generates_decoder_encoders = false
}
fidl_cpp_library("${target_name}_hlcpp") {
forward_variables_from(invoker,
[
"public_deps",
"visibility",
])
forward_variables_from(common_options, "*")
output_stem = _hlcpp_output_stem
bindings_flavor = "hlcpp"
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" ])
public_deps = [ ":${target_name}_hlcpp" ]
}
# Set up an 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
#
# Define LLCPP target, if requested.
if (llcpp_public_deps != []) {
fidl_cpp_codegen("${target_name}_llcpp") {
forward_variables_from(common_options, "*")
output_stem = _llcpp_output_stem
bindings_flavor = "llcpp"
fidlgen_tool = "//tools/fidl/fidlgen_llcpp"
generates_test_base_header = true
}
fidl_cpp_library("${target_name}_llcpp") {
forward_variables_from(invoker,
[
"public_deps",
"visibility",
])
forward_variables_from(common_options, "*")
output_stem = _llcpp_output_stem
bindings_flavor = "llcpp"
additional_public_deps = llcpp_public_deps
}
}
# Unified C++ Bindings
#
# The unified bindings imports the natural types from HLCPP and the
# entire LLCPP bindings, and extends it with messaging APIs that works
# with the natural types.
#
# Define the unified bindings target.
# Note that we also check llcpp_public_deps here, because we would not
# generate the corresponding LLCPP bindings if it was empty.
if (llcpp_public_deps != []) {
fidl_cpp_codegen("${target_name}_cpp") {
forward_variables_from(common_options, "*")
output_stem = _unified_cpp_output_stem
bindings_flavor = "cpp"
fidlgen_tool = "//tools/fidl/fidlgen_cpp"
generates_test_base_header = false
additional_includes_natural_types = true
additional_includes_wire_bindings = true
}
fidl_cpp_library("${target_name}_cpp") {
forward_variables_from(invoker,
[
"public_deps",
"visibility",
])
forward_variables_from(common_options, "*")
output_stem = _unified_cpp_output_stem
bindings_flavor = "cpp"
additional_public_deps = [
":${fidl_target_name}_cpp_natural_types",
":${fidl_target_name}_llcpp",
]
}
}
#
# Fuzzers (also dependent on HLCPP and LLCPP)
#
# Define fuzzer targets.
fidl_cpp_codegen("${target_name}_libfuzzer") {
testonly = true
forward_variables_from(invoker, [ "fuzzers" ])
forward_variables_from(common_options, "*")
output_stem = _libfuzzer_output_stem
bindings_flavor = "libfuzzer"
fidlgen_tool = "//tools/fidl/fidlgen_libfuzzer"
generates_test_base_header = false
# Note: Must have matching `includes_decoder_encoders` in `fidl_cpp_library` below.
generates_decoder_encoders = true
additional_includes_hlcpp_bindings = true
additional_includes_wire_bindings = true
}
# Define fuzzer header library.
fidl_cpp_library("${target_name}_libfuzzer") {
testonly = true
forward_variables_from(invoker,
[
"public_deps",
"visibility",
])
forward_variables_from(common_options, "*")
output_stem = _libfuzzer_output_stem
bindings_flavor = "libfuzzer"
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.
header_only = true
# Note: Must have matching `generates_decoder_encoders` in `fidl_cpp_codegen` above.
# Note: decoder/encoders are completely enabled/disabled by `invoker.includes_decoder_encoders`.
# Its sources and headers do not honour `invoker.(header|source)_only`.
includes_decoder_encoders = true
}
# 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) {
_fidl_cpp_fuzzer(target_name) {
testonly = true
forward_variables_from(invoker, "*")
fidl_target_name = fidl_target_name
}
}
}
}