Arduino でサブディレクトリにファイル分割する方法

ソフトウェアを開発する際、ソースコードが増えてくるとファイル分割し、さらにはサブディレクトリに整理したくなることがあるかと思います。
目的としては、ビルド時間の短縮や可読性アップなどでしょうか。私の場合、特に大きなライブラリ(SDカード関連など)を使用する際は、ラッパーを介して呼び出すようにしています。具体的には、ライブラリのヘッダはラッパーを定義したソースファイルにのみインクルードし、メインソースファイルにはライブラリ実装が直接含まれないようにします。するとライブラリヘッダを含むソースファイルのコンパイル結果を再利用できるため、ビルド時間を大幅に短縮することができます。
それはさておき Arduino 君は癖がすごいので、ノリでサブディレクトリを作成しても全然ビルドできません。そこで Arduino の仕様を探索しましたので対処策をいざ投下
TL;DR
プロジェクトのルートディレクトリに src
ディレクトリを作成し、その中にサブディレクトリを配置します。こうするとサブディレクトリ下に配置したソースファイルもビルド対象に含んでくれます。
project
├── project.ino
└── src <-- これだけ!
├── common
│ ├── vector2d.hpp
│ └── vector2d.cpp
├── hardware
│ ├── motor.hpp
│ └── motor.cpp
└── network
├── mdns.hpp
├── mdns.cpp
├── http_server.hpp
└── http_server.cpp
ヘッダをインクルードする際は各ファイルからの相対パスでインクルードします。
ino ファイルからの場合こうです。
#include "src/common/vector2d.hpp"
#include "src/hardware/motor.hpp"
#include "src/network/mdns.hpp"
#include "src/network/http_server.hpp"
サブディレクトリ内のソースファイルの場合こうです。
// src/hardware/motor.cpp
#include "motor.hpp"
#include "../common/vector2d.hpp"
うまくいかないケース
例えば次のようにディレクトリを分割したとします。やりますよね、私やりました。
project
├── project.ino
├── common
│ ├── vector2d.hpp
│ └── vector2d.cpp
├── hardware
│ ├── motor.hpp
│ └── motor.cpp
└── network
├── mdns.hpp
├── mdns.cpp
├── http_server.hpp
└── http_server.cpp
project.ino ファイルはこんな感じ。
#include "common/vector2d.hpp"
#include "hardware/motor.hpp"
#include "network/mdns.hpp"
#include "network/http_server.hpp"
この状態でビルドすると、サブディレクトリ下のソースファイルがビルド対象に含まれず、リンクエラーになります。それもマングリング後の読みたくないやつ
ただ、ヘッダファイルのインクルードはできます。つまりソースファイルを完全排除してヘッダオンリーで実装している場合は問題ありません。
仕様をのぞく時、仕様もまた*
Arduino の仕様を覗いてみると、src
ディレクトリ以下のソースファイルは再帰的にビルドされるとあります。
Sketch specification (Arduino CLI docs)
注意点 1
src ディレクトリ以下に配置したソースファイルは Arduino IDE から見えなくなります。そこでオヌヌメは VSCode でプロジェクトを開き、Arduino IDE でビルドすることです。
注意点 2
src
ディレクトリはインクルードディレクトリにならない点に注意です。相対パスでインクルードする必要があります。
いいえ
// src/hardware/motor.cpp
#include "hardware/motor.hpp"
#include "common/vector2d.hpp"
プレティーグッド
// src/hardware/motor.cpp
#include "motor.hpp"
#include "../common/vector2d.hpp"
注意点 3
サブディレクトリの名前を変更したとき、Arduino はこれを検知してくれません。なので以前のオブジェクトファイルが残ってしまい、リンクエラーになります。
↓ の中にオブジェクトファイルがキャッシュされるので、エラーになったら削除してください。パスが違う場合はリンクエラーのメッセージを読むと書いてあります。
C:\Users\<USERNAME>\AppData\Local\arduino\sketches\
まとめ
- Arduino のビルドシステムは
src
ディレクトリ以下を再帰的に探索する - ソースは
src
配下に置き、ヘッダは相対パスでインクルードする - キャッシュの残骸でリンクエラーになることもあるので注意
というわけで、Arduino のプロジェクトで適切にファイルをサブディレクトリに分割する方法でした。ご覧いただきありがとうございました~