Skip to content

Compiled the module manager C sources, which no check had ever built - #689

Merged
fdesbiens merged 3 commits into
eclipse-threadx:devfrom
fdesbiens:chore/compile-module-manager-c-sources
Sep 9, 2026
Merged

Compiled the module manager C sources, which no check had ever built#689
fdesbiens merged 3 commits into
eclipse-threadx:devfrom
fdesbiens:chore/compile-module-manager-c-sources

Conversation

@fdesbiens

Copy link
Copy Markdown
Contributor

Follow-up to #672.

Problem

#672 corrected this script's assembly glob and brought the module ports into the count — but only their assembly. Their C stayed outside every check:

  • 27 files of portable module manager under common_modules/module_manager/src
  • the per-port code under ports_module/<core>/gnu/module_manager/src

By this script's own standard, stated three times in its comments — a port that is simply absent from the count reads as covered293 files across nine Arm module ports were compiled by nothing, with either compiler.

Change

A sixth stage compiles the portable module manager plus each port's own C, once per Arm module port. Every 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.

Two details the ports themselves dictate, both found by running the stage rather than by reading:

SMP ports take common_smp/inc. Pairing cortex_a35_smp with the single-core headers hid _tx_thread_smp_protect and _tx_thread_smp_unprotect behind implicit declarations and lost tx_thread_smp_core_executing from TX_THREAD — fourteen files reporting errors for a port that builds correctly. That was the harness being wrong, not the port.

TrustZone ports need -mcmse, or cmse_nonsecure_entry is silently ignored rather than honoured. tx_thread_secure_stack.c additionally carries GCC's optimize attribute, which clang does not implement. That one is a genuine toolchain divergence in a file GCC builds cleanly, so it is suppressed by name rather than left to fail.

Result

== Module manager C sources, one per Arm module port ==
  cortex_a35: 30 of 30 compiled
  cortex_a35_smp: 30 of 30 compiled
  cortex_a7: 33 of 33 compiled
  cortex_m0+: 32 of 32 compiled
  cortex_m23: 36 of 36 compiled
  cortex_m3: 32 of 32 compiled
  cortex_m33: 36 of 36 compiled
  cortex_m4: 32 of 32 compiled
  cortex_m7: 32 of 32 compiled

Full run green, all stages, including the example builds.

The stage was verified to actually fail

A check that only ever passes proves nothing, so defects were injected into a throwaway worktree: one in common_modules, one in a single port's own source. The shared defect is reported under every port; the port-specific one only under that port; counts decrement correspondingly.

Interaction with #639

The stage globs ports_module/*/gnu/module_manager/src, so a new module port is picked up automatically with no edit here — which is the point, but it means #639 (Cortex-R52 module manager port) is affected.

Running this stage against #639's head, nine ports pass and R52 reports:

ports_module/cortex_r52/gnu/module_manager/src/txm_module_manager_offset_check.c:58:
  fatal error: 'mpu.h' file not found

mpu.h exists only under ports/cortex_r52/gnu/example_build/{fvp_baser_aemv8r,s32z280_evb}/. With either on the include path the file compiles cleanly, so it is not broken — it is a port source depending on a board-specific header from an example build directory, and it cannot be compiled as a port source on its own. Worth noting that the two board copies of mpu.h differ, and this file exists to statically assert offsets the port's assembly hard-codes, so which board is reachable is not obviously immaterial.

That is for #639 to resolve; flagging it here because merging this first will surface it there.

fdesbiens and others added 3 commits September 1, 2026 17:26
eclipse-threadx#672 corrected this script's assembly glob and brought the module ports into
the count, but only their assembly. Their C stayed outside every check: 27
files of portable module manager under common_modules, plus the per-port code
under ports_module/<core>/gnu/module_manager/src. By this script's own standard
-- a port simply absent from the count reads as covered -- 293 files across
nine Arm module ports were compiled by nothing, with either compiler.

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. Two details the ports themselves
dictate:

  An SMP port's control blocks come from common_smp. Pairing cortex_a35_smp
  with the single-core headers hid _tx_thread_smp_protect and
  _tx_thread_smp_unprotect behind implicit declarations and lost
  tx_thread_smp_core_executing from TX_THREAD, so fourteen files reported
  errors for a port that builds correctly.

  The TrustZone ports carry cmse_nonsecure_entry, which needs -mcmse to be
  honoured rather than ignored. tx_thread_secure_stack.c also carries GCC's
  optimize attribute, which clang does not implement; that divergence is
  suppressed by name, for a file GCC builds cleanly.

All nine ports compile: 293 of 293. Verified that the stage fails as intended
by injecting a defect into a throwaway worktree -- a defect in common_modules
is reported under every port, one in a port's own source only under that port.

Assisted-by: Claude Code (Opus 5)
Three corrections to the new stage, all found by running it against dev
rather than against the tree it was written on.

The workflows did not trigger on common_modules. Both check_clang.yml and
check_gcc.yml list ports_module but not common_modules, and the portable
module manager under it is the larger half of what the stage compiles --
28 of the 31 to 37 files each port builds, plus two include directories.
A change there left the stage unrun, which is the same "absent from the
count reads as covered" that the stage exists to close. Both lists gain
common_modules, and they stay identical to each other as the comment in
each asks.

A deliberate deprecation notice read as a build failure. Since this PR
was opened, txm_module_manager_absolute_load.c gained a #pragma message
steering callers to the extended entry point. The stage treats any
compiler output as a failure, so that one notice failed every port: nine
failures on a tree where nothing is wrong. Pragma messages are now
waived for the stage, because a notice to callers is not a defect in the
file that carries it.

That failure also printed nothing. Both C stages report by grepping the
output for "error:", so a diagnostic that is not an error produced a bare
FAIL line with no reason under it, and the only way to learn the reason
was to reproduce the compile by hand. Both stages now fall back to
showing what the compiler actually said.

The TrustZone attribute waiver is narrowed to the one file that needs it.
tx_thread_secure_stack.c carries GCC's optimize attribute, which clang
does not implement; it is the only file among the 300-odd this stage
compiles that does. Waiving the warning for the whole port would have
swallowed a stray unknown attribute anywhere else in it.

Verified with the same toolchain CI uses, ATfE 22.1.0: the full script
passes, and every port compiles every file.

  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

The counts are each one higher than this PR first reported, because
txm_module_manager_absolute_load_extended.c has landed since. The stage
picked it up with no edit, which is what globbing the directories was for.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
@fdesbiens

Copy link
Copy Markdown
Contributor Author

Merged dev and pushed three corrections, all found by running the stage against current dev rather than the tree it was written on. Verified with the same toolchain CI uses, ATfE 22.1.0.

The stage would have failed on merge

Run against dev as it stands, every port failed on txm_module_manager_absolute_load.c — nine failures, rc=1:

== Module manager C sources, one per Arm module port ==
  FAIL: cortex_a35: common_modules/module_manager/src/txm_module_manager_absolute_load.c
  cortex_a35: 30 of 31 compiled
  ...
9 LLVM toolchain check(s) failed.

Nothing is wrong with the file. Since this PR was opened, #699 gave it a #pragma message steering callers to the extended entry point:

warning: txm_module_manager_absolute_load() is deprecated. Use
txm_module_manager_absolute_load_extended() ... [-W#pragma-messages]

The stage treats any compiler output as a failure, and it compiles that file once per port, so one deliberate notice failed all nine. Pragma messages are now waived for the stage — a notice to callers is not a defect in the file carrying it.

This is the check doing its job before it even lands: the interaction only exists because the green atfe run on this PR predates #699, and nothing else in CI compiles these files.

That failure printed no reason

Both C stages report by grepping the output for error:, so a diagnostic that is not an error gave a bare FAIL: line with nothing under it. Working out why cost a hand-reproduced compile, and would have cost the same in CI. Both stages now fall back to showing what the compiler actually said.

The workflows did not trigger on common_modules

clang_check.yml and gcc_check.yml both list ports_module but not common_modules. The portable module manager under it is the larger half of what this stage compiles — 28 of the 31–37 files per port, plus two include directories — so a change there left the stage unrun. That is the same "absent from the count reads as covered" this stage exists to close. Both lists gain it, and stay identical as the comment in each asks.

The TrustZone waiver is narrowed

-Wno-unknown-attributes applied to every file of the m23 and m33 ports. tx_thread_secure_stack.c is the only file among the 300-odd compiled here that carries GCC's optimize attribute (checked), so the waiver is now scoped to it; a stray unknown attribute anywhere else in those ports is still reported. Confirmed load-bearing — without it that file warns, with it the port is silent.

Result

Full script green, rc=0, all stages:

  cortex_a35: 31 of 31        cortex_a35_smp: 31 of 31    cortex_a7: 34 of 34
  cortex_m0+: 33 of 33        cortex_m23: 37 of 37        cortex_m3: 33 of 33
  cortex_m33: 37 of 37        cortex_m4: 33 of 33         cortex_m7: 33 of 33

Each count is one higher than first reported, because txm_module_manager_absolute_load_extended.c has landed since. The stage picked it up with no edit here, which is what globbing the directories was for.

Still open

check_gcc.sh has no equivalent stage, so GCC — the project's default compiler — still does not build these files. Its module port handling reaches assembly only. Worth a follow-up rather than widening this PR to two compilers; the common_modules path entry added to gcc_check.yml here is already in place for it.

@fdesbiens
fdesbiens merged commit 8be4c45 into eclipse-threadx:dev Sep 9, 2026
10 checks passed
@fdesbiens
fdesbiens deleted the chore/compile-module-manager-c-sources branch September 9, 2026 18:39
fdesbiens added a commit to fdesbiens/threadx-fd that referenced this pull request Sep 9, 2026
…header

txm_module_manager_offset_check.c reached into the board's mpu.h for
MPU_MODULE_LOAD_REGION. That header lives only under the two example build
directories, so the file could not be compiled as a port source at all --
which nothing noticed until eclipse-threadx#689 landed and started compiling the module
manager C of every Arm module port. The cortex_r52 lane then reported:

    txm_module_manager_offset_check.c:58:10: fatal error: 'mpu.h' file not found

The pairing was the wrong way round. The region number describes what this
port's scheduler does -- tx_thread_schedule.S carries it as a .equ -- so a
board support package header is not where it belongs. It now sits in
txm_module_port.h beside TXM_MODULE_MPU_FIRST_REGION and
TXM_MODULE_MPU_TOTAL_ENTRIES, the constants it is asserted against, and the
offset check compiles with no board on the include path.

The board check is kept rather than dropped. Where a board header is
reachable, __has_include pulls it in and a third assertion holds the board's
MPU_MODULE_LOAD_REGION to the port's value, so a board support package
cannot quietly name a different region than the scheduler enables. Both
existing assertions keep exactly the strength they had; the first was always
a header against a literal.

Verified with the pinned toolchains:

  check_clang.sh   all checks pass, cortex_r52 34 of 34 compiled, was 33 of 34
  check_gcc.sh     all checks pass
  compiles standalone, and against both board headers
  a board set to 17 instead of 16 fails the new assertion, as intended

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
fdesbiens added a commit that referenced this pull request Sep 10, 2026
…716)

#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/<core>/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) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant