mirror of
https://github.com/eclipse-threadx/threadx.git
synced 2026-10-06 06:59:08 +08:00
The ARMv7-A and ARMv8-A ports are generated by update.ps1, which needs PowerShell, so the cortex-a job in ports_arch_check ran on a Windows image and nobody could reproduce it locally on Linux. Add update.sh beside each update.ps1, with the same cores, compilers, copy sets and patches, and move the job to the same Linux image as everything else. The bash scripts were checked against the PowerShell ones by comparing what each reports as drifted. They agree exactly on the 63 files the Windows job last reported, and differ on 12 more, which turn out to be a defect in update.ps1 rather than in the port. Its two .cproject patterns are written as 'value=`"cortex-a7`"' with backticks that survive into the pattern, so that replacement has never matched, while the neighbouring Cortex-A7.NoFPU pattern has no backticks and always worked. The result is that the AC6 example builds for the A5, A8, A9, A12, A15 and A17 cores name cortex-a7 as their CPU while their FPU string is correct. The bash scripts do what the PowerShell ones intended, so regenerating corrects those twelve files. Restore ports_arch as the source for the rest. The implementation of _tx_thread_smp_time_get from #555 was applied to the twenty four generated SMP ports and never to ports_arch, which still held MOV x0, #0 with a FIXME comment, so regenerating would have replaced a working generic timer read with a stub. That implementation now lives in the source. The remaining differences are cosmetic and resolve in favour of the source: a trailing blank line in 38 copies of tx_thread_schedule.S and comment spacing in one tx_port.h. Note that the Cortex-A VFP fix is already present in ports_arch and was never at risk, contrary to what the description of the port consistency checks change said before this was measured. Extend scripts/check_ports.sh to run the A profile generators too, and make it fail when a generator fails or is missing rather than reporting a clean tree, which would have been a false pass. Pin every workflow to ubuntu-24.04. ubuntu-latest already resolves to that image, so nothing changes today, but a future migration becomes a deliberate commit rather than something that happens underneath the -m32 builds. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
239 lines
9.2 KiB
Bash
Executable File
239 lines
9.2 KiB
Bash
Executable File
#!/bin/bash
|
|
##############################################################################
|
|
# Copyright (C) 2026 Eclipse ThreadX contributors
|
|
#
|
|
# This program and the accompanying materials are made available under the
|
|
# terms of the MIT License which is available at
|
|
# https://opensource.org/licenses/MIT.
|
|
#
|
|
# AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
|
|
# The AI-generated portions may be considered public domain (CC0-1.0)
|
|
# and not subject to the project's licence. The human contributor has
|
|
# reviewed and verified that the code is correct.
|
|
#
|
|
# SPDX-License-Identifier: MIT and CC0-1.0
|
|
##############################################################################
|
|
|
|
# Consistency checks for the port trees. Run it before cutting a release, or
|
|
# any time the ports have been touched:
|
|
#
|
|
# scripts/check_ports.sh
|
|
#
|
|
# Options:
|
|
# --no-regen Skip the reproducibility check, which needs a clean tree.
|
|
# --quiet Print only failures and the summary.
|
|
#
|
|
# Exit status is 0 when every check passes and 1 otherwise, so the same
|
|
# command serves CI and the command line.
|
|
#
|
|
# Each check exists because a real defect reached the repository through it:
|
|
#
|
|
# 1. Reproducibility. The Cortex-M ports are generated by the copy scripts,
|
|
# but fixes were applied to the generated copies instead of the source for
|
|
# eight months. The next run of those scripts would have reverted them.
|
|
#
|
|
# 2. Preprocessor balance. A fix left ports/cortex_m85/iar/inc/tx_port.h with
|
|
# one more #endif than #if, so that header could not compile.
|
|
#
|
|
# 3. Code at file scope. A fix left a second, headerless copy of a function
|
|
# body in ports/cortex_m4/ac6/inc/tx_port.h, which placed statements
|
|
# outside any function. See issue 569.
|
|
#
|
|
# The last section reports, without failing, on port families that have no copy
|
|
# script and so cannot be checked for reproducibility.
|
|
#
|
|
# Headers under example_build are skipped: those trees vendor third party SDK
|
|
# code, which is not ours to hold to these rules.
|
|
|
|
set -u
|
|
|
|
cd "$(dirname "$(realpath "$0")")/.."
|
|
|
|
regen=1
|
|
quiet=0
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--no-regen) regen=0 ;;
|
|
--quiet) quiet=1 ;;
|
|
-h|--help) sed -n '17,30p' "$0"; exit 0 ;;
|
|
*) echo "Unknown option: $arg" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
failures=0
|
|
|
|
say() { [ "$quiet" -eq 1 ] || echo "$@"; }
|
|
fail() { echo " FAIL: $*"; failures=$((failures + 1)); }
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 1. The generated ports must be reproducible from ports_arch.
|
|
# --------------------------------------------------------------------------
|
|
say ""
|
|
say "== Generated ports are reproducible from ports_arch =="
|
|
|
|
if [ "$regen" -eq 0 ]; then
|
|
say " skipped (--no-regen)"
|
|
elif ! command -v git >/dev/null 2>&1; then
|
|
say " skipped (git not available)"
|
|
elif [ -n "$(git status --porcelain -uno)" ]; then
|
|
fail "the working tree has uncommitted changes; commit or stash them, or pass --no-regen"
|
|
else
|
|
# A generator that fails or is missing must not leave the tree looking
|
|
# clean, which would be a false pass.
|
|
generator_failed=0
|
|
run_generator() {
|
|
if ! "$@" >/dev/null 2>&1; then
|
|
fail "generator failed: $*"
|
|
generator_failed=1
|
|
fi
|
|
}
|
|
|
|
run_generator ./scripts/copy_armv7_m.sh
|
|
run_generator ./scripts/copy_armv8_m.sh
|
|
run_generator ./scripts/copy_module_armv7_m.sh
|
|
run_generator env -C ports_arch/ARMv7-A ./update.sh --port-sets tx \
|
|
--copy-common-files --copy-port-files --copy-example --patch-files
|
|
run_generator env -C ports_arch/ARMv8-A ./update.sh --port-sets tx,tx_smp \
|
|
--copy-common-files --copy-port-files --copy-example --patch-files
|
|
|
|
drift="$(git status --porcelain -uno)"
|
|
if [ -n "$drift" ]; then
|
|
fail "running the copy scripts changed $(echo "$drift" | wc -l) file(s)."
|
|
echo " The ports below were edited directly instead of through ports_arch."
|
|
echo " Re-apply the change to ports_arch and re-run the copy scripts."
|
|
echo "$drift" | sed 's/^/ /'
|
|
git checkout -- . >/dev/null 2>&1
|
|
else
|
|
say " ok: the copy scripts change nothing"
|
|
fi
|
|
fi
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 2. Preprocessor directives must balance in every port header.
|
|
# --------------------------------------------------------------------------
|
|
say ""
|
|
say "== Preprocessor directives balance =="
|
|
|
|
unbalanced=0
|
|
while IFS= read -r f; do
|
|
result="$(awk '
|
|
/^[ \t]*#[ \t]*(if|ifdef|ifndef)/ { depth++ }
|
|
/^[ \t]*#[ \t]*endif/ {
|
|
depth--
|
|
if (depth < 0 && first == 0) { first = NR }
|
|
}
|
|
END { printf "%d %d", depth, first }
|
|
' "$f")"
|
|
depth="${result% *}"
|
|
first="${result#* }"
|
|
if [ "$depth" -ne 0 ]; then
|
|
if [ "$first" -ne 0 ]; then
|
|
fail "$f: #endif without a matching #if at line $first (final depth $depth)"
|
|
else
|
|
fail "$f: $depth unterminated #if (final depth $depth)"
|
|
fi
|
|
unbalanced=$((unbalanced + 1))
|
|
fi
|
|
done < <(find ports ports_arch ports_module ports_smp -name "*.h" -type f \
|
|
! -path "*/example_build/*" 2>/dev/null | sort)
|
|
|
|
[ "$unbalanced" -eq 0 ] && say " ok: every port header balances"
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 3. No statements outside a function body in a port header.
|
|
# --------------------------------------------------------------------------
|
|
# Tracks brace depth, ignoring preprocessor lines, comments and strings, and
|
|
# reports any statement that lands at depth zero. Declarations, typedefs,
|
|
# externs and macro definitions are expected there; assignments, calls and
|
|
# dereferences are not, and an orphaned function body shows up as exactly that.
|
|
say ""
|
|
say "== No code at file scope in port headers =="
|
|
|
|
orphans=0
|
|
while IFS= read -r f; do
|
|
result="$(awk '
|
|
# Skip preprocessor lines, including multi-line macro bodies, whose
|
|
# continuations are statements by design.
|
|
/^[ \t]*#/ { if (/\\[ \t]*$/) { in_macro = 1 }; next }
|
|
in_macro { if (!/\\[ \t]*$/) { in_macro = 0 }; next }
|
|
|
|
# Skip comments, both kinds, including continuation lines.
|
|
in_comment { if (/\*\//) { in_comment = 0 }; next }
|
|
/^[ \t]*\/\// { next }
|
|
/\/\*/ { if (!/\*\//) { in_comment = 1 }; next }
|
|
|
|
{
|
|
line = $0
|
|
gsub(/"[^"]*"/, "", line)
|
|
gsub(/\/\/.*$/, "", line)
|
|
stripped = line
|
|
gsub(/^[ \t]+|[ \t]+$/, "", stripped)
|
|
|
|
# A statement at file scope ends in a semicolon and either assigns,
|
|
# dereferences or opens a control structure. Declarations and
|
|
# prototypes also end in a semicolon but match none of these.
|
|
if (depth == 0 && stripped ~ /;[ \t]*$/ &&
|
|
(stripped ~ /^\*\(/ ||
|
|
stripped ~ /^[A-Za-z_][A-Za-z0-9_]*([ \t]*(\[[^]]*\]|->[ \t]*[A-Za-z_][A-Za-z0-9_]*|\.[A-Za-z_][A-Za-z0-9_]*))*[ \t]*=[^=]/ ||
|
|
stripped ~ /^(return|if|while|for|switch|do)[ \t(]/)) {
|
|
print NR ": " stripped
|
|
found++
|
|
}
|
|
|
|
n = gsub(/{/, "{", line); m = gsub(/}/, "}", line)
|
|
depth += n - m
|
|
if (depth < 0) { depth = 0 }
|
|
}
|
|
END { exit (found > 0 ? 1 : 0) }
|
|
' "$f")"
|
|
if [ -n "$result" ]; then
|
|
fail "$f: statement(s) outside any function body"
|
|
echo "$result" | sed 's/^/ /'
|
|
orphans=$((orphans + 1))
|
|
fi
|
|
done < <(find ports ports_arch ports_module ports_smp -name "*.h" -type f \
|
|
! -path "*/example_build/*" 2>/dev/null | sort)
|
|
|
|
[ "$orphans" -eq 0 ] && say " ok: no port header carries code at file scope"
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 4. Report only: families with no copy script.
|
|
# --------------------------------------------------------------------------
|
|
# These are maintained by hand, so a fix applied to one toolchain can silently
|
|
# miss the others. Nothing here fails the run; it is a prompt to look.
|
|
say ""
|
|
say "== Families with no copy script (report only) =="
|
|
|
|
if [ "$quiet" -eq 0 ]; then
|
|
for family in ports/cortex_m0 ports/cortex_m0+ ports/cortex_m23; do
|
|
[ -d "$family" ] || continue
|
|
for probe in "dsb 0xF" "isb 0xF"; do
|
|
have=""; missing=""
|
|
for header in "$family"/*/inc/tx_port.h; do
|
|
[ -f "$header" ] || continue
|
|
grep -q "0xE000ED04" "$header" || continue
|
|
tool="$(basename "$(dirname "$(dirname "$header")")")"
|
|
if grep -q "$probe" "$header"; then
|
|
have="$have $tool"
|
|
else
|
|
missing="$missing $tool"
|
|
fi
|
|
done
|
|
if [ -n "$have" ] && [ -n "$missing" ]; then
|
|
echo " $family: \"$probe\" present in$have but absent in$missing"
|
|
fi
|
|
done
|
|
done
|
|
say " (nothing above means the toolchains within each family agree)"
|
|
fi
|
|
|
|
# --------------------------------------------------------------------------
|
|
say ""
|
|
if [ "$failures" -eq 0 ]; then
|
|
say "All port consistency checks passed."
|
|
exit 0
|
|
fi
|
|
|
|
echo "$failures port consistency check(s) failed."
|
|
exit 1
|