Files
crosspoint-reader/scripts/patch_bt_mem.py
Justin Mitchell 4564c3e743 Make BT memory patch script robust to package location
Replace hardcoded path lookup with a multi-strategy search that tries PlatformIO's package API first, then falls back to globbing common package directories. This handles cases where the framework package name or location varies across installations.
2026-08-05 15:18:44 -04:00

96 lines
3.5 KiB
Python

"""
PlatformIO pre-build script: make the arduino-esp32 core define _btLibraryInUse
in a BLE-controller-only build.
The core header cores/esp32/esp32-hal-bt-mem.h emits a constructor
(_setBtLibraryInUse) that references `_btLibraryInUse` whenever BLE hardware is
present, but the core only DEFINES that symbol (in esp32-hal-bt.c) when an IDF
host stack -- CONFIG_BLUEDROID_ENABLED or CONFIG_NIMBLE_ENABLED -- is enabled.
CrossPoint runs the ESP-IDF BLE *controller* only, with NimBLE-Arduino supplying
the host (both IDF host stacks are disabled in custom_sdkconfig to avoid duplicate
npl_freertos_* symbols). In that configuration the core never defines the symbol,
so NimBLE's constructor link-fails with "undefined reference to `_btLibraryInUse'"
-- and it fails in links that application source cannot reach (the final firmware
link and the arduino-lib-builder core-rebuild's dummy firmware).
Fix: turn the header's `extern bool _btLibraryInUse;` DECLARATION into a weak
DEFINITION. Every translation unit that includes the header (NimBLE) then provides
the symbol itself; the linker merges the duplicate weak defs, and the core's own
strong definition still wins whenever a host stack is actually enabled. It must
live in the header, not our src, because only the header is seen by NimBLE's TU in
every link.
Idempotent text edit. Patching bumps the header mtime, so NimBLE recompiles
against it on the next build.
"""
Import("env") # noqa: F821 (SCons-injected global)
import glob
import os
EXTERN_DECL = "extern bool _btLibraryInUse;"
MARKER = "patch_bt_mem.py"
WEAK_DEF = (
"__attribute__((weak)) bool _btLibraryInUse = false; /* CrossPoint " + MARKER + " */"
)
def _find_header(env):
rel = os.path.join("cores", "esp32", "esp32-hal-bt-mem.h")
candidates = []
# 1) Ask the platform for the framework package dir (works when the name matches).
try:
fw = env.PioPlatform().get_package_dir("framework-arduinoespressif32") # noqa: F821
if fw:
candidates.append(os.path.join(fw, rel))
except Exception:
pass
# 2) Glob the packages dir(s) -- robust to the exact package name/version.
pkg_dirs = []
for key in ("PROJECT_PACKAGES_DIR", "PROJECT_CORE_DIR"):
try:
d = env.subst("$%s" % key) # noqa: F821
if d:
pkg_dirs.append(d)
except Exception:
pass
pkg_dirs.append(os.path.expanduser(os.path.join("~", ".platformio", "packages")))
for base in pkg_dirs:
pattern = os.path.join(base, "**", "framework-arduinoespressif32*", rel)
candidates.extend(glob.glob(pattern, recursive=True))
for c in candidates:
if os.path.isfile(c):
return c
return None
def patch_bt_mem(env):
header = _find_header(env)
if not header:
print("patch_bt_mem: esp32-hal-bt-mem.h not found; skipping (controller-only "
"link may fail on _btLibraryInUse)")
return
with open(header, "r") as f:
text = f.read()
if MARKER in text:
print("patch_bt_mem: already patched (%s)" % header)
return
if EXTERN_DECL not in text:
print("patch_bt_mem: '%s' not found in %s; NOT patching (upstream header "
"changed -- update this script)" % (EXTERN_DECL, header))
return
text = text.replace(EXTERN_DECL, WEAK_DEF, 1)
with open(header, "w") as f:
f.write(text)
print("patch_bt_mem: patched %s (_btLibraryInUse now weak-defined)" % header)
patch_bt_mem(env) # noqa: F821