What happened?
rust_bindgen fails when it consumes compile flags from the C++ toolchain provided by apple_support on macOS. The action receives the Apple toolchain's placeholder paths literally:
-isysroot __BAZEL_XCODE_SDKROOT__
-F__BAZEL_XCODE_SDKROOT__/System/Library/Frameworks
-F__BAZEL_XCODE_DEVELOPER_DIR__/Platforms/MacOSX.platform/Developer/Library/Frameworks
This causes bindgen to fail with:
warning: no such sysroot directory: '__BAZEL_XCODE_SDKROOT__'
fatal error: 'stddef.h' file not found
The same injected aws-lc-sys setup builds successfully when a hermetic LLVM C++ toolchain is selected for macOS. That toolchain supplies bindgen with a usable sysroot and Clang resource directory. The failure is specific to using the bare apple_support/Xcode C++ toolchain.
Using hermetic LLVM for the whole macOS build is not always a practical workaround: mixed C++/Rust projects may need to keep Xcode's Apple-provided toolchain for their existing C++ targets while still using rust_bindgen for Rust dependencies.
This appears distinct from #22: that issue fixed the environment for rustc, while this failure is in the custom RustBindgen action registered by @rules_rs//rs:rules_rust_bindgen.bzl.
Standalone reproducer
Environment used:
- macOS 26.5, arm64
- Xcode 26.6
- Bazel 9.2.0
rules_rs 0.0.104
apple_support 2.8.0
rules_cc 0.2.22
MODULE.bazel:
module(name = "rules_rs_apple_bindgen_repro")
# Keep apple_support before rules_cc so its C++ toolchain is configured.
bazel_dep(name = "apple_support", version = "2.8.0")
bazel_dep(name = "rules_cc", version = "0.2.22")
bazel_dep(name = "aws-lc", version = "5.1.0.bcr.2")
bazel_dep(name = "rules_rs", version = "0.0.104")
rust_toolchains = use_extension("@rules_rs//rs/toolchains:module_extension.bzl", "toolchains")
rust_toolchains.toolchain(
edition = "2021",
version = "1.95.0",
)
use_repo(rust_toolchains, "default_rust_toolchains")
register_toolchains("@default_rust_toolchains//...")
crate = use_extension("@rules_rs//rs:extensions.bzl", "crate")
crate.from_cargo(
name = "crates",
cargo_lock = "//:Cargo.lock",
cargo_toml = "//:Cargo.toml",
platform_triples = ["aarch64-apple-darwin"],
)
crate.annotation(
crate = "aws-lc-rs",
gen_build_script = "off",
)
crate.annotation(
additive_build_file = "@rules_rs//3rd_party/aws-lc-sys:additive.BUILD.bazel",
crate = "aws-lc-sys",
deps = ["@crates//:aws_lc_sys_build_info"],
extra_aliased_targets = {"aws_lc_sys_build_info": "aws_lc_sys_build_info"},
gen_build_script = "off",
rustc_flags = ["--cfg=use_bindgen_pregenerated"],
)
use_repo(crate, "crates")
inject_repo(crate, "aws-lc")
rules_rust_bindgen = use_extension("@rules_rs//rs:rules_rust_bindgen.bzl", "rules_rust_bindgen")
use_repo(rules_rust_bindgen, "rules_rust_bindgen")
register_toolchains("@rules_rust_bindgen//:all")
Cargo.toml:
[package]
name = "rules-rs-apple-bindgen-repro"
version = "0.1.0"
edition = "2021"
[dependencies]
aws-lc-rs = "=1.15.2"
BUILD.bazel:
load("@crates//:defs.bzl", "all_crate_deps")
load("@rules_rs//rs:rust_binary.bzl", "rust_binary")
rust_binary(
name = "repro",
srcs = ["main.rs"],
deps = all_crate_deps(normal = True),
)
main.rs:
fn main() {
let _ = aws_lc_rs::digest::SHA256;
}
Then run:
cargo generate-lockfile
bazel build //:repro --verbose_failures
Root cause
_clang_compile_flags correctly obtains the Apple C++ toolchain flags through cc_common, but _rust_bindgen_impl registers a plain ctx.actions.run. Apple's C++ compiler wrapper normally resolves the __BAZEL_XCODE_*__ placeholders. Since the bindgen executable is invoked directly, that resolution never happens.
This explains why selecting hermetic LLVM for macOS avoids the problem: its compile flags already point at a concrete hermetic SDK/resource directory and do not rely on the Apple placeholder-expansion wrapper.
Using apple_support.run resolves the placeholders and supplies the selected Xcode environment. One additional detail is required: the bundled libclang also needs Xcode Clang's builtin resource headers (stdarg.h, etc.). Keeping --no-include-path-detection on Apple still fails after resolving the SDK root, so Apple targets need bindgen include-path detection enabled under the Xcode environment. Non-Apple targets can retain the current hermetic behavior.
Possible fix
The following patch was tested against the standalone reproducer above; the build completed successfully.
diff --git a/rs/rules_rust_bindgen.bzl b/rs/rules_rust_bindgen.bzl
--- a/rs/rules_rust_bindgen.bzl
+++ b/rs/rules_rust_bindgen.bzl
@@
+load("@apple_support//lib:apple_support.bzl", "apple_support")
load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "C_COMPILE_ACTION_NAME")
@@
+ is_apple = apple_support.target_os_from_rule_ctx(ctx, fail_on_missing_constraint = False) != None
output = ctx.outputs.out
args = ctx.actions.args()
- args.add("--no-include-path-detection")
+ if not is_apple:
+ args.add("--no-include-path-detection")
@@
- ctx.actions.run(
+ action_kwargs = dict(
executable = bindgen_toolchain.bindgen,
arguments = [args],
inputs = compilation_context.headers,
outputs = [output],
tools = cc_toolchain.all_files,
mnemonic = "RustBindgen",
progress_message = "Generating Rust bindings for {}".format(header.short_path),
toolchain = _BINDGEN_TOOLCHAIN_TYPE,
)
+ if is_apple:
+ apple_support.run(
+ actions = ctx.actions,
+ apple_platform_info = apple_support.platform_info_from_rule_ctx(ctx),
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig],
+ xcode_path_resolve_level = apple_support.xcode_path_resolve_level.args_and_files,
+ **action_kwargs
+ )
+ else:
+ ctx.actions.run(**action_kwargs)
@@
- },
- fragments = ["cpp"],
+ } | apple_support.action_required_attrs() | apple_support.platform_constraint_attrs(),
+ fragments = ["apple", "cpp"],
It may be preferable to retain --no-include-path-detection and obtain the Xcode Clang resource directory another way. I used include-path detection here because the Apple toolchain's cc_common command line contains the SDK placeholders but does not include Clang's builtin resource directory; bindgen otherwise advances from missing stddef.h to missing stdarg.h.
What happened?
rust_bindgenfails when it consumes compile flags from the C++ toolchain provided byapple_supporton macOS. The action receives the Apple toolchain's placeholder paths literally:This causes bindgen to fail with:
The same injected
aws-lc-syssetup builds successfully when a hermetic LLVM C++ toolchain is selected for macOS. That toolchain supplies bindgen with a usable sysroot and Clang resource directory. The failure is specific to using the bareapple_support/Xcode C++ toolchain.Using hermetic LLVM for the whole macOS build is not always a practical workaround: mixed C++/Rust projects may need to keep Xcode's Apple-provided toolchain for their existing C++ targets while still using
rust_bindgenfor Rust dependencies.This appears distinct from #22: that issue fixed the environment for
rustc, while this failure is in the customRustBindgenaction registered by@rules_rs//rs:rules_rust_bindgen.bzl.Standalone reproducer
Environment used:
rules_rs0.0.104apple_support2.8.0rules_cc0.2.22MODULE.bazel:Cargo.toml:BUILD.bazel:main.rs:Then run:
Root cause
_clang_compile_flagscorrectly obtains the Apple C++ toolchain flags throughcc_common, but_rust_bindgen_implregisters a plainctx.actions.run. Apple's C++ compiler wrapper normally resolves the__BAZEL_XCODE_*__placeholders. Since the bindgen executable is invoked directly, that resolution never happens.This explains why selecting hermetic LLVM for macOS avoids the problem: its compile flags already point at a concrete hermetic SDK/resource directory and do not rely on the Apple placeholder-expansion wrapper.
Using
apple_support.runresolves the placeholders and supplies the selected Xcode environment. One additional detail is required: the bundled libclang also needs Xcode Clang's builtin resource headers (stdarg.h, etc.). Keeping--no-include-path-detectionon Apple still fails after resolving the SDK root, so Apple targets need bindgen include-path detection enabled under the Xcode environment. Non-Apple targets can retain the current hermetic behavior.Possible fix
The following patch was tested against the standalone reproducer above; the build completed successfully.
It may be preferable to retain
--no-include-path-detectionand obtain the Xcode Clang resource directory another way. I used include-path detection here because the Apple toolchain'scc_commoncommand line contains the SDK placeholders but does not include Clang's builtin resource directory; bindgen otherwise advances from missingstddef.hto missingstdarg.h.