mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-24 08:12:28 +00:00
Server roles (slice 7) - CommonCLI split into upstream's shape; region CLI moved into CommonCLI; ClientACL, RegionMap, TransportKeyStore and both server meshes in upstream shape over an Arduino compat shim (helpers/compat: Arduino.h, Stream/Print, Serial, SHA256, FILESYSTEM/File over Zephyr fs) - room server processAck port; `get acl` prints as upstream - room: default name Room-XXXX, display/input rotation, live radio UI refresh Event loops (slice 7b) - repeater and room server share server_main_common with a ServerRole descriptor; shared event bits in src/mesh_events.h - room server deadline-driven (idle wakes 2.2/s -> 0.08/s); companion loop() runs on housekeeping (closes the lazy contact-flush gap) - `get loop.wakes` in debug builds (CONFIG_ZEPHCORE_LOOP_WAKE_STATS) Companion transports and WiFi (slice 8) - upstream MultiSerialInterface: BLE, USB/UART and TCP are each a BaseSerialInterface, every connected transport gets every frame; CompanionSerial holds a lossless reply; shared frame_txq for BLE/TCP - SerialCompanionTransport and LinuxTCPTransport replaced by TcpCompanionTransport; ZephyrBLE split (gatt layout, DFU) - WiFi companion (upstream wifi.ssid/pwd/enabled CLI, TCP port 5000) on boards declaring `capabilities: wifi` in zephcore.yml: the PSRAM ESP32-S3 boards, plus Heltec V3 (140 contacts), Wireless Tracker (130) and both C6 boards (280); ESP32-S3 without PSRAM gets bounded WiFi buffers and a measured 72 KB heap; PSRAM boards' internal heap 72 KB - TCP queues: own depth (ZEPHCORE_TCP_QUEUE_SIZE=8), RX waits instead of dropping - RAM: companion ClientACL dropped; main/BT-TX/TCP stacks sized from measured peaks (-7 KB nRF companions, -9 KB ESP32 companions) - ESP32 WiFi stopped working under ~200 frames/s unicast. Fixed by: patches/zephyr/0017 never block the WiFi task in the RX callback; patches/zephyr/0018 lock shared_multi_heap (heap corruption with the WiFi heap in PSRAM); patches/modules/hal_espressif/0001 backport of hal_espressif af97fb02df (PSRAM realloc) Storage and prefs (slice 9) - prefs are now prefs.json through upstream's ConfigSerializer (ported verbatim), with upstream's key names for shared fields and ZephCore's own under "zc"; on first boot the binary new_prefs / repeater prefs is migrated and kept, so a downgrade still boots with the old settings - helpers/PrefsCodec: both legacy binary layouts (companion 272 B, server 311 B) as Zephyr-free codecs; sanitizeNodePrefs is the only validator - fix: server prefs could be replaced by a truncated file when a write failed; identity, prefs and channel writes go through one checked atomic replace (ZephyrFsUtil) - fix: a corrupt cad_auto byte turned adaptive CAD off instead of the default on - fix: a deliberate 300 s GPS duty on a repeater was rewritten to 48 h on every boot (that migration now runs on legacy files only) - one identity loader for all roles (IdentityFile); an unusable key file is always parked as .bad before regenerating - server store no longer stamps repeater defaults on room server and observer; companion first-boot volume policy moved into the store (adoptVolume) - ZephyrDataStore 1435 -> ~850 lines
251 lines
11 KiB
CMake
251 lines
11 KiB
CMake
# Apply patches/zephyr/*.patch and patches/modules/*/*.patch to the west tree.
|
|
#
|
|
# Unified diffs applied via `git apply` at configure time. Idempotent: a stamp
|
|
# file in each target tree tracks (patch hashes + target HEAD), and a matching
|
|
# stamp is re-verified against the working tree. Conflicts are fatal.
|
|
#
|
|
# This lives in its own module because it has to run from TWO places, and the
|
|
# order matters -- the same constraint as cmake/zephyr_new_files.cmake:
|
|
#
|
|
# - zephcore/CMakeLists.txt, for the application image.
|
|
# - zephcore/sysbuild/CMakeLists.txt, BEFORE find_package(Sysbuild).
|
|
#
|
|
# Under --sysbuild, sysbuild configures the MCUboot image first, and MCUboot
|
|
# runs a full devicetree pass over the same board DTS the app uses. A board
|
|
# whose DTS uses a property that only a patch adds to an upstream binding
|
|
# (lna-bypass-gpios, from 0003's semtech,sx126x-base.yaml hunk, in the board
|
|
# DTS of heltec_wifi_lora32_v43 / _v4_r8 / heltec_wireless_tracker_v2) then
|
|
# fails MCUboot's pass on a fresh tree -- "'lna-bypass-gpios' ... is not
|
|
# declared in 'properties:'" -- because the app's CMakeLists, the only place
|
|
# that used to apply the patches, has not run yet. CI never saw it: build.sh
|
|
# builds the non-sysbuild boards first, which patches the tree before any
|
|
# ESP32 sysbuild runs. A fresh checkout building one of those boards first
|
|
# did.
|
|
#
|
|
# Running twice per configure costs nothing: the second run finds the stamp
|
|
# matching and the patched paths dirty, and skips.
|
|
#
|
|
# Expects ZEPHCORE_SOURCE_DIR, ZEPHCORE_ZEPHYR_DIR and ZEPHCORE_MODULES_DIR to
|
|
# be set by the caller.
|
|
|
|
function(zephcore_apply_patches PATCH_DIR TARGET_DIR LABEL)
|
|
file(GLOB PATCH_FILES "${PATCH_DIR}/*.patch")
|
|
list(SORT PATCH_FILES)
|
|
if(NOT PATCH_FILES)
|
|
return()
|
|
endif()
|
|
# Stamp = hash(patch contents + target HEAD). Skip if unchanged.
|
|
execute_process(
|
|
COMMAND git rev-parse HEAD
|
|
WORKING_DIRECTORY "${TARGET_DIR}"
|
|
OUTPUT_VARIABLE _target_head
|
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
ERROR_QUIET
|
|
)
|
|
set(_hash_input "${_target_head}")
|
|
foreach(_pf ${PATCH_FILES})
|
|
file(MD5 "${_pf}" _h)
|
|
string(APPEND _hash_input "${_h}")
|
|
endforeach()
|
|
string(MD5 _stamp_hash "${_hash_input}")
|
|
set(_stamp "${TARGET_DIR}/.zephcore_${LABEL}.stamp")
|
|
if(EXISTS "${_stamp}")
|
|
file(READ "${_stamp}" _existing_hash)
|
|
string(STRIP "${_existing_hash}" _existing_hash)
|
|
if(_existing_hash STREQUAL _stamp_hash)
|
|
# The stamp hashes HEAD + patch contents and says nothing about the
|
|
# working tree. A `git reset --hard` / `git checkout -- .` in the
|
|
# target tree reverts our patched files but leaves this stamp behind
|
|
# (it is untracked), and `west update` at an already-current
|
|
# revision does not move HEAD -- so the hash still matches and the
|
|
# build would skip patching and compile a pristine tree. Depending
|
|
# on which patch was lost that is either a confusing configure-time
|
|
# failure or, worse, a silently unpatched binary.
|
|
#
|
|
# Verify instead of trusting: every patch here modifies tracked
|
|
# files only (none create or delete), so a patch that is applied
|
|
# always leaves its own paths dirty against HEAD. Paths clean
|
|
# against HEAD therefore means "not applied" exactly. Note this
|
|
# deliberately does NOT compare tree content to the patch: a dirty
|
|
# path is left alone, so the edit-the-driver-then-regenerate-the-
|
|
# patch workflow still builds your in-progress edits untouched.
|
|
set(_stamp_stale FALSE)
|
|
foreach(_pf ${PATCH_FILES})
|
|
execute_process(
|
|
COMMAND git apply --numstat "${_pf}"
|
|
WORKING_DIRECTORY "${TARGET_DIR}"
|
|
OUTPUT_VARIABLE _vfy_numstat
|
|
ERROR_QUIET
|
|
)
|
|
string(REGEX MATCHALL "[^\t\n]+\t[^\t\n]+\t[^\t\n]+"
|
|
_vfy_lines "${_vfy_numstat}")
|
|
set(_vfy_paths "")
|
|
foreach(_line ${_vfy_lines})
|
|
string(REGEX REPLACE "^[^\t]+\t[^\t]+\t" "" _p "${_line}")
|
|
list(APPEND _vfy_paths "${_p}")
|
|
endforeach()
|
|
if(_vfy_paths)
|
|
execute_process(
|
|
COMMAND git diff --quiet -- ${_vfy_paths}
|
|
WORKING_DIRECTORY "${TARGET_DIR}"
|
|
RESULT_VARIABLE _vfy_clean
|
|
OUTPUT_QUIET ERROR_QUIET
|
|
)
|
|
if(_vfy_clean EQUAL 0)
|
|
get_filename_component(_vfy_name "${_pf}" NAME)
|
|
message(STATUS
|
|
" [${LABEL}] Stamp says applied but ${_vfy_name} "
|
|
"is missing from the tree — re-applying all.")
|
|
set(_stamp_stale TRUE)
|
|
break()
|
|
endif()
|
|
endif()
|
|
endforeach()
|
|
if(NOT _stamp_stale)
|
|
message(STATUS " [${LABEL}] Patches already applied, skipping.")
|
|
return()
|
|
endif()
|
|
else()
|
|
message(STATUS " [${LABEL}] Patch set changed, re-applying...")
|
|
endif()
|
|
endif()
|
|
foreach(PATCH_FILE ${PATCH_FILES})
|
|
get_filename_component(PATCH_NAME ${PATCH_FILE} NAME)
|
|
set(PATCH_FILE_TO_APPLY "${PATCH_FILE}")
|
|
# Prepare LF/CRLF patch variants for robust matching across clones.
|
|
file(READ "${PATCH_FILE}" _patch_content)
|
|
string(REPLACE "\r\n" "\n" _patch_lf "${_patch_content}")
|
|
set(_patch_crlf "${_patch_lf}")
|
|
string(REPLACE "\n" "\r\n" _patch_crlf "${_patch_crlf}")
|
|
set(_tmp_patch_dir "${CMAKE_BINARY_DIR}/zephcore_patch_tmp/${LABEL}")
|
|
file(MAKE_DIRECTORY "${_tmp_patch_dir}")
|
|
set(_tmp_patch_lf "${_tmp_patch_dir}/${PATCH_NAME}.lf")
|
|
set(_tmp_patch_crlf "${_tmp_patch_dir}/${PATCH_NAME}.crlf")
|
|
file(WRITE "${_tmp_patch_lf}" "${_patch_lf}")
|
|
file(WRITE "${_tmp_patch_crlf}" "${_patch_crlf}")
|
|
# Dry-run check
|
|
execute_process(
|
|
COMMAND git apply --check "${PATCH_FILE_TO_APPLY}"
|
|
WORKING_DIRECTORY "${TARGET_DIR}"
|
|
RESULT_VARIABLE PATCH_CHECK
|
|
ERROR_VARIABLE PATCH_ERR
|
|
)
|
|
# Retry check with explicit LF/CRLF variants to avoid EOL drift issues.
|
|
if(NOT PATCH_CHECK EQUAL 0)
|
|
execute_process(
|
|
COMMAND git apply --check "${_tmp_patch_lf}"
|
|
WORKING_DIRECTORY "${TARGET_DIR}"
|
|
RESULT_VARIABLE PATCH_CHECK_LF
|
|
ERROR_VARIABLE PATCH_ERR_LF
|
|
)
|
|
if(PATCH_CHECK_LF EQUAL 0)
|
|
set(PATCH_FILE_TO_APPLY "${_tmp_patch_lf}")
|
|
set(PATCH_CHECK 0)
|
|
else()
|
|
execute_process(
|
|
COMMAND git apply --check "${_tmp_patch_crlf}"
|
|
WORKING_DIRECTORY "${TARGET_DIR}"
|
|
RESULT_VARIABLE PATCH_CHECK_CRLF
|
|
ERROR_VARIABLE PATCH_ERR_CRLF
|
|
)
|
|
if(PATCH_CHECK_CRLF EQUAL 0)
|
|
set(PATCH_FILE_TO_APPLY "${_tmp_patch_crlf}")
|
|
set(PATCH_CHECK 0)
|
|
else()
|
|
# Keep the most relevant error from the last attempted variant.
|
|
set(PATCH_ERR "${PATCH_ERR_CRLF}")
|
|
endif()
|
|
endif()
|
|
endif()
|
|
# Stale patches from previous build: reset affected files, retry
|
|
if(NOT PATCH_CHECK EQUAL 0)
|
|
# Extract affected file paths from numstat
|
|
execute_process(
|
|
COMMAND git apply --numstat "${PATCH_FILE_TO_APPLY}"
|
|
WORKING_DIRECTORY "${TARGET_DIR}"
|
|
OUTPUT_VARIABLE PATCH_NUMSTAT
|
|
ERROR_QUIET
|
|
)
|
|
# numstat format: "adds\tdels\tpath"
|
|
string(REGEX MATCHALL "[^\t\n]+\t[^\t\n]+\t[^\t\n]+" NUMSTAT_LINES "${PATCH_NUMSTAT}")
|
|
set(PATCH_PATHS "")
|
|
foreach(_line ${NUMSTAT_LINES})
|
|
string(REGEX REPLACE "^[^\t]+\t[^\t]+\t" "" _path "${_line}")
|
|
list(APPEND PATCH_PATHS "${_path}")
|
|
endforeach()
|
|
if(PATCH_PATHS)
|
|
# This discards any uncommitted edits in these files of the
|
|
# target tree (e.g. an in-progress driver change not yet
|
|
# regenerated into the patch) -- say so loudly.
|
|
message(WARNING " [${LABEL}] ${PATCH_NAME} does not apply; "
|
|
"resetting these files in ${TARGET_DIR} to HEAD, discarding "
|
|
"any local edits in them: ${PATCH_PATHS}")
|
|
execute_process(
|
|
COMMAND git checkout -- ${PATCH_PATHS}
|
|
WORKING_DIRECTORY "${TARGET_DIR}"
|
|
ERROR_QUIET
|
|
)
|
|
# Retry dry-run
|
|
execute_process(
|
|
COMMAND git apply --check "${PATCH_FILE_TO_APPLY}"
|
|
WORKING_DIRECTORY "${TARGET_DIR}"
|
|
RESULT_VARIABLE PATCH_CHECK
|
|
ERROR_VARIABLE PATCH_ERR
|
|
)
|
|
endif()
|
|
endif()
|
|
if(NOT PATCH_CHECK EQUAL 0)
|
|
message(FATAL_ERROR
|
|
"ZephCore patch FAILED to apply: ${PATCH_NAME}\n"
|
|
"Target tree: ${TARGET_DIR}\n"
|
|
"Error:\n${PATCH_ERR}\n"
|
|
"Upstream likely changed — rebase the patch:\n"
|
|
" cd ${TARGET_DIR}\n"
|
|
" git diff -- <file> # inspect current upstream\n"
|
|
" # Regenerate: git diff -- <file> > ${PATCH_FILE}\n"
|
|
)
|
|
endif()
|
|
# Apply
|
|
execute_process(
|
|
COMMAND git apply "${PATCH_FILE_TO_APPLY}"
|
|
WORKING_DIRECTORY "${TARGET_DIR}"
|
|
RESULT_VARIABLE APPLY_RESULT
|
|
ERROR_VARIABLE APPLY_ERR
|
|
)
|
|
if(NOT APPLY_RESULT EQUAL 0)
|
|
message(FATAL_ERROR "git apply failed unexpectedly: ${PATCH_NAME}\n${APPLY_ERR}")
|
|
endif()
|
|
message(STATUS " [${LABEL}] Applied: ${PATCH_NAME}")
|
|
endforeach()
|
|
file(WRITE "${_stamp}" "${_stamp_hash}")
|
|
endfunction()
|
|
|
|
if(EXISTS ${ZEPHCORE_SOURCE_DIR}/patches/zephyr)
|
|
message(STATUS "Applying ZephCore patches to Zephyr...")
|
|
zephcore_apply_patches(
|
|
"${ZEPHCORE_SOURCE_DIR}/patches/zephyr"
|
|
"${ZEPHCORE_ZEPHYR_DIR}"
|
|
"zephyr"
|
|
)
|
|
endif()
|
|
|
|
# Apply patches to loramac-node module
|
|
if(EXISTS ${ZEPHCORE_SOURCE_DIR}/patches/modules/loramac-node)
|
|
message(STATUS "Applying ZephCore patches to loramac-node...")
|
|
zephcore_apply_patches(
|
|
"${ZEPHCORE_SOURCE_DIR}/patches/modules/loramac-node"
|
|
"${ZEPHCORE_MODULES_DIR}/lib/loramac-node"
|
|
"loramac-node"
|
|
)
|
|
endif()
|
|
|
|
# Apply patches to hal_espressif module
|
|
if(EXISTS ${ZEPHCORE_SOURCE_DIR}/patches/modules/hal_espressif)
|
|
message(STATUS "Applying ZephCore patches to hal_espressif...")
|
|
zephcore_apply_patches(
|
|
"${ZEPHCORE_SOURCE_DIR}/patches/modules/hal_espressif"
|
|
"${ZEPHCORE_MODULES_DIR}/hal/espressif"
|
|
"hal_espressif"
|
|
)
|
|
endif()
|