月別アーカイブ: 2010年8月

fastmake

以前は VisualStudio の nmake を使っていました。
最近は fastmake を使っています。

fastmake

●特徴

・高速動作させるための様々な記述が可能。
・豊富な組み込みコマンド。
・Windows native で exe ファイル一つのみ。
・基本的に gmake 互換。
・-j の並列化可能。ただし .PARALLEL 宣言したルールのみ
・コマンド行は TAB じゃなくても良い。

●高速な理由

一番効果的だと感じたのがサブシェル呼出を徹底的に廃したこと。
例えば普通の make だとコマンド実行でシェルを経由するので余計に時間がかかります。
echo や del などのよく使う命令もシェルコマンドなのでシェルが走っています。

# nmake
echo_abc:
    @echo A
    @echo B
    @echo C

この場合 shell が 3回起動するので非常に低速です。
(UNIX shell だと (~;~) 等で呼び出しをまとめたりできる。)

fastmake は echo, rm, mkdir, cd 等よく使う機能が組み込まれており、
サブシェル呼出を必要としません。
コンパイラなどのコマンド実行もシェルを経由せずに直接 exec 可能です。

# fastmake
echo_abc:
    $(echo A)
    $(echo B)
    $(echo C)

%.obj .NOSHELL .PARALLEL : %.cpp
    $(CC) $(CFLAGS) -c -o $@ $<

$(echo~) は組み込み関数です。リダイレクトの代わりに $(echo ~,outpufile)
で直接出力ファイル名を指定することも可能です。
.NOSHELL 指定はシェルを経由せずに直接コマンドを exec しています。

Makefile 中さらに別の Makefile を呼び出すことがよくあります。
やはり通常は make の呼出のためにシェルを経由します。

# nmake
android:
    $(MAKE) -f Android.mak all

fastmake は自分自身を呼び出す組み込み関数があります。

# fastmake
android .PHONY :
    $(make -f Android.mak all)

この場合同一プロセス内で実行可能で、シェル呼び出しも外部コマンドの呼出も
スキップします。プロセス起動が無いため高速です。

フォルダまるごと削除も簡単に。

# nmake で rm -fr $(OBJDIR) 相当
OBJDIR = obj
clean:
    if exist $(OBJDIR) rmdir /Q /S $(OBJDIR)

# fastmake
OBJDIR = obj
clean:
    $(rm $(OBJDIR))

●便利な機能など

ドキュメントにも書いてありますが .COMBINE 指定があります。

%.obj: %.cpp
    cl $@ $<

等のパターンマッチは、生成するファイルごとに個別にコマンドを実行します。

cl a.cpp
cl b.cpp
cl c.cpp
~

.COMBINE 指定すると 1回のコマンド呼び出しにまとめて実行されます。

%.obj .COMBINE : %.cpp
    cl $@ $<

cl a.cpp b.cpp c.cpp ~

フォルダごとに make -f Win32.mak clean を実行する場合次のように書けます。

# fastmake
WIN32_DIRS = math file d3d11 gl3 gles2 network script
clean_win32 .PHONY :
    $(foreach dir,$(WIN32_DIRS),$(make -C $(dir) -f Win32.mak clean))

ドキュメントには下記の方法も紹介されていますが単純に foreach した方が速いようです。

# fastmake
WIN32_DIRS = math file d3d11 gl3 gles2 network script
clean_win32 .PHONY :  $(WIN32_DIRS:+.win32clean) ;
%.win32clean .PHONY :
    $(make -C $* -f Win32.mak clean)

内部コマンドは副作用として実行される関数扱いなので、マクロ置換できる場所なら
どこにでも書けます。下記の make もマクロ展開時に実行されます。

DIRS = a b c
A:=$(foreach dir,$(DIRS),$(make -f sub.mak))

結果が不要な内部コマンドは空文字になるようです。

$(echo $(echo B)A$(echo C))
 ↓
B
C
A

●気がついたことなど

環境変数はデフォルトでは取り込まれません。
個別に .IMPORT 指定が必要です。

.IMPORT .IGNORE : PATH DXSDK_DIR

変数 include への代入が出来ません。

include = $(DXSDK_DIR)\\include

コンパイラへ渡す環境変数として "include" が用いられることがありますが、
上のように書くと include 命令と勘違いしてしまうようです。
export 宣言することで回避できます。

export include=$(DXSDK_DIR)\\include

Makefile 中に C言語のブロックコメント /*~*/ が使えるように拡張されています。
最近これが問題になることが分かってきました。

COPYFILE_SRC = system/*.dds

こんな感じの記述があると、system"/*".dds の "/*" 以降がコメント扱いになり
無視されてしまいます。エラーが出ないので厄介です。

.FOREACH が意外に汎用性がありません。
パターン定義以外ではあまりうまく機能しないようです。

$(make ~) の make 呼び出しでは -j 指定を変更できません。
下の例の (1) では job 数が変わりません。
job 数を変更する場合は (2) のように自分自身を実行しなおす必要があります。

# (1)
android .PHONY :
    $(make -j 10 -f Android.mak all)

# (2)
FMAKE = fastmake.exe
android .PHONY .NOSHELL :
    $(FMAKE) -j 10 -f Android.mak all

nmake だとファイルの存在確認が簡単にできますが fastmake ではすっきり
書けません。要研究。

# nmake
MAYAPATH = C:\Program Files\Autodesk\Maya2011
!if EXIST( "$(MAYAPATH)" )
~
!endif
# fastmake
PROGFILES = C:\\Program Files
PROGFILES_AUTO = $(PROGFILES)\\Autodesk
.IF $(ls $(PROGFILES),d,Autodesk) != ""
  .IF $(ls $(PROGFILES_AUTO),d,Maya2011) != ""
~
  .END
.END