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
1 change: 1 addition & 0 deletions rust/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package(default_visibility = ["//visibility:public"])

exports_files([
"known_shas.bzl",
"nightly_versions.bzl",
"repositories.bzl",
"defs.bzl",
"toolchain.bzl",
Expand Down
32 changes: 32 additions & 0 deletions rust/nightly_versions.bzl
Original file line number Diff line number Diff line change
@@ -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",
}
22 changes: 15 additions & 7 deletions rust/private/repository_utils.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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}"],
)
"""
Expand All @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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 = """\
Expand Down
42 changes: 41 additions & 1 deletion rust/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
12 changes: 11 additions & 1 deletion rust/toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -571,13 +571,15 @@ 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,
default_edition = ctx.attr.default_edition,
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"),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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(
Expand Down
24 changes: 2 additions & 22 deletions test/unit/windows_stdlib/windows_stdlib_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
78 changes: 76 additions & 2 deletions util/fetch_shas/fetch_shas.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand All @@ -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()
Loading