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

# Copyright 2020 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("gn_configs.gni")
import("gn_sdk_configs.gni")

declare_args() {
  # TODO(fxbug.dev/93346): After soft-migration: required offers = [ "fuchsia.logger.LogSink" ]
  fuchsia_cmc_compile_must_offer_protocol = []
  fuchsia_cmc_compile_must_use_protocol = []
}

# Internal template for the cmc tool.
#
# Invokes cmc
#
# Example:
#
# ```
# _cmc_tool("validate") {
#   inputs = [ manifest ]
#   outputs = [ stamp_file ]
#
#   args = [
#      "--stamp",
#      rebase_path(stamp_file, root_build_dir),
#      "validate",
#      rebase_path(invoker.manifest),
#   ]
# }
# ```
#
# Parameters:
#
#  inputs (required)
#    List of files that are input for cmc.
#    Type: list(path)
#
#  outputs (required)
#    List paths that are output for the run of cmc.
#    Type: list(path)
#
#  args (required)
#    List command line args  for  cmc.
#    Type: list(path)
#
#  depfile
#  deps
#  public_deps
#  testonly
#  visibility
#
template("_cmc_tool") {
  action(target_name) {
    forward_variables_from(invoker,
                           [
                             "depfile",
                             "deps",
                             "public_deps",
                             "testonly",
                             "visibility",
                           ])
    script = gn_sdk_root + "/gn_run_binary.py"
    _cmc_tool_path = "${fuchsia_tool_dir}/cmc"

    assert(defined(invoker.inputs), "inputs is a required parameter.")
    assert(defined(invoker.outputs), "outputs is a required parameter.")
    assert(defined(invoker.args), "args is a required parameter.")

    inputs =
        [
          _cmc_tool_path,

          # Depend on the SDK hash, to ensure rebuild if the SDK tools change.
          fuchsia_sdk_manifest_file,
        ] + invoker.inputs

    outputs = invoker.outputs

    args = [ rebase_path(_cmc_tool_path, root_build_dir) ] + invoker.args
  }
}

# Compiles a Components Framework manifest (.cml) file to .cm
#
# Example:
#
# ```
# cmc_compile(_compiled_manifest_target) {
#      forward_variables_from(invoker, [ "deps" ])
#      manifest = rebase_path(manifest)
# }
# ```
#
# Parameters:
#
#   manifest (required)
#     The input Component Framework manifest source (.cml) file.
#     The file must have the extension ".cml".
#
#   output_file (optional)
#     Full path of the output file to generate. If not provided, it will be
#     derived from target_name and output_name.
#
#   output_name (optional)
#     Name of the output file to generate. Defaults to $target_name.
#     This should not include a file extension (.cm)
#
#   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.
#
#   deps
#   public_deps
#   testonly
#   visibility
#
template("cmc_compile") {
  assert(defined(invoker.manifest), "manifest file required")
  if (defined(invoker.output_file)) {
    output_file = invoker.output_file
  } else {
    output_name = get_path_info(invoker.manifest, "name")
    if (defined(invoker.output_name)) {
      output_name = invoker.output_name
    }

    # Output in a subdir with `target_name` to avoid collisions when a
    # manifest file is re-used for multiple component targets.
    output_file = "$target_gen_dir/$target_name/$output_name.cm"
  }

  _cmc_tool(target_name) {
    forward_variables_from(invoker,
                           [
                             "deps",
                             "manifest",
                             "public_deps",
                             "testonly",
                             "visibility",
                             "required_offers",
                             "required_uses",
                           ])

    inputs = [ manifest ]

    outputs = [ output_file ]

    depfile = "$target_gen_dir/$target_name/$target_name.d"

    args = [
      "compile",
      rebase_path(manifest, root_build_dir),
      "--output",
      rebase_path(output_file, root_build_dir),
      "--includeroot",
      rebase_path("//", root_build_dir),
      "--includepath",
      rebase_path("$fuchsia_sdk/pkg/", root_build_dir),
      "--includepath",
      get_path_info(invoker.manifest, "dir"),
      "--depfile",
      rebase_path(depfile, root_build_dir),
    ]

    if (defined(invoker.config_values_package_path)) {
      args += [
        "--config-package-path",
        invoker.config_values_package_path,
      ]
    }

    if (!defined(invoker.required_offers)) {
      required_offers = fuchsia_cmc_compile_must_offer_protocol
    }

    foreach(offer, required_offers) {
      args += [
        "--must-offer-protocol",
        offer,
      ]
    }

    if (!defined(invoker.required_uses)) {
      required_uses = fuchsia_cmc_compile_must_use_protocol
    }

    foreach(use_decl, required_uses) {
      args += [
        "--must-use-protocol",
        use_decl,
      ]
    }
  }
}

# Merges multiple component manifest files into one.
#
# Combines mutliple component manifests into a single manifest.
# This is useful for merging fragments of sandbox configurations into
# a single component manifest.
#
# Example
#
# ```
#   cmc_merge("combined_cml") {
#     sources = ["sandbox.cml", "services.cml", "program.cml"]
#     output_name = "my-component.cml"
#   }
# ```
#
# Parameters
#
#   sources
#     The list of cml files to merge together.
#
#     Type: list of strings (filepath)
#
#   output_name [optional]
#     The name of the merged cml file. This file is created in $target_out_dir.
#     If not specified, $target_name.cml is used.
#
#     Type: string
#
#   Standard parameters:
#     deps
#     testonly
#     visibility
#
template("cmc_merge") {
  _cmc_tool(target_name) {
    forward_variables_from(invoker,
                           [
                             "deps",
                             "output_name",
                             "sources",
                             "testonly",
                             "visibility",
                           ])
    if (!defined(output_name)) {
      output_name = "${target_name}.cml"
    }

    merged_output = "${target_out_dir}/${output_name}"
    inputs = invoker.sources
    outputs = [ merged_output ]

    args = [
      "merge",
      "--output",
      rebase_path(merged_output, root_build_dir),
    ]

    foreach(source, sources) {
      args += [ rebase_path(source, root_build_dir) ]
    }
  }
}