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