mirror of
https://github.com/eclipse-threadx/threadx.git
synced 2026-10-06 06:59:08 +08:00
* Stated the compiler and coverage requirements instead of citing a file no contributor can open
Seven comments across five files cited a maintainer-local document as the
source for two project requirements: that GCC 14 on Linux is the default
compiler, and that the coverage target is 100%. That document is not part of
this repository and is not published anywhere, so the citation gave a reader
nothing to follow -- it named a source they cannot open, in place of simply
stating the requirement.
Both requirements are real and both stay. Only the pointer goes: each comment
now states the requirement on its own terms, which is what the surrounding
prose was already doing everywhere else.
cmake/cortex_r52.cmake the pinned reference toolchain
scripts/check_gcc.sh why the script exists
.github/workflows/gcc_check.yml why the workflow exists, and the
GCC_VERSION pin
.github/workflows/r52_fvp.yml the GCC_VERSION pin
.github/workflows/regression_template.yml the coverage floor, twice
Comments only; no behaviour changes. Two paragraphs are rewrapped where the
shorter text left a ragged line. Verified that scripts/check_gcc.sh still
parses and prints its help from the header range it slices, and that
cmake/cortex_r52.cmake still configures the Cortex-R52 build.
Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
* Said who the pinned toolchain default in the CMake toolchain files serves
Both cmake/cortex_r52.cmake and cmake/cortex_m52.cmake default
ARM_TOOLCHAIN_PATH to a toolchains directory under the user's home, guarded by
an EXISTS check. Nothing said whether CI relies on that, and the natural
reading is that it does.
It does not. The three workflows that install a toolchain unpack it into the
workspace and cache it there, and r52_fvp.yml puts that directory on PATH
before configuring; scripts/check_gcc.sh passes -DARM_TOOLCHAIN_PATH at each of
its three CMake call sites. On a runner the guarded directory is absent, the
EXISTS check falls through, and the compiler comes from PATH. The default only
ever fires on a developer machine, where it is what makes a no-flag build work.
Both comments now say that, so the default is not mistaken for a CI dependency
and not removed as dead code. cortex_r52.cmake carries the explanation and
cortex_m52.cmake refers to it, matching the cross-reference already there.
The r52 comment also claimed absolute paths mean "the build does not depend on
PATH ordering", which is only true where the pinned directory exists -- in CI
the build depends on PATH and nothing else. Qualified accordingly.
Comments only; no behaviour changes. Verified that both toolchain files still
configure, and that the fall-through is real: with HOME pointed at a directory
holding no toolchains, cortex_r52.cmake configures against the arm-none-eabi-gcc
found on PATH.
Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
749 lines
32 KiB
Bash
Executable File
749 lines
32 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 seven 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, compile the module manager C sources once per Arm module port, 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 14 on Linux is the project's default compiler, 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: $*"; }
|
|
|
|
# The C stages treat any compiler output as a failure, and a #pragma message is
|
|
# a deliberate notice to callers rather than a defect in the file that carries
|
|
# it -- txm_module_manager_absolute_load.c deprecates itself in favour of the
|
|
# extended entry point, and the module manager stage compiles it once per port.
|
|
#
|
|
# check_clang.sh suppresses these at the compiler with -Wno-#pragma-messages.
|
|
# GCC has no equivalent: the note is unconditional, and neither -Wno-pragmas
|
|
# nor any other -W option silences it -- verified with 14.3.rel1. So it is
|
|
# filtered out of the output here instead, along with the source quote and
|
|
# caret GCC prints beneath it. The skip ends at the next line that starts a
|
|
# diagnostic of its own, so an error following a waived note is still reported.
|
|
strip_pragma_messages() {
|
|
awk '
|
|
/note: .#pragma message:/ { skip = 1; next }
|
|
skip && /^ *[0-9]* *\|/ { next }
|
|
{ skip = 0; print }
|
|
'
|
|
}
|
|
|
|
# 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 [ "$asm_only" -eq 0 ]; then
|
|
say ""
|
|
say "== Module manager C sources, one per Arm module port =="
|
|
|
|
# #689 added this stage to check_clang.sh alone, so the module manager C
|
|
# stayed unbuilt by the project's declared default compiler: 28 files of
|
|
# portable module manager under common_modules, plus the three to nine
|
|
# per-port files under ports_module/<core>/gnu/module_manager/src. This is
|
|
# the GCC half, and it is deliberately the same stage -- same ports, same
|
|
# headers, same counts -- because a port covered by one check and not the
|
|
# other implies a parity the checks list does not have.
|
|
#
|
|
# Each module port ships its own tx_port.h and txm_module_port.h, carrying
|
|
# the control-block extensions the dispatch code needs, so a port is
|
|
# compiled against its own headers rather than the base port's.
|
|
module_skipped=""
|
|
for dir in ports_module/*/gnu/module_manager/src; do
|
|
[ -d "$dir" ] || continue
|
|
core="$(echo "$dir" | cut -d/ -f2)"
|
|
spec="${PORT_TARGET[$core]:-}"
|
|
if [ -z "$spec" ]; then
|
|
module_skipped="$module_skipped $core"
|
|
continue
|
|
fi
|
|
|
|
inc="ports_module/$core/gnu/inc"
|
|
if [ ! -f "$inc/tx_port.h" ] || [ ! -f "$inc/txm_module_port.h" ]; then
|
|
module_skipped="$module_skipped $core(headers)"
|
|
continue
|
|
fi
|
|
|
|
# shellcheck disable=SC2086
|
|
set -- $spec
|
|
target="$1"; cpu="$2"; shift 2; extra="$*"
|
|
CC="$(cc_for "$target")"
|
|
|
|
# An SMP port's control blocks come from common_smp; pairing it with the
|
|
# single-core headers hides _tx_thread_smp_protect behind an implicit
|
|
# declaration instead of compiling the port that is actually shipped.
|
|
case "$core" in
|
|
*_smp) kernel_inc="common_smp/inc" ;;
|
|
*) kernel_inc="common/inc" ;;
|
|
esac
|
|
|
|
# The TrustZone ports carry cmse_nonsecure_entry, which needs -mcmse to
|
|
# be honoured rather than ignored.
|
|
port_extra=""
|
|
if [ -f "$inc/tx_secure_interface.h" ]; then
|
|
port_extra="-mcmse"
|
|
fi
|
|
|
|
count=0; bad=0
|
|
for src in common_modules/module_manager/src/*.c "$dir"/*.c; do
|
|
[ -f "$src" ] || continue
|
|
count=$((count + 1))
|
|
output="$("$CC" -mcpu="$cpu" $extra $port_extra \
|
|
-I"$inc" -I"$kernel_inc" -Icommon_modules/inc \
|
|
-Icommon_modules/module_manager/inc \
|
|
-c "$src" -o /dev/null 2>&1 | strip_pragma_messages)"
|
|
if [ -n "$output" ]; then
|
|
fail "$core: $src"
|
|
# Show the error lines when there are any, and otherwise
|
|
# whatever the compiler did say -- a FAIL with nothing under it
|
|
# sends the reader off to reproduce the command by hand.
|
|
if echo "$output" | grep -q "error:"; then
|
|
echo "$output" | grep "error:" | head -3 | sed 's/^/ /'
|
|
else
|
|
echo "$output" | head -3 | sed 's/^/ /'
|
|
fi
|
|
bad=$((bad + 1)); failures=$((failures + 1))
|
|
fi
|
|
done
|
|
say " $core: $((count - bad)) of $count compiled"
|
|
done
|
|
if [ -n "$module_skipped" ]; then
|
|
say " no target mapping, skipped:$module_skipped"
|
|
fi
|
|
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
|