| # Copyright 2021 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/dist/resource.gni") | 
 | import("//build/json/validate_json.gni") | 
 |  | 
 | # Defines an SDK element. | 
 | # | 
 | # Direct instantiation of `sdk_element` is not supported. Element authors should | 
 | # use specialized templates, e.g. `host_tool_sdk_element`, instead. | 
 | # | 
 | # Outputs | 
 | # | 
 | #   $target_gen_dir/${target_name}_sdk_metadata.json | 
 | #     A metadata file describing the element. | 
 | #     * This file is included in the final SDK and may be used by tools that | 
 | #       interact with the SDK. | 
 | # | 
 | # Parameters | 
 | # | 
 | #   schema (required) | 
 | #     Scope describing the element metadata JSON schema. It contains the | 
 | #     following fields: | 
 | #     * source (required) | 
 | #       A GN label for the JSON schema file constraining this element's | 
 | #       metadata. The schemata can be found under //sdk/schema. | 
 | #     * deps (optional) | 
 | #       A list of GN labels for schemas included by the source. Defaults | 
 | #       to //sdk/schema/common-00000000.json. | 
 | # | 
 | #   meta (required) | 
 | #     Scope describing the element metadata that is packaged together with | 
 | #     the element contents. See `files`. It contains the following fields: | 
 | #     * contents (required unless `source` is set) | 
 | #       This field structure is dictated by the JSON schema. | 
 | #     * source (required if `contents` is not set) | 
 | #       A source label of the file containing the metadata. | 
 | #     * dest (optional) | 
 | #       A path relative to the root of the SDK element in the package. By | 
 | #       default, this is "${target_name}_sdk_metadata.json". | 
 | # | 
 | #   files (optional) | 
 | #     List of scopes describing the contents of this element. | 
 | #     * A file scope has the following attributes: | 
 | #       source (required) | 
 | #         A GN label representing the source. Must be absolute, | 
 | #         beginning with "//". | 
 | #       dest (required) | 
 | #         Destination path in the SDK relative to the element root. | 
 | # | 
 | #   deps (optional) | 
 | #     A list of labels of targets contributing the content to the SDK. | 
 | # | 
 | #   api (optional) [TODO(b/204903307): implement the api parameter] | 
 | #     Scope describing the API surface of the SDK element. If the contents of | 
 | #     the precomputed digest differ from the generated one, the build will | 
 | #     fail and the change will have to be explicitly acknowledged. The scope | 
 | #     api contains the following fields: | 
 | #     * digest (required) | 
 | #       A GN label for a JSON file containing a map of file paths to MD5 | 
 | #       hashes. The file paths are source files in the files scope unless the | 
 | #       sources field below is set. The digest can be customized by specifying | 
 | #       a digester tool. | 
 | #     * sources (required if `files` scope is not set) | 
 | #       A list of GN sources labels to use for digest computation. In case of a C++ | 
 | #       library, this will be a list of headers, which constitute the element's | 
 | #       API surface. If absent, the `files` scope is used to generate the | 
 | #       digest. | 
 | #     * digester (optional) | 
 | #       A GN label for a custom digester tool. | 
 | # | 
 | #  Metadata | 
 | #    sdk_adapter | 
 | #      The metadata is intended to be consumed only by the | 
 | #      sdk_element_adapter() template. It will re removed once all SDK build | 
 | #      targets have been migrated. | 
 | #    * files | 
 | #      A list of file scope specifying the mapping between the source and | 
 | #      destination. | 
 | #      * source | 
 | #        The absolute label of the source that is mapped into the SDK> | 
 | #      * dest | 
 | #        A path relative to the SDK package root. | 
 | #    * meta | 
 | #      A scope specifying the element metadata mapping. The contents are | 
 | #      identical to the file scope. | 
 | template("sdk_element") { | 
 |   if (defined(invoker.deps)) { | 
 |     gn_deps = invoker.deps | 
 |   } else { | 
 |     gn_deps = [] | 
 |   } | 
 |  | 
 |   # Validate the schema scope. | 
 |   assert(defined(invoker.schema), "Must define the schema scope.") | 
 |   schema = invoker.schema | 
 |   assert(defined(schema.source), "The schema scope must define a source.") | 
 |   schema_source = schema.source | 
 |  | 
 |   if (defined(schema.deps)) { | 
 |     schema_deps = schema.deps | 
 |   } else { | 
 |     schema_deps = [ | 
 |       "//sdk/schema/common-00000000.json", | 
 |       "//sdk/schema/sdk/sdk_common-00000000.json", | 
 |     ] | 
 |   } | 
 |  | 
 |   # Validate the file scope. | 
 |   if (defined(invoker.files)) { | 
 |     sdk_files = invoker.files | 
 |  | 
 |     foreach(file, sdk_files) { | 
 |       assert(defined(file.source), | 
 |              "A file mapping '$file' must specify a source.") | 
 |       assert(defined(file.dest), | 
 |              "A file mapping '$file must specify a destination.") | 
 |     } | 
 |   } else { | 
 |     sdk_files = [] | 
 |   } | 
 |  | 
 |   # Validate the metadata scope. | 
 |   assert(defined(invoker.meta), "Must define the metadata scope.") | 
 |   element_meta = invoker.meta | 
 |   assert(defined(element_meta.contents) || defined(element_meta.source), | 
 |          "Must define either a metadata contents or a source") | 
 |   assert(!(defined(element_meta.contents) && defined(element_meta.source)), | 
 |          "Metadata contents and the source are mutually exclusive") | 
 |  | 
 |   # Prepare and validate the element metadata against the schema. | 
 |   meta_target = "${target_name}.metadata_generator" | 
 |   meta_file = "${target_gen_dir}/${target_name}_sdk_metadata.json" | 
 |  | 
 |   if (defined(element_meta.contents)) { | 
 |     generated_file(meta_target) { | 
 |       forward_variables_from(invoker, [ "testonly" ]) | 
 |       visibility = [ ":*" ] | 
 |       outputs = [ meta_file ] | 
 |       contents = element_meta.contents | 
 |       output_conversion = "json" | 
 |     } | 
 |   } else { | 
 |     copy(meta_target) { | 
 |       sources = [ element_meta.source ] | 
 |       outputs = [ meta_file ] | 
 |     } | 
 |   } | 
 |  | 
 |   # Validate the metadata against the schema. | 
 |   meta_validator_target = "${target_name}.meta_validator" | 
 |   validate_json(meta_validator_target) { | 
 |     forward_variables_from(invoker, [ "testonly" ]) | 
 |     visibility = [ ":*" ] | 
 |     use_valico = true  # Use rust-based validator | 
 |     data = meta_file | 
 |     schema = schema_source | 
 |     sources = schema_deps | 
 |     deps = [ ":${meta_target}" ] | 
 |   } | 
 |  | 
 |   # Map the metadata file so it is packaged together with other files. | 
 |   element_files = sdk_files | 
 |  | 
 |   if (defined(element_meta.dest)) { | 
 |     meta_dest = element_meta.dest | 
 |   } else { | 
 |     meta_dest = "${target_name}_sdk_metadata.json" | 
 |   } | 
 |  | 
 |   element_files += [ | 
 |     { | 
 |       source = meta_file | 
 |       dest = meta_dest | 
 |     }, | 
 |   ] | 
 |  | 
 |   # TODO(b/216398199): Decide whether to include the schemata | 
 |   # with element_files. | 
 |  | 
 |   # Create a distribution manifest for the sdk_package() template. | 
 |   manifest_target = "${target_name}.manifest_generator" | 
 |   resource_group(manifest_target) { | 
 |     forward_variables_from(invoker, [ "testonly" ]) | 
 |     visibility = [ ":*" ] | 
 |     files = element_files | 
 |     deps = [ ":${meta_target}" ] + gn_deps | 
 |   } | 
 |  | 
 |   # TODO(b/204903307): .api generation/validation | 
 |  | 
 |   group(target_name) { | 
 |     forward_variables_from(invoker, | 
 |                            [ | 
 |                              "testonly", | 
 |                              "visibility", | 
 |                            ]) | 
 |     deps = [ ":${meta_validator_target}" ] | 
 |     public_deps = [ | 
 |       ":${manifest_target}", | 
 |       ":${meta_target}", | 
 |     ] | 
 |  | 
 |     # The metadata is exported for the sole benefit of the sdk_adapter(). | 
 |     # No other template should take a dependency on it. | 
 |     metadata = { | 
 |       sdk_adapter = [ | 
 |         { | 
 |           meta = { | 
 |             source = meta_file | 
 |             dest = meta_dest | 
 |           } | 
 |           files = sdk_files | 
 |         }, | 
 |       ] | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | # Produces sdk_molecule-compatible metadata from an sdk_element. | 
 | # | 
 | # This template converts SDK element metadata into SDK atom metadata, to allow | 
 | # SDK atoms to be gradually migrated to sdk_element without breaking the | 
 | # monolithic SDK build. | 
 | # | 
 | # Outputs | 
 | # | 
 | #   $target_gen_dir/${target_name}.sdk | 
 | #     A manifest describing which files are included in the element. | 
 | # | 
 | #   $target_gen_dir/${target_name}_adapter.meta.json | 
 | #     A metadata file describing the element. | 
 | # | 
 | # Parameters | 
 | # | 
 | #   deps (required) | 
 | #     A single element list containing an sdk_element GN label. | 
 | # | 
 | #   category (optional) | 
 | #     The SDK atom category. Defaults to `partner`. | 
 | template("sdk_element_adapter") { | 
 |   assert(defined(invoker.deps), "Must define deps.") | 
 |  | 
 |   if (defined(invoker.category)) { | 
 |     category = invoker.category | 
 |   } else { | 
 |     category = "partner" | 
 |   } | 
 |  | 
 |   # Collect GN metadata corresponding to the sdk_element. | 
 |   gn_meta_collector_target = "${target_name}.gn_meta_collector" | 
 |   gn_meta_file = "${target_gen_dir}/${target_name}_gn_meta.json" | 
 |  | 
 |   # .sdk format manifest | 
 |   atom_manifest_file = "${target_gen_dir}/${target_name}.sdk" | 
 |  | 
 |   # .meta.json format metadata | 
 |   atom_meta_file = "${target_gen_dir}/${target_name}_adapter.meta.json" | 
 |  | 
 |   generated_file(gn_meta_collector_target) { | 
 |     outputs = [ gn_meta_file ] | 
 |     data_keys = [ "sdk_adapter" ] | 
 |     output_conversion = "json" | 
 |     deps = invoker.deps | 
 |   } | 
 |  | 
 |   action(target_name) { | 
 |     forward_variables_from(invoker, | 
 |                            [ | 
 |                              "testonly", | 
 |                              "visibility", | 
 |                            ]) | 
 |     depfile = "$target_gen_dir/$target_name.d" | 
 |     deps = [ ":${gn_meta_collector_target}" ] | 
 |     public_deps = invoker.deps | 
 |     outputs = [ | 
 |       atom_meta_file, | 
 |       atom_manifest_file, | 
 |     ] | 
 |     script = "//build/sdk/sdk_element_adapter.py" | 
 |     sources = [ gn_meta_file ] | 
 |     args = [ | 
 |       "--atom-manifest-path", | 
 |       rebase_path(atom_manifest_file, root_build_dir), | 
 |       "--atom-meta-path", | 
 |       rebase_path(atom_meta_file, root_build_dir), | 
 |       "--depfile-path", | 
 |       rebase_path(depfile, root_build_dir), | 
 |       "--gn-label", | 
 |       get_label_info(":${target_name}", "label_with_toolchain"), | 
 |       "--gn-meta-path", | 
 |       rebase_path(gn_meta_file, root_build_dir), | 
 |       "--root-build-dir", | 
 |       root_build_dir, | 
 |       "--sdk-category", | 
 |       category, | 
 |     ] | 
 |  | 
 |     # --root-build-dir exposes the output directory name | 
 |     no_output_dir_leaks = false | 
 |   } | 
 | } |