メモの日々


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
(以下略)