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