chromium/third_party/fuchsia-gn-sdk/src/component.gni

# Copyright 2019 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("cmc.gni")

declare_args() {
  # Enable code coverage for Fuchsia components.
  fuchsia_code_coverage = false
}

# Defines a Fuchsia component's manifest.
# See: https://fuchsia.dev/fuchsia-src/development/components/build
#
# Parameters
#
#   manifest [required]
#     The component manifest.
#     Type: path
#
#   component_name [optional]
#     The name of the component.
#     Type: string
#     Default: target_name
#
#   check_includes [optional]
#     Check against expect_includes() in deps.
#     Warning: Do not disable this unless there is a good, documented reason.
#     Type: boolean
#     Default: true
#
#   restricted_features [optional]
#     The set of restricted features to allow.
#     The set of features is allowlisted here: //tools/cmc/build/restricted_features/BUILD.gn
#     where each feature name is represented by a group of the same name.
#     Type: list of strings
#     Default: []
#
#   experimental_force_runner [optional]
#     Set the --experimental-force-runner flag to the given value.
#     This flag is experimental and may be removed without warning.
#     Type: string
#
#   required_offers (optional)
#     [list of strings] The set of protocols that should be offered to every child.
#
#   required_uses (optional)
#     [list of strings] The set of protocols that each child must use.
#
#   include_coverage_shard (optional)
#     Whether to include the `coverage.shard.cml` in a merged manifest.
#     Overrides the default based on `fuchsia_code_coverage`.
#     Should only be used in rare exceptions that do not use CML-based routing.
#
#   data_deps
#   deps
#   testonly
#   visibility
template("fuchsia_component_manifest") {
  assert(
      defined(invoker.manifest),
      "A `manifest` argument was missing when calling fuchsia_component_manifest($target_name)")

  forward_variables_from(invoker, [ "manifest" ])

  if (defined(invoker.include_coverage_shard)) {
    include_coverage_shard = invoker.include_coverage_shard
  } else {
    include_coverage_shard = fuchsia_code_coverage
  }

  if (include_coverage_shard) {
    merged_manifest = "${target_name}-coverage"
    manifest_file = get_path_info(manifest, "file")

    # Put the coverage merged manifest in a coverage directory to avoid
    # collision with no base manifest.
    manifest_rel_path = "coverage/${manifest_file}"
    cmc_merge(merged_manifest) {
      forward_variables_from(invoker,
                             [
                               "deps",
                               "testonly",
                             ])
      sources = [
        "${fuchsia_sdk}/pkg/sys/testing/coverage.shard.cml",
        manifest,
      ]
      output_name = manifest_rel_path
    }
    manifest = "${target_out_dir}/${manifest_rel_path}"
  }

  _manifest_name = get_path_info(manifest, "name")
  cmc_compile(target_name) {
    forward_variables_from(invoker,
                           [
                             "testonly",
                             "visibility",
                             "required_offers",
                             "required_uses",
                           ])
    manifest = rebase_path(manifest)

    # NOTE: must be kept in sync with path in structured_config.gni
    config_values_package_path = "$_manifest_name.cvf"
    deps = []
    if (defined(invoker.deps)) {
      deps += invoker.deps
    }
    if (include_coverage_shard) {
      deps += [ ":${merged_manifest}" ]
    }
  }
}

# Defines a Fuchsia component.
# See: https://fuchsia.dev/fuchsia-src/glossary#component
#
# A component is defined by a component manifest.
# A component is a unit of executable software on Fuchsia. When the componet is executed, there are usually many files
# needed at runtime, such as the executable itself, shared libraries, and possibly other data resources. These files are
# specified by using the GN concept of "data_deps".
#
# Components are distributed using a fuchsia package which can contain multiple components. The component manifest and
# the runtime dependencies of the component are packaged using the fuchsia_package rule.
#
# By default, the runtime depenencies of the component are included in the package using relative paths calcualted from
# the "closest" root of: $root_gen_dir, $root_dir, $out_dir. If a specific path is desired in the package, the "resources" parameter can
# be used to explicitly specify the path of a specific file.
#
# A component is launched by a URL which identifies the package and the component manifest.
# See: https://fuchsia.dev/fuchsia-src/glossary#component_url
#
# Example
#
# ```
#   fuchsia_component("my_component") {
#      manifest = "meta/component-manifest.cml"
#      resources = [
#        {
#          path = "testdata/use_case1.json"
#          dest = "data/testdata/use_case1.json"
#        },
#      ]
#      data_deps = [ ":hello_world_executable"]
#   }
# ```
#
# Parameters
#
#   data_deps
#     The labels of targets that generate the files needed at runtime by the component.
#     At minimum, this should include the label of the binary to include in the component.
#
#     Type: list of labels
#
#   manifest [optional]
#     The source manifest file for this component.
#
#     Type: string (filepath)
#
#   manifest_output_name [optional]
#     The name of the manifest file contained in the distribution that this
#     component is included in. The output name should have no extension or
#     prefixes. The resulting filename will have the ".cm" extension.
#     For example, if `manifest` is "foo.cml" and `manifest_output_name` is "bar",
#     the filename will be "bar.cm".
#
#     Type: string
#     Default: The base file name of `manifest` without an extension.
#
#   resources [optional]
#     Lists additional files to include for runtime access by the component.
#     Defines the resources in a package containing this component. A resource
#     is a data file that may be produced by the build system, checked in to a
#     source repository, or produced by another system that runs before the
#     build. Resources are placed in the `data/` directory of the assembled
#     package.
#
#     Type: list of scopes.  Entries in a scope in the resources list:
#       path (required)
#             Location of resource in source or build directory. If the
#             resource is checked in, this will typically be specified
#             as a path relative to the BUILD.gn file containing the
#             `package()` target. If the resource is generated, this will
#             typically be specified relative to `$target_gen_dir`.
#
#        dest (required) string (path) Location the resource will be placed within `data/`.
#
#   cm_label [optional]
#     Use label of a fuchsia_component_manifest target instead of supplying the manifest source.
#     Type: string, GN label e.g. `:my-manifest`
#
#   manifest_deps [optional]
#     Dependencies for the component's manifest, in case it is generated by another target.
#     Type: list of targets
#
#   required_offers (optional)
#     [list of strings] The set of protocols that should be offered to every child.
#
#   required_uses (optional)
#     [list of strings] The set of protocols that each child must use.
#
#   Standard parameters:
#     data_deps
#     deps
#     testonly
#     visibility
#
#   Metadata
#     contents - list of scopes describing files for this component. This metadata is consumed by
#       The fuchsia_package rule, which describes the entries used.
#       Entries in scope:
#         type: the usage type of the element, manifest or resource.
#                Each type can have specific properties included in the scope.
#         source [type == manifest || resource] The compiled manifest file to include in
#           the package for this component.
#         output_name [type == manifest] The basename of the manifest as it should appear in the package.
#         manifest_version [type == manifest]
#       dest: [type == resource] The package relative path of the resource.

template("fuchsia_component") {
  forward_variables_from(invoker, [ "manifest_output_name" ])

  _manifest_defined = defined(invoker.manifest)
  _cm_label_defined = defined(invoker.cm_label)
  assert(
      (_manifest_defined && !_cm_label_defined) ||
          (!_manifest_defined && _cm_label_defined),
      "Exactly one of `manifest` or `cm_label` argument must be specified when calling fuchsia_component($target_name)")

  if (defined(invoker.manifest)) {
    # Compile the manifest from source
    cm_target = "${target_name}_manifest_compile"
    fuchsia_component_manifest(cm_target) {
      forward_variables_from(invoker,
                             [
                               "deps",
                               "manifest",
                               "testonly",
                               "visibility",
                               "required_offers",
                               "required_uses",
                               "include_coverage_shard",
                             ])
      if (defined(invoker.manifest_deps)) {
        if (!defined(deps)) {
          deps = []
        }
        deps += invoker.manifest_deps
      }
    }
    cm_label = ":${cm_target}"
  } else {
    cm_label = invoker.cm_label
  }

  _compiled_manifest_outputs = get_target_outputs(cm_label)
  _manifest_source = _compiled_manifest_outputs[0]

  group(target_name) {
    forward_variables_from(invoker,
                           [
                             "data",
                             "deps",
                             "data_deps",
                             "resources",
                             "resources_in_json_files",
                             "testonly",
                             "visibility",
                           ])

    # Data is used for adding files as runtime dependencies. Files used as
    # sources in the resources parameter are added as data to make sure
    # non-generated files listed are captured as dependencies.
    if (!defined(data)) {
      data = []
    }

    if (!defined(resources)) {
      resources = []
    }

    if (!defined(deps)) {
      deps = []
    }
    deps += [ cm_label ]

    # Create the component metadata entries. These capture the manifest information and
    # the resources parameter contents.  The metadata is intended for the fuchsia_package rule.
    component_contents = [
      {
        type = "manifest"
        source = rebase_path(_manifest_source)
        if (defined(manifest_output_name)) {
          output_name = manifest_output_name
        }
      },
    ]
    foreach(resource, resources) {
      data += [ resource.path ]
      component_contents += [
        {
          type = "resource"
          source = rebase_path(resource.path)
          dest = resource.dest
        },
      ]
    }

    # TODO(richkadel): Changed from the Fuchsia GN SDK to add
    # `resources_in_json_files, so additional resources could be computed at
    # compile time (in this case, compiled Dart libraries).
    if (!defined(resources_in_json_files)) {
      resources_in_json_files = []
    }
    foreach(file, resources_in_json_files) {
      data += [ file ]
      component_contents += [
        {
          type = "json_of_resources"
          source = file
        },
      ]
    }

    # The contents of the component are enumerated in the
    # metadata.
    metadata = {
      contents = [ component_contents ]
    }
  }
}