やっぱりプロジェクトの構造、特にCMake,CMakeListsの使い方が良く分かりません。そこで、ESP-IDF Programming GuideでCMakeListsを検索したらこんな下りを見つけました。
簡単に訳すと
- トップレベルの
- CMakeLists.txt
- プロジェクトをどの様にビルドするか設定するファイル
- /tools/cmake/project.cmake ファイルをインクルードする。
- sdkconfig
- プロジェクト構成ファイル。
- idf.py menuconfigの実行で作成/更新
- プロジェクト内のすべてのコンポーネント(ESP-IDF自体を含む)の構成を保持。
- Optional “components” directory
- 再利用可能なコードやサードパーティコンポーネントを含めると便利
- EXTRA_COMPONENT_DIRSをトップレベルのCMakeLists.txtに設定すると他の場所のコンポーネントを探す事が出来る
- プロジェクトに多数のソースファイルがある場合、コンポーネントでグループ化することをお勧め。
- このディレクトリーはプロジェクトに必ず有るとは限らない。
- “main” directory
- プロジェクトのソースコードを含む特別なコンポーネントディレクトリー。
- 「main」はデフォルト名。変更可能。
- CMake変数COMPONENT_DIRSにはこのコンポーネントが含まれている。
- “build” directory
- ビルド出力が作成される場所
- このディレクトリに中間ビルドファイルを生成。
- 中間オブジェクトファイルとライブラリ、および最終的なバイナリ出力ファイルが含まれる。
- CMakeLists.txt
コンポーネントディレクトリーの構造に関して、
- CMakeLists.txt
- 全てのコンポーネントファイルが保有。
- コンポーネントのビルドプロセス、およびプロジェクト全体への統合を制御するための変数定義が含まれる。
Kconfig
file- menuconfigでオプションを設定できる様になる
- プロジェクトの一部をオーバーライドするための特別なファイル、Kconfig.projbuildファイルとproject_include.cmakeファイルが有る。
分かった様な分からない様な。
サンプルプロジェクト(下記)を用いて詳細を説明しています。
- autoProject/
- CMakeLists.txt
- components/
- car/
- CMakeLists.txt
- car.c
- car.h
- engine/
- CMakeLists.txt
- engine.c
- include/
- engine.h
- spark_plug/
- CMakeLists.txt
- plug.c
- plug.h
理解を深める為に同じ様なプロジェクトを製作して説明通りに行こうと思います。先ずは下記のプロジェクトを作成します。
~/esp/autoProject/
- CMakeLists.txt
- components/
- car/
- CMakeLists.txt
- car.c
- car.h
- engine/
- CMakeLists.txt
- engine.c
- include/
- engine.h
- spark_plug/
- CMakeLists.txt
- plug.c
- plug.h
- main/
- CMakeLists.txt
- autoProject.c
- プロジェクト直下の、
- CMakeLists.txt
# For more information about build system see # https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html # The following five lines of boilerplate have to be in your project's # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) set(EXTRA_COMPONENT_DIRS $ENV{HOME}/esp/autoProject/components) project(autoProject)
- 8行:set(EXTRA_COMPONENT_DIRS $ENV{HOME}/esp/autoProject/components)
- メイン関数がcomponentsフォルダーの関数を使用する為ここで参照先を指定
- $ENV{HOME}/esp/autoProject/componentsはcomponentsフォルダーのパス。
- これでビルド時このパスが参照される。
- mainフォルダー
- autoProject.c
#include <stdio.h&st; #include "car.h" #include "engine.h" #include "plug.h" void app_main(void) { printf("autoProject\n"); car(); engine(); plug(); }
- メイン関数。他で定義されている car(),engine(),plug()関数を実行。
- それぞれのヘッダーの読み込みが必要と思われるが、プロジェクト直下のCMakeLists.txtでset(EXTRA_COMPONENT_DIRS 〜)を指定しているので不要。
- CMakeLists.txt
idf_component_register(SRCS "autoProject.c" INCLUDE_DIRS ".")
- ソースファイルと読み込み先の指定。
- autoProject.c
- componentsフォルダー
- carフォルダー
- car.c
#include <stdio.h&st; #include "car.h" void car() { printf("%s needs ",CAR_MODEL); }
- 定義された文字列、”CAR_MODEL”を表示する関数
- car.h
#include "engine.h" #ifdef ENGINE_IS_HYBRID #define CAR_MODEL "Hybrid" #else #define CAR_MODEL "Normal" #endif void car(void);
- 3から7行で、”CAR_MODEL”に定義する文字列を指定。ここで、engineフォルダーで定義されている ”ENGINE_IS_HYBRID”を使用。
- CMakeLists.txt
idf_component_register(SRCS "car.c" INCLUDE_DIRS "." REQUIRES engine)
- engineフォルダーで定義されている”ENGINE_IS_HYBRID”を使用するのでREQUIRES engine が必要
- car.c
- engineフォルダー
- engine.c
#include <stdio.h&st; #include "engine.h" #include "plug.h" void engine() { printf("Engine. \nEngine needs "); } void sub_engine() { printf("Sub_engine is %s",Sub_Eng); }
- engine関数とsub_engine関数の定義。
- sub_engine関数で使用されている”Sub_Eng”は”plug.h”で宣言されている。
- もしengine()関数のみ外部から参照するなら、#include “engine.h” が、 sub_engine()関数を参照するなら、”#include plug.h”も必要になる。
- includeフォルダー
- engine.h
#define ENGINE_IS_HYBRID void engine(void); void sub_engine(void);
- ここで、ENGINE_IS_HYBRIDが定義されている。
- engine.h
- CMakeLists.txt
idf_component_register(SRCS "engine.c" INCLUDE_DIRS "include" PRIV_REQUIRES spark_plug)
- INCLUDE_DIRS “include” に注意
- engine.c
- spark_plugフォルダー
- plug.c
#include <stdio.h&st; #include "plug.h" void plug(void) { printf("Plug\n"); }
- plug.h
#define Sub_Eng "Option" void plug(void);
- CMakeLists.txt
idf_component_register(SRCS "plug.c" INCLUDE_DIRS ".")
- plug.c
- carフォルダー
- CMakeLists.txt
次に~/esp/autoProject/に移動しCPUの指定を行います。今回はESP32を使用するので、idf.py set-target esp32を実行します。実行後プロジェクトにsdkconfigファイルとbuild フォルダーが追加されます。
~/esp/autoProject/
- CMakeLists.txt
- components/
- car/
- CMakeLists.txt
- car.c
- car.h
- engine/
- CMakeLists.txt
- engine.c
- include/
- engine.h
- spark_plug/
- CMakeLists.txt
- plug.c
- plug.h
- main/
- CMakeLists.txt
- autoProject.c
- sdkconfig
- build/
- ........
- ........
今回コンフィグの設定は有りません。idf.py menuconfigは省略。ビルド、モニターコマンドidf.py -p [USB port] flash monitorを実行。実行されるとモニターに以下が表示されます。
.......
........
I (308) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
autoProject
HYBRID_car needs
Engine. Engine needs
Plug
最後の4行が今回のプログラムの出力です。各表示とプログラムの関係は以下の様になっています。
- autoProject ー> printf(“autoProject\n”);
- HYBRID_car needs ー> car();
- Engine. Engine needs ー> engine();
- Plug ー> plug();
今度は、プロジェクト、コンポーネント直下の CMakeLists.txt の役割が何と無く理解で来た様な気がします。
プロジェクト直下のCMakeLists.txtの COMPONENT_DIRS と EXTRA_COMPONENT_DIRS
EXTRA_COMPONENT_DIRSは、補助的な関数(今回の例で言えば、car.c engine.c plug.c など)を個別のフォルダーにまとめプログラムを管理しやすくした状態で、ビルド時にコンパイラーそこを参照するように指定するのに使いました。これと似たコマンドにCOMPONENT_DIRSが有ります。両者の説明は以下の様になっています。
- COMPONENT_DIRS:
- コンポーネントを検索するためのディレクトリ。
- デフォルトはIDF_PATH / components、PROJECT_DIR / components、およびEXTRA_COMPONENT_DIRSです。
- これらの場所でコンポーネントを検索したくない場合は、この変数をオーバーライドします。
- EXTRA_COMPONENT_DIRS:
- コンポーネントを検索するための追加のディレクトリのオプションのリスト。
- パスは、プロジェクトディレクトリからの相対パス、または絶対パスにすることができます。
これを今回のautoProjectに当てはめると、各検索パスは以下の様になります。
- COMPONENT_DIRS:
- デフォルト:IDF_PATH / component
- PROJECT_DIR / components:autoProject / components
- EXTRA_COMPONENT_DIRS:autoProject / components
- EXTRA_COMPONENT_DIRS:
- autoProject / components
だとすると、今回EXTRA_COMPONENT_DIRSで指定したパス autoProject / componentsは既にCOMPONENT_DIRSに設定されている事になります。
それを確かめる為に下記の作業をして見ました。
- プロジェクト直下のCMakeLists.txtのset(EXTRA_COMPONENT_DIRS $ENV{HOME}/esp/autoProject/components)を削除してビルド
- ===>問題無く実行出来ました。
- その状態でcomponentsフォルダーをcompに変更してビルド
- ===>エラー。 autoProject.cの”car.h”が見つからない
- プロジェクト直下のCMakeLists.txtのset(EXTRA_COMPONENT_DIRS $ENV{HOME}/esp/autoProject/comp)に変更してビルド
- ===>問題無く実行出来ました。
確かにCOMPONENT_DIRSに登録されている事が分かりました。そもそもこの2つどう使い分けるのか。
そこで、COMPONENT_DIRSのデフォルトパス IDF_PATH / component を見ると以下の様になっていました。
@G500:~/esp/esp-idf/components$ ls
README.md esp_eth esptool_py protobuf-c
app_trace esp_event fatfs protocomm
app_update esp_gdbstub freemodbus pthread
asio esp_hid freertos riscv
bootloader esp_http_client hal sdmmc
bootloader_support esp_http_server heap soc
bt esp_https_ota http_parser spi_flash
cmock esp_https_server idf_test spiffs
coap esp_hw_support ieee802154 tcp_transport
console esp_lcd json tcpip_adapter
cxx esp_local_ctrl linux tinyusb
driver esp_netif log touch_element
efuse esp_phy lwip ulp
esp-tls esp_pm mbedtls unity
esp32 esp_ringbuf mdns usb
esp32c2 esp_rom mqtt vfs
esp32c3 esp_serial_slave_link newlib wear_levelling
esp32h2 esp_system nvs_flash wifi_provisioning
esp32s2 esp_timer openssl wpa_supplicant
esp32s3 esp_websocket_client openthread xtensa
esp_adc_cal esp_wifi partition_table
esp_common espcoredump perfmon
@G500:~/esp/esp-idf/components$
ブルートゥース、WiFi、タイマー等のフォルダーが有りました。これらはシステムで予め用意されたコンポーネント群です。つまり、COMPONENT_DIRSはシステムが用意したコンポーネント群を指している様です。そう考えると、EXTRA_COMPONENT_DIRSはそれ以外に参照して欲しいパス(例えばユーザーが開発中のコンポーネントのパス)を指定する時に使用するとなります。
コンポーネント直下のCMakeLists.txtの REQUIRES
と PRIV_REQUIRES
この2つも使い方が良く分かりません。ESP-IDF Programming Guideでは下記の様に説明しています。
REQUIRES
should be set to all components whose header files are #included from the public header files of this component.
PRIV_REQUIRES
should be set to all components whose header files are #included from any source files in this component, unless already listed inREQUIRES
. Also any component which is required to be linked in order for this component to function correctly.
使い方は今回のサンプルから想像することにします。
次回は
次回は、Kconfigに付いて調べたいと思います。