はじめに
NuttXにビルトインアプリケーションを追加する方法を調査した。
ここが日本語でよくまとまっていた。
プログラムを作成する場所
プログラムをどこに追加するかと言うのが一つ考えどころだが、apps/README.txt
によると、
Use of the name ''apps/external'' is suggested because that name is included in the .gitignore file and will save you some nuisance when working with GIT.
ということなので、apps/external
以下にプログラムを追加する。
$ mkdir -p external/helloworld $ cd external/helloworld
apps/external
以下はリポジトリの管理からは外れているので自分で好き勝手に管理することができる。
$ git init
ただし、externalより下のディレクトリはビルド対象に含まれていないので、シンボリックリンクを貼る。
$ ln -s external/helloworld helloworld
プログラムの作成
次のファイル作成する。
- Kconfig
- Make.defs
- Makefile
- helloworld_main.c
これらは、apps/examples/hello
を参考に、というかコピーしてきて自分のプログラムに合わせて変更する感じで良い。
Kconfig
config EXTERNAL_HELLO tristate "\"Hello, World!\" example" default n ---help--- Enable the \"Hello, World!\" example if EXTERNAL_HELLO config EXTERNAL_HELLO_PROGNAME string "Program name" default "helloworld" depends on BUILD_LOADABLE ---help--- This is the name of the program that will be use when the NSH ELF program is installed. config EXTERNAL_HELLO_PRIORITY int "Hello task priority" default 100 config EXTERNAL_HELLO_STACKSIZE int "Hello stack size" default 2048 endif
この例では、次のコンフィグレーションがmake menuconfig
で設定出来るようになっている。
- プログラム名
- タスクの優先度
- スタックサイズ
今回は無いが、他のライブラリを使用する場合などは依存関係も定義できる。
プログラム名については後で詳しく説明する。
Make.defs
make menuconfig
でプログラムが有効化された場合に、CONFIGURED_APPS
にプログラムのディレクトリをappsからの相対パスで追加する。
ifneq ($(CONFIG_EXTERNAL_HELLO),) CONFIGURED_APPS += helloworld endif
CONFIGURE_APPSに追加されたディレクトリがビルド対象となる。
Makefile
-include $(TOPDIR)/Make.defs # Hello, World! built-in application info CONFIG_EXTERNAL_HELLO_PRIORITY ?= SCHED_PRIORITY_DEFAULT CONFIG_EXTERNAL_HELLO_STACKSIZE ?= 2048 APPNAME = helloworld PRIORITY = $(CONFIG_EXTERNAL_HELLO_PRIORITY) STACKSIZE = $(CONFIG_EXTERNAL_HELLO_STACKSIZE) # Hello, World! Example ASRCS = CSRCS = MAINSRC = helloworld_main.c CONFIG_EXTERNAL_HELLO_PROGNAME ?= hello$(EXEEXT) PROGNAME = $(CONFIG_EXTERNAL_HELLO_PROGNAME) MODULE = CONFIG_EXTERNAL_HELLO include $(APPDIR)/Application.mk
ここでは主に次の変数を設定している。
変数 | 概要 |
---|---|
APPNAME | アプリケーション名 |
PRIORITY | タスクの優先度 |
STACKSIZE | スタックサイズ |
ASRCS | アセンブラソース |
CSRCS | Cソース |
MAINSRC | メインのソース |
PROGNAME | プログラム名 |
MODULE | ローダブルバイナリ用 |
APPNAME、PRIORITY、STACKSIZEの項目はKconfigで設定されてない場合のデフォルト値を考慮した記述になっている。
MODULEはCONFIG_EXTERNAL_HELLOにm
を設定した場合に正しくローダブルバイナリを出力(ローダブルビルド)できるようにするために必要な変数。
ローダブルバイナリ
NuttXはelfをロードする機能を持っているので、アプリケーション単体をelfファイルとして作成し、 NuttX上のファイルシステムにおいてある動的にロード、実行することができる。
フラットビルドの場合はローダブルバイナリだったとしても実行時のアドレス空間は単一となる。
ローダブルビルドを有効にするにはCONFIG_BUILD_LOADABLE
をyにする必要がある。その上でアプリのコンフィグをmに設定する。
APPNAMEとPROGNAME
APPNAMEとPROGNAMEで何が違うのか。
詳細はAPPNAME vs. PROGNAMEを参照。
ただ、リンク元の情報は古くなっているので今のBUILD_LOADABLE
とBUILD_KERNEL
の説明がごちゃまぜになっている。
以前はカーネルビルド(BUILD_KERNEL)でしかローダブルバイナリのロードができなかったようなので、その名残だと思われる。
フラットビルドかつローダブルビルドしない、つまり単一のバイナリに結合されるケースについて特に用語見つからなかったので、 ここでは「単一ビルド」と呼ぶことにする。
単一ビルドの場合、アプリケーションはlibapps.a
というライブラリとして生成されnuttxにリンクされる。
多くの場合プログラムのエントリポイントはmain()だが、単一ビルドの場合は、同じシンボルを使用できないのでそれぞれにエントリポイントの名前つける必要がある。
nshでは$(APPNAME)_main
をそれぞれのアプリケーションのエントリポイントとして探すというルールがあるため、単一ビルドの場合はAPPNAME
が必要となる。
PROGNAMEはローダブルビルドした場合に生成される実行ファイルの名前を設定する。
そのため、これらはそれぞれ別の役割を持っていると言える。
APPNAMEとエントリポイントとMAINSRC
先述の通りAPPNAMEは単一ビルド時にアプリケーションのエントリポイントを探すために使用される。 そのためAPPNAMEとエントリポイントとなる関数の名前は次のルールに則っている必要がある。
$(APPNAME)_main
また、MAINSRCに設定するファイル名もエントリポイントと一致している必要がある。
APPNAMEが「helloworld」だった場合の設定例を次に示す。
項目 | 概要 |
---|---|
APPNAME | helloworld |
エントリポイント | helloworld_main |
MAINSRC | helloworld_main.c |
単一ビルド時の
helloworld_main.c
/**************************************************************************** * Included Files ****************************************************************************/ #include <nuttx/config.h> #include <stdio.h> /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * hello_main ****************************************************************************/ #if defined(BUILD_MODULE) int main(int argc, FAR char *argv[]) #else int helloworld_main(int argc, char *argv[]) #endif { printf("!!Hello, World!!\n"); return 0; }
BUILD_MODULEが定義されている時は、エントリポイントをmain
、それ意外ではhelloworld_main
としている。
BUILD_MODULEはプログラムをローダブルビルドした場合に自動的に定義される。
ビルド
nuttxディレクトリで次のコマンドを実行する。
$ make clean $ make menuconfig
メニューで追加したプログラムを有効化してビルドする。
単一ビルドの場合は、次のようにBuiltin Apps:にhelloworldが追加される。
nsh> help help usage: help [-v] [<cmd>] [ cp exec kill mv set uname ? cmp exit ls mw sh umount basename dirname false mb ps sleep unset break dd free mkdir pwd test usleep cat df help mh rm time xd cd echo hexdump mount rmdir true Builtin Apps: hello helloworld