Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions extensions/bindgen/private/bindgen.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,17 @@ def _rust_bindgen_impl(ctx):
rust_toolchain = ctx.toolchains[Label("@rules_rust//rust:toolchain_type")]
user_flags = [f.split("=")[0] for f in ctx.attr.bindgen_flags]

# Ignore `nightly` or `beta` versions.
if rust_toolchain.version and rust_toolchain.version[0].isdigit():
# Ignore `nightly` or `beta` versions (`version_semver` is `None` for channel labels).
version_semver = getattr(rust_toolchain, "version_semver", None)
if version_semver:
# Pass `--rust-target` so bindgen knows the actual toolchain version.
# Without this, bindgen defaults to it's built-in `LATEST_STABLE_RUST`
# which may be older than the actual toolchain version and would
# reject newer editions (e.g. edition 2024 requires `rust-target >= 1.85.0`).
# bindgen only accepts `MAJOR.MINOR[.PATCH]`, so drop any pre-release
# (`-nightly`) or build-metadata (`+...`) part of the version.
if "--rust-target" not in user_flags:
args.add("--rust-target=%s" % rust_toolchain.version)
args.add("--rust-target=%d.%d.%d" % (version_semver.major, version_semver.minor, version_semver.patch))

if "--rust-edition" not in user_flags:
args.add("--rust-edition=%s" % rust_toolchain.default_edition)
Expand Down
146 changes: 146 additions & 0 deletions extensions/bindgen/test/bindgen_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ load("@rules_cc//cc:cc_toolchain_config_lib.bzl", "feature", "flag_group", "flag
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_toolchain")
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
load("@rules_cc//cc/toolchains:cc_toolchain_config_info.bzl", "CcToolchainConfigInfo")
load("@rules_rust//cargo:defs.bzl", "cargo_toolchain")
load("@rules_rust//rust:defs.bzl", "rust_binary")
load("@rules_rust//rust:toolchain.bzl", "rust_stdlib_filegroup", "rust_toolchain")
load("@rules_rust_bindgen//:defs.bzl", "rust_bindgen_library")
load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite")
load("@rules_testing//lib:truth.bzl", "matching")
Expand Down Expand Up @@ -354,6 +356,148 @@ def _test_optimization_flags(name):
},
)

def _fake_rust_toolchain(name, version):
"""Registers mock `rust_toolchain` and `cargo_toolchain`s that declare `version`.

The toolchains are only ever used for analysis (the tests never execute
`rustc` or `cargo`), so empty executables and an arbitrary file standing in
for the standard library are sufficient.

`--extra_toolchains` applies to every configuration, including the exec
configuration that builds the bindgen CLI and its crates. Their
`cargo_build_script` targets fail analysis when the `rust_toolchain` and
`cargo_toolchain` they resolve disagree on `version`, so a `cargo_toolchain`
stating the same `version` is registered together with the `rust_toolchain`.

Returns:
list: The labels of the `toolchain` targets, to be passed to
`--extra_toolchains` together.
"""
mock_rustc = name + "_mock_rustc"
write_file(
name = mock_rustc,
out = name + "/mock_rustc.exe",
content = [],
is_executable = True,
)

mock_rustdoc = name + "_mock_rustdoc"
write_file(
name = mock_rustdoc,
out = name + "/mock_rustdoc.exe",
content = [],
is_executable = True,
)

mock_cargo = name + "_mock_cargo"
write_file(
name = mock_cargo,
out = name + "/mock_cargo.exe",
content = [],
is_executable = True,
)

std_libs = name + "_std_libs"
rust_stdlib_filegroup(
name = std_libs,
srcs = [mock_rustc],
)

rust_toolchain(
name = name + "_rust_toolchain",
binary_ext = "",
default_edition = "2021",
dylib_ext = ".so",
exec_triple = "x86_64-unknown-linux-gnu",
rust_doc = mock_rustdoc,
rust_std = std_libs,
rustc = mock_rustc,
staticlib_ext = ".a",
stdlib_linkflags = [],
target_triple = "x86_64-unknown-linux-gnu",
version = version,
)

native.toolchain(
name = name,
toolchain = name + "_rust_toolchain",
toolchain_type = "@rules_rust//rust:toolchain_type",
)

cargo_toolchain(
name = name + "_cargo_toolchain",
cargo = mock_cargo,
version = version,
)

native.toolchain(
name = name + "_cargo",
toolchain = name + "_cargo_toolchain",
toolchain_type = "@rules_rust//cargo:toolchain_type",
)

return [
str(native.package_relative_label(name)),
str(native.package_relative_label(name + "_cargo")),
]

def _test_rust_target_from_prerelease_version_impl(env, target):
env.expect.that_int(len(target.actions)).is_greater_than(0)
env.expect.that_action(target.actions[0]).mnemonic().contains("RustBindgen")

# bindgen only accepts `MAJOR.MINOR[.PATCH]` (or the literal `nightly`) for
# `--rust-target`; a pre-release suffix such as `-nightly` makes it fail with
# "the patch version number must be an unsigned 64-bit integer".
env.expect.that_action(target.actions[0]).contains_at_least_args(
["--rust-target=1.87.0"],
)
env.expect.that_action(target.actions[0]).not_contains_arg(
"--rust-target=1.87.0-nightly",
)

def _test_rust_target_from_prerelease_version(name):
# Test that a `rust_toolchain` declaring a pre-release `version` (as
# toolchains built from a nightly commit do) results in a plain
# `MAJOR.MINOR.PATCH` `--rust-target`.

toolchains = _fake_rust_toolchain(name + "_toolchain", version = "1.87.0-nightly")

_create_simple_rust_bindgen_library(name)

analysis_test(
name = name,
target = name + "_rust_bindgen__bindgen",
impl = _test_rust_target_from_prerelease_version_impl,
config_settings = {
"//command_line_option:extra_toolchains": toolchains,
},
)

def _test_no_rust_target_for_channel_label_impl(env, target):
env.expect.that_int(len(target.actions)).is_greater_than(0)
env.expect.that_action(target.actions[0]).mnemonic().contains("RustBindgen")
env.expect.that_action(target.actions[0]).argv().not_contains_predicate(
matching.str_startswith("--rust-target="),
)

def _test_no_rust_target_for_channel_label(name):
# Test that a `rust_toolchain` whose `version` is a bare channel label
# (`nightly`, `beta`) does not produce a `--rust-target` flag, leaving
# bindgen on its built-in default.

toolchains = _fake_rust_toolchain(name + "_toolchain", version = "nightly")

_create_simple_rust_bindgen_library(name)

analysis_test(
name = name,
target = name + "_rust_bindgen__bindgen",
impl = _test_no_rust_target_for_channel_label_impl,
config_settings = {
"//command_line_option:extra_toolchains": toolchains,
},
)

def bindgen_test_suite(name):
test_suite(
name = name,
Expand All @@ -365,5 +509,7 @@ def bindgen_test_suite(name):
_test_strip_xclang,
_test_isystem_preservation,
_test_optimization_flags,
_test_rust_target_from_prerelease_version,
_test_no_rust_target_for_channel_label,
],
)
Loading