298 lines
9.9 KiB
Plaintext
298 lines
9.9 KiB
Plaintext
# Copyright 2022 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/config/clang/clang.gni")
|
|
import("//build/config/rust.gni")
|
|
import("//build/config/sysroot.gni")
|
|
import("//build/rust/mixed_static_library.gni")
|
|
|
|
# Template to generate and build Rust bindings for a set of C++ headers using
|
|
# Crubit's `rs_bindings_from_cc` tool.
|
|
#
|
|
# This template expands to a `mixed_static_library` named "<target>_rs_api" and
|
|
# containing the Rust side of the bindings (as well as internal C++ thunks
|
|
# needed to support the bindings).
|
|
#
|
|
# The generated out/.../gen/.../<target>_rs_api.rs is machine-generated, but
|
|
# should be fairly readable (inspecting it might be useful to discover the
|
|
# imported bindings and their shape).
|
|
#
|
|
# Parameters:
|
|
#
|
|
# bindings_target:
|
|
# The C++ target (e.g. a `source_set`) that Rust bindings should be
|
|
# generated for.
|
|
#
|
|
# public_headers:
|
|
# The .h files to generate bindings for.
|
|
#
|
|
# Implementation note: This doesn't just take *all* the headers of the
|
|
# `bindings_target`, because typically only a *subset* of headers provides
|
|
# the *public* API that bindings are needed for.
|
|
#
|
|
# TODO(crbug.com/1329611): Internal headers should still to be included in
|
|
# the targets_and_headers metadata...
|
|
#
|
|
# deps:
|
|
# Other `rs_bindings_from_cc` targets that the bindings need to depend on
|
|
# (e.g. because APIs in the `public_headers` refer to `struct`s declared in
|
|
# those other targets. Note how in the usage example below bindings for
|
|
# `struct Goat` are provided by `goat_rs_api`, and that therefore the
|
|
# bindings for the `TeleportGoat` provided by `teleport_rs_api` depend on
|
|
# `goat_rs_api`).
|
|
#
|
|
# Oftentimes `deps` can be a copy of the `public_deps` of the
|
|
# `bindings_target`, but depending on targets with the suffix "_rs_api".
|
|
# Still, there are scenarios where `deps` don't parallel *all* entries from
|
|
# `public_deps`:
|
|
# * `public_deps` that don't expose Rust APIs (i.e. there are no
|
|
# "..._rs_api" targets to depend on).
|
|
# * `public_deps` that Crubit bindings don't depend on (dependencies that
|
|
# don't provide re-exportable C++ APIs, or that only provide items
|
|
# that are ignored by Crubit - e.g. `#define`s).
|
|
#
|
|
# Usage example:
|
|
#
|
|
# BUILD.gn:
|
|
# import("//build/rust/rs_bindings_from_cc.gni")
|
|
# import("//build/rust/rust_executable.gni")
|
|
#
|
|
# rust_executable("my_target") {
|
|
# crate_root = "main.rs"
|
|
# sources = [ "main.rs" ]
|
|
# deps = [ ":teleport_rs_api" ]
|
|
# ]
|
|
#
|
|
# # This will generate "teleport_rs_api" target that provides Rust
|
|
# # bindings for the "teleport.h" header from the ":teleport" source
|
|
# # set.
|
|
# rs_bindings_from_cc("teleport_rs_api") {
|
|
# bindings_target = ":teleport"
|
|
# public_headers = ["teleport.h"]
|
|
# deps = [ ":goat_rs_api" ] # Parallel's `public_deps` of ":teleport".
|
|
# }
|
|
#
|
|
# source_set("teleport") {
|
|
# sources = [ "teleport.h", ... ]
|
|
# public_deps = [ ":goat" ]
|
|
# }
|
|
#
|
|
# rs_bindings_from_cc("goat_rs_api") {
|
|
# bindings_target = ":goat"
|
|
# public_headers = ["goat.h"]
|
|
# }
|
|
# source_set("goat") {
|
|
# sources = [ "goat.h", ... ]
|
|
# }
|
|
#
|
|
# teleport.h:
|
|
# #include "goat.h"
|
|
# void TeleportGoat(const Goat& goat_to_teleport);
|
|
#
|
|
# goat.h:
|
|
# struct Goat { ... };
|
|
#
|
|
# main.rs:
|
|
# fn main() {
|
|
# let g: goat_rs_api::Goat = ...;
|
|
# teleport_rs_api::TeleportGoat(&g);
|
|
# }
|
|
#
|
|
# Debugging and implementation notes:
|
|
#
|
|
# - Consider running the build while CRUBIT_DEBUG environment variable is set.
|
|
# This will generate additional `.ir` file and log extra information from
|
|
# the `run_rs_bindings_from_cc.py` script (e.g. full cmdlines for invoking
|
|
# `rs_bindings_from_cc`).
|
|
#
|
|
template("rs_bindings_from_cc") {
|
|
# Mandatory parameter: bindings_target.
|
|
assert(defined(invoker.bindings_target),
|
|
"Must specify the C target to make bindings for.")
|
|
_bindings_target = invoker.bindings_target
|
|
|
|
# Mandatory/unavoidable parameter: target_name
|
|
_lib_target_name = target_name
|
|
_base_target_name = get_label_info(_bindings_target, "name")
|
|
assert(_lib_target_name == "${_base_target_name}_rs_api",
|
|
"The convention is that bindings for `foo` are named `foo_rs_api`")
|
|
|
|
# Mandatory parameter: public_headers.
|
|
assert(defined(invoker.public_headers),
|
|
"Must specify the public C headers to make bindings for.")
|
|
_rebased_public_headers = []
|
|
foreach(hdr, invoker.public_headers) {
|
|
_rebased_public_headers += [ rebase_path(hdr) ]
|
|
}
|
|
|
|
# Optional parameter: testonly.
|
|
_testonly = false
|
|
if (defined(invoker.testonly)) {
|
|
_testonly = invoker.testonly
|
|
}
|
|
|
|
# Optional parameter: visibility.
|
|
if (defined(invoker.visibility)) {
|
|
_visibility = invoker.visibility
|
|
}
|
|
|
|
# Optional parameter: deps.
|
|
#
|
|
# TODO(crbug.com/1329611): Can we somehow assert that `_deps` only contains
|
|
# some "..._rs_api" targets crated via
|
|
# `mixed_static_library($_lib_target_name)` below? foreach(dep, _deps) {
|
|
# assert something }
|
|
_deps = []
|
|
if (defined(invoker.deps)) {
|
|
_deps = invoker.deps
|
|
}
|
|
|
|
# Various names and paths that are shared across multiple targets defined
|
|
# in the template here.
|
|
_gen_bindings_target_name = "${_lib_target_name}_gen_bindings"
|
|
_gen_metadata_target_name = "${_lib_target_name}_gen_metadata"
|
|
_metadata_target_name = "${_lib_target_name}_metadata"
|
|
_metadata_path = "${target_gen_dir}/${_lib_target_name}_meta.json"
|
|
_rs_out_path = "${target_gen_dir}/${_lib_target_name}.rs"
|
|
_cc_out_path = "${target_gen_dir}/${_lib_target_name}_impl.cc"
|
|
|
|
# Calculating the --targets_and_headers snippet for the *current* target
|
|
# and putting it into GN's `metadata`.
|
|
group(_metadata_target_name) {
|
|
testonly = _testonly
|
|
visibility = [
|
|
":${_gen_metadata_target_name}",
|
|
":${_lib_target_name}",
|
|
]
|
|
deps = []
|
|
|
|
metadata = {
|
|
# The data below corresponds to a single-target entry inside
|
|
# `--targets_and_headers` cmdline argument of `rs_bindings_from_cc`.
|
|
crubit_target_and_headers = [
|
|
{
|
|
# The `get_label_info` call below expands ":foo_rs_api" into
|
|
# something like "//dir/bar/baz:foo_rs_api". Crubit assumes that
|
|
# there is a colon + uses the after-colon-suffix as the name of the
|
|
# crate.
|
|
t = get_label_info(":${_lib_target_name}", "label_no_toolchain")
|
|
h = _rebased_public_headers
|
|
},
|
|
]
|
|
}
|
|
}
|
|
|
|
# Gathering --targets-and-headers data from *all* transitive dependencies and
|
|
# putting them into the file at `_metadata_path`.
|
|
generated_file(_gen_metadata_target_name) {
|
|
testonly = _testonly
|
|
visibility = [ ":${_gen_bindings_target_name}" ]
|
|
|
|
deps = [ ":${_metadata_target_name}" ]
|
|
deps += _deps
|
|
|
|
testonly = _testonly
|
|
outputs = [ _metadata_path ]
|
|
output_conversion = "json"
|
|
data_keys = [ "crubit_target_and_headers" ]
|
|
|
|
# `walk_keys` are used to limit how deep the transitive dependency goes.
|
|
# This is important, because Crubit doesn't care about all the `deps` or
|
|
# `public_deps` of the `_bindings_target`. (See also the doc comment about
|
|
# `rs_bindings_from_cc.deps` parameter at the top of this file.)
|
|
walk_keys = [ "crubit_metadata_deps" ]
|
|
}
|
|
|
|
# Exposing the generated Rust bindings.
|
|
mixed_static_library(_lib_target_name) {
|
|
testonly = _testonly
|
|
if (defined(_visibility)) {
|
|
visibility = _visibility
|
|
}
|
|
|
|
sources = [ _cc_out_path ]
|
|
deps = _deps
|
|
deps += [
|
|
":${_gen_bindings_target_name}",
|
|
":${_metadata_target_name}",
|
|
"//third_party/crubit:deps_of_rs_api_impl",
|
|
_bindings_target,
|
|
]
|
|
|
|
# Chromium already covers `chromium/src/` and `out/Release/gen` in the
|
|
# include path, but we need to explicitly add `out/Release` below. This
|
|
# is needed, because `--public_headers` passed to Crubit use paths relative
|
|
# to the `out/Release` directory. See also b/239238801.
|
|
include_dirs = [ root_build_dir ]
|
|
|
|
rs_sources = [ _rs_out_path ]
|
|
rs_crate_name = _lib_target_name
|
|
rs_crate_root = _rs_out_path
|
|
rs_deps = _deps
|
|
rs_deps += [
|
|
":${_gen_bindings_target_name}",
|
|
"//third_party/crubit:deps_of_rs_api",
|
|
]
|
|
|
|
metadata = {
|
|
crubit_metadata_deps = _deps + [ ":${_metadata_target_name}" ]
|
|
}
|
|
}
|
|
|
|
# Invoking Crubit's `rs_bindings_from_cc` tool to generate Rust bindings.
|
|
action(_gen_bindings_target_name) {
|
|
testonly = _testonly
|
|
if (defined(_visibility)) {
|
|
visibility = _visibility
|
|
}
|
|
|
|
script = "//build/rust/run_rs_bindings_from_cc.py"
|
|
inputs = [ "//third_party/rust-toolchain/bin/rs_bindings_from_cc" ]
|
|
sources = invoker.public_headers
|
|
outputs = [
|
|
_rs_out_path,
|
|
_cc_out_path,
|
|
]
|
|
|
|
deps = [ ":${_gen_metadata_target_name}" ]
|
|
args = [
|
|
# Target-specific outputs:
|
|
"--rs_out",
|
|
rebase_path(_rs_out_path),
|
|
"--cc_out",
|
|
rebase_path(_cc_out_path),
|
|
|
|
# Target-specific inputs:
|
|
"--public_headers",
|
|
string_join(",", _rebased_public_headers),
|
|
"--targets_and_headers_from_gn",
|
|
rebase_path(_metadata_path),
|
|
]
|
|
|
|
# Several important compiler flags come from default_compiler_configs
|
|
configs = default_compiler_configs
|
|
if (defined(invoker.configs)) {
|
|
configs += invoker.configs
|
|
}
|
|
args += [
|
|
"--",
|
|
"{{defines}}",
|
|
"{{include_dirs}}",
|
|
"{{cflags}}",
|
|
|
|
# This path contains important C headers (e.g. stddef.h) and {{cflags}}
|
|
# does not include it. Normally this path is implicitly added by clang but
|
|
# it does not happen for libclang.
|
|
#
|
|
# Add it last so includes from deps and configs take precedence.
|
|
"-isystem" + rebase_path(
|
|
clang_base_path + "/lib/clang/" + clang_version + "/include",
|
|
root_build_dir),
|
|
|
|
# Passes C comments through as rustdoc attributes.
|
|
"-fparse-all-comments",
|
|
]
|
|
}
|
|
}
|