mirror of
https://github.com/eclipse-threadx/threadx.git
synced 2026-10-06 06:59:08 +08:00
* Made the example builds work with LLVM, and fixed four that were broken on Linux The gnu example builds are the natural LLVM path: Arm Toolchain for Embedded is LLVM based, consumes GNU ld linker scripts, and needs none of the scatter files or Arm DS projects the ac6 examples carry. So rather than port the ac6 examples, teach the gnu scripts to drive either toolchain. Each script now selects the toolchain from a TOOLCHAIN variable that defaults to gnu, so every existing invocation behaves exactly as before. TOOLCHAIN=atfe switches the compiler, adds the target triple, passes the entry symbol through to the linker rather than to the driver, and links the toolchain's own semihosting library in place of --specs=nosys.specs, which is a GCC spec file mechanism with no LLVM equivalent. That last choice avoids adding a syscall stub source to every example. The scripts are parameterised rather than duplicated. Copies of build scripts would drift the first time one side was edited, which is the failure this repository has just spent several changes recovering from. Four sample scripts could not run on a case-sensitive filesystem at all. They compiled MP_PrivateTimer.s and V7.s while the files on disk are MP_PrivateTimer.S and v7.s, and the Cortex-A5 and A9 scripts named MP_GIC.s where the file is MP_GIC.S. The link lines named V7.o accordingly. Corrected to match the files, which is why the Cortex-A5, A7, A8 and A9 examples now build on Linux where before they could not. Extend scripts/check_clang.sh to link the example builds as well as compile the sources, since compiling proves the sources parse while only linking exercises entry symbols, linker scripts and the C library together. Eight examples are listed as not expected to link, each with its reason, so the gaps stay visible rather than being silently skipped. Five of them fail with the GNU toolchain too and are therefore not LLVM problems: the Cortex-M0 example's crt0 references __text_load_start__, __text_start__ and __text_end__, which its linker script never defines, while the Cortex-M4 script defines the equivalents; the arm9, arm11, Cortex-R4 and Cortex-R5 examples need newlib multilib variants that are not present in every GNU toolchain packaging. The Cortex-A12, A15 and A17 examples fail only with LLVM, because their link line omits -nostartfiles so the toolchain's own crt0 is linked and wants picolibc's __data_start, __data_source, __data_size and __bss_size, which their linker script does not define. Verified by building every example with both toolchains. Seven link with both: Cortex-A5, A7, A8, A9, M3, M4 and M7. The GNU results are unchanged where they worked before, and now also succeed for the four scripts with the case bug. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com> * Resolved the compiler path before running the example builds The example stage runs each build script from inside its own directory, so a relative path passed with --clang stopped resolving there and every example build failed instantly. Local runs passed an absolute path and did not show it; the CI job passes a path relative to the workspace root, which did. Resolve the compiler to an absolute path once, before any directory change, and print it so the toolchain in use is visible in the log. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com> * Parameterised the archiver as well, and stopped the check hiding failures The toolchain selection covered the compiler but not arm-none-eabi-ar, which the scripts invoke 628 times to assemble the library archive. A machine with an LLVM toolchain and no GNU one therefore could not build any example, which is exactly the situation in CI: the runner has no Arm GNU toolchain installed. Local runs had one, so this only appeared once the job ran. Select the archiver alongside the compiler, taking llvm-ar from beside clang in the toolchain. The four scripts that call arm-none-eabi-ld directly are left alone: they are arm9, arm11, Cortex-R4 and Cortex-R5, all already listed as not expected to link, and their invocations are specific to GNU ld in ways that parameterising would not resolve. The check reported "example build produced no image" and then filtered the log for lines containing "error", which hid the actual cause, since a missing tool reports "command not found" or "No such file or directory". It now prints the tail of the log. That filtering cost two CI round trips to diagnose something the first run already knew. Verified by shadowing arm-none-eabi-gcc, arm-none-eabi-ar, arm-none-eabi-ld and the aarch64 equivalents with stubs that fail loudly, then running the whole check: all 711 sources assemble, all eight profiles compile and all seven example builds link without any GNU tool being invoked. The GNU default path still produces an identical image. The diagnostics were confirmed by pointing the archiver at a name that does not exist and checking that the reason appears. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
274 lines
11 KiB
Bash
Executable File
274 lines
11 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. Listed
|
|
# explicitly rather than silently skipped, so the gaps stay visible.
|
|
#
|
|
# These fail with the GNU toolchain too, so they are not LLVM problems:
|
|
# cortex_m0 cortexm0_crt0.S references __text_load_start__,
|
|
# __text_start__ and __text_end__, which its linker
|
|
# script never defines. The Cortex-M4 script defines the
|
|
# equivalents, so this is a gap in that one example.
|
|
# arm9 arm11 need newlib multilib variants for those CPUs, which are
|
|
# cortex_r4 cortex_r5 not present in every GNU toolchain packaging.
|
|
#
|
|
# This one is specific to LLVM:
|
|
# cortex_a12 a15 a17 their link line omits -nostartfiles, so the toolchain's
|
|
# own crt0 is linked, and it needs picolibc's
|
|
# __data_start, __data_source, __data_size and __bss_size,
|
|
# which the example's linker script does not define.
|
|
EXAMPLES_EXPECTED_TO_FAIL="arm9 arm11 cortex_m0 cortex_r4 cortex_r5 cortex_a12 cortex_a15 cortex_a17"
|
|
|
|
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; 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
|