| # 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/toolchain.gni") |
| import("//build/json/validate_json.gni") |
| import("//build/sdk/plasa/plasa_fragment_fidl.gni") |
| import("//build/sdk/sdk_atom.gni") |
| import("//build/testing/fidl_api_compatibility_test.gni") |
| import("//build/testing/golden_files.gni") |
| |
| # Declares a bindings agnostic FIDL library. See //build/fidl/fidl.gni for the |
| # full FIDL template which produces bindings. This template does the following: |
| # |
| # - Generates JSON IR for the library. |
| # - Validates the JSON IR against //tools/fidl/fidlc/schema.json. |
| # - Lints source files with fidl-lint. |
| # - Performs SDK checks and produces an sdk_atom target (if applicable). |
| # - Generates API summaries (if applicable). |
| # - Runs API compatibility tests (if applicable). |
| # |
| # Parameters |
| # |
| # * library_name |
| # - Required: The name of the FIDL library. |
| # - 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 |
| # |
| # * sources |
| # - Required: See //build/fidl/fidl.gni for a description. |
| # - Type: list(path) |
| # |
| # * api, excluded_checks, experimental_checks, experimental_flags, goldens_dir, |
| # non_fidl_deps, sdk_category |
| # - Optional: See //build/fidl/fidl.gni for a description. |
| # |
| # * testonly, visibility, public_deps |
| # - Optional: Usual GN meanings. |
| # |
| template("fidl_library") { |
| assert( |
| current_toolchain == fidl_toolchain, |
| "This template can only be used in the FIDL toolchain $fidl_toolchain.") |
| |
| assert(defined(invoker.sources), "sources is required") |
| assert(defined(invoker.library_name), "library_name is required") |
| assert(defined(invoker.fidl_ir_json), "fidl_ir_json is required") |
| |
| main_target_name = target_name |
| |
| is_sdk_included = |
| defined(invoker.sdk_category) && invoker.sdk_category != "excluded" |
| is_sdk_included_publishable = |
| is_sdk_included && invoker.sdk_category != "internal" && |
| invoker.sdk_category != "experimental" && |
| invoker.sdk_category != "partner_internal" |
| is_sdk_included_publishable_nontest = |
| is_sdk_included_publishable && invoker.sdk_category != "cts" |
| requires_compatibility_tests = is_sdk_included_publishable || |
| (defined(invoker.sdk_category) && |
| invoker.sdk_category == "partner_internal") |
| |
| # Additional dependencies to add when this target is published in an SDK. |
| sdk_atom_deps = [] |
| |
| goldens_dir = "//sdk/history" |
| if (defined(invoker.goldens_dir)) { |
| goldens_dir = invoker.goldens_dir |
| } |
| |
| if (!is_sdk_included) { |
| not_needed([ "sdk_atom_deps" ]) |
| } |
| if (!requires_compatibility_tests) { |
| not_needed([ "goldens_dir" ]) |
| } |
| if (!is_sdk_included_publishable_nontest) { |
| not_needed(invoker, [ "api" ]) |
| } |
| |
| 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" |
| |
| forward_variables_from(invoker, |
| [ |
| "library_name", |
| "fidl_ir_json", |
| ]) |
| fidl_ir(compilation_target_name) { |
| forward_variables_from(invoker, |
| [ |
| "experimental_flags", |
| "non_fidl_deps", |
| "public_deps", |
| "sources", |
| "testonly", |
| ]) |
| visibility = [ ":*" ] |
| fidl_target_name = main_target_name |
| fidl_library_name = library_name |
| gen_dir = target_gen_dir |
| json_representation = fidl_ir_json |
| target_api_level = platform_version.fidl_fuchsia_api_level |
| } |
| |
| summary_file_json = "$target_gen_dir/$target_name.api_summary.json" |
| plasa_fragment_file = summary_file_json |
| plasa_fragment_target_name = "${target_name}_plasa" |
| plasa_fragment_fidl(plasa_fragment_target_name) { |
| forward_variables_from(invoker, [ "testonly" ]) |
| visibility = [ |
| ":*", |
| "//sdk/fidl:plasa", |
| ] |
| fidl_ir = fidl_ir_json |
| output_fragment_file = plasa_fragment_file |
| dest = "fidl" |
| deps = [ ":$compilation_target_name" ] |
| } |
| |
| sdk_atom_deps += [ ":$plasa_fragment_target_name" ] |
| |
| action(lint_target_name) { |
| forward_variables_from(invoker, |
| [ |
| "sources", |
| "testonly", |
| ]) |
| visibility = [ ":*" ] |
| |
| script = "//build/scripts/run_and_stamp.sh" |
| |
| # 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 ] |
| if (defined(invoker.non_fidl_deps)) { |
| deps += invoker.non_fidl_deps |
| } |
| |
| # Add the executable itself as an input. |
| inputs = [ host_executable ] |
| |
| outputs = [ lint_stamp_file ] |
| |
| args = [ rebase_path(lint_stamp_file, root_build_dir) ] |
| |
| # Don't lint FIDL libraries used to test FIDL itself. |
| if (filter_include([ get_label_info(target_name, "dir") ], |
| [ |
| "//sdk/testing/fidl/*", |
| "//src/devices/tools/fidlgen_banjo/tests/fidl/*", |
| "//src/lib/fidl/*", |
| "//src/tests/benchmarks/fidl/benchmark_suite/*", |
| "//src/tests/fidl/*", |
| "//tools/fidl/fidlc/testdata/*", |
| ]) != []) { |
| 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 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, |
| ] |
| } |
| } |
| |
| experimental_checks = [] |
| if (defined(invoker.experimental_checks)) { |
| experimental_checks = invoker.experimental_checks |
| } |
| |
| foreach(experimental_check, experimental_checks) { |
| args += [ |
| "-x", |
| experimental_check, |
| ] |
| } |
| |
| foreach(source, sources) { |
| args += [ rebase_path(source, root_build_dir) ] |
| } |
| } |
| } |
| |
| # Enforce compatibility checks for all published SDK FIDLs in the Fuchsia |
| # namespace. |
| if (requires_compatibility_tests) { |
| # The current level might be a supported level. Don't double count it. |
| current_level = platform_version.in_development_api_level |
| policy_for_current_level = "no_breaking_changes" |
| |
| # policy_for_current_level is unused when regenerating golden files. |
| not_needed([ "policy_for_current_level" ]) |
| |
| api_levels = [ "$current_level" ] |
| |
| # Set api level to the next level if generate golden files tag is set to true. |
| if (defined(include_next_api_level) && include_next_api_level) { |
| next_level = 1 + platform_version.in_development_api_level |
| api_levels += [ "$next_level" ] |
| } |
| |
| foreach(supported_level, platform_version.supported_fuchsia_api_levels) { |
| if (supported_level == current_level) { |
| policy_for_current_level = "no_changes" |
| } else { |
| api_levels += [ "$supported_level" ] |
| } |
| } |
| |
| foreach(level, api_levels) { |
| fidl_ir_target_name = "${main_target_name}_compile_$level" |
| fidl_ir(fidl_ir_target_name) { |
| forward_variables_from(invoker, |
| [ |
| "experimental_flags", |
| "non_fidl_deps", |
| "public_deps", |
| "sources", |
| "testonly", |
| ]) |
| fidl_target_name = main_target_name |
| fidl_library_name = library_name |
| target_api_level = level |
| goldens_dir = goldens_dir |
| |
| # Avoid clobbering existing FIDL sources. |
| gen_dir = "$target_gen_dir/$level" |
| json_representation = "$gen_dir/$target_name.fidl.json" |
| out_json_summary = "$gen_dir/$library_name.api_summary.json" |
| } |
| sdk_atom_deps += [ ":$fidl_ir_target_name" ] |
| |
| # TODO(101964): Remove this. It's needed because we use the goldens as |
| # action.inputs so the build expects them to exist, but APIs outside the |
| # "fuchsia" namespace aren't compatibility tested and don't have goldens. |
| in_fuchsia_namespace = |
| library_name == |
| "fuchsia." + string_replace(library_name, "fuchsia.", "", 1) |
| not_needed([ "in_fuchsia_namespace" ]) |
| |
| if (defined(api_compatibility_testing) && api_compatibility_testing && |
| in_fuchsia_namespace) { |
| test_target = "${target_name}_${level}_compatibility_test" |
| fidl_api_compatibility_test(test_target) { |
| forward_variables_from(invoker, [ "testonly" ]) |
| target_api_level = level |
| current = "$target_gen_dir/$level/$library_name.api_summary.json" |
| |
| if (level == "$current_level") { |
| golden = "$goldens_dir/$level/$library_name.api_summary.json" |
| policy = policy_for_current_level |
| } else { |
| golden = "$goldens_dir/$level/$library_name.api_summary.json" |
| 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 = fidl_ir_json |
| schema = "//tools/fidl/fidlc/schema.json" |
| deps = [ ":$compilation_target_name" ] |
| } |
| |
| sdk_id = "sdk://fidl/$library_name" |
| |
| group(main_target_name) { |
| forward_variables_from(invoker, |
| [ |
| "testonly", |
| "visibility", |
| ]) |
| |
| metadata = { |
| if (defined(invoker.metadata)) { |
| forward_variables_from(invoker.metadata, "*") |
| } |
| |
| # Metadata to allow us to query all FIDL IR files. |
| fidl_json = [ rebase_path(fidl_ir_json, root_build_dir) ] |
| generated_sources = fidl_json |
| |
| if (is_fuchsia) { |
| component_catalog = [ |
| { |
| sdk_id = sdk_id |
| label = get_label_info(":$target_name", "label_with_toolchain") |
| }, |
| ] |
| } |
| } |
| |
| public_deps = [ |
| ":$compilation_target_name", |
| ":$lint_target_name", |
| ] |
| |
| deps = [ |
| ":$plasa_fragment_target_name", |
| ":$verification_target_name", |
| ] |
| |
| # Ignore the FIDL libraries that are only present in private versions of |
| # the SDK as they don't have the same versioning constraints. |
| target_dir = get_label_info(main_target_name, "dir") |
| is_vendor_library = |
| string_replace(target_dir, "//vendor/", "") != target_dir |
| if (!is_vendor_library && defined(invoker.sdk_category) && |
| invoker.sdk_category == "partner") { |
| deps += [ "//sdk/fidl:sdk_fidl_partner_allowlist" ] |
| } |
| |
| # Ensures that all libraries that are in the "partner_internal" category are |
| # in the "sdk_partner_internal_libs_allowlist" target. This is used to |
| # ensure that the API compatibility tests actually run on these libraries. |
| if (defined(invoker.sdk_category) && |
| invoker.sdk_category == "partner_internal") { |
| deps += [ "//sdk/fidl:sdk_partner_internal_libs_allowlist" ] |
| } |
| } |
| |
| if (is_sdk_included) { |
| sdk_category = invoker.sdk_category |
| if (is_sdk_included_publishable_nontest) { |
| 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 = [] |
| if (defined(invoker.public_deps)) { |
| foreach(dep, invoker.public_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 |
| } |
| |
| sdk_atom("${target_name}_sdk") { |
| id = sdk_id |
| |
| 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 = { |
| # Metadata to allow us to query all FIDL IR and source files. |
| fidl_json = [ rebase_path(fidl_ir_json, 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(fidl_ir_json, root_build_dir) |
| category = invoker.sdk_category |
| }, |
| ] |
| } |
| } |
| |
| non_sdk_deps = [ |
| ":$compilation_target_name", |
| ":$meta_target_name", |
| ] + sdk_atom_deps |
| |
| deps = [] |
| if (defined(invoker.public_deps)) { |
| foreach(dep, invoker.public_deps) { |
| label = get_label_info(dep, "label_no_toolchain") |
| deps += [ "${label}_sdk" ] |
| } |
| } |
| } |
| } |
| } |