From c63ec8672646e4b1698c8b8a5764b0c08025cf23 Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 28 Aug 2026 13:46:45 +0100 Subject: [PATCH 1/4] libraries: guard TinyUSB include for PlatformIO only Under PlatformIO, framework-bundled libraries do not receive lib_deps include paths, so the unguarded #include hard-fails every SAMD build with USE_TINYUSB. Adafruit_TinyUSB.h lives in the library's src/, and only src/arduino is ever added to the include path (platform.txt does this for arduino-cli; PlatformIO's atmelsam builder does the same), so the header is unreachable from these five sources. Skip the include under PlatformIO when it is unreachable, keeping it plain and unconditional everywhere else. The nesting is deliberate: the include is what the Arduino builder discovers the TinyUSB library from, and its dependency-detection pass cannot parse a __has_include() expression -- it then finds no dependency, TinyUSB is never linked, and the usbstack=tinyusb examples fail with undefined references to Serial and Adafruit_USBD_CDC::begin. Putting __has_include() inside #ifdef PLATFORMIO leaves it in a branch arduino-cli skips without evaluating, so arduino-cli behaviour is unchanged. Verified both ways in CI. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01TJa4WYfEHBFUhnJVRLja7A --- libraries/I2S/src/I2S.cpp | 13 ++++++++++++- .../src/SAMD_AnalogCorrection.cpp | 13 ++++++++++++- libraries/SPI/SPI.cpp | 13 ++++++++++++- libraries/Servo/src/samd/Servo.cpp | 13 ++++++++++++- libraries/Wire/Wire.cpp | 13 ++++++++++++- 5 files changed, 60 insertions(+), 5 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 00837e202..2628d5870 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -38,9 +38,20 @@ static I2SDevice_SAMD21G18x i2sd(*I2S); #include "I2S.h" #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB +// For Serial when selecting TinyUSB (also causes the Arduino builder to link +// the TinyUSB library). Outside PlatformIO this include must stay plain and +// unconditional: the Arduino builder discovers the library from it, and its +// dependency-detection pass cannot parse a __has_include() expression. +#ifdef PLATFORMIO +// PlatformIO does not give framework-bundled libraries the lib_deps include +// paths, so the header can be unreachable here; skip it instead of failing. +#if !defined(__has_include) || __has_include() #include #endif +#else +#include +#endif +#endif int I2SClass::_beginCount = 0; diff --git a/libraries/SAMD_AnalogCorrection/src/SAMD_AnalogCorrection.cpp b/libraries/SAMD_AnalogCorrection/src/SAMD_AnalogCorrection.cpp index e39ab43e3..7deeaf0bd 100644 --- a/libraries/SAMD_AnalogCorrection/src/SAMD_AnalogCorrection.cpp +++ b/libraries/SAMD_AnalogCorrection/src/SAMD_AnalogCorrection.cpp @@ -20,9 +20,20 @@ #include "SAMD_AnalogCorrection.h" #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB +// For Serial when selecting TinyUSB (also causes the Arduino builder to link +// the TinyUSB library). Outside PlatformIO this include must stay plain and +// unconditional: the Arduino builder discovers the library from it, and its +// dependency-detection pass cannot parse a __has_include() expression. +#ifdef PLATFORMIO +// PlatformIO does not give framework-bundled libraries the lib_deps include +// paths, so the header can be unreachable here; skip it instead of failing. +#if !defined(__has_include) || __has_include() #include #endif +#else +#include +#endif +#endif void analogReadCorrection (int offset, uint16_t gain) { diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 0393752d7..42c0564d5 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -23,9 +23,20 @@ #include #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB +// For Serial when selecting TinyUSB (also causes the Arduino builder to link +// the TinyUSB library). Outside PlatformIO this include must stay plain and +// unconditional: the Arduino builder discovers the library from it, and its +// dependency-detection pass cannot parse a __has_include() expression. +#ifdef PLATFORMIO +// PlatformIO does not give framework-bundled libraries the lib_deps include +// paths, so the header can be unreachable here; skip it instead of failing. +#if !defined(__has_include) || __has_include() #include #endif +#else +#include +#endif +#endif #define SPI_IMODE_NONE 0 #define SPI_IMODE_EXTINT 1 diff --git a/libraries/Servo/src/samd/Servo.cpp b/libraries/Servo/src/samd/Servo.cpp index ba07e70a7..5cf289978 100644 --- a/libraries/Servo/src/samd/Servo.cpp +++ b/libraries/Servo/src/samd/Servo.cpp @@ -22,9 +22,20 @@ #include #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB +// For Serial when selecting TinyUSB (also causes the Arduino builder to link +// the TinyUSB library). Outside PlatformIO this include must stay plain and +// unconditional: the Arduino builder discovers the library from it, and its +// dependency-detection pass cannot parse a __has_include() expression. +#ifdef PLATFORMIO +// PlatformIO does not give framework-bundled libraries the lib_deps include +// paths, so the header can be unreachable here; skip it instead of failing. +#if !defined(__has_include) || __has_include() #include #endif +#else +#include +#endif +#endif #if defined(__SAMD51__) // Different prescalers depending on FCPU (avoid overflowing 16-bit counter) diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index da9732c59..f79ad0038 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -25,9 +25,20 @@ extern "C" { #include #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB +// For Serial when selecting TinyUSB (also causes the Arduino builder to link +// the TinyUSB library). Outside PlatformIO this include must stay plain and +// unconditional: the Arduino builder discovers the library from it, and its +// dependency-detection pass cannot parse a __has_include() expression. +#ifdef PLATFORMIO +// PlatformIO does not give framework-bundled libraries the lib_deps include +// paths, so the header can be unreachable here; skip it instead of failing. +#if !defined(__has_include) || __has_include() #include #endif +#else +#include +#endif +#endif #include "Wire.h" From 2b9f06b3636c67428f773fcb4d3beba4058b97ac Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Wed, 16 Sep 2026 17:31:35 +0100 Subject: [PATCH 2/4] Update platform version to 1.7.18 --- platform.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform.txt b/platform.txt index bcebfe664..002aefdef 100644 --- a/platform.txt +++ b/platform.txt @@ -20,7 +20,7 @@ # https://arduino.github.io/arduino-cli/0.33/platform-specification/ name=Adafruit SAMD (32-bits ARM Cortex-M0+ and Cortex-M4) Boards -version=1.7.17 +version=1.7.18 # Compile variables # ----------------- From 73b156aa2f59d3786fefc8e99c079d2cd64e907d Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 18 Sep 2026 15:24:19 +0100 Subject: [PATCH 3/4] libraries: simplify TinyUSB guard to a single flat condition Apply hathach's review suggestion from adafruit/ArduinoCore-samd#396: one #if covering PLATFORMIO and __has_include, replacing the nested #ifdef PLATFORMIO form. Co-Authored-By: Claude Opus 5 (1M context) --- libraries/I2S/src/I2S.cpp | 13 +++---------- .../src/SAMD_AnalogCorrection.cpp | 13 +++---------- libraries/SPI/SPI.cpp | 13 +++---------- libraries/Servo/src/samd/Servo.cpp | 13 +++---------- libraries/Wire/Wire.cpp | 13 +++---------- 5 files changed, 15 insertions(+), 50 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 2628d5870..8873c080c 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -38,17 +38,10 @@ static I2SDevice_SAMD21G18x i2sd(*I2S); #include "I2S.h" #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB (also causes the Arduino builder to link -// the TinyUSB library). Outside PlatformIO this include must stay plain and -// unconditional: the Arduino builder discovers the library from it, and its -// dependency-detection pass cannot parse a __has_include() expression. -#ifdef PLATFORMIO +// For Serial when selecting TinyUSB. // PlatformIO does not give framework-bundled libraries the lib_deps include -// paths, so the header can be unreachable here; skip it instead of failing. -#if !defined(__has_include) || __has_include() -#include -#endif -#else +// paths, so the header can be unreachable there; skip it instead of failing. +#if !defined(PLATFORMIO) || !defined(__has_include) || __has_include() #include #endif #endif diff --git a/libraries/SAMD_AnalogCorrection/src/SAMD_AnalogCorrection.cpp b/libraries/SAMD_AnalogCorrection/src/SAMD_AnalogCorrection.cpp index 7deeaf0bd..690add310 100644 --- a/libraries/SAMD_AnalogCorrection/src/SAMD_AnalogCorrection.cpp +++ b/libraries/SAMD_AnalogCorrection/src/SAMD_AnalogCorrection.cpp @@ -20,17 +20,10 @@ #include "SAMD_AnalogCorrection.h" #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB (also causes the Arduino builder to link -// the TinyUSB library). Outside PlatformIO this include must stay plain and -// unconditional: the Arduino builder discovers the library from it, and its -// dependency-detection pass cannot parse a __has_include() expression. -#ifdef PLATFORMIO +// For Serial when selecting TinyUSB. // PlatformIO does not give framework-bundled libraries the lib_deps include -// paths, so the header can be unreachable here; skip it instead of failing. -#if !defined(__has_include) || __has_include() -#include -#endif -#else +// paths, so the header can be unreachable there; skip it instead of failing. +#if !defined(PLATFORMIO) || !defined(__has_include) || __has_include() #include #endif #endif diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 42c0564d5..cc4605b91 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -23,17 +23,10 @@ #include #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB (also causes the Arduino builder to link -// the TinyUSB library). Outside PlatformIO this include must stay plain and -// unconditional: the Arduino builder discovers the library from it, and its -// dependency-detection pass cannot parse a __has_include() expression. -#ifdef PLATFORMIO +// For Serial when selecting TinyUSB. // PlatformIO does not give framework-bundled libraries the lib_deps include -// paths, so the header can be unreachable here; skip it instead of failing. -#if !defined(__has_include) || __has_include() -#include -#endif -#else +// paths, so the header can be unreachable there; skip it instead of failing. +#if !defined(PLATFORMIO) || !defined(__has_include) || __has_include() #include #endif #endif diff --git a/libraries/Servo/src/samd/Servo.cpp b/libraries/Servo/src/samd/Servo.cpp index 5cf289978..5d4de4e43 100644 --- a/libraries/Servo/src/samd/Servo.cpp +++ b/libraries/Servo/src/samd/Servo.cpp @@ -22,17 +22,10 @@ #include #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB (also causes the Arduino builder to link -// the TinyUSB library). Outside PlatformIO this include must stay plain and -// unconditional: the Arduino builder discovers the library from it, and its -// dependency-detection pass cannot parse a __has_include() expression. -#ifdef PLATFORMIO +// For Serial when selecting TinyUSB. // PlatformIO does not give framework-bundled libraries the lib_deps include -// paths, so the header can be unreachable here; skip it instead of failing. -#if !defined(__has_include) || __has_include() -#include -#endif -#else +// paths, so the header can be unreachable there; skip it instead of failing. +#if !defined(PLATFORMIO) || !defined(__has_include) || __has_include() #include #endif #endif diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index f79ad0038..741fd1354 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -25,17 +25,10 @@ extern "C" { #include #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB (also causes the Arduino builder to link -// the TinyUSB library). Outside PlatformIO this include must stay plain and -// unconditional: the Arduino builder discovers the library from it, and its -// dependency-detection pass cannot parse a __has_include() expression. -#ifdef PLATFORMIO +// For Serial when selecting TinyUSB. // PlatformIO does not give framework-bundled libraries the lib_deps include -// paths, so the header can be unreachable here; skip it instead of failing. -#if !defined(__has_include) || __has_include() -#include -#endif -#else +// paths, so the header can be unreachable there; skip it instead of failing. +#if !defined(PLATFORMIO) || !defined(__has_include) || __has_include() #include #endif #endif From c52580f717f57af340dcd8a368a8cf5216e271d8 Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 18 Sep 2026 15:38:49 +0100 Subject: [PATCH 4/4] libraries: drop the TinyUSB include under PlatformIO instead of probing for it Replaces the __has_include probe with a plain #ifndef PLATFORMIO. The include exists only so the Arduino builder discovers and links Adafruit_TinyUSB_Arduino; none of these five translation units references Serial or any TinyUSB symbol, so under PlatformIO -- where the header is unreachable and the discovery side effect does not exist -- it can simply be left out. Background ---------- Only src/arduino of Adafruit_TinyUSB_Arduino is ever placed on the include path: platform.txt line 80 does it for arduino-cli, and PlatformIO's atmelsam platform appends the same path to CPPPATH for the Adafruit vendor core. So Adafruit_USBD_CDC.h resolves everywhere while resolves from neither. arduino-cli survives that because the unresolved include is itself what drives library discovery; PlatformIO gives framework-bundled libraries no equivalent step, so every SAMD build with USE_TINYUSB dies with libraries/Servo/src/samd/Servo.cpp:26:10: fatal error: Adafruit_TinyUSB.h: No such file or directory Why not __has_include --------------------- Both previous attempts used __has_include, and the flat form has now been measured red four times on this repo's matrix -- three runs before opening the PR, and run 35356063732 on the previous commit, which applied the review suggestion #if !defined(PLATFORMIO) || !defined(__has_include) || __has_include() verbatim. Both usbstack=tinyusb legs failed while the other ten passed: CorrectADCResponse.ino:43: undefined reference to `Adafruit_USBD_CDC::begin(unsigned long)' CorrectADCResponse.ino:144: undefined reference to `Serial' collect2: error: ld returned 1 exit status The failures are link-time only, never compile errors: GCC 9 handles __has_include fine, but the Arduino builder's dependency-detection pass cannot, so it finds no dependency and TinyUSB is never linked. Placing !defined(PLATFORMIO) first does not help, because __has_include is resolved while the #if line is expanded, before its operands are evaluated -- short-circuiting never gets the chance to hide it. The earlier nested form worked only by burying the expression inside an #ifdef PLATFORMIO branch that arduino-cli skips without reading. #ifndef PLATFORMIO avoids the construct altogether: arduino-cli's branch holds a byte-identical unconditional include, and PlatformIO takes no branch at all. Co-Authored-By: Claude Opus 5 (1M context) --- libraries/I2S/src/I2S.cpp | 8 ++++---- .../SAMD_AnalogCorrection/src/SAMD_AnalogCorrection.cpp | 8 ++++---- libraries/SPI/SPI.cpp | 8 ++++---- libraries/Servo/src/samd/Servo.cpp | 8 ++++---- libraries/Wire/Wire.cpp | 8 ++++---- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 8873c080c..e3b1464dd 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -38,10 +38,10 @@ static I2SDevice_SAMD21G18x i2sd(*I2S); #include "I2S.h" #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB. -// PlatformIO does not give framework-bundled libraries the lib_deps include -// paths, so the header can be unreachable there; skip it instead of failing. -#if !defined(PLATFORMIO) || !defined(__has_include) || __has_include() +// For Serial when selecting TinyUSB; the Arduino builder discovers and links +// the library from this include. PlatformIO never resolves it (framework- +// bundled libraries get no lib_deps include paths), so skip it there. +#ifndef PLATFORMIO #include #endif #endif diff --git a/libraries/SAMD_AnalogCorrection/src/SAMD_AnalogCorrection.cpp b/libraries/SAMD_AnalogCorrection/src/SAMD_AnalogCorrection.cpp index 690add310..919fd784a 100644 --- a/libraries/SAMD_AnalogCorrection/src/SAMD_AnalogCorrection.cpp +++ b/libraries/SAMD_AnalogCorrection/src/SAMD_AnalogCorrection.cpp @@ -20,10 +20,10 @@ #include "SAMD_AnalogCorrection.h" #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB. -// PlatformIO does not give framework-bundled libraries the lib_deps include -// paths, so the header can be unreachable there; skip it instead of failing. -#if !defined(PLATFORMIO) || !defined(__has_include) || __has_include() +// For Serial when selecting TinyUSB; the Arduino builder discovers and links +// the library from this include. PlatformIO never resolves it (framework- +// bundled libraries get no lib_deps include paths), so skip it there. +#ifndef PLATFORMIO #include #endif #endif diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index cc4605b91..0037241bb 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -23,10 +23,10 @@ #include #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB. -// PlatformIO does not give framework-bundled libraries the lib_deps include -// paths, so the header can be unreachable there; skip it instead of failing. -#if !defined(PLATFORMIO) || !defined(__has_include) || __has_include() +// For Serial when selecting TinyUSB; the Arduino builder discovers and links +// the library from this include. PlatformIO never resolves it (framework- +// bundled libraries get no lib_deps include paths), so skip it there. +#ifndef PLATFORMIO #include #endif #endif diff --git a/libraries/Servo/src/samd/Servo.cpp b/libraries/Servo/src/samd/Servo.cpp index 5d4de4e43..3b1302b47 100644 --- a/libraries/Servo/src/samd/Servo.cpp +++ b/libraries/Servo/src/samd/Servo.cpp @@ -22,10 +22,10 @@ #include #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB. -// PlatformIO does not give framework-bundled libraries the lib_deps include -// paths, so the header can be unreachable there; skip it instead of failing. -#if !defined(PLATFORMIO) || !defined(__has_include) || __has_include() +// For Serial when selecting TinyUSB; the Arduino builder discovers and links +// the library from this include. PlatformIO never resolves it (framework- +// bundled libraries get no lib_deps include paths), so skip it there. +#ifndef PLATFORMIO #include #endif #endif diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index 741fd1354..6943bfde3 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -25,10 +25,10 @@ extern "C" { #include #ifdef USE_TINYUSB -// For Serial when selecting TinyUSB. -// PlatformIO does not give framework-bundled libraries the lib_deps include -// paths, so the header can be unreachable there; skip it instead of failing. -#if !defined(PLATFORMIO) || !defined(__has_include) || __has_include() +// For Serial when selecting TinyUSB; the Arduino builder discovers and links +// the library from this include. PlatformIO never resolves it (framework- +// bundled libraries get no lib_deps include paths), so skip it there. +#ifndef PLATFORMIO #include #endif #endif