mirror of
https://github.com/eclipse-threadx/threadx.git
synced 2026-10-06 06:59:08 +08:00
cortex_m / Cortex M0 build (push) Canceled after 0s
cortex_m / Cortex M3 build (push) Canceled after 0s
cortex_m / Cortex M4 build (push) Canceled after 0s
cortex_m / Cortex M7 build (push) Canceled after 0s
gcc_check / gnu (push) Canceled after 0s
regression_test / tx (push) Canceled after 0s
regression_test / smp (push) Canceled after 0s
regression_test / freertos (push) Canceled after 0s
regression_test / deploy (push) Canceled after 0s
regression_template / run_tests (push) Canceled after 0s
regression_template / deploy_code_coverage (push) Canceled after 0s
ports/cortex_r52/gnu/CMakeLists.txt rejects two option combinations at
configure time -- TX_R52_ENABLE_VFP without TX_R52_FLOAT_ABI=hard, and
TX_R52_ENABLE_FIQ_NESTING without TX_R52_ENABLE_FIQ. Nothing exercised
either. A guard that has stopped firing looks exactly like a guard nobody
has tripped, so both could have been silently disabled by a typo in a
variable name at any point and no check would have noticed.
check_gcc.sh gains a sixth stage that configures each rejected combination
and asserts the configure fails. It needs no new harness: the script already
runs cmake as a subprocess for the CMake example builds, and the port's
CMakeLists is included by the toolchain file alone, so no example flags are
needed and the two negative configures stop almost immediately.
Two things the stage does that a thinner version would not.
It asserts the message text, not just the exit status. A configure that
fails for an unrelated reason would otherwise be recorded as a guard doing
its job.
It also configures the SUPPORTED combination and requires that to succeed.
Two negative assertions on their own are satisfied by a guard that rejects
everything -- the port would be unbuildable and the check would still pass.
The positive case is what separates a guard that works from one that is
merely always on.
Verified negatively, four deliberate breaks, each caught:
VFP guard condition forced false -> "configure succeeded, but this
combination cannot build"
FIQ nesting guard forced false -> the same, on that case
guard fires, message text changed -> "configure failed, but not on the
expected guard"
VFP guard condition forced true -> "the supported combination was
refused"
The fourth is the one the positive case exists for and the only one a
refusals-only stage would have missed. ports/cortex_r52/gnu/CMakeLists.txt
was confirmed byte-identical to dev afterwards.
Full run passes: exit 0, all six stages, and --asm-only correctly skips the
new one.
Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
651 lines
28 KiB
Bash
Executable File
651 lines
28 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
|
|
##############################################################################
|
|
|
|
# Builds the Arm ports with the GNU toolchain, in six stages: assemble every
|
|
# assembly source of every Arm gnu port, assemble again the parts guarded by
|
|
# feature macros, compile the common C sources for one core per architecture
|
|
# profile, link the example builds, both the script-driven ones and those
|
|
# driven by CMake, and finally assert that the option combinations the
|
|
# Cortex-R52 port refuses are in fact refused. Only the linking stages need a
|
|
# target C library.
|
|
#
|
|
# scripts/check_gcc.sh # both drivers from PATH
|
|
# scripts/check_gcc.sh --arm-none-eabi /path/to/toolchain/bin \
|
|
# --aarch64-none-elf /path/to/toolchain/bin
|
|
#
|
|
# Options:
|
|
# --arm-none-eabi <path> arm-none-eabi-gcc, or the directory holding it.
|
|
# Defaults to $ARM_NONE_EABI_GCC then to PATH.
|
|
# --aarch64-none-elf <path> aarch64-none-elf-gcc, or its directory.
|
|
# Defaults to $AARCH64_NONE_ELF_GCC then to PATH.
|
|
# --asm-only Skip the C sources and the example builds.
|
|
# --no-examples Skip the example builds.
|
|
# --quiet Print only failures and the summary.
|
|
#
|
|
# Exit status is 0 when everything builds and 1 otherwise.
|
|
#
|
|
# Why this exists: GCC is the project's declared default compiler (AGENTS.md,
|
|
# "The default compiler for the project is GCC 14 on Linux"), it is what the
|
|
# gnu ports exist for, and it is what nearly every downstream user builds with
|
|
# -- and until this script landed, nothing in CI compiled a line of any port
|
|
# with it. The only cross-compilation check that ran was the LLVM one, so the
|
|
# ATfE path was better guarded than the GNU one, on ports whose directory is
|
|
# literally named gnu.
|
|
#
|
|
# This is the companion to scripts/check_clang.sh and deliberately mirrors it
|
|
# stage for stage. They are two scripts rather than one with a --toolchain flag
|
|
# because the flag surface differs (a prefixed driver against --target=), the
|
|
# C library differs, and the set of examples that can link differs. Folding
|
|
# them together makes it easy to weaken one check while working on the other.
|
|
#
|
|
# It compiles and links; it executes nothing. The Cortex-R52 FVP ctest suite is
|
|
# a separate matter, and the RISC-V, MIPS, RX and ARC families are outside it
|
|
# entirely -- every skip is printed by name below.
|
|
#
|
|
# Arm GNU toolchain releases:
|
|
# https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads
|
|
|
|
set -u
|
|
|
|
cd "$(dirname "$(realpath "$0")")/.."
|
|
|
|
# Two toolchains, not one. Arm ships arm-none-eabi (AArch32: every M and R
|
|
# profile core and the A32 A-profile ports) and aarch64-none-elf as separate
|
|
# downloads, and PORT_TARGET below maps every port to one of exactly those two
|
|
# triples. Both are required: making a missing one a soft skip would let a run
|
|
# cover half the tree and still say "all checks passed", which is the failure
|
|
# this script was written to end.
|
|
CC_ARM="${ARM_NONE_EABI_GCC:-}"
|
|
CC_AARCH64="${AARCH64_NONE_ELF_GCC:-}"
|
|
asm_only=0
|
|
no_examples=0
|
|
quiet=0
|
|
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--arm-none-eabi)
|
|
[ "$#" -ge 2 ] || { echo "Error: --arm-none-eabi needs a path" >&2; exit 2; }
|
|
CC_ARM="$2"; shift 2 ;;
|
|
--aarch64-none-elf)
|
|
[ "$#" -ge 2 ] || { echo "Error: --aarch64-none-elf needs a path" >&2; exit 2; }
|
|
CC_AARCH64="$2"; shift 2 ;;
|
|
--asm-only) asm_only=1; no_examples=1; shift ;;
|
|
--no-examples) no_examples=1; shift ;;
|
|
--quiet) quiet=1; shift ;;
|
|
-h|--help) sed -n '17,36p' "$0"; exit 0 ;;
|
|
*) echo "Error: unknown option '$1'" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
say() { [ "$quiet" -eq 1 ] || echo "$@"; }
|
|
fail() { echo " FAIL: $*"; }
|
|
|
|
# Accept either the driver itself or the directory holding it, since a
|
|
# toolchain is unpacked as a tree and naming its bin directory is the natural
|
|
# thing to reach for. Resolve to an absolute path: the example stages run the
|
|
# build scripts from inside their own directories, so a relative path would
|
|
# stop resolving there.
|
|
resolve_gcc() {
|
|
triple="$1"
|
|
given="$2"
|
|
|
|
if [ -z "$given" ]; then
|
|
command -v "${triple}-gcc" >/dev/null 2>&1 || return 1
|
|
command -v "${triple}-gcc"
|
|
return 0
|
|
fi
|
|
if [ -d "$given" ]; then
|
|
for candidate in "$given/${triple}-gcc" "$given/bin/${triple}-gcc"; do
|
|
[ -x "$candidate" ] && { realpath "$candidate"; return 0; }
|
|
done
|
|
return 1
|
|
fi
|
|
[ -x "$given" ] || return 1
|
|
realpath "$given"
|
|
}
|
|
|
|
missing=""
|
|
CC_ARM="$(resolve_gcc arm-none-eabi "$CC_ARM")" || missing="$missing arm-none-eabi"
|
|
CC_AARCH64="$(resolve_gcc aarch64-none-elf "$CC_AARCH64")" || missing="$missing aarch64-none-elf"
|
|
|
|
if [ -n "$missing" ]; then
|
|
echo "Error: compiler(s) not found:$missing"
|
|
echo "Pass --arm-none-eabi and --aarch64-none-elf, set ARM_NONE_EABI_GCC and"
|
|
echo "AARCH64_NONE_ELF_GCC, or put both drivers on PATH. Downloads:"
|
|
echo " https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads"
|
|
exit 1
|
|
fi
|
|
|
|
say ""
|
|
say "Using: $CC_ARM"
|
|
say " $("$CC_ARM" --version | head -1)"
|
|
say " $CC_AARCH64"
|
|
say " $("$CC_AARCH64" --version | head -1)"
|
|
|
|
# The example build scripts and the CMake toolchain file call the drivers by
|
|
# their bare prefixed names, so both bin directories go on PATH for the linking
|
|
# stages. Done once, here, rather than per stage: a second toolchain arriving
|
|
# on PATH halfway through a run is exactly the kind of difference that makes a
|
|
# failure irreproducible.
|
|
PATH="$(dirname "$CC_ARM"):$(dirname "$CC_AARCH64"):$PATH"
|
|
export PATH
|
|
|
|
# Pick the driver for a port from its triple, so the map below stays the single
|
|
# place a port's architecture is decided.
|
|
cc_for() {
|
|
case "$1" in
|
|
aarch64-none-elf) echo "$CC_AARCH64" ;;
|
|
*) echo "$CC_ARM" ;;
|
|
esac
|
|
}
|
|
|
|
# Each port directory is mapped explicitly to a target triple and CPU. Do not
|
|
# replace this with prefix matching: cortex_a5* also matches cortex_a53 and
|
|
# cortex_a55, which are AArch64, and assembling those as ARM32 produces a flood
|
|
# of misleading errors.
|
|
#
|
|
# Copied verbatim from scripts/check_clang.sh. Keep the two identical -- a port
|
|
# covered by one check and not the other is worse than one covered by neither,
|
|
# because the checks list implies parity that does not exist.
|
|
declare -A PORT_TARGET=(
|
|
[cortex_m0]="arm-none-eabi cortex-m0 -mthumb"
|
|
[cortex_m0+]="arm-none-eabi cortex-m0plus -mthumb"
|
|
[cortex_m3]="arm-none-eabi cortex-m3 -mthumb"
|
|
[cortex_m4]="arm-none-eabi cortex-m4 -mthumb"
|
|
[cortex_m7]="arm-none-eabi cortex-m7 -mthumb"
|
|
[cortex_m23]="arm-none-eabi cortex-m23 -mthumb"
|
|
[cortex_m33]="arm-none-eabi cortex-m33 -mthumb"
|
|
[cortex_m52]="arm-none-eabi cortex-m52 -mthumb -mfloat-abi=hard"
|
|
[cortex_m55]="arm-none-eabi cortex-m55 -mthumb -mfloat-abi=hard"
|
|
[cortex_m85]="arm-none-eabi cortex-m85 -mthumb -mfloat-abi=hard"
|
|
[cortex_a5]="arm-none-eabi cortex-a5"
|
|
[cortex_a7]="arm-none-eabi cortex-a7"
|
|
[cortex_a8]="arm-none-eabi cortex-a8"
|
|
[cortex_a9]="arm-none-eabi cortex-a9"
|
|
[cortex_a12]="arm-none-eabi cortex-a12"
|
|
[cortex_a15]="arm-none-eabi cortex-a15"
|
|
[cortex_a17]="arm-none-eabi cortex-a17"
|
|
[cortex_a5_smp]="arm-none-eabi cortex-a5"
|
|
[cortex_a7_smp]="arm-none-eabi cortex-a7"
|
|
[cortex_a9_smp]="arm-none-eabi cortex-a9"
|
|
[cortex_r4]="arm-none-eabi cortex-r4"
|
|
[cortex_r5]="arm-none-eabi cortex-r5"
|
|
[cortex_r52]="arm-none-eabi cortex-r52"
|
|
[cortex_a34]="aarch64-none-elf cortex-a34"
|
|
[cortex_a35]="aarch64-none-elf cortex-a35"
|
|
[cortex_a53]="aarch64-none-elf cortex-a53"
|
|
[cortex_a55]="aarch64-none-elf cortex-a55"
|
|
[cortex_a57]="aarch64-none-elf cortex-a57"
|
|
[cortex_a65]="aarch64-none-elf cortex-a65"
|
|
[cortex_a65ae]="aarch64-none-elf cortex-a65ae"
|
|
[cortex_a72]="aarch64-none-elf cortex-a72"
|
|
[cortex_a73]="aarch64-none-elf cortex-a73"
|
|
[cortex_a75]="aarch64-none-elf cortex-a75"
|
|
[cortex_a76]="aarch64-none-elf cortex-a76"
|
|
[cortex_a76ae]="aarch64-none-elf cortex-a76ae"
|
|
[cortex_a77]="aarch64-none-elf cortex-a77"
|
|
[cortex_a34_smp]="aarch64-none-elf cortex-a34"
|
|
[cortex_a35_smp]="aarch64-none-elf cortex-a35"
|
|
[cortex_a53_smp]="aarch64-none-elf cortex-a53"
|
|
[cortex_a55_smp]="aarch64-none-elf cortex-a55"
|
|
[cortex_a57_smp]="aarch64-none-elf cortex-a57"
|
|
[cortex_a65_smp]="aarch64-none-elf cortex-a65"
|
|
[cortex_a65ae_smp]="aarch64-none-elf cortex-a65ae"
|
|
[cortex_a72_smp]="aarch64-none-elf cortex-a72"
|
|
[cortex_a73_smp]="aarch64-none-elf cortex-a73"
|
|
[cortex_a75_smp]="aarch64-none-elf cortex-a75"
|
|
[cortex_a76_smp]="aarch64-none-elf cortex-a76"
|
|
[cortex_a76ae_smp]="aarch64-none-elf cortex-a76ae"
|
|
[cortex_a77_smp]="aarch64-none-elf cortex-a77"
|
|
[cortex_a78_smp]="aarch64-none-elf cortex-a78"
|
|
)
|
|
|
|
# Assembly guarded by a feature macro is invisible to the stage above, which
|
|
# assembles with default flags and so lets the preprocessor discard every #ifdef
|
|
# block before the assembler sees it. These are the macros a user can turn on;
|
|
# each file carrying one is assembled again with it defined.
|
|
#
|
|
# This is not hypothetical, and it is not only clang's finding. The Cortex-M23
|
|
# module manager carried "POP {r0, lr}" in exactly these paths -- invalid on
|
|
# Armv8-M Baseline, where the 16-bit POP takes r0-r7 and pc only -- for six
|
|
# months after the identical fix landed in its non-module sibling, because no
|
|
# glob in either script reached ports_module until #672.
|
|
FEATURE_MACROS="TX_ENABLE_VFP_SUPPORT TX_ENABLE_FIQ_SUPPORT TX_LOW_POWER
|
|
TX_ENABLE_EXECUTION_CHANGE_NOTIFY"
|
|
|
|
# TX_ENABLE_IRQ_NESTING and TX_ENABLE_FIQ_NESTING are deliberately not here.
|
|
# They guard no assembly in the trees this script walks: the nesting start and
|
|
# end routines are separate files compiled unconditionally, and the macros only
|
|
# feed the TX_PORT_SPECIFIC_BUILD_OPTIONS bitfield in tx_port.h. Adding them
|
|
# would assemble nothing new and imply coverage that does not exist.
|
|
|
|
# Extra flags for the VFP paths, per core.
|
|
#
|
|
# check_clang.sh's equivalent map has one entry and a comment saying "Do not
|
|
# extend this to the A profile ports ... their defaults are already correct."
|
|
# That is true of clang and false of GCC, so do not copy that comment here.
|
|
# arm-none-eabi-gcc defaults to -mfloat-abi=soft, which disables the FPU
|
|
# outright, and every VFP file then fails with
|
|
#
|
|
# selected processor does not support 'vmrs r1,FPSCR' in ARM mode
|
|
#
|
|
# -mfloat-abi=hard alone is the fix, and it is the right one: it selects the
|
|
# core's own default FPU rather than naming one. Naming a -d16 FPU is the trap
|
|
# the clang script warns about -- the A-profile paths save D16-D31, which exist
|
|
# only on a 32-register FPU, so a -d16 choice turns 28 working files into
|
|
# "register expected".
|
|
#
|
|
# Cortex-R4 is the exception, in both scripts and for the same reason: its FPU
|
|
# is an option rather than part of the core, so -mfloat-abi=hard alone gives
|
|
# "selected architecture lacks an FPU" and an explicit -mfpu is required. The
|
|
# value matches check_clang.sh's, so the two scripts say the same thing about
|
|
# the same port.
|
|
#
|
|
# Every entry below was measured against arm-gnu-toolchain 14.3.rel1. A core
|
|
# that acquires a VFP-guarded file without an entry here fails loudly rather
|
|
# than silently, which is the intended behaviour.
|
|
declare -A VFP_EXTRA=(
|
|
[cortex_a5]="-mfloat-abi=hard"
|
|
[cortex_a7]="-mfloat-abi=hard"
|
|
[cortex_a8]="-mfloat-abi=hard"
|
|
[cortex_a9]="-mfloat-abi=hard"
|
|
[cortex_a12]="-mfloat-abi=hard"
|
|
[cortex_a15]="-mfloat-abi=hard"
|
|
[cortex_a17]="-mfloat-abi=hard"
|
|
[cortex_a5_smp]="-mfloat-abi=hard"
|
|
[cortex_a7_smp]="-mfloat-abi=hard"
|
|
[cortex_a9_smp]="-mfloat-abi=hard"
|
|
[cortex_r4]="-mfpu=vfpv3-d16 -mfloat-abi=softfp"
|
|
[cortex_r5]="-mfloat-abi=hard"
|
|
[cortex_r52]="-mfloat-abi=hard"
|
|
)
|
|
|
|
# One core per architecture profile for the C sources. Compiling all of them
|
|
# for every core would multiply the run time without adding coverage, since the
|
|
# port headers differ by profile rather than by core.
|
|
#
|
|
# cortex_r52 earns a slot of its own next to cortex_r5 because Armv8-R AArch32
|
|
# is a separate profile rather than a variant of Armv7-R. That port is written
|
|
# by hand instead of generated from ports_arch, and its tx_port.h differs
|
|
# accordingly, so cortex_r5 does not stand in for it.
|
|
C_CORES="cortex_m0 cortex_m4 cortex_m23 cortex_m33 cortex_m55 cortex_a7 cortex_a53 cortex_r5 cortex_r52"
|
|
|
|
# Example builds driven by CMake rather than by a build_threadx.sh pair. These
|
|
# are covered by their own stage below, so the script-driven loop passes over
|
|
# them without reporting them as a gap.
|
|
CMAKE_EXAMPLE_CORES="cortex_r52"
|
|
|
|
# Example builds that are not expected to link, with the reason. Named by
|
|
# their port directory, which covers both ports/ and ports_smp/. Listed
|
|
# explicitly rather than silently skipped, so the gaps stay visible.
|
|
#
|
|
# The same four as check_clang.sh, and the reason there is stated in terms of
|
|
# GNU tooling because that is where it was reproduced:
|
|
#
|
|
# their linker scripts define the .init and .fini sections but not the _init
|
|
# and _fini symbols. Those come from crti.o and crtn.o, which -nostartfiles
|
|
# leaves out, so newlib's fini.c cannot resolve them and the link ends with
|
|
# "undefined reference to `_fini'".
|
|
#
|
|
# The 27 AArch64 examples failed the same way until #673, which links crti.o
|
|
# and crtn.o back. They are deliberately absent from this list: they link.
|
|
EXAMPLES_EXPECTED_TO_FAIL="arm9 arm11 cortex_r4 cortex_r5"
|
|
|
|
failures=0
|
|
skipped=""
|
|
|
|
# --------------------------------------------------------------------------
|
|
say ""
|
|
say "== Assembly sources of every Arm gnu port =="
|
|
|
|
total=0
|
|
# The module ports keep their assembly in module_manager/src, not src.
|
|
# check_clang.sh read ports_module/*/gnu/src until #672; that directory does
|
|
# not exist, the [ -d ] guard skipped it in silence, and 116 files across nine
|
|
# Arm module ports were assembled by no check with either compiler. This script
|
|
# has never had that hole and must not acquire it.
|
|
for dir in ports/*/gnu/src ports_smp/*/gnu/src ports_module/*/gnu/module_manager/src; do
|
|
[ -d "$dir" ] || continue
|
|
core="$(echo "$dir" | cut -d/ -f2)"
|
|
spec="${PORT_TARGET[$core]:-}"
|
|
if [ -z "$spec" ]; then
|
|
skipped="$skipped $core"
|
|
continue
|
|
fi
|
|
# shellcheck disable=SC2086
|
|
set -- $spec
|
|
target="$1"; cpu="$2"; shift 2; extra="$*"
|
|
CC="$(cc_for "$target")"
|
|
|
|
for src in "$dir"/*.S "$dir"/*.s; do
|
|
[ -f "$src" ] || continue
|
|
total=$((total + 1))
|
|
output="$("$CC" -mcpu="$cpu" $extra -c "$src" -o /dev/null 2>&1)"
|
|
if [ -n "$output" ]; then
|
|
fail "$src"
|
|
echo "$output" | sed 's/^/ /'
|
|
failures=$((failures + 1))
|
|
fi
|
|
done
|
|
done
|
|
say " $((total - failures)) of $total assembled"
|
|
if [ -n "$skipped" ]; then
|
|
say " not Arm, skipped:$(echo $skipped | tr ' ' '\n' | sort -u | tr '\n' ' ')"
|
|
say " (arm9 and arm11 are Arm; they are skipped for having no PORT_TARGET entry)"
|
|
fi
|
|
|
|
# --------------------------------------------------------------------------
|
|
say ""
|
|
say "== Assembly behind feature macros =="
|
|
|
|
for macro in $FEATURE_MACROS; do
|
|
macro_total=0
|
|
macro_bad=0
|
|
for src in $(grep -rl "$macro" ports/*/gnu/src/*.S ports_smp/*/gnu/src/*.S \
|
|
ports_module/*/gnu/module_manager/src/*.S \
|
|
2>/dev/null | sort); do
|
|
core="$(echo "$src" | cut -d/ -f2)"
|
|
spec="${PORT_TARGET[$core]:-}"
|
|
[ -n "$spec" ] || continue
|
|
# shellcheck disable=SC2086
|
|
set -- $spec
|
|
target="$1"; cpu="$2"; shift 2; extra="$*"
|
|
CC="$(cc_for "$target")"
|
|
|
|
# The FPU flags apply to the VFP paths only; the other macros guard no
|
|
# floating-point code and do not need them.
|
|
fpu=""
|
|
if [ "$macro" = "TX_ENABLE_VFP_SUPPORT" ]; then
|
|
fpu="${VFP_EXTRA[$core]:-}"
|
|
fi
|
|
|
|
macro_total=$((macro_total + 1))
|
|
output="$("$CC" -mcpu="$cpu" $extra $fpu \
|
|
-D"$macro" -c "$src" -o /dev/null 2>&1)"
|
|
if [ -n "$output" ]; then
|
|
fail "$src with -D$macro"
|
|
# Not filtered on "error": GCC prefixes its diagnostics with an
|
|
# "Assembler messages:" line and a missing tool says nothing of the
|
|
# kind, so filtering would hide the case worth seeing.
|
|
echo "$output" | head -4 | sed 's/^/ /'
|
|
macro_bad=$((macro_bad + 1))
|
|
failures=$((failures + 1))
|
|
fi
|
|
done
|
|
if [ "$macro_total" -eq 0 ]; then
|
|
say " $macro: no assembly is guarded by it"
|
|
else
|
|
say " $macro: $((macro_total - macro_bad)) of $macro_total assembled"
|
|
fi
|
|
done
|
|
|
|
# --------------------------------------------------------------------------
|
|
if [ "$asm_only" -eq 0 ]; then
|
|
say ""
|
|
say "== Common C sources, one core per architecture profile =="
|
|
|
|
for core in $C_CORES; do
|
|
spec="${PORT_TARGET[$core]:-}"
|
|
[ -n "$spec" ] || continue
|
|
# shellcheck disable=SC2086
|
|
set -- $spec
|
|
target="$1"; cpu="$2"; shift 2; extra="$*"
|
|
CC="$(cc_for "$target")"
|
|
|
|
count=0; bad=0
|
|
for src in common/src/*.c; do
|
|
count=$((count + 1))
|
|
output="$("$CC" -mcpu="$cpu" $extra \
|
|
-Iports/"$core"/gnu/inc -Icommon/inc -c "$src" -o /dev/null 2>&1)"
|
|
if [ -n "$output" ]; then
|
|
fail "$core: $src"
|
|
echo "$output" | head -4 | sed 's/^/ /'
|
|
bad=$((bad + 1)); failures=$((failures + 1))
|
|
fi
|
|
done
|
|
say " $core: $((count - bad)) of $count compiled"
|
|
done
|
|
fi
|
|
|
|
# --------------------------------------------------------------------------
|
|
if [ "$no_examples" -eq 0 ]; then
|
|
say ""
|
|
say "== Example builds, linked with GNU ld =="
|
|
|
|
example_ok=0
|
|
example_total=0
|
|
example_known=""
|
|
example_nodriver=""
|
|
example_nosample=""
|
|
for dir in ports/*/gnu/example_build ports_smp/*/gnu/example_build; do
|
|
[ -d "$dir" ] || continue
|
|
core="$(echo "$dir" | cut -d/ -f2)"
|
|
|
|
# Anything on the expected-to-fail list is reported before any other
|
|
# filter is applied, so a name placed there can never drop out of the
|
|
# output. arm9 and arm11 are the cases that matter: they are Arm ports
|
|
# with example drivers, but they carry no PORT_TARGET entry, so the
|
|
# Arm test below would discard them.
|
|
case " $EXAMPLES_EXPECTED_TO_FAIL " in
|
|
*" $core "*) example_known="$example_known $core"; continue ;;
|
|
esac
|
|
|
|
# Arm ports only, the same rule the assembly stage applies. Naming
|
|
# linux or mips32 as a gap here would be noise, not information.
|
|
[ -n "${PORT_TARGET[$core]:-}" ] || continue
|
|
|
|
# Covered by the CMake stage below rather than here.
|
|
case " $CMAKE_EXAMPLE_CORES " in
|
|
*" $core "*) continue ;;
|
|
esac
|
|
|
|
# A driverless example is not covered by this stage, so say so rather
|
|
# than dropping out in silence. A port that is simply absent from the
|
|
# count reads as covered.
|
|
if [ ! -f "$dir/build_threadx.sh" ]; then
|
|
example_nodriver="$example_nodriver $core"
|
|
continue
|
|
fi
|
|
if [ ! -f "$dir/build_threadx_sample.sh" ]; then
|
|
example_nosample="$example_nosample $core"
|
|
continue
|
|
fi
|
|
example_total=$((example_total + 1))
|
|
|
|
# Clean on both sides. A stale sample_threadx.out from a previous
|
|
# toolchain makes a failed link report success, because the success
|
|
# test below is the existence of the output file rather than the exit
|
|
# status of the driver.
|
|
rm -f "$dir"/*.o "$dir"/*.a "$dir"/*.out "$dir"/*.map 2>/dev/null || true
|
|
|
|
# TOOLCHAIN is *unset*, not set: the build scripts already default to
|
|
# GNU with `: "${TOOLCHAIN:=gnu}"`, and a stray TOOLCHAIN=atfe from a
|
|
# developer's shell or an earlier command would otherwise make this
|
|
# stage silently check the other compiler.
|
|
log="$(cd "$dir" && unset TOOLCHAIN && ./build_threadx.sh 2>&1 && \
|
|
./build_threadx_sample.sh 2>&1)" || true
|
|
|
|
if [ -f "$dir/sample_threadx.out" ]; then
|
|
example_ok=$((example_ok + 1))
|
|
else
|
|
fail "$core: example build produced no image"
|
|
# Not filtered on "error": a missing tool reports "command not
|
|
# found" or "Permission denied", and filtering hid exactly that.
|
|
echo "$log" | tail -6 | sed 's/^/ /'
|
|
failures=$((failures + 1))
|
|
fi
|
|
rm -f "$dir"/*.o "$dir"/*.a "$dir"/*.out "$dir"/*.map 2>/dev/null || true
|
|
done
|
|
say " $example_ok of $example_total example builds linked"
|
|
if [ -n "$example_known" ]; then
|
|
say " known not to link, see the list at the top of this script:$example_known"
|
|
fi
|
|
if [ -n "$example_nosample" ]; then
|
|
say " has build_threadx.sh but no build_threadx_sample.sh, so not linked:$example_nosample"
|
|
fi
|
|
if [ -n "$example_nodriver" ]; then
|
|
say " no script driver, so outside this stage:$example_nodriver"
|
|
fi
|
|
fi
|
|
|
|
# --------------------------------------------------------------------------
|
|
# The Cortex-R52 examples are built by CMake, so they need a toolchain file
|
|
# rather than the build_threadx.sh pair. Same compiler, same purpose as the
|
|
# stage above.
|
|
if [ "$no_examples" -eq 0 ]; then
|
|
say ""
|
|
say "== CMake example builds, linked with GNU ld =="
|
|
|
|
if ! command -v cmake >/dev/null 2>&1 || ! command -v ninja >/dev/null 2>&1; then
|
|
say " skipped: cmake and ninja are both required"
|
|
else
|
|
for core in $CMAKE_EXAMPLE_CORES; do
|
|
build_dir="$(mktemp -d)"
|
|
# ARM_TOOLCHAIN_PATH follows --arm-none-eabi, so this stage uses
|
|
# the same compiler as every other stage rather than the absolute
|
|
# path cmake/cortex_r52.cmake pins by default.
|
|
if cmake -S . -B "$build_dir" -G Ninja \
|
|
-DCMAKE_TOOLCHAIN_FILE="cmake/${core}.cmake" \
|
|
-DARM_TOOLCHAIN_PATH="$(dirname "$CC_ARM")" \
|
|
-DTX_R52_BUILD_FVP_EXAMPLE=ON \
|
|
-DTX_R52_ENABLE_MPU=ON >"$build_dir/configure.log" 2>&1; then
|
|
# Read the image list from the generated graph instead of
|
|
# repeating it here, so adding a target cannot silently escape
|
|
# this check. The images are EXCLUDE_FROM_ALL, so "ninja" alone
|
|
# would build none of them.
|
|
#
|
|
# The cmake_object_order_depends_target_* entries are CMake's
|
|
# own ordering phonies, one per real image and named after it.
|
|
# Counting those doubled the total and reported ten images built
|
|
# where there are five.
|
|
images="$(ninja -C "$build_dir" -t targets all 2>/dev/null \
|
|
| grep -oE '^[A-Za-z0-9_]+\.elf' \
|
|
| grep -v '^cmake_' | sort -u)"
|
|
if [ -z "$images" ]; then
|
|
fail "$core: no .elf targets found in the CMake graph"
|
|
failures=$((failures + 1))
|
|
else
|
|
built=0; total=0
|
|
for image in $images; do
|
|
total=$((total + 1))
|
|
if ninja -C "$build_dir" "$image" \
|
|
>"$build_dir/$image.log" 2>&1; then
|
|
built=$((built + 1))
|
|
else
|
|
fail "$core: $image did not build"
|
|
tail -6 "$build_dir/$image.log" | sed 's/^/ /'
|
|
failures=$((failures + 1))
|
|
fi
|
|
done
|
|
say " $core: $built of $total images linked"
|
|
fi
|
|
else
|
|
fail "$core: CMake configure failed"
|
|
tail -6 "$build_dir/configure.log" | sed 's/^/ /'
|
|
failures=$((failures + 1))
|
|
fi
|
|
rm -rf "$build_dir"
|
|
done
|
|
fi
|
|
fi
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Option combinations the port refuses at configure time.
|
|
#
|
|
# ports/cortex_r52/gnu/CMakeLists.txt rejects two combinations outright rather
|
|
# than letting them reach the compiler. Both are cheap to get wrong in a way
|
|
# that is invisible: a typo in a variable name makes the condition constant,
|
|
# and a guard that never fires is indistinguishable from one that is never
|
|
# tripped. So these assert the exit status AND the text, because a guard that
|
|
# fires for the wrong reason would otherwise pass.
|
|
#
|
|
# No example flags are needed: the port CMakeLists is included by the toolchain
|
|
# file alone, so the guards are reached without configuring anything else. That
|
|
# keeps the stage to three configures, two of which stop almost immediately.
|
|
#
|
|
# The third is the positive counterpart, and it is not decoration: a guard that
|
|
# rejects everything would satisfy both negative cases on its own. Asserting
|
|
# that the supported combination still configures is what distinguishes a guard
|
|
# that works from one that is merely always on.
|
|
#
|
|
# This is the only negative check in the script. Everything else here asserts
|
|
# that something builds; this stage asserts that two things refuse to, and that
|
|
# a third still does not.
|
|
if [ "$asm_only" -eq 0 ]; then
|
|
say ""
|
|
say "== Option combinations the Cortex-R52 port must refuse =="
|
|
|
|
if ! command -v cmake >/dev/null 2>&1; then
|
|
say " skipped: cmake is required"
|
|
else
|
|
# name | expected text in the error | options
|
|
refuse_case() {
|
|
case_name="$1"; want="$2"; shift 2
|
|
build_dir="$(mktemp -d)"
|
|
if cmake -S . -B "$build_dir" -G Ninja \
|
|
-DCMAKE_TOOLCHAIN_FILE=cmake/cortex_r52.cmake \
|
|
-DARM_TOOLCHAIN_PATH="$(dirname "$CC_ARM")" \
|
|
"$@" >"$build_dir/configure.log" 2>&1; then
|
|
fail "$case_name: configure succeeded, but this combination cannot build"
|
|
failures=$((failures + 1))
|
|
elif ! grep -q "$want" "$build_dir/configure.log"; then
|
|
fail "$case_name: configure failed, but not on the expected guard"
|
|
fail " wanted text: $want"
|
|
tail -6 "$build_dir/configure.log" | sed 's/^/ /'
|
|
failures=$((failures + 1))
|
|
else
|
|
say " $case_name: refused"
|
|
fi
|
|
rm -rf "$build_dir"
|
|
}
|
|
|
|
refuse_case "VFP without a hard float ABI" \
|
|
"TX_R52_ENABLE_VFP requires TX_R52_FLOAT_ABI=hard" \
|
|
-DTX_R52_ENABLE_VFP=ON
|
|
|
|
refuse_case "FIQ nesting without FIQ" \
|
|
"TX_R52_ENABLE_FIQ_NESTING requires TX_R52_ENABLE_FIQ" \
|
|
-DTX_R52_ENABLE_FIQ_NESTING=ON
|
|
|
|
# The counterpart: the supported spelling of the first case must still
|
|
# configure, so a guard cannot pass this stage by rejecting everything.
|
|
build_dir="$(mktemp -d)"
|
|
if cmake -S . -B "$build_dir" -G Ninja \
|
|
-DCMAKE_TOOLCHAIN_FILE=cmake/cortex_r52.cmake \
|
|
-DARM_TOOLCHAIN_PATH="$(dirname "$CC_ARM")" \
|
|
-DTX_R52_ENABLE_VFP=ON -DTX_R52_FLOAT_ABI=hard \
|
|
-DTX_R52_ENABLE_FIQ=ON -DTX_R52_ENABLE_FIQ_NESTING=ON \
|
|
>"$build_dir/configure.log" 2>&1; then
|
|
say " the supported combination: accepted"
|
|
else
|
|
fail "the supported combination was refused"
|
|
tail -6 "$build_dir/configure.log" | sed 's/^/ /'
|
|
failures=$((failures + 1))
|
|
fi
|
|
rm -rf "$build_dir"
|
|
fi
|
|
fi
|
|
|
|
# --------------------------------------------------------------------------
|
|
say ""
|
|
if [ "$failures" -eq 0 ]; then
|
|
say "All GNU toolchain checks passed."
|
|
exit 0
|
|
fi
|
|
|
|
echo "$failures GNU toolchain check(s) failed."
|
|
exit 1
|