blob: a77dcdfedbef6180bdc441545d64b886e8a0147e [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.
import("//build/fidl/fidl.gni")
import("//build/go/go_binary.gni")
import("//build/go/go_library.gni")
import("//build/rust/rustc_binary.gni")
import("//build/toolchain/ifs_shared_library.gni")
import("//tools/fidl/lib/fidlgentest/fidlgentest_go_test.gni")
import("zither_golden_files.gni")
import("zither_library.gni")
group("tests") {
testonly = true
deps = [
# basic unit tests
":unittests($host_toolchain)",
# golden testing
":aliases",
":bits",
":constants",
":enums",
":experimental_zx_types",
":multifile",
":output_namespace_override",
":resources",
":structs",
":zx.test",
]
}
if (is_host) {
go_binary("zither") {
library = ":main"
}
go_library("main") {
visibility = [ ":*" ]
source_dir = "cmd"
sources = [ "main.go" ]
deps = [
":common",
"backends/asm",
"backends/c",
"backends/go_runtime",
"backends/golang",
"backends/kernel",
"backends/legacy_syscall_cdecl",
"backends/rust",
"backends/rust_syscall",
"backends/syscall_docs",
"backends/zircon_ifs",
"//tools/fidl/lib/fidlgen",
"//tools/lib/color",
"//tools/lib/flagmisc",
"//tools/lib/logger",
]
}
go_library("common") {
visibility = [ "./*" ]
deps = [
"//tools/fidl/lib/fidlgen",
"//tools/fidl/lib/fidlgen_cpp",
]
sources = [
"c_family.go",
"names.go",
"readme.go",
"zither_ir.go",
"zither_ir_test.go",
]
}
fidlgentest_go_test("unittests") {
library = ":common"
output_name = "zither-unittests"
deps = [
"//third_party/golibs:github.com/google/go-cmp",
"//tools/fidl/lib/fidlgen",
"//tools/fidl/lib/fidlgentest",
]
}
}
#
# Build-time golden tests
#
# zither_golden_test() defines a suite of build-time golden tests for zither
# backends: a FIDL library and its expected bindings are supplied, and the diff
# checks are made as build actions. Further, a check is made that the bindings
# give valid, compilable code.
#
# `target_name` is assumed to be the associated test case name, as is formally
# defined by the subdirectory names in testdata/ (see testdata/README.md).
# This template further assumes the following properties of the directory's
# organizational structure:
# * FIDL sources are found in testdata/${case_name}/, at the root of the
# 'case data directory';
# * Goldens, for a given backend, are found in
# testdata/${case_name}/goldens/${backend}, the 'backend's case golden
# directory'
#
# Parameters:
#
# * sources
# - Required: FIDL files comprising a single library, from which bindings
# will be generated, relative to the case FIDL directory. The name of this
# library is assumed to be `"zither." + string_replace(case_name, "_", ".")`.
# We derive the FIDL library name with character replacement since
# underscores are not a permitted as library name characters and a period
# is the defined separator. One exception to this is "zx.test": if that is
# the target name, then the library is expected to be called "zx", which
# permits the testing of things that FIDL currently requires to be defined
# in a library of that name.
# - Type: list(relative path)
#
# * experimental_flags
# - Optional: See fidl().
# - Type: list(string)
#
# * backend_parameters
# - Optional:
# - Type: scope
#
# * cases
# - Required: A list of test cases.
# - Type: list(scope)
#
# Each scope contains:
# * backend:
# - Required: The backend with which to generate bindings. Must be one
# of `zither_supported_backends` (see zither_library.gni).
# - Type: string
# * files
# - Required: The list of names for the files to be generated by the
# backend. This list should be in one-to-one correspondence with the
# the goldens found in the backend's case golden directory - or "*" in
# the case of backends with dynamic outputs.
# - Type: list(string) or "*"
#
template("zither_golden_test") {
assert(defined(invoker.sources), "no sources given")
assert(defined(invoker.cases) && invoker.cases != [], "no test cases defined")
main_target = target_name
if (target_name == "zx.test") {
library_target = "zx"
} else {
library_target = "zither." + string_replace(target_name, "_", ".")
}
case_name = target_name
case_data_dir = "testdata/${case_name}"
extra_backend_parameters = {
if (defined(invoker.backend_parameters)) {
forward_variables_from(invoker.backend_parameters, "*")
}
}
fidl(library_target) {
forward_variables_from(invoker, [ "experimental_flags" ])
sources = []
foreach(source, invoker.sources) {
sources += [ "${case_data_dir}/$source" ]
}
enable_zither = true
zither = extra_backend_parameters
}
all_cases = []
foreach(case, invoker.cases) {
if (case.backend == "go" && current_cpu == "riscv64") {
# Go builds are not supported for RISC-V.
case = false
}
if (case != false && !support_rust &&
(case.backend == "rust" || case.backend == "rust_syscall")) {
# Skip Rust cases.
case = false
}
if (case != false) {
all_cases += [ case ]
}
}
case_deps = []
foreach(case, all_cases) {
assert(defined(case.backend), "`${case}` must provide `backend`")
assert(defined(case.files) && case.files != [],
"`${case}` must provide `files`")
backend_label = ":${library_target}_zither.${case.backend}"
goldens_target =
"_zither_golden_test.${target_name}-${case.backend}.zither_golden_files"
compilation_check_target =
"_zither_golden_test.${target_name}-${case.backend}.compilation_check"
zither_golden_files(goldens_target) {
fidl = ":$library_target"
backend = case.backend
_golden_dir = "${case_data_dir}/goldens/${case.backend}"
if (case.files == "*") {
source_dir = _golden_dir
} else {
sources = []
foreach(file, case.files) {
sources += [ "${_golden_dir}/${file}" ]
}
}
}
case_deps += [ ":$goldens_target" ]
# Clear from previous iteration.
backend_info = {
}
backend_params = {
}
output_namespace_info = {
}
backend_info = supported_zither_backend_info[case.backend]
# Reconstruct the output subdirectory per `zither_library()` documentation,
# taking any explicit override into account.
backend_params = {
if (defined(extra_backend_parameters[case.backend])) {
forward_variables_from(extra_backend_parameters[case.backend], "*")
}
}
if (defined(backend_params.output_namespace)) {
output_namespace = backend_params.output_namespace
} else {
output_namespace_info = {
prefix_parts = []
suffix_parts = []
part_separator = "/"
forward_variables_from(backend_info.output_namespace, "*")
parts = prefix_parts
if (defined(library_name_separator)) {
parts +=
[ string_replace(library_target, ".", library_name_separator) ]
}
parts += suffix_parts
path = string_join(part_separator, parts)
}
if (output_namespace_info.path != "") {
output_namespace = output_namespace_info.path
}
}
# A per (backend, case) output directory for use below, if needed.
# Not related to the zither output directory.
case_output_dir = "$target_gen_dir/${target_name}-${case.backend}"
# Now verify that the bindings give valid code.
if (case.backend == "c" || case.backend == "asm") {
create_source_target = "${compilation_check_target}.create_source"
if (case.backend == "c") {
suffix = "c"
} else if (case.backend == "asm") {
suffix = "S"
} else {
suffix = "cc"
}
source = "$case_output_dir/compile-check.$suffix"
action(create_source_target) {
visibility = [ ":*" ]
testonly = true
script = "scripts/create-file-with-includes.py"
args = [
"--comment",
"Generated by //" + rebase_path(script, "//"),
"--output",
rebase_path(source, root_build_dir),
]
foreach(file, case.files) {
if (get_path_info(file, "extension") != "md") {
args += [ "${output_namespace}/${file}" ]
}
}
outputs = [ source ]
}
# loadable_module() over source_set() to pass `-z defs` muster.
loadable_module(compilation_check_target) {
visibility = [ ":*" ]
testonly = true
sources = [ source ]
deps = [
":$create_source_target",
backend_label,
]
}
} else if (case.backend == "go") {
# We create a simple main file that imports the generated package, which
# will feed into our compilation check. There is unfortunately no real
# equivalent of `source_set()` in our go build.
create_main_target = "${compilation_check_target}.create_main"
main_file = "$case_output_dir/main.go"
generated_file(create_main_target) {
visibility = [ ":*" ]
testonly = true
contents = [
"package main",
"",
"import (",
" _ \"${output_namespace}\"", # `_` to avoid usage complaints
" _ \"syscall/zx\"", # Required by fuchsia support quirks.
")",
"",
"func main() {}",
"",
]
outputs = [ main_file ]
}
# The generated main file now needs to be declared in a package/library.
main_library_target = "${compilation_check_target}.main_library"
main_pkg_name = rebase_path(case_output_dir, root_build_dir)
go_library(main_library_target) {
visibility = [ ":*" ]
testonly = true
name = main_pkg_name
source_dir = case_output_dir
sources = [ rebase_path(main_file, source_dir) ]
deps = [ backend_label ]
non_go_deps = [ ":$create_main_target" ]
}
go_binary(compilation_check_target) {
library = ":$main_library_target"
visibility = [ ":*" ]
testonly = true
}
} else if (case.backend == "zircon_ifs") {
if (current_toolchain == default_toolchain) {
backend_outputs = get_target_outputs("${backend_label}.gen")
ifs = ""
foreach(output, backend_outputs) {
if (get_path_info(output, "extension") == "ifs") {
ifs = output
}
}
ifs_shared_library(compilation_check_target) {
abi = ifs
deps = [ backend_label ]
}
} else {
group(compilation_check_target) {
testonly = true
deps = [ ":$compilation_check_target($default_toolchain)" ]
}
}
not_needed([
"backend_label",
"case_output_dir",
"output_namespace",
])
} else if (case.backend == "rust" || case.backend == "kernel" ||
case.backend == "legacy_syscall_cdecl" ||
case.backend == "rust_syscall" || case.backend == "go_runtime" ||
case.backend == "syscall_docs") {
# The generated rust library targets should already enforce compilation.
#
# The kernel and syscall-related backends generate sources that rely on a
# number of other kernel and vDSO-specific definitions. Given that and
# the fact the singular use-case for these sources will be exercised in
# any default build invocation, it is neither worthwhile nor all that
# practical to do a compilation check here.
group(compilation_check_target) {
visibility = [ ":*" ]
testonly = true
deps = [ backend_label ]
}
not_needed([
"case_output_dir",
"output_namespace",
])
}
case_deps += [ ":$compilation_check_target" ]
}
group(main_target) {
testonly = true
visibility = [ ":*" ]
deps = case_deps
}
}
# FIDL: testdata/constants/
# Goldens: testdata/constants/goldens/${backend}/
zither_golden_test("constants") {
sources = [ "constants.test.fidl" ]
cases = [
{
backend = "c"
files = [
"constants.h",
"README.md",
]
},
{
backend = "asm"
files = [ "constants.h" ]
},
{
backend = "go"
files = [
"constants.go",
"pkg_name.txt",
]
},
{
backend = "rust"
files = [
"constants.rs",
"lib.rs",
]
},
]
}
# FIDL: testdata/enums/
# Goldens: testdata/enums/goldens/${backend}/
zither_golden_test("enums") {
sources = [ "enums.test.fidl" ]
cases = [
{
backend = "c"
files = [
"enums.h",
"README.md",
]
},
{
backend = "asm"
files = [ "enums.h" ]
},
{
backend = "go"
files = [
"enums.go",
"pkg_name.txt",
]
},
{
backend = "rust"
files = [
"enums.rs",
"lib.rs",
]
},
]
}
# FIDL: testdata/bits/
# Goldens: testdata/bits/goldens/${backend}/
zither_golden_test("bits") {
sources = [ "bits.test.fidl" ]
cases = [
{
backend = "c"
files = [
"bits.h",
"README.md",
]
},
{
backend = "asm"
files = [ "bits.h" ]
},
{
backend = "go"
files = [
"bits.go",
"pkg_name.txt",
]
},
{
backend = "rust"
files = [
"bits.rs",
"lib.rs",
]
},
]
}
# FIDL: testdata/structs/
# Goldens: testdata/structs/goldens/${backend}/
zither_golden_test("structs") {
sources = [ "structs.test.fidl" ]
cases = [
{
backend = "c"
files = [
"structs.h",
"README.md",
]
},
{
backend = "asm"
files = [ "structs.h" ]
},
{
backend = "go"
files = [
"pkg_name.txt",
"structs.go",
]
},
{
backend = "rust"
files = [
"structs.rs",
"lib.rs",
]
},
]
}
# FIDL: testdata/multifile/
# Goldens: testdata/multifile/goldens/${backend}/
zither_golden_test("multifile") {
sources = [
"a.test.fidl",
"b.test.fidl",
"c.test.fidl",
]
cases = [
{
backend = "c"
files = [
"a.h",
"b.h",
"c.h",
"README.md",
]
},
{
backend = "asm"
files = [
"a.h",
"b.h",
"c.h",
]
},
{
backend = "go"
files = [
"a.go",
"b.go",
"c.go",
"pkg_name.txt",
]
},
{
backend = "rust"
files = [
"a.rs",
"b.rs",
"c.rs",
"lib.rs",
]
},
]
}
# FIDL: testdata/output_namespace_override/
# Goldens: testdata/constants/goldens/${backend}/
zither_golden_test("output_namespace_override") {
sources = [ "output_namespace_override.test.fidl" ]
backend_parameters = {
c = {
output_namespace = "my/custom/c/header/guard"
}
asm = {
output_namespace = "my/custom/asm/header/guard"
}
}
cases = [
{
backend = "c"
files = [
"output_namespace_override.h",
"README.md",
]
},
{
backend = "asm"
files = [ "output_namespace_override.h" ]
},
]
}
# FIDL: testdata/aliases/
# Goldens: testdata/aliases/goldens/${backend}/
zither_golden_test("aliases") {
sources = [ "aliases.test.fidl" ]
cases = [
{
backend = "c"
files = [
"aliases.h",
"README.md",
]
},
{
backend = "go"
files = [
"aliases.go",
"pkg_name.txt",
]
},
{
backend = "rust"
files = [
"aliases.rs",
"lib.rs",
]
},
]
}
# FIDL: testdata/zx/
# Goldens: testdata/zx/goldens/${backend}/
zither_golden_test("zx.test") {
experimental_flags = [ "zx_c_types" ]
sources = [ "zx.test.fidl" ]
cases = [
{
backend = "zircon_ifs"
files = [ "zircon.ifs" ]
},
{
backend = "kernel"
files = [
"category.inc",
"kernel.inc",
"kernel-wrappers.inc",
"syscalls.inc",
"zx-syscall-numbers.h",
]
},
{
backend = "legacy_syscall_cdecl"
files = [
"cdecls.inc",
"testonly-cdecls.inc",
"cdecls-next.inc",
]
},
{
backend = "rust_syscall"
files = [ "definitions.rs" ]
},
{
backend = "go_runtime"
files = [
"syscalls_fuchsia.go",
"syscalls_fuchsia_amd64.s",
"syscalls_fuchsia_arm64.s",
"vdso_keys_fuchsia.go",
"vdsocalls_fuchsia_amd64.s",
"vdsocalls_fuchsia_arm64.s",
]
},
{
backend = "syscall_docs"
files = "*"
},
]
}
# TODO(https://fxbug.dev/42061412): These tests reference some experimental built-in
# types introduced in the context of this bug.
zither_golden_test("experimental_zx_types") {
experimental_flags = [ "zx_c_types" ]
sources = [ "experimental_zx_types.test.fidl" ]
cases = [
{
backend = "asm"
files = [ "experimental_zx_types.h" ]
},
{
backend = "c"
files = [
"experimental_zx_types.h",
"README.md",
]
},
{
backend = "go"
files = [ "experimental_zx_types.go" ]
},
{
backend = "rust"
files = [
"experimental_zx_types.rs",
"lib.rs",
]
},
]
}
# FIDL: testdata/resources/
# Goldens: testdata/resources/goldens/${backend}/
zither_golden_test("resources") {
sources = [ "resources.test.fidl" ]
cases = [
{
backend = "c"
files = [
"resources.h",
"README.md",
]
},
{
backend = "go"
files = [ "resources.go" ]
},
{
backend = "rust"
files = [
"resources.rs",
"lib.rs",
]
},
]
}