xref: /aosp_15_r20/external/cronet/third_party/rust/cxx/chromium_integration/rust_cxx.gni (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1# Copyright 2022 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import("//build/config/clang/clang.gni")
6import("//build/config/rust.gni")
7import("//build/config/sysroot.gni")
8
9set_defaults("rust_cxx") {
10  native_header_deps = []
11}
12
13# Template to build Rust/C++ bindings using the tooling
14# described at https://cxx.rs.
15#
16# (Specifically, this builds the C++ side of the bindings.
17# The Rust side of the bindings are not 'built' as such, but
18# automatically expanded by the Rust compiler by virtue of a
19# proc macro which is a simple dependency relationship on the
20# Rust target).
21#
22# This template expands to a source_set containing the
23# C++ side of the bindings. Simply treat it as a dependency.
24#
25# If you're using this, consider whether you should be using
26# rust_static_library.gni or mixed_static_library.gni which have
27# facilities to do the same code generation in an easier
28# way.
29#
30# Parameters:
31#
32# sources:
33#   The .rs files containing a #[cxx::bridge] section.
34#
35# deps (optional)
36#   C++ targets on which the headers depend in order to build
37#   successfully.
38#
39# export_symbols
40#   Whether the C++ side of the bindings should be exported
41#   from any shared objects in which it finds itself.
42#   This is not necessary if the Rust and C++ side of the
43#   bindings will always find themselves within the same binary,
44#   which is usually the case in a non-component build.
45#   Even in a component build, the Rust and C++ code will
46#   often find itself within the same binary and no exporting
47#   is required. However, there may be other binaries - most
48#   commonly Rust unit test executables - which contain the
49#   Rust side of the bindings but not the C++ side. In such
50#   a case, it's important that the C++ side symbols are
51#   exported such that the Rust unit tests can link against
52#   them. This does the equivalent of adding BASE_EXPORT
53#   or similar.
54#   Example:
55#     Non-component build:
56#       group of libfoo.a, foo.rlib # contain
57#          # C++ and Rust side bindings, always both linked
58#          # into final targets
59#       foo_rs_unittests # contains Rust side bindings,
60#          # and any C++ dependencies which will include
61#          # all of the libfoo code.
62#     Component build:
63#       libfoo.so # contains C++ and Rust side bindings
64#       foo_rs_unittests # contains Rust side test code,
65#          # but links against libfoo.so to get the C++
66#          # side bindings. So libfoo.so must make those
67#          # symbols visible.
68template("rust_cxx") {
69  assert(defined(invoker.sources),
70         "Must specify the Rust file to use as input.")
71  _target_name = target_name
72  _testonly = false
73  if (defined(invoker.testonly)) {
74    _testonly = invoker.testonly
75  }
76  if (defined(invoker.visibility)) {
77    _visibility = invoker.visibility
78  }
79
80  action_foreach("${_target_name}_gen") {
81    testonly = _testonly
82    visibility = [ ":${_target_name}" ]
83    if (defined(_visibility)) {
84      visibility += _visibility
85    }
86
87    sources = invoker.sources
88
89    output_h = "{{source_gen_dir}}/{{source_file_part}}.h"
90    output_cc = "{{source_gen_dir}}/{{source_file_part}}.cc"
91
92    # Below we use $rust_macro_toolchain rather than $host_toolchain since we
93    # are building a standalone Rust target that is not part of the Chromium
94    # production build, and this unblocks it from building while the Chromium
95    # stdlib is still compiling, further freeing up other Rust proc-macro
96    # targets (if they used cxxbridge for some reason).
97    cxxbridge_target =
98        "//third_party/rust/cxxbridge_cmd/v1:cxxbridge($rust_macro_toolchain)"
99
100    cxxbridge_out_dir = get_label_info(cxxbridge_target, "root_out_dir")
101    cxxbridge_executable = "${cxxbridge_out_dir}/cxxbridge"
102
103    # The executable is always built with the `rust_macro_toolchain` which
104    # targets the `host_os`. The rule here is on the `target_toolchain` which
105    # can be different (e.g. compiling on Linux, targeting Windows).
106    if (host_os == "win") {
107      cxxbridge_executable = "${cxxbridge_executable}.exe"
108    }
109
110    script = "//third_party/rust/cxx/chromium_integration/run_cxxbridge.py"
111    inputs = [
112      cxxbridge_executable,
113      script,
114    ]
115    outputs = [
116      output_h,
117      output_cc,
118    ]
119    deps = [ cxxbridge_target ]
120
121    args = [
122      "--exe",
123      rebase_path(cxxbridge_executable, root_build_dir),
124      "--header",
125      output_h,
126      "--cc",
127      output_cc,
128      "{{source}}",
129      "--",
130    ]
131
132    if (invoker.export_symbols) {
133      # See explanation of 'export_symbols' above for what this does.
134      # Implementation note: we could have required users of this template to
135      # specify a preprocessor symbol, e.g. BASE_EXPORT, which would vary
136      # per-component. However, since we're building only the definition of the
137      # bindings, not any header files, the export specifications are
138      # predictable and we don't need to foist that complexity on users of this
139      # template. The default behavior here should be correct. If this proves to
140      # be insufficient in future, this template should be modified to accept a
141      # parameter where users can specify 'BASE_EXPORT' or the equivalent for
142      # their component. cxxbridge --cxx-impl-annotations adds this annotation
143      # to each exported C++ function.
144      args += [ "--cxx-impl-annotations" ]
145      if (is_win) {
146        args += [ "__declspec(dllexport)" ]
147      } else {
148        args += [ "__attribute__((visibility(\"default\")))" ]
149      }
150    }
151  }
152
153  static_library(target_name) {
154    forward_variables_from(invoker, [ "deps" ])
155
156    testonly = _testonly
157    if (defined(invoker.visibility)) {
158      visibility = invoker.visibility
159    }
160    sources = get_target_outputs(":${target_name}_gen")
161    if (!defined(deps)) {
162      deps = []
163    }
164    deps += [
165      ":${target_name}_gen",
166      "//build/rust:cxx_cppdeps",
167    ]
168    if (defined(invoker.configs)) {
169      configs = []
170      configs = invoker.configs
171    }
172  }
173}
174