feat(build): migrated native pipeline to bazel with remote caching
- Replaced the napi-cli/cargo-zigbuild/cargo-xwin/sccache build path with Bazel: rules_rust + crate_universe over Cargo.lock, hermetic zig cc toolchains (linux-gnu pinned to glibc 2.17, linux-musl), host Xcode for darwin, and a repo-local hermetic clang-cl + llvm-ml + xwin toolchain for windows-msvc (bazel/toolchains/msvc). - All eight shipped addons build as //:natives-<target> via the release transition in bazel/defs.bzl (opt, thin LTO, cgu=16, stripped, canonical .node naming); scripts/bazel-natives.ts is the single driver for local dev and CI. - Rust validation moved to bazel test + clippy aspects (strict workspace policy for opted-in crates, default lints elsewhere, mirroring cargo semantics) and the rustfmt aspect; cargo stays as the dev-iteration surface, with brush-core/brush-builtins promoted to workspace members and excluded from cargo dev tasks to keep their historical scope. - CI caches through an in-cluster bazel-remote action cache (TLS + basic auth, cluster-internal only); GitHub-hosted runners never touch the infrastructure and use an actions/cache-backed disk cache instead. - Deleted the hand-rolled caching machinery: ci-target-cache, ci-native-artifact-cache, ci-build-native, native-source-hash, find-native-artifacts, restore-linux-native, native-prewarm workflow, ensure-* toolchain actions, and all sccache/Swatinem wiring. - Warm native rebuilds drop from ~20 minutes to seconds; a cold client with a warm remote cache rebuilds the linux x64 pair in ~2.5 minutes.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# Namespace package for repo-local Bazel rules (defs.bzl).
|
||||
@@ -0,0 +1,48 @@
|
||||
# Generated from [workspace.lints] in Cargo.toml (see .bazelrc clippy-strict).
|
||||
# Applies only to crates that opt in via `[lints] workspace = true`.
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Amismatched_lifetime_syntaxes
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Wclippy::all
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Dclippy::correctness
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Wclippy::nursery
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Wclippy::pedantic
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Wclippy::perf
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Wclippy::style
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Dclippy::suspicious
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Wclippy::allow_attributes_without_reason
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::borrow_as_ptr
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::cast_lossless
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::cast_possible_truncation
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::cast_possible_wrap
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::cast_precision_loss
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::cast_ptr_alignment
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::cast_sign_loss
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::default_trait_access
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::enum_glob_use
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::float_cmp
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::inconsistent_struct_constructor
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::inline_always
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::items_after_statements
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::let_underscore_untyped
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::match_same_arms
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::match_wildcard_for_single_variants
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::missing_errors_doc
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::missing_fields_in_debug
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::missing_panics_doc
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::must_use_candidate
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::needless_pass_by_value
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::option_if_let_else
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::ptr_as_ptr
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::redundant_closure_for_method_calls
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::ref_as_ptr
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::return_self_not_must_use
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::significant_drop_tightening
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::similar_names
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::struct_excessive_bools
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::too_many_arguments
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::too_many_lines
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::tuple_array_conversions
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Wclippy::undocumented_unsafe_blocks
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::unreadable_literal
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::unsafe_derive_deserialize
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::verbose_bit_mask
|
||||
build:clippy-strict --@rules_rust//rust/settings:clippy_flag=-Aclippy::wildcard_imports
|
||||
@@ -0,0 +1,78 @@
|
||||
"""Rules for producing release-grade, canonically named pi_natives addons.
|
||||
|
||||
`native_addon` transitions //crates/pi-natives:pi_natives onto a shipping
|
||||
platform with the release codegen profile (opt, thin LTO, cgu=16, stripped —
|
||||
mirrors the cargo `ci` profile) and renames the produced shared library to the
|
||||
loader's canonical `pi_natives.<platform>-<arch>[-<variant>].node` filename.
|
||||
|
||||
Encoding the profile in the transition means `bazel build //:natives-<t>` is
|
||||
always release-grade regardless of -c, and every addon shares one cache entry
|
||||
per (platform, source) pair.
|
||||
"""
|
||||
|
||||
_ADDON_RUSTC_FLAGS = [
|
||||
"-Ccodegen-units=16",
|
||||
"-Cstrip=symbols",
|
||||
]
|
||||
|
||||
def _addon_transition_impl(settings, attr):
|
||||
return {
|
||||
"//command_line_option:platforms": str(attr.platform),
|
||||
"//command_line_option:compilation_mode": "opt",
|
||||
"@rules_rust//rust/settings:lto": "thin",
|
||||
"@rules_rust//rust/settings:extra_rustc_flags": _ADDON_RUSTC_FLAGS,
|
||||
}
|
||||
|
||||
_addon_transition = transition(
|
||||
implementation = _addon_transition_impl,
|
||||
inputs = [],
|
||||
outputs = [
|
||||
"//command_line_option:platforms",
|
||||
"//command_line_option:compilation_mode",
|
||||
"@rules_rust//rust/settings:lto",
|
||||
"@rules_rust//rust/settings:extra_rustc_flags",
|
||||
],
|
||||
)
|
||||
|
||||
_SHARED_LIB_EXTENSIONS = ("so", "dylib", "dll")
|
||||
|
||||
def _native_addon_impl(ctx):
|
||||
libs = [
|
||||
f
|
||||
for f in ctx.attr.lib[0][DefaultInfo].files.to_list()
|
||||
if f.extension in _SHARED_LIB_EXTENSIONS
|
||||
]
|
||||
if len(libs) != 1:
|
||||
fail("expected exactly one shared library from {}, got: {}".format(
|
||||
ctx.attr.lib[0].label,
|
||||
[f.short_path for f in libs],
|
||||
))
|
||||
# Scope under the rule name: gnu and musl addons share canonical filenames
|
||||
# (the loader never sees both), so bare package-level outputs would collide.
|
||||
out = ctx.actions.declare_file(ctx.label.name + "/" + ctx.attr.out)
|
||||
ctx.actions.symlink(output = out, target_file = libs[0])
|
||||
return [DefaultInfo(files = depset([out]))]
|
||||
|
||||
native_addon = rule(
|
||||
implementation = _native_addon_impl,
|
||||
doc = "Release build of the pi_natives cdylib for one shipping platform, " +
|
||||
"renamed to the loader's canonical .node filename.",
|
||||
attrs = {
|
||||
"lib": attr.label(
|
||||
cfg = _addon_transition,
|
||||
mandatory = True,
|
||||
doc = "The rust_shared_library target (//crates/pi-natives:pi_natives).",
|
||||
),
|
||||
"platform": attr.label(
|
||||
mandatory = True,
|
||||
doc = "//bazel/platforms platform to build for.",
|
||||
),
|
||||
"out": attr.string(
|
||||
mandatory = True,
|
||||
doc = "Canonical addon filename, e.g. pi_natives.linux-x64-baseline.node.",
|
||||
),
|
||||
"_allowlist_function_transition": attr.label(
|
||||
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
|
||||
),
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,61 @@
|
||||
# One platform per shipped pi_natives addon. linux platforms inherit the zig
|
||||
# libc-aware platforms so cc toolchain resolution pins glibc 2.17 (the shipped
|
||||
# portability floor) or musl; the cpu_variant constraint selects -Ctarget-cpu
|
||||
# in //crates/pi-natives. darwin/win32 resolve against the host Xcode toolchain
|
||||
# and //bazel/toolchains/msvc respectively.
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
platform(
|
||||
name = "linux-x64-baseline",
|
||||
constraint_values = ["//bazel/variants:baseline"],
|
||||
parents = ["@zig_sdk//libc_aware/platform:linux_amd64_gnu.2.17"],
|
||||
)
|
||||
|
||||
platform(
|
||||
name = "linux-x64-modern",
|
||||
constraint_values = ["//bazel/variants:modern"],
|
||||
parents = ["@zig_sdk//libc_aware/platform:linux_amd64_gnu.2.17"],
|
||||
)
|
||||
|
||||
platform(
|
||||
name = "linux-arm64",
|
||||
parents = ["@zig_sdk//libc_aware/platform:linux_arm64_gnu.2.17"],
|
||||
)
|
||||
|
||||
platform(
|
||||
name = "linux-musl-x64-baseline",
|
||||
constraint_values = ["//bazel/variants:baseline"],
|
||||
parents = ["@zig_sdk//libc_aware/platform:linux_amd64_musl"],
|
||||
)
|
||||
|
||||
platform(
|
||||
name = "linux-musl-arm64",
|
||||
parents = ["@zig_sdk//libc_aware/platform:linux_arm64_musl"],
|
||||
)
|
||||
|
||||
platform(
|
||||
name = "darwin-x64-baseline",
|
||||
constraint_values = [
|
||||
"@platforms//os:macos",
|
||||
"@platforms//cpu:x86_64",
|
||||
"//bazel/variants:baseline",
|
||||
],
|
||||
)
|
||||
|
||||
platform(
|
||||
name = "darwin-arm64",
|
||||
constraint_values = [
|
||||
"@platforms//os:macos",
|
||||
"@platforms//cpu:arm64",
|
||||
],
|
||||
)
|
||||
|
||||
platform(
|
||||
name = "win32-x64-baseline",
|
||||
constraint_values = [
|
||||
"@platforms//os:windows",
|
||||
"@platforms//cpu:x86_64",
|
||||
"//bazel/variants:baseline",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,83 @@
|
||||
# Repo-local toolchains, registered in MODULE.bazel BEFORE @rust_toolchains//:all.
|
||||
#
|
||||
# musl wrappers: rules_rust's generated gnu and musl rust toolchains share
|
||||
# (os, cpu) constraints, so plain registration order would hand musl platforms
|
||||
# the gnu std. These wrappers re-register the musl toolchains gated on
|
||||
# @zig_sdk//libc:musl (carried by //bazel/platforms:linux-musl-*), and lose to
|
||||
# nothing on gnu platforms because the constraint cannot match there.
|
||||
#
|
||||
# //bazel/toolchains/msvc: hermetic clang-cl + lld-link + xwin CRT/SDK
|
||||
# cc toolchain for x86_64-pc-windows-msvc cross builds from linux.
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
_MUSL_RUST_TOOLCHAINS = {
|
||||
"linux-x64-to-musl-x64": (
|
||||
["@platforms//os:linux", "@platforms//cpu:x86_64"],
|
||||
["@platforms//os:linux", "@platforms//cpu:x86_64"],
|
||||
"@rust_linux_x86_64__x86_64-unknown-linux-musl__nightly_tools//:rust_toolchain",
|
||||
),
|
||||
"linux-x64-to-musl-arm64": (
|
||||
["@platforms//os:linux", "@platforms//cpu:x86_64"],
|
||||
["@platforms//os:linux", "@platforms//cpu:aarch64"],
|
||||
"@rust_linux_x86_64__aarch64-unknown-linux-musl__nightly_tools//:rust_toolchain",
|
||||
),
|
||||
"darwin-arm64-to-musl-x64": (
|
||||
["@platforms//os:osx", "@platforms//cpu:aarch64"],
|
||||
["@platforms//os:linux", "@platforms//cpu:x86_64"],
|
||||
"@rust_macos_aarch64__x86_64-unknown-linux-musl__nightly_tools//:rust_toolchain",
|
||||
),
|
||||
"darwin-arm64-to-musl-arm64": (
|
||||
["@platforms//os:osx", "@platforms//cpu:aarch64"],
|
||||
["@platforms//os:linux", "@platforms//cpu:aarch64"],
|
||||
"@rust_macos_aarch64__aarch64-unknown-linux-musl__nightly_tools//:rust_toolchain",
|
||||
),
|
||||
"linux-arm64-to-musl-arm64": (
|
||||
["@platforms//os:linux", "@platforms//cpu:aarch64"],
|
||||
["@platforms//os:linux", "@platforms//cpu:aarch64"],
|
||||
"@rust_linux_aarch64__aarch64-unknown-linux-musl__nightly_tools//:rust_toolchain",
|
||||
),
|
||||
"linux-arm64-to-musl-x64": (
|
||||
["@platforms//os:linux", "@platforms//cpu:aarch64"],
|
||||
["@platforms//os:linux", "@platforms//cpu:x86_64"],
|
||||
"@rust_linux_aarch64__x86_64-unknown-linux-musl__nightly_tools//:rust_toolchain",
|
||||
),
|
||||
}
|
||||
|
||||
[
|
||||
toolchain(
|
||||
name = "rust-musl-" + name,
|
||||
exec_compatible_with = exec_cv,
|
||||
target_compatible_with = target_cv + ["@zig_sdk//libc:musl"],
|
||||
target_settings = ["@rules_rust//rust/toolchain/channel:nightly"],
|
||||
toolchain = tools,
|
||||
toolchain_type = "@rules_rust//rust:toolchain",
|
||||
)
|
||||
for name, (exec_cv, target_cv, tools) in _MUSL_RUST_TOOLCHAINS.items()
|
||||
]
|
||||
|
||||
# msvc cross cc toolchain: @msvc_cc generates its wrappers for whichever host
|
||||
# fetches it, so one toolchain() per supported exec host points at the same
|
||||
# cc_toolchain — only the variant matching the local host can ever resolve.
|
||||
# target_compatible_with pins these to windows/x86_64, so they can never
|
||||
# shadow the zig cc toolchains on linux (or the host Xcode toolchain on mac).
|
||||
_MSVC_EXEC_HOSTS = {
|
||||
"linux-x64": ["@platforms//os:linux", "@platforms//cpu:x86_64"],
|
||||
"linux-arm64": ["@platforms//os:linux", "@platforms//cpu:aarch64"],
|
||||
"darwin-arm64": ["@platforms//os:osx", "@platforms//cpu:aarch64"],
|
||||
"darwin-x64": ["@platforms//os:osx", "@platforms//cpu:x86_64"],
|
||||
}
|
||||
|
||||
[
|
||||
toolchain(
|
||||
name = "msvc-cc-from-" + name,
|
||||
exec_compatible_with = exec_cv,
|
||||
target_compatible_with = [
|
||||
"@platforms//os:windows",
|
||||
"@platforms//cpu:x86_64",
|
||||
],
|
||||
toolchain = "@msvc_cc//:cc_toolchain",
|
||||
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
|
||||
)
|
||||
for name, exec_cv in _MSVC_EXEC_HOSTS.items()
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
# Hermetic clang-cl + lld-link + xwin (MSVC CRT/SDK) cross toolchain for
|
||||
# x86_64-pc-windows-msvc, replacing cargo-xwin. The repository rules live in
|
||||
# llvm.bzl / sysroot.bzl / cc.bzl (instantiated from MODULE.bazel); the
|
||||
# toolchain() registrations live in //bazel/toolchains. See NOTES.md for the
|
||||
# knobs and what still needs validation on can.internal.
|
||||
@@ -0,0 +1,139 @@
|
||||
# msvc cross toolchain — knobs & validation notes
|
||||
|
||||
Hermetic clang-cl + lld-link + xwin (MSVC CRT/SDK) cc toolchain for
|
||||
`x86_64-pc-windows-msvc`, cross-linking from linux-x64 (CI) and darwin (dev)
|
||||
exec hosts. Replaces cargo-xwin.
|
||||
|
||||
## Layout
|
||||
|
||||
| Piece | Where | Why separate |
|
||||
| --- | --- | --- |
|
||||
| `@llvm_msvc_tools` | `llvm.bzl` | LLVM 20.1.7 release archive for the fetching host, pruned to clang-cl/lld-link/llvm-lib/llvm-rc/llvm-mt + `lib/clang/*/include`. Downloads (~2 GiB) are sha256-pinned → Bazel repository cache. |
|
||||
| `@xwin_sysroot` | `sysroot.bzl` | xwin 0.6.5 (pinned per-host sha256) runs `splat` in the repo rule. The ~1 GiB CRT/SDK payload comes from the Microsoft CDN via xwin itself and is **not** in Bazel's repo cache — a cold output base re-downloads it. Keep `sysroot.bzl` stable. |
|
||||
| `@msvc_cc` | `cc.bzl` | Wrapper scripts + `cc_toolchain` + MSVC feature config (copied from the resolved rules_cc, like `@local_config_cc`). Cheap to regenerate — iterate flags here. |
|
||||
| `toolchain()`s | `//bazel/toolchains` (`msvc-cc-from-*`) | One per exec host (linux-x64/arm64, darwin-arm64/x64), all pointing at `@msvc_cc//:cc_toolchain`; only the local host's variant can resolve. `target_compatible_with = [windows, x86_64]` ⇒ can never shadow zig on linux. |
|
||||
| MODULE.bazel | `# --- msvc cross toolchain ---` section | `rules_cc` 0.2.17 (= Bazel 9.2's builtin pin) + the three `use_repo_rule` instantiations. Plus one target-suffixed env key inside the existing `audiopus_sys` annotation (see below). |
|
||||
|
||||
## Design decisions
|
||||
|
||||
- **No toolchains_llvm**: it wants to register full host cc toolchains, which
|
||||
risks shadowing the zig linux toolchains. Direct LLVM release fetch instead.
|
||||
- **Wrappers self-locate from `$0`** (execroot-relative sibling repos), so they
|
||||
work from Bazel actions (cwd = execroot) *and* from build scripts, where
|
||||
rules_rust `${pwd}`-expands `CC`/`AR` to absolute paths and cc-rs/cmake spawn
|
||||
tools from other cwds.
|
||||
- **CRT: dynamic `/MD`** (rules_cc's msvc branch default outside `dbg` without
|
||||
the `static_link_msvcrt` feature) — matches what napi/cc-rs produced under
|
||||
cargo-xwin (rust msvc targets default to dynamic CRT without `+crt-static`).
|
||||
- **SSE floor in the wrapper, not annotations**: `-msse4.1 -msse4.2` live in the
|
||||
clang-cl wrapper, which only ever targets win32-x64 (baseline = x86-64-v2 ⊇
|
||||
SSE4.2). This is the old build-native.ts CFLAGS hack, windows-only by
|
||||
construction.
|
||||
- **cmake generator via target-suffixed env**: `crate_universe` cannot select()
|
||||
annotations per platform, and a second `crate.annotation` for the same crate
|
||||
hard-fails the extension (`_insert_annotation` dupe check; `annotation_select`
|
||||
is unused/broken in rules_rust 0.71.3). Instead the existing `audiopus_sys`
|
||||
annotation carries `CMAKE_GENERATOR_x86_64_pc_windows_msvc=Ninja` — cmake-rs
|
||||
reads `VAR_<triple>` before `VAR`, so the key is inert for all other targets.
|
||||
cmake-rs sets `CMAKE_SYSTEM_NAME=Windows` itself when target ≠ host.
|
||||
- **cmake tool discovery**: wrappers are named bare `clang-cl`, `lld-link`,
|
||||
`llvm-lib`, `llvm-rc`, `llvm-mt` (no `.sh`) because CMake's
|
||||
`CMakeFindBinUtils`/`find_program` probes for those names next to
|
||||
`CMAKE_C_COMPILER`, and cc-rs sniffs the MSVC/clang-cl tool family from the
|
||||
basename. The clang-cl wrapper also exports link.exe-style `LIB` and passes
|
||||
`-fuse-ld=lld-link` so compiler-driver links (cmake `try_compile` ABI checks)
|
||||
resolve CRT import libs without a toolchain file.
|
||||
- **ring 0.17.14 needs no perl/nasm**: verified in its build.rs — crates.io
|
||||
tarballs use `pregenerated/*-nasm.o` objects directly for windows-msvc
|
||||
(`use_nasm()` path only shells out during the maintainer packaging step).
|
||||
The .o files flow through `cc::Build.object()` into llvm-lib.
|
||||
|
||||
## Reproducibility / cache implications
|
||||
|
||||
- First fetch per host: ~2 GiB LLVM (repo-cache backed) + ~15 MiB xwin +
|
||||
~1 GiB MS CDN splat (not repo-cache backed). Splat repo ends up ~800 MiB.
|
||||
- `--manifest-version 17` pins the VS2022 channel, but Microsoft advances the
|
||||
channel payload over time → splat is stable day-to-day, not bit-reproducible
|
||||
forever (same property cargo-xwin had). Action keys only depend on the files
|
||||
actually read, so remote-cache hit rates degrade gracefully after an MS bump.
|
||||
- Toolchain binaries differ per exec host (linux vs mac clang) → win32 link
|
||||
actions do not share remote-cache entries across host OSes. CI is
|
||||
linux-x64-only for this target, so this only affects dev machines.
|
||||
|
||||
## Already verified (darwin-arm64 dev host, 2026-07-27)
|
||||
|
||||
- `bazel build --nobuild //:natives-win32-x64-baseline` analyzes clean;
|
||||
`cquery deps(...)` confirms `@msvc_cc//:cc_toolchain` (not the host Xcode
|
||||
toolchain) resolved for the windows target. Full fetch + splat took ~2.5 min
|
||||
on a fast link. Repin (`bun run bazel:repin`) already run for the
|
||||
`CMAKE_GENERATOR_x86_64_pc_windows_msvc` annotation key.
|
||||
- Wrapper smoke test outside Bazel: `clang-cl /MD` compiled a windows.h +
|
||||
smmintrin.h SSE4.1 program and driver-linked it via `-fuse-ld=lld-link` +
|
||||
`LIB` into a valid PE32+ exe; the standalone `lld-link` wrapper (rustc's
|
||||
`-Clinker` path) and `llvm-lib` also produced a PE exe / ar archive.
|
||||
- xwin's `10.0.26100 -> .` self-referential version symlinks broke Bazel's
|
||||
glob ("too many levels of symbolic links"); `sysroot.bzl` now prunes any
|
||||
readdir entry whose realpath equals its parent, post-splat.
|
||||
- blake3 MASM (`ml64.exe` from cc-rs on non-windows hosts): `bin/ml64.exe` /
|
||||
`bin/ml64` shims exec `llvm-ml -m64`. All four blake3 1.8.5
|
||||
`blake3_*_x86-64_windows_msvc.asm` files assemble to valid amd64 COFF
|
||||
objects through the shim (invoked cc-rs-style via PATH with joined `/Fo`).
|
||||
cc-rs finds the shim through the blake3 crate.annotation, which prepends
|
||||
`$${pwd}/external/+msvc_cc_repository+msvc_cc/bin` to the build-script PATH
|
||||
(`$$` because annotation env runs through Bazel make-var expansion; the
|
||||
rules_rust runner then substitutes `${pwd}` → exec root). The wrapper dir
|
||||
reaches the sandbox via the cc toolchain's `all_files`. NOTE: the PATH entry
|
||||
hardcodes @msvc_cc's canonical repo name — keep in sync if the repo rule or
|
||||
repo name changes. Repin for this annotation already run.
|
||||
- audiopus_sys/opus cmake exe links (can.internal finding #2): cmake's
|
||||
`vs_link_exe` demands rc/mt tools that `find_program` can't locate on a
|
||||
linux/mac PATH. @msvc_cc now generates `toolchain.cmake` (self-locating via
|
||||
`CMAKE_CURRENT_LIST_DIR`: compiler/linker/rc/mt = the wrappers) handed to
|
||||
cmake-rs through `CMAKE_TOOLCHAIN_FILE_x86_64_pc_windows_msvc` in the
|
||||
audiopus_sys annotation (same `$${pwd}` + canonical-repo-path mechanism as
|
||||
the blake3 shim). Second failure mode fixed in the same file: `try_compile`
|
||||
defaults to the Debug config → `/MDd` → `msvcrtd.lib`, which the lean splat
|
||||
(like cargo-xwin's) does not carry; toolchain.cmake pins
|
||||
`CMAKE_TRY_COMPILE_CONFIGURATION=Release`, `CMAKE_POLICY_DEFAULT_CMP0091=NEW`
|
||||
and `CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL` (/MD everywhere).
|
||||
Verified on darwin: scratch `project(C)` + `add_executable` configures with
|
||||
"Clang 20.1.7 with MSVC-like command-line" and links a valid PE32+ exe
|
||||
through vs_link_exe with the wrapper rc/mt/linker. Repin already run.
|
||||
|
||||
## What to verify on can.internal (linux-x64)
|
||||
|
||||
1. `bazel build //:natives-win32-x64-baseline` end-to-end link; check the
|
||||
produced `pi_natives.win32-x64-baseline.node` imports (dumpbin/llvm-readobj:
|
||||
expect VCRUNTIME140/api-ms-win-crt-* → `/MD`, no static CRT).
|
||||
2. LLVM 20.1.7 Linux-X64 binaries are built on a newish Ubuntu: confirm the
|
||||
kata runner image's glibc is ≥ 2.35-ish and has `libtinfo6`/`libstdc++6`
|
||||
(usual LLVM release-binary runtime deps).
|
||||
3. `ninja` + `cmake` must be on the audiopus_sys build-script PATH
|
||||
(`/usr/local/bin:/usr/bin:/bin`) on the kata image — same requirement the
|
||||
old ensure-cmake action satisfied for cargo-xwin.
|
||||
4. audiopus_sys configure: cmake should report
|
||||
"Clang with MSVC-like command-line", take `toolchain.cmake` (log shows the
|
||||
vs_link_exe --rc/--mt pointing into the wrapper dir), and produce opus.lib
|
||||
with /MD objects. If mt is ever asked to actually merge manifests, note
|
||||
the official LLVM llvm-mt lacks libxml2 and would error — not hit today.
|
||||
5. tree-sitter grammar compiles via cc-rs: wrapper is picked up as `CC`
|
||||
(family detection needs the basename to contain `clang-cl` — it does).
|
||||
6. ring: no `nasm`/`perl` spawns in the build-script log; archive step uses
|
||||
the `llvm-lib` wrapper.
|
||||
7. blake3: build-script log should show `ml64.exe` resolving to the shim (no
|
||||
"failed to find tool" error); spot-check the assembled objects land in the
|
||||
rlib.
|
||||
8. Repo fetch time/disk on the pods (first fetch ~3 GiB, ~25 min worst case);
|
||||
consider pre-warming the bazel output base or a persistent
|
||||
`--repository_cache` volume if it hurts.
|
||||
|
||||
## Knobs
|
||||
|
||||
- LLVM version/sha256s: `llvm.bzl` (`_LLVM_VERSION`, `_LLVM_DISTS`). 20.1.7 is
|
||||
the newest release with archives for all four host tuples.
|
||||
- xwin version + manifest channel: `sysroot.bzl` (`_XWIN_VERSION` 0.6.5 — last
|
||||
release with darwin binaries; `_XWIN_MANIFEST_VERSION` "17").
|
||||
- Compile/link flags, include/libpath set, CRT choice: wrapper templates and
|
||||
`cc_toolchain_config` attrs in `cc.bzl`.
|
||||
- Static CRT if ever needed: build with the standard `static_link_msvcrt`
|
||||
feature (`--features=static_link_msvcrt`) instead of editing flags.
|
||||
@@ -0,0 +1,279 @@
|
||||
"""Repository rule generating the hermetic clang-cl + xwin MSVC cc toolchain.
|
||||
|
||||
The @msvc_cc repo holds only cheap generated files — wrapper shell scripts and
|
||||
the cc_toolchain/cc_toolchain_config BUILD — so iterating on flags here never
|
||||
invalidates the big @llvm_msvc_tools / @xwin_sysroot downloads (their rules
|
||||
live in llvm.bzl / sysroot.bzl on purpose).
|
||||
|
||||
Wrappers derive every path from $0 (execroot-relative sibling repos), so they
|
||||
work from Bazel actions (cwd = execroot) and from build scripts, where
|
||||
rules_rust ${pwd}-expands CC/AR to absolute paths and cc-rs/cmake spawn the
|
||||
tools from other working directories:
|
||||
|
||||
bin/clang-cl --target=x86_64-pc-windows-msvc, /imsvc CRT+SDK includes,
|
||||
-msse4.1 -msse4.2 (win32-x64 baseline floor; clang-cl
|
||||
enforces per-function target features, real MSVC does not),
|
||||
-fuse-ld=lld-link + exported link.exe-style LIB so driver
|
||||
links (cmake try_compile) resolve the CRT import libs.
|
||||
bin/lld-link /libpath for CRT + SDK libs, then pass-through; rustc uses
|
||||
it via -Clinker for the msvc linker flavor.
|
||||
bin/llvm-lib archiver (lib.exe replacement, cc-rs AR + cmake CMAKE_AR).
|
||||
bin/llvm-rc resource compiler (cmake finds it next to the compiler).
|
||||
bin/llvm-mt manifest tool (cmake CMAKE_MT probe).
|
||||
bin/ml64.exe MASM shim → llvm-ml -m64 (also bare `ml64`): cc-rs looks
|
||||
bin/ml64 up `ml64.exe` on PATH for x64 msvc .asm (blake3); the
|
||||
blake3 crate.annotation prepends this dir to PATH.
|
||||
|
||||
The MSVC-flavored feature/action config (dynamic CRT /MD by default — matching
|
||||
what napi/cc-rs produced under cargo-xwin; /MT only via the standard
|
||||
static_link_msvcrt feature) comes from rules_cc's own
|
||||
windows_cc_toolchain_config.bzl, copied into the repo exactly like
|
||||
@local_config_cc does, so it always matches the resolved rules_cc version.
|
||||
"""
|
||||
|
||||
_CLANG_CL_WRAPPER = """\
|
||||
#!/bin/sh
|
||||
# Generated by //bazel/toolchains/msvc:cc.bzl. clang-cl driver for
|
||||
# x86_64-pc-windows-msvc against the @xwin_sysroot MSVC CRT + Windows SDK.
|
||||
set -eu
|
||||
execroot="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
llvm="$execroot/external/{llvm}"
|
||||
splat="$execroot/external/{xwin}/splat"
|
||||
# lld-link (spawned through -fuse-ld for compiler-driver links, e.g. cmake
|
||||
# try_compile) resolves CRT/SDK import libs via the link.exe-style LIB env.
|
||||
LIB="$splat/crt/lib/x86_64;$splat/sdk/lib/um/x86_64;$splat/sdk/lib/ucrt/x86_64"
|
||||
export LIB
|
||||
# -msse4.1/-msse4.2: clang-cl enforces per-function target features (real MSVC
|
||||
# does not); opus' silk/x86 SSE4.1 units fail without the feature enabled
|
||||
# globally. The win32-x64 addon floor is x86-64-v2 (SSE4.2 inclusive), so this
|
||||
# is safe for every C dep — same flags the cargo-xwin pipeline exported.
|
||||
exec "$llvm/bin/clang-cl" \\
|
||||
--target=x86_64-pc-windows-msvc \\
|
||||
-fuse-ld=lld-link \\
|
||||
-msse4.1 \\
|
||||
-msse4.2 \\
|
||||
-Wno-unused-command-line-argument \\
|
||||
"/imsvc$splat/crt/include" \\
|
||||
"/imsvc$splat/sdk/include/ucrt" \\
|
||||
"/imsvc$splat/sdk/include/um" \\
|
||||
"/imsvc$splat/sdk/include/shared" \\
|
||||
"/imsvc$splat/sdk/include/winrt" \\
|
||||
"/imsvc$splat/sdk/include/cppwinrt" \\
|
||||
"$@"
|
||||
"""
|
||||
|
||||
_LLD_LINK_WRAPPER = """\
|
||||
#!/bin/sh
|
||||
# Generated by //bazel/toolchains/msvc:cc.bzl. lld-link with the @xwin_sysroot
|
||||
# CRT + SDK libpaths; rustc invokes it via -Clinker (msvc linker flavor).
|
||||
set -eu
|
||||
execroot="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
llvm="$execroot/external/{llvm}"
|
||||
splat="$execroot/external/{xwin}/splat"
|
||||
# rustc addresses lld in generic multi-flavor style ("-flavor link" first);
|
||||
# the single-flavor lld-link binary would treat "link" as an input file.
|
||||
if [ "${{1:-}}" = "-flavor" ]; then shift 2; fi
|
||||
exec "$llvm/bin/lld-link" \\
|
||||
"/libpath:$splat/crt/lib/x86_64" \\
|
||||
"/libpath:$splat/sdk/lib/um/x86_64" \\
|
||||
"/libpath:$splat/sdk/lib/ucrt/x86_64" \\
|
||||
"$@"
|
||||
"""
|
||||
|
||||
_PASSTHROUGH_WRAPPER = """\
|
||||
#!/bin/sh
|
||||
# Generated by //bazel/toolchains/msvc:cc.bzl.
|
||||
set -eu
|
||||
execroot="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
exec "$execroot/external/{llvm}/bin/{tool}" "$@"
|
||||
"""
|
||||
|
||||
_ML64_WRAPPER = """\
|
||||
#!/bin/sh
|
||||
# Generated by //bazel/toolchains/msvc:cc.bzl. MASM shim: cc-rs assembles .asm
|
||||
# for x64 msvc targets (blake3's blake3_*_x86-64_windows_msvc.asm) by invoking
|
||||
# `ml64.exe` from PATH on non-windows hosts; llvm-ml is a MASM-compatible
|
||||
# replacement — same shim cargo-xwin installed. The blake3 crate.annotation in
|
||||
# MODULE.bazel prepends this bin/ dir to the build script PATH.
|
||||
set -eu
|
||||
execroot="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
exec "$execroot/external/{llvm}/bin/llvm-ml" -m64 "$@"
|
||||
"""
|
||||
|
||||
_NOP_WRAPPER = """\
|
||||
#!/bin/sh
|
||||
# Generated by //bazel/toolchains/msvc:cc.bzl. Placeholder for tools the MSVC
|
||||
# toolchain does not have (gcov/nm/objcopy/objdump/strip).
|
||||
exit 0
|
||||
"""
|
||||
|
||||
# CMake toolchain file for the `cmake` crate (audiopus_sys' bundled opus).
|
||||
# cmake's vs_link_exe helper insists on rc/mt tools for MSVC-ABI exe links
|
||||
# (try_compile links a test exe), and CMake's own find_program would only look
|
||||
# for `rc`/`mt` on PATH. CMAKE_CURRENT_LIST_DIR makes the file self-locating —
|
||||
# no canonical repo names involved. Handed to build scripts via the
|
||||
# CMAKE_TOOLCHAIN_FILE_x86_64_pc_windows_msvc annotation env in MODULE.bazel.
|
||||
_TOOLCHAIN_CMAKE = """\
|
||||
# Generated by //bazel/toolchains/msvc:cc.bzl — clang-cl + lld-link + xwin
|
||||
# CRT/SDK cross toolchain for x86_64-pc-windows-msvc (mirrors the toolchain
|
||||
# file cargo-xwin used to generate).
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR AMD64)
|
||||
set(CMAKE_C_COMPILER "${CMAKE_CURRENT_LIST_DIR}/bin/clang-cl")
|
||||
set(CMAKE_CXX_COMPILER "${CMAKE_CURRENT_LIST_DIR}/bin/clang-cl")
|
||||
set(CMAKE_LINKER "${CMAKE_CURRENT_LIST_DIR}/bin/lld-link")
|
||||
set(CMAKE_RC_COMPILER "${CMAKE_CURRENT_LIST_DIR}/bin/llvm-rc")
|
||||
set(CMAKE_MT "${CMAKE_CURRENT_LIST_DIR}/bin/llvm-mt")
|
||||
# The xwin splat carries release CRT import libs only (msvcrt.lib, no
|
||||
# msvcrtd.lib — same as cargo-xwin). try_compile defaults to the Debug
|
||||
# configuration, whose /MDd would demand the debug CRT; pin try_compile to
|
||||
# Release and the runtime library to dynamic release /MD for every config
|
||||
# (CMP0091 NEW makes CMAKE_MSVC_RUNTIME_LIBRARY authoritative even for
|
||||
# projects with ancient cmake_minimum_required, e.g. bundled opus).
|
||||
set(CMAKE_TRY_COMPILE_CONFIGURATION Release)
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0091 NEW)
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDLL)
|
||||
"""
|
||||
|
||||
_BUILD = """\
|
||||
# Generated by //bazel/toolchains/msvc:cc.bzl — hermetic clang-cl + lld-link +
|
||||
# xwin CRT/SDK cc toolchain for x86_64-pc-windows-msvc. The toolchain() targets
|
||||
# registering this live in //bazel/toolchains (one per supported exec host).
|
||||
|
||||
load("@rules_cc//cc/toolchains:cc_toolchain.bzl", "cc_toolchain")
|
||||
load(":windows_cc_toolchain_config.bzl", "cc_toolchain_config")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(name = "empty")
|
||||
|
||||
filegroup(
|
||||
name = "wrappers",
|
||||
srcs = glob(["bin/*"]) + ["toolchain.cmake"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all_files",
|
||||
srcs = [
|
||||
":wrappers",
|
||||
"@llvm_msvc_tools//:all",
|
||||
"@xwin_sysroot//:sysroot",
|
||||
],
|
||||
)
|
||||
|
||||
cc_toolchain_config(
|
||||
name = "clang_cl_xwin_config",
|
||||
abi_libc_version = "local",
|
||||
abi_version = "local",
|
||||
archiver_flags = ["/MACHINE:X64"],
|
||||
compiler = "clang-cl",
|
||||
cpu = "x64_windows",
|
||||
dbg_mode_debug_flag = "/DEBUG",
|
||||
default_link_flags = ["/MACHINE:X64"],
|
||||
fastbuild_mode_debug_flag = "/DEBUG",
|
||||
host_system_name = "local",
|
||||
msvc_cl_path = "bin/clang-cl",
|
||||
msvc_env_include = "{env_include}",
|
||||
msvc_env_lib = "{env_lib}",
|
||||
msvc_env_path = "/usr/bin:/bin",
|
||||
msvc_env_tmp = "/tmp",
|
||||
msvc_lib_path = "bin/llvm-lib",
|
||||
msvc_link_path = "bin/lld-link",
|
||||
msvc_ml_path = "bin/ml64.exe",
|
||||
supports_parse_showincludes = False,
|
||||
target_libc = "msvcrt",
|
||||
target_system_name = "x86_64-pc-windows-msvc",
|
||||
tool_paths = {{
|
||||
"ar": "bin/llvm-lib",
|
||||
"cpp": "bin/clang-cl",
|
||||
"gcc": "bin/clang-cl",
|
||||
"gcov": "bin/msvc-nop",
|
||||
"ld": "bin/lld-link",
|
||||
"ml": "bin/ml64.exe",
|
||||
"nm": "bin/msvc-nop",
|
||||
"objcopy": "bin/msvc-nop",
|
||||
"objdump": "bin/msvc-nop",
|
||||
"strip": "bin/msvc-nop",
|
||||
}},
|
||||
toolchain_identifier = "clang_cl_xwin_x64",
|
||||
)
|
||||
|
||||
cc_toolchain(
|
||||
name = "cc_toolchain",
|
||||
all_files = ":all_files",
|
||||
ar_files = ":all_files",
|
||||
as_files = ":all_files",
|
||||
compiler_files = ":all_files",
|
||||
dwp_files = ":empty",
|
||||
linker_files = ":all_files",
|
||||
objcopy_files = ":empty",
|
||||
strip_files = ":empty",
|
||||
supports_param_files = 1,
|
||||
toolchain_config = ":clang_cl_xwin_config",
|
||||
toolchain_identifier = "clang_cl_xwin_x64",
|
||||
)
|
||||
"""
|
||||
|
||||
def _msvc_cc_impl(rctx):
|
||||
llvm = rctx.attr.llvm_repo.repo_name
|
||||
xwin = rctx.attr.xwin_repo.repo_name
|
||||
splat = "external/{}/splat".format(xwin)
|
||||
|
||||
rctx.file("bin/clang-cl", _CLANG_CL_WRAPPER.format(llvm = llvm, xwin = xwin), executable = True)
|
||||
rctx.file("bin/lld-link", _LLD_LINK_WRAPPER.format(llvm = llvm, xwin = xwin), executable = True)
|
||||
for tool in ("llvm-lib", "llvm-rc", "llvm-mt"):
|
||||
rctx.file("bin/" + tool, _PASSTHROUGH_WRAPPER.format(llvm = llvm, tool = tool), executable = True)
|
||||
rctx.file("bin/msvc-nop", _NOP_WRAPPER, executable = True)
|
||||
# cc-rs resolves the x64 MASM assembler as `ml64.exe` from PATH on
|
||||
# non-windows hosts (blake3); ship both spellings of the shim.
|
||||
for name in ("ml64.exe", "ml64"):
|
||||
rctx.file("bin/" + name, _ML64_WRAPPER.format(llvm = llvm), executable = True)
|
||||
|
||||
# The MSVC feature/action-config machinery, verbatim from the resolved
|
||||
# rules_cc (same mechanism @local_config_cc uses on real Windows hosts).
|
||||
rctx.template("windows_cc_toolchain_config.bzl", rctx.attr._config_bzl, executable = False)
|
||||
|
||||
# CMake toolchain file for the cmake crate (no placeholders — it
|
||||
# self-locates via CMAKE_CURRENT_LIST_DIR, so no .format here).
|
||||
rctx.file("toolchain.cmake", _TOOLCHAIN_CMAKE, executable = False)
|
||||
|
||||
# INCLUDE/LIB for raw cc_* compile/link actions (cwd = execroot, so the
|
||||
# execroot-relative entries resolve). The rust graph routes everything
|
||||
# through the wrappers, which pass /imsvc and /libpath themselves.
|
||||
env_include = ";".join([
|
||||
splat + "/crt/include",
|
||||
splat + "/sdk/include/ucrt",
|
||||
splat + "/sdk/include/um",
|
||||
splat + "/sdk/include/shared",
|
||||
splat + "/sdk/include/winrt",
|
||||
splat + "/sdk/include/cppwinrt",
|
||||
])
|
||||
env_lib = ";".join([
|
||||
splat + "/crt/lib/x86_64",
|
||||
splat + "/sdk/lib/um/x86_64",
|
||||
splat + "/sdk/lib/ucrt/x86_64",
|
||||
])
|
||||
rctx.file(
|
||||
"BUILD.bazel",
|
||||
_BUILD.format(env_include = env_include, env_lib = env_lib),
|
||||
executable = False,
|
||||
)
|
||||
|
||||
msvc_cc_repository = repository_rule(
|
||||
implementation = _msvc_cc_impl,
|
||||
doc = "clang-cl/lld-link wrapper scripts + MSVC-flavored cc_toolchain for x86_64-pc-windows-msvc.",
|
||||
attrs = {
|
||||
"llvm_repo": attr.label(
|
||||
default = "@llvm_msvc_tools//:BUILD.bazel",
|
||||
doc = "Anchor into the pruned LLVM tools repo (canonical name source; not fetched here).",
|
||||
),
|
||||
"xwin_repo": attr.label(
|
||||
default = "@xwin_sysroot//:BUILD.bazel",
|
||||
doc = "Anchor into the xwin CRT/SDK sysroot repo (canonical name source; not fetched here).",
|
||||
),
|
||||
"_config_bzl": attr.label(
|
||||
default = "@rules_cc//cc/private/toolchain:windows_cc_toolchain_config.bzl",
|
||||
doc = "rules_cc's Windows cc_toolchain_config implementation, copied into this repo.",
|
||||
),
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,126 @@
|
||||
"""Repository rule fetching the LLVM binaries used by the msvc cross toolchain.
|
||||
|
||||
Downloads the official LLVM release archive for the host that fetches the repo
|
||||
(linux-x64 CI pods, darwin dev hosts) and prunes it down to the clang-cl /
|
||||
lld-link / llvm-lib / llvm-rc slice plus the clang builtin headers
|
||||
(lib/clang/<major>/include — immintrin.h & co, required for the SSE units in
|
||||
bundled opus). The pruned tree is ~300 MiB instead of ~10 GiB.
|
||||
|
||||
The archive download (~1.5-2 GiB) goes through repository_ctx.download_and_extract
|
||||
with a pinned sha256, so it lands in Bazel's content-addressed repository cache:
|
||||
re-fetches after `bazel clean --expunge` or a .bzl edit only pay extraction.
|
||||
|
||||
Checksums are the official llvm-project release assets, cross-checked against
|
||||
bazel-contrib/toolchains_llvm's distribution table.
|
||||
"""
|
||||
|
||||
_LLVM_VERSION = "20.1.7"
|
||||
|
||||
# host key -> (release asset suffix, sha256)
|
||||
_LLVM_DISTS = {
|
||||
"linux-arm64": ("Linux-ARM64", "832f2802a29457dc758f56e26e98558c6cd0e45fcd07186f540cb6e7f4e59385"),
|
||||
"linux-x64": ("Linux-X64", "8494c98a774051a40bfe1187a2d6442f4bc107598998bbe1673d9bb1572cfd6f"),
|
||||
"macos-arm64": ("macOS-ARM64", "6aa75de00575ad0663183b00f00f39992ded611b5136e57649ace1e6a53c0d16"),
|
||||
"macos-x64": ("macOS-X64", "ccf82ffe7e136ee49659cb57157856a7963d0950fac3d05aabba0db75bfba26f"),
|
||||
}
|
||||
|
||||
# bin/ entries to keep. Symlink chains (clang-cl -> clang -> clang-20,
|
||||
# llvm-lib -> llvm-ar, lld-link -> lld) are closed over at fetch time by
|
||||
# resolving realpaths, so version-suffixed real binaries need no hardcoding.
|
||||
_KEEP_BINS = [
|
||||
"clang",
|
||||
"clang-cl",
|
||||
"lld",
|
||||
"lld-link",
|
||||
"llvm-ar",
|
||||
"llvm-lib",
|
||||
"llvm-ml",
|
||||
"llvm-mt",
|
||||
"llvm-rc",
|
||||
]
|
||||
|
||||
def _host_key(rctx):
|
||||
os_name = rctx.os.name.lower()
|
||||
arch = rctx.os.arch.lower()
|
||||
if os_name.startswith("linux"):
|
||||
host_os = "linux"
|
||||
elif os_name.startswith("mac") or os_name.startswith("darwin"):
|
||||
host_os = "macos"
|
||||
else:
|
||||
fail("bazel/toolchains/msvc: unsupported exec host OS for the msvc cross toolchain: " + rctx.os.name)
|
||||
if arch in ("amd64", "x86_64", "x64"):
|
||||
host_cpu = "x64"
|
||||
elif arch in ("aarch64", "arm64"):
|
||||
host_cpu = "arm64"
|
||||
else:
|
||||
fail("bazel/toolchains/msvc: unsupported exec host CPU for the msvc cross toolchain: " + rctx.os.arch)
|
||||
return host_os + "-" + host_cpu
|
||||
|
||||
_BUILD = """\
|
||||
# Generated by //bazel/toolchains/msvc:llvm.bzl — pruned LLVM {version} for the
|
||||
# msvc cross toolchain ({host} exec host). Consumed by @msvc_cc wrappers.
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
name = "bin",
|
||||
srcs = glob(["bin/*"]),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "builtin_headers",
|
||||
srcs = glob(["lib/clang/*/include/**"]),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all",
|
||||
srcs = [
|
||||
":bin",
|
||||
":builtin_headers",
|
||||
],
|
||||
)
|
||||
"""
|
||||
|
||||
def _llvm_msvc_tools_impl(rctx):
|
||||
key = _host_key(rctx)
|
||||
if key not in _LLVM_DISTS:
|
||||
fail("bazel/toolchains/msvc: no pinned LLVM release archive for host " + key)
|
||||
suffix, sha256 = _LLVM_DISTS[key]
|
||||
prefix = "LLVM-{}-{}".format(_LLVM_VERSION, suffix)
|
||||
rctx.report_progress("Downloading LLVM {} ({}, ~2 GiB, repository-cache backed)".format(_LLVM_VERSION, suffix))
|
||||
rctx.download_and_extract(
|
||||
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-{}/{}.tar.xz".format(_LLVM_VERSION, prefix),
|
||||
sha256 = sha256,
|
||||
stripPrefix = prefix,
|
||||
)
|
||||
|
||||
# Close the keep-set over symlink targets, then prune bin/.
|
||||
keep = {name: None for name in _KEEP_BINS}
|
||||
bin_dir = rctx.path("bin")
|
||||
for name in _KEEP_BINS:
|
||||
tool = bin_dir.get_child(name)
|
||||
if tool.exists:
|
||||
keep[tool.realpath.basename] = None
|
||||
for entry in bin_dir.readdir():
|
||||
if entry.basename not in keep:
|
||||
rctx.delete(entry)
|
||||
|
||||
# Prune everything outside bin/ and lib/clang/<ver>/include.
|
||||
for entry in rctx.path(".").readdir():
|
||||
if entry.basename not in ("bin", "lib"):
|
||||
rctx.delete(entry)
|
||||
lib_dir = rctx.path("lib")
|
||||
for entry in lib_dir.readdir():
|
||||
if entry.basename != "clang":
|
||||
rctx.delete(entry)
|
||||
for verdir in lib_dir.get_child("clang").readdir():
|
||||
for entry in verdir.readdir():
|
||||
if entry.basename != "include":
|
||||
rctx.delete(entry)
|
||||
|
||||
rctx.file("BUILD.bazel", _BUILD.format(version = _LLVM_VERSION, host = key), executable = False)
|
||||
|
||||
llvm_msvc_tools_repository = repository_rule(
|
||||
implementation = _llvm_msvc_tools_impl,
|
||||
doc = "Pruned LLVM release binaries (clang-cl/lld-link/llvm-lib/llvm-rc) for the exec host.",
|
||||
)
|
||||
@@ -0,0 +1,156 @@
|
||||
"""Repository rule materializing the MSVC CRT + Windows SDK sysroot via xwin.
|
||||
|
||||
Downloads a pinned xwin release binary (github.com/Jake-Shadle/xwin) for the
|
||||
host that fetches the repo and runs `xwin --accept-license splat`, producing:
|
||||
|
||||
splat/crt/include VC toolset headers
|
||||
splat/crt/lib/x86_64 VC toolset libs (msvcrt.lib & co)
|
||||
splat/sdk/include/{ucrt,um,shared,winrt,cppwinrt}
|
||||
splat/sdk/lib/{ucrt,um}/x86_64
|
||||
|
||||
Cache implications: only the small xwin binary tarball goes through Bazel's
|
||||
repository cache. The CRT/SDK payload (~1 GiB) is downloaded from the
|
||||
Microsoft CDN by xwin itself inside this rule, so a cold fetch (clean
|
||||
--expunge, sysroot.bzl edit, new output base) re-downloads it. The xwin
|
||||
download cache is deleted after splatting to keep the repo at ~800 MiB.
|
||||
Keep this file stable; wrapper/toolchain iteration lives in cc.bzl precisely
|
||||
so it never invalidates this repo.
|
||||
|
||||
Reproducibility: --manifest-version pins the VS channel (17 = VS2022), but
|
||||
Microsoft advances the channel's payload over time, so the splat is stable
|
||||
day-to-day, not bit-reproducible forever — same property the cargo-xwin
|
||||
pipeline had.
|
||||
|
||||
xwin release binaries stop shipping darwin builds after 0.6.5; 0.6.5 is the
|
||||
newest version covering linux-musl + darwin hosts, which is why it is pinned.
|
||||
"""
|
||||
|
||||
_XWIN_VERSION = "0.6.5"
|
||||
_XWIN_MANIFEST_VERSION = "17"
|
||||
|
||||
# host key -> (release triple, sha256), from the published .sha256 assets.
|
||||
_XWIN_DISTS = {
|
||||
"linux-arm64": ("aarch64-unknown-linux-musl", "5e131007fad7c5f30d2f41090b49937fb8f16a787e5a95b4b3140e88d174dab2"),
|
||||
"linux-x64": ("x86_64-unknown-linux-musl", "9fd53950b064d067f42428a69453b927656cae68dbd7f8d3f86dcb81c80dd22d"),
|
||||
"macos-arm64": ("aarch64-apple-darwin", "21acd1e35d23b72efc8c47cbeda5028989f98089174b8c87074f892b65adbc01"),
|
||||
"macos-x64": ("x86_64-apple-darwin", "ecb2b19b30fa3786749fae600ad60b034b0c1c6a604ecf9f35f682c23a40e54b"),
|
||||
}
|
||||
|
||||
def _host_key(rctx):
|
||||
os_name = rctx.os.name.lower()
|
||||
arch = rctx.os.arch.lower()
|
||||
if os_name.startswith("linux"):
|
||||
host_os = "linux"
|
||||
elif os_name.startswith("mac") or os_name.startswith("darwin"):
|
||||
host_os = "macos"
|
||||
else:
|
||||
fail("bazel/toolchains/msvc: unsupported exec host OS for xwin: " + rctx.os.name)
|
||||
if arch in ("amd64", "x86_64", "x64"):
|
||||
host_cpu = "x64"
|
||||
elif arch in ("aarch64", "arm64"):
|
||||
host_cpu = "arm64"
|
||||
else:
|
||||
fail("bazel/toolchains/msvc: unsupported exec host CPU for xwin: " + rctx.os.arch)
|
||||
return host_os + "-" + host_cpu
|
||||
|
||||
_BUILD = """\
|
||||
# Generated by //bazel/toolchains/msvc:sysroot.bzl — MSVC CRT + Windows SDK
|
||||
# splatted by xwin {version} (manifest-version {manifest}). Consumed by the
|
||||
# @msvc_cc wrappers via /imsvc and /libpath.
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
name = "crt_includes",
|
||||
srcs = glob(["splat/crt/include/**"]),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "crt_libs",
|
||||
srcs = glob(["splat/crt/lib/**"]),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "sdk_includes",
|
||||
srcs = glob(["splat/sdk/include/**"]),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "sdk_libs",
|
||||
srcs = glob(["splat/sdk/lib/**"]),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "sysroot",
|
||||
srcs = [
|
||||
":crt_includes",
|
||||
":crt_libs",
|
||||
":sdk_includes",
|
||||
":sdk_libs",
|
||||
],
|
||||
)
|
||||
"""
|
||||
|
||||
def _xwin_sysroot_impl(rctx):
|
||||
key = _host_key(rctx)
|
||||
if key not in _XWIN_DISTS:
|
||||
fail("bazel/toolchains/msvc: no pinned xwin release binary for host " + key)
|
||||
triple, sha256 = _XWIN_DISTS[key]
|
||||
prefix = "xwin-{}-{}".format(_XWIN_VERSION, triple)
|
||||
rctx.download_and_extract(
|
||||
url = "https://github.com/Jake-Shadle/xwin/releases/download/{}/{}.tar.gz".format(_XWIN_VERSION, prefix),
|
||||
sha256 = sha256,
|
||||
stripPrefix = prefix,
|
||||
)
|
||||
rctx.report_progress("Splatting MSVC CRT + Windows SDK via xwin (first fetch ~1 GiB from the Microsoft CDN)")
|
||||
result = rctx.execute(
|
||||
[
|
||||
rctx.path("xwin"),
|
||||
"--accept-license",
|
||||
"--arch",
|
||||
"x86_64",
|
||||
"--variant",
|
||||
"desktop",
|
||||
"--manifest-version",
|
||||
_XWIN_MANIFEST_VERSION,
|
||||
"--cache-dir",
|
||||
".xwin-cache",
|
||||
"splat",
|
||||
"--copy",
|
||||
"--output",
|
||||
"splat",
|
||||
],
|
||||
timeout = 3600,
|
||||
)
|
||||
if result.return_code != 0:
|
||||
fail("bazel/toolchains/msvc: xwin splat failed (exit {}):\n{}\n{}".format(
|
||||
result.return_code,
|
||||
result.stdout,
|
||||
result.stderr,
|
||||
))
|
||||
|
||||
# Drop the download cache (the splat is a --copy, not links into it).
|
||||
rctx.delete(".xwin-cache")
|
||||
|
||||
# xwin drops `<sdk-version> -> .` alias symlinks (e.g. sdk/lib/10.0.26100)
|
||||
# so version-qualified include/lib paths resolve. They are self-referential
|
||||
# directory cycles, which Bazel's glob refuses to traverse — and nothing in
|
||||
# this toolchain uses version-qualified paths, so drop them.
|
||||
for dirname in ("splat/sdk/include", "splat/sdk/lib", "splat/crt/lib", "splat/crt/include"):
|
||||
dirpath = rctx.path(dirname)
|
||||
if not dirpath.exists:
|
||||
continue
|
||||
for entry in dirpath.readdir():
|
||||
if entry.realpath == dirpath.realpath:
|
||||
rctx.delete(entry)
|
||||
|
||||
rctx.file(
|
||||
"BUILD.bazel",
|
||||
_BUILD.format(version = _XWIN_VERSION, manifest = _XWIN_MANIFEST_VERSION),
|
||||
executable = False,
|
||||
)
|
||||
|
||||
xwin_sysroot_repository = repository_rule(
|
||||
implementation = _xwin_sysroot_impl,
|
||||
doc = "MSVC CRT + Windows SDK sysroot splatted by a pinned xwin release.",
|
||||
)
|
||||
@@ -0,0 +1,99 @@
|
||||
# Rust target-triple config_settings for crate_universe's rendered selects
|
||||
# (MODULE.bazel sets platforms_template = "@@//bazel/triples:{triple}").
|
||||
#
|
||||
# rules_rust's builtin @rules_rust//rust/platform settings can't express musl:
|
||||
# gnu and musl triples share (os, cpu), so the libc axis rides on
|
||||
# @zig_sdk//libc. The gnu settings match both the zig gnu.2.17 platforms and
|
||||
# libc-unconstrained platforms (host builds, GH runners); musl requires the
|
||||
# explicit constraint carried by //bazel/platforms:linux-musl-*.
|
||||
|
||||
load("@bazel_skylib//lib:selects.bzl", "selects")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
_LINUX_CPUS = {
|
||||
"x86_64": "x86_64",
|
||||
"aarch64": "aarch64",
|
||||
}
|
||||
|
||||
[
|
||||
config_setting(
|
||||
name = "_linux-{}-libc-default".format(cpu),
|
||||
constraint_values = [
|
||||
"@platforms//os:linux",
|
||||
"@platforms//cpu:" + cv,
|
||||
"@zig_sdk//libc:unconstrained",
|
||||
],
|
||||
)
|
||||
for cpu, cv in _LINUX_CPUS.items()
|
||||
]
|
||||
|
||||
[
|
||||
config_setting(
|
||||
name = "_linux-{}-libc-gnu217".format(cpu),
|
||||
constraint_values = [
|
||||
"@platforms//os:linux",
|
||||
"@platforms//cpu:" + cv,
|
||||
"@zig_sdk//libc:gnu.2.17",
|
||||
],
|
||||
)
|
||||
for cpu, cv in _LINUX_CPUS.items()
|
||||
]
|
||||
|
||||
selects.config_setting_group(
|
||||
name = "x86_64-unknown-linux-gnu",
|
||||
match_any = [
|
||||
":_linux-x86_64-libc-default",
|
||||
":_linux-x86_64-libc-gnu217",
|
||||
],
|
||||
)
|
||||
|
||||
selects.config_setting_group(
|
||||
name = "aarch64-unknown-linux-gnu",
|
||||
match_any = [
|
||||
":_linux-aarch64-libc-default",
|
||||
":_linux-aarch64-libc-gnu217",
|
||||
],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "x86_64-unknown-linux-musl",
|
||||
constraint_values = [
|
||||
"@platforms//os:linux",
|
||||
"@platforms//cpu:x86_64",
|
||||
"@zig_sdk//libc:musl",
|
||||
],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "aarch64-unknown-linux-musl",
|
||||
constraint_values = [
|
||||
"@platforms//os:linux",
|
||||
"@platforms//cpu:aarch64",
|
||||
"@zig_sdk//libc:musl",
|
||||
],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "x86_64-apple-darwin",
|
||||
constraint_values = [
|
||||
"@platforms//os:macos",
|
||||
"@platforms//cpu:x86_64",
|
||||
],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "aarch64-apple-darwin",
|
||||
constraint_values = [
|
||||
"@platforms//os:macos",
|
||||
"@platforms//cpu:arm64",
|
||||
],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "x86_64-pc-windows-msvc",
|
||||
constraint_values = [
|
||||
"@platforms//os:windows",
|
||||
"@platforms//cpu:x86_64",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
# x64 ISA variant axis for shipped addons: `baseline` (x86-64-v2) runs on any
|
||||
# 2010+ CPU, `modern` (x86-64-v3) needs AVX2. Non-x64 targets use `default`.
|
||||
# The constraint rides on //bazel/platforms:* and drives -Ctarget-cpu selects
|
||||
# in //crates/pi-natives.
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
constraint_setting(
|
||||
name = "cpu_variant",
|
||||
default_constraint_value = ":default",
|
||||
)
|
||||
|
||||
constraint_value(
|
||||
name = "default",
|
||||
constraint_setting = ":cpu_variant",
|
||||
)
|
||||
|
||||
constraint_value(
|
||||
name = "baseline",
|
||||
constraint_setting = ":cpu_variant",
|
||||
)
|
||||
|
||||
constraint_value(
|
||||
name = "modern",
|
||||
constraint_setting = ":cpu_variant",
|
||||
)
|
||||
Reference in New Issue
Block a user