• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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