| # Copyright 2017 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/compiled_action.gni") |
| import("//build/json/validate_json.gni") |
| import("//build/sdk/plasa/config.gni") |
| import("//build/testing/golden_files.gni") |
| import("config.gni") |
| |
| # Defines an SDK element. |
| # |
| # Outputs |
| # |
| # $target_gen_dir/$target_name.sdk |
| # A manifest describing what files pertain to the atom and which other atoms |
| # are required by this atom. |
| # |
| # $target_gen_dir/$target_name.meta.json |
| # A metadata file describing the atom. |
| # This file is included in the final SDK and used to e.g. drive the |
| # inclusion of the atom in a different build system. |
| # |
| # Parameters |
| # |
| # id |
| # Identifier of this element within SDKs. |
| # The identifier should represent the canonical base path of the element |
| # within SDKs according to the standard layout (https://fuchsia.dev/fuchsia-src/development/sdk/layout.md). |
| # For an element at $ROOT/pkg/foo, the id should be "sdk://pkg/foo". |
| # |
| # category |
| # Describes the availability of the element. |
| # Possible values, from most restrictive to least restrictive: |
| # - excluded : the atom may not be included in SDKs; |
| # - experimental : the atom is available with no quality guarantee; |
| # - internal : the atom is exposed within the Fuchsia tree; |
| # - cts : the atom may be used in the Fuchsia Compatibility Test Suite; |
| # - partner : the atom may be used by select partners; |
| # - public : the atom may be included in published SDKs. |
| # |
| # meta |
| # Scope describing the element's metadata file. |
| # See the "Metadata scope" section for how to populate this attribute. |
| # |
| # files |
| # List of scopes describing the contents of this element. |
| # See the "File scopes" section for how to describe files. |
| # |
| # file_list |
| # Path to a file containing file mappings. |
| # Each line in the file should contain a "dest=source" mapping, similarly to |
| # file scopes. |
| # |
| # api (optional) |
| # Path to the file representing the API canonically exposed by this atom. |
| # This file is used to ensure modifications to the API are explicitly |
| # acknowledged. |
| # If this attribute is set, one of "current_api" and "api_contents" must be |
| # set as well. |
| # |
| # current_api (optional) |
| # Path to the file representing the API locally exposed by this atom. |
| # This file is used to verify that the API has not changed locally. |
| # Only relevant when "api" is set, and mutually exclusive with |
| # "api_contents". |
| # |
| # api_contents (optional) |
| # List of scopes for the files making up the atom's API. |
| # This list will be used to verify that the API has not changed locally. |
| # This is very roughly approximated by checking whether the files themselves |
| # have changed at all. |
| # See the "File scopes" section for how to describe files. |
| # Only relevant when "api" is set, and mutually exclusive with |
| # "current_api". |
| # |
| # deps (optional) |
| # List of GN labels for other SDK elements this element depends on at build |
| # time. |
| # These labels must point to "sdk_atom" targets. |
| # |
| # non_sdk_deps (optional) |
| # List of GN labels which this target needs built. |
| # |
| # Metadata scope |
| # |
| # This scope describes a metadata file to be added to the SDK element. Its |
| # supported attributes are: |
| # |
| # source (optional) |
| # Path to the metadata file. |
| # |
| # value (optional) |
| # Scope representing the metadata contents. |
| # |
| # NOTE: Exactly one of `source` or `value` must be set. |
| # |
| # dest (required) |
| # Path to the metadata file in the SDK, relatively to the SDK root |
| # |
| # schema (required) |
| # Name of the schema for this file, ignoring the extension. |
| # Metadata files are hosted under //build/sdk/meta. |
| # If the metadata file conforms to //build/sdk/meta/foo.json, the |
| # present attribute should have a value of "foo". |
| # |
| # type (optional) |
| # Type of the atom. Defaults to schema. |
| # |
| # File scopes |
| # |
| # Each scope describes a file to be added to the SDK element. The supported |
| # attributes are: |
| # |
| # source (required) |
| # Path to the original file. |
| # This path may be absolute or relative to the target's directory. |
| # |
| # dest (required) |
| # Destination path of the file relative to the SDK root. |
| |
| template("sdk_atom") { |
| assert(defined(invoker.category), "Must define an SDK category") |
| category = invoker.category |
| |
| assert(defined(invoker.id), "Must define an SDK ID") |
| |
| assert(defined(invoker.meta), "Must specify some metadata") |
| meta = invoker.meta |
| |
| if (defined(invoker.api)) { |
| if (!defined(invoker.current_api) && !defined(invoker.api_contents)) { |
| assert(false, "Must set one of 'current_api' and 'api_contents'") |
| } else if (defined(invoker.current_api) && defined(invoker.api_contents)) { |
| assert(false, "Must set only one of 'current_api' and 'api_contents'") |
| } |
| } |
| |
| gn_deps = [] |
| if (defined(invoker.non_sdk_deps)) { |
| gn_deps = invoker.non_sdk_deps |
| } |
| |
| dep_manifests = [] |
| if (defined(invoker.deps)) { |
| gn_deps += invoker.deps |
| foreach(dep, invoker.deps) { |
| gen_dir = get_label_info(dep, "target_gen_dir") |
| name = get_label_info(dep, "name") |
| dep_manifests += [ "$gen_dir/$name.sdk" ] |
| } |
| } |
| |
| # Some elements contain only the metadata. |
| if (!defined(invoker.files)) { |
| files = [] |
| } else { |
| files = invoker.files |
| } |
| file_args = [] |
| file_inputs = [] |
| foreach(file, files) { |
| assert(defined(file.source), "File $file does not specify a source.") |
| assert(defined(file.dest), "File $file does not specify a destination.") |
| file_inputs += [ file.source ] |
| file_args += [ |
| "--file", |
| file.dest, |
| rebase_path(file.source, root_build_dir), |
| ] |
| } |
| |
| meta_target_name = "${target_name}_meta" |
| meta_content = invoker.meta |
| meta_file = "$target_gen_dir/$target_name.meta.json" |
| |
| if (defined(meta_content.value)) { |
| meta_generated_file_target = "${target_name}_meta_generated_file" |
| generated_file(meta_generated_file_target) { |
| forward_variables_from(invoker, [ "testonly" ]) |
| outputs = [ meta_file ] |
| contents = meta_content.value |
| output_conversion = "json" |
| deps = gn_deps |
| } |
| meta_deps = [ ":$meta_generated_file_target" ] |
| } else { |
| meta_copy_target_name = "${target_name}_meta_copy" |
| assert(defined(meta_content.source), "Meta scope needs a source or value") |
| |
| # Copy the file to a canonical location for access by other rules. |
| # TODO(fxbug.dev/5364): instead, make sure that all atoms generate their metadata |
| # file in the right location. |
| copy(meta_copy_target_name) { |
| forward_variables_from(invoker, [ "testonly" ]) |
| |
| sources = [ meta_content.source ] |
| |
| outputs = [ meta_file ] |
| |
| deps = gn_deps |
| } |
| |
| meta_deps = [ ":$meta_copy_target_name" ] |
| } |
| |
| # Verify that the metadata complies with the specified schema. |
| validate_json(meta_target_name) { |
| forward_variables_from(invoker, [ "testonly" ]) |
| data = meta_file |
| schema = "//build/sdk/meta/${meta_content.schema}.json" |
| sources = [ |
| # This file is imported by all schemas. |
| "//build/sdk/meta/common.json", |
| ] |
| public_deps = meta_deps |
| } |
| |
| # Add the metadata file to the set of files to include in SDKs. |
| file_args += [ |
| "--file", |
| meta.dest, |
| rebase_path(meta_file, root_build_dir), |
| ] |
| |
| if (defined(invoker.api)) { |
| if (defined(invoker.api_contents)) { |
| assert(invoker.api_contents != [], "api_contents cannot be empty") |
| |
| generate_api_target_name = "${target_name}_generate_api" |
| current_api_file = "$target_gen_dir/$target_name.api" |
| |
| action(generate_api_target_name) { |
| forward_variables_from(invoker, [ "testonly" ]) |
| |
| script = "//build/sdk/compute_atom_api.py" |
| |
| inputs = [] |
| |
| outputs = [ current_api_file ] |
| |
| args = [ |
| "--output", |
| rebase_path(current_api_file, root_build_dir), |
| ] |
| deps = gn_deps |
| |
| foreach(file, invoker.api_contents) { |
| inputs += [ file.source ] |
| args += [ |
| "--file", |
| file.dest, |
| rebase_path(file.source, root_build_dir), |
| ] |
| } |
| } |
| } else { |
| current_api_file = invoker.current_api |
| } |
| |
| verify_api_target_name = "${target_name}_verify_api" |
| golden_files(verify_api_target_name) { |
| forward_variables_from(invoker, [ "testonly" ]) |
| comparisons = [ |
| { |
| candidate = current_api_file |
| golden = invoker.api |
| }, |
| ] |
| warn_on_changes = warn_on_sdk_changes |
| |
| if (defined(invoker.api_contents)) { |
| deps = [ ":$generate_api_target_name" ] |
| } |
| } |
| } |
| |
| # Builds a manifest representing this element. |
| action(target_name) { |
| forward_variables_from(invoker, |
| [ |
| "metadata", |
| "testonly", |
| ]) |
| |
| type = meta.schema |
| |
| # TODO(fxbug.dev/85082): Read the type from the schema file. |
| if (defined(meta.type)) { |
| type = meta.type |
| } |
| manifest = "$target_gen_dir/$target_name.sdk" |
| depfile = "$manifest.d" |
| |
| script = "//build/sdk/create_atom_manifest.py" |
| |
| public_deps = gn_deps + [ ":$meta_target_name" ] |
| if (defined(invoker.api)) { |
| public_deps += [ ":$verify_api_target_name" ] |
| } |
| |
| inputs = dep_manifests + file_inputs + [ |
| # Imported by the action's script. |
| "//build/sdk/sdk_common.py", |
| ] |
| |
| outputs = [ manifest ] |
| |
| args = [ |
| "--id", |
| invoker.id, |
| "--out", |
| rebase_path(manifest, root_build_dir), |
| "--depfile", |
| rebase_path(depfile, root_build_dir), |
| "--gn-label", |
| get_label_info(":$target_name", "label_with_toolchain"), |
| "--category", |
| category, |
| "--meta", |
| meta.dest, |
| "--type", |
| type, |
| "--deps", |
| ] + rebase_path(dep_manifests, root_build_dir) + file_args |
| |
| if (generate_plasa_artifacts && defined(meta.plasa_manifest)) { |
| args += [ |
| "--plasa", |
| rebase_path(meta.plasa_manifest, root_build_dir), |
| ] |
| } |
| |
| if (defined(invoker.file_list)) { |
| inputs += [ invoker.file_list ] |
| args += [ |
| "--file-list", |
| rebase_path(invoker.file_list, root_build_dir), |
| ] |
| } |
| |
| # The manifest output contains the output directory. |
| no_output_dir_leaks = false |
| } |
| } |