1、概述
make 是最古老的Make 是构建工具,不是编译工具。Make 是"指挥官",编译器(如 gcc、clang)才是"士兵"。Make 负责组织和调度编译任务,但具体的编译工作是由编译器(gcc / clang / MSVC)完成的。CMake 是更上层的构建系统生成器。它根据高级描述(CMakeLists.txt)生成构建脚本文件供构建工具使用。
2、make
make使用流程
/*make 当前目录下的makefile文件,生成默认构建所有*/
make all
/*make 引用指定makefile文件,生成默认构建所有*/
make all --file=Makefile.debug
/*make 引用指定makefile文件,只生成静态库*/
make static --file=Makefile.debug
2.1、make命令参数
基本控制参数
| 参数 | 短参数 | 功能描述 | 示例 | 适用场景 |
|---|---|---|---|---|
**--file=FILE** |
-f FILE |
指定使用的 Makefile 文件 | make -f MyMakefile make --file=Makefile.debug |
使用非标准名称的 Makefile |
**--directory=DIR** |
-C DIR |
在指定目录中执行 make | make -C src make -C build |
在子目录中构建项目 |
**--always-make** |
-B |
强制重新构建所有目标 | make -B make --always-make |
确保完全重新构建 |
**--assume-new=FILE** |
-W FILE |
假设目标文件已修改 | make -W main.c |
强制重建特定目标 |
**--assume-old=FILE** |
-o FILE |
假设目标文件是旧的 | make -o libcommon.a |
避免重新构建大型库 |
并行构建参数
| 参数 | 短参数 | 功能描述 | 示例 | 说明 |
|---|---|---|---|---|
**--jobs[=N]** |
-j [N] |
并行执行 N 个任务 | make -j8 make -j make --jobs=4 |
不指定 N 时尽可能并行 |
**--load-average[=N]** |
-l [N] |
限制系统平均负载 | make -l 4.0 make --load-average=2.5 |
防止系统过载 |
**--max-load[=N]** |
无 | 同 --load-average |
make --max-load=3.0 |
GNU make 4.0+ |
2.2、make目标
如make all中的 all是在 Makefile 中定义的目标。这些目标不是 make 的内置功能,而是 Makefile 作者定义的约定。通过 .PHONY声明伪目标可以避免与同名文件冲突。合理的命名约定使 Makefile 更易于使用和维护。标准的 make 目标约定提高了项目的可移植性和一致性,开发者看到熟悉的 make clean、make install等命令就知道如何操作。
常见标准目标及其作用
| 目标名 | 标准用途 | 是否通常有命令 | 是否伪目标 (.PHONY) | 示例 |
|---|---|---|---|---|
**all** |
默认构建所有 | 通常无,只有依赖 | 是 | all: app lib |
**clean** |
清理构建产物 | 是 | 是 | clean:; rm -f *.o app |
**install** |
安装到系统 | 是 | 是 | install: app; install app /usr/local/bin |
**uninstall** |
卸载安装的文件 | 是 | 是 | uninstall:; rm -f /usr/local/bin/app |
test 或 **check** |
运行测试 | 是 | 是 | test:; ./runtests.sh |
**dist** |
创建发布包 | 是 | 是 | dist:; tar -czf app-1.0.tar.gz src/ |
distclean 或 **realclean** |
彻底清理 | 是 | 是 | distclean: clean; rm -f config.h |
**help** |
显示帮助信息 | 是 | 是 | help:; @echo "Usage: make [all|clean|install]" |
2.3、makefile解析
# ============================================================================
# 工具链配置,makefile多工具链的设计
# ============================================================================
# 选择工具链: gcc, clang, msvc, arm-gcc
TOOLCHAIN ?= gcc
# 根据工具链设置变量
ifeq ($(TOOLCHAIN),gcc)
# GNU GCC 工具链
CC = gcc
CXX = g++
AR = ar
STRIP = strip
OBJCOPY = objcopy
OBJDUMP = objdump
SIZE = size
# 编译选项
ARFLAGS = rcs
CFLAGS = -Wall -Wextra -O2 -fPIC
CXXFLAGS = -Wall -Wextra -O2 -std=c++17
LDFLAGS = -lm -lpthread
else ifeq ($(TOOLCHAIN),clang)
# Clang/LLVM 工具链
CC = clang
CXX = clang++
AR = llvm-ar
STRIP = llvm-strip
OBJCOPY = llvm-objcopy
OBJDUMP = llvm-objdump
SIZE = llvm-size
# 编译选项
CFLAGS += -Wall -Wextra
LDFLAGS +=
else ifeq ($(TOOLCHAIN),msvc)
# Microsoft Visual C++
CC = cl
CXX = cl
AR = lib
STRIP = echo
OBJCOPY = echo
OBJDUMP = echo
SIZE = echo
# 编译选项
CFLAGS += /nologo /W3
LDFLAGS += /nologo
else ifeq ($(TOOLCHAIN),arm-gcc)
# ARM GCC 交叉编译
CROSS_COMPILE = arm-none-eabi-
CC = $(CROSS_COMPILE)gcc
CXX = $(CROSS_COMPILE)g++
AR = $(CROSS_COMPILE)ar
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
SIZE = $(CROSS_COMPILE)size
# ARM 特定选项
CFLAGS += -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard
LDFLAGS += -nostdlib -T link.ld
else
$(error 未知工具链: $(TOOLCHAIN))
endif
2.4、不同平台主要差异对比表格
| 修改项 | Linux/Unix-like | Windows (MinGW/MSYS2) | Windows (MSVC) | macOS |
|---|---|---|---|---|
| 编译器变量 | CC = gcc CXX = g++ |
CC = gcc CXX = g++ |
CC = cl CXX = cl |
CC = clang CXX = clang++ |
| 可执行文件扩展名 | 无 或 .out |
.exe |
.exe |
无 |
| 静态库扩展名 | .a(libxxx.a) |
.a |
.lib |
.a |
| 动态库扩展名 | .so(libxxx.so) |
.dll .dll.a(导入库) |
.dll .lib(导入库) |
.dylib |
| 编译选项 | -fPIC -shared -Wall -O2 |
-shared -Wall -O2 |
/O2 /MD /W3 /DLL |
-fPIC -dynamiclib -Wall -O2 |
| 链接选项 | -Ldir -llib -Wl,-rpath, |
-Ldir -llib -Wl,--out-implib, |
/LIBPATH:dir libname.lib /DLL |
-Ldir -llib -install_name @rpath/ |
| 归档工具 | AR = ar ARFLAGS = rcs |
AR = ar ARFLAGS = rcs |
AR = lib ARFLAGS = /OUT: |
AR = ar ARFLAGS = rcs |
| 删除命令 | RM = rm -f RMDIR = rm -rf |
RM = rm -f RMDIR = rm -rf |
RM = del /Q RMDIR = rmdir /S /Q |
RM = rm -f RMDIR = rm -rf |
| 复制命令 | CP = cp |
CP = cp |
CP = copy |
CP = cp |
| 创建目录 | MKDIR = mkdir -p |
MKDIR = mkdir -p |
MKDIR = mkdir |
MKDIR = mkdir -p |
| 文件路径分隔符 | / |
/(MinGW) ``(CMD) |
\` 或/` |
/ |
| 行结束符 | LF (\n) |
LF (\n) (MinGW) CRLF (\r\n) |
CRLF (\r\n) |
LF (\n) |
| 环境变量引用 | $(VAR)或 ${VAR} |
$(VAR)或 ${VAR}(MinGW) %VAR% |
%VAR% |
$(VAR)或 ${VAR} |
| 动态库搜索路径 | LD_LIBRARY_PATH -Wl,-rpath, |
PATH 当前目录 |
PATH 当前目录 |
DYLD_LIBRARY_PATH @rpath |
| 调试工具 | gdb, valgrind | gdb (MinGW) WinDbg | WinDbg, VS Debugger | lldb, Instruments |
| 导出符号 | 默认全部导出 可用 .map文件控制 |
默认不导出 需要 __declspec(dllexport) |
默认不导出 需要 __declspec(dllexport) |
默认全部导出 可用 -exported_symbols_list |
| 大小写敏感 | 是 | 否 (NTFS) | 否 (NTFS) | 是 (APFS/HFS+) |
通过合理使用宏,可以创建高度可移植的 Makefile,轻松在不同平台间切换。
对于现代 C/C++ 项目,推荐使用 CMake,因为它:
-
支持几乎所有平台和编译器
-
有丰富的模块和工具
-
易于集成到 IDE
-
社区支持强大
-
自动处理大多数平台差异
3、cmake
CMake 是更上层的构建系统生成器。它根据高级描述(CMakeLists.txt)生成 Makefile或 build.ninja构建脚本文件,然后由 Make 或 Ninja 来执行。CMake 本身不执行构建。
使用cmake生成ninja 构建脚本流程如下 :
# 1. 使用 CMake 生成 Ninja 构建文件
cmake -S . -B build -G Ninja
# 2. 使用 Ninja 进行构建(在 build 目录中)
cmake --build build
# 或者直接调用 ninja 命令
cd build && ninja
# 3. 常用 ninja 命令
ninja # 构建默认目标
ninja all # 构建所有目标
ninja target_name # 构建指定目标
ninja clean # 清理
ninja -t clean # 更彻底的清理
ninja -j 8 # 指定 8 个并行任务
ninja -v # 显示详细的命令行(用于调试)
构建:
① cmake --build .
│
▼
② 读取 CMakeCache.txt
│
├── CMAKE_GENERATOR = "Unix Makefiles" → 执行 make,make 读 Makefile
├── CMAKE_GENERATOR = "Ninja" → 执行 ninja,ninja 读 build.ninja
└── CMAKE_GENERATOR = "Visual Studio" → 执行 msbuild,msbuild 读 .sln
编译:
① cmake --build . 启动
│
▼
② 读取 CMakeCache.txt
→ 获取 CMAKE_GENERATOR(如 "Unix Makefiles")
→ 获取 CMAKE_MAKE_PROGRAM(如 /usr/bin/make)
│
▼
③ 启动对应的构建工具(如 make)
│
▼
④ make 读取 Makefile,开始编译
3.1、cmake命令参数
cmake --help 命令执行结果的最后有输出当前cmake默认的生成器,以及支持的所有生成器。

基本配置参数
| 参数 | 短参数 | 功能描述 | 示例 | 说明 |
|---|---|---|---|---|
**-S <path>** |
无 | 指定源代码目录 | cmake -S . cmake -S src |
CMake 3.13+ 推荐格式 |
**-B <path>** |
无 | 指定构建目录 | cmake -B build cmake -B out |
CMake 3.13+ 推荐格式 |
**-G <generator>** |
无 | 指定生成器 | cmake -G "Unix Makefiles" cmake -G Ninja cmake -G "Visual Studio 16 2019" |
选择构建系统生成器 |
**-D <var>[:<type>]=<value>** |
无 | 定义或设置缓存变量 | -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/usr/local |
可选的类型:BOOL, FILEPATH, PATH, STRING, INTERNAL |
**--toolchain <file>** |
无 | --toolchain arm-toolchain.cmake |
指定工具链文件 | 支持通配符 |
**-DCMAKE_TOOLCHAIN_FILE=<file>** |
无 | -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake |
指定工具链文件 | 在配置前执行的脚本 |
**--build <dir>** |
无 | 构建已配置的项目 | cmake --build build |
|
**--parallel [<jobs>]** |
-j [<jobs>] |
并行构建作业数 | --parallel 8 -j 4 |
/*step 1
cmake构建, 启动参数缺失则从当前目录按默认值执行,构建文件也是生成到当前目录。
生成的构建文件放在当前目录./build 文件夹下
工具链配置文件为toolchainfile.cmake,相当于makefile中的CC,AR,编译选项配置。
cmake文件解析时USE_LOCAL_CJSON定义为ON
*/
cmake -B build -DCMAKE_TOOLCHAIN_FILE="/aarch64-buildroot-linux-gnu_sdk-buildroot/share/buildroot/toolchainfile.cmake" -DUSE_LOCAL_CJSON=ON
/*step 2
cmake编译, cmake --build <构建目录>
引用./build 的构建文件,编译生成的文件也是在./build目录下
*/
cmake --build build
正确理解:-S 指定源代码目录
cmake -S . -B build # 从当前目录查找 CMakeLists.txt
cmake -S src -B build # 从 src 目录查找 CMakeLists.txt
cmake -S ../project -B build # 从上级的 project 目录查找
cmake --build #如果的使用makefile生成器此操作会自动调用make all命令。
3.2、.cmake文件解析
cmak构建时工具会自动找CMakeLists.txt(也是.cmake文件,附合cmake语法)。其它非自动执行的.cmake文件一般是被CMakeLists.txt引用。或者手动引用。