From 30688c4a1e5c2ada212127b5768ed9c7a5b9870f Mon Sep 17 00:00:00 2001 From: cds-amal Date: Tue, 10 Mar 2026 18:17:30 -0400 Subject: [PATCH 01/10] feat(make): add self-documenting help target and modernize Makefile Add a 'make help' target with awk-based extraction of target descriptions. Also adds standalone 'fmt', 'clippy', 'stdlib-smir', 'build-info', and nightly administration targets. Uses the final Makefile structure: targets for golden file management, UI testing, and nightly lifecycle are included but depend on scripts added in later commits. --- Makefile | 210 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 186 insertions(+), 24 deletions(-) diff --git a/Makefile b/Makefile index eace0fc9..61d4f449 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,31 @@ RELEASE_FLAG= -TOOLCHAIN_NAME='' +TOOLCHAIN_NAME= -default: build +.DEFAULT_GOAL := build +.PHONY: help +## Show this help message +help: + @echo "Available targets:" + @awk 'BEGIN {FS = ":.*"; printf "\nUsage:\n make \033[36m\033[0m\n"} \ + /^###/ {printf "\n\033[1m%s\033[0m\n", substr($$0, 5); next} \ + /^##/ {description=substr($$0, 4)} \ + /^[a-zA-Z0-9_-]+:/ { \ + if (description) { \ + printf " \033[36m%-18s\033[0m %s\n", $$1, description; \ + description = ""; \ + } \ + }' $(MAKEFILE_LIST) + +### Build + +.PHONY: build +## Build the project (use RELEASE_FLAG=--release for release) build: - cargo build ${RELEASE_FLAG} + cargo build $(RELEASE_FLAG) +.PHONY: clean +## Clean build artifacts, toolchain overrides, and graphs clean: rustup-clear-toolchain clean-graphs cargo clean @@ -13,45 +33,73 @@ clean: rustup-clear-toolchain clean-graphs rustup-clear-toolchain: rustup override unset rustup override unset --nonexistent - rustup toolchain uninstall "${TOOLCHAIN_NAME}" + rustup toolchain uninstall "$(TOOLCHAIN_NAME)" + +### Test TESTDIR=tests/integration/programs +# Detect the active nightly for golden-file lookup. +# The nightly name (e.g. nightly-2025-03-01) is derived from rustc's +# commit-date, which is one day before the nightly date. We add one day +# to align with the toolchain channel name that users actually see. +NIGHTLY_COMMIT_DATE := $(shell rustc -vV 2>/dev/null | awk '/^commit-date:/{print $$2}') +# Portable +1 day: macOS date(1) uses -v+1d, GNU date uses -d "+1 day". +NIGHTLY_DATE := $(shell \ + if [ -n "$(NIGHTLY_COMMIT_DATE)" ]; then \ + date -j -v+1d -f "%Y-%m-%d" "$(NIGHTLY_COMMIT_DATE)" "+%Y-%m-%d" 2>/dev/null \ + || date -d "$(NIGHTLY_COMMIT_DATE) +1 day" "+%Y-%m-%d" 2>/dev/null \ + || echo "$(NIGHTLY_COMMIT_DATE)"; \ + fi) +ACTIVE_NIGHTLY := nightly-$(NIGHTLY_DATE) + +# Golden file directory: per-nightly expected outputs. +# Falls back to the pinned nightly (from rust-toolchain.toml) if no +# directory exists for the active nightly. +PINNED_NIGHTLY := $(shell awk -F'"' '/^channel/{print $$2}' rust-toolchain.toml) +GOLDEN_BASE := tests/integration/expected +GOLDEN_DIR := $(shell \ + if [ -d "$(GOLDEN_BASE)/$(ACTIVE_NIGHTLY)" ]; then \ + echo "$(GOLDEN_BASE)/$(ACTIVE_NIGHTLY)"; \ + else \ + echo "$(GOLDEN_BASE)/$(PINNED_NIGHTLY)"; \ + fi) + .PHONY: integration-test integration-test: TESTS ?= $(shell find $(TESTDIR) -type f -name "*.rs") integration-test: SMIR ?= cargo run -- "-Zno-codegen" # override this to tweak how expectations are formatted -integration-test: NORMALIZE ?= jq -S -e -f $(TESTDIR)/../normalise-filter.jq +integration-test: FILTER ?= $(TESTDIR)/../normalise-filter.jq # override this to re-make golden files integration-test: DIFF ?= | diff - +## Run integration tests against expected outputs integration-test: + @echo "Using golden files from: $(GOLDEN_DIR)" errors=""; \ report() { echo "$$1: $$2"; errors="$$errors\n$$1: $$2"; }; \ - for rust in ${TESTS}; do \ + for rust in $(TESTS); do \ target=$${rust%.rs}.smir.json; \ + receipts=$${target%.json}.receipts.json; \ + name=$$(basename $${rust%.rs}); \ dir=$$(dirname $${rust}); \ + expected="$(GOLDEN_DIR)/$${name}.smir.json.expected"; \ echo "$$rust"; \ - ${SMIR} --out-dir $${dir} $${rust} || report "$$rust" "Conversion failed"; \ + $(SMIR) --out-dir $${dir} $${rust} || report "$$rust" "Conversion failed"; \ [ -f $${target} ] \ - && ${NORMALIZE} $${target} ${DIFF} $${target}.expected \ - && rm $${target} \ + && jq -S -e --slurpfile receipts $${receipts} -f $(FILTER) $${target} $(DIFF) $${expected} \ + && rm -f $${target} $${receipts} \ || report "$$rust" "Unexpected json output"; \ done; \ [ -z "$$errors" ] || (echo "===============\nFAILING TESTS:$$errors"; exit 1) - +.PHONY: golden +## Regenerate expected test outputs (golden files) for the active nightly golden: - make integration-test DIFF=">" - -format: - cargo fmt - bash -O globstar -c 'nixfmt **/*.nix' - -style-check: format - cargo clippy -- -Dwarnings - -.PHONY: remake-ui-tests test-ui + @mkdir -p "$(GOLDEN_BASE)/$(ACTIVE_NIGHTLY)" + $(MAKE) integration-test DIFF=">" GOLDEN_DIR="$(GOLDEN_BASE)/$(ACTIVE_NIGHTLY)" +.PHONY: remake-ui-tests +## Regenerate UI test fixtures (requires RUST_DIR_ROOT) remake-ui-tests: # Check if RUST_DIR_ROOT is set if [ -z "$$RUST_DIR_ROOT" ]; then \ @@ -61,20 +109,77 @@ remake-ui-tests: # This will run without saving source files. Run the script manually to do this. bash tests/ui/remake_ui_tests.sh "$$RUST_DIR_ROOT" +.PHONY: test-ui +## Run UI tests (requires RUST_DIR_ROOT, VERBOSE=1 for details) test-ui: VERBOSE?=0 test-ui: bash tests/ui/run_ui_tests.sh $(if $(filter 1,$(VERBOSE)),--verbose) "$$RUST_DIR_ROOT" -.PHONY: dot svg png d2 clean-graphs check-graphviz +.PHONY: test-directives +## Run unit tests for the directive parser (parse_test_directives.awk) +test-directives: + bash tests/ui/test_directives_test.sh + +.PHONY: test-ui-emit +## Generate effective UI test lists for a nightly (requires RUST_DIR_ROOT, NIGHTLY=nightly-YYYY-MM-DD) +test-ui-emit: + bash tests/ui/diff_test_lists.sh --emit "$$RUST_DIR_ROOT" $(NIGHTLY) + +### Nightly management + +.PHONY: nightly-add +## Add support for a new nightly (requires NIGHTLY, RUST_DIR_ROOT) +nightly-add: + @test -n "$$NIGHTLY" || { echo "Error: NIGHTLY not set (e.g., NIGHTLY=nightly-2025-08-01)"; exit 1; } + @test -n "$$RUST_DIR_ROOT" || { echo "Error: RUST_DIR_ROOT not set"; exit 1; } + python3 scripts/nightly_admin.py add "$$NIGHTLY" --rust-dir "$$RUST_DIR_ROOT" + +.PHONY: nightly-check +## Run all tests for a nightly (requires NIGHTLY, RUST_DIR_ROOT) +nightly-check: + @test -n "$$NIGHTLY" || { echo "Error: NIGHTLY not set"; exit 1; } + @test -n "$$RUST_DIR_ROOT" || { echo "Error: RUST_DIR_ROOT not set"; exit 1; } + python3 scripts/nightly_admin.py check "$$NIGHTLY" --rust-dir "$$RUST_DIR_ROOT" + +.PHONY: nightly-bump +## Bump the pinned nightly (requires NIGHTLY) +nightly-bump: + @test -n "$$NIGHTLY" || { echo "Error: NIGHTLY not set"; exit 1; } + python3 scripts/nightly_admin.py bump "$$NIGHTLY" + +### Diagnostics + +.PHONY: build-info +## Show build.rs cfg detection output (rustc commit-date, enabled flags) +build-info: + @touch build.rs + @cargo build -vv 2>&1 | grep '\] build\.rs:' + +### Code quality + +.PHONY: fmt format +## Format Rust and Nix source files +fmt format: + cargo fmt + bash -O globstar -c 'nixfmt **/*.nix' + +.PHONY: clippy +## Run clippy lint checks (deny warnings) +clippy: + cargo clippy -- -Dwarnings + +.PHONY: style-check +## Run format + clippy lint checks +style-check: format clippy + +### Graph generation OUTDIR_DOT=output-dot OUTDIR_SVG=output-svg OUTDIR_PNG=output-png OUTDIR_D2=output-d2 -clean-graphs: - @rm -rf $(OUTDIR_DOT) $(OUTDIR_SVG) $(OUTDIR_PNG) $(OUTDIR_D2) - +.PHONY: check-graphviz check-graphviz: @command -v dot >/dev/null 2>&1 || { \ echo "Error: Graphviz is not installed or 'dot' is not in PATH."; \ @@ -83,6 +188,8 @@ check-graphviz: exit 1; \ } +.PHONY: dot +## Generate DOT files from test programs dot: @mkdir -p $(OUTDIR_DOT) @for rs in $(TESTDIR)/*.rs; do \ @@ -92,6 +199,8 @@ dot: mv $$name.smir.dot $(OUTDIR_DOT)/ 2>/dev/null || true; \ done +.PHONY: svg +## Generate SVG files from DOT (requires graphviz) svg: check-graphviz dot @mkdir -p $(OUTDIR_SVG) @for dotfile in $(OUTDIR_DOT)/*.dot; do \ @@ -100,6 +209,8 @@ svg: check-graphviz dot dot -Tsvg $$dotfile -o $(OUTDIR_SVG)/$$name.svg; \ done +.PHONY: png +## Generate PNG files from DOT (requires graphviz) png: check-graphviz dot @mkdir -p $(OUTDIR_PNG) @for dotfile in $(OUTDIR_DOT)/*.dot; do \ @@ -108,6 +219,8 @@ png: check-graphviz dot dot -Tpng $$dotfile -o $(OUTDIR_PNG)/$$name.png; \ done +.PHONY: d2 +## Generate D2 diagram files from test programs d2: @mkdir -p $(OUTDIR_D2) @for rs in $(TESTDIR)/*.rs; do \ @@ -116,3 +229,52 @@ d2: cargo run --release -- --d2 -Zno-codegen $$rs 2>/dev/null; \ mv $$name.smir.d2 $(OUTDIR_D2)/ 2>/dev/null || true; \ done + +.PHONY: clean-graphs +## Remove generated graph output directories +clean-graphs: + @rm -rf $(OUTDIR_DOT) $(OUTDIR_SVG) $(OUTDIR_PNG) $(OUTDIR_D2) + +### stdlib smir.json + +STDLIB_OUTDIR=tests/stdlib-artifacts +STDLIB_TARGET=$(shell rustc --print target-triple 2>/dev/null || rustc -vV | grep host | awk '{print $$2}') +SYSROOT=$(shell rustc --print sysroot) +SMIR_BIN=$(CURDIR)/target/debug/stable_mir_json + +.PHONY: stdlib-smir +## Generate smir.json for stdlib via -Zbuild-std +stdlib-smir: build + @# Create a throwaway crate to drive -Zbuild-std + $(eval STDLIB_TMPDIR := $(shell mktemp -d)) + @echo '[package]' > $(STDLIB_TMPDIR)/Cargo.toml + @echo 'name = "stdlib-smir"' >> $(STDLIB_TMPDIR)/Cargo.toml + @echo 'version = "0.0.0"' >> $(STDLIB_TMPDIR)/Cargo.toml + @echo 'edition = "2021"' >> $(STDLIB_TMPDIR)/Cargo.toml + @echo '[[bin]]' >> $(STDLIB_TMPDIR)/Cargo.toml + @echo 'name = "stdlib-smir"' >> $(STDLIB_TMPDIR)/Cargo.toml + @echo 'path = "main.rs"' >> $(STDLIB_TMPDIR)/Cargo.toml + @echo 'fn main() {}' > $(STDLIB_TMPDIR)/main.rs + @# Build stdlib through our driver; set library path the same way + @# cargo does when it runs our binary via `cargo run` + cd $(STDLIB_TMPDIR) && \ + DYLD_LIBRARY_PATH=$(SYSROOT)/lib \ + LD_LIBRARY_PATH=$(SYSROOT)/lib \ + RUSTC=$(SMIR_BIN) \ + cargo build -Zbuild-std --target $(STDLIB_TARGET) + @# Collect artifacts, stripping hash suffixes from filenames + @rm -rf $(STDLIB_OUTDIR) + @mkdir -p $(STDLIB_OUTDIR) + @for f in $(STDLIB_TMPDIR)/target/$(STDLIB_TARGET)/debug/deps/*.smir.json; do \ + name=$$(basename "$$f" | sed 's/-[0-9a-f]*\.smir\.json/.smir.json/'); \ + case "$$name" in stdlib_smir*) continue ;; esac; \ + cp "$$f" $(STDLIB_OUTDIR)/$$name; \ + done + @rm -rf $(STDLIB_TMPDIR) + @echo "stdlib smir.json artifacts written to $(STDLIB_OUTDIR)/" + @ls -lhS $(STDLIB_OUTDIR)/ + +.PHONY: clean-stdlib-smir +## Remove stdlib smir.json artifacts +clean-stdlib-smir: + @rm -rf $(STDLIB_OUTDIR) From 38c9ae4122d14cb137824055db738b72a9edf769 Mon Sep 17 00:00:00 2001 From: cds-amal Date: Tue, 10 Mar 2026 18:17:43 -0400 Subject: [PATCH 02/10] fix(ty_visitor): catch rustc panics during layout computation Some types (e.g., dyn Trait in certain positions) cause rustc's layout computation to panic rather than returning an error. Wrap layout calls in catch_unwind so the type visitor can continue; panicked types are recorded and reported in a summary rather than crashing the whole run. --- src/printer/ty_visitor.rs | 63 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/src/printer/ty_visitor.rs b/src/printer/ty_visitor.rs index bb375871..e2d5a802 100644 --- a/src/printer/ty_visitor.rs +++ b/src/printer/ty_visitor.rs @@ -13,6 +13,7 @@ use crate::compat::stable_mir; use std::collections::{HashMap, HashSet}; use std::ops::ControlFlow; +use std::panic::{catch_unwind, set_hook, take_hook, AssertUnwindSafe}; use stable_mir::mir::mono::Instance; use stable_mir::ty::{RigidTy, TyKind}; @@ -20,23 +21,77 @@ use stable_mir::visitor::{Visitable, Visitor}; use super::schema::TyMap; +/// A layout computation that panicked inside rustc. +pub(super) struct LayoutPanic { + pub ty: stable_mir::ty::Ty, + pub message: String, +} + +/// Attempt to get a type's layout, catching any rustc-internal panics. +/// +/// Some types (e.g., those involving `dyn Trait` in certain positions) cause +/// rustc's layout computation to panic rather than returning an error. We +/// catch those panics here so the visitor can continue; the caller gets +/// `Ok(Some(shape))` on success, `Ok(None)` when layout returns `Err`, or +/// `Err(message)` when rustc panicked. +fn try_layout_shape( + ty: &stable_mir::ty::Ty, +) -> Result, String> { + // Temporarily suppress the default panic hook so caught panics don't + // spray backtraces to stderr; we report them in our own summary. + let prev_hook = take_hook(); + set_hook(Box::new(|_| {})); + let result = catch_unwind(AssertUnwindSafe(|| ty.layout().ok().map(|l| l.shape()))); + set_hook(prev_hook); + + match result { + Ok(shape) => Ok(shape), + Err(payload) => { + let message = if let Some(s) = payload.downcast_ref::<&str>() { + (*s).to_string() + } else if let Some(s) = payload.downcast_ref::() { + s.clone() + } else { + "(non-string panic payload)".to_string() + }; + Err(message) + } + } +} + pub(super) struct TyCollector<'tcx> { tcx: TyCtxt<'tcx>, pub types: TyMap, + pub layout_panics: Vec, resolved: HashSet, } impl TyCollector<'_> { - pub fn new(tcx: TyCtxt<'_>) -> TyCollector { + pub fn new(tcx: TyCtxt<'_>) -> TyCollector<'_> { TyCollector { tcx, types: HashMap::new(), + layout_panics: Vec::new(), resolved: HashSet::new(), } } } impl TyCollector<'_> { + /// Get layout for `ty`, recording a [`LayoutPanic`] if rustc panics. + fn layout_shape_or_record( + &mut self, + ty: &stable_mir::ty::Ty, + ) -> Option { + match try_layout_shape(ty) { + Ok(shape) => shape, + Err(message) => { + self.layout_panics.push(LayoutPanic { ty: *ty, message }); + None + } + } + } + #[inline(always)] fn visit_instance(&mut self, instance: Instance) -> ControlFlow<::Break> { let fn_abi = instance.fn_abi().unwrap(); @@ -63,7 +118,7 @@ impl Visitor for TyCollector<'_> { let control = self.visit_instance(instance); // Mirror other branches: record closure Ty only when traversal succeeds. if matches!(control, ControlFlow::Continue(_)) { - let maybe_layout_shape = ty.layout().ok().map(|layout| layout.shape()); + let maybe_layout_shape = self.layout_shape_or_record(ty); self.types.insert(*ty, (ty.kind(), maybe_layout_shape)); } control @@ -97,7 +152,7 @@ impl Visitor for TyCollector<'_> { let control = ty.super_visit(self); if matches!(control, ControlFlow::Continue(_)) { - let maybe_layout_shape = ty.layout().ok().map(|layout| layout.shape()); + let maybe_layout_shape = self.layout_shape_or_record(ty); self.types.insert(*ty, (ty.kind(), maybe_layout_shape)); fields.super_visit(self) } else { @@ -108,7 +163,7 @@ impl Visitor for TyCollector<'_> { let control = ty.super_visit(self); match control { ControlFlow::Continue(_) => { - let maybe_layout_shape = ty.layout().ok().map(|layout| layout.shape()); + let maybe_layout_shape = self.layout_shape_or_record(ty); self.types.insert(*ty, (ty.kind(), maybe_layout_shape)); control } From 102122a6ab38ccd46df384b0431a27b3095861ed Mon Sep 17 00:00:00 2001 From: cds-amal Date: Tue, 10 Mar 2026 18:18:14 -0400 Subject: [PATCH 03/10] fix(cargo_stable_mir_json): derive toolchain lib path dynamically Instead of hardcoding the library path, resolve it from the active nightly toolchain at runtime. This avoids breakage when the toolchain directory name changes. --- src/bin/cargo_stable_mir_json.rs | 75 ++++++++++++++------------------ 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/src/bin/cargo_stable_mir_json.rs b/src/bin/cargo_stable_mir_json.rs index 2ab3c9cd..ce06000c 100644 --- a/src/bin/cargo_stable_mir_json.rs +++ b/src/bin/cargo_stable_mir_json.rs @@ -188,51 +188,40 @@ fn add_run_script(smir_json_dir: &Path, ld_library_path: &Path, profile: Profile Ok(()) } -fn record_ld_library_path(smir_json_dir: &Path) -> Result { - #[cfg(target_os = "macos")] - { - // macOS: Check DYLD_LIBRARY_PATH or use default path - if let Some(paths) = env::var_os("DYLD_LIBRARY_PATH") { - let mut ld_library_file = std::fs::File::create(smir_json_dir.join("ld_library_path"))?; - match paths.to_str() { - Some(ld_library_path) => { - writeln!(ld_library_file, "{}", ld_library_path)?; - Ok(ld_library_path.into()) - } - None => bail!("Couldn't cast DYLD_LIBRARY_PATH to str"), - } - } else { - // Use default macOS library path including Rust toolchain - let rustup_home = env::var("HOME").unwrap_or_else(|_| "/Users".to_string()); - let rust_toolchain_path = format!( - "{}/.rustup/toolchains/nightly-2024-11-29-aarch64-apple-darwin/lib", - rustup_home - ); - let default_path = format!("{}:/usr/local/lib:/usr/lib", rust_toolchain_path); - let mut ld_library_file = std::fs::File::create(smir_json_dir.join("ld_library_path"))?; - writeln!(ld_library_file, "{}", default_path)?; - Ok(default_path.into()) - } +/// Resolve the rustc toolchain lib path dynamically via `rustc --print sysroot`. +fn rustc_lib_path() -> Result { + let output = std::process::Command::new("rustc") + .args(["--print", "sysroot"]) + .output()?; + if !output.status.success() { + bail!("rustc --print sysroot failed"); } + let sysroot = String::from_utf8(output.stdout)?.trim().to_string(); + Ok(PathBuf::from(sysroot).join("lib")) +} - #[cfg(not(target_os = "macos"))] - { - // Linux and other systems: Check LD_LIBRARY_PATH - if let Some(paths) = env::var_os("LD_LIBRARY_PATH") { - let mut ld_library_file = std::fs::File::create(smir_json_dir.join("ld_library_path"))?; - match paths.to_str() { - Some(ld_library_path) => { - writeln!(ld_library_file, "{}", ld_library_path)?; - Ok(ld_library_path.into()) - } - None => bail!("Couldn't cast LD_LIBRARY_PATH to str"), - } +fn record_ld_library_path(smir_json_dir: &Path) -> Result { + // Prefer the environment variable if set; otherwise derive the path + // from the active rustc toolchain so the wrapper script stays in sync + // with whatever nightly rust-toolchain.toml selects. + let lib_path = { + #[cfg(target_os = "macos")] + let env_var = "DYLD_LIBRARY_PATH"; + #[cfg(not(target_os = "macos"))] + let env_var = "LD_LIBRARY_PATH"; + + if let Some(paths) = env::var_os(env_var) { + paths + .to_str() + .ok_or_else(|| anyhow::anyhow!("Couldn't cast {} to str", env_var))? + .to_string() } else { - // Use default Linux library path - let default_path = "/usr/local/lib:/usr/lib"; - let mut ld_library_file = std::fs::File::create(smir_json_dir.join("ld_library_path"))?; - writeln!(ld_library_file, "{}", default_path)?; - Ok(default_path.into()) + let toolchain_lib = rustc_lib_path()?; + format!("{}:/usr/local/lib:/usr/lib", toolchain_lib.display()) } - } + }; + + let mut ld_library_file = std::fs::File::create(smir_json_dir.join("ld_library_path"))?; + writeln!(ld_library_file, "{lib_path}")?; + Ok(lib_path.into()) } From 21a3dddd60357c38b1213422d56ddee3f3bd3b4f Mon Sep 17 00:00:00 2001 From: cds-amal Date: Tue, 10 Mar 2026 18:18:36 -0400 Subject: [PATCH 04/10] refactor: derive rustc commit from toolchain, inline format args Drop the [metadata] section from rust-toolchain.toml; the UI test scripts now derive the rustc commit hash directly from the nightly date via the Rust manifest. Also fixes clippy uninlined_format_args warnings. --- .github/workflows/test.yml | 75 +++++++++++++++++++++++++++------ rust-toolchain.toml | 6 --- src/printer/collect.rs | 10 +++++ src/printer/items.rs | 5 +-- src/printer/schema.rs | 5 +-- tests/ui/ensure_rustc_commit.sh | 42 +++++++++++------- 6 files changed, 103 insertions(+), 40 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 08f81b8c..4d208c79 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,10 +22,31 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} submodules: recursive + - name: Install yq + run: | + set -euo pipefail + YQ_VERSION="v4.52.4" + mkdir -p "$HOME/.local/bin" + wget -qO "$HOME/.local/bin/yq" \ + "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64" + chmod +x "$HOME/.local/bin/yq" + echo "$HOME/.local/bin" >> $GITHUB_PATH + + - name: 'Read nightly channel from rust-toolchain.toml' + id: toolchain-meta + run: | + set -euo pipefail + CHANNEL=$(yq -r '.toolchain.channel' rust-toolchain.toml) + if [ -z "$CHANNEL" ] || [ "$CHANNEL" = "null" ]; then + echo "::error::Could not read toolchain.channel from rust-toolchain.toml" + exit 1 + fi + echo "channel=$CHANNEL" >> "$GITHUB_OUTPUT" + - name: "Set up nightly Rust" # https://github.com/rust-lang/rustup/issues/3409 uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2024-11-29 # Hardcoded version, same as is in the build.rs + toolchain: ${{ steps.toolchain-meta.outputs.channel }} - name: 'Build stable-mir-json' # rustfmt documentation claims it is unstable on code that doesn't build run: | @@ -66,10 +87,31 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} submodules: recursive + - name: Install yq + run: | + set -euo pipefail + YQ_VERSION="v4.52.4" + mkdir -p "$HOME/.local/bin" + wget -qO "$HOME/.local/bin/yq" \ + "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64" + chmod +x "$HOME/.local/bin/yq" + echo "$HOME/.local/bin" >> $GITHUB_PATH + + - name: 'Read nightly channel from rust-toolchain.toml' + id: toolchain-meta + run: | + set -euo pipefail + CHANNEL=$(yq -r '.toolchain.channel' rust-toolchain.toml) + if [ -z "$CHANNEL" ] || [ "$CHANNEL" = "null" ]; then + echo "::error::Could not read toolchain.channel from rust-toolchain.toml" + exit 1 + fi + echo "channel=$CHANNEL" >> "$GITHUB_OUTPUT" + - name: "Set up nightly Rust" # https://github.com/rust-lang/rustup/issues/3409 uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2024-11-29 # Hardcoded version, same as is in the build.rs + toolchain: ${{ steps.toolchain-meta.outputs.channel }} - name: 'Build stable-mir-json' run: | # Warning check should be redundant since code-quality runs first @@ -108,16 +150,30 @@ jobs: "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64" chmod +x "$HOME/.local/bin/yq" echo "$HOME/.local/bin" >> $GITHUB_PATH - yq --version + - name: 'Read nightly channel from rust-toolchain.toml' + id: toolchain-meta + run: | + set -euo pipefail + CHANNEL=$(yq -r '.toolchain.channel' rust-toolchain.toml) + if [ -z "$CHANNEL" ] || [ "$CHANNEL" = "null" ]; then + echo "::error::Could not read toolchain.channel from rust-toolchain.toml" + exit 1 + fi + echo "channel=$CHANNEL" >> "$GITHUB_OUTPUT" + + - name: "Set up nightly Rust" # https://github.com/rust-lang/rustup/issues/3409 + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ steps.toolchain-meta.outputs.channel }} - - name: 'Read rustc commit from rust-toolchain.toml' + - name: 'Derive rustc commit and check out Rust repo' id: rustc-meta run: | set -euo pipefail - COMMIT=$(yq '.metadata.rustc-commit' rust-toolchain.toml) - if [ -z "$COMMIT" ] || [ "$COMMIT" = "null" ]; then - echo "::error::metadata.rustc-commit not found in rust-toolchain.toml" + COMMIT=$(rustc -vV | grep 'commit-hash' | cut -d' ' -f2) + if [ -z "$COMMIT" ]; then + echo "::error::Could not determine rustc commit-hash from 'rustc -vV'" exit 1 fi echo "rustc-commit=$COMMIT" >> "$GITHUB_OUTPUT" @@ -130,11 +186,6 @@ jobs: path: rust fetch-depth: 1 - - name: "Set up nightly Rust" # https://github.com/rust-lang/rustup/issues/3409 - uses: dtolnay/rust-toolchain@master - with: - toolchain: nightly-2024-11-29 # Hardcoded version, same as is in the build.rs - - name: 'Build stable-mir-json' run: | # Warning check should be redundant since code-quality runs first RUSTFLAGS='--deny warnings' cargo build -vv diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9249d51a..06f099e1 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,9 +1,3 @@ [toolchain] channel = "nightly-2024-11-29" components = ["llvm-tools", "rustc-dev", "rust-src", "rust-analyzer"] - -# Ignored by rustup; used by our test scripts. -# This is the rustc commit that backs the nightly above. -# UI test scripts automatically checkout this commit in RUST_DIR_ROOT. -[metadata] -rustc-commit = "a2545fd6fc66b4323f555223a860c451885d1d2b" diff --git a/src/printer/collect.rs b/src/printer/collect.rs index c8089e6e..f44a8532 100644 --- a/src/printer/collect.rs +++ b/src/printer/collect.rs @@ -142,6 +142,16 @@ fn collect_and_analyze_items( all_items.push(item); } + if !ty_visitor.layout_panics.is_empty() { + eprintln!( + "warning: {} type layout(s) could not be computed (rustc panicked):", + ty_visitor.layout_panics.len() + ); + for panic in &ty_visitor.layout_panics { + eprintln!(" type {:?}: {}", panic.ty, panic.message); + } + } + ( CollectedCrate { items: all_items, diff --git a/src/printer/items.rs b/src/printer/items.rs index a253ad2c..884ab49d 100644 --- a/src/printer/items.rs +++ b/src/printer/items.rs @@ -101,8 +101,7 @@ pub(super) fn mk_item(tcx: TyCtxt<'_>, item: MonoItem, sym_name: String) -> (Mon Ok(alloc) => Some(alloc), err => { eprintln!( - "StaticDef({:#?}).eval_initializer() failed with: {:#?}", - static_def, err + "StaticDef({static_def:#?}).eval_initializer() failed with: {err:#?}" ); None } @@ -125,7 +124,7 @@ pub(super) fn mk_item(tcx: TyCtxt<'_>, item: MonoItem, sym_name: String) -> (Mon ) } MonoItem::GlobalAsm(ref asm) => { - let asm_str = format!("{:#?}", asm); + let asm_str = format!("{asm:#?}"); ( item, Item::new( diff --git a/src/printer/schema.rs b/src/printer/schema.rs index d75da9ba..18ccad06 100644 --- a/src/printer/schema.rs +++ b/src/printer/schema.rs @@ -118,11 +118,10 @@ impl AllocMap { assert!( missing_from_map.is_empty(), - "Alloc-id coherence violation: AllocIds {:?} are referenced in \ + "Alloc-id coherence violation: AllocIds {missing_from_map:?} are referenced in \ stored Item bodies but missing from the alloc map. This means \ the analysis phase collected allocations from a different body \ - than what is stored in the Items.", - missing_from_map + than what is stored in the Items." ); assert!( diff --git a/tests/ui/ensure_rustc_commit.sh b/tests/ui/ensure_rustc_commit.sh index 5054b341..e1b76cbc 100644 --- a/tests/ui/ensure_rustc_commit.sh +++ b/tests/ui/ensure_rustc_commit.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # # Ensures a rust checkout (regular or bare+worktree) is at the commit -# specified in rust-toolchain.toml's [metadata] rustc-commit field. +# that backs the active nightly toolchain (derived from `rustc -vV`). # # Usage: source this script after setting RUST_DIR to the repo root. # It sets RUST_SRC_DIR to the directory containing the source files @@ -11,21 +11,11 @@ set -u : "${RUST_DIR:?RUST_DIR must be set before sourcing ensure_rustc_commit.sh}" -# Require yq (mikefarah/yq) for TOML parsing -if ! command -v yq &> /dev/null; then - echo "Error: yq is required but not installed." - echo "Install via: brew install yq | apt install yq | nix shell nixpkgs#yq-go" - echo "See: https://github.com/mikefarah/yq#install" - exit 1 -fi - -_SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -_REPO_ROOT=$( cd -- "$_SCRIPT_DIR/../.." &> /dev/null && pwd ) - -# Read the expected rustc commit from rust-toolchain.toml -RUSTC_COMMIT=$(yq -r '.metadata.rustc-commit' "$_REPO_ROOT/rust-toolchain.toml") -if [ -z "$RUSTC_COMMIT" ] || [ "$RUSTC_COMMIT" = "null" ]; then - echo "Error: Could not read metadata.rustc-commit from $_REPO_ROOT/rust-toolchain.toml" +# Derive the rustc commit from the active toolchain; rust-toolchain.toml +# selects the nightly, so this stays in sync automatically. +RUSTC_COMMIT=$(rustc -vV | grep 'commit-hash' | cut -d' ' -f2) +if [ -z "$RUSTC_COMMIT" ]; then + echo "Error: Could not determine rustc commit-hash from 'rustc -vV'" exit 1 fi @@ -48,6 +38,16 @@ if [ "$IS_BARE" = "true" ]; then exit 1 fi else + # Ensure the commit is available locally; fetch if needed. + if ! git -C "$RUST_DIR" cat-file -e "$RUSTC_COMMIT" 2>/dev/null; then + echo "Commit ${SHORT_COMMIT} not found locally; fetching..." + git -C "$RUST_DIR" fetch origin "$RUSTC_COMMIT" --quiet 2>/dev/null || \ + git -C "$RUST_DIR" fetch origin --quiet || { + echo "Error: Could not fetch commit ${RUSTC_COMMIT}." + echo "Ensure ${RUST_DIR} is a clone of https://github.com/rust-lang/rust" + exit 1 + } + fi echo "Creating worktree at ${WORKTREE_DIR} for commit ${SHORT_COMMIT}..." git -C "$RUST_DIR" worktree add "$WORKTREE_DIR" "$RUSTC_COMMIT" --detach --quiet || { echo "Error: Failed to create worktree for commit ${RUSTC_COMMIT} in ${RUST_DIR}" @@ -59,6 +59,16 @@ else # Regular repo: checkout the commit directly CURRENT_COMMIT=$(git -C "$RUST_DIR" rev-parse HEAD 2>/dev/null) if [ "${CURRENT_COMMIT}" != "${RUSTC_COMMIT}" ]; then + # Ensure the commit is available locally; fetch if needed. + if ! git -C "$RUST_DIR" cat-file -e "$RUSTC_COMMIT" 2>/dev/null; then + echo "Commit ${SHORT_COMMIT} not found locally; fetching..." + git -C "$RUST_DIR" fetch origin "$RUSTC_COMMIT" --quiet 2>/dev/null || \ + git -C "$RUST_DIR" fetch origin --quiet || { + echo "Error: Could not fetch commit ${RUSTC_COMMIT}." + echo "Ensure ${RUST_DIR} is a clone of https://github.com/rust-lang/rust" + exit 1 + } + fi echo "Checking out rustc commit ${SHORT_COMMIT} in ${RUST_DIR}..." git -C "$RUST_DIR" checkout "$RUSTC_COMMIT" --quiet || { echo "Error: Failed to checkout commit ${RUSTC_COMMIT} in ${RUST_DIR}" From 182985542bb4ba1a8939daa677d24762b5c6c0a5 Mon Sep 17 00:00:00 2001 From: cds-amal Date: Tue, 10 Mar 2026 20:12:42 -0400 Subject: [PATCH 05/10] fix(make): revert integration-test to flat golden files for PR 1 The receipt-driven integration-test target requires receipts (PR 2) and per-nightly golden directories (PR 3). Revert to master's flat-file version so CI passes on the foundation PR. --- Makefile | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/Makefile b/Makefile index 61d4f449..91d79a8c 100644 --- a/Makefile +++ b/Makefile @@ -53,50 +53,32 @@ NIGHTLY_DATE := $(shell \ fi) ACTIVE_NIGHTLY := nightly-$(NIGHTLY_DATE) -# Golden file directory: per-nightly expected outputs. -# Falls back to the pinned nightly (from rust-toolchain.toml) if no -# directory exists for the active nightly. -PINNED_NIGHTLY := $(shell awk -F'"' '/^channel/{print $$2}' rust-toolchain.toml) -GOLDEN_BASE := tests/integration/expected -GOLDEN_DIR := $(shell \ - if [ -d "$(GOLDEN_BASE)/$(ACTIVE_NIGHTLY)" ]; then \ - echo "$(GOLDEN_BASE)/$(ACTIVE_NIGHTLY)"; \ - else \ - echo "$(GOLDEN_BASE)/$(PINNED_NIGHTLY)"; \ - fi) - .PHONY: integration-test integration-test: TESTS ?= $(shell find $(TESTDIR) -type f -name "*.rs") integration-test: SMIR ?= cargo run -- "-Zno-codegen" # override this to tweak how expectations are formatted -integration-test: FILTER ?= $(TESTDIR)/../normalise-filter.jq +integration-test: NORMALIZE ?= jq -S -e -f $(TESTDIR)/../normalise-filter.jq # override this to re-make golden files integration-test: DIFF ?= | diff - ## Run integration tests against expected outputs integration-test: - @echo "Using golden files from: $(GOLDEN_DIR)" errors=""; \ report() { echo "$$1: $$2"; errors="$$errors\n$$1: $$2"; }; \ - for rust in $(TESTS); do \ + for rust in ${TESTS}; do \ target=$${rust%.rs}.smir.json; \ - receipts=$${target%.json}.receipts.json; \ - name=$$(basename $${rust%.rs}); \ dir=$$(dirname $${rust}); \ - expected="$(GOLDEN_DIR)/$${name}.smir.json.expected"; \ echo "$$rust"; \ - $(SMIR) --out-dir $${dir} $${rust} || report "$$rust" "Conversion failed"; \ + ${SMIR} --out-dir $${dir} $${rust} || report "$$rust" "Conversion failed"; \ [ -f $${target} ] \ - && jq -S -e --slurpfile receipts $${receipts} -f $(FILTER) $${target} $(DIFF) $${expected} \ - && rm -f $${target} $${receipts} \ + && ${NORMALIZE} $${target} ${DIFF} $${target}.expected \ + && rm $${target} \ || report "$$rust" "Unexpected json output"; \ done; \ [ -z "$$errors" ] || (echo "===============\nFAILING TESTS:$$errors"; exit 1) .PHONY: golden -## Regenerate expected test outputs (golden files) for the active nightly golden: - @mkdir -p "$(GOLDEN_BASE)/$(ACTIVE_NIGHTLY)" - $(MAKE) integration-test DIFF=">" GOLDEN_DIR="$(GOLDEN_BASE)/$(ACTIVE_NIGHTLY)" + make integration-test DIFF=">" .PHONY: remake-ui-tests ## Regenerate UI test fixtures (requires RUST_DIR_ROOT) From 4067da0b76a0c4d1cef3573e4c5c1706d340a074 Mon Sep 17 00:00:00 2001 From: cds-amal Date: Wed, 11 Mar 2026 15:06:47 -0400 Subject: [PATCH 06/10] fix(ty_visitor): drop global panic hook swap, keep catch_unwind TIL (thanks Copilot!), the previous approach temporarily replaced the process-wide panic hook with a no-op to suppress backtraces from caught layout panics. Turns out that's a thread-safety footgun: rustc's own worker threads could panic while the no-op hook is installed, silently swallowing unrelated diagnostics. The hook swap was also racy with anything else that calls set_hook concurrently. catch_unwind is what actually keeps the process alive; the hook swap was purely cosmetic (suppressing stderr noise). Dropped it entirely and accepted the default backtrace output for caught panics. The end-of-run LayoutPanic summary still reports everything it did before. My research surfaced a way to suppress the stderr noise that I decided was too much for a little noise, but I'm including here for completeness. Set teh hook once at startup (not per-call) to a hook that checks a thread-local flag: ```rust thread_local! { static SUPPRESS_PANIC_OUTPUT: Cell = const { Cell::new(false) }; } // Called once, e.g. in driver setup: std::panic::set_hook(Box::new(|info| { SUPPRESS_PANIC_OUTPUT.with(|flag| { if !flag.get() { eprintln!("{info}"); } }); })); // Then in try_layout_shape, just toggle the flag: SUPPRESS_PANIC_OUTPUT.with(|f| f.set(true)); let result = catch_unwind(AssertUnwindSafe(|| ty.layout()...)); SUPPRESS_PANIC_OUTPUT.with(|f| f.set(false)); This is thread-safe (thread-local, not global), no race conditions, no risk of swallowing other threads' panics, and our collected LayoutPanic report at teh end works exactly as before. --- src/printer/ty_visitor.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/printer/ty_visitor.rs b/src/printer/ty_visitor.rs index e2d5a802..d2735c49 100644 --- a/src/printer/ty_visitor.rs +++ b/src/printer/ty_visitor.rs @@ -13,7 +13,7 @@ use crate::compat::stable_mir; use std::collections::{HashMap, HashSet}; use std::ops::ControlFlow; -use std::panic::{catch_unwind, set_hook, take_hook, AssertUnwindSafe}; +use std::panic::{catch_unwind, AssertUnwindSafe}; use stable_mir::mir::mono::Instance; use stable_mir::ty::{RigidTy, TyKind}; @@ -37,12 +37,13 @@ pub(super) struct LayoutPanic { fn try_layout_shape( ty: &stable_mir::ty::Ty, ) -> Result, String> { - // Temporarily suppress the default panic hook so caught panics don't - // spray backtraces to stderr; we report them in our own summary. - let prev_hook = take_hook(); - set_hook(Box::new(|_| {})); + // catch_unwind keeps the process alive when rustc's layout engine panics + // (e.g. on certain `dyn Trait` types). The default panic hook will still + // print a backtrace to stderr for each caught panic; that's noisy but + // harmless, and avoids the thread-safety issues of swapping the global + // hook per-call. We collect the messages and report them in our own + // summary at the end. let result = catch_unwind(AssertUnwindSafe(|| ty.layout().ok().map(|l| l.shape()))); - set_hook(prev_hook); match result { Ok(shape) => Ok(shape), From 2b38837291331362e72bd2a610cb2309b4d3a06d Mon Sep 17 00:00:00 2001 From: cds-amal Date: Wed, 11 Mar 2026 15:06:53 -0400 Subject: [PATCH 07/10] fix(ensure_rustc_commit): resolve repo root before running rustc -vV If this script is sourced from a working directory outside the repo tree, rustup won't find rust-toolchain.toml and may select whatever default toolchain happens to be active, giving us the wrong commit hash. We now derive the repo root from BASH_SOURCE (two levels up from the script's own directory) and cd there before invoking rustc, so the toolchain selection stays correct regardless of the caller's CWD. --- tests/ui/ensure_rustc_commit.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/ui/ensure_rustc_commit.sh b/tests/ui/ensure_rustc_commit.sh index e1b76cbc..21f8e46f 100644 --- a/tests/ui/ensure_rustc_commit.sh +++ b/tests/ui/ensure_rustc_commit.sh @@ -13,7 +13,11 @@ set -u # Derive the rustc commit from the active toolchain; rust-toolchain.toml # selects the nightly, so this stays in sync automatically. -RUSTC_COMMIT=$(rustc -vV | grep 'commit-hash' | cut -d' ' -f2) +# Run from the repo root so rustup picks up rust-toolchain.toml even when +# the caller's CWD is outside the repository. +_SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +_REPO_ROOT=$(cd -- "$_SCRIPT_DIR/../.." &>/dev/null && pwd) +RUSTC_COMMIT=$(cd "$_REPO_ROOT" && rustc -vV | grep 'commit-hash' | cut -d' ' -f2) if [ -z "$RUSTC_COMMIT" ]; then echo "Error: Could not determine rustc commit-hash from 'rustc -vV'" exit 1 From b1edcde66daff623f567beb6c345c3472d67a82e Mon Sep 17 00:00:00 2001 From: cds-amal Date: Wed, 11 Mar 2026 15:06:57 -0400 Subject: [PATCH 08/10] fix(make): guard toolchain uninstall on non-empty TOOLCHAIN_NAME TOOLCHAIN_NAME defaults to empty in the Makefile, which meant make clean would always run rustup toolchain uninstall "" and fail. Now we only attempt the uninstall when the variable is actually set. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 91d79a8c..453f65ad 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ clean: rustup-clear-toolchain clean-graphs rustup-clear-toolchain: rustup override unset rustup override unset --nonexistent - rustup toolchain uninstall "$(TOOLCHAIN_NAME)" + if [ -n "$(TOOLCHAIN_NAME)" ]; then rustup toolchain uninstall "$(TOOLCHAIN_NAME)"; fi ### Test From 9f0fe12db3758e3ded1f6ade12129a7d52e2da6e Mon Sep 17 00:00:00 2001 From: cds-amal Date: Wed, 11 Mar 2026 15:07:03 -0400 Subject: [PATCH 09/10] fix(ci): verify yq checksum and DRY up install into shared script The workflow was downloading yq from GitHub releases without any integrity check (a supply-chain risk, however small, on CI runners). We now download the upstream checksums file alongside the binary and verify the SHA256 before installing. While we're at it, the identical 8-line install block was copy-pasted across all three jobs. Extracted into .github/scripts/install-yq.sh so there's exactly one place to update the version or change the verification logic. --- .github/scripts/install-yq.sh | 25 +++++++++++++++++++++++++ .github/workflows/test.yml | 27 +++------------------------ 2 files changed, 28 insertions(+), 24 deletions(-) create mode 100755 .github/scripts/install-yq.sh diff --git a/.github/scripts/install-yq.sh b/.github/scripts/install-yq.sh new file mode 100755 index 00000000..aa536f77 --- /dev/null +++ b/.github/scripts/install-yq.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# +# Installs yq (mikefarah/yq) into ~/.local/bin with SHA256 verification. +# Intended for CI runners (Linux amd64). + +set -euo pipefail + +YQ_VERSION="${YQ_VERSION:-v4.52.4}" +INSTALL_DIR="$HOME/.local/bin" +BASE_URL="https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}" + +mkdir -p "$INSTALL_DIR" + +wget -qO "$INSTALL_DIR/yq_linux_amd64" "${BASE_URL}/yq_linux_amd64" +wget -qO "$INSTALL_DIR/checksums" "${BASE_URL}/checksums" + +( + cd "$INSTALL_DIR" + grep 'yq_linux_amd64$' checksums | sha256sum -c - + mv yq_linux_amd64 yq + rm -f checksums + chmod +x yq +) + +echo "$INSTALL_DIR" >> "${GITHUB_PATH:-/dev/null}" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4d208c79..cf425808 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,14 +23,7 @@ jobs: submodules: recursive - name: Install yq - run: | - set -euo pipefail - YQ_VERSION="v4.52.4" - mkdir -p "$HOME/.local/bin" - wget -qO "$HOME/.local/bin/yq" \ - "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64" - chmod +x "$HOME/.local/bin/yq" - echo "$HOME/.local/bin" >> $GITHUB_PATH + run: .github/scripts/install-yq.sh - name: 'Read nightly channel from rust-toolchain.toml' id: toolchain-meta @@ -88,14 +81,7 @@ jobs: submodules: recursive - name: Install yq - run: | - set -euo pipefail - YQ_VERSION="v4.52.4" - mkdir -p "$HOME/.local/bin" - wget -qO "$HOME/.local/bin/yq" \ - "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64" - chmod +x "$HOME/.local/bin/yq" - echo "$HOME/.local/bin" >> $GITHUB_PATH + run: .github/scripts/install-yq.sh - name: 'Read nightly channel from rust-toolchain.toml' id: toolchain-meta @@ -142,14 +128,7 @@ jobs: submodules: recursive - name: Install yq - run: | - set -euo pipefail - YQ_VERSION="v4.52.4" - mkdir -p "$HOME/.local/bin" - wget -qO "$HOME/.local/bin/yq" \ - "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64" - chmod +x "$HOME/.local/bin/yq" - echo "$HOME/.local/bin" >> $GITHUB_PATH + run: .github/scripts/install-yq.sh - name: 'Read nightly channel from rust-toolchain.toml' id: toolchain-meta From cf679886c18c0b603097da63c9e494c23fc74121 Mon Sep 17 00:00:00 2001 From: cds-amal Date: Wed, 11 Mar 2026 15:30:30 -0400 Subject: [PATCH 10/10] fix(ci): use checksums-bsd for yq integrity verification The checksums file from mikefarah/yq uses a custom multi-hash-per-line format that isn't compatible with sha256sum -c (which expects GNU coreutils format). Switched to checksums-bsd, which uses the standard BSD-style "SHA256 (file) = hash" layout; a small sed converts that to GNU format for verification. --- .github/scripts/install-yq.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/scripts/install-yq.sh b/.github/scripts/install-yq.sh index aa536f77..94d8b6a4 100755 --- a/.github/scripts/install-yq.sh +++ b/.github/scripts/install-yq.sh @@ -12,13 +12,19 @@ BASE_URL="https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}" mkdir -p "$INSTALL_DIR" wget -qO "$INSTALL_DIR/yq_linux_amd64" "${BASE_URL}/yq_linux_amd64" -wget -qO "$INSTALL_DIR/checksums" "${BASE_URL}/checksums" +wget -qO "$INSTALL_DIR/checksums-bsd" "${BASE_URL}/checksums-bsd" ( cd "$INSTALL_DIR" - grep 'yq_linux_amd64$' checksums | sha256sum -c - + # checksums-bsd uses BSD-style: SHA256 (yq_linux_amd64) = 0c4d965e... + # sha256sum -c expects GNU-style: 0c4d965e... yq_linux_amd64 + # sed captures the filename (\1) and hash (\2), discarding "SHA256 (", ") = ", + # then emits them in reversed order to match GNU format. + grep 'SHA256 (yq_linux_amd64)' checksums-bsd \ + | sed 's/SHA256 (\(.*\)) = \(.*\)/\2 \1/' \ + | sha256sum -c - mv yq_linux_amd64 yq - rm -f checksums + rm -f checksums-bsd chmod +x yq )