From 6e703597542b1252a4ae363107a13df186637d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Wed, 9 Sep 2026 17:35:06 -0400 Subject: [PATCH] Compiled the module manager C sources with GCC, as clang already did #689 added the module manager stage to check_clang.sh alone. The GCC half was never written, 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//gnu/module_manager/src, across nine Arm module ports. The stage is deliberately check_clang.sh's, port for port and header for header, because a port covered by one check and not the other implies a parity the checks list does not have. The same two details the ports dictate carry over: an SMP port's control blocks come from common_smp rather than common, and the TrustZone ports need -mcmse for their cmse_nonsecure_entry functions to be honoured rather than ignored. The clang-only waiver does not: GCC implements the optimize attribute that tx_thread_secure_stack.c carries, so nothing needs suppressing for that file. One divergence is forced by the toolchain. txm_module_manager_absolute_load.c carries a #pragma message steering callers to the extended entry point, and the C stages treat any compiler output as a failure. check_clang.sh silences it with -Wno-#pragma-messages; GCC has no equivalent, and neither -Wno-pragmas nor any other -W option suppresses the note -- verified with 14.3.rel1. The note is therefore filtered out of the stage's output instead, together with the source quote GCC prints beneath it. The filter stops at the next line that begins a diagnostic of its own, so an error immediately following a waived note is still reported; that case is what the injected-defect run below checks. The workflow needed no trigger change: #689 added common_modules/** to both path lists in advance, for the stage that had yet to arrive. Its header comment is brought in line with what the workflow now runs. Verified with the toolchain CI pins, arm-gnu-toolchain 14.3.rel1, both triples. The full script passes and every port compiles every file, matching the counts check_clang.sh reports for the same nine ports: cortex_a35 31/31 cortex_a35_smp 31/31 cortex_a7 34/34 cortex_m0+ 33/33 cortex_m23 37/37 cortex_m3 33/33 cortex_m33 37/37 cortex_m4 33/33 cortex_m7 33/33 Verified that the stage fails as intended by injecting defects into a throwaway worktree: one in common_modules on the line straight after the waived pragma note, reported under all nine ports, and one in a single port's own source, reported only under that port. Assisted-by: Claude Code (Opus 5) --- .github/workflows/gcc_check.yml | 14 ++-- scripts/check_gcc.sh | 109 ++++++++++++++++++++++++++++++-- 2 files changed, 112 insertions(+), 11 deletions(-) diff --git a/.github/workflows/gcc_check.yml b/.github/workflows/gcc_check.yml index eabbd0a7e..f26cf5704 100644 --- a/.github/workflows/gcc_check.yml +++ b/.github/workflows/gcc_check.yml @@ -1,10 +1,11 @@ name: gcc_check -# Builds the Arm ports with the Arm GNU toolchain, in five stages: assemble +# Builds the Arm ports with the Arm 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, then link the example builds, both the script-driven ones and those -# driven by CMake. +# profile, compile the module manager C sources once per Arm module port, then +# link the example builds, both the script-driven ones and those driven by +# CMake. # # Why this exists: GCC is the project's declared default compiler (AGENTS.md, # "The default compiler for the project is GCC 14 on Linux") and until this @@ -13,9 +14,10 @@ name: gcc_check # guarded than the GNU one, on ports whose directory is literally named gnu. # # What it covers: 840 assembly sources across 40 port families, 469 of them -# again behind feature macros, common/src for nine cores, 42 script-driven -# example links and the five Cortex-R52 CMake images. Every skip is printed by -# name with a reason -- run scripts/check_gcc.sh --help, or read its header. +# again behind feature macros, common/src for nine cores, 302 module manager C +# files across the nine Arm module ports, 42 script-driven example links and +# the five Cortex-R52 CMake images. Every skip is printed by name with a reason +# -- run scripts/check_gcc.sh --help, or read its header. # # What it does not cover: it compiles and links and **executes nothing**. The # Cortex-R52 FVP ctest suite is not part of it. RISC-V, MIPS, RX and ARC are diff --git a/scripts/check_gcc.sh b/scripts/check_gcc.sh index ae603ea61..190d50f8b 100755 --- a/scripts/check_gcc.sh +++ b/scripts/check_gcc.sh @@ -14,13 +14,13 @@ # SPDX-License-Identifier: MIT and CC0-1.0 ############################################################################## -# Builds the Arm ports with the GNU toolchain, in six stages: assemble every +# 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, 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. +# 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 \ @@ -93,6 +93,25 @@ 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 @@ -420,6 +439,86 @@ if [ "$asm_only" -eq 0 ]; then 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//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 ""