构建系统:Makefile 与 CMake

一、Makefile 基础

1. 最小 Makefile

hello: hello.c
    gcc -o hello hello.c

注意:命令前必须是 TAB,不能用空格。

2. 多文件 Makefile

CC      = gcc
CFLAGS  = -O2 -Wall -g
LDFLAGS =

SRCS    = main.c foo.c bar.c
OBJS    = $(SRCS:.c=.o)

TARGET  = app

.PHONY: all clean install
all: $(TARGET)

$(TARGET): $(OBJS)
    $(CC) $(LDFLAGS) -o $@ $^

%.o: %.c
    $(CC) $(CFLAGS) -c -o $@ $<

clean:
    rm -f $(OBJS) $(TARGET)

install: $(TARGET)
    install -m 0755 $(TARGET) /usr/local/bin

3. 自动变量

$@  目标
$<  第一个依赖
$^  所有依赖
$*  通配符匹配的部分
$(@D) 目标目录
$(@F) 目标文件名

4. 隐含规则

Makefile 内置:
- %.o: %.c$(CC) $(CFLAGS) -c -o $@ $<
- %.c: %.y → bison
- %.c: %.l → flex

5. 常用函数

$(wildcard *.c)              # 文件名列表
$(patsubst %.c, %.o, $(SRCS))
$(subst old, new, text)
$(addprefix src/, foo.c)
$(shell uname -r)
$(error stop)
$(warning caution)

6. 条件判断

ifeq ($(DEBUG),1)
    CFLAGS += -O0 -g3
else
    CFLAGS += -O2
endif

ifdef ARCH
    CC = $(ARCH)-linux-gnu-gcc
endif

7. 子目录

SUBDIRS = lib app

all:
    @for d in $(SUBDIRS); do $(MAKE) -C $$d || exit 1; done

二、CMake

1. 最小 CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(app C)

add_executable(app main.c)
target_link_libraries(app m)

2. 常用命令

# 变量
set(SOURCES main.c foo.c bar.c)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")

# 可执行
add_executable(app ${SOURCES})

# 静态库
add_library(mylib STATIC src1.c src2.c)
target_link_libraries(app mylib)

# 动态库
add_library(mylib SHARED src1.c)
set_target_properties(mylib PROPERTIES VERSION 1.0 SOVERSION 1)

# 子目录
add_subdirectory(lib)
add_subdirectory(app)

# 安装
install(TARGETS app RUNTIME DESTINATION bin)
install(FILES config.h DESTINATION include)

# 找依赖
find_package(PkgConfig REQUIRED)
pkg_check_modules(GLIB REQUIRED glib-2.0)
target_include_directories(app PRIVATE ${GLIB_INCLUDE_DIRS})
target_link_libraries(app PRIVATE ${GLIB_LIBRARIES})

# 找头文件和库
find_path(MY_INCLUDE_DIR foo.h)
find_library(MY_LIB foo)

3. 选项

option(BUILD_TESTS "Build tests" ON)
option(USE_OPENSSL "Use OpenSSL" OFF)
if(BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif()

4. 命令行

cmake -B build -DCMAKE_BUILD_TYPE=Release \
               -DCMAKE_INSTALL_PREFIX=/usr \
               -DBUILD_TESTS=ON
cmake --build build -j$(nproc)
ctest --test-dir build --output-on-failure
cmake --install build --prefix /tmp/install

三、其他构建系统(了解)

系统 场景
autotools (autoconf + automake + libtool) 老牌 C 项目(Linux 内核外许多)
meson + ninja GNOME、新项目(如 systemd、QEMU)
Bazel / Pants / Buck2 Google 系、大规模
xmake 中文社区,跨平台
scons Python 风格

四、内核 / U-Boot 的 Makefile 风格