feat(espidf): honor CDCOnBoot menu option in 4-part FQBNs
arduino-cli FQBNs may carry a menu-option suffix (vendor:arch:board_id:Menu1=val1,...). The espidf lane previously derived cdc_on_boot exclusively from the board id's boards.txt default, so a generic-devkit board could never be built with Serial on the USB Serial/JTAG CDC port. - _fqbn_board_id_and_options(): split board id and menu options, keeping the old last-segment behaviour for plain 3-part FQBNs. - _arduino_board_flags(): CDCOnBoot=cdc overlays cdc_on_boot=1 on the boards.txt base flags (mirroring <id>.menu.CDCOnBoot.cdc.build. cdc_on_boot=1); the per-board cache stores only base flags, overrides are applied on a copy. - _arduino_variant(): board id extraction goes through the same helper so a menu suffix no longer corrupts the variant lookup. First user: the ESP32-C3-LCDkit board def pins esp32:esp32:esp32c3:CDCOnBoot=cdc — the real kit has no UART bridge, its only console is the USB Serial/JTAG port, and the esp32c3js engine now models the always-attached host that HWCDC's connection gate requires (esp32c3js v1.8.0).
This commit is contained in:
parent
7dc88b45e0
commit
3558d9ba39
|
|
@ -657,6 +657,29 @@ class ESPIDFCompiler:
|
|||
# Cache: boards.txt is a few hundred KB and never changes at runtime.
|
||||
_variant_cache: dict = {}
|
||||
|
||||
@staticmethod
|
||||
def _fqbn_board_id_and_options(board_fqbn: str) -> tuple[str, dict]:
|
||||
"""Split an arduino FQBN into (board_id, menu-option dict).
|
||||
|
||||
arduino-cli syntax: ``vendor:arch:board_id[:Menu1=val1,Menu2=val2]``.
|
||||
The espidf lane must understand the 4-part option-suffix form too,
|
||||
not just plain 3-part FQBNs — the C3-LCDkit board def pins
|
||||
``CDCOnBoot=cdc`` this way (the real kit has no UART bridge; its only
|
||||
console is the USB Serial/JTAG port). Menu keys a caller does not
|
||||
recognise are simply ignored.
|
||||
"""
|
||||
parts = board_fqbn.split(':')
|
||||
opts: dict = {}
|
||||
if len(parts) >= 4 and parts[3]:
|
||||
for kv in parts[3].split(','):
|
||||
if '=' in kv:
|
||||
k, v = kv.split('=', 1)
|
||||
opts[k.strip()] = v.strip()
|
||||
board_id = parts[2]
|
||||
else:
|
||||
board_id = parts[-1]
|
||||
return board_id.split('?')[0].strip(), opts
|
||||
|
||||
def _arduino_variant(self, board_fqbn: str, idf_target: str) -> str:
|
||||
"""The Arduino VARIANT for this FQBN, from arduino-esp32's boards.txt.
|
||||
|
||||
|
|
@ -670,7 +693,7 @@ class ESPIDFCompiler:
|
|||
is read rather than guessed; anything not found falls back to the chip,
|
||||
which is the previous behaviour.
|
||||
"""
|
||||
board_id = board_fqbn.split(':')[-1].split('?')[0].strip()
|
||||
board_id, _ = self._fqbn_board_id_and_options(board_fqbn)
|
||||
if not board_id:
|
||||
return idf_target
|
||||
key = (board_id, idf_target)
|
||||
|
|
@ -726,10 +749,16 @@ class ESPIDFCompiler:
|
|||
ESP32S3 expose UART0's default RX pin (GPIO44) as a plain Dx pin,
|
||||
so building them with Serial on UART0 is not just unfaithful: a
|
||||
pinMode() on that pin tears the UART driver down mid-sketch.
|
||||
|
||||
A ``CDCOnBoot`` menu option in the FQBN suffix overrides the board's
|
||||
boards.txt default, mirroring arduino-cli
|
||||
(``<id>.menu.CDCOnBoot.cdc.build.cdc_on_boot=1``): the C3-LCDkit pins
|
||||
``esp32:esp32:esp32c3:CDCOnBoot=cdc`` so its console matches the real
|
||||
kit, which only exposes the USB Serial/JTAG port.
|
||||
"""
|
||||
board_id = board_fqbn.split(':')[-1].split('?')[0].strip()
|
||||
board_id, menu_opts = self._fqbn_board_id_and_options(board_fqbn)
|
||||
if board_id in self._board_flags_cache:
|
||||
return self._board_flags_cache[board_id]
|
||||
return self._apply_menu_overrides(self._board_flags_cache[board_id], menu_opts)
|
||||
flags = {'board': None, 'cdc_on_boot': False}
|
||||
roots = [
|
||||
r
|
||||
|
|
@ -755,6 +784,19 @@ class ESPIDFCompiler:
|
|||
if flags['board'] is not None:
|
||||
break
|
||||
self._board_flags_cache[board_id] = flags
|
||||
return self._apply_menu_overrides(flags, menu_opts)
|
||||
|
||||
@staticmethod
|
||||
def _apply_menu_overrides(flags: dict, menu_opts: dict) -> dict:
|
||||
"""Overlay FQBN menu options on the boards.txt base flags.
|
||||
|
||||
Only ``CDCOnBoot`` is understood today (``cdc`` -> 1, ``default`` ->
|
||||
the boards.txt value). Returns a copy so the per-board cache never
|
||||
holds an override.
|
||||
"""
|
||||
cdc = menu_opts.get('CDCOnBoot')
|
||||
if cdc == 'cdc':
|
||||
return {**flags, 'cdc_on_boot': True}
|
||||
return flags
|
||||
|
||||
def _idf_target(self, board_fqbn: str) -> str:
|
||||
|
|
|
|||
|
|
@ -463,3 +463,44 @@ def test_ninja_log_step_count(tmp_path) -> None:
|
|||
encoding='utf-8',
|
||||
)
|
||||
assert _ninja_log_step_count(build_dir) == 2
|
||||
|
||||
|
||||
# ── FQBN menu-option suffix (CDCOnBoot) ──────────────────────────────────
|
||||
|
||||
|
||||
def test_fqbn_board_id_plain(compiler: ESPIDFCompiler) -> None:
|
||||
assert compiler._fqbn_board_id_and_options('esp32:esp32:esp32c3') == (
|
||||
'esp32c3', {},
|
||||
)
|
||||
|
||||
|
||||
def test_fqbn_board_id_with_menu_suffix(compiler: ESPIDFCompiler) -> None:
|
||||
board_id, opts = compiler._fqbn_board_id_and_options(
|
||||
'esp32:esp32:esp32c3:CDCOnBoot=cdc'
|
||||
)
|
||||
assert board_id == 'esp32c3'
|
||||
assert opts == {'CDCOnBoot': 'cdc'}
|
||||
|
||||
|
||||
def test_fqbn_menu_suffix_multiple_options(compiler: ESPIDFCompiler) -> None:
|
||||
board_id, opts = compiler._fqbn_board_id_and_options(
|
||||
'esp32:esp32:esp32s3:CDCOnBoot=cdc,PSRAM=opi'
|
||||
)
|
||||
assert board_id == 'esp32s3'
|
||||
assert opts == {'CDCOnBoot': 'cdc', 'PSRAM': 'opi'}
|
||||
|
||||
|
||||
def test_menu_override_cdc_flips_flag(compiler: ESPIDFCompiler) -> None:
|
||||
base = {'board': 'ESP32C3_DEV', 'cdc_on_boot': False}
|
||||
out = compiler._apply_menu_overrides(base, {'CDCOnBoot': 'cdc'})
|
||||
assert out['cdc_on_boot'] is True
|
||||
# The cache's base dict must never be mutated by an override.
|
||||
assert base['cdc_on_boot'] is False
|
||||
|
||||
|
||||
def test_menu_override_default_keeps_boards_txt_value(
|
||||
compiler: ESPIDFCompiler,
|
||||
) -> None:
|
||||
base = {'board': 'ESP32C3_DEV', 'cdc_on_boot': False}
|
||||
assert compiler._apply_menu_overrides(base, {'CDCOnBoot': 'default'}) is base
|
||||
assert compiler._apply_menu_overrides(base, {}) is base
|
||||
|
|
|
|||
Loading…
Reference in New Issue