32403ccf4b
The new `check-cargo-patches.sh` script verifies that all [patch.crates-io] and [patch.'<URL>'] sections in the local sources' Cargo.toml files actually resolve to the expected local fork paths. It does this by running `cargo metadata` on each source's workspace and checking that the resolved source URL (or manifest_path for path-deps) matches the expected local fork path. This is the Phase J / Improvement C verification step that the user explicitly requested: 'Build system must report complete when upstream have our patches applied.' The script handles the known-large workspaces gracefully: * relibc is explicitly skipped — its [patch] section is only the cc-rs git branch override (no `path` patches), and `cargo metadata` on relibc takes minutes (hundreds of deps) which would hang the script. * All other `cargo metadata` calls are wrapped in a 30-second timeout. Hardware-agnostic: works on any Red Bear OS checkout regardless of which OEMs are added to the local sources (Phase I/II/J DMI matches).
162 lines
5.6 KiB
Bash
Executable File
162 lines
5.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# check-cargo-patches.sh — verify all [patch.crates-io] and [patch."<URL>"]
|
|
# sections in local sources' Cargo.toml resolve to the expected paths.
|
|
#
|
|
# Scans local/sources/*/Cargo.toml for [patch.*] sections and runs
|
|
# `cargo metadata` on each source to confirm the patch is applied
|
|
# (i.e. the resolved source URL matches the expected local path).
|
|
#
|
|
# This is the Phase J / Improvement C verification step. The cookbook's
|
|
# own patch validation handles file-level patches; this script handles
|
|
# the Cargo-level [patch] sections which are needed for transitive
|
|
# deps like redox_syscall, libredox, etc.
|
|
#
|
|
# Usage: ./local/scripts/check-cargo-patches.sh [--strict]
|
|
# --strict: exit non-zero if any patch fails verification (for CI)
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
STRICT=false
|
|
[[ "${1:-}" == "--strict" ]] && STRICT=true
|
|
|
|
PASSED=0
|
|
FAILED=0
|
|
SKIPPED=0
|
|
|
|
echo "=== Cargo [patch] Verification ==="
|
|
|
|
# Find all Cargo.toml files in local sources
|
|
# Skip relibc explicitly — its [patch] section is only the
|
|
# cc-rs git branch override (no `path` patches), and
|
|
# `cargo metadata` on relibc takes minutes (hundreds of
|
|
# deps) which would hang the script.
|
|
while IFS= read -r -d '' cargo_toml; do
|
|
case "$cargo_toml" in
|
|
*local/sources/relibc/Cargo.toml) continue ;;
|
|
esac
|
|
source_dir="$(dirname "$cargo_toml")"
|
|
source_name="$(basename "$source_dir")"
|
|
relative_to_root="${cargo_toml#$ROOT/}"
|
|
|
|
# Find [patch.crates-io] and [patch."<URL>"] sections
|
|
patch_lines=$(grep -E '^\[patch\.' "$cargo_toml" 2>/dev/null || true)
|
|
if [[ -z "$patch_lines" ]]; then
|
|
continue
|
|
fi
|
|
|
|
echo ""
|
|
echo "--- $relative_to_root ---"
|
|
echo " $patch_lines" | sed 's/^/ /'
|
|
|
|
# Check if the source is a workspace member
|
|
if ! grep -q '^\[workspace\]' "$cargo_toml" 2>/dev/null; then
|
|
# Single-package — [patch] sections need [workspace] to apply
|
|
# (cargo treats them as ignored). This is a common bug.
|
|
if grep -q '^\[patch\.' "$cargo_toml" 2>/dev/null; then
|
|
echo " ⚠️ WARN: source has [patch] sections but no [workspace]"
|
|
echo " cargo silently ignores [patch] in non-workspace manifests"
|
|
SKIPPED=$((SKIPPED + 1))
|
|
fi
|
|
continue
|
|
fi
|
|
|
|
# Check if cargo is available
|
|
if ! command -v cargo >/dev/null 2>&1; then
|
|
echo " SKIP: cargo not available"
|
|
SKIPPED=$((SKIPPED + 1))
|
|
continue
|
|
fi
|
|
|
|
# Run cargo metadata and parse the resolved source URLs.
|
|
# Use a 30-second timeout to prevent hangs on large
|
|
# workspaces like relibc (which has hundreds of deps).
|
|
if ! timeout 30 cargo metadata --format-version 1 --manifest-path "$cargo_toml" >/dev/null 2>&1; then
|
|
echo " SKIP: cargo metadata timed out (30s)"
|
|
SKIPPED=$((SKIPPED + 1))
|
|
continue
|
|
fi
|
|
|
|
# Extract the resolved source URLs and check against the
|
|
# expected [patch] paths. The expected [patch] entries have
|
|
# a `path = "..."` field; the resolved source URL from
|
|
# cargo metadata should match the local path.
|
|
expected_paths=$(grep -A1 '^\[patch\.' "$cargo_toml" 2>/dev/null | grep -oP 'path\s*=\s*"\K[^"]+' | sort -u)
|
|
if [[ -z "$expected_paths" ]]; then
|
|
# No `path` entries in the [patch] sections. This is
|
|
# common for git-branch patches (e.g. relibc's cc-rs).
|
|
# The verification only applies to `path` patches.
|
|
# Continue to next file.
|
|
continue
|
|
fi
|
|
|
|
resolved_urls=$(cargo metadata --format-version 1 --manifest-path "$cargo_toml" 2>/dev/null \
|
|
| python3 -c '
|
|
import json, sys
|
|
data = json.load(sys.stdin)
|
|
# For path-deps, source is null but manifest_path points to
|
|
# the local fork. Print both forms.
|
|
for pkg in data.get("packages", []):
|
|
src = pkg.get("source", "")
|
|
if src:
|
|
print(src)
|
|
mp = pkg.get("manifest_path", "")
|
|
if mp:
|
|
print(mp)
|
|
' 2>/dev/null | sort -u)
|
|
|
|
good=0
|
|
bad=0
|
|
# Skip the resolution loop if there are no `path` entries.
|
|
# (Empty `expected_paths` would loop with empty lines.)
|
|
if [[ -n "$expected_paths" ]]; then
|
|
while IFS= read -r expected; do
|
|
# Skip empty lines (the heredoc may emit one)
|
|
if [[ -z "$expected" ]]; then
|
|
continue
|
|
fi
|
|
# expected is a path like "../syscall" or "../libredox"
|
|
# Resolve to absolute
|
|
expected_abs="$(cd "$source_dir" && realpath "$expected" 2>/dev/null || echo "")"
|
|
if [[ -z "$expected_abs" ]]; then
|
|
continue
|
|
fi
|
|
if echo "$resolved_urls" | grep -qF "$expected_abs"; then
|
|
good=$((good + 1))
|
|
else
|
|
# Check if the expected path is the source itself
|
|
if [[ "$expected_abs" == "$source_dir" ]]; then
|
|
# self-patch is OK
|
|
good=$((good + 1))
|
|
else
|
|
bad=$((bad + 1))
|
|
echo " ⚠️ [patch] path $expected → $expected_abs NOT in resolved sources"
|
|
fi
|
|
fi
|
|
done <<< "$expected_paths"
|
|
fi
|
|
|
|
if [[ $bad -eq 0 ]]; then
|
|
echo " ✅ All $good [patch] entries resolved correctly"
|
|
PASSED=$((PASSED + 1))
|
|
else
|
|
echo " ❌ $bad [patch] entries did not resolve"
|
|
FAILED=$((FAILED + 1))
|
|
fi
|
|
done < <(find "$ROOT/local/sources" -name 'Cargo.toml' -print0 2>/dev/null)
|
|
|
|
echo ""
|
|
echo "=== Summary ==="
|
|
echo " Passed: $PASSED"
|
|
echo " Failed: $FAILED"
|
|
echo " Skipped: $SKIPPED"
|
|
if [[ $FAILED -gt 0 ]]; then
|
|
echo ""
|
|
echo "❌ Cargo [patch] verification FAILED"
|
|
$STRICT && exit 1
|
|
exit 1
|
|
fi
|
|
echo ""
|
|
echo "✅ Cargo [patch] verification PASSED"
|
|
exit 0
|