-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Release/9.1 into maintenance-10.x #11793
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
27e6322
docs: seed RAM/flash optimization guide with PR #11718 technique
sensei-hacker 75aec88
Add CI PR comment for RAM/flash usage delta vs. base branch
sensei-hacker dc1dcb8
Fix GitHub Actions script-injection risk in size-report workflows
sensei-hacker bb4fa3f
Address code review: target-coverage wording, JSON escaping, test hyg…
sensei-hacker b09680f
Fix baseline-publish race flagged by Qodo review on PR #11790
sensei-hacker 1a953f6
Merge pull request #11791 from sensei-hacker/feature-ci-ram-flash-dif…
sensei-hacker 4029d98
Fix arm-none-eabi-size not found on real CI runners
sensei-hacker d69afcd
Merge pull request #11795 from sensei-hacker/feature-ci-ram-flash-dif…
sensei-hacker File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| #!/bin/bash | ||
| # | ||
| # Run the size tool on every built .elf and write a small JSON report of | ||
| # flash/RAM usage per target, consumed by ci-size-report.yml. | ||
| # | ||
| # Usage: extract-size-report.sh <build-dir> <output-json> [size-tool] | ||
| # | ||
| # flash = .text + .data (what's programmed into flash) | ||
| # ram = .data + .bss (what's reserved in RAM at runtime) | ||
| # | ||
| # Runs inside the (unprivileged) build job on the PR's own checkout, so a | ||
| # PR could in principle modify this script to misreport its own numbers. | ||
| # Accepted tradeoff: this feature is informational/non-gating, and a real | ||
| # overflow still fails the link step regardless of what this script says. | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| BUILD_DIR=${1:?usage: extract-size-report.sh <build-dir> <output-json> [size-tool]} | ||
| OUTPUT_JSON=${2:?usage: extract-size-report.sh <build-dir> <output-json> [size-tool]} | ||
| SIZE_TOOL=${3:-} | ||
|
|
||
| if [ -z "$SIZE_TOOL" ]; then | ||
| if command -v arm-none-eabi-size >/dev/null 2>&1; then | ||
| SIZE_TOOL=arm-none-eabi-size | ||
| else | ||
| # cmake/arm-none-eabi-checks.cmake downloads its own toolchain into | ||
| # tools/ and adds it to PATH — but only inside that cmake process | ||
| # via set(ENV{PATH} ...), which doesn't persist to a later CI step's | ||
| # shell. Fall back to the same location CMake would have used, | ||
| # relative to the repo root (this script must be run from there). | ||
| SIZE_TOOL=$(compgen -G 'tools/arm-gnu-toolchain-*/bin/arm-none-eabi-size' 2>/dev/null | head -n1 || true) | ||
| if [ -z "$SIZE_TOOL" ]; then | ||
| echo "::error::arm-none-eabi-size not found on PATH or under tools/arm-gnu-toolchain-*/bin/" >&2 | ||
| exit 1 | ||
| fi | ||
| fi | ||
| fi | ||
|
|
||
| # CMake's RUNTIME_OUTPUT_DIRECTORY puts built executables under | ||
| # <build-dir>/bin/ (see cmake/main.cmake), not <build-dir> directly — search | ||
| # instead of assuming a fixed depth, in case that ever changes. | ||
| mapfile -t ELFS < <(find "$BUILD_DIR" -maxdepth 3 -name '*.elf' | sort) | ||
| if [ "${#ELFS[@]}" -eq 0 ]; then | ||
| echo "::warning::No .elf files found under $BUILD_DIR, writing empty size report" | ||
| echo '{}' > "$OUTPUT_JSON" | ||
| exit 0 | ||
| fi | ||
|
|
||
| JQ_ARGS=() | ||
| for elf in "${ELFS[@]}"; do | ||
| target=$(basename "$elf" .elf) | ||
|
|
||
| # Berkeley format: " text data bss dec hex filename" | ||
| read -r text data bss _dec _hex _name < <("$SIZE_TOOL" -B "$elf" | tail -n1) | ||
|
|
||
| flash=$((text + data)) | ||
| ram=$((data + bss)) | ||
|
|
||
| JQ_ARGS+=(--argjson "entry_${#JQ_ARGS[@]}" "{\"target\":\"${target}\",\"flash\":${flash},\"ram\":${ram}}") | ||
| done | ||
|
|
||
| # Build via jq rather than manual string concatenation, so the target name | ||
| # (an .elf basename, not otherwise validated) is JSON-escaped properly | ||
| # instead of relying on it never containing a special character. | ||
| jq -n "${JQ_ARGS[@]}" 'reduce $ARGS.named[] as $e ({}; .[$e.target] = {flash: $e.flash, ram: $e.ram})' \ | ||
| > "$OUTPUT_JSON" | ||
|
|
||
| echo "Wrote size report for ${#ELFS[@]} target(s) to $OUTPUT_JSON" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| #!/bin/bash | ||
| # | ||
| # Merge multiple per-shard size-report.json files (see extract-size-report.sh) | ||
| # into one aggregate JSON, used by the "upload-artifacts" job in ci.yml. | ||
| # | ||
| # Usage: merge-size-reports.sh <output-json> <input-json>... | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| OUTPUT_JSON=${1:?usage: merge-size-reports.sh <output-json> <input-json>...} | ||
| shift | ||
|
|
||
| if [ "$#" -eq 0 ]; then | ||
| echo '{}' > "$OUTPUT_JSON" | ||
| exit 0 | ||
| fi | ||
|
|
||
| jq -s 'add' "$@" > "$OUTPUT_JSON" | ||
| echo "Merged $# size report(s) into $OUTPUT_JSON" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| // Pure logic for the "PR RAM/Flash usage delta" comment: diffs a PR's | ||
| // size-report.json against a base-branch baseline and renders the markdown | ||
| // comment body. Deliberately has no network/filesystem/GitHub Actions | ||
| // dependency so it can be unit tested directly and reused (via require) | ||
| // from the actions/github-script step in ci-size-report.yml. | ||
| // | ||
| // Report shape: { "<target>": { "flash": <bytes>, "ram": <bytes> }, ... } | ||
|
|
||
| 'use strict'; | ||
|
|
||
| // 4 representative targets spanning flash/RAM size tiers (manager-approved | ||
| // set, 2026-08-17). Note MATEKF722 and MATEKF765 are both STM32F7 parts — | ||
| // "one per family" in the loose sense of distinct flash/RAM budgets, not | ||
| // one per silicon line. No AT32 target is covered; flagged back to the | ||
| // manager as a possible coverage gap, not decided unilaterally here. | ||
| const REPRESENTATIVE_TARGETS = ['MATEKF405', 'MATEKF722', 'MATEKF765', 'MATEKH743']; | ||
|
|
||
| // Below this magnitude a delta is noise (rounding/toolchain jitter), not a | ||
| // real change worth calling out. | ||
| const NOISE_THRESHOLD_BYTES = 32; | ||
|
|
||
| function formatDelta(deltaBytes, baseBytes) { | ||
| const sign = deltaBytes > 0 ? '+' : deltaBytes < 0 ? '' : '±'; | ||
| const pct = baseBytes > 0 ? (deltaBytes / baseBytes) * 100 : 0; | ||
| const pctStr = baseBytes > 0 ? ` (${sign}${pct.toFixed(2)}%)` : ''; | ||
| return `${sign}${deltaBytes} B${pctStr}`; | ||
| } | ||
|
|
||
| // Returns an array of row objects, one per representative target, each | ||
| // either a comparison row or a status row (missing from PR/baseline). | ||
| function diffSizeReports(prReport, baselineReport) { | ||
| return REPRESENTATIVE_TARGETS.map((target) => { | ||
| const pr = prReport[target]; | ||
| const base = baselineReport ? baselineReport[target] : undefined; | ||
|
|
||
| if (!pr && !base) { | ||
| return { target, status: 'not-built' }; | ||
| } | ||
| if (!pr) { | ||
| return { target, status: 'missing-from-pr' }; | ||
| } | ||
| if (!base) { | ||
| return { target, status: 'no-baseline', flash: pr.flash, ram: pr.ram }; | ||
| } | ||
|
|
||
| const flashDelta = pr.flash - base.flash; | ||
| const ramDelta = pr.ram - base.ram; | ||
| return { | ||
| target, | ||
| status: 'compared', | ||
| flash: pr.flash, | ||
| ram: pr.ram, | ||
| baseFlash: base.flash, | ||
| baseRam: base.ram, | ||
| flashDelta, | ||
| ramDelta, | ||
| notable: Math.abs(flashDelta) >= NOISE_THRESHOLD_BYTES || Math.abs(ramDelta) >= NOISE_THRESHOLD_BYTES, | ||
| }; | ||
| }); | ||
| } | ||
|
|
||
| // docLink: string URL to link, or null/undefined to omit the doc-link line. | ||
| function renderComment({ prReport, baselineReport, shortSha, docLink, marker }) { | ||
| const rows = diffSizeReports(prReport, baselineReport); | ||
| const lines = [marker, '**RAM / Flash usage vs. base branch** — commit `' + shortSha + '`', '']; | ||
|
|
||
| if (!baselineReport) { | ||
| lines.push( | ||
| '> No size baseline is available yet for this PR\'s base branch ' + | ||
| '(first run after this feature shipped, or a new branch). ' + | ||
| 'This comment will show deltas once a baseline exists.', | ||
| '' | ||
| ); | ||
| } | ||
|
|
||
| const anyComparable = rows.some((r) => r.status === 'compared' || r.status === 'no-baseline'); | ||
| if (anyComparable) { | ||
| lines.push('| Target | Flash Δ | RAM Δ |', '|---|---|---|'); | ||
| for (const row of rows) { | ||
| if (row.status === 'compared') { | ||
| const flashCell = formatDelta(row.flashDelta, row.baseFlash); | ||
| const ramCell = formatDelta(row.ramDelta, row.baseRam); | ||
| const notableMark = row.notable ? ' ⚠️' : ''; | ||
| lines.push(`| ${row.target}${notableMark} | ${flashCell} | ${ramCell} |`); | ||
| } else if (row.status === 'no-baseline') { | ||
| lines.push(`| ${row.target} | ${row.flash} B (no baseline) | ${row.ram} B (no baseline) |`); | ||
| } else if (row.status === 'missing-from-pr') { | ||
| lines.push(`| ${row.target} | not built by this PR | not built by this PR |`); | ||
| } | ||
| // 'not-built': omit entirely, nothing meaningful to say | ||
| } | ||
| lines.push(''); | ||
| } else { | ||
| lines.push( | ||
| '_None of the representative targets (' + REPRESENTATIVE_TARGETS.join(', ') + ') ' + | ||
| 'were built by this PR — no size comparison to show._', | ||
| '' | ||
| ); | ||
| } | ||
|
|
||
| if (docLink) { | ||
| lines.push(`See [RAM/flash optimization guide](${docLink}) for techniques to reduce usage.`, ''); | ||
| } | ||
|
|
||
| return lines.join('\n').trimEnd() + '\n'; | ||
| } | ||
|
|
||
| module.exports = { REPRESENTATIVE_TARGETS, NOISE_THRESHOLD_BYTES, diffSizeReports, renderComment, formatDelta }; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.