mffw -- Makefile フレームワーク

$Id: Readme-mffw.html,v 1.8 2002/11/07 06:54:30 oka Exp $

Date:   2001/01/14
author: OKA Toshiyuki
Copyright (c) 2001-2002 by LangEdge Inc.

#-----------------------------------------------------------------------
# 使用・複製・修正・配布に関する許諾条件:
# 本ファイルは、以下の5条件が全て遵守される場合に限り、公序良俗に反しな
# い範囲で、商用・非商用を問わず、いかなる個人または組織に対しても対価を
# 支払うことなく、誰でも自由に、使用・複製・修正・配布の全部または一部の
# 行為を行うことができる。
# 
# (1) この「使用・複製・修正・配布に関する許諾条件」にある文言を一切修正
#     しないこと。
# 
# (2) ファイルの先頭部に記述してある author行および Copyright行または、
#     そのいずれかを削除したり修正したりしないこと。ただし、author行また
#     は Copyright行で記述されている当の個人または組織 (以後、「著作者」
#     と呼ぶ) は、自身に関する記述に限り、削除したり修正したりすることが
#     できる。
# 
# (3) ファイルに何らかの修正を加えた場合には、修正した個人または組織に関
#     する author行と Copyright 行を追加することができる。その場合、追加
#     された記述に対しても (2)項の規定が適用される。
# 
# (4) 本ファイルを使用、複製、修正または配布した結果として、いかなる種類
#     の損失、損害または不利益が発生しても、「著作者」がその責を一切負わ
#     ないことに同意し、かつ「著作者」にその責を一切負わせないこと。
# 
# (5) 本ファイルをコンパイラに適用して得られたバイナリオブジェクトは、そ
#     のコンパイルを実行した個人または組織の所有物であり、「著作者」との
#     間には、一切の権利・義務関係が存在しないことに同意する。
#-----------------------------------------------------------------------

C/C++ を使用するプロジェクト用に、汎用で使える Makefile のフレームワーク (以下、mffw と呼ぶ) を用意した。各プロジェクトは、このフレームワークを使 用することで、自前の Makefile を用意しなくても、一通りの作業を行うことが できる。

●簡単な導入

とりあえず使ってみるためには、次のようにする:

  1. GNUmake を用意する。名前は gmake にする。 Linux や *BSD なら標準で装備されていると思う。 Windows の場合は cygwin を導入するとよい。
  2. 添付の Makefile および Makefile.fw を、 プロジェクトのソースディレクトリツリーのトップにコピーする。 Makefile は、Makefile.fw を引数として再帰的に gmake を起動してくれる。
    % cp Makefile Makefile.fw {ProjectDir}/
    
  3. プロジェクトの各ディレクトリに mffw.conf を用意する。 これは、Makefile および Makefile.fw にインクルードされる。 このファイルにより、作成するコマンドやライブラリ、ソースファイル名などを定義する。
    % cd {ProjectDir}
    % vi mffw.conf
    
  4. プロジェクトのトップディレクトリで gmake を実行
    % gmake
    
    'debug' というディレクトリが作られ、その中に、 コンパイルされたオブジェクトやコマンドが格納される。

samle というディレクトリに単純なプロジェクトを用意しておいたので、 ここに Makefile と Makefile.fw をコピーして gmake を実行してみるとよいだろう。 (本稿末尾の「典型的なプロジェクト構成」を参照)

% cp Makefile Makefile.fw sample/
% cd sample
% gmake
% main/debug/main

●設定ファイル (mffw.conf)

Makefile.fw は、最初に作業ディレクトリにある mffw.conf というファイルを インクルードする。mffw.conf にソースファイル(群)の名前とターゲットの名前 を書いておく。

ターゲット指定として、少なくとも次のうちの1つを定義しなければならない。


CMDTARGET 作成するコマンド名
LIBTARGET 作成するライブラリ名

設定ファイルでターゲットだけが指定されている場合、mffw は作業ディレクトリ に格納されているソースファイルを勝手にコンパイル & リンクする。

[例]
CMDTARGET := foo
この例では foo という名前の実行ファイルを作成することを指示している。ソー スファイルの指定がないので、作業ディレクトリに格納されているソースファイ ルをすべてコンパイル & リンクして foo という名前の実行ファイルを作成する。 (sample/foo/mffw.conf も参照のこと)

ターゲットを構成するソースファイルを明示的に指定するには、次の変数を設定 する。


CMDSRCS CMDTARGET に直接リンクされるオブジェクトのソースファイル
LIBSRCS LIBTARGET の構成要素になるオブジェクトのソースファイル
YACCSRCS yacc で処理されるソースファイル
LEXSRCS lex で処理されるソースファイル

これらの変数には、スペースで区切って複数のファイル名を記述することができ る。

[例]
LIBTARGET := foo.a
LIBSRCS   := foo.cpp bar.c
この例では foo.cpp および bar.c をコンパイルして foo.a というライブラリを 作成することを指示している。

もし上記ソースファイルが一つも指定されていなかった場合は、

したがって、設定ファイルに CMDTARGET または LIBTARGET を記述するだけで、 作業ディレクトリに格納されているソースファイルからターゲットを作成することが可能である。

さらに、

となる。

他にも以下のような変数を設定できる。

PROJECT プロジェクト名を指定する。make 開始時のバナー表示で使用する。
(省略時: カレントの作業ディレクトリ名)
ROOTDIR 種々のディレクトリ指定の起点となるディレクトリ。フルパスで指定する。
(省略時: プロジェクトのトップディレクトリ)
SUBDIRS 当該作業ディレクトリのビルド前にビルド処理を実行しておきたいサブディレクトリを指定。 ディレクトリ名をスペースで区切って複数指定することができる。
例: SUBDIRS := foo bar
POSTDIRS 当該作業ディレクトリのビルド後にビルド処理を実行させたいサブディレクトリを指定。 ディレクトリ名をスペースで区切って複数指定することができる。
例: POSTDIRS := foo bar
COMMON_INCDIRS プロジェクト全体で共通に参照されるヘッダーファイルのサーチパスを指定。 ディレクトリ名をスペースで区切って並べる。この指定はサブディレクトリにも伝播する。
例: COMMON_INCDIRS := $(ROOTDIR)/include /proj/include
INCDIRS 個々の作業ディレクトリごとに指定するヘッダーファイルのサーチパスを指定。 COMMON_INCDIRS の指定とマージされる。 ディレクトリ名をスペースで区切って並べる。サブディレクトリには伝播しない。
(省略時: .)
CMDTARGETDIR コマンドターゲットを作成する場所。この指定は、サブディレクトリにも伝播する。
例: CMDTARGETDIR := $(ROOTDIR)/bin/$(BUILDCONFIG)
(省略時: ./$(BUILDCONFIG))
CMDDIR コマンドターゲットを作成する場所。 この指定は上位ディレクトリにおける CMDTARGETDIR の指定よりも優先されるが、 当該作業ディレクトリでのみ有効となる。
LIBTARGETDIR ライブラリターゲットを作成する場所。この指定は、サブディレクトリにも伝播する。
例: LIBTARGETDIR := $(ROOTDIR)/libs/$(BUILDCONFIG)
(省略時: ./$(BUILDCONFIG))
LIBDIR ライブラリターゲットを作成する場所。 この指定は上位ディレクトリにおける LIBTARGETDIR の指定よりも優先されるが、 当該作業ディレクトリでのみ有効となる。
OBJTARGETDIR オブジェクトファイルを作成する場所。この指定は、サブディレクトリにも伝播する。
例: LIBTARGETDIR := $(ROOTDIR)/objs/$(BUILDCONFIG)
(省略時: ./$(BUILDCONFIG))
OBJDIR オブジェクトファイルを作成する場所。 この指定は上位ディレクトリにおける OBJTARGETDIR の指定よりも優先されるが、 当該作業ディレクトリでのみ有効となる。
COMPILER 使用する C++ コンパイラのコマンド名を指定する。
(省略時: g++)
CCOMPILER 使用する C コンパイラのコマンド名を指定する。
(省略時: gcc)
RELEASE_FLAGS リリース用のコンパイル/リンクスイッチ。サブディレクトリにも伝播する。
(省略時: -O -DNDEBUG)
DEBUG_FLAGS デバッグ用のコンパイル/リンクスイッチ。サブディレクトリにも伝播する。
(省略時: -g -D_DEBUG -DDEBUG)
WARNING 警告レベルの制御スイッチ。サブディレクトリにも伝播する。
(省略時: -Wall)
CPPDEFINES プロジェクトで共通に指定するプリプロセッサ用定義。
LINKER 使用するリンカのコマンド名およびオプションを指定する。
DEBUG_LDFLAGS デバッグ用のリンクスイッチ。サブディレクトリにも伝播する。
(省略時: -g)
RELEASE_LDFLAGS リリース用のリンクスイッチ。サブディレクトリにも伝播する。
(省略時: -static)
NoStrip 定義すると、リリース用コマンドをビルドした後に、strip を行わない。 デフォルトでは リリース用コマンドをビルドした後は、strip が実行される。
ARCHIVER 使用するアーカイバのコマンド名およびオプションを指定する。
(省略時: ar rs)
CMDOBJS 当該作業ディレクトリ内では管理されていないが、 CMDTARGET を作成する際には一緒にリンクされるオブジェクト。 スペースで区切ることにより複数指定可。
LIBS 当該作業ディレクトリ内では管理されていないが、 CMDTARGET を作成する際には一緒にリンクされるライブラリ。 スペースで区切ることにより複数指定可。
LIBOBJS 当該作業ディレクトリ内では管理されていないが、 LIBTARGET を作成する際には一緒にアーカイブに取り込まれるオブジェクト。 スペースで区切ることにより複数指定可。
YaccLexClean 定義すると、yacc/lex で生成される C/C++ ソースも clean の対象になる。

これらの変数は mffw.conf で記述する以外に、環境変数として指定してもよい。

上記の変数の記述の際に、以下の Makefile.fw 内部変数を使用できる:

BUILDCONFIG 構成種別情報。 release または debug に設定される
mffw.conf の例

●添付の Makefile とターゲット

添付の Makefile は Makefile.fw のラッパーである。 Makefile.fw と このラッパー Makefile をプロジェクトのトップディレクトリに コピーし、各作業ディレクトリに mffw.conf を配置すれば、一応のビルドシス テムが出来上がる。

ラッパー Makefile には、ターゲットとして debug / release / clean / depend が記述されている。それぞれ、

make debug   ⇒ gmake -f Makefile.fw debug=1
make release ⇒ gmake -f Makefile.fw release=1
make clean   ⇒ gmake -f Makefile.fw clean
make depend  ⇒ gmake -f Makefile.fw depend

を実行する。

make debug

デバッグ用ビルドを実行する。 構成情報として、BUILDCONFIG 変数が 'debug' に設定される。 オブジェクトやライブラリ、コマンドは、デフォルトでは ./debug/ ディレクトリに作成されるが、 OBJTARGETDIR, LIBTARGETDIR, CMDTARGETDIR を設定することにより、格納場所を指定することができる。

make release

リリース用ビルドを実行する。 構成情報として、BUILDCONFIG 変数が 'release' に設定される。 オブジェクトやライブラリ、コマンドは、デフォルトでは ./release/ ディレクトリに作成されるが、 OBJTARGETDIR, LIBTARGETDIR, CMDTARGETDIR を設定することにより、格納場所を指定することができる。

make clean

オブジェクトファイル、ライブラリファイル、コマンドファイルおよび依存関係ファイル (depend.mf) を消去する。一からフルビルドを実行したいときに用いる。

make depend

make の ターゲットとして depend を指定すると、 ソースファイルとヘッダーファイルの依存関係を記述したファイル (depend.mf) を生成する。 ソースファイルやヘッダーファイルをプロジェクトに追加したときは、 必ず make depend を実行すべきである。

●サブディレクトリの再帰的なビルド

mffw.conf に、SUBDIRS または POSTDIRS を記述すると、 それらで指定された各サブディレクトリ dir に対して、

gmake -f Makefile.fw -C $(dir) $(BUILDCONFIG)=1 target

を実行する。 SUBDIRS で指定されたディレクトリはカレントの作業ディレクトリより前に処理され、 POSTDIRS で指定されたディレクトリはカレントの作業ディレクトリより後に処理される。

●カスタムビルド (customrules.mf)

標準的なビルドプロセスから外れた処理を実行したい場合は、customrules.mf というファイルにルールを記述する。

customrules.mf は、Makefile と Makefile.fw の両方に、それぞれの末尾でイン クルードされる。Makefile.fw では、'MakefileFramework' が定義されているの で、ifdef MakefileFramework を使用して、それぞれのルールを切り替えるとよ い。

強制的に実行させたいルールがある場合は、 そのルールのトリガーとなるターゲット(複数可)を CUSTOMTARGETS という変数に記述する。 メインのビルドプロセスに先立ち、 CUSTOMTARGETS に記述されたターゲットの処理が実行される。

例: EUCのソースを SJISのソースに変換する
CUSTOMTARGETS := foo_sjis.cpp

foo_sjis.cpp: foo_euc.cpp
	nkf -s $^ > $@
例2: テストを実行するターゲットを付加する
test:
ifdef MakefileFramework
	$(BUILDCONFIG)/testprog    # テストプログラムの実行
else
	gmake -f Makefile.fw test  # Makefil.fw の test を呼び出す
endif

●単体ビルド

添付の unitmake というシェルスクリプトを使うと、複数階層のディレクトリ ツリーから構成されているプロジェクトにおいて、あるサブディレクトリツリー の下だけをビルドすることができる。当該サブディレクトリに移動し、 unitmake を実行すればよい。

% cd <サブディレクトリ>
% unitmake

unitmake は、Makefile.fw が見つかるまで、コマンドを実行したディレクトリ から上にたどってゆき、Makefile.fw が見つかったところで、今度は逆順にデ ィレクトリツリーを降りてくる。その際、通過するディレクトリにある mffw.conf の設定が読み込まれる。

●典型的なプロジェクト構成

sample/
  └┬─ Makefile (添付 Makefile をコピーする)
    ├─ Makefile.fw (添付 Makefile.fw をコピーする)
    │
    ├─ mffw.conf
    │       SUBDIRS      = foo yacc main
    │       ROOTDIR     := $(shell pwd)
    │       OBJTARGETDIR = $(ROOTDIR)/objects/$(BUILDCONFIG)
    │       LIBTARGETDIR = $(ROOTDIR)/libs/$(BUILDCONFIG)
    │       
    ├─ foo/
    │    └┬─ mffw.conf
    │      │       LIBTARGET = foo.a
    │      │
    │      ├─ foo1.cpp
    │      └─ foo2.cpp
    │
    ├─ yacc/
    │    └┬─ mffw.conf
    │      │       YaccLexClean = 1
    │      │       YACCSRCS     = yacc.y
    │      │
    │      ├─ yacc.y
    │      ├─ yacc.tab.c (yacc によって生成される)
    │      ├─ yacc.tab.h (yacc によって生成される)
    │      │
    │      ├─ release/  (自動的に作成される)
    │      │    └── yacc.tab.o
    │      │
    │      └─ debug/    (自動的に作成される)
    │            └── yacc.tab.o
    │
    ├─ main/
    │    └┬─ mffw.conf
    │      │       CMDTARGET = main
    │      │       CMDSRCS   = main.cpp
    │      │       CMDOBJS   = $(OBJTARGETDIR)/yacc.tab.o
    │      │       LIBS      = $(LIBTARGETDIR)/foo.a
    │      │
    │      ├─ main.cpp
    │      │
    │      ├─ debug/    (自動的に作成される)
    │      │    └─ main
    │      │
    │      └─ release/  (自動的に作成される)
    │            └─ main
    │
    ├─ objects/  (自動的に作成される)
    │    └┬─ debug/    (自動的に作成される)
    │      │    └┬─ foo1.o
    │      │      ├─ foo2.o
    │      │      ├─ yacc.tab.o
    │      │      └─ main.o
    │      │
    │      └─ release/  (自動的に作成される)
    │            └┬─ foo1.o
    │              ├─ foo2.o
    │              ├─ yacc.tab.o
    │              └─ main.o
    │
    └─ libs/  (自動的に作成される)
          └┬─ debug/    (自動的に作成される)
            │    └─ foo.a
            │
            └─ release/  (自動的に作成される)
                  └─ foo.a