メモの日々


2009年06月21日(日) [長年日記]

[c++][c][dev] C++用Makefileのサンプル (2)

ちょっと前にMakefileをメモしたけど、文字コードを変換してからビルドしたいケースもあるので、それに対応したMakefileも作ってみた。

  • 文字コードをCP932からUTF-8へ、改行コードをCRLFからLFへ変換している。変換にはNKFを使用。
  • 何もしないとコンパイルエラー時に変換後のソースファイルパスが表示されるが、それだとVimからmakeを実行したときに変換後のソースにジャンプしてしまい不便。なので、sedを使ってソースファイルパスを変換前のものに置換している。更に念のため変換後のソースから書込権限を除いておく。
  • sedで上の置換をする際にパイプを使っているが、そうするとコンパイルエラーになっても終了コードがエラーにならなくなり困る。そこでBASHのpipefailオプションを使って終了コードがエラーになるようにした。
# GNU make用です
build_base_dir = build
program = $(build_base_dir)/sample

# ここにビルド対象とするディレクトリを全て書く
src_dirs = src
src_dirs += src/aaa
src_dirs += src/aaa/ccc
src_dirs += src/bbb

SHELL=/bin/bash
CPPFLAGS = -I $(build_base_dir)/src
CXXFLAGS = -Wall -g

headers = $(wildcard $(addsuffix /*.h, $(src_dirs)))
sources = $(wildcard $(addsuffix /*.cpp, $(src_dirs)))
headers_utf8 = $(headers:%.h=$(build_base_dir)/%.h)
sources_utf8 = $(sources:%.cpp=$(build_base_dir)/%.cpp)
objects = $(sources_utf8:%.cpp=%.o)
depends = $(addsuffix .d, $(objects))
build_dirs = $(addprefix $(build_base_dir)/, $(src_dirs))

.PHONY: all
all: $(build_dirs) $(headers_utf8) $(sources_utf8) $(program)

.PHONY: clean
clean:
	rm -rf $(build_base_dir)

$(program): $(objects)
	$(CXX) $(LDFLAGS) -o $@ $^

%.o: %.cpp
	set -o pipefail; $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< 2>&1 \
	    | sed --unbuffered 's|$(build_base_dir)/||'
	set -o pipefail; $(CXX) $(CPPFLAGS) -MM $< \
	    | sed 's|^\([^\.]*\)\.o|$(@D)/\1\.o|' > $@.d

$(build_base_dir)/%.cpp: %.cpp
	rm -f $@
	nkf --ic=CP932 --oc=UTF-8 -Lu -d $< > $@
	chmod a-w $@

$(build_base_dir)/%.h: %.h
	rm -f $@
	nkf --ic=CP932 --oc=UTF-8 -Lu -d $< > $@
	chmod a-w $@

$(build_dirs):
	mkdir -p $@

-include $(depends)

先日の例と同じ構成のソースファイルをビルドすると、

.
|-- Makefile
|-- build
|   |-- sample
|   `-- src
|       |-- aaa
|       |   |-- a.cpp
|       |   |-- a.h
|       |   |-- a.o
|       |   |-- a.o.d
|       |   `-- ccc
|       |       |-- c.cpp
|       |       |-- c.h
|       |       |-- c.o
|       |       `-- c.o.d
|       |-- bbb
|       |   |-- b.cpp
|       |   |-- b.h
|       |   |-- b.o
|       |   `-- b.o.d
|       |-- main.cpp
|       |-- main.o
|       `-- main.o.d
`-- src
    |-- aaa
    |   |-- a.cpp
    |   |-- a.h
    |   `-- ccc
    |       |-- c.cpp
    |       `-- c.h
    |-- bbb
    |   |-- b.cpp
    |   `-- b.h
    `-- main.cpp

のようになる。buildの下にあるのが変換後のソースファイル。

手元の環境で実行すると次のように出力された。

mkdir -p build/src
mkdir -p build/src/aaa
mkdir -p build/src/aaa/ccc
mkdir -p build/src/bbb
rm -f build/src/aaa/a.h
nkf --ic=CP932 --oc=UTF-8 -Lu -d src/aaa/a.h > build/src/aaa/a.h
chmod a-w build/src/aaa/a.h
rm -f build/src/aaa/ccc/c.h
nkf --ic=CP932 --oc=UTF-8 -Lu -d src/aaa/ccc/c.h > build/src/aaa/ccc/c.h
chmod a-w build/src/aaa/ccc/c.h
rm -f build/src/bbb/b.h
nkf --ic=CP932 --oc=UTF-8 -Lu -d src/bbb/b.h > build/src/bbb/b.h
chmod a-w build/src/bbb/b.h
rm -f build/src/main.cpp
nkf --ic=CP932 --oc=UTF-8 -Lu -d src/main.cpp > build/src/main.cpp
chmod a-w build/src/main.cpp
rm -f build/src/aaa/a.cpp
nkf --ic=CP932 --oc=UTF-8 -Lu -d src/aaa/a.cpp > build/src/aaa/a.cpp
chmod a-w build/src/aaa/a.cpp
rm -f build/src/aaa/ccc/c.cpp
nkf --ic=CP932 --oc=UTF-8 -Lu -d src/aaa/ccc/c.cpp > build/src/aaa/ccc/c.cpp
chmod a-w build/src/aaa/ccc/c.cpp
rm -f build/src/bbb/b.cpp
nkf --ic=CP932 --oc=UTF-8 -Lu -d src/bbb/b.cpp > build/src/bbb/b.cpp
chmod a-w build/src/bbb/b.cpp
set -o pipefail; g++ -I build/src -Wall -g -c -o build/src/main.o build/src/main.cpp 2>&1 \
            | sed --unbuffered 's|build/||'
set -o pipefail; g++ -I build/src -MM build/src/main.cpp \
            | sed 's|^\([^\.]*\)\.o|build/src/\1\.o|' > build/src/main.o.d
set -o pipefail; g++ -I build/src -Wall -g -c -o build/src/aaa/a.o build/src/aaa/a.cpp 2>&1 \
            | sed --unbuffered 's|build/||'
set -o pipefail; g++ -I build/src -MM build/src/aaa/a.cpp \
            | sed 's|^\([^\.]*\)\.o|build/src/aaa/\1\.o|' > build/src/aaa/a.o.d
set -o pipefail; g++ -I build/src -Wall -g -c -o build/src/aaa/ccc/c.o build/src/aaa/ccc/c.cpp 2>&1 \
            | sed --unbuffered 's|build/||'
set -o pipefail; g++ -I build/src -MM build/src/aaa/ccc/c.cpp \
            | sed 's|^\([^\.]*\)\.o|build/src/aaa/ccc/\1\.o|' > build/src/aaa/ccc/c.o.d
set -o pipefail; g++ -I build/src -Wall -g -c -o build/src/bbb/b.o build/src/bbb/b.cpp 2>&1 \
            | sed --unbuffered 's|build/||'
set -o pipefail; g++ -I build/src -MM build/src/bbb/b.cpp \
            | sed 's|^\([^\.]*\)\.o|build/src/bbb/\1\.o|' > build/src/bbb/b.o.d
g++  -o build/sample build/src/main.o build/src/aaa/a.o build/src/aaa/ccc/c.o build/src/bbb/b.o

やること

  • アイスを食べる
  • 請求書