2014年6月1日日曜日

makefile、それは避けて通れない道

気づけば、また日にちがあいてしまった。。
技術系ブログなのだから、もっと頻度を上げたいんだけどなぁ。

ということは、さておき。


オープン系、とくに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 件のコメント:

コメントを投稿