【OS zephyr】make与cmake

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,因为它:

  1. 支持几乎所有平台和编译器

  2. 有丰富的模块和工具

  3. 易于集成到 IDE

  4. 社区支持强大

  5. 自动处理大多数平台差异

3、cmake

CMake 是更上层的构建系统生成器。它根据高级描述(CMakeLists.txt)生成 Makefilebuild.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引用。或者手动引用。

相关推荐
大树8810 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠11 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质11 小时前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush411 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行52011 小时前
Linux 11 动态监控指令top
linux
小宇宙Zz11 小时前
Maven依赖冲突
java·服务器·maven
Inhand陈工12 小时前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智12 小时前
ARP代理--工作原理
运维·网络·arp·arp代理
不会C语言的男孩12 小时前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
shushangyun_13 小时前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化