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