blob: 866de159f0b9ecd811019260725f8be8e929f4f1 [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/cpp/hlcpp_visibility.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"
root = "${invoker.fidl_gen_dir}/${invoker.bindings_flavor}"
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,
[
"applicable_licenses",
"testonly",
"fidl_ir_json",
"fidl_ir_target",
])
visibility = generation_visibility
tool = invoker.fidlgen_tool
mnemonic = "FIDLGEN"
inputs = [
fidl_ir_json,
"${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(fidl_ir_json, 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_ir_target ]
metadata = {
generated_sources = rebase_path(outputs, root_build_dir)
}
}
}
# Generates C++ code for a given FIDL library: new C++ bindings,
# high-level C++ bindings, or the fuzzer header library.
#
# Parameters
#
# fidl_gen_dir (required)
# The directory into which bindings should be generated.
#
# bindings_flavor (required)
# Identifies which kind of bindings (hlcpp, cpp, ...).
# This should be the same flavor as the one used when instantiating
# the fidl_cpp_library template.
#
# dep_suffix (optional)
# What to add to the end of FIDL dependency labels to get the label for bindings.
# 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.
#
# 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") {
forward_variables_from(invoker,
[
"fidl_target",
"fidl_gen_dir",
])
generation_target_name = "${fidl_target}_${invoker.bindings_flavor}_generate"
root = "$fidl_gen_dir/${invoker.bindings_flavor}"
not_needed([ "root" ])
if (defined(invoker.generated_headers)) {
config_target_name = "${target_name}__config"
config(config_target_name) {
include_dirs = [ root ]
}
}
source_set(target_name) {
forward_variables_from(invoker,
[
"applicable_licenses",
"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" ]
}
}
# The generated bindings code is part of the SDK surface. Our customers compile
# the generated code with potentially stricter warnings, so we need to ensure
# that the code works in their diverse environments.
#
# TODO(https://fxbug.dev/42071929): Remove -Wshadow when all SDK code builds with that flag.
cflags_cc = [ "-Wshadow" ]
configs += [ "//build/config:sdk_extra_warnings" ]
if (defined(invoker.additional_configs)) {
configs += invoker.additional_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)",
":${fidl_target}($fidl_toolchain)",
]
# Map FIDL library dependencies to generated library dependencies
# of the same type (identified by bindings_flavor or dep_suffix).
dep_suffix = "_${invoker.bindings_flavor}"
if (defined(invoker.dep_suffix)) {
dep_suffix = invoker.dep_suffix
}
not_needed([ "dep_suffix" ])
if (defined(invoker.public_deps)) {
if (invoker.public_deps != []) {
foreach(dep, invoker.public_deps) {
label = get_label_info(dep, "label_no_toolchain")
if (label != "//zircon/vdso/zx:zx") {
public_deps += [ "${label}${dep_suffix}" ]
}
}
}
}
if (defined(invoker.additional_public_deps)) {
public_deps += invoker.additional_public_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 (required)
# Name of the GN target corresponding to the FIDL library.
#
# bindings_flavor (required)
# Identifies which kind of bindings (hlcpp, cpp, ...).
# This should be the same suffix as the one used when instantiating
# the fidl_cpp_codegen 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.
#
# 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, "*")
}
not_needed(invoker, [ "library_name" ])
}
} else {
# No-op under FIDL toolchain
not_needed([ "target_name" ])
not_needed(invoker, "*")
}
}
# Generates various C++ FIDL bindings: New C++ bindings, HLCPP, and fuzzers.
#
# Parameters
#
# * library_name
# - Required: The name of the FIDL library.
# - Type: string
#
# * fidl_target
# - Required: The name of the associated fidl() target.
# - Type: string
#
# * fidl_ir_json
# - Required: The path to the associated FIDL IR JSON file.
# - Type: path
#
# * fidl_ir_target
# - Required: The label of the target that generates the FIDL IR JSON file.
# - Type: label
#
# * sdk_category
# - Optional: See //build/fidl/fidl.gni for a description.
# - Type: string
#
# * enable_hlcpp
# - Required: See //build/fidl/fidl.gni for a description.
# - Type: boolean
#
# * enable_cpp
# - Optional: See //build/fidl/fidl.gni for a description.
# - Type: boolean
#
# * testonly, visibility, public_deps
# - Optional: Usual GN meanings.
# 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") {
assert(defined(invoker.library_name),
"fidl_cpp_family(\"$target_name\") must define `library_name`")
assert(defined(invoker.fidl_target),
"fidl_cpp_family(\"$target_name\") must define `fidl_target`")
assert(defined(invoker.fidl_ir_json),
"fidl_cpp_family(\"$target_name\") must define `fidl_ir_json`")
assert(defined(invoker.fidl_ir_target),
"fidl_cpp_family(\"$target_name\") must define `fidl_ir_target`")
assert(defined(invoker.enable_hlcpp),
"fidl_cpp_family(\"$target_name\") must define `enable_hlcpp`")
forward_variables_from(invoker,
[
"library_name",
"enable_hlcpp",
])
enable_cpp = !defined(invoker.enable_cpp) || invoker.enable_cpp
# libfuzzer currently depends on HLCPP
if (defined(invoker.fuzzers) ||
(defined(invoker.golden_fuzzer) && invoker.golden_fuzzer)) {
enable_hlcpp = true
}
if (enable_hlcpp) {
library_name_slashes = string_replace(library_name, ".", "/")
}
if (current_toolchain == fidl_toolchain) {
not_needed(invoker, [ "sdk_category" ])
}
if (enable_hlcpp) {
#
# 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",
"$library_name_slashes/cpp/tables.c",
]
}
_hlcpp_target = "${target_name}_hlcpp"
fidl_cpp_codegen(_hlcpp_target) {
forward_variables_from(invoker,
[
"applicable_licenses",
"testonly",
"fidl_gen_dir",
"fidl_ir_json",
"fidl_ir_target",
])
forward_variables_from(hlcpp_options, "*")
fidlgen_tool = "//tools/fidl/fidlgen_hlcpp"
}
fidl_cpp_library("${target_name}_hlcpp") {
forward_variables_from(invoker,
[
"applicable_licenses",
"testonly",
"visibility",
"public_deps",
"fidl_target",
"fidl_gen_dir",
])
forward_variables_from(hlcpp_options, "*")
if (is_fuchsia) {
additional_public_deps = [ "//sdk/lib/fidl/cpp" ]
} else {
additional_public_deps = [ "//sdk/lib/fidl/cpp:cpp_base" ]
}
if (!defined(visibility) && !(defined(testonly) && testonly)) {
visibility = hlcpp_visibility
}
}
}
# Only define the SDK Atoms in the default toolchain.
if (current_toolchain == default_toolchain) {
# 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("${target_name}_hlcpp_sdk") {
atom = ":$sdk_target_name($fidl_toolchain)"
}
}
} else {
not_needed(invoker, [ "sdk_category" ])
}
#
# New C++ bindings
#
if (enable_cpp) {
_cpp_target = "${target_name}_cpp"
fidl_cpp_library(_cpp_target) {
forward_variables_from(invoker,
[
"applicable_licenses",
"testonly",
"visibility",
"public_configs",
"public_deps",
"fidl_target",
"fidl_gen_dir",
])
bindings_flavor = "cpp"
generated_headers = [
"fidl/$library_name/cpp/common_types.h",
"fidl/$library_name/cpp/markers.h",
"fidl/$library_name/cpp/wire_types.h",
"fidl/$library_name/cpp/natural_types.h",
"fidl/$library_name/cpp/wire.h",
"fidl/$library_name/cpp/natural_ostream.h",
"fidl/$library_name/cpp/type_conversions.h",
"fidl/$library_name/cpp/fidl.h",
]
generated_sources = [
"fidl/$library_name/cpp/common_types.cc",
"fidl/$library_name/cpp/natural_ostream.cc",
"fidl/$library_name/cpp/natural_types.cc",
"fidl/$library_name/cpp/type_conversions.cc",
"fidl/$library_name/cpp/wire_types.cc",
]
additional_public_deps = [
"//sdk/lib/fidl/cpp/wire",
"//sdk/lib/fit",
"//src/lib/fidl/cpp:cpp_base",
"//src/lib/fidl/cpp:natural_ostream",
]
if (is_fuchsia) {
generated_headers += [
"fidl/$library_name/cpp/wire_messaging.h",
"fidl/$library_name/cpp/natural_messaging.h",
]
generated_sources += [
"fidl/$library_name/cpp/natural_messaging.cc",
"fidl/$library_name/cpp/wire_messaging.cc",
]
additional_public_deps += [
"//sdk/lib/fidl",
"//src/lib/fidl/cpp",
]
}
if (defined(invoker.contains_drivers) && invoker.contains_drivers) {
generated_headers += [ "fidl/$library_name/cpp/driver/wire.h" ]
additional_public_deps += [
"//sdk/lib/fidl_driver",
"//sdk/lib/fidl_driver:fidl_driver_natural",
]
if (is_fuchsia) {
generated_headers += [
"fidl/$library_name/cpp/driver/wire_messaging.h",
"fidl/$library_name/cpp/driver/natural_messaging.h",
]
generated_sources += [
"fidl/$library_name/cpp/driver/natural_messaging.cc",
"fidl/$library_name/cpp/driver/wire_messaging.cc",
]
}
}
}
# Testing target
fidl_cpp_library("${target_name}_cpp_testing") {
forward_variables_from(invoker,
[
"applicable_licenses",
"testonly",
"visibility",
"public_configs",
"fidl_target",
"fidl_gen_dir",
"public_deps",
])
bindings_flavor = "cpp"
dep_suffix = "_cpp_testing"
generated_headers = [
"fidl/$library_name/cpp/test_base.h",
"fidl/$library_name/cpp/wire_test_base.h",
]
testonly = true
additional_public_deps = [ ":${_cpp_target}" ]
}
# SDK atoms for the new c++ bindings core and testing.
if (current_toolchain == default_toolchain) {
if (defined(invoker.sdk_category) && invoker.sdk_category != "excluded") {
sdk_atom_alias("${_cpp_target}_sdk") {
atom = ":$sdk_target_name($fidl_toolchain)"
}
sdk_atom_alias("${target_name}_cpp_testing_sdk") {
atom = ":$sdk_target_name($fidl_toolchain)"
}
}
}
}
if (enable_hlcpp) {
fidl_cpp_library("${target_name}_cpp_hlcpp_conversion") {
forward_variables_from(invoker,
[
"applicable_licenses",
"testonly",
"visibility",
"public_configs",
"fidl_target",
"fidl_gen_dir",
"public_deps",
])
bindings_flavor = "cpp"
dep_suffix = "_cpp_hlcpp_conversion"
generated_headers = [ "fidl/$library_name/cpp/hlcpp_conversion.h" ]
additional_public_deps = [
":${_cpp_target}",
":${_hlcpp_target}",
"//src/lib/fidl/cpp:hlcpp_conversion",
]
}
}
fidl_cpp_codegen("${target_name}_cpp") {
forward_variables_from(invoker,
[
"applicable_licenses",
"testonly",
"fidl_gen_dir",
"fidl_ir_json",
"fidl_ir_target",
])
bindings_flavor = "cpp"
fidlgen_tool = "//tools/fidl/fidlgen_cpp"
generated_headers = [
"fidl/$library_name/cpp/common_types.h",
"fidl/$library_name/cpp/driver/natural_messaging.h",
"fidl/$library_name/cpp/driver/wire_messaging.h",
"fidl/$library_name/cpp/driver/wire.h",
"fidl/$library_name/cpp/fidl.h",
"fidl/$library_name/cpp/hlcpp_conversion.h",
"fidl/$library_name/cpp/markers.h",
"fidl/$library_name/cpp/natural_messaging.h",
"fidl/$library_name/cpp/natural_ostream.h",
"fidl/$library_name/cpp/natural_types.h",
"fidl/$library_name/cpp/test_base.h",
"fidl/$library_name/cpp/type_conversions.h",
"fidl/$library_name/cpp/wire_messaging.h",
"fidl/$library_name/cpp/wire_test_base.h",
"fidl/$library_name/cpp/wire_types.h",
"fidl/$library_name/cpp/wire.h",
]
generated_sources = [
"fidl/$library_name/cpp/common_types.cc",
"fidl/$library_name/cpp/driver/natural_messaging.cc",
"fidl/$library_name/cpp/driver/wire_messaging.cc",
"fidl/$library_name/cpp/natural_messaging.cc",
"fidl/$library_name/cpp/natural_ostream.cc",
"fidl/$library_name/cpp/natural_types.cc",
"fidl/$library_name/cpp/type_conversions.cc",
"fidl/$library_name/cpp/wire_messaging.cc",
"fidl/$library_name/cpp/wire_types.cc",
]
additional_visibility = [
":${target_name}",
":${target_name}_testing",
":${target_name}_hlcpp_conversion",
]
}
#
# Fuzzers (also dependent on HLCPP and LLCPP)
#
if (enable_hlcpp) {
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,
[
"applicable_licenses",
"testonly",
"fuzzers",
"golden_fuzzer",
"fidl_gen_dir",
"fidl_ir_json",
"fidl_ir_target",
])
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,
[
"applicable_licenses",
"testonly",
"visibility",
"public_deps",
"fidl_target",
"fidl_gen_dir",
])
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 new C++ bindings headers
# generated by the clauses above, so the generated target needs
# that bindings library target in its public_deps.
":${fidl_target}_cpp",
":${fidl_target}_hlcpp",
]
# 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,
[
"applicable_licenses",
"testonly",
"visibility",
"public_deps",
"fidl_target",
"fidl_gen_dir",
])
forward_variables_from(libfuzzer_options, "*")
testonly = true
defines = library_defines
additional_public_deps = [ ":${fidl_target}_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,
[
"applicable_licenses",
"testonly",
"visibility",
"public_deps",
"fidl_target",
"fidl_gen_dir",
])
forward_variables_from(libfuzzer_options, "*")
testonly = true
defines = [ "GOLDEN_FUZZER" ]
additional_public_deps = [ ":${fidl_target}_libfuzzer" ]
generated_sources = libfuzzer_sources
}
}
}