blob: 60a6a5a0fb51d42bfed4436d8045d7514c2187fb [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/config/fuchsia/platform_version.gni")
import("//build/fidl/fidl_ir.gni")
import("//build/fidl/linting_exceptions.gni")
import("//build/fidl/toolchain.gni")
import("//build/json/validate_json.gni")
import("//build/sdk/sdk_atom.gni")
import("//build/testing/fidl_api_compatibility_test.gni")
import("//build/testing/golden_file.gni")
declare_args() {
# Whether libraries under //vendor should be linted.
vendor_linting = false
# Whether to run API compatibility tests.
api_compatibility_testing = true
}
# Generates some representation of a FIDL library that's consumable by Language
# bindings generators.
#
# The parameters for this template are defined in //build/fidl/fidl.gni. The
# relevant parameters in this template are:
# - api;
# - name;
# - sdk_category;
# - sources.
# - lenient_api_summary
#
# should_lint (optional, boolean)
# If set to false, the linting step is skipped.
template("fidl_library") {
assert(
current_toolchain == fidl_toolchain,
"This template can only be used in the FIDL toolchain $fidl_toolchain.")
assert(defined(invoker.sources), "A FIDL library requires some sources.")
main_target_name = target_name
library_name = target_name
if (defined(invoker.name)) {
library_name = invoker.name
}
experimental_flags = []
if (defined(invoker.experimental_flags)) {
experimental_flags = invoker.experimental_flags
}
is_sdk_publishable =
defined(invoker.sdk_category) && invoker.sdk_category != "internal" &&
invoker.sdk_category != "experimental" &&
invoker.sdk_category != "excluded"
# Additional dependencies to add when this target is published in an SDK.
sdk_atom_deps = []
not_needed([ "sdk_atom_deps" ])
json_representation = "$target_gen_dir/$target_name.fidl.json"
lint_stamp_file = "$target_gen_dir/$target_name.linted"
compilation_target_name = "${target_name}_compile"
verification_target_name = "${target_name}_verify"
lint_target_name = "${target_name}_lint"
# Only artifacts that have various associated FIDL generated targets.
fidl_deps = []
# Artifacts other than FIDL, that are also dependencies.
non_fidl_deps = []
if (defined(invoker.non_fidl_deps)) {
non_fidl_deps += invoker.non_fidl_deps
}
if (defined(invoker.deps)) {
fidl_deps += invoker.deps - non_fidl_deps
}
if (defined(invoker.public_deps)) {
fidl_deps += invoker.public_deps
}
summary_file_json = "$target_gen_dir/$target_name.api_summary.json"
fidl_ir(compilation_target_name) {
forward_variables_from(invoker,
[
"deps",
"fidl_deps",
"json_representation",
"public_deps",
"sources",
"testonly",
])
fidl_target_name = main_target_name
fidl_library_name = library_name
out_json_summary = summary_file_json
deps += [ ":$lint_target_name" ] + non_fidl_deps
}
action(lint_target_name) {
visibility = [ ":*" ]
script = "//build/scripts/run_and_stamp.sh"
forward_variables_from(invoker,
[
"sources",
"testonly",
])
# run_and_stamp.sh runs this tool, and touches lint_stamp_file if successful
_tool_with_no_output = "//tools/fidl/fidlc:fidl-lint"
# Construct the host toolchain version of the tool.
host_tool = "${_tool_with_no_output}($host_toolchain)"
# Get the path to the executable.
if (!defined(tool_output_name)) {
tool_output_name = get_label_info(host_tool, "name")
}
tool_out_dir = get_label_info(host_tool, "root_out_dir")
host_executable = "$tool_out_dir/$tool_output_name"
deps = [ host_tool ] + non_fidl_deps
# Add the executable itself as an input.
inputs = [ host_executable ]
outputs = [ lint_stamp_file ]
should_lint = !defined(invoker.should_lint) || invoker.should_lint
# unlinted_libraries defined in linting_exceptions.gni
foreach(library, unlinted_libraries) {
if (library == library_name) {
should_lint = false
}
}
full_label = get_label_info(":bogus", "label_no_toolchain")
in_vendor = string_replace(full_label, "//vendor", "") != full_label
if (in_vendor && !vendor_linting) {
# Disable lints for libraries under //vendor.
# TODO(fxbug.dev/36800): remove this exception.
should_lint = false
}
fidl_to_lint = []
if (should_lint) {
foreach(source, sources) {
if (string_replace(source, ".test.", "") == source) {
# Don't automatically lint test.fidl and test.<various>.fidl files
fidl_to_lint += [ rebase_path(source, root_build_dir) ]
}
}
}
args = [ rebase_path(lint_stamp_file, root_build_dir) ]
if (fidl_to_lint == []) {
args += [ ":" ] # NOOP - Nothing to lint, but touch the stamp file
} else {
args += [ rebase_path(host_executable, root_build_dir) ]
excluded_checks = []
if (defined(invoker.excluded_checks)) {
excluded_checks = invoker.excluded_checks
}
if (excluded_checks != []) {
# Add path to fidl-lint executable, and --must-find-excluded-checks option, so fidl-lint will
# return an error if any excluded check is no longer required. Excluded checks are only
# allowed if the target |fidl_to_lint| files still violate those checks. After updating
# the FIDL files to resolve a lint error, remove the check-id from the list of excluded
# checks in the fidl() target to prevent the same lint errors from creeping back in, in
# the future.
args += [ "--must-find-excluded-checks" ]
foreach(excluded_check, excluded_checks) {
args += [
"-e",
excluded_check,
]
}
}
args += fidl_to_lint
}
}
# Enforce compatibility checks for all published SDK FIDLs.
if (is_sdk_publishable) {
# A kill switch for API summarization and compatibility testing.
# Set to true to permit compilations that have summary errors.
lenient_api_summary = false
if (defined(invoker.lenient_api_summary)) {
lenient_api_summary = invoker.lenient_api_summary
}
# Ensure changes to API level HEAD are acknowledged.
test_target = "${target_name}_compatibility_test"
fidl_api_compatibility_test(test_target) {
forward_variables_from(invoker, [ "testonly" ])
warn_on_changes = lenient_api_summary
target_api_level = "HEAD"
golden = "$library_name.api_summary.json"
current = summary_file_json
deps = [ ":$compilation_target_name" ]
policy = "ack_changes"
}
sdk_atom_deps += [ ":$test_target" ]
# Check for compatibility with supported API levels.
foreach(level, platform_version.supported_fuchsia_api_levels) {
fidl_ir_target_name = "${main_target_name}_compile_$level"
fidl_ir(fidl_ir_target_name) {
forward_variables_from(invoker,
[
"deps",
"fidl_deps",
"public_deps",
"sources",
"experimental_flags",
"testonly",
])
fidl_target_name = main_target_name
fidl_library_name = library_name
target_api_level = "$level"
# Avoid clobbering existing FIDL sources.
gen_dir = "$target_gen_dir/$level"
out_json_summary = "$gen_dir/$library_name.api_summary.json"
json_representation = "$gen_dir/$target_name.fidl.json"
deps = non_fidl_deps
}
sdk_atom_deps += [ ":$fidl_ir_target_name" ]
if (api_compatibility_testing) {
test_target = "${target_name}_${level}_compatibility_test"
fidl_api_compatibility_test(test_target) {
forward_variables_from(invoker, [ "testonly" ])
warn_on_changes = lenient_api_summary
target_api_level = "$level"
goldens_dir = "//sdk/history"
if (defined(invoker.goldens_dir)) {
goldens_dir = invoker.goldens_dir
}
golden = "$goldens_dir/$level/$library_name.api_summary.json"
current = "$target_gen_dir/$level/$library_name.api_summary.json"
if (level == platform_version.current_fuchsia_api_level) {
policy = "no_breaking_changes"
} else {
policy = "no_changes"
}
deps = [ ":$fidl_ir_target_name" ]
}
sdk_atom_deps += [ ":$test_target" ]
}
}
}
validate_json(verification_target_name) {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":*" ]
data = json_representation
schema = "//tools/fidl/fidlc/schema.json"
deps = [ ":$compilation_target_name" ]
}
group(main_target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
# Metadata to allow us to query all FIDL IR files.
metadata = {
fidl_json = [ rebase_path(json_representation, root_build_dir) ]
generated_sources = fidl_json
}
public_deps = [
":$compilation_target_name",
":$lint_target_name",
]
deps = [
":$compilation_target_name",
":$verification_target_name",
]
}
if (defined(invoker.sdk_category) && invoker.sdk_category != "excluded") {
sdk_category = invoker.sdk_category
if (sdk_category == "partner" || sdk_category == "public") {
api_reference = "$library_name.api"
if (defined(invoker.api)) {
api_reference = invoker.api
}
}
# Process sources.
file_base = "fidl/$library_name"
all_files = []
sdk_sources = []
foreach(source, invoker.sources) {
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 = []
foreach(dep, fidl_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/fidl/gen_sdk_meta.py"
inputs = sdk_metas
outputs = [ meta_file ]
args = [
"--out",
rebase_path(meta_file, root_build_dir),
"--name",
library_name,
"--root",
file_base,
]
args += [ "--specs" ] + rebase_path(sdk_metas, root_build_dir)
args += [ "--sources" ] + sdk_sources
deps = sdk_deps
}
api_normalize_target_name = "${target_name}_normalize"
normalized_file = "$target_gen_dir/${target_name}.normalized"
to_normalize = []
foreach(file, all_files) {
to_normalize += [ file.source ]
}
action(api_normalize_target_name) {
script = "//build/fidl/normalize.py"
inputs = to_normalize
outputs = [ normalized_file ]
args = [
"--out",
rebase_path(normalized_file, root_build_dir),
"--files",
] + rebase_path(to_normalize, root_build_dir)
}
# Files included in the CTS SDK.
sdk_atom("${target_name}_cts_sdk") {
testonly = true
id = "sdk://cts/$library_name"
category = "cts"
files = [
{
source = summary_file_json
dest = string_replace(summary_file_json, "$root_build_dir/", "")
},
]
meta = {
dest = "$file_base/cts/meta.json"
schema = "cts_resource"
value = {
type = "resource"
name = target_name
resources = []
foreach(file, files) {
resources += [ file.dest ]
}
}
}
non_sdk_deps = [ ":$compilation_target_name" ]
}
sdk_atom("${target_name}_sdk") {
id = "sdk://fidl/$library_name"
category = sdk_category
if (defined(api_reference)) {
api = api_reference
api_contents = [
{
source = summary_file_json
dest = "fidl/$library_name"
},
]
}
meta = {
source = meta_file
dest = "$file_base/meta.json"
schema = "fidl_library"
}
files = all_files
# Metadata to allow us to query all FIDL IR and source files.
metadata = {
fidl_json = [ rebase_path(json_representation, root_build_dir) ]
generated_sources = fidl_json
# Metadata for FIDL SDK files.
if (defined(invoker.sdk_category)) {
sdk_fidl_json_data = [
{
name = library_name
ir = rebase_path(json_representation, root_build_dir)
category = invoker.sdk_category
},
]
}
}
non_sdk_deps = [
":$api_normalize_target_name",
":$compilation_target_name",
":$meta_target_name",
] + sdk_atom_deps
deps = []
foreach(dep, fidl_deps) {
label = get_label_info(dep, "label_no_toolchain")
deps += [ "${label}_sdk" ]
}
}
}
}