| # 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 |
| } |
| } |