【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引用。或者手动引用。

相关推荐
❀搜不到1 小时前
ubuntu 更新cmake
linux·运维·ubuntu
Mr_pyx1 小时前
TypeScript 完全入门指南:从基础到项目配置
linux·运维·ubuntu
LinuxRos1 小时前
从 MCU 到 Linux:机器人嵌入式OTA升级原理解密
linux·单片机·嵌入式硬件·物联网·iot
志栋智能1 小时前
安全超自动化如何支持业务快速安全地创新?
运维·安全·自动化
console.log('npc')1 小时前
git发版上线的时候,打tag标签方便jenkins部署
运维·git·jenkins
Frank_refuel1 小时前
Linux网络之网络编程套接字
linux·运维·网络
minji...2 小时前
Linux 高级IO(一)理解IO及其本质,理解五种IO模型,非阻塞IO,fcntl
服务器·网络·多路转接·高级io·非阻塞io·五种io模型·阻塞io
lisanmengmeng2 小时前
gitlab 配置的jenkins 链接错误
运维·gitlab·jenkins
week@eight2 小时前
Linux - Kafka
linux·kafka