ここしばらく更新されていなかったlinux-nextが約10日ぶりに更新(9/4の次が9/15)されたので、喜んでgit fetchしてTinker Boardのカーネルを書き換えたところ、ウンともスンとも言わなくなってしまいました。
質の悪いことにエラーメッセージも何も出さずにハングアップするため、起動しなくなった原因がさっぱりわかりません。
仕方ないので頑張ってgit bisectし、起動しなくなった原因のコミットを見つけましたが、実は9/17にLKMLにて既に指摘済みで、直し方まで検討されていました(LKMLへのリンク)。今までの苦労は何だったのか。
結果だけ見ると、最初にLKMLのメールスレッドに気づいていればbisectなんて不要でした。しかし、一切エラーメッセージを出さずにハングするので、検索のヒントがありません。ノーヒントでLKMLの当該メールスレッドを探せたか?と考えてみると、ちょっと難しい気がします。
いわゆる鶏と卵の問題ですね……。
シェルからmakeに渡す環境変数とmake変数の関係を知らなくて、かなりハマったのでメモしておきます。
まず、どういう関係か説明します。下記のようなMakefileを2つ用意します。
$ tree . |-- Makefile `-- sub `-- Makefile
VAR_A = aaa
VAR_B = bbb
all:
@echo "In parent"
@echo "VAR_A: '$(VAR_A)'"
@echo "VAR_B: '$(VAR_B)'"
@echo "VAR_C: '$(VAR_C)'"
make -C sub
all:
@echo "In sub"
@echo "VAR_A: '$(VAR_A)'"
@echo "VAR_B: '$(VAR_B)'"
@echo "VAR_C: '$(VAR_C)'"
親Makefileは変数VAR_A, VAR_Bを書き換え、子sub/Makefileを再帰的に呼び出します。各Makefileでは変数の値を表示しています。
では、トップディレクトリにてmakeを実行してみましょう。
$ make In parent VAR_A: 'aaa' ★VAR_Aは設定した値になっている★ VAR_B: 'bbb' ★VAR_Bは設定した値になっている★ VAR_C: '' ★VAR_Cは特に書き換えていないので空★ make -C sub make[1]: Entering directory '/home/katsuhiro/share/falcon/projects/c/makefile_env_var/sub' In sub VAR_A: '' ★VAR_Aは渡されない★ VAR_B: '' ★VAR_Bは渡されない★ VAR_C: '' ★VAR_Cは渡されない★ make[1]: Leaving directory '/home/katsuhiro/share/falcon/projects/c/makefile_env_var/sub'
ご覧の通り、親のMakefileで値を設定したVAR_AやVAR_Bといった変数は、子のsub/Makefileに「引き継がれません」。
外部からmakeに変数を渡すには、下記の2つの方法があります。
私は今まで、この2つの渡し方に何も差はないと思っていたのですが、実は全く動きが違いました。
下記のようにVAR_A, VAR_Cを環境変数として与えると、子Makefile側の結果がかなり変わります。
$ VAR_A=A VAR_C=C make In parent VAR_A: 'aaa' ★VAR_Aは親が設定した値になる★ VAR_B: 'bbb' VAR_C: 'C' ★VAR_Cは特に書き換えていないので、渡された値のまま★ make -C sub make[1]: Entering directory '/home/katsuhiro/share/falcon/projects/c/makefile_env_var/sub' In sub VAR_A: 'aaa' ★VAR_Aが渡される、Aではなく親が設定した値aaaになっている★ VAR_B: '' VAR_C: 'C' ★VAR_Cが渡される、値は変わらず★ make[1]: Leaving directory '/home/katsuhiro/share/falcon/projects/c/makefile_env_var/sub'
何も渡さなかった場合の実行結果と異なり、子のsub/MakefileにもVAR_A, VAR_Cが渡されます。もし、親Makefileが変数の値を書き換えた場合は、書き換えた値が子Makefileに渡されます。
この動作は知りませんでした。特に子プロセスへの変数の渡し方が変わる点が衝撃的です。
下記のようにVAR_A, VAR_Cをmakeに渡すと、環境変数として渡す場合とは違う結果になります。
$ make VAR_A=A VAR_C=C In parent VAR_A: 'A' ★VAR_Aは親が設定した値にならない★ VAR_B: 'bbb' VAR_C: 'C' make -C sub make[1]: Entering directory '/home/katsuhiro/share/falcon/projects/c/makefile_env_var/sub' In sub VAR_A: 'A' ★VAR_Aが渡される、VAR_Aは親が設定した値にならない★ VAR_B: '' VAR_C: 'C' ★VAR_Cが渡される、値は変わらず★ make[1]: Leaving directory '/home/katsuhiro/share/falcon/projects/c/makefile_env_var/sub'
環境変数で渡した場合と同様に、子Makefileに変数の内容が渡されます。その点は同じです。しかし、親Makefileで行っているaaaという値の代入が無効化され、渡される値が全く違います。
この動作も知りませんでした……。私はMakefileやmakeとは長い付き合いですし、動きも何となくわかっていた気分になっていましたが、勘違いだったようです。makeは難しすぎます。
この動きによって、何が困ったかを紹介しておきます。同じ状況に陥る人は、まずもっていないと思いますけど、ご参考まで。
現象としては「makeでビルドすると成功するが、debuild(Debianのパッケージング作成スクリプト)経由でビルドすると失敗する」です。この現象から原因が予想できた人、あなたは凄い(少なくとも私より凄い)です!この先は読む必要はございません。
下記のような感じの、ちょっと変わったMakefileを使っているソフトウェアをビルドしていました。
もう一つ大事な点は、親Makefileが使っているLDFLAGSを、子 ./configureに渡すと「そんなライブラリはないというエラー」になってしまう欠点があることです。
じゃあビルドエラーが起きるのか?というとそうではなく、先ほどご紹介した通り、何も指定せずにmakeを起動すれば、親Makefileの変数は、子 ./configureに渡りませんから、makeだけ実行すればエラーを起こさずビルドできるのです。
一見、正常にビルドできて問題ないように見えますが、このソフトウェアを *.debにパッケージングしようとするとハマります。Debianのパッケージ作成スクリプトdebuildは下記のような動作をするからです。
そろそろ何が起きるか予想が付くでしょう。そう、こうなるんです。
この華麗なコンボが決まって、makeとするとビルドが成功し、debuild経由だとビルドが失敗する、謎のビルド環境ができあがります。
こんなバグ、初見で分かる、はずもなし。
Makefileは大抵の人には難しすぎます。Makefileを手で書いているといつか地獄に落ちますよ、CMakeとかautomakeを使いましょう。便利だよ!
< | 2019 | > | ||||
<< | < | 09 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | - | - | - | - | - |
合計:
本日: