tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

rust_bindgen_generator.gni (12142B)


      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 
      5 import("//chromium/build/config/clang/clang.gni")
      6 import("//chromium/build/config/rust.gni")
      7 import("//chromium/build/config/sysroot.gni")
      8 import("//chromium/build/rust/rust_static_library.gni")
      9 
     10 if (is_win) {
     11   import("//chromium/build/toolchain/win/win_toolchain_data.gni")
     12 }
     13 
     14 _bindgen_path = "${rust_bindgen_root}/bin/bindgen"
     15 if (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
     22 if (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 # If you're developing first-party code then consider using `rust_bindgen`
     31 # instead of `rust_bindgen_generator` to simplify your build targets.
     32 #
     33 # This template expands to an action that generates the Rust side of the
     34 # bindings. Add it as a dependency through `bindgen_deps` on your
     35 # rust_target then incorporate it into your library through
     36 #
     37 # mod bindings {
     38 #    include!(concat!(env!("OUT_DIR"), "/{rust_bindgen_generator_target_name}.rs"));
     39 # }
     40 #
     41 # Parameters:
     42 #
     43 # header:
     44 #   The .h file to generate bindings for.
     45 #
     46 # deps: (optional)
     47 #   C targets on which the headers depend in order to build successfully.
     48 #
     49 # configs: (optional)
     50 #   C compilation targets determine the correct list of -D and -I flags based
     51 #   on their dependencies and any configs applied. The same applies here. Set
     52 #   any configs here as if this were a C target.
     53 #
     54 # cpp: (optional)
     55 #   Use C++ mode to consume the header instead of C mode (the default).
     56 #
     57 # bindgen_flags: (optional)
     58 #   The additional bindgen flags which are passed to the executable. A `--` will
     59 #   be prepended to each flag. So use `bindgen_flags = [ "foo" ]` to pass
     60 #   `--foo` to bindgen.
     61 #
     62 # wrap_static_fns: (optional)
     63 #   If set to true, enables binding `static` and `static inline` functions in
     64 #   the header. Setting this causes the template to emit a source_set target
     65 #   named "${target_name}_static_fns", which must be incorporated into the
     66 #   build. Additionally, `get_target_outputs` will return both the Rust file and
     67 #   a generated C file, but callers can rely on the Rust file being first.
     68 template("rust_bindgen_generator") {
     69   assert(defined(invoker.header),
     70          "Must specify the C/C++ header file to make bindings for.")
     71   _wrap_static_fns = defined(invoker.wrap_static_fns) && invoker.wrap_static_fns
     72 
     73   if (!defined(invoker.cpp)) {
     74     _cpp = false
     75   } else {
     76     _cpp = invoker.cpp
     77   }
     78 
     79   # This is only created if the bindgen has wrap_static_fns to true
     80   _static_fn_target_name = target_name + "_static_fns"
     81   if (defined(invoker.library_name)) {
     82     _library_name = invoker.library_name
     83   }
     84 
     85   _bindgen_target_name = target_name
     86   action(target_name) {
     87     # bindgen relies on knowing the {{defines}} and {{include_dirs}} required
     88     # to build the C++ headers which it's parsing. These are passed to the
     89     # script's args and are populated using deps and configs.
     90     forward_variables_from(invoker,
     91                            TESTONLY_AND_VISIBILITY + [
     92                                  "configs",
     93                                  "deps",
     94                                  "public_configs",
     95                                  "public_deps",
     96                                  "output_name",
     97                                ])
     98     sources = [ invoker.header ]
     99     if (!defined(configs)) {
    100       configs = []
    101     }
    102 
    103     # Get rid of the visibility, the user should not control visibility.
    104     # We have to do it that way otherwise we might get "visibility not used"
    105     # errors.
    106     visibility = []
    107 
    108     # This should only be allowed to be used by third_party code.
    109     # First-party code should use `rust_bindgen` template.
    110     # We're intentionally not allowing visibility to be forwarded
    111     # to prevent people from doing visibility = ["*"] which will
    112     # allow a rust_bindgen_generator to be used by 1P folks.
    113     visibility = [ "//third_party/*" ]
    114     if (defined(_library_name)) {
    115       # Allow the copy target to be able to depend on the generated file.
    116       visibility += [ ":${_library_name}_${_bindgen_target_name}_copy" ]
    117     }
    118 
    119     if (_wrap_static_fns) {
    120       visibility += [ ":$_static_fn_target_name" ]
    121     }
    122 
    123     # Several important compiler flags come from default_compiler_configs
    124     configs += default_compiler_configs
    125 
    126     output_dir = "${target_gen_dir}/${target_name}"
    127     if (!defined(output_name)) {
    128       output_name = target_name
    129     }
    130 
    131     output_file = "$output_dir/${output_name}.rs"
    132 
    133     script = rebase_path("//chromium/build/rust/run_bindgen.py")
    134     inputs = [
    135       _bindgen_path,
    136       "//chromium/build/action_helpers.py",
    137       "//chromium/build/gn_helpers.py",
    138       "//chromium/build/rust/filter_clang_args.py",
    139     ]
    140 
    141     depfile = "$target_out_dir/${target_name}.d"
    142     outputs = [ output_file ]
    143 
    144     args = [
    145       "--exe",
    146       rebase_path(_bindgen_path, root_build_dir),
    147       "--header",
    148       rebase_path(invoker.header, root_build_dir),
    149       "--depfile",
    150       rebase_path(depfile, root_build_dir),
    151       "--output",
    152       rebase_path(output_file, root_build_dir),
    153       "--libclang-path",
    154       rebase_path(_libclang_path, root_build_dir),
    155     ]
    156 
    157     if (_wrap_static_fns) {
    158       if (_cpp) {
    159         out_gen_c = "$output_dir/${target_name}.cc"
    160       } else {
    161         out_gen_c = "$output_dir/${target_name}.c"
    162       }
    163       outputs += [ out_gen_c ]
    164       args += [
    165         "--wrap-static-fns",
    166         rebase_path(out_gen_c, root_build_dir),
    167       ]
    168     }
    169 
    170     if (is_linux) {
    171       # Linux clang, and clang libs, use a shared libstdc++, which we must
    172       # point to.
    173       args += [
    174         "--ld-library-path",
    175         rebase_path(clang_base_path + "/lib", root_build_dir),
    176       ]
    177     }
    178 
    179     args += [ "--bindgen-flags" ]
    180     if (defined(invoker.bindgen_flags)) {
    181       foreach(flag, invoker.bindgen_flags) {
    182         args += [ flag ]
    183       }
    184     }
    185     if (_cpp) {
    186       args += [ "enable-cxx-namespaces" ]
    187     }
    188 
    189     # Everything below is passed through to libclang.
    190     args += [ "--" ]
    191 
    192     if (_cpp) {
    193       args += [
    194         "-x",
    195         "c++",
    196       ]
    197     }
    198 
    199     args += [
    200       "{{defines}}",
    201       "{{include_dirs}}",
    202       "{{cflags}}",
    203     ]
    204     if (_cpp) {
    205       args += [ "{{cflags_cc}}" ]
    206     } else {
    207       args += [ "{{cflags_c}}" ]
    208     }
    209 
    210     # libclang will run the system `clang` to find the "resource dir" which it
    211     # places before the directory specified in `-isysroot`.
    212     # https://github.com/llvm/llvm-project/blob/699e0bed4bfead826e210025bf33e5a1997c018b/clang/lib/Tooling/Tooling.cpp#L499-L510
    213     #
    214     # This means include files are pulled from the wrong place if the `clang`
    215     # says the wrong thing. We point it to our clang's resource dir which will
    216     # make it behave consistently with our other command line flags and allows
    217     # system headers to be found.
    218     clang_resource_dir =
    219         rebase_path(clang_base_path + "/lib/clang/" + clang_version,
    220                     root_build_dir)
    221     args += [
    222       "-resource-dir",
    223       clang_resource_dir,
    224     ]
    225 
    226     # The `--sysroot` flag is not working as expected and gets ignored (we don't
    227     # fully understand why, see b/328510249). But we add `-isystem` to point at
    228     # the headers in the sysroot which are otherwise not found.
    229     if (sysroot != "") {
    230       if (is_win) {
    231         args +=
    232             [ "-I" + rebase_path(sysroot + "/usr/include/", root_build_dir) ]
    233       } else {
    234         args += [
    235           "-isystem",
    236           rebase_path(sysroot + "/usr/include/", root_build_dir),
    237         ]
    238       }
    239     }
    240 
    241     if (is_win) {
    242       # On Windows we fall back to using system headers from a sysroot from
    243       # depot_tools. This is negotiated by python scripts and the result is
    244       # available in //build/toolchain/win/win_toolchain_data.gni. From there
    245       # we get the `include_flags_imsvc` which point to the system headers.
    246       if (host_cpu == "x86") {
    247         win_toolchain_data = win_toolchain_data_x86
    248       } else if (host_cpu == "x64") {
    249         win_toolchain_data = win_toolchain_data_x64
    250       } else if (host_cpu == "arm64") {
    251         win_toolchain_data = win_toolchain_data_arm64
    252       } else {
    253         error("Unsupported host_cpu, add it to win_toolchain_data.gni")
    254       }
    255       args += win_toolchain_data.include_flags_imsvc_list
    256     }
    257 
    258     # Passes C comments through as rustdoc attributes.
    259     if (is_win) {
    260       args += [ "/clang:-fparse-all-comments" ]
    261     } else {
    262       args += [ "-fparse-all-comments" ]
    263     }
    264 
    265     # Default configs include "-fvisibility=hidden", and for some reason this
    266     # causes bindgen not to emit function bindings. Override it.
    267     if (!is_win) {
    268       args += [ "-fvisibility=default" ]
    269     }
    270 
    271     if (is_win) {
    272       # We pass MSVC style flags to clang on Windows, and libclang needs to be
    273       # told explicitly to accept them.
    274       args += [ "--driver-mode=cl" ]
    275 
    276       # On Windows, libclang adds arguments that it then fails to understand.
    277       # -fno-spell-checking
    278       # -fallow-editor-placeholders
    279       # These should not cause bindgen to fail.
    280       args += [ "-Wno-unknown-argument" ]
    281 
    282       # C++ mode makes bindgen pass /TP to libclang (to parse as C++) which
    283       # then libclang says is unused.
    284       args += [ "-Wno-unused-command-line-argument" ]
    285 
    286       # Replace these two arguments with a version that clang-cl can parse.
    287       args += [
    288         "/clang:-fno-spell-checking",
    289         "/clang:-fallow-editor-placeholders",
    290       ]
    291     }
    292 
    293     if (is_cfi) {
    294       # LLVM searches for a default CFI ignorelist at (exactly)
    295       # $(cwd)/lib/clang/$(llvm_version)/share/cfi_ignorelist.txt
    296       # Even if we provide a custom -fsanitize-ignorelist, the absence
    297       # of this default file will cause a fatal error. clang finds
    298       # it within third_party/llvm-build, but for bindgen our cwd
    299       # is the $out_dir. We _could_ create this file at the right
    300       # location within the outdir using a "copy" target, but as
    301       # we don't actually generate code within bindgen, the easier
    302       # option is to tell bindgen to ignore all CFI ignorelists.
    303       args += [ "-fno-sanitize-ignorelist" ]
    304     }
    305     if (!defined(_library_name)) {
    306       not_needed([ _bindgen_target_name ])
    307     }
    308   }
    309 
    310   if (_wrap_static_fns) {
    311     source_set(_static_fn_target_name) {
    312       forward_variables_from(invoker,
    313                              TESTONLY_AND_VISIBILITY + [
    314                                    "deps",
    315                                    "configs",
    316                                    "public_configs",
    317                                    "public_deps",
    318                                  ])
    319       bindgen_output = get_target_outputs(":${_bindgen_target_name}")
    320       if (!defined(deps)) {
    321         deps = []
    322       }
    323       deps += [ ":${_bindgen_target_name}" ]
    324 
    325       if (_cpp) {
    326         sources = filter_include(bindgen_output, [ "*.cc" ])
    327       } else {
    328         sources = filter_include(bindgen_output, [ "*.c" ])
    329       }
    330 
    331       # bindgen generates a C file whose include is relative to the directory it
    332       # runs from.
    333       include_dirs = [ root_build_dir ]
    334 
    335       # Get rid of the visibility, the user should not control visibility.
    336       # We have to do it that way otherwise we might get "visibility not used"
    337       # errors.
    338       visibility = []
    339       if (defined(_library_name)) {
    340         # Allow both the rust target declared through `rust_bindgen` to depend
    341         # on this library.
    342         visibility = [ ":${_library_name}" ]
    343       } else {
    344         # This should only be allowed to be used by third_party code.
    345         # First-party code should use `rust_bindgen` template.
    346         # We're intentionally not allowing visibility to be forwarded
    347         # to prevent people from doing visibility = ["*"] which will
    348         # allow a rust_bindgen_generator to be used by 1P folks.
    349         visibility = [ "//third_party/*" ]
    350       }
    351     }
    352   } else {
    353     not_needed([ _static_fn_target_name ])
    354   }
    355 }