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") 8import("//build/rust/rust_static_library.gni") 9 10if (is_win) { 11 import("//build/toolchain/win/win_toolchain_data.gni") 12} 13 14_rustc_base_path = rust_sysroot 15 16# TODO(danakj): When we're using the Android prebuilt toolchain, there's no 17# bindgen present. bindgen is for the host platform so using the linux one will 18# work. 19if (!use_chromium_rust_toolchain) { 20 _rustc_base_path = "//third_party/rust-toolchain" 21} 22 23_bindgen_path = "${_rustc_base_path}/bin/bindgen" 24if (is_win) { 25 _bindgen_path = "${_bindgen_path}.exe" 26} 27 28# Template to build Rust/C bindings with bindgen. 29# 30# This template expands to a static_library containing the Rust side of the 31# bindings. Simply treat it as a public dependency. 32# 33# Parameters: 34# 35# header: 36# The .h file to generate bindings for. 37# 38# deps: (optional) 39# C targets on which the headers depend in order to build successfully. 40# 41# configs: (optional) 42# C compilation targets determine the correct list of -D and -I flags based 43# on their dependencies and any configs applied. The same applies here. Set 44# any configs here as if this were a C target. 45# 46# bindgen_flags: (optional) 47# the additional bindgen flags which are passed to the executable 48# 49# Rust targets depending on the output must include! the generated file. 50# 51template("rust_bindgen") { 52 assert(defined(invoker.header), 53 "Must specify the C header file to make bindings for.") 54 action(target_name) { 55 # bindgen relies on knowing the {{defines}} and {{include_dirs}} required 56 # to build the C++ headers which it's parsing. These are passed to the 57 # script's args and are populated using deps and configs. 58 forward_variables_from(invoker, 59 TESTONLY_AND_VISIBILITY + [ 60 "deps", 61 "configs", 62 ]) 63 64 sources = [ invoker.header ] 65 66 if (!defined(configs)) { 67 configs = [] 68 } 69 70 # Several important compiler flags come from default_compiler_configs 71 configs += default_compiler_configs 72 73 output_dir = "$target_gen_dir" 74 out_gen_rs = "$output_dir/${target_name}.rs" 75 76 script = rebase_path("//build/rust/run_bindgen.py") 77 inputs = [ _bindgen_path ] 78 79 depfile = "$target_out_dir/${target_name}.d" 80 outputs = [ out_gen_rs ] 81 82 lib_path = "" 83 if (is_linux) { 84 # Linux clang, and clang libs, use a shared libstdc++, which we must 85 # point to. 86 clang_ld_path = rebase_path(clang_base_path + "/lib", root_build_dir) 87 lib_path += "${clang_ld_path}:" 88 } 89 rust_ld_path = rebase_path(_rustc_base_path + "/lib", root_build_dir) 90 lib_path += "${rust_ld_path}" 91 92 args = [ 93 "--exe", 94 rebase_path(_bindgen_path), 95 "--header", 96 rebase_path(invoker.header, root_build_dir), 97 "--depfile", 98 rebase_path(depfile, root_build_dir), 99 "--output", 100 rebase_path(out_gen_rs, root_build_dir), 101 "--ld-library-path", 102 lib_path, 103 ] 104 105 if (defined(invoker.bindgen_flags)) { 106 args += [ "--bindgen-flags" ] 107 foreach(flag, invoker.bindgen_flags) { 108 args += [ flag ] 109 } 110 } 111 112 args += [ 113 "--", 114 "{{defines}}", 115 "{{include_dirs}}", 116 "{{cflags}}", 117 "{{cflags_c}}", 118 ] 119 120 # Clang ships with some headers, which are installed along side the binary, 121 # and which clang itself finds by default, but libclang does not (see also 122 # https://reviews.llvm.org/D95396 which would resolve this but was reverted). 123 clang_headers = rebase_path( 124 clang_base_path + "/lib/clang/" + clang_version + "/include", 125 root_build_dir) 126 if (is_win) { 127 args += [ "-imsvc" + clang_headers ] 128 } else { 129 args += [ "-isystem" + clang_headers ] 130 } 131 132 if (is_win) { 133 # On Windows we fall back to using system headers from a sysroot from 134 # depot_tools. This is negotiated by python scripts and the result is 135 # available in //build/toolchain/win/win_toolchain_data.gni. From there 136 # we get the `include_flags_imsvc` which point to the system headers. 137 if (host_cpu == "x86") { 138 win_toolchain_data = win_toolchain_data_x86 139 } else if (host_cpu == "x64") { 140 win_toolchain_data = win_toolchain_data_x64 141 } else if (host_cpu == "arm64") { 142 win_toolchain_data = win_toolchain_data_arm64 143 } else { 144 error("Unsupported host_cpu, add it to win_toolchain_data.gni") 145 } 146 args += [ "${win_toolchain_data.include_flags_imsvc}" ] 147 } 148 149 # Passes C comments through as rustdoc attributes. 150 if (is_win) { 151 args += [ "/clang:-fparse-all-comments" ] 152 } else { 153 args += [ "-fparse-all-comments" ] 154 } 155 156 # Default configs include "-fvisibility=hidden", and for some reason this 157 # causes bindgen not to emit function bindings. Override it. 158 if (!is_win) { 159 args += [ "-fvisibility=default" ] 160 } 161 162 if (is_win) { 163 # We pass MSVC style flags to clang on Windows, and libclang needs to be 164 # told explicitly to accept them. 165 args += [ "--driver-mode=cl" ] 166 167 # On Windows, libclang adds arguments that it then fails to understand. 168 # -fno-spell-checking 169 # -fallow-editor-placeholders 170 # These should not cause bindgen to fail. 171 args += [ "-Wno-unknown-argument" ] 172 173 # Replace these two arguments with a version that clang-cl can parse. 174 args += [ 175 "/clang:-fno-spell-checking", 176 "/clang:-fallow-editor-placeholders", 177 ] 178 } 179 180 if (!is_cfi) { 181 # LLVM searches for a default CFI ignorelist at (exactly) 182 # $(cwd)/lib/clang/$(llvm_version)/share/cfi_ignorelist.txt 183 # Even if we provide a custom -fsanitize-ignorelist, the absence 184 # of this default file will cause a fatal error. clang finds 185 # it within third_party/llvm-build, but for bindgen our cwd 186 # is the $out_dir. We _could_ create this file at the right 187 # location within the outdir using a "copy" target, but as 188 # we don't actually generate code within bindgen, the easier 189 # option is to tell bindgen to ignore all CFI ignorelists. 190 args += [ "-fno-sanitize-ignorelist" ] 191 } 192 } 193} 194