# 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/rust_static_library.gni") if (is_win) { import("//build/toolchain/win/win_toolchain_data.gni") } _rustc_base_path = rust_sysroot # TODO(danakj): When we're using the Android prebuilt toolchain, there's no # bindgen present. bindgen is for the host platform so using the linux one will # work. if (!use_chromium_rust_toolchain) { _rustc_base_path = "//third_party/rust-toolchain" } _bindgen_path = "${_rustc_base_path}/bin/bindgen" if (is_win) { _bindgen_path = "${_bindgen_path}.exe" } # Template to build Rust/C bindings with bindgen. # # This template expands to a static_library containing the Rust side of the # bindings. Simply treat it as a public dependency. # # Parameters: # # header: # The .h file to generate bindings for. # # deps: (optional) # C targets on which the headers depend in order to build successfully. # # configs: (optional) # C compilation targets determine the correct list of -D and -I flags based # on their dependencies and any configs applied. The same applies here. Set # any configs here as if this were a C target. # # bindgen_flags: (optional) # the additional bindgen flags which are passed to the executable # # Rust targets depending on the output must include! the generated file. # template("rust_bindgen") { assert(defined(invoker.header), "Must specify the C header file to make bindings for.") action(target_name) { # bindgen relies on knowing the {{defines}} and {{include_dirs}} required # to build the C++ headers which it's parsing. These are passed to the # script's args and are populated using deps and configs. forward_variables_from(invoker, TESTONLY_AND_VISIBILITY + [ "deps", "configs", ]) sources = [ invoker.header ] if (!defined(configs)) { configs = [] } # Several important compiler flags come from default_compiler_configs configs += default_compiler_configs output_dir = "$target_gen_dir" out_gen_rs = "$output_dir/${target_name}.rs" script = rebase_path("//build/rust/run_bindgen.py") inputs = [ _bindgen_path ] depfile = "$target_out_dir/${target_name}.d" outputs = [ out_gen_rs ] lib_path = "" if (is_linux) { # Linux clang, and clang libs, use a shared libstdc++, which we must # point to. clang_ld_path = rebase_path(clang_base_path + "/lib", root_build_dir) lib_path += "${clang_ld_path}:" } rust_ld_path = rebase_path(_rustc_base_path + "/lib", root_build_dir) lib_path += "${rust_ld_path}" args = [ "--exe", rebase_path(_bindgen_path), "--header", rebase_path(invoker.header, root_build_dir), "--depfile", rebase_path(depfile, root_build_dir), "--output", rebase_path(out_gen_rs, root_build_dir), "--ld-library-path", lib_path, ] if (defined(invoker.bindgen_flags)) { args += [ "--bindgen-flags" ] foreach(flag, invoker.bindgen_flags) { args += [ flag ] } } args += [ "--", "{{defines}}", "{{include_dirs}}", "{{cflags}}", "{{cflags_c}}", ] # Clang ships with some headers, which are installed along side the binary, # and which clang itself finds by default, but libclang does not (see also # https://reviews.llvm.org/D95396 which would resolve this but was reverted). clang_headers = rebase_path( clang_base_path + "/lib/clang/" + clang_version + "/include", root_build_dir) if (is_win) { args += [ "-imsvc" + clang_headers ] } else { args += [ "-isystem" + clang_headers ] } if (is_win) { # On Windows we fall back to using system headers from a sysroot from # depot_tools. This is negotiated by python scripts and the result is # available in //build/toolchain/win/win_toolchain_data.gni. From there # we get the `include_flags_imsvc` which point to the system headers. if (host_cpu == "x86") { win_toolchain_data = win_toolchain_data_x86 } else if (host_cpu == "x64") { win_toolchain_data = win_toolchain_data_x64 } else if (host_cpu == "arm64") { win_toolchain_data = win_toolchain_data_arm64 } else { error("Unsupported host_cpu, add it to win_toolchain_data.gni") } args += [ "${win_toolchain_data.include_flags_imsvc}" ] } # Passes C comments through as rustdoc attributes. if (is_win) { args += [ "/clang:-fparse-all-comments" ] } else { args += [ "-fparse-all-comments" ] } # Default configs include "-fvisibility=hidden", and for some reason this # causes bindgen not to emit function bindings. Override it. if (!is_win) { args += [ "-fvisibility=default" ] } if (is_win) { # We pass MSVC style flags to clang on Windows, and libclang needs to be # told explicitly to accept them. args += [ "--driver-mode=cl" ] # On Windows, libclang adds arguments that it then fails to understand. # -fno-spell-checking # -fallow-editor-placeholders # These should not cause bindgen to fail. args += [ "-Wno-unknown-argument" ] # Replace these two arguments with a version that clang-cl can parse. args += [ "/clang:-fno-spell-checking", "/clang:-fallow-editor-placeholders", ] } if (!is_cfi) { # LLVM searches for a default CFI ignorelist at (exactly) # $(cwd)/lib/clang/$(llvm_version)/share/cfi_ignorelist.txt # Even if we provide a custom -fsanitize-ignorelist, the absence # of this default file will cause a fatal error. clang finds # it within third_party/llvm-build, but for bindgen our cwd # is the $out_dir. We _could_ create this file at the right # location within the outdir using a "copy" target, but as # we don't actually generate code within bindgen, the easier # option is to tell bindgen to ignore all CFI ignorelists. args += [ "-fno-sanitize-ignorelist" ] } } }