aimdevel’s blog

勉強したことを書きます

bitbakeのオペレータ文法を調べる

yoctoのレシピを書く際に変数設定のオペレータの意味を正確に理解できていなかったため調べてまとめ、手元で動作確認を行った。
動作確認の方法は、local.confに変数を記述して、それがどのように反映されるかをbitbake-getvarで確認するという手順を使った。使用したyoctoのバージョンはkirkstoneである。
参考にしたドキュメントは以下。

3 Syntax and Operators — Bitbake dev documentation

目次

bitbakeの変数処理タイミング

今回調べて初めて把握したが、bitbakeの変数の処理が行われるタイミングは2回ある。
1回目が変数がパースされたタイミング、2回目はすべての変数をパースした後のタイミングである。
1回目のタイミングではオペレータで指定された処理行われ、2回目のタイミングではオーバーライドシンタックスで指定された処理や変数展開が行われる。
これを踏まえて以下のオペレータとオーバーライドシンタックスの説明を読むと理解がしやすいと思う。

オペレータ

=

単純な代入。下位のレイヤで定義されている値を上書きする。
以下の例のように後から定義された値が優先されて反映されている。

  • local.conf
MY_VAR = "MY_MY"
MY_VAR = "MY_MY_MY"
  • bitbake-getvar MY_VAR
#
# $MY_VAR [2 operations]
#   set /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:280
#     "MY_MY"
#   set /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:281
#     "MY_MY_MY"
# pre-expansion value:
#   "MY_MY_MY"
MY_VAR="MY_MY_MY"

?=

デフォルト値の割り当て。
これがパースされるタイミングですでにほかの値が割り当てられていた場合には、そちらが優先されこのオペレータで定義された値は無視される。

  • local.conf
MY_VAR = "MY_MY"
MY_VAR ?= "MY_MY_MY"
  • bitbake-getvar MY_VAR
#
# $MY_VAR [2 operations]
#   set /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:280
#     "MY_MY"
#   set? /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:281
#     "MY_MY_MY"
# pre-expansion value:
#   "MY_MY"
MY_VAR="MY_MY"

??=

ウィークデフォルト値の設定。
ほかのオペレータで値が割り当てられていない場合にこのオペレータで指定した値が使用される。?=よりも優先度が低いデフォルト値。

  • local.conf
MY_VAR ?= "MY_MY"
MY_VAR ??= "MY_MY_MY"
  • bitbake-getvar MY_VAR
#
# $MY_VAR [2 operations]
#   set? /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:280
#     "MY_MY"
#   set /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:281
#     [_defaultval] "MY_MY_MY"
# pre-expansion value:
#   "MY_MY"
MY_VAR="MY_MY"

:=

変数を即時展開して反映する。
パースされたタイミングの変数の値を展開するので、その後の変更は反映されない。ただし、パースされた時点で未定義の変数があった場合は、それは変数として残る。
以下の例で3つの変数にこのオペレータを使用した場合の挙動を説明する。
local.conf内の記述は以下のとおりである。

  • local.conf
MY_A = "A"
MY_VAR_A := "${MY_A}"
MY_A = "B"
MY_VAR_B = "${MY_A}${MY_C}"
MY_C = "C"
MY_C := "${MY_C}_MYC"

MY_VAR_A

MY_VAR_A := "${MY_A}" という記述の場合は、この記述がパースされる時点でMY_A = "A" であるので、MY_VAR_A = "A" と展開される。

  • bitbake-getvar MY_VAR_A
#
# $MY_VAR_A
#   immediate /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:280
#     "${MY_A}"
MY_VAR_A="A"

MY_VAR_B

MY_VAR_B := "${MY_A}${MY_C}" という記述の場合は、この記述がパースされる時点で MY_A = "B" で、MY_Cは未定義である。そのため、オペレータの処理時には一度MY_VAR_B := "B${MY_C}" のような形で展開され、その後の変数展開のタイミングで変数MY_C の展開が行われるという動きになるようだ。

  • bitbake-getvar MY_VAR_B
#
# $MY_VAR_B
#   immediate /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:282
#     "${MY_A}${MY_C}"
MY_VAR_B="BC_MYC"

MY_C

MY_C は初めにMY_C = "C" で定義された後で、MY_C := "${MY_C}_MYC" という自分自身を参照する形で再指定されている。
これは循環参照になってしまうように見えるが、このオペレータに限ってはそうはならない。それはこの記述をパースした時点でMY_C は"C_MYC" という値に確定できるためだと思われる。 オペレータが=などの場合は当然循環参照になり、bitbake-getvarコマンドがエラーを吐く。

  • bitbake-getvar MY_C
#
# $MY_C [2 operations]
#   set /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:283
#     "C"
#   immediate /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:284
#     "${MY_C}_MYC"
# pre-expansion value:
#   "C_MYC"
MY_C="C_MYC"

+= and =+

リスト変数に値を追加する。
+=がappendで末尾に値を追加。=+がprependで先頭に値を追加。追加するときに勝手にスペースをつけてくれるので、変数の値にスペースはいらない。

  • local.conf
MY_VAR = "MY"
MY_A = "A"
MY_B = "B"
MY_VAR += "${MY_A}"
MY_VAR =+ "${MY_B}"
  • bitbake-getvar MY_VAR
#
# $MY_VAR [3 operations]
#   set /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:279
#     "MY"
#   append /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:282
#     "${MY_A}"
#   prepend /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:283
#     "${MY_B}"
# pre-expansion value:
#   "${MY_B} MY ${MY_A}"
MY_VAR="B MY A"

.= and =.

変数に値を追加するが、+=や=+と違ってスペースが付与されない。そのため文字列の結合のような意味合いになる。
.=がappendで末尾に値を追加で、=.がprependで先頭に値を追加。

  • local.conf
MY_VAR = "MY"
MY_A = "A"
MY_B = "B "
MY_VAR .= "${MY_A}"
MY_VAR =. "${MY_B}"
  • bitbake-getvar MY_VAR
#
# $MY_VAR [3 operations]
#   set /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:279
#     "MY"
#   postdot /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:282
#     "${MY_A}"
#   predot /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:283
#     "${MY_B}"
# pre-expansion value:
#   "${MY_B}MY${MY_A}"
MY_VAR="B MYA"

オーバーライドシンタックス

オペレータではなく変数の末尾に変数の末尾に:(コロン)をつけて記述して変数を操作する方式もある。
オペレータで行う場合との違いは、appendやprependが実行されるタイミングで、オペレータではパース時に即時反映されるのに対し、こちらでは変数パース後に反映される。
そのためオペレータで値を上書きされないメリットがあり、必ず設定したい値などはこの方法で指定する方が良い。

:append and :prepend

値のappendとprepend。
.=などと同様に自動でスペースは入らないので、スペースを入れたい場合は自分で管理する必要がある。
以下の例で実際の動作を見ていく。
まず、local.confは以下のようになっていて、:appendと:prependを指定した後の行でMY_VARを上書きするようにMY_VAR = "NO_MY" と記述してある。
これの最終的な値を bitbake-getvar MY_VAR コマンドで確認すると、:appendと:prependの指定は無視されず、上書き後の値 "NO_MY" への文字列の追加処理が行われている。

  • local.conf
MY_VAR = "MY"
MY_A = "A"
MY_B = "B "
MY_VAR:append = "${MY_A}"
MY_VAR:prepend = "${MY_B}"
MY_VAR = "NO_MY"
  • bitbake-getvar MY_VAR
#
# $MY_VAR [4 operations]
#   set /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:279
#     "MY"
#   :append /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:282
#     "${MY_A}"
#   :prepend /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:283
#     "${MY_B}"
#   set /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:284
#     "NO_MY"
# pre-expansion value:
#   "${MY_B}NO_MY${MY_A}"
MY_VAR="B NO_MYA"

ちなみに、:appendではなく.=で記述すると以下のようになり、MY_VAR .= "${MY_A}" の指定は、MY_VAR = "NO_MY" の指定で上書きされてしまうことがわかる。

  • local.conf
MY_VAR = "MY"
MY_A = "A"
MY_B = "B "
MY_VAR .= "${MY_A}"
MY_VAR:prepend = "${MY_B}"
MY_VAR = "NO_MY"
  • bitbake-getvar MY_VAR
#
# $MY_VAR [4 operations]
#   set /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:279
#     "MY"
#   postdot /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:282
#     "${MY_A}"
#   :prepend /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:283
#     "${MY_B}"
#   set /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:284
#     "NO_MY"
# pre-expansion value:
#   "${MY_B}NO_MY"
MY_VAR="B NO_MY"

:remove

リストから値を削除する。
こちらも:appendなどと同様のタイミングに適用される。
以下の例では、MY_VAR に設定した値をMY_VAR:remove で削除している。この例の通り、MY_VAR:removeが指定された時点では存在しない値”D”が削除されることから、remove とappend を記述する順番は関係なく、必ずappend が先に処理され、そこで完成したリストからremove で値を削除する処理順序となっていることがわかる。

  • local.conf
MY_VAR = "MY A B B C"
MY_VAR:remove = "A B C D"
MY_VAR:append = " D"
  • bitbake-getvar MY_VAR
#
# $MY_VAR [3 operations]
#   set /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:279
#     "MY A B B C"
#   :remove /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:280
#     "A B C D"
#   :append /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:281
#     " D"
# pre-expansion value:
#   "MY A B B C D"
MY_VAR="MY     "

リスト内に変数が含まれる場合も同様に調べると、以下のような結果になった。
変数で指定した値も削除対象となっていることから、:appendや:removeの処理順序は、:append -> 変数展開 -> :remove となっていることが分かった。

  • local.conf
MY_VAR = "MY A B B C ${MY_VAR_2}"
MY_VAR_2 = " A B C"
MY_VAR:remove = "A B C D"
MY_VAR:append = " D"
  • bitbake-getvar MY_VAR
#
# $MY_VAR [3 operations]
#   set /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:279
#     "MY A B B C ${MY_VAR_2}"
#   :remove /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:281
#     "A B C D"
#   :append /home/user/yocto/yocto-workspace/rpi-build/conf/local.conf:282
#     " D"
# pre-expansion value:
#   "MY A B B C ${MY_VAR_2} D"
MY_VAR="MY         "

まとめ

オペレータとオーバーライドシンタックスの仕様をまとめた。
まだbitbakeの文法の理解が浅いので、引き続きドキュメントを読んでまとめようと思う。
次はファイルのパースされる順番、つまりどのファイルの設定が優先されるのかというところを調べたい。