Subfolders in a sketch folder

Today I found out I can include and compile files mentioned like this:

#include "comm/UART_thingies.h"

, i.e. I can reach a file in a comm subfolder. It's not visible on the editor, but it's reachable.

It works:

  1. Both on Linux and Windows.
  2. Both with Arduino IDE 2.x and 1.x.

But! Around 2020 there were plenty of topics around here stating that subfolders in a sketch folder must be placed inside a subfolder named src, no exceptions. Therefore my experience contradicts those statements.

Does it mean that the devs have in a certain moment omitted the requirement for a presence of a subfolder named src and let us include files from whatever subfolders we want?

No.

.h files can be anywhere, .cpp files can't.

Ah, I see. So, in order to truly include comm/UART_thingies.h and comm/UART_thingies.cpp, I still have to put it like this:

#include "src/comm/UART_thingies.h"

?

I think it's just the .cpp files that need to be in src. I think the headers can be anywhere. Someone tell me if I'm wrong about that.

This is strange, I cannot include any header from src/header_1.h, but I do can do it from code/header_1.h.

UPD: include works if there're no .cpp files in the same folder. Weird.

UPD 2: nevermind, I sorted it out. Indeed, as you said, including .h files works alright from any folder. And .ccp files inside an src subfolder do get compiled (while .cpp files outside such subfolder do not).

What's more, ALL the cpp files in the src folder get compiled, regardless of whether or not they end up being linked to. So if there's an error even in some file you're not using, it will still get thrown at compile.

Let's work backwards

  • a C/C++ executable results from linking a runtime with a main module, and any additional modules that are referenced.
  • a module generally results from a single source file; usually with a .cpp extension, but some others are supported
    • .ino is a specific exception: all such files in the sketch directory are glued together and some extra magic is done to create the main module.
  • each source file has a corresponding header, which declares what's in the module
    • the implementation in the source file must conform to that declaration to fulfill that contract
    • consumers of the module rely on that declaration so that the implementation can be linked
  • headers are used via a preprocessor #include, which at its core finds text and inserts it in that place

For Arduino sketches, the linker joins

  • the main module from the .ino file(s)
  • modules from libraries
  • modules from compiling all the C++ source files in the sketch folder
    • its src directory
      • and all its subdirectories recursively

When using #include, you can reference any file anywhere, and that part will work. But if that header has a corresponding implementation -- almost always the case -- then the corresponding module must be known to the linker, usually because the compiler just built it (or it used a cached result).

As you have surely seen, the header is usually in the same directory as the source file, but that's not actually required.

TL;DR: put your source in src