Repair constrained nRF52 release builds and retain size-failure diagnostics

This commit is contained in:
mikecarper committed 2026-09-13 09:37:50 -07:00
1 parent dd64eae4f9
commit 5e10a319ab
4 files changed
+157 -14

No files matched your search

+1
View File
@@ -116,6 +116,7 @@ jobs:
- name: Verify firmware memory budgets and nRF52 Bluetooth startup
run: |
bash test/test_build_size_detection.sh
python3 -B test/test_firmware_ram.py
python3 -B test/test_cascade_release_package.py
python3 -B test/test_t096_full_memory.py
+48 -14
View File
@@ -2696,6 +2696,9 @@ requires_dram_limited_neighbors() {
# 8 KiB static reserve at 254 neighbors. Use their board-declared 50-entry
# tables when the OTA manager is included.
case "${1,,}" in
heltec_t096_repeater_lora_ota_no_external_sensors|\
heltec_t1_repeater_lora_ota_no_external_sensors)
return 0 ;;
generic_e22_sx1262_repeater_lora_ota_no_external_sensors|\
generic_e22_sx1268_repeater_lora_ota_no_external_sensors|\
heltec_v2_repeater_lora_ota_no_external_sensors|\
@@ -3266,7 +3269,8 @@ apply_nrf52_size_profile() {
if [ "${PIO_ENV_PLATFORM_BY_NAME[$env_name]:-}" != "NRF52_PLATFORM" ]; then
return 0
fi
if ! is_nrf52_companion_radio_full_target "$env_name" \
if ! is_repeater_role_target "$env_name" \
&& ! is_nrf52_companion_radio_full_target "$env_name" \
&& { ! is_lora_ota_build "$env_name" \
|| ! is_lora_ota_only_target "$env_name"; }; then
return 0
@@ -3276,10 +3280,26 @@ apply_nrf52_size_profile() {
# runtime software Ed25519 fallback is linked alongside CC310, that setting
# fully expands repeated Curve25519 arithmetic and wastes tens of kilobytes.
# Keep hardware crypto, RNG mixing, the software fallback, and board features;
# select the size optimizer for both constrained self-updating images and
# Full Companion source images, especially their diagnostic profile.
# select the size optimizer for repeaters, constrained self-updating images
# and Full Companion source images, especially their diagnostic profile.
# Full-sensor repeaters also need this after adding the second radio profile;
# changing optimization preserves their sensors and protocol features.
append_platformio_build_unflags "-Ofast"
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -Os"
# These color-display repeaters reserve a 64 KiB reset-retained OTA arena
# plus a 25 KiB framebuffer. Keep their board-declared 50 neighbours and
# the complete flood-rule engine with a smaller table so runtime startup
# allocations also fit; do not lower the heap qualification requirement.
case "${env_name,,}" in
heltec_t096_repeater_lora_ota_no_external_sensors|\
heltec_t1_repeater_lora_ota_no_external_sensors)
append_platformio_build_unflags "-DFLOOD_PACKET_FILTER_SLOTS=63"
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DFLOOD_PACKET_FILTER_SLOTS=16"
record_build_reduction \
"mesh.flood_rules limited to 16 by measured internal RAM; complete rule engine, color display, GPS, and OTA retained"
;;
esac
}
apply_lora_ota_no_external_sensors_profile() {
@@ -4029,6 +4049,29 @@ restore_platformio_build_flags() {
fi
}
run_pio_with_size_detection() {
local build_output_log
local build_status
local -a output_statuses
# SCons can delete the environment's build directory when flags change.
# A tee log inside that directory is then unlinked while still open, hiding
# the size error from the measured-size fallback after PlatformIO exits.
build_output_log=$(mktemp "${TMPDIR:-/tmp}/meshcore-build-XXXXXX.log") || return 1
pio "$@" 2>&1 | tee "$build_output_log"
output_statuses=("${PIPESTATUS[@]}")
build_status=${output_statuses[0]}
if [ "${output_statuses[1]}" -ne 0 ]; then
build_status=1
elif [ "$build_status" -ne 0 ] \
&& grep -Eiq \
'will not fit in region|region .+ overflowed by|section .+ will not fit|sketch too big|program size is greater than maximum|exceed(s|ing).*(flash|partition|app)' \
"$build_output_log"; then
build_status=42
fi
rm -f -- "$build_output_log"
return "$build_status"
}
build_firmware() {
local env_name=$1
local pio_env_name
@@ -4271,17 +4314,8 @@ build_firmware() {
pio_run_args+=(-t mergebin)
fi
if [ "$build_status" -eq 0 ]; then
local build_output_dir="${PIO_BUILD_DIR_OVERRIDE:-${PLATFORMIO_BUILD_DIR:-.pio/build}}/${pio_env_name}"
local build_output_log="${build_output_dir}/.meshcore-build-output.log"
mkdir -p -- "$build_output_dir"
pio "${pio_run_args[@]}" 2>&1 | tee "$build_output_log"
build_status=${PIPESTATUS[0]}
if [ "$build_status" -ne 0 ] \
&& grep -Eiq \
'will not fit in region|region .+ overflowed by|section .+ will not fit|sketch too big|program size is greater than maximum|exceed(s|ing).*(flash|partition|app)' \
"$build_output_log"; then
build_status=42
fi
run_pio_with_size_detection "${pio_run_args[@]}"
build_status=$?
fi
if [ "$build_status" -eq 0 ]; then
collect_build_artifacts "$env_name" "$env_platform" "$pio_env_name" "$firmware_filename"
+14
View File
@@ -95,6 +95,20 @@ before choosing an update method.
## Reproduce the release
Follow-up builds repair the constrained profiles without relaxing qualification:
- nRF52 repeaters use `-Os` instead of `-Ofast`, retaining their sensor drivers
and protocol features while reducing flash use.
- T096 and T1 LoRa OTA repeaters retain their board-defined 50 neighbors and
use 16 flood-rule slots. The complete rule engine, color display, GPS and OTA
remain enabled. The T096's available startup heap rises from 72,492 to 81,468
bytes, and the T1's from 72,868 to 81,844 bytes. Required startup allocations
fall from 88,410 to 79,010 bytes. These are linked capacity checks, not soak
measurements. Full Companion capacities are unchanged.
- Size-failure detection keeps its temporary output log outside PlatformIO's
build tree, so SCons cleanup cannot erase the evidence needed by the measured
size fallback. CI exercises cleanup, ordinary compiler failures and log errors.
Use a clean checkout of the release commit and a fresh output directory:
```bash
+94
View File
@@ -0,0 +1,94 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
source build.sh
fail() { echo "test_build_size_detection: $*" >&2; exit 1; }
test_root=$(mktemp -d "${TMPDIR:-/tmp}/meshcore-size-tests.XXXXXX")
test_root=$(realpath "$test_root")
[[ "$test_root" == /*/meshcore-size-tests.* ]] || fail "unexpected temporary path"
trap 'rm -rf -- "$test_root"' EXIT
export TMPDIR=$test_root
managed_dir=$test_root/build/test
pio() {
# Model the real SCons cleanup that previously unlinked the tee log.
[[ "$managed_dir" == "$test_root/"* ]] || return 99
rm -rf -- "$managed_dir"
touch "$test_root/pio-called"
printf '%s\n' "$pio_message"
return "$pio_result"
}
check_status() {
local expected=$1 actual=0
mkdir -p "$managed_dir"
if run_pio_with_size_detection run -e test > "$test_root/output" 2>&1; then
actual=0
else
actual=$?
fi
[ "$actual" -eq "$expected" ] || fail "expected status $expected, got $actual"
[ ! -d "$managed_dir" ] || fail "fake PlatformIO did not clean its build directory"
if compgen -G "$test_root/meshcore-build-*.log" > /dev/null; then
fail "temporary build-output log was not released"
fi
}
pio_result=1
for pio_message in \
"firmware.elf section .text will not fit in region FLASH" \
"region FLASH overflowed by 43080 bytes" \
"Error: The program size is greater than maximum allowed"; do
check_status 42
grep -Fq "$pio_message" "$test_root/output" || fail "compiler output was lost"
done
pio_message='error: missing declaration'
check_status 1
pio_result=2
check_status 2
pio_result=0
pio_message='compilation succeeded'
check_status 0
# An output failure must not look like a successful or merely oversized build.
tee() { cat > /dev/null; return 1; }
check_status 1
unset -f tee
rm -f "$test_root/pio-called"
TMPDIR=$test_root/missing
if run_pio_with_size_detection run -e test > /dev/null 2>&1; then
fail "missing log directory was accepted"
fi
[ ! -e "$test_root/pio-called" ] || fail "PIO ran without diagnostic storage"
# Keep sensors and protocol features while shrinking nRF52 repeater code.
PIO_ENV_PLATFORM_BY_NAME[ikoka_nano_nrf_22dbm_repeater]=NRF52_PLATFORM
PLATFORMIO_BUILD_FLAGS=''
PLATFORMIO_BUILD_UNFLAGS=''
apply_nrf52_size_profile ikoka_nano_nrf_22dbm_repeater
[[ "$PLATFORMIO_BUILD_FLAGS" == *' -Os'* ]] || fail "full-sensor repeater omitted size optimization"
[[ "$PLATFORMIO_BUILD_UNFLAGS" == *'-Ofast'* ]] || fail "aggressive optimization was not removed"
[[ "$PLATFORMIO_BUILD_FLAGS" != *'-UENV_INCLUDE_'* ]] || fail "sensor support was reduced"
for target in Heltec_t096_repeater_lora_ota_no_external_sensors Heltec_t1_repeater_lora_ota_no_external_sensors; do
PIO_ENV_PLATFORM_BY_NAME[$target]=NRF52_PLATFORM
PLATFORMIO_BUILD_FLAGS=''
PLATFORMIO_BUILD_UNFLAGS=''
apply_repeater_neighbor_capacity "$target"
apply_nrf52_size_profile "$target"
[[ "$PLATFORMIO_BUILD_FLAGS" == *'-DMAX_NEIGHBOURS=50'* ]] || fail "$target omitted RAM-safe neighbours"
[[ "$PLATFORMIO_BUILD_FLAGS" == *'-DFLOOD_PACKET_FILTER_SLOTS=16'* ]] || fail "$target omitted RAM-safe rule table"
[[ "$PLATFORMIO_BUILD_FLAGS" != *'MESH_MIN_RUNTIME_HEAP'* ]] || fail "runtime RAM guard was overridden"
done
for target in Heltec_t096_companion_radio_full_femon RAK_4631_repeater; do
PIO_ENV_PLATFORM_BY_NAME[$target]=NRF52_PLATFORM
PLATFORMIO_BUILD_FLAGS=''
PLATFORMIO_BUILD_UNFLAGS=''
apply_nrf52_size_profile "$target"
[[ "$PLATFORMIO_BUILD_FLAGS" != *'FLOOD_PACKET_FILTER_SLOTS'* ]] || fail "$target received unrelated table reductions"
[[ "$PLATFORMIO_BUILD_FLAGS" != *'MAX_NEIGHBOURS'* ]] || fail "$target received unrelated neighbour reductions"
done
PIO_ENV_PLATFORM_BY_NAME[Heltec_v4_repeater]=ESP32_PLATFORM
PLATFORMIO_BUILD_FLAGS=''
apply_nrf52_size_profile Heltec_v4_repeater
[ -z "$PLATFORMIO_BUILD_FLAGS" ] || fail "nRF52 policy changed an ESP32 target"
echo 'Build-size diagnostics and constrained nRF52 recipes passed.'