v8 147.3.0

Rust bindings to V8
Documentation
# 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")
}

_bindgen_path = "${rust_bindgen_root}/bin/bindgen"
if (host_os == "win") {
  _bindgen_path = "${_bindgen_path}.exe"
}

_rustfmt_path = "${rust_bindgen_root}/bin/rustfmt"
if (!use_chromium_rust_toolchain &&
    (host_cpu == "ppc64" || host_cpu == "s390x")) {
  _rustfmt_path = rust_sysroot_absolute + "/bin/rustfmt"
} else if (host_os == "win") {
  _rustfmt_path = "${_rustfmt_path}.exe"
}

# On Windows, the libclang.dll is beside the bindgen.exe, otherwise it is in
# ../lib.
_libclang_path = rust_bindgen_root
if (!use_chromium_rust_toolchain &&
    (host_cpu == "ppc64" || host_cpu == "s390x")) {
  _libclang_path = rust_sysroot_absolute + "/lib64"
} else if (host_os == "win") {
  _libclang_path += "/bin"
} else {
  _libclang_path += "/lib"
}

# Template to build Rust/C bindings with bindgen.
#
# If you're developing first-party code then consider using `rust_bindgen`
# instead of `rust_bindgen_generator` to simplify your build targets.
#
# This template expands to an action that generates the Rust side of the
# bindings. Add it as a dependency through `bindgen_deps` on your
# rust_target then incorporate it into your library through
#
# mod bindings {
#    include!(concat!(env!("OUT_DIR"), "/{rust_bindgen_generator_target_name}.rs"));
# }
#
# 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.
#
# cpp: (optional)
#   Use C++ mode to consume the header instead of C mode (the default).
#
# bindgen_flags: (optional)
#   The additional bindgen flags which are passed to the executable. A `--` will
#   be prepended to each flag. So use `bindgen_flags = [ "foo" ]` to pass
#   `--foo` to bindgen.
#
# wrap_static_fns: (optional)
#   If set to true, enables binding `static` and `static inline` functions in
#   the header. Setting this causes the template to emit a source_set target
#   named "${target_name}_static_fns", which must be incorporated into the
#   build. Additionally, `get_target_outputs` will return both the Rust file and
#   a generated C file, but callers can rely on the Rust file being first.
template("rust_bindgen_generator") {
  assert(defined(invoker.header),
         "Must specify the C/C++ header file to make bindings for.")
  _wrap_static_fns = defined(invoker.wrap_static_fns) && invoker.wrap_static_fns

  if (!defined(invoker.cpp)) {
    _cpp = false
  } else {
    _cpp = invoker.cpp
  }

  # This is only created if the bindgen has wrap_static_fns to true
  _static_fn_target_name = target_name + "_static_fns"
  if (defined(invoker.library_name)) {
    _library_name = invoker.library_name
  }

  _bindgen_target_name = target_name
  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 + [
                                 "configs",
                                 "deps",
                                 "public_configs",
                                 "public_deps",
                                 "output_name",
                               ])
    sources = [ invoker.header ]
    if (!defined(configs)) {
      configs = []
    }

    # Get rid of the visibility, the user should not control visibility.
    # We have to do it that way otherwise we might get "visibility not used"
    # errors.
    visibility = []

    # This should only be allowed to be used by third_party code.
    # First-party code should use `rust_bindgen` template.
    # We're intentionally not allowing visibility to be forwarded
    # to prevent people from doing visibility = ["*"] which will
    # allow a rust_bindgen_generator to be used by 1P folks.
    visibility = [ "//third_party/*" ]
    if (defined(_library_name)) {
      # Allow the copy target to be able to depend on the generated file.
      visibility += [ ":${_library_name}" ]
    }

    if (_wrap_static_fns) {
      visibility += [ ":$_static_fn_target_name" ]
    }

    # Several important compiler flags come from default_compiler_configs
    configs += default_compiler_configs

    # Bindgen uses the pinned version of clang, and these warnings only apply
    # to the very newest version.
    configs -= [ "//build/config/compiler:tot_warnings" ]

    output_dir = "${target_gen_dir}/${target_name}"
    if (!defined(output_name)) {
      output_name = target_name
    }

    output_file = "$output_dir/${output_name}.rs"

    script = rebase_path("//build/rust/gni_impl/run_bindgen.py")
    inputs = [
      _bindgen_path,
      _rustfmt_path,
      "//build/action_helpers.py",
      "//build/gn_helpers.py",
      "//build/rust/gni_impl/filter_clang_args.py",
    ]

    depfile = "$target_out_dir/${target_name}.d"
    outputs = [ output_file ]

    args = [
      "--bindgen-exe",
      rebase_path(_bindgen_path, root_build_dir),
      "--rustfmt-exe",
      rebase_path(_rustfmt_path, root_build_dir),
      "--header",
      rebase_path(invoker.header, root_build_dir),
      "--depfile",
      rebase_path(depfile, root_build_dir),
      "--output",
      rebase_path(output_file, root_build_dir),
      "--libclang-path",
      rebase_path(_libclang_path, root_build_dir),
    ]

    if (_wrap_static_fns) {
      if (_cpp) {
        out_gen_c = "$output_dir/${target_name}.cc"
      } else {
        out_gen_c = "$output_dir/${target_name}.c"
      }
      outputs += [ out_gen_c ]
      args += [
        "--wrap-static-fns",
        rebase_path(out_gen_c, root_build_dir),
      ]
    }

    if (is_linux) {
      # Linux clang, and clang libs, use a shared libstdc++, which we must
      # point to.
      args += [
        "--ld-library-path",
        rebase_path(clang_base_path + "/lib", root_build_dir),
      ]
    }

    args += [ "--bindgen-flags" ]
    if (defined(invoker.bindgen_flags)) {
      foreach(flag, invoker.bindgen_flags) {
        args += [ flag ]
      }
    }
    if (_cpp) {
      args += [ "enable-cxx-namespaces" ]
    }

    # Everything below is passed through to libclang.
    args += [ "--" ]

    if (_cpp) {
      args += [
        "-x",
        "c++",
      ]
    }

    args += [
      "{{defines}}",
      "{{include_dirs}}",
      "{{cflags}}",
    ]
    if (_cpp) {
      args += [ "{{cflags_cc}}" ]
    } else {
      args += [ "{{cflags_c}}" ]
    }

    # libclang will run the system `clang` to find the "resource dir" which it
    # places before the directory specified in `-isysroot`.
    # https://github.com/llvm/llvm-project/blob/699e0bed4bfead826e210025bf33e5a1997c018b/clang/lib/Tooling/Tooling.cpp#L499-L510
    #
    # This means include files are pulled from the wrong place if the `clang`
    # says the wrong thing. We point it to our clang's resource dir which will
    # make it behave consistently with our other command line flags and allows
    # system headers to be found.
    clang_resource_dir =
        rebase_path(clang_base_path + "/lib/clang/" + clang_version,
                    root_build_dir)
    args += [
      "-resource-dir",
      clang_resource_dir,
    ]

    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_list
    }

    # 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" ]

      # C++ mode makes bindgen pass /TP to libclang (to parse as C++) which
      # then libclang says is unused.
      args += [ "-Wno-unused-command-line-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" ]
    }
    if (!defined(_library_name) || !_wrap_static_fns) {
      not_needed([ _bindgen_target_name ])
    }
  }

  if (_wrap_static_fns) {
    source_set(_static_fn_target_name) {
      forward_variables_from(invoker,
                             TESTONLY_AND_VISIBILITY + [
                                   "deps",
                                   "configs",
                                   "public_configs",
                                   "public_deps",
                                 ])
      bindgen_output = get_target_outputs(":${_bindgen_target_name}")
      if (!defined(deps)) {
        deps = []
      }
      deps += [ ":${_bindgen_target_name}" ]

      if (_cpp) {
        sources = filter_include(bindgen_output, [ "*.cc" ])
      } else {
        sources = filter_include(bindgen_output, [ "*.c" ])
      }

      # bindgen generates a C file whose include is relative to the directory it
      # runs from.
      include_dirs = [ root_build_dir ]

      # Get rid of the visibility, the user should not control visibility.
      # We have to do it that way otherwise we might get "visibility not used"
      # errors.
      visibility = []
      if (defined(_library_name)) {
        # Allow both the rust target declared through `rust_bindgen` to depend
        # on this library.
        visibility = [ ":${_library_name}" ]
      } else {
        # This should only be allowed to be used by third_party code.
        # First-party code should use `rust_bindgen` template.
        # We're intentionally not allowing visibility to be forwarded
        # to prevent people from doing visibility = ["*"] which will
        # allow a rust_bindgen_generator to be used by 1P folks.
        visibility = [ "//third_party/*" ]
      }
    }
  } else {
    not_needed([ _static_fn_target_name ])
  }
}