chromium/third_party/rust/cxx/chromium_integration/rust_cxx.gni

# 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")

set_defaults("rust_cxx") {
  native_header_deps = []
}

# Template to build Rust/C++ bindings using the tooling
# described at https://cxx.rs.
#
# (Specifically, this builds the C++ side of the bindings.
# The Rust side of the bindings are not 'built' as such, but
# automatically expanded by the Rust compiler by virtue of a
# proc macro which is a simple dependency relationship on the
# Rust target).
#
# This template expands to a source_set containing the
# C++ side of the bindings. Simply treat it as a dependency.
#
# If you're using this, consider whether you should be using
# rust_static_library.gni or mixed_static_library.gni which have
# facilities to do the same code generation in an easier
# way.
#
# Parameters:
#
# sources:
#   The .rs files containing a #[cxx::bridge] section.
#
# deps (optional)
#   C++ targets on which the headers depend in order to build
#   successfully.
#
# export_symbols
#   Whether the C++ side of the bindings should be exported
#   from any shared objects in which it finds itself.
#   This is not necessary if the Rust and C++ side of the
#   bindings will always find themselves within the same binary,
#   which is usually the case in a non-component build.
#   Even in a component build, the Rust and C++ code will
#   often find itself within the same binary and no exporting
#   is required. However, there may be other binaries - most
#   commonly Rust unit test executables - which contain the
#   Rust side of the bindings but not the C++ side. In such
#   a case, it's important that the C++ side symbols are
#   exported such that the Rust unit tests can link against
#   them. This does the equivalent of adding BASE_EXPORT
#   or similar.
#   Example:
#     Non-component build:
#       group of libfoo.a, foo.rlib # contain
#          # C++ and Rust side bindings, always both linked
#          # into final targets
#       foo_rs_unittests # contains Rust side bindings,
#          # and any C++ dependencies which will include
#          # all of the libfoo code.
#     Component build:
#       libfoo.so # contains C++ and Rust side bindings
#       foo_rs_unittests # contains Rust side test code,
#          # but links against libfoo.so to get the C++
#          # side bindings. So libfoo.so must make those
#          # symbols visible.
template("rust_cxx") {
  assert(defined(invoker.sources),
         "Must specify the Rust file to use as input.")
  _target_name = target_name
  _testonly = false
  if (defined(invoker.testonly)) {
    _testonly = invoker.testonly
  }
  if (defined(invoker.visibility)) {
    _visibility = invoker.visibility
  }

  action_foreach("${_target_name}_gen") {
    testonly = _testonly
    visibility = [ ":${_target_name}" ]
    if (defined(_visibility)) {
      visibility += _visibility
    }

    sources = invoker.sources

    output_h = "{{source_gen_dir}}/{{source_file_part}}.h"
    output_cc = "{{source_gen_dir}}/{{source_file_part}}.cc"

    # Below we use $rust_macro_toolchain rather than $host_toolchain since we
    # are building a standalone Rust target that is not part of the Chromium
    # production build, and this unblocks it from building while the Chromium
    # stdlib is still compiling, further freeing up other Rust proc-macro
    # targets (if they used cxxbridge for some reason).
    cxxbridge_target =
        "//third_party/rust/cxxbridge_cmd/v1:cxxbridge($rust_macro_toolchain)"

    cxxbridge_out_dir = get_label_info(cxxbridge_target, "root_out_dir")
    cxxbridge_executable = "${cxxbridge_out_dir}/cxxbridge"

    # The executable is always built with the `rust_macro_toolchain` which
    # targets the `host_os`. The rule here is on the `target_toolchain` which
    # can be different (e.g. compiling on Linux, targeting Windows).
    if (host_os == "win") {
      cxxbridge_executable = "${cxxbridge_executable}.exe"
    }

    script = "//third_party/rust/cxx/chromium_integration/run_cxxbridge.py"
    inputs = [
      cxxbridge_executable,
      script,
    ]
    outputs = [
      output_h,
      output_cc,
    ]
    deps = [ cxxbridge_target ]

    args = [
      "--exe",
      rebase_path(cxxbridge_executable, root_build_dir),
      "--header",
      output_h,
      "--cc",
      output_cc,
      "{{source}}",
      "--",
    ]

    if (invoker.export_symbols) {
      # See explanation of 'export_symbols' above for what this does.
      # Implementation note: we could have required users of this template to
      # specify a preprocessor symbol, e.g. BASE_EXPORT, which would vary
      # per-component. However, since we're building only the definition of the
      # bindings, not any header files, the export specifications are
      # predictable and we don't need to foist that complexity on users of this
      # template. The default behavior here should be correct. If this proves to
      # be insufficient in future, this template should be modified to accept a
      # parameter where users can specify 'BASE_EXPORT' or the equivalent for
      # their component. cxxbridge --cxx-impl-annotations adds this annotation
      # to each exported C++ function.
      args += [ "--cxx-impl-annotations" ]
      if (is_win) {
        args += [ "__declspec(dllexport)" ]
      } else {
        args += [ "__attribute__((visibility(\"default\")))" ]
      }
    }
  }

  static_library(target_name) {
    forward_variables_from(invoker, [ "deps" ])

    testonly = _testonly
    if (defined(invoker.visibility)) {
      visibility = invoker.visibility
    }
    sources = get_target_outputs(":${target_name}_gen")
    if (!defined(deps)) {
      deps = []
    }
    deps += [
      ":${target_name}_gen",
      "//build/rust:cxx_cppdeps",
    ]
    if (defined(invoker.configs)) {
      configs = []
      configs = invoker.configs
    }
  }
}