blob: fea9180bde60439b9e14f272fc27f81e2a171ad4 [file] [log] [blame]
# Copyright 2019 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/dist/generated_resource.gni")
import("//build/host.gni")
import("//build/rust/rustc_library.gni")
import("//build/testing/host_test.gni")
import("//build/testing/host_test_data.gni")
import("//build/sdk/sdk_atom.gni")
# Private template to generate an SDK Atom for bind_library.
#
template("_bind_library_sdk") {
library_name = target_name
if (defined(invoker.name)) {
library_name = invoker.name
}
# Process sources.
file_base = "bind/$library_name"
all_files = []
sdk_sources = []
sdk_unrebased_sources = []
source = invoker.source
sdk_unrebased_sources += [ source ]
relative_source = rebase_path(source, ".")
if (string_replace(relative_source, "..", "bogus") != relative_source) {
# If the source file is not within the same directory, just use the file
# name.
relative_source = get_path_info(source, "file")
}
destination = "$file_base/$relative_source"
sdk_sources += [ destination ]
all_files += [
{
source = source
dest = destination
},
]
# Identify metadata for dependencies.
sdk_metas = []
sdk_deps = []
all_deps = []
if (defined(invoker.deps)) {
all_deps = invoker.deps
}
foreach(dep, all_deps) {
full_label = get_label_info(dep, "label_no_toolchain")
sdk_dep = "${full_label}_sdk"
sdk_deps += [ sdk_dep ]
gen_dir = get_label_info(sdk_dep, "target_gen_dir")
name = get_label_info(sdk_dep, "name")
sdk_metas += [ "$gen_dir/$name.meta.json" ]
}
# Generate the library metadata.
meta_file = "$target_gen_dir/${target_name}.sdk_meta.json"
meta_target_name = "${target_name}_meta"
action(meta_target_name) {
script = "//build/bind/gen_sdk_meta.py"
inputs = sdk_metas
# Use the unrebased source name. GN automatically converts
# sources to be relative to the build directory.
sources = sdk_unrebased_sources
outputs = [ meta_file ]
args = [
"--out",
rebase_path(meta_file, root_build_dir),
"--name",
library_name,
"--root",
file_base,
"--specs",
] + rebase_path(sdk_metas, root_build_dir) + [ "--sources" ] +
sdk_sources
deps = sdk_deps
}
sdk_atom("${target_name}_sdk") {
id = "sdk://bind/$library_name"
category = invoker.sdk_category
meta = {
source = meta_file
dest = "$file_base/meta.json"
schema = "bind_library"
}
files = all_files
non_sdk_deps = [ ":$meta_target_name" ]
deps = []
foreach(dep, all_deps) {
label = get_label_info(dep, "label_no_toolchain")
deps += [ "${label}_sdk" ]
}
}
}
# Declares a suite of tests for a driver's bind rules.
#
# The tests run as host tests by invoking the compiler. To avoid specifying the rules and bind
# library dependencies twice use the `tests` parameter on the bind_rules template to generate this
# target with the appropriate parameters.
#
# Parameters
#
# rules (required)
# [path]: Path to the bind rules source file.
#
# tests (required)
# [path]: Path to a test specification. The test specification is a JSON file defining a set
# of devices and the expected result of the bind rules when applied to that device. The file
# must adhere to the JSON schema defined by //src/devices/bind/debugger/tests_schema.json.
#
# target (optional)
# [label]: The test target. Defaults to target_name.
#
# deps (optional)
# [list of labels]: List of bind_library targets included by the bind rules.
#
# visibility
# Forwarded from invoker.
#
template("bind_host_test") {
assert(defined(invoker.rules), "Need a bind rules source")
assert(defined(invoker.tests), "Need a test specification")
test_target = "_${target_name}_test"
group(target_name) {
testonly = true
if (is_host) {
deps = [ ":${test_target}" ]
}
forward_variables_from(invoker, [ "visibility" ])
}
if (is_host) {
response_file_target = "_${target_name}_response_file"
response_file = "${target_gen_dir}/${target_name}.rsp"
test_data_dir = "${root_out_dir}/test_data/bind-tests/${target_name}"
test_data_target = "_${target_name}_test_data"
generated_file(response_file_target) {
visibility = [ ":*" ]
testonly = true
forward_variables_from(invoker, [ "deps" ])
data_keys = [ "test_sources" ]
outputs = [ "${response_file}" ]
}
host_test_data(test_data_target) {
visibility = [ ":*" ]
sources = [
"${host_tools_dir}/bindc",
invoker.rules,
invoker.tests,
response_file,
]
outputs = [ "${test_data_dir}/{{source_file_part}}" ]
deps = [
":${response_file_target}",
"//tools/bindc:host($host_toolchain)",
]
}
host_test(test_target) {
visibility = [ ":*" ]
if (defined(invoker.target)) {
target = invoker.target
} else {
target = get_label_info(":${target_name}", "label_with_toolchain")
}
binary_path = "${test_data_dir}/bindc"
rules_filename = get_path_info(invoker.rules, "file")
test_spec_filename = get_path_info(invoker.tests, "file")
response_file_filename = get_path_info(response_file, "file")
args = [
"test",
"--lint",
rebase_path("${test_data_dir}/${rules_filename}", root_build_dir),
"--test-spec",
rebase_path("${test_data_dir}/${test_spec_filename}", root_build_dir),
"--include-file",
rebase_path("${test_data_dir}/${response_file_filename}",
root_build_dir),
]
deps = [
":${response_file_target}",
":${test_data_target}",
"//tools/bindc:bin($host_toolchain)",
]
}
} else {
not_needed(invoker,
[
"deps",
"tests",
"rules",
"target",
test_target,
])
}
}
# Declares a suite of tests for a driver's bind rules.
#
# This template is provided for convenience. It handles redirect bind_host_test to the host
# toolchain.
#
# Parameters
#
# rules (required)
# [path]: Path to the bind rules source file.
#
# tests (required)
# [path]: Path to a test specification. The test specification is a JSON file defining a set
# of devices and the expected result of the bind rules when applied to that device. The file
# must adhere to the JSON schema defined by //src/devices/bind/debugger/tests_schema.json.
#
# target (optional)
# [label]: The test target. Defaults to target_name.
#
# deps (optional)
# [list of labels]: List of bind_library targets included by the bind rules.
#
# visibility
# Forwarded from invoker.
#
template("bind_test") {
assert(defined(invoker.rules), "Need a bind rules source")
assert(defined(invoker.tests), "Need a test specification")
test_target = "${target_name}_test"
group(target_name) {
forward_variables_from(invoker, [ "visibility" ])
testonly = true
# Redirect to the host toolchain.
deps = [ ":${test_target}($host_toolchain)" ]
}
original_target_name = target_name
bind_host_test(test_target) {
if (defined(invoker.target)) {
target = invoker.target
} else {
target =
get_label_info(":${original_target_name}", "label_with_toolchain")
}
rules = invoker.rules
tests = invoker.tests
forward_variables_from(invoker,
[
"deps",
"visibility",
])
}
}
# Call the bindc tool with the correct arguments.
#
# This template calls the bindc tool. It can either generate a C header or a
# bytecode file.
# For more details refer to //tools/bindc/README.md.
#
# Parameters
#
# rules (required)
# [path]: Path to the bind rules source file.
# Note: This becomes optional when disable_autobind is true. See below.
#
# deps (optional)
# [lib of labels]: List of bind_library targets included by the bind rules.
#
# header_output (optional)
# [path]: Name of the header file generated by the tool (defaults to target name + ".h")
# Only valid if generate_bytecode is false.
#
# bytecode_output (optional)
# [path]: Name of the bytecode file generated by the tool (defaults to target name + ".bindbc")
# Only valid if generate_bytecode is true.
#
# disable_autobind (optional)
# [bool]: Configure the bind compiler to disable autobind, so that the driver must be bound on
# a user's request. If this is set to true, then the rules parameter becomes optional and when
# it's omitted the driver will bind unconditionally (but must be bound manually.) Defaults to
# false.
# TODO(fxbug.dev/43400): Eventually this option should be removed when we can define this
# configuration in the driver's component manifest.
#
# generate_bytecode (optional)
# [bool]: Output a bytecode file, instead of a C header file.
#
# testonly, visibility
# Forwarded from invoker.
#
template("bindc") {
assert(defined(invoker.rules) || (defined(invoker.disable_autobind) &&
invoker.disable_autobind == true),
"Need a bind rules source")
generate_bytecode = false
if (defined(invoker.generate_bytecode)) {
generate_bytecode = invoker.generate_bytecode
}
output_file = ""
if (generate_bytecode) {
output_file = "$target_name.bindbc"
if (defined(invoker.bytecode_output)) {
output_file = invoker.bytecode_output
}
} else {
output_file = "$target_name.h"
if (defined(invoker.header_output)) {
output_file = invoker.header_output
}
}
response_file_target = "_${target_name}_response_file"
response_file = "${target_gen_dir}/${target_name}.rsp"
generated_file(response_file_target) {
visibility = [ ":*" ]
forward_variables_from(invoker,
[
"deps",
"testonly",
])
data_keys = [ "sources" ]
outputs = [ "${response_file}" ]
}
compiled_action(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
tool = "//tools/bindc:bin"
tool_output_name = "bindc"
if (defined(invoker.rules)) {
sources = [ invoker.rules ]
}
depfile = "$target_gen_dir/$target_name.d"
args = [
"compile",
"--lint",
"--output",
rebase_path("$target_gen_dir/$output_file", root_build_dir),
"--include-file",
rebase_path(response_file, root_build_dir),
"--depfile",
rebase_path(depfile, root_build_dir),
]
if (defined(invoker.disable_autobind) && invoker.disable_autobind) {
args += [ "--disable-autobind" ]
}
if (generate_bytecode) {
args += [ "--output-bytecode" ]
}
if (defined(invoker.rules)) {
args += [ rebase_path(invoker.rules, root_build_dir) ]
}
if (!defined(invoker.disable_new_bytecode) ||
!invoker.disable_new_bytecode) {
args += [ "--use-new-bytecode" ]
}
inputs = [ response_file ]
outputs = [ "$target_gen_dir/$output_file" ]
deps = [ ":$response_file_target" ]
if (defined(invoker.deps)) {
deps += invoker.deps
}
metadata = {
if (generate_bytecode) {
# This metadata lets the packaging system know where to put the bytecode file.
distribution_entries = [
{
source = rebase_path("$target_gen_dir/$output_file", root_build_dir)
destination = "meta/bind/$output_file"
label = get_label_info(":$target_name", "label_with_toolchain")
},
]
} else {
# This ensures the generated header files are made available for static
# analysis without doing a full build.
generated_sources = rebase_path(outputs, root_build_dir)
}
}
}
}
# Declares a driver's bind rules.
#
# Generates both a C header and a bytecode file withe the necessary bind rules.
# For more details refer to //tools/bindc/README.md.
#
# Parameters
#
# rules (required)
# [path]: Path to the bind rules source file.
# Note: This becomes optional when disable_autobind is true. See below.
#
# deps (optional)
# [lib of labels]: List of bind_library targets included by the bind rules.
#
# header_output (optional)
# |string|: Name of the bind header file.
# Defaults to no header
#
# bind_output (optional)
# |string|: Name of the bind binary file
# Defaults to target_name + ".bindbc"
#
# tests (optional)
# [path]: Path to a test specification. If this parameter is set then the template will
# create an additional bind_test target with the name "${target_name}_test". This allows you
# to define tests without specifying the bind library dependencies and rules file twice.
#
# disable_autobind (optional)
# [bool]: Configure the bind compiler to disable autobind, so that the driver must be bound on
# a user's request. If this is set to true, then the rules parameter becomes optional and when
# it's omitted the driver will bind unconditionally (but must be bound manually.) Defaults to
# false.
# TODO(fxbug.dev/43400): Eventually this option should be removed when we can define this
# configuration in the driver's component manifest.
#
# testonly, visibility
# Forwarded from invoker.
#
template("driver_bind_rules") {
assert(defined(invoker.rules) || (defined(invoker.disable_autobind) &&
invoker.disable_autobind == true),
"Need a bind rules source")
bind_output = "$target_name.bindbc"
if (defined(invoker.bind_output)) {
bind_output = invoker.bind_output
}
if (defined(invoker.header_output)) {
header_output = invoker.header_output
header_file_target = "${target_name}_header"
bindc(header_file_target) {
forward_variables_from(invoker,
[
"rules",
"deps",
"testonly",
"disable_autobind",
"disable_new_bytecode",
"visibility",
])
header_output = "$header_output"
}
}
bytecode_file_target = "${target_name}_bytecode"
bindc(bytecode_file_target) {
forward_variables_from(invoker,
[
"rules",
"deps",
"testonly",
"disable_autobind",
"disable_new_bytecode",
"visibility",
])
bytecode_output = bind_output
generate_bytecode = true
}
group(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
public_deps = [ ":${bytecode_file_target}" ]
if (defined(invoker.header_output)) {
public_deps += [ ":${header_file_target}" ]
}
}
if (defined(invoker.tests)) {
bind_test("${target_name}_test") {
forward_variables_from(invoker,
[
"rules",
"deps",
"tests",
])
}
}
}
# Generates a target for a bind library's C++ bindings.
# Only for internal use by bind_library.
template("_bind_library_cpp") {
if (defined(invoker.name)) {
library_name = invoker.name
} else {
library_name = target_name
}
library_name_slashes = string_replace(library_name, ".", "/")
cpp_header_file = "${target_gen_dir}/${target_name}/bind_cpp/bind/${library_name_slashes}/cpp/bind.h"
cpp_header_file_gen_target = "${target_name}_cpp_header_gen"
cpp_header_file_target = "${target_name}_cpp"
include_config = "${target_name}_include_config"
config(include_config) {
include_dirs = [ "${target_gen_dir}/${target_name}/bind_cpp" ]
}
compiled_action(cpp_header_file_gen_target) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
tool = "//tools/bindc:bin"
tool_output_name = "bindc"
sources = [ invoker.source ]
if (defined(invoker.source_dep)) {
deps = [ invoker.source_dep ]
}
args = [
"generate-cpp",
"--lint",
"--output",
rebase_path("${cpp_header_file}", root_build_dir),
rebase_path("${invoker.source}", root_build_dir),
]
outputs = [ cpp_header_file ]
metadata = {
generated_sources = rebase_path(outputs, root_build_dir)
}
}
source_set(cpp_header_file_target) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
deps = [ ":${cpp_header_file_gen_target}" ]
sources = [ cpp_header_file ]
public_configs = [ ":${include_config}" ]
if (defined(invoker.public_deps)) {
public_deps = []
foreach(pub_dep, invoker.public_deps) {
dep_full_label = get_label_info(pub_dep, "label_no_toolchain")
public_deps += [ "${dep_full_label}_cpp" ]
}
}
}
}
# Generates a target for a bind library's Rust bindings.
# Only for internal use by bind_library.
template("_bind_library_rust") {
if (defined(invoker.name)) {
library_name = invoker.name
} else {
library_name = target_name
}
library_name_underscore = string_replace(library_name, ".", "_")
rust_file =
"${target_gen_dir}/${target_name}/rust/${library_name_underscore}_lib.rs"
crate_name = "bind_${library_name_underscore}"
rust_file_gen_target = "${target_name}_rust_file_gen"
rust_library_target = "${target_name}_rust"
crate_output_directory = "${target_out_dir}/${target_name}"
compiled_action(rust_file_gen_target) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
tool = "//tools/bindc:bin"
tool_output_name = "bindc"
sources = [ invoker.source ]
if (defined(invoker.source_dep)) {
deps = [ invoker.source_dep ]
}
args = [
"generate-rust",
"--lint",
"--output",
rebase_path("${rust_file}", root_build_dir),
rebase_path("${invoker.source}", root_build_dir),
]
outputs = [ rust_file ]
metadata = {
generated_sources = rebase_path(outputs, root_build_dir)
}
}
rustc_library(rust_library_target) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
edition = "2018"
name = crate_name
output_dir = crate_output_directory
non_rust_deps = [ ":${rust_file_gen_target}" ]
sources = [ rust_file ]
source_root = rust_file
configs -= [ "//build/config/rust:allow_unused_results" ]
if (defined(invoker.public_deps)) {
deps = []
foreach(pub_dep, invoker.public_deps) {
dep_full_label = get_label_info(pub_dep, "label_no_toolchain")
deps += [ "${dep_full_label}_rust" ]
}
}
}
}
# Declares a bind library.
#
# Declare a bind library that may be included by other libraries or bind rules. For more details,
# refer to //tools/bindc/README.md.
#
# Parameters
#
# source (required)
# [path]: Path to the library source file.
#
# public_deps (optional)
# [list of labels]: List of other bind_library targets included by the library.
#
# source_dep (optional)
# [label]: The label of a target that generates the source input.
#
# name (optional)
# [string] Name of the library. Defaults to the target's name.
#
# testonly, visibility
# Forwarded from invoker.
#
template("bind_library") {
assert(defined(invoker.source), "Need a source file")
_bind_library_cpp(target_name) {
forward_variables_from(invoker, "*", [ "sdk_category" ])
}
# Rust tools (eg. rust_rlib) are not defined in the Zircon toolchain.
if (zircon_toolchain == false) {
_bind_library_rust(target_name) {
forward_variables_from(invoker, "*", [ "sdk_category" ])
}
}
test_data_dir = "${target_out_dir}/${target_name}/test_data/bind-tests/"
test_data_target = "${target_name}_test_data"
copy(test_data_target) {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":*" ]
sources = [ invoker.source ]
outputs = [ "${test_data_dir}/{{source_file_part}}" ]
if (defined(invoker.source_dep)) {
deps = [ invoker.source_dep ]
}
}
group(target_name) {
metadata = {
sources = [ rebase_path(invoker.source, root_build_dir) ]
# Adds metadata for test_spec().
test_runtime_deps = get_target_outputs(":${test_data_target}")
# Adds metadata for bind_test().
test_sources = rebase_path(get_target_outputs(":${test_data_target}"),
root_build_dir)
}
deps = [ ":${test_data_target}" ]
if (defined(invoker.source_dep)) {
deps += [ invoker.source_dep ]
}
forward_variables_from(invoker,
[
"public_deps",
"testonly",
"visibility",
])
}
if (defined(invoker.sdk_category)) {
_bind_library_sdk("$target_name") {
forward_variables_from(invoker, "*")
}
}
}