Makefile による別ディレクトリーへのオブジェクト、従属規則のセーブ

今まで、従属規則の生成には、X11 のコマンドであるところの「makedepend」を長らく使って来た、しかし、makedepend コマンドが無い場合(X11 関係アーカイブをインストールする必要がある)
や、コンパイラがインクルードファイルを検索する挙動とは多少違う場合もあったりしていた。
そこで、「gcc」の「-MM」オプションを使ってインクルードパスを収集する方法に切り替えた。
※このコンパイラーオプションは、コンパイラー自らのパスでインクルードファイルのフルパスを出力する、この時、「#if、#endif」などの制御文なども評価する。

通常、コンパイラーは、ソースコードと同じ場所にオブジェクトを生成する、ソースコードを共有して、複数のアプリケーションを作成すると、それでは、不具合が起こる場合もあり(コンパイラーオプションや、デバッグ、リリースビルドなど)、アプリケーション毎に、生成されたオブジェクトコードを管理したい。
そこで、生成オブジェクトを特定のディレクトリーに集中させるようにした。

また、複数のアプリケーションで Makefile の修正を最小限に出来るように、多少の工夫をしてみた。

Makefile の構文を学習しなおしたり、google 先生に教えてもらった make 関係のリンクを観たりで、自分の思ったような構成に出来たので紹介する。

ディレクトリーは以下のような構成にしている。

project:
    common:      ---> アプリケーション共通のソースコードを格納するディレクトリー
        ...:     ---> ソースモジュール毎のディレクトリー
            ...  ---> ソース郡
        ...:
            ...
        ...:
            ...
    xxxx:           ---> xxxx アプリケーションディレクトリー
        xxxx.cpp    ---> xxxx アプリケーション固有のソースコード
        Makefile    ---> xxxx アプリケーションの Makefile
    yyyy:           ---> yyyy アプリケーションディレクトリー
        yyyy.cpp
        Makefile

・各アプリケーションで共通するソースコード郡を「common」ディレクトリーに配置している。
・アプリケーションは、専用ディレクトリーを設けて、そこで、生成された二次ファイルを格納する。
※通常、「release」、「debug」のどちらか

一番のキモは、-MM コマンドの仕様で痛い部分を回避する為の小細工で、以下のリンクを参考にした。
※スイマセン、殆ど同じような物です、VPATH の指定が追加されたくらいです。

Makefile 別ディレクトリに中間ファイル & 自動依存関係設定

この問題は、オブジェクトファイルのパスがフルパスにならない点で、これを改善する為、sed による整形を使って、回避している。
リンクの sed スクリプトでは、「.o」のマッチングが不十分で、特定のファイル名の組み合わせなどで整形を失敗する、そこで、適切に修正した。
「\.o:」「.」を「\.」にする事で、正確に「.」(ピリオド)にマッチするようになる、また、一応、セパレーター「:」を追加して、厳密にマッチするようにしてある。
※詳しくは sed の仕様、-MM の説明を参照の事

「VPATH」を指定すると、make が、ファイルを検索するルートを指定出来るので、関係ソースコードを羅列する部分がスッキリする。

・以下の例は、アプリケーション「player」の Makefile です。
・「TARGET」アプリケーション名(Windows では .exe を追加する)
・「ICON_RC」アプリケーションアイコン指定がある場合、リソースの記述を行う
icon.rcの例

EXE_ICON ICON res/player.ico

・「BUILD」ビルドディレクトリー(debug、又は、release)
※従属規則 *.d 、とオブジェクトファイル *.o が格納される。
・「VPATH」共有ソースコードのルートディレクトリー
・「CSOURCES」C 言語ソースコードのリスト
・「PSOURCES」C++ 言語ソースコードのリスト
・「STDLIBS」標準的ライブラリーのリスト
・「OPTLIBS」オプションライブラリーのリスト
・「INC_SYS」システム系のインクルードパス
・「INC_LIB」ローカルライブラリーのインクルードパス
※従属規則に含まれない
・「PINC_APP」C++ 言語用、インクルードパス
・「CINC_APP」C 言語用、インクルードパス
※従属規則に含まれる
・「LIBDIR」ローカルライブラリーオブジェクトのリンクパス

# Makefile
TARGET    = player.exe

ICON_RC   = icon.rc

# 'debug' or 'release'
BUILD     = release

VPATH     = ../common

CSOURCES  = minizip/ioapi.c \
            minizip/unzip.c

PSOURCES  = main.cpp \
            player.cpp \
            core/glcore.cpp \
.....

STDLIBS    =
OPTLIBS    =    glfw3 glew32 opengl32 glu32 gdi32 imm32 \
                pthread \
                openal winmm dsound \
                png16 jpeg_x86 openjp2 \
                freetype \
                id3tag \
                z \
                mad \
                faad mp4ff

INC_SYS     =    /usr/local/boost_1_54_0

INC_LIB	    =    /usr/local/include \
                 /usr/local/include/libpng16 \
                 /usr/local/include/libjpeg_x86 \
                 /usr/local/include/openjpeg-2.0 \
                 /usr/local/include/freetype2

PINC_APP    =   . ../common
CINC_APP    =   . ../common
LIBDIR      =   /usr/local/lib

INC_S   =    $(addprefix -I, $(INC_SYS))
INC_L   =    $(addprefix -I, $(INC_LIB))
INC_P   =    $(addprefix -I, $(PINC_APP))
INC_C   =    $(addprefix -I, $(CINC_APP))
CINCS   =    $(INC_S) $(INC_L) $(INC_C)
PINCS   =    $(INC_S) $(INC_L) $(INC_P)
LIBS    =    $(addprefix -L, $(LIBDIR))
LIBN    =    $(addprefix -l, $(STDLIBS))
LIBN   +=    $(addprefix -l, $(OPTLIBS))

#
# Compiler, Linker Options, Resource_compiler
#
CP      =    g++
CC      =    gcc
LK      =    g++
RC      =    windres

POPT    =   -O2 -std=gnu++11
COPT    =   -O2
LOPT    =

PFLAGS  =   -DWIN32 -DHAVE_STDINT_H
CFLAGS  =   -DWIN32

ifeq ($(BUILD),debug)
    POPT += -g
    COPT += -g
    PFLAGS += -DDEBUG
    CFLAGS += -DDEBUG
endif

ifeq ($(BUILD),release)
    PFLAGS += -DNDEBUG
    CFLAGS += -DNDEBUG
endif

# 	-static-libgcc -static-libstdc++
LFLAGS	=

# -Wuninitialized -Wunused -Werror -Wshadow
CCWARN	= -Wimplicit -Wreturn-type -Wswitch \
          -Wformat
CPPWARN	= -Wall

OBJECTS	= $(addprefix $(BUILD)/,$(patsubst %.cpp,%.o,$(PSOURCES))) \
          $(addprefix $(BUILD)/,$(patsubst %.c,%.o,$(CSOURCES)))
DEPENDS = $(patsubst %.o,%.d, $(OBJECTS))

ifdef ICON_RC
    ICON_OBJ = $(addprefix $(BUILD)/,$(patsubst %.rc,%.o,$(ICON_RC)))
endif

.PHONY: all clean
.SUFFIXES :
.SUFFIXES : .rc .hpp .h .c .cpp .o

all: $(BUILD) $(TARGET)

$(TARGET): $(OBJECTS) $(ICON_OBJ) Makefile
    $(LK) $(LFLAGS) $(LIBS) $(OBJECTS) $(ICON_OBJ) $(LIBN) -o $(TARGET)

$(BUILD)/%.o : %.c
    mkdir -p $(dir $@); \
    $(CC) -c $(COPT) $(CFLAGS) $(CINCS) $(CCWARN) -o $@

lt;

$(BUILD)/%.o : %.cpp
mkdir -p $(dir $@); \
$(CP) -c $(POPT) $(PFLAGS) $(PINCS) $(CPWARN) -o $@


lt;

$(ICON_OBJ): $(ICON_RC)
$(RC) -i


lt; -o $@

$(BUILD)/%.d : %.c
mkdir -p $(dir $@); \
$(CC) -MM -DDEPEND_ESCAPE $(COPT) $(CFLAGS) $(INC_C)


lt; \
| sed 's/$(notdir $*)\.o:/$(subst /,\/,$(patsubst %.d,%.o,$@) $@):/' > $@ ; \
[ -s $@ ] || rm -f $@

$(BUILD)/%.d : %.cpp
mkdir -p $(dir $@); \
$(CP) -MM -DDEPEND_ESCAPE $(POPT) $(PFLAGS) $(INC_P)


lt; \
| sed 's/$(notdir $*)\.o:/$(subst /,\/,$(patsubst %.d,%.o,$@) $@):/' > $@ ; \
[ -s $@ ] || rm -f $@

run:
./$(TARGET)

clean:
rm -rf $(BUILD) $(TARGET)

clean_depend:
rm -f $(DEPENDS)

dllname:
objdump -p $(TARGET) | grep "DLL Name"

tarball:
tar cfvz $(subst .exe,,$(TARGET))_$(shell date +%Y%m%d%H).tgz \
*.[hc]pp Makefile ../common/*/*.[hc]pp ../common/*/*.[hc]

bin_zip:
$(LK) $(LFLAGS) $(LIBS) $(OBJECTS) icon.o $(LIBN) -mwindows -o $(TARGET)
rm -f $(subst .exe,,$(TARGET))_$(shell date +%Y%m%d%H)_bin.zip
zip $(subst .exe,,$(TARGET))_$(shell date +%Y%m%d%H)_bin.zip *.exe *.dll res/*.*

-include $(DEPENDS)

完全な Makefile は Github にプシュした!