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_bindgen_path = "${rust_bindgen_root}/bin/bindgen" 15if (host_os == "win") { 16 _bindgen_path = "${_bindgen_path}.exe" 17} 18 19# On Windows, the libclang.dll is beside the bindgen.exe, otherwise it is in 20# ../lib. 21_libclang_path = rust_bindgen_root 22if (host_os == "win") { 23 _libclang_path += "/bin" 24} else { 25 _libclang_path += "/lib" 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 args = [ 83 "--exe", 84 rebase_path(_bindgen_path, root_build_dir), 85 "--header", 86 rebase_path(invoker.header, root_build_dir), 87 "--depfile", 88 rebase_path(depfile, root_build_dir), 89 "--output", 90 rebase_path(out_gen_rs, root_build_dir), 91 "--libclang-path", 92 rebase_path(_libclang_path, root_build_dir), 93 ] 94 95 if (is_linux) { 96 # Linux clang, and clang libs, use a shared libstdc++, which we must 97 # point to. 98 args += [ 99 "--ld-library-path", 100 rebase_path(clang_base_path + "/lib", root_build_dir), 101 ] 102 } 103 104 if (defined(invoker.bindgen_flags)) { 105 args += [ "--bindgen-flags" ] 106 foreach(flag, invoker.bindgen_flags) { 107 args += [ flag ] 108 } 109 } 110 111 args += [ 112 "--", 113 "{{defines}}", 114 "{{include_dirs}}", 115 "{{cflags}}", 116 "{{cflags_c}}", 117 ] 118 119 # libclang will run the system `clang` to find the "resource dir" which it 120 # places before the directory specified in `-isysroot`. 121 # https://github.com/llvm/llvm-project/blob/699e0bed4bfead826e210025bf33e5a1997c018b/clang/lib/Tooling/Tooling.cpp#L499-L510 122 # 123 # This means include files are pulled from the wrong place if the `clang` 124 # says the wrong thing. We point it to our clang's resource dir which will 125 # make it behave consistently with our other command line flags and allows 126 # system headers to be found. 127 clang_resource_dir = 128 rebase_path(clang_base_path + "/lib/clang/" + clang_version, 129 root_build_dir) 130 args += [ 131 "-resource-dir", 132 clang_resource_dir, 133 ] 134 135 if (is_win) { 136 # On Windows we fall back to using system headers from a sysroot from 137 # depot_tools. This is negotiated by python scripts and the result is 138 # available in //build/toolchain/win/win_toolchain_data.gni. From there 139 # we get the `include_flags_imsvc` which point to the system headers. 140 if (host_cpu == "x86") { 141 win_toolchain_data = win_toolchain_data_x86 142 } else if (host_cpu == "x64") { 143 win_toolchain_data = win_toolchain_data_x64 144 } else if (host_cpu == "arm64") { 145 win_toolchain_data = win_toolchain_data_arm64 146 } else { 147 error("Unsupported host_cpu, add it to win_toolchain_data.gni") 148 } 149 args += [ "${win_toolchain_data.include_flags_imsvc}" ] 150 } 151 152 # Passes C comments through as rustdoc attributes. 153 if (is_win) { 154 args += [ "/clang:-fparse-all-comments" ] 155 } else { 156 args += [ "-fparse-all-comments" ] 157 } 158 159 # Default configs include "-fvisibility=hidden", and for some reason this 160 # causes bindgen not to emit function bindings. Override it. 161 if (!is_win) { 162 args += [ "-fvisibility=default" ] 163 } 164 165 if (is_win) { 166 # We pass MSVC style flags to clang on Windows, and libclang needs to be 167 # told explicitly to accept them. 168 args += [ "--driver-mode=cl" ] 169 170 # On Windows, libclang adds arguments that it then fails to understand. 171 # -fno-spell-checking 172 # -fallow-editor-placeholders 173 # These should not cause bindgen to fail. 174 args += [ "-Wno-unknown-argument" ] 175 176 # Replace these two arguments with a version that clang-cl can parse. 177 args += [ 178 "/clang:-fno-spell-checking", 179 "/clang:-fallow-editor-placeholders", 180 ] 181 } 182 183 if (is_cfi) { 184 # LLVM searches for a default CFI ignorelist at (exactly) 185 # $(cwd)/lib/clang/$(llvm_version)/share/cfi_ignorelist.txt 186 # Even if we provide a custom -fsanitize-ignorelist, the absence 187 # of this default file will cause a fatal error. clang finds 188 # it within third_party/llvm-build, but for bindgen our cwd 189 # is the $out_dir. We _could_ create this file at the right 190 # location within the outdir using a "copy" target, but as 191 # we don't actually generate code within bindgen, the easier 192 # option is to tell bindgen to ignore all CFI ignorelists. 193 args += [ "-fno-sanitize-ignorelist" ] 194 } 195 } 196} 197