diff --git a/backend/app/services/arduino_cli.py b/backend/app/services/arduino_cli.py index 0496a383..5f75be93 100644 --- a/backend/app/services/arduino_cli.py +++ b/backend/app/services/arduino_cli.py @@ -55,6 +55,41 @@ _NETWORK_MARKERS = ( ) + +# ── Compiler diagnostics that need a Velxio answer, not the IDE's ──────────── +# arduino-pico gates its Bluetooth libraries behind a static_assert whose text +# tells the user to flip "Tools->IP/Bluetooth Stack" — a menu that exists in the +# Arduino IDE and not here. Left alone it reads like a setting we forgot to +# expose. The truth is simpler and worth saying plainly: the CYW43439/RM2's +# Bluetooth side is not emulated, only its WiFi side is, so there is nothing to +# enable. +_BT_ASSERT_MARKERS = ("_needsbt.h", "This library needs Bluetooth enabled") + +_BT_NOT_EMULATED_NOTE = ( + "\n" + "Velxio: Bluetooth is not emulated on this board. The CYW43439 (the RM2\n" + "module on the Pimoroni Pico Plus 2 W, and the same chip on the Pico W)\n" + "is emulated for WiFi only — association, DHCP, DNS and real HTTP all\n" + "work — but its Bluetooth controller is not modelled, so there is no\n" + "'IP/Bluetooth Stack' setting to turn on here. Sketches that use WiFi.h,\n" + "HTTPClient or WiFiServer compile and run.\n" +) + + +def annotate_build_stderr(stderr: str | None) -> str | None: + """Append a Velxio-authored note to compiler output we know is misleading. + + Kept separate from humanize_cli_error: that one rewrites arduino-cli's own + failures, this one leaves the compiler's diagnostics intact and adds context + underneath, because the developer still needs the real error text. + """ + if not stderr: + return stderr + if any(marker in stderr for marker in _BT_ASSERT_MARKERS): + return stderr.rstrip() + "\n" + _BT_NOT_EMULATED_NOTE + return stderr + + def humanize_cli_error(raw: str | None, *, action: str = "run arduino-cli") -> str: """One readable sentence for an arduino-cli failure. @@ -688,7 +723,7 @@ class ArduinoCLIService: "success": False, "error": "Compilation failed", "stdout": result.stdout, - "stderr": result.stderr + "stderr": annotate_build_stderr(result.stderr), } except Exception as e: diff --git a/test/backend/unit/test_arduino_cli_bt_note.py b/test/backend/unit/test_arduino_cli_bt_note.py new file mode 100644 index 00000000..f1e9d72f --- /dev/null +++ b/test/backend/unit/test_arduino_cli_bt_note.py @@ -0,0 +1,37 @@ +""" +The Bluetooth compile error says something Velxio can answer better. + +arduino-pico gates its BT libraries behind a static_assert telling the user to +use the Arduino IDE's "Tools->IP/Bluetooth Stack" menu. In Velxio that menu does +not exist, so the message reads like a setting we forgot to expose rather than +what it is: the CYW43439's Bluetooth side is not emulated at all. +""" + +from app.services.arduino_cli import annotate_build_stderr + +REAL_ERROR = ( + "/root/.arduino15/packages/rp2040/hardware/rp2040/6.0.0/cores/rp2040/_needsbt.h:4:24: " + "error: static assertion failed: This library needs Bluetooth enabled. " + "Use the 'Tools->IP/Bluetooth Stack' menu in the IDE to enable it\n" + "Error during build: exit status 1\n" +) + + +def test_the_bluetooth_assert_gets_a_velxio_answer(): + out = annotate_build_stderr(REAL_ERROR) + # The compiler's own text survives — the developer still needs it. + assert '_needsbt.h' in out + assert 'static assertion failed' in out + # ...and we say what is actually true underneath. + assert 'Bluetooth is not emulated on this board' in out + assert 'WiFi' in out + + +def test_an_ordinary_error_is_left_alone(): + ordinary = "sketch.ino:5:3: error: 'foo' was not declared in this scope\n" + assert annotate_build_stderr(ordinary) == ordinary + + +def test_empty_output_is_passed_through(): + assert annotate_build_stderr(None) is None + assert annotate_build_stderr('') == ''