Makefile覚書

 makefileの使い方というか、自分の流儀の紹介。

C/C++用のmakefile

 makefileで結構困るのが、オプションの指定。 同じプロジェクトでもマシンによってオプションは違うし、 当然スパコンではコンパイラも違う。それを直すたびに svnやcvsで変更されたと判定されてしまう。 かといってホスト判別とかやっていると面倒だし、個別の情報が全部入るのは 好ましくない。

 というわけで、試行錯誤の末、オプションを別ファイルに分けることで 落ち着いている。マシンごとに共通のディレクトリに入れる流儀も あるようだが、僕は各プロジェクトのディレクトリに入れている。 同じマシンでも、プロジェクトによってオプションが変わるからだ。

 具体的には、以下のようなmakefileを作る。 言語はC++、拡張子は*.c,*.ccだとして、そのディレクトリには プロジェクトにかかわるソースしか置かないものとする。

#------------------------------------------------------
# Makefile for C/C++ Program
#------------------------------------------------------
# Target: a.out
# Author: H. Watanabe
#------------------------------------------------------

TARGET=a.out

#------------------------------------------------------
# Default Parameters
#------------------------------------------------------

CC=g++
OPT=
INC=
LIB=

#------------------------------------------------------
# Compile Option
#------------------------------------------------------

-include makefile.opt

#------------------------------------------------------
# Definition
#------------------------------------------------------

.SUFFIXES:.cc .c .o .h

#---
# Source Files
#---

SRC=$(shell ls *.cc)
HED=$(shell ls *.h)
OBJ=$(SRC:.cc=.o)

#------------------------------------------------------
# rules
#------------------------------------------------------

all: $(TARGET)
$(TARGET): $(OBJ)
	$(CC) $(OPT) -o $(TARGET) $(OBJ) $(LIB)

.c.o:
	$(CC) $(OPT) -c $< $(INC)
.cc.o:
	$(CC) $(OPT) -c $< $(INC)

dep:
	g++ -MM -MG $(SRC) >makefile.depend

clean:
	rm -f $(TARGET) $(TARGET).exe
	rm -f *.o *.obj
	rm -f *~ *.~*

tar:
	tar cvzf $(TARGET).tar.gz $(SRC) $(HED) makefile

#--------------------------------------------------
-include makefile.depend

 これで、ほとんどのプロジェクトで最初のTARGETを書き換えるだけで makeできるはず。最初に

% make dep
とやることで、依存関係ファイル makefile.dependが作られる。 その後makeする。 オプションは、makefile.optというファイルに書いて同じところにおいておく。 たとえば、Intelの純正コンパイラを使う例なら
CC=icc
OPT=-O3 -xW -axW -align -ip -ipo -Kc++
などと書いておく。MPIとしてmpichを使うなら、
LIB=-L/usr/local/mpich/lib -lmpich
INC=-I/usr/local/mpich/include 
などもあわせて指定する。 要するにローカルな設定(コンパイラ、オプション、ライブラリパス)などを すべてmakefile.optに押し込める。するとmakefileはプロジェクト共通となるので リポジトリに入れっぱなしでよいので便利。 makefile.optとmakefile.dependはリポジトリには入れない。

 大きなプロジェクトの場合、cleanの使い方がちょっと面倒になる。 たとえば、ソースとドキュメントを管理しているとき、 ソースは *.oや*.objが邪魔者だが、ドキュメントでは *.objを 消されると困る、なんて場合がある(tgifを使っているときとか)。 昔、 cleanルールをfind と xargsの組み合わせで作成して 根こそぎ消して、ひどい目にあったことがある。 こういう場合、ソースとドキュメントを別々のディレクトリ (srcとdocとか)に分けて、make -C を使うと良い。

 たとえば、srcとdocに分けたとき、一番上のmakefileの cleanのルールは

clean:
	make -C src clean
	make -C doc clean
と書く。それぞれ中身は src/makefile、doc/makefileに書いておく。 なるべくローカルな情報はローカルに、という原則を守ると管理が楽だ。

 最後のmake tar もあると時々便利。


Tex用のmakefile

TARGET=hoge
EPS=$(shell ls *.eps)
TEX=latex
DVIPDF=/cygdrive/c/usr/local/bin/dvipdfmx.exe

.SUFFIXES: .tex .dvi

all:$(TARGET).dvi

$(TARGET).dvi: $(TARGET).tex $(EPS)

.tex.dvi:
	$(TEX) $<
	$(TEX) $<

tar:
	tar cvzf $(TARGET).tar.gz $(TARGET).* *.eps

pdf: $(TARGET).dvi
	$(DVIPDF) $(TARGET)

clean:
	rm -f $(TARGET).dvi $(TARGET).aux $(TARGET).log $(TARGET).toc
	rm -f $(TARGET).pdf $(TARGET).tar.gz 
	rm -f *.pbm *.bmc
多くの場合、TARGETを書きかえるだけ、また日本語文書なら TEXをplatexにするだけでそのまま使える。 cygwin上でpdfを作れるようにするために別インストールした dvipdfを使っているので DVIPDFを再定義している。 また、epsファイルへの依存性もあると便利。 make pdf や make tarもあると便利。 TOCを作る場合はもう一度コンパイルしなきゃいけなかったり、 もっときれいなやりかたもあると思うけれど、とりあえずこれで不自由しない。

Mac OS用のtex makefile

 Mac用にもxdviはあるけれど、いきなりpdfにしてしまった方が便利なので、こんな感じにして使っている。

TARGET=hoge
EPS=$(shell ls *.eps)
TEX=platex
DVIPDF=dvipdfmx

.SUFFIXES: .tex .dvi .pdf

all:$(TARGET).pdf
	open $(TARGET).pdf

$(TARGET).dvi: $(TARGET).tex $(EPS)

$(TARGET).pdf: $(TARGET).dvi

.tex.dvi:
	$(TEX) $<
	$(TEX) $<

tar:
	tar cvzf $(TARGET).tar.gz $(TARGET).* *.eps

.dvi.pdf:
	$(DVIPDF) $<

clean:
	rm -f $(TARGET).dvi $(TARGET).aux $(TARGET).log 
	rm -f $(TARGET).pdf $(TARGET).tar.gz 
	rm -f *.pbm *.bmc
	rm -f *~

 最後openでpdfを開くとちょっと便利。

データやtgifファイルからepsを作るmakefile

 データからhoge.epsを作るgnuplotスクリプトをhoge.pltとして作っておく。つまり、

$ gnuplot hoge.plt
としたらhoge.epsができるものとする。また、図をtgifで作っているのなら、 hoge.objからhoge.epsができるようになっているだろう。この、*.pltと*.objから *.epsを作るmakefile例は以下の通り。

PLT=$(shell ls *.plt) 
OBJ=$(shell ls *.obj) 
EPS=$(OBJ:.obj=.eps) $(PLT:.plt=.eps)

.SUFFIXES: .plt .eps .obj

all: $(EPS) 

.plt.eps:
	gnuplot $<

.obj.eps:
	tgif -print -eps $<

clean:
	rm -f $(EPS)

 make一発でデータからepsが更新されて便利。依存関係を書くのが面倒なら、 なんか変更があるたびにmake clean;make してしまえば良い。texのmakeと組み合わせれば データの更新からtexのpdf化までmake一発。


Author: Hiroshi Watanabe

トップページへ戻る