Makefileいろいろ
Emacs23は最低限文化的なEmacsだと思うインターネットの闇です。
問題
「今のビルド環境、絶対パスとかレガシーなスクリプトが多すぎて手に負えない。作りなおして」 と言われた。新人の私が。
問題設定
- 管理しているソースから複数のプロダクトを作る
ここでは便宜上それらをsofta, softb, softcと呼ぶ
それらは全ての.cファイルを使うわけではなくsoftaでは使うけどsoftbでは使わない.cファイルが普通にファイルに並んでいる - さらにいろいろなアーキテクチャで動作させる
便宜上mips, arm32で動かさなきゃいけないことにしよう
それらも割とフリーダムにこのアーキでは使わないソースだ、とか#ifdef
で切り分けとかがガンガン行われている そのごっちゃになっているのを管理しているのは各プログラムフェーズごとに区切られたディレクトリに存在する
list.txt
ファイル
例えば以下のようにディレクトリがあるとき、どのようにlist.txt
が配置されているのか示す。include/ front/ middle/ back/ src/ front/ list.txt opt1.c opt2.c ... middle/ list_mips.txt list_arm32.txt analysis.c fusion.c ... back/ list_mips_softa.txt list_mips_softb.txt list_mips_softc.txt list_arm32_softa.txt list_arm32_softb.txt list_arm32_softc.txt ... include/ front/ middle/ back/
list.txt
ファイルはアーキ・ソフトに関係ないところではこの名前がついている- それらごとに依存ファイルが違う場合にはそれぞれ
list
ファイルが存在し、 その中に使うファイルが書かれている
- 昔のレガシービルド環境ではそれら
list.txt
ファイルの配置やベタ書きされたこのファイルはこういうふうにコンパイルしなきゃダメ、 みたいな情報からMakefileを自動生成してビルドしているが、絶対パスモリモリで大変厳しいことになっている
私の試行
絶対パスを含まないクリーンなMakefileを書くぞと決意
考えられる問題は
- アーキ・ソフトウエアごとに切り分けされた膨大な依存関係をどのように記述するのか
- ヘッダーファイルもごちゃごちゃで、ファイルをまたぐと同じ名前のヘッダーファイルが存在するため
どのように
-I
の指定をしていくのか - どうやって対象にしたいアーキやソフトウエアを選択するのか
autoconf
のようなツールの知見もないし社内システムだからそんなに綺麗にしなくてもいいし
プロジェクトは割とひぃひぃ言っているのであまり労力もかけなくない
考えたアプローチを下に示してみる
- トップレベルは
Makefile
ここから各ソフトウエア(softa, softb, softc)をビルドするMakefile_ARCH_SOFT
を呼び出す - アーキは
make
コマンドを実行するときに受け取ることにする
使うときはmake ARCH=mips
のようにしてもらう - 依存関係の記述はめんどくさいので今までの
list.txt
ファイルを流用する(後述) 登場人物は
Makefile
は全部の入り口
ここでは受け取ったアーキテクチャごとに分岐するということだけを行う
inclde Makefile.shared include setopt all: fofta softb softc softa: ifeq($(TARGET_ARCH), mips) $(MAKE) -f Makefile_mips_softa else ifeq($(TARGET_ARCH), arm32) $(MAKE) -f Makefile_arm32_softb end end softb: ...(同様)... softc: ...(同様)... clean: ...
Makefile.shared
は設定が書かれた全部で共有すべきもの
export TARGET_ARCH= export HOST_ARCH= export SOFT= CC=cc DEBUG= LIB=-lgc -lm -ldl DEF=-D USE_HUSHIGINA_MACRO -D ENABLE_FAST -D HOGEHOGE_DEBUG # なぜかsrc/の中のファイルでもヘッダーが存在するものがあるので殴りたい INCLUDE=-I include/front -I include/middle -I include/back -I src/back CFLAG=$(LIB) $(INCLUDE) .c.o: $(CC) $(CFLAG) $(DEBUG) -c -o $@ @<
Makefile_mips_softa
を代表して示してみる
SOFT=softa ifdef TARGET_ARCH include setopt end INCLUDE+= -I include/aa -I include/bb DEF+= -D XXX_SOFTA -D ... include Makefile.shared include common_shared include common_softa ARCH_OBJS=aa.a bb.a cc.a ... OBJS=$(COMMON_OBJS) $(ARCH_OBJS) $(SOFT_OBJS) softa: $(OBJS) $(CC) -o $@ aa.a: $(shell bash selector.sh src/aa $(SOFT) $(TARGET_ARCH)) $(AR) $@ rcs src/aa/*.o ...
このMakefile
はたくさんのMakefile断片をインクルードしている
- ビルドに必要な共通の設定が書かれたMakefile.shared
- 全てのソフトウエアで共通の依存関係が書かれたcommon_shared
- softa
に特有の.a
ファイルの依存関係が書かれたcommon_softa
- アーキテクチャ固有の依存関係はベタ書きしている
- setopt
は$(ARCH_TARGET)
などがセットされているかチェックしなければはじく
またselector.sh
は重要な役割を果たす
DIRECTORYと$(TARGET_ARCH)
と$(SOFT)
を受け取り適切なlist.txt
ファイルを選ぶシェルスクリプトである
例えば上のaa.a: $(shell bash selector.sh src/aa $(SOFT) $(ARCH))
では
ディレクトリsrc/aa
といまビルドしたいソフト名softa
, アーキ名を受け取り
list.txt
があればlist.txt
の中身、list_mips.txt
があればその中身、list_softa.txt
があればその中身、
list_mips_softa.txt
があればその中身とまさに適切なlist.txt
を選ぶセレクターの役割を果たす
これらによって作られた.a
ファイルを集めてきて実行形式を作る
辛さ
- なんか
Makefile
の種類がどんどん増えてきたんですけど……
同じことを二度書きたくないのでcommon_shared
やcommon_softa
といったように依存関係を分けたのだけど、 ファイルの数が増えていってイヤンだなぁと思ってきている selector.sh
は最初は賢いように思ってたのだけど、なかなかつらい
なんせ毎回実行するわけだからヘボいマシンだと実行自体に時間がかかってしまう
また依存関係をmake
が解析するときにこれは実行されるので結構実行される順番に気を使わないといけない
includeする順番によってはあれ、フラグまだ足してなかった!!!!みたいなことになる.c.o
のルールでヘッダーファイルを見に行くオプションを全部に胸痛にしたので、
全部書いたので同じヘッダーファイルがあった場合にオプションの並び順で見に行く先がかわる!!!!!!!
これは大変つらい、むしろ同じファイル名を政治的に変更しに行くことを考えたほうが良さそう#ifdef
とかでソースが切り分けしまくってるから違うアーキ・ソフトをコンパイルしようとすると
全部ビルドしなおし。ビルド時間……
結論
いい方法あったら教えてください
よい転職先は、まぁもうちょっと経ったら教えてください……
MLを使いたいなぁと思っている間に2か月経ってMakefileとshell scriptを書いていた
— インターネットの闇 (@no_maddo) June 6, 2016
駆逐艦にお世話されたい欲求が高まってきた
— インターネットの闇 (@no_maddo) June 6, 2016
定期的にはいふりになりたい欲求とデススティンガーになりたい欲求が交互に襲いかかってくる
— インターネットの闇 (@no_maddo) June 6, 2016