| # Copyright 2026 The Fuchsia Authors. All rights reserved. |
| # 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_prefix.gni") |
| import("//build/config/clang/clang_target_toolchain_info.gni") |
| import("//build/config/current_target_tuple.gni") |
| import("//build/config/fuchsia_cxx_version.gni") |
| import("//build/config/sysroot.gni") |
| import("//build/rust/config.gni") |
| import("//build/testing/config.gni") |
| import("//build/testing/golden_files.gni") |
| |
| # Might as well always default to using the bleeding edge, just as we do with |
| # our toolchain. |
| default_bindgen_rust_version = "nightly" |
| |
| # Defines a target that runs rust bindgen on C header files. |
| # |
| # Parameters |
| # |
| # * headers |
| # - Required: List of paths to the input C header files. |
| # - Type: list(path) |
| # |
| # * output_name |
| # - Optional: Name of the output Rust file (relative to output_path). |
| # - Type: string |
| # - Default: "${target_name}.rs" |
| # |
| # * deps |
| # - Optional: List of targets that generate `headers` or other dependencies. |
| # - Type: list(label) |
| # |
| # * cpp |
| # - Optional: Whether to compile the headers as C++. |
| # - Type: bool |
| # - Default: false |
| # |
| # * rust_version |
| # - Optional: The Rust compiler version to target. |
| # - Type: string |
| # - Default: default_bindgen_rust_version |
| # |
| # * rust_edition |
| # - Optional: The Rust edition to target. |
| # - Type: string ("2018" | "2021" | "2024") |
| # - Default: Maximum supported edition of the targeted Rust version. |
| # |
| # * expect_lints |
| # - Optional: List of lints to expect in the generated code (generates `#![expect(lint)]`). |
| # - Type: list(string) |
| # |
| # * notice_year |
| # - Optional: The year to use in the copyright notice header. |
| # - Type: string |
| # |
| # * generator_label |
| # - Optional: The label of the target that generated this target. |
| # - Type: label |
| # - Default: get_label_info(":$target_name", "label_no_toolchain") |
| # |
| # * raw_lines |
| # - Optional: List of raw lines of code to prepend to the generated file. |
| # - Type: list(string) |
| # |
| # * explicit_padding |
| # - Optional: Whether to emit explicit padding fields. |
| # - Type: bool |
| # |
| # * layout_tests |
| # - Optional: Whether to generate layout tests. |
| # - Type: bool |
| # |
| # * include_dirs |
| # - Optional: List of include directories to pass to clang. |
| # - Type: list(path) |
| # |
| # * std_impls |
| # - Optional: List of non-derivable std traits to implement. |
| # - Type: list(string) |
| # |
| # * std_derives |
| # - Optional: List of std traits to derive. |
| # - Type: list(string) |
| # |
| # * auto_derive_traits |
| # - Optional: Rules for automatically deriving traits. Each element is a |
| # list of two strings: a regex used to match the type name, and a |
| # comma-separated list of traits to derive. |
| # - Type: list(list) |
| # |
| # * replacements |
| # - Optional: Rules for replacing text. Each element is a list of two |
| # strings: a regex used to match the original text, and the text to |
| # replace it with. The replacement text can use backreferences for |
| # captured groups. |
| # - Type: list(list(string)) |
| # |
| # * function_allowlist |
| # - Optional: A list of regexes. Free-standing functions which do not |
| # match one of these regexes will not be generated. |
| # - Type: list(string) |
| # |
| # * var_allowlist |
| # - Optional: A list of regexes. Free-standing variables which do not |
| # match any of these regexes will not be generated. |
| # - Type: list(string) |
| # |
| # * type_allowlist |
| # - Optional: A list of regexes. Types which do not match any of |
| # these regexes will not be generated. |
| # - Type: list(string) |
| # |
| # * function_blocklist |
| # - Optional: A list of regexes. Free-standing functions which match any |
| # of these regexes will not be generated. |
| # - Type: list(string) |
| # |
| # * type_blocklist |
| # - Optional: A list of regexes. Types which match any of these regexes |
| # will not be generated. |
| # - Type: list(string) |
| # |
| # * var_blocklist |
| # - Optional: A list of regexes. Free-standing variables and static members |
| # which match any of these regexes will not be generated. |
| # - Type: list(string) |
| # |
| # * opaque_types |
| # - Optional: A list of regexes. Types which match any of these regexes |
| # will be marked as opaque layout blobs. |
| # - Type: list(string) |
| # |
| # * no_debug_types |
| # - Optional: A list of regexes. Types which match any of these regexes |
| # will not derive `Debug`. |
| # - Type: list(string) |
| # |
| # * rustified_enums |
| # - Optional: A list of regexes. Enums which match any of these regexes |
| # will be generated as Rust enums instead of constants. |
| # - Type: list(string) |
| # |
| # * use_core |
| # - Optional: Whether to use `core` instead of `std`. |
| # - Type: bool |
| # - Default: true |
| # |
| # * additional_bindgen_flags |
| # - Optional: List of additional flags to pass to bindgen. |
| # - Type: list(string) |
| # |
| # * fuchsia_api_level |
| # - Optional: API level to pass to clang ("NEXT", "HEAD", "PLATFORM", or an integer string). |
| # - Type: string |
| # |
| # * additional_clang_flags |
| # - Optional: List of additional flags to pass to clang. |
| # - Type: list(string) |
| # |
| # * testonly, visibility |
| # - Optional: Usual GN meanings. |
| # |
| template("rustc_bindgen") { |
| assert(defined(invoker.headers), "Must specify headers") |
| |
| if (defined(invoker.output_name)) { |
| _output_name = invoker.output_name |
| } else { |
| _output_name = "${target_name}.rs" |
| } |
| |
| _output_path = "$target_gen_dir/${_output_name}" |
| _bindgen_bin = "//prebuilt/third_party/rust_bindgen/$host_platform/bindgen" |
| _rustfmt_bin = "//prebuilt/third_party/rust/$host_platform/bin/rustfmt" |
| _rustfmt_config = "//rustfmt.toml" |
| |
| action(target_name) { |
| forward_variables_from(invoker, |
| [ |
| "testonly", |
| "visibility", |
| ]) |
| |
| script = "//build/rust/new-bindgen.py" |
| |
| deps = [] |
| if (defined(invoker.deps)) { |
| deps += invoker.deps |
| } |
| |
| inputs = invoker.headers + [ |
| _bindgen_bin, |
| _rustfmt_bin, |
| _rustfmt_config, |
| ] |
| |
| outputs = [ _output_path ] |
| |
| _depfile = "${_output_path}.d" |
| depfile = _depfile |
| |
| args = [] |
| foreach(header, invoker.headers) { |
| args += [ |
| "--input", |
| rebase_path(header, root_build_dir), |
| ] |
| } |
| args += [ |
| "--output", |
| rebase_path(_output_path, root_build_dir), |
| "--depfile", |
| rebase_path(_depfile, root_build_dir), |
| "--bindgen", |
| rebase_path(_bindgen_bin, root_build_dir), |
| "--rustfmt", |
| rebase_path(_rustfmt_bin, root_build_dir), |
| "--clang-target", |
| current_target_tuple, |
| ] |
| |
| rust_version = default_bindgen_rust_version |
| if (defined(invoker.rust_version)) { |
| rust_version = invoker.rust_version |
| } |
| args += [ "--rust-version=${rust_version}" ] |
| |
| if (defined(invoker.rust_edition)) { |
| args += [ "--rust-edition=" + invoker.rust_edition ] |
| } |
| |
| if (defined(invoker.expect_lints)) { |
| foreach(x, invoker.expect_lints) { |
| args += [ "--expect-lint=${x}" ] |
| } |
| } |
| if (defined(invoker.notice_year)) { |
| args += [ |
| "--notice-year", |
| "${invoker.notice_year}", |
| ] |
| } |
| |
| _generator_label = "" |
| if (defined(invoker.generator_label)) { |
| _generator_label = invoker.generator_label |
| } else { |
| _generator_label = get_label_info(":$target_name", "label_no_toolchain") |
| } |
| args += [ |
| "--generator-label", |
| _generator_label, |
| ] |
| |
| if (defined(invoker.raw_lines)) { |
| foreach(x, invoker.raw_lines) { |
| args += [ "--raw-line=${x}" ] |
| } |
| } |
| if (defined(invoker.explicit_padding)) { |
| if (invoker.explicit_padding) { |
| args += [ "--explicit-padding" ] |
| } else { |
| args += [ "--no-explicit-padding" ] |
| } |
| } |
| if (defined(invoker.layout_tests)) { |
| if (invoker.layout_tests) { |
| args += [ "--layout-tests" ] |
| } else { |
| args += [ "--no-layout-tests" ] |
| } |
| } |
| if (defined(invoker.include_dirs)) { |
| foreach(x, invoker.include_dirs) { |
| args += [ "--include-dir=" + rebase_path(x, root_build_dir) ] |
| } |
| } |
| if (defined(invoker.std_impls)) { |
| foreach(x, invoker.std_impls) { |
| args += [ "--std-impl=${x}" ] |
| } |
| } |
| if (defined(invoker.std_derives)) { |
| foreach(x, invoker.std_derives) { |
| args += [ "--std-derive=${x}" ] |
| } |
| } |
| if (defined(invoker.auto_derive_traits)) { |
| foreach(x, invoker.auto_derive_traits) { |
| _traits = "" |
| foreach(t, x[1]) { |
| if (_traits != "") { |
| _traits += "," |
| } |
| _traits += t |
| } |
| args += [ |
| "--auto-derive-trait", |
| "${x[0]}=${_traits}", |
| ] |
| } |
| } |
| if (defined(invoker.replacements)) { |
| foreach(x, invoker.replacements) { |
| if (x[1] == "") { |
| args += [ |
| "--replacement", |
| x[0], |
| ] |
| } else { |
| args += [ |
| "--replacement", |
| x[0], |
| x[1], |
| ] |
| } |
| } |
| } |
| if (defined(invoker.function_allowlist)) { |
| foreach(x, invoker.function_allowlist) { |
| args += [ "--allowlist-function=${x}" ] |
| } |
| } |
| if (defined(invoker.var_allowlist)) { |
| foreach(x, invoker.var_allowlist) { |
| args += [ "--allowlist-var=${x}" ] |
| } |
| } |
| if (defined(invoker.type_allowlist)) { |
| foreach(x, invoker.type_allowlist) { |
| args += [ "--allowlist-type=${x}" ] |
| } |
| } |
| if (defined(invoker.function_blocklist)) { |
| foreach(x, invoker.function_blocklist) { |
| args += [ "--blocklist-function=${x}" ] |
| } |
| } |
| if (defined(invoker.type_blocklist)) { |
| foreach(x, invoker.type_blocklist) { |
| args += [ "--blocklist-type=${x}" ] |
| } |
| } |
| if (defined(invoker.var_blocklist)) { |
| foreach(x, invoker.var_blocklist) { |
| args += [ "--blocklist-var=${x}" ] |
| } |
| } |
| if (defined(invoker.opaque_types)) { |
| foreach(x, invoker.opaque_types) { |
| args += [ "--opaque-type=${x}" ] |
| } |
| } |
| if (defined(invoker.no_debug_types)) { |
| foreach(x, invoker.no_debug_types) { |
| args += [ "--no-debug=${x}" ] |
| } |
| } |
| if (defined(invoker.rustified_enum)) { |
| foreach(x, invoker.rustified_enum) { |
| args += [ "--rustified-enum=${x}" ] |
| } |
| } |
| |
| if (!defined(invoker.use_core) || invoker.use_core) { |
| args += [ "--use-core" ] |
| } else { |
| args += [ "--no-use-core" ] |
| } |
| |
| if (defined(invoker.additional_bindgen_flags)) { |
| foreach(x, invoker.additional_bindgen_flags) { |
| args += [ "--bindgen-flag=${x}" ] |
| } |
| } |
| |
| if (defined(invoker.fuchsia_api_level)) { |
| _api_level = invoker.fuchsia_api_level |
| if (_api_level == "NEXT") { |
| _api_level = "4291821568" |
| } else if (_api_level == "HEAD") { |
| _api_level = "4292870144" |
| } else if (_api_level == "PLATFORM") { |
| _api_level = "4293918720" |
| } |
| args += [ |
| "--fuchsia-api-level", |
| _api_level, |
| ] |
| } |
| _clang_resource_dir = |
| "${rebased_clang_dir}/${clang_target_toolchain_info.resource_dir}" |
| args += [ |
| "--clang-resource-dir", |
| _clang_resource_dir, |
| ] |
| |
| # All flags after "--" go directly to clang. |
| args += [ |
| "--", |
| |
| "{{defines}}", |
| "{{include_dirs}}", |
| "{{cflags}}", |
| ] |
| |
| if (defined(invoker.cpp) && invoker.cpp) { |
| args += [ |
| "-x", |
| "c++", |
| "-std=c++${fuchsia_cxx_version}", |
| "-stdlib=libc++", |
| "-I${rebased_clang_dir}/include/c++/v1", |
| "-I${rebased_clang_dir}/include/${current_target_tuple}/c++/v1", |
| "{{cflags_cc}}", |
| ] |
| } else { |
| args += [ "{{cflags_c}}" ] |
| } |
| |
| if (defined(sysroot) && sysroot != "") { |
| args += [ "--sysroot=" + rebase_path(sysroot, root_build_dir) ] |
| } |
| |
| if (defined(invoker.additional_clang_flags)) { |
| args += invoker.additional_clang_flags |
| } |
| |
| if (defined(invoker.defines)) { |
| foreach(d, invoker.defines) { |
| args += [ "-D${d}" ] |
| } |
| } |
| } |
| } |
| |
| # Defines a target that compares generated bindgen with checked-in golden file. |
| # |
| # Parameters |
| # |
| # * checked_in_source |
| # - Required: Path to the checked-in Rust file to compare against. |
| # - Type: path |
| # |
| # * bindgen_toolchain |
| # - Optional: The GN toolchain under which to run bindgen. It is important |
| # that it be fixed so that the goldens are independent of the current |
| # target build configuration. |
| # - Type: label |
| # - Default: host_x64_toolchain |
| # |
| # See rustc_bindgen() for other parameters. |
| # |
| template("rustc_bindgen_golden") { |
| assert(defined(invoker.headers), "Must specify headers") |
| assert(defined(invoker.checked_in_source), "Must specify checked_in_source") |
| |
| main_target = target_name |
| bindgen_target = "${target_name}_generated" |
| |
| forward_variables_from(invoker, [ "bindgen_toolchain" ]) |
| if (!defined(bindgen_toolchain)) { |
| bindgen_toolchain = host_x64_toolchain |
| } |
| |
| generated_filename = "${target_name}_generated.rs" |
| generated_dir = |
| get_label_info(":${main_target}(${bindgen_toolchain})", "target_gen_dir") |
| generated_filepath = "${generated_dir}/${generated_filename}" |
| if (current_toolchain == bindgen_toolchain) { |
| rustc_bindgen(bindgen_target) { |
| visibility = [ ":*" ] |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "bindgen_toolchain", |
| "checked_in_source", |
| "output", |
| "visibility", |
| ]) |
| |
| output_name = generated_filename |
| generator_label = get_label_info(":$target_name", "label_no_toolchain") |
| } |
| } else { |
| not_needed(invoker, |
| "*", |
| [ |
| "deps", |
| "testonly", |
| "visibility", |
| ]) |
| } |
| |
| golden_files(main_target) { |
| forward_variables_from(invoker, |
| [ |
| "deps", |
| "testonly", |
| "visibility", |
| ]) |
| if (!defined(deps)) { |
| deps = [] |
| } |
| deps += [ ":${bindgen_target}(${bindgen_toolchain})" ] |
| |
| comparisons = [ |
| { |
| golden = invoker.checked_in_source |
| candidate = generated_filepath |
| }, |
| ] |
| } |
| } |