Files
threadx/scripts/check_clang.sh
T
Frédéric Desbiens 51ca412b36 Added build scripts for the AArch64 examples, which had none (#597)
The AArch64 examples could only be built through the Arm Development Studio
project files beside them. There was no script, so nothing in CI or on a
developer's machine could build them, and the sources they need were never
named anywhere: vectors.S, v8_aarch64.S and v8_utils.S were present but
unreferenced, which is why a hand written link failed on GetCPUID, GetAffinity,
InvalidateUDCaches and ZeroBlock. Those four are defined in v8_aarch64.S and
v8_utils.S, in the tree all along.

Add one pair of scripts, in ports_arch so every AArch64 port receives them.
Both derive the -mcpu value and the kernel source directory from the port
directory they sit in, so a single pair serves the twelve ThreadX ports and the
twelve SMP ports, the latter building against common_smp and picking up the C
sources those ports carry alongside the assembly. TOOLCHAIN=atfe selects Arm
Toolchain for Embedded in place of the GNU toolchain, as it does for the ARMv7
example scripts.

One symbol genuinely had no definition. startup.S calls
initialise_monitor_handles to open the standard file handles over a debugger
connection; the GNU toolchain provides it in libgloss through
--specs=rdimon.specs, while picolibc has no equivalent and neither does the
LLVM toolchain's semihosting library. semihost_stub.S supplies a weak no-op,
linked for that toolchain only, so a real definition always wins.

Extend scripts/check_clang.sh to cover ports_smp as well as ports, since the
example builds now exist there too.

Verified by building every AArch64 example with Arm Toolchain for Embedded:
twelve ThreadX ports at 314,744 to 315,256 bytes of text and twelve SMP ports
at 325,304 to 325,880. scripts/check_clang.sh now reports 35 of 35 example
builds linking with none failing, the twenty-four new ones plus the eleven that
already built. The images have not been executed: the sample
targets the Base platform peripheral addresses, which the available emulator
does not provide.

Cortex-A34, and the Cortex-A34 and A78 SMP ports, are left out. They are not in
the generator's core list, so they receive nothing from ports_arch and cannot be
checked for reproducibility either. Bringing them in rewrites 114 files in those
three directories, which deserves its own change.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
2026-08-11 08:54:04 -04:00

265 lines
10 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 an LLVM based toolchain: every assembly source of
# every Arm gnu port, and the common C sources for one core per architecture
# profile. Compilation only, no linking, so no target C library is required
# profile, then links the example builds that have a script driver.
#
# scripts/check_clang.sh # clang from PATH
# scripts/check_clang.sh --clang /path/to/clang
# CLANG=/path/to/clang scripts/check_clang.sh
#
# Options:
# --clang <path> Compiler to use; defaults to $CLANG then to clang.
# --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: the gnu ports are only ever built with GNU tooling, and GNU
# as accepts several non-canonical forms that LLVM's assembler rejects. Those
# forms accumulated unnoticed. This check also covers Arm Toolchain for
# Embedded, which is LLVM based and is the successor to Arm Compiler 6, so the
# ac6 code paths are exercised here as well.
#
# Arm Toolchain for Embedded releases:
# https://github.com/arm/arm-toolchain/releases
set -u
cd "$(dirname "$(realpath "$0")")/.."
CC="${CLANG:-clang}"
asm_only=0
no_examples=0
quiet=0
while [ "$#" -gt 0 ]; do
case "$1" in
--clang) [ "$#" -ge 2 ] || { echo "Error: --clang needs a path" >&2; exit 2; }; CC="$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,33p' "$0"; exit 0 ;;
*) echo "Error: unknown option '$1'" >&2; exit 2 ;;
esac
done
say() { [ "$quiet" -eq 1 ] || echo "$@"; }
fail() { echo " FAIL: $*"; }
if ! command -v "$CC" >/dev/null 2>&1 && [ ! -x "$CC" ]; then
echo "Error: compiler '$CC' not found."
echo "Pass --clang <path>, set CLANG, or install Arm Toolchain for Embedded:"
echo " https://github.com/arm/arm-toolchain/releases"
exit 1
fi
# Resolve to an absolute path. The example stage runs the build scripts from
# inside their own directories, so a relative compiler path would stop
# resolving there.
if [ -e "$CC" ]; then
CC="$(realpath "$CC")"
else
CC="$(command -v "$CC")"
fi
say ""
say "Using: $CC"
say " $("$CC" --version | head -1)"
# 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.
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_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"
)
# 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.
C_CORES="cortex_m0 cortex_m4 cortex_m23 cortex_m33 cortex_m55 cortex_a7 cortex_a53 cortex_r5"
# 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.
#
# These fail with the GNU toolchain too, so they are not LLVM problems:
# arm9 arm11 need newlib multilib variants for those CPUs, which are
# cortex_r4 cortex_r5 not present in every GNU toolchain packaging.
EXAMPLES_EXPECTED_TO_FAIL="arm9 arm11 cortex_r4 cortex_r5"
failures=0
skipped=""
# --------------------------------------------------------------------------
say ""
say "== Assembly sources of every Arm gnu port =="
total=0
for dir in ports/*/gnu/src ports_smp/*/gnu/src ports_module/*/gnu/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="$*"
for src in "$dir"/*.S "$dir"/*.s; do
[ -f "$src" ] || continue
total=$((total + 1))
output="$("$CC" --target="$target" -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' ' ')"
fi
# --------------------------------------------------------------------------
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="$*"
count=0; bad=0
for src in common/src/*.c; do
count=$((count + 1))
output="$("$CC" --target="$target" -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" | grep "error:" | head -3 | 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 lld =="
example_ok=0
example_total=0
example_known=""
for dir in ports/*/gnu/example_build ports_smp/*/gnu/example_build; do
[ -f "$dir/build_threadx.sh" ] && [ -f "$dir/build_threadx_sample.sh" ] || continue
core="$(echo "$dir" | cut -d/ -f2)"
case " $EXAMPLES_EXPECTED_TO_FAIL " in
*" $core "*) example_known="$example_known $core"; continue ;;
esac
example_total=$((example_total + 1))
rm -f "$dir"/*.o "$dir"/*.a "$dir"/*.out "$dir"/*.map 2>/dev/null || true
log="$(cd "$dir" && TOOLCHAIN=atfe ATFE_CLANG="$CC" ./build_threadx.sh 2>&1 && \
TOOLCHAIN=atfe ATFE_CLANG="$CC" ./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
fi
# --------------------------------------------------------------------------
say ""
if [ "$failures" -eq 0 ]; then
say "All LLVM toolchain checks passed."
exit 0
fi
echo "$failures LLVM toolchain check(s) failed."
exit 1