v8 147.3.0

Rust bindings to V8
Documentation
# Copyright 2026 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import("//build/toolchain/kythe.gni")

# Defines a C++ `source_set` (and supporting targets) that contains C++
# bindings for using/calling-into a given Rust crate.
#
# `cpp_api_from_rust.gni` should only be `import`ed from
# `gni_impl/rust_target.gni`.  The public way to use these bindings is the
# `cpp_api_from_rust` parameter of `rust_static_library` and/or `cargo_crate`
# templates.
#
# Parameters
#
#   rust_target
#     Label of the Rust library to generate the bindings for.
#
#   rustc_env_and_flags
#     Path to the `.rustflags.json` file generated by an earlier invocation
#     of `rustc`.  (`cpp_api_from_rust` needs to be invoked with the same
#     arguments, target platform, etc. as `rust_target`.)
#
#   crate_name
#     Name (potentially mangled/auto-generated) of the crate that `rust_target`
#     is built into.
#
#   original_attributes
#     The original `cc_bindings_from_rs = { ... }` dictionary (the value of
#     the `cc_bindings_from_rs` attribute of a target like
#     `rust_static_library`).
#
#   sources
#   deps
#     Dependencies for compiling `rust_target`.
#
#
template("cpp_api_from_rust") {
  _rspgen_target_name = "${target_name}_crubit_rspgen"
  _action_target_name = "${target_name}_crubit_action"
  _rust_impl_target_name = "${target_name}_crubit_impl"
  _h_target_name = target_name

  assert(defined(invoker.deps))
  assert(defined(invoker.sources))
  assert(defined(invoker.rustc_env_and_flags))
  assert(defined(invoker.rust_target))
  assert(defined(invoker.original_attributes))
  assert(defined(invoker.crate_name))
  _crate_name = invoker.crate_name
  _rust_target = invoker.rust_target
  _rust_target_name = get_label_info(_rust_target, "name")
  _rust_target_dir = get_label_info(_rust_target, "dir")
  _h_out = "${target_gen_dir}/${_rust_target_name}.h"
  _rs_out = "${_h_out}.impl.rs"

  _original_attributes = invoker.original_attributes
  assert(defined(_original_attributes.cpp_namespace))
  _cpp_namespace = _original_attributes.cpp_namespace

  if (!defined(_original_attributes.deps)) {
    _original_attributes.deps = []
  }
  _is_rust_stdlib_target = _rust_target_dir == "//build/rust/std/rules"
  if (!_is_rust_stdlib_target) {
    _original_attributes.deps += [ "//build/rust/std:std_bindings" ]
  }

  _crubit_support_path_format =
      "\"third_party/rust-toolchain/lib/crubit/support/{header}\""
  _cpp_api_from_rust_exe_path =
      "//third_party/rust-toolchain/bin/cc_bindings_from_rs"
  _rustfmt_exe_path = "//third_party/rust-toolchain/bin/rustfmt"
  _rustfmt_config_path = "//.rustfmt.toml"
  if (host_os == "win") {
    _cpp_api_from_rust_exe_path += ".exe"
    _rustfmt_exe_path += ".exe"
  }

  # TODO(lukasza): Make this also work on other host platforms.  (Possibly
  # hoisting this `cpp_api_from_rust`-agnostic functionality into some other,
  # reusable location elsewhere.)
  if (host_os == "linux") {
    _clang_format_exe_path = "//buildtools/linux64-format/clang-format"
  } else if (host_os == "win") {
    _clang_format_exe_path = "//buildtools/win-format/clang-format.exe"
  }

  _action_rsp_path = "$target_out_dir/$_action_target_name.rsp"
  generated_file(_rspgen_target_name) {
    forward_variables_from(invoker, [ "testonly" ])
    forward_variables_from(_original_attributes, [ "deps" ])
    outputs = [ _action_rsp_path ]
    data_keys = [
      "crate_header",
      "crate_namespace",
    ]
    walk_keys = []  # Only metadata of direct, non-transitive `deps`.
    visibility = [ ":${_action_target_name}" ]
  }

  action(_action_target_name) {
    forward_variables_from(invoker,
                           [
                             "sources",
                             "deps",
                             "testonly",
                           ])
    deps += [ ":${_rspgen_target_name}" ]

    visibility = [
      ":$_h_target_name",
      ":$_rust_impl_target_name",
    ]

    script = "//build/rust/gni_impl/cpp_api_from_rust_wrapper.py"
    inputs = [
      # `import`ed for `LoadRustEnvAndFlags`, etc.
      "//build/rust/gni_impl/rustc_wrapper.py",

      # Tools / executables:
      _cpp_api_from_rust_exe_path,
      _rustfmt_exe_path,
      _rustfmt_config_path,
    ]
    outputs = [
      _h_out,
      _rs_out,
    ]

    # `cpp_api_from_rust_wrapper.py` flags:
    args = [
      "--cpp-api-from-rust-exe-path",
      rebase_path(_cpp_api_from_rust_exe_path, root_build_dir),

      "--rustc-env-and-flags",
      rebase_path(invoker.rustc_env_and_flags, root_build_dir),
      "@" + rebase_path(_action_rsp_path, root_build_dir),
    ]

    # `cpp_api_from_rust` flags:
    args += [
      "--",

      "--h-out",
      rebase_path(_h_out, root_build_dir),

      "--rs-out",
      rebase_path(_rs_out, root_build_dir),

      "--crubit-support-path-format",
      _crubit_support_path_format,

      "--rustfmt-exe-path",
      rebase_path(_rustfmt_exe_path, root_build_dir),

      "--rustfmt-config-path",
      rebase_path(_rustfmt_config_path, root_build_dir),

      "--crate-namespace",
      "self=${_cpp_namespace}",
    ]
    if (defined(_clang_format_exe_path)) {
      inputs += [ _clang_format_exe_path ]
      args += [
        "--clang-format-exe-path",
        rebase_path(_clang_format_exe_path, root_build_dir),
      ]
    }
    if (enable_kythe_annotations) {
      args += [
        "--kythe-annotations",
        "--kythe-default-corpus",
        kythe_corpus_identifier,
      ]
    }
  }

  # Using `rust_library` instead of `rust_static_library` to minimize
  # dependencies of `cpp_api_from_rust.gni` (otherwise `import`ing
  # `cpp_api_from_rust.gni` from `rust_target.gni` would lead to
  # a cycle).
  rust_library(_rust_impl_target_name) {
    forward_variables_from(invoker,
                           [
                             "deps",
                             "testonly",
                           ])
    if (!defined(deps)) {
      deps = []
    }
    deps += [
      ":$_action_target_name",
      "//build/rust/crubit:cpp_api_from_rust_bindings_rs_deps",
      _rust_target,
    ]
    visibility = [ ":$_h_target_name" ]

    # Without going through `rust_static_library` / `rust_target` we have to
    # implement our own little mangling scheme.  Thankfully, the only user
    # of this crate is `_h_target_name`, which only consumes C ABI top-level
    # entrypoints - this means that the crate name isn't used / doesn't leak
    # anywhere.  And therefore it doesn't matter what kind of mangling we do,
    # as long as we ensure that it is globally unique.
    _crate_name_suffix =
        string_hash(get_label_info(_rust_target_name, "label_with_toolchain"))
    crate_name = "${_rust_target_name}_crubit_${_crate_name_suffix}"
    output_name = crate_name

    crate_root = _rs_out
    sources = [ crate_root ]
    configs += [ "//build/rust:default_edition" ]
  }

  source_set(_h_target_name) {
    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)

    forward_variables_from(_original_attributes, [ "deps" ])
    if (!defined(deps)) {
      deps = []
    }
    deps += [
      ":$_action_target_name",
      ":$_rust_impl_target_name",
      "//build/rust/crubit:cpp_api_from_rust_bindings_cpp_deps",
    ]

    _gen_dir_relative_h_out = rebase_path(_h_out, root_gen_dir)
    metadata = {
      crate_header =
          [ "--crate-header=${_crate_name}=${_gen_dir_relative_h_out}" ]
      crate_namespace = [ "--crate-namespace=${_crate_name}=${_cpp_namespace}" ]
    }

    public = [ _h_out ]
  }
}