2024年05月02日(木) [長年日記]
■ [c++][c][dev] C++用Makefileのサンプル (4)
Makefileを書く機会があった。Makefileは楽しい。
以前に書いたMakefileのサンプルを書き直したくなったので書く。
ソースファイルとMakefileの構成を以前と少し変えて次の通りとする。
. ├── Makefile └── src ├── app1 │ ├── aaa │ │ ├── a.cpp │ │ ├── a.h │ │ └── ccc │ │ ├── c.cpp │ │ └── c.h │ ├── bbb │ │ ├── b.cpp │ │ └── b.h │ └── main.cpp └── app2 ├── aaa │ ├── a.cpp │ ├── a.h │ └── ccc │ ├── c.cpp │ └── c.h ├── bbb │ ├── b.cpp │ └── b.h └── main.cpp
1つのMakefileでapp1とapp2という2つの実行ファイルを作れるようにしたい。
作成したMakefileは次の通り。
# GNU make用です SHELL := /bin/bash Dirs = $(shell find $(1) -type d -not -path '*/.*') Files = $(wildcard $(addsuffix /*$(strip $(2)), $(1))) SrcFiles = $(call Files, $(call Dirs, $(1)), .cpp) build_dir := build CPPFLAGS := CXXFLAGS := -Wall -O3 -ggdb3 LDFLAGS := LDLIBS := # app1 app1_bin := $(build_dir)/app1 app1_src_dir := src/app1 app1_src_files := $(call SrcFiles, $(app1_src_dir)) app1_obj_files := $(app1_src_files:%.cpp=$(build_dir)/%.o) dep_files += $(app1_obj_files:%.o=%.d) app1: CPPFLAGS += -I$(app1_src_dir) # app2 app2_bin := $(build_dir)/app2 app2_src_dir := src/app2 app2_src_files := $(call SrcFiles, $(app2_src_dir)) app2_obj_files := $(app2_src_files:%.cpp=$(build_dir)/%.o) dep_files += $(app2_obj_files:%.o=%.d) app2: CPPFLAGS += -I$(app2_src_dir) # app1とapp2に共通のソースファイルがある場合は、dep_filesから重複を除去しておきたい。 # 次の処理を生かせばできる。 #dep_files := $(sort $(dep_files)) build_subdirs := $(sort $(dir $(dep_files))) .PHONY: all all: app1 app2 .PHONY: app1 app1: $(app1_bin) .PHONY: app2 app2: $(app2_bin) .PHONY: clean clean: $(RM) -r $(build_dir) $(app1_bin): $(app1_obj_files) $(CXX) $^ -o $@ $(LDFLAGS) $(LDLIBS) $(app2_bin): $(app2_obj_files) $(CXX) $^ -o $@ $(LDFLAGS) $(LDLIBS) $(build_dir)/%.o: %.cpp | $(build_subdirs) $(CXX) $< -c -o $@ $(CPPFLAGS) $(CXXFLAGS) -MMD -MP $(build_subdirs): mkdir -p $@ -include $(dep_files)
- ソースファイル一覧は、SrcFilesという関数を作ってそれで得られるようにした。
- 「app1: CPPFLAGS += -I$(app1_src_dir)」の行はtarget-specific variablesを使用していて、同一名の変数へターゲット毎に別々の値を設定している。
- 「\$(build_dir)/%.o: %.cpp | $(build_subdirs)」の行はorder-only prerequisitesを使用していて、これにより \$(build_subdirs) 内の各ディレクトリは .o ファイルより前に作られるが \$(build_subdirs) 内のディレクトリのタイムスタンプが新しくても .o ファイルは作り直されないようになる。
- GCCの -MMD -MP といったオプションについては以前にメモした。
実行結果は次の通り。
$ make --version GNU Make 4.3 Built for x86_64-redhat-linux-gnu Copyright (C) 1988-2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. $ make -j mkdir -p build/src/app1/ mkdir -p build/src/app1/aaa/ mkdir -p build/src/app1/aaa/ccc/ mkdir -p build/src/app1/bbb/ mkdir -p build/src/app2/ mkdir -p build/src/app2/aaa/ mkdir -p build/src/app2/aaa/ccc/ mkdir -p build/src/app2/bbb/ g++ src/app1/main.cpp -c -o build/src/app1/main.o -Isrc/app1 -Wall -O3 -ggdb3 -MMD -MP g++ src/app1/aaa/a.cpp -c -o build/src/app1/aaa/a.o -Isrc/app1 -Wall -O3 -ggdb3 -MMD -MP g++ src/app1/aaa/ccc/c.cpp -c -o build/src/app1/aaa/ccc/c.o -Isrc/app1 -Wall -O3 -ggdb3 -MMD -MP g++ src/app1/bbb/b.cpp -c -o build/src/app1/bbb/b.o -Isrc/app1 -Wall -O3 -ggdb3 -MMD -MP g++ src/app2/main.cpp -c -o build/src/app2/main.o -Isrc/app2 -Wall -O3 -ggdb3 -MMD -MP g++ src/app2/bbb/b.cpp -c -o build/src/app2/bbb/b.o -Isrc/app2 -Wall -O3 -ggdb3 -MMD -MP g++ src/app2/aaa/a.cpp -c -o build/src/app2/aaa/a.o -Isrc/app2 -Wall -O3 -ggdb3 -MMD -MP g++ src/app2/aaa/ccc/c.cpp -c -o build/src/app2/aaa/ccc/c.o -Isrc/app2 -Wall -O3 -ggdb3 -MMD -MP g++ build/src/app1/main.o build/src/app1/aaa/a.o build/src/app1/aaa/ccc/c.o build/src/app1/bbb/b.o -o build/app1 g++ build/src/app2/main.o build/src/app2/bbb/b.o build/src/app2/aaa/a.o build/src/app2/aaa/ccc/c.o -o build/app2 $ tree . . ├── Makefile ├── build │ ├── app1 │ ├── app2 │ └── src │ ├── app1 │ │ ├── aaa │ │ │ ├── a.d │ │ │ ├── a.o │ │ │ └── ccc │ │ │ ├── c.d │ │ │ └── c.o │ │ ├── bbb │ │ │ ├── b.d │ │ │ └── b.o │ │ ├── main.d │ │ └── main.o │ └── app2 │ ├── aaa │ │ ├── a.d │ │ ├── a.o │ │ └── ccc │ │ ├── c.d │ │ └── c.o │ ├── bbb │ │ ├── b.d │ │ └── b.o │ ├── main.d │ └── main.o └── src (以下略)