From 3332308dd9902420407da9280b29837935f86bb1 Mon Sep 17 00:00:00 2001 From: UebelAndre Date: Thu, 9 Apr 2026 07:36:10 -0700 Subject: [PATCH] Added `iso_date` and `channel` to `rust_toolchain`. --- rust/BUILD.bazel | 1 + rust/nightly_versions.bzl | 32 ++++++++ rust/private/repository_utils.bzl | 22 ++++-- rust/repositories.bzl | 42 +++++++++- rust/toolchain.bzl | 12 ++- .../windows_stdlib/windows_stdlib_test.bzl | 24 +----- util/fetch_shas/fetch_shas.py | 78 ++++++++++++++++++- 7 files changed, 178 insertions(+), 33 deletions(-) create mode 100644 rust/nightly_versions.bzl diff --git a/rust/BUILD.bazel b/rust/BUILD.bazel index f4edbd8b61..c15c0adf82 100644 --- a/rust/BUILD.bazel +++ b/rust/BUILD.bazel @@ -4,6 +4,7 @@ package(default_visibility = ["//visibility:public"]) exports_files([ "known_shas.bzl", + "nightly_versions.bzl", "repositories.bzl", "defs.bzl", "toolchain.bzl", diff --git a/rust/nightly_versions.bzl b/rust/nightly_versions.bzl new file mode 100644 index 0000000000..3a6a3a9f4f --- /dev/null +++ b/rust/nightly_versions.bzl @@ -0,0 +1,32 @@ +"""A module containing a mapping of nightly iso dates to Rust versions. + +This is a generated file -- see //util/fetch_shas +""" + +# Each entry marks the first tracked nightly date where the Rust +# version changed. To resolve a given iso_date, find the latest +# entry whose date is <= the target date. +NIGHTLY_VERSION_TRANSITIONS = { + "2023-09-19": "1.74.0", + "2023-10-05": "1.75.0", + "2023-11-16": "1.76.0", + "2023-12-28": "1.77.0", + "2024-02-08": "1.78.0", + "2024-03-21": "1.79.0", + "2024-05-02": "1.80.0", + "2024-06-13": "1.81.0", + "2024-07-25": "1.82.0", + "2024-09-05": "1.83.0", + "2024-10-17": "1.84.0", + "2024-11-28": "1.85.0", + "2025-01-09": "1.86.0", + "2025-02-20": "1.87.0", + "2025-04-03": "1.88.0", + "2025-05-13": "1.89.0", + "2025-08-07": "1.91.0", + "2025-09-18": "1.92.0", + "2025-10-30": "1.93.0", + "2025-12-11": "1.94.0", + "2026-01-22": "1.95.0", + "2026-03-05": "1.96.0", +} diff --git a/rust/private/repository_utils.bzl b/rust/private/repository_utils.bzl index efbfa4afa4..ad8aea4814 100644 --- a/rust/private/repository_utils.bzl +++ b/rust/private/repository_utils.bzl @@ -369,6 +369,8 @@ rust_toolchain( opt_level = {opt_level}, strip_level = {strip_level}, version = "{version}", + channel = "{channel}", + iso_date = {iso_date}, tags = ["rust_version={version}"], ) """ @@ -378,12 +380,14 @@ def BUILD_for_rust_toolchain( exec_triple, target_triple, version, - allocator_library, - global_allocator_library, - default_edition, - include_rustfmt, - include_llvm_tools, - include_linker, + channel, + iso_date = None, + allocator_library = None, + global_allocator_library = None, + default_edition = "", + include_rustfmt = False, + include_llvm_tools = False, + include_linker = False, include_objcopy = False, stdlib_linkflags = None, extra_rustc_flags = None, @@ -396,7 +400,9 @@ def BUILD_for_rust_toolchain( name (str): The name of the toolchain declaration exec_triple (triple): The rust-style target that this compiler runs on target_triple (triple): The rust-style target triple of the tool - version (str): The Rust version for the toolchain. + version (str): The semver Rust version for the toolchain (e.g. `1.94.1`). + channel (str): The Rust release channel (`stable`, `nightly`, or `beta`). + iso_date (str, optional): The ISO date of the nightly or beta release (e.g. `2026-03-26`). allocator_library (str, optional): Target that provides allocator functions when rust_library targets are embedded in a cc_binary. global_allocator_library (str, optional): Target that provides allocator functions when a global allocator is used with cc_common_link. This target is only used in the target configuration; exec builds still use the symbols provided @@ -476,6 +482,8 @@ def BUILD_for_rust_toolchain( opt_level = opt_level, strip_level = strip_level, version = version, + channel = channel, + iso_date = repr(iso_date), ) _build_file_for_toolchain_template = """\ diff --git a/rust/repositories.bzl b/rust/repositories.bzl index ea78cdb96b..e4bd37f4c6 100644 --- a/rust/repositories.bzl +++ b/rust/repositories.bzl @@ -5,6 +5,7 @@ Repository rules for defining Rust dependencies and toolchains. load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") +load("//rust:nightly_versions.bzl", "NIGHTLY_VERSION_TRANSITIONS") load("//rust/platform:triple.bzl", "get_host_triple", "triple") load("//rust/platform:triple_mappings.bzl", "triple_to_constraint_set") load("//rust/private:common.bzl", "rust_common") @@ -447,6 +448,32 @@ def _include_rust_objcopy(version, iso_date): return False +def _resolve_nightly_version(iso_date): + """Resolve a nightly iso_date to its underlying Rust semver version. + + First tries a direct lookup, then falls back to scanning sorted + transition dates for the last entry whose date is <= the requested + iso_date. + + Args: + iso_date (str): The nightly ISO date (e.g. "2026-03-26"). + + Returns: + str: The resolved Rust version (e.g. "1.96.0"), or None if + the date precedes all tracked transitions. + """ + direct = NIGHTLY_VERSION_TRANSITIONS.get(iso_date) + if direct: + return direct + + result = None + for transition_date in sorted(NIGHTLY_VERSION_TRANSITIONS): + if transition_date <= iso_date: + result = NIGHTLY_VERSION_TRANSITIONS[transition_date] + else: + break + return result + def _rust_toolchain_tools_repository_impl(ctx): """The implementation of the rust toolchain tools repository rule.""" sha256s = dict(ctx.attr.sha256s) @@ -459,6 +486,17 @@ def _rust_toolchain_tools_repository_impl(ctx): check_version_valid(ctx.attr.version, iso_date) + if version in ("nightly", "beta"): + channel = version + else: + channel = "stable" + + toolchain_version = version + if channel == "nightly" and iso_date: + resolved = _resolve_nightly_version(iso_date) + if resolved: + toolchain_version = resolved + exec_triple = triple(ctx.attr.exec_triple) include_linker = True @@ -556,7 +594,9 @@ def _rust_toolchain_tools_repository_impl(ctx): extra_exec_rustc_flags = ctx.attr.extra_exec_rustc_flags, opt_level = ctx.attr.opt_level if ctx.attr.opt_level else None, strip_level = ctx.attr.strip_level if ctx.attr.strip_level else None, - version = ctx.attr.version, + version = toolchain_version, + channel = channel, + iso_date = iso_date, )) # Not all target triples are expected to have dev components diff --git a/rust/toolchain.bzl b/rust/toolchain.bzl index 74883231b8..10465b5a78 100644 --- a/rust/toolchain.bzl +++ b/rust/toolchain.bzl @@ -571,6 +571,7 @@ def _rust_toolchain_impl(ctx): all_files = depset(transitive = all_files_depsets), binary_ext = ctx.attr.binary_ext, cargo = sysroot.cargo, + channel = ctx.attr.channel, clippy_driver = sysroot.clippy, cargo_clippy = sysroot.cargo_clippy, compilation_mode_opts = compilation_mode_opts, @@ -578,6 +579,7 @@ def _rust_toolchain_impl(ctx): dylib_ext = ctx.attr.dylib_ext, env = ctx.attr.env, exec_triple = exec_triple, + iso_date = ctx.attr.iso_date, libstd_and_allocator_ccinfo = make_local_ccinfo(ctx.attr.allocator_library[CcInfo], "std"), libstd_and_global_allocator_ccinfo = make_local_ccinfo(ctx.attr.global_allocator_library[CcInfo], "std"), nostd_and_global_allocator_ccinfo = make_local_ccinfo(ctx.attr.global_allocator_library[CcInfo], "no_std_with_alloc"), @@ -657,6 +659,10 @@ rust_toolchain = rule( allow_single_file = True, cfg = "exec", ), + "channel": attr.string( + doc = "The Rust release channel (`stable`, `nightly`, or `beta`).", + default = "", + ), "clippy_driver": attr.label( doc = "The location of the `clippy-driver` binary. Can be a direct source or a filegroup containing one item.", allow_single_file = True, @@ -724,6 +730,10 @@ rust_toolchain = rule( doc = "Target that provides allocator functions for when a global allocator is present.", default = Label("//rust/private/cc:global_allocator_library"), ), + "iso_date": attr.string( + doc = "The ISO date of the nightly or beta release (e.g. `2026-03-26`). Empty for stable releases.", + default = "", + ), "linker": attr.label( doc = "The label to an explicit linker to use (e.g. rust-lld, ld, link-ld.exe, etc.). Linker binaries must be runnable in the exec configuration, so cfg = \"exec\" is used. To choose a linker based on the target platform, use a select() when providing this attribute. The select() will be evaluated against the target platform before the exec transition is applied, allowing platform-specific linker selection while ensuring the selected linker is built for the exec platform.", cfg = "exec", @@ -844,7 +854,7 @@ rust_toolchain = rule( ), ), "version": attr.string( - doc = "The version of the Rust compiler. (E.g. `1.94.1`, nightly/2026-03-26`)", + doc = "The version of the Rust compiler (e.g. `1.94.1`).", default = "", ), "_codegen_units": attr.label( diff --git a/test/unit/windows_stdlib/windows_stdlib_test.bzl b/test/unit/windows_stdlib/windows_stdlib_test.bzl index c32255eece..d8c9978cac 100644 --- a/test/unit/windows_stdlib/windows_stdlib_test.bzl +++ b/test/unit/windows_stdlib/windows_stdlib_test.bzl @@ -46,34 +46,14 @@ def _build_for_rust_toolchain_windows_flags_test_impl(ctx): exec_triple = msvc_triple, target_triple = msvc_triple, version = "1.75.0", - allocator_library = None, - global_allocator_library = None, - default_edition = "2021", - include_rustfmt = False, - include_llvm_tools = False, - include_linker = False, - stdlib_linkflags = None, - extra_rustc_flags = None, - extra_exec_rustc_flags = None, - opt_level = None, - strip_level = None, + channel = "stable", ) rendered_gnu = BUILD_for_rust_toolchain( name = "tc_gnu", exec_triple = gnu_triple, target_triple = gnu_triple, version = "1.75.0", - allocator_library = None, - global_allocator_library = None, - default_edition = "2021", - include_rustfmt = False, - include_llvm_tools = False, - include_linker = False, - stdlib_linkflags = None, - extra_rustc_flags = None, - extra_exec_rustc_flags = None, - opt_level = None, - strip_level = None, + channel = "stable", ) asserts.true( diff --git a/util/fetch_shas/fetch_shas.py b/util/fetch_shas/fetch_shas.py index 3c0d1eff1b..6cc5ba8465 100755 --- a/util/fetch_shas/fetch_shas.py +++ b/util/fetch_shas/fetch_shas.py @@ -1,16 +1,17 @@ #!/usr/bin/env python3.11 -"""A script for generating the `//rust:known_shas.bzl` file.""" +"""A script for generating the `//rust:known_shas.bzl` and `//rust:nightly_versions.bzl` files.""" import json import logging import os +import re import shutil import subprocess import sys import tempfile import tomllib from pathlib import Path -from typing import Any, Dict, Sequence +from typing import Any, Dict, List, Sequence, Tuple KNOWN_SHAS_TEMPLATE = """\ \"\"\"A module containing a mapping of Rust tools to checksums @@ -21,6 +22,20 @@ FILE_KEY_TO_SHA = {} """ +NIGHTLY_VERSIONS_TEMPLATE = """\ +\"\"\"A module containing a mapping of nightly iso dates to Rust versions. + +This is a generated file -- see //util/fetch_shas +\"\"\" + +# Each entry marks the first tracked nightly date where the Rust +# version changed. To resolve a given iso_date, find the latest +# entry whose date is <= the target date. +NIGHTLY_VERSION_TRANSITIONS = {{ +{} +}} +""" + def download_manifest_data( stable_versions: Sequence[str], nightly_versions: Sequence[str], output_dir: Path @@ -214,6 +229,53 @@ def download_direct_sha256s( } +def extract_nightly_version_transitions( + manifest_data: Dict[str, Dict[str, Any]], +) -> List[Tuple[str, str]]: + """Extract a transition table mapping nightly iso dates to Rust versions. + + Only includes entries where the version changed from the previous entry, + keeping the table compact. + + Args: + manifest_data: The parsed manifest data from download_manifest_data. + + Returns: + A sorted list of (iso_date, version) tuples at transition points. + """ + nightly_info = manifest_data.get("nightly", {}) + date_to_version = {} + + for iso_date, info in nightly_info.items(): + rustc_pkg = info.get("pkg", {}).get("rustc", {}) + rustc_version_str = rustc_pkg.get("version", "") + if not rustc_version_str: + logging.warning("No rustc version found for nightly %s", iso_date) + continue + + match = re.match(r"^(\d+\.\d+\.\d+)", rustc_version_str) + if not match: + logging.warning( + "Could not parse version from %r for nightly %s", + rustc_version_str, + iso_date, + ) + continue + + date_to_version[iso_date] = match.group(1) + + sorted_dates = sorted(date_to_version.keys()) + transitions = [] + prev_version = None + for date in sorted_dates: + version = date_to_version[date] + if version != prev_version: + transitions.append((date, version)) + prev_version = version + + return transitions + + def load_data(file: Path) -> Sequence[str]: """Load a `fetch_shas_*.txt` file @@ -357,6 +419,11 @@ def main() -> None: ) ) + nightly_transitions = extract_nightly_version_transitions(manifest_data) + logging.info( + "Identified %s nightly version transitions.", len(nightly_transitions) + ) + finally: if not "RULES_RUST_FETCH_SHAS_DEBUG" in os.environ: shutil.rmtree(tmp_dir) @@ -371,6 +438,13 @@ def main() -> None: ) logging.info("Done. Wrote %s", known_shas_file.relative_to(workspace_dir)) + nightly_versions_file = workspace_dir / "rust/nightly_versions.bzl" + transitions_str = "\n".join( + ' "{}": "{}",'.format(date, ver) for date, ver in nightly_transitions + ) + nightly_versions_file.write_text(NIGHTLY_VERSIONS_TEMPLATE.format(transitions_str)) + logging.info("Done. Wrote %s", nightly_versions_file.relative_to(workspace_dir)) + if __name__ == "__main__": main()