技術系ブログなのだから、もっと頻度を上げたいんだけどなぁ。
ということは、さておき。
オープン系、とくにC/C++を扱っていると必ずmakefileと対峙するときがある。
今回はそんなお話。
久々にmakefileをいじって、効率よくコンパイルできるようにしようとしたのだけれど。
makefile内で設定した変数がいつ値がセットされるのか?をよくよく考える必要があるよ、ということです。
背景ですが、複数の異なるプロセッサに対して、同じソースでそれぞれで動作するバイナリを作成したい、そんなときにハマりました。
以下、はまったときのmakefile
projA: PRJ_SUFFIX=_A projA: proj projB: PRJ_SUFFIX=_B projB: proj OBJDIR=obj$(PRJ_SUFFIX) BINDIR=bin$(PRJ_SUFFIX) SRCS=main.c tool.c OBJS=$(patsubst %.c,$(OBJDIR)/%.o,$(SRCS)) CFLAGS=-D__DEF$(PRJ_SUFFIX)__ proj: $(BINDIR)/proj $(BINDIR)/proj: $(OBJS) @if [ ! -e $(BINDIR) ]; then mkdir -p $(BINDIR);fi gcc -o $@ $(OBJS) $(OBJS): $(OBJDIR)/%.o: %.c @if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR);fi gcc -o $@ $(CFLAGS) -c $<
projAのときはprojA用のdefineを定義し、projBのときはprojB用のdefineを定義することで、プロセッサ個別のソースをコンパイルする、という流れです。
何が問題か?というと、ターゲットごとに設定した変数の値がmakefile内ですべてに適用される訳ではない、ということ。
具体的にみていくと、まずターゲット特有の変数を参照した他の変数は、正しく値が変わります。
上記の例でいけば、PRJ_SUFFIXはprojAのときは正しく"_A"が設定され、それを参照したOBJDIRも"obj_A"となります。
しかし、問題なのはターゲットにある変数で、例えば以下の場合。
$(OBJS): $(OBJDIR)/%.o: %.c @if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR);fi gcc -o $@ $(CFLAGS) -c $<
このときの「ターゲット」としてはprojA特有の変数によって$(OBJDIR)/%.oは"obj_A/%.o"となっているのでいいのですが、自動変数である$@が正しく設定されないのです。。projA特有の変数が空の状態で設定されるので、この場合$@が"obj/%.o"という値を返してきます。
これ、ほんと悩みましたよ。。echoで出してみてやっと理解。
おそらく$@だけは最初の処理で確定してしまうのでしょうね。
ちなみに、PRJ_SUFFIXを外部から環境変数として渡すと期待通りの動きをするので、余計悩みました。
ということで、正しく動くmakefileは以下です。
projA: PRJ_SUFFIX=_A projA: proj projB: PRJ_SUFFIX=_B projB: proj OBJDIR=obj$(PRJ_SUFFIX) BINDIR=bin$(PRJ_SUFFIX) SRCS=main.c tool.c OBJS=$(patsubst %.c,$(OBJDIR)/%.o,$(SRCS)) CFLAGS=-D__DEF$(PRJ_SUFFIX)__ proj: $(BINDIR)/proj $(BINDIR)/proj: $(OBJS) @if [ ! -e $(BINDIR) ]; then mkdir -p $(BINDIR);fi gcc -o $(BINDIR)/$(@F) $(OBJS) $(OBJS): $(OBJDIR)/%.o: %.c @if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR);fi gcc -o $(OBJDIR)/$(@F) $(CFLAGS) -c $<
$(@F)は、ディレクトリを除いたファイル名部分を取り出す書式です。
以下、参考URL。助かりましたぁ。
GNU make
0 件のコメント:
コメントを投稿