読者です 読者をやめる 読者になる 読者になる

Makefileを書くのに疲れた人のためのSCons

dtlのサンプルプログラムやテストプログラムのビルドをSConsでやってみた。

dtlの複数あるサンプルプログラムのMakefileはこんな風になっている。

Makefile
CPP               = g++
CPPFLAGS          = -c -O2 -Wall
OBJS_FLAGS        = -O2 -o
STRDIFF_OBJS      = strdiff.o common.o
STRDIFF3_OBJS     = strdiff3.o common.o
UNIDIFF_OBJS      = unidiff.o common.o
UNISTRDIFF_OBJS   = unistrdiff.o common.o
INTDIFF_OBJS      = intdiff.o
INTDIFF3_OBJS     = intdiff3.o
PATCH_OBJS        = patch.o common.o
FPATCH_OBJS       = fpatch.o common.o
ALL_TARGETS       = strdiff strdiff3 unidiff unistrdiff intdiff intdiff3 patch fpatch

DEPENDENCY_FILE   = Makefile.depend

.cpp.o:
        $(CPP) $(CPPFLAGS) $<

all : $(ALL_TARGETS)

strdiff : $(STRDIFF_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(STRDIFF_OBJS)

strdiff3 : $(STRDIFF3_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(STRDIFF3_OBJS)

unidiff : $(UNIDIFF_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(UNIDIFF_OBJS)

unistrdiff : $(UNISTRDIFF_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(UNISTRDIFF_OBJS)

intdiff : $(INTDIFF_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(INTDIFF_OBJS)

intdiff3 : $(INTDIFF3_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(INTDIFF3_OBJS)

patch : $(PATCH_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(PATCH_OBJS)

fpatch : $(FPATCH_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(FPATCH_OBJS)

clean:
        ${RM} *.o *~ $(ALL_TARGETS)

depend:
        @echo "generating dependency file"
        @$(CPP) -MM *.cpp > $(DEPENDENCY_FILE)

include $(DEPENDENCY_FILE)

各サンプルプログラムがターゲットになっていて、依存関係の解析はGCCの-MMオプションを使ってMakefile.dependに出力し、そのMakefile.dependをMakefileがインクルードするようにしている。

makeでビルド
$ make
g++ -c -O2 -Wall strdiff.cpp
g++ -c -O2 -Wall common.cpp
g++ -O2 -o strdiff strdiff.o common.o
g++ -c -O2 -Wall strdiff3.cpp
g++ -O2 -o strdiff3 strdiff3.o common.o
g++ -c -O2 -Wall unidiff.cpp
g++ -O2 -o unidiff unidiff.o common.o
g++ -c -O2 -Wall unistrdiff.cpp
g++ -O2 -o unistrdiff unistrdiff.o common.o
g++ -c -O2 -Wall intdiff.cpp
g++ -O2 -o intdiff intdiff.o
g++ -c -O2 -Wall intdiff3.cpp
g++ -O2 -o intdiff3 intdiff3.o
g++ -c -O2 -Wall patch.cpp
g++ -O2 -o patch patch.o common.o
g++ -c -O2 -Wall fpatch.cpp
g++ -O2 -o fpatch fpatch.o common.o
$

一方、同じことをSConsでやろうとするとこうなる。

SConstruct
env = Environment(CCFLAGS='-Wall -O2')
targets = { 'strdiff'    : ['strdiff.cpp',    'common.cpp'],
            'intdiff'    : ['intdiff.cpp'],
            'strdiff3'   : ['strdiff3.cpp',   'common.cpp'],
            'intdiff3'   : ['intdiff3.cpp',   'common.cpp'],
            'unidiff'    : ['unidiff.cpp',    'common.cpp'],
            'unistrdiff' : ['unistrdiff.cpp', 'common.cpp'],
            'patch'      : ['patch.cpp',      'common.cpp'],
            'fpatch'     : ['fpatch.cpp',     'common.cpp']
            }

for target in targets:
    objs = targets[target]
    env.Program(target, obs)

Makefileみたいに奇妙で覚えづらい呪文($@, $<, $^, $%, $?, $+, $*)はないし、
Pythonで書けるからとてもプログラムっぽくなった。
しかもdependターゲットはもう必要ない!(ここ重要)

sconsでビルド
$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o common.o -c -Wall -O2 common.cpp
g++ -o fpatch.o -c -Wall -O2 fpatch.cpp
g++ -o fpatch fpatch.o common.o
g++ -o intdiff.o -c -Wall -O2 intdiff.cpp
g++ -o intdiff intdiff.o
g++ -o intdiff3.o -c -Wall -O2 intdiff3.cpp
g++ -o intdiff3 intdiff3.o common.o
g++ -o patch.o -c -Wall -O2 patch.cpp
g++ -o patch patch.o common.o
g++ -o strdiff.o -c -Wall -O2 strdiff.cpp
g++ -o strdiff strdiff.o common.o
g++ -o strdiff3.o -c -Wall -O2 strdiff3.cpp
g++ -o strdiff3 strdiff3.o common.o
g++ -o unidiff.o -c -Wall -O2 unidiff.cpp
g++ -o unidiff unidiff.o common.o
g++ -o unistrdiff.o -c -Wall -O2 unistrdiff.cpp
g++ -o unistrdiff unistrdiff.o common.o
scons: done building targets.
$


ただ、プロジェクトの規模が大きくなるとSConsは使えないらしい↓


http://twitter.com/taku910/status/13854036710:twitter:detail:left


もうちょっと詳しめなのを↓に書きました。


Makefileを書くのに疲れた人のためのSCons