当前位置: 首页 > backend >正文

Makefile与CMake

一、Makefile 核心内容

1. Makefile 基础结构与工作原理
  • 三要素
    • 目标(Target):要生成的文件或执行的操作(如可执行文件、清理操作)。
    • 依赖(Dependency):生成目标所需的文件或其他目标。
    • 命令(Command):生成目标的具体指令(需以Tab 键开头)。
  • 工作原理
    Make 通过检查依赖文件的修改时间,仅重新编译更新过的文件,提高编译效率。例如:
    simple: main.o foo.o    # 目标:simple,依赖:main.o和foo.ogcc -o simple main.o foo.o  # 命令
    main.o: main.c          # 子目标:main.o依赖main.cgcc -c main.c -o main.o
    
2. 关键特性与语法
  • 伪对象(.PHONY)
    避免 Make 将目标视为同名文件,强制执行命令。例如:

    .PHONY: clean
    clean:rm simple main.o foo.o
    
     
    • 若不声明.PHONY,当目录存在clean文件时,make clean会认为目标已更新,不执行删除命令。
  • 变量与自动变量

    • 自定义变量:用于存储重复内容(如编译器、文件列表),例:
      CC = gcc
      SRCS = main.c foo.c
      OBJS = $(SRCS:.c=.o)  # 将.c替换为.o
      
    • 自动变量
      • $@:当前目标名(如simple)。
      • $^:所有依赖文件(如main.o foo.o)。
      • $<:第一个依赖文件(如main.c)。
      $(EXE): $(OBJS)$(CC) -o $@ $^  # 等价于gcc -o simple main.o foo.o
      
  • 函数与第三方库依赖

    • wildcard:获取指定模式的文件列表,例:SRCS = $(wildcard *.c)
    • patsubst:字符串替换,例:OBJS = $(patsubst %.c, %.o, $(SRCS))
    • 第三方库链接:通过-I指定头文件路径,-L指定库路径,-l指定库名,例:

      makefile

      CFLAGS += -I./include
      LDFLAGS += -L./lib -lpthread
      
3. 实战范例
  • 简单示例

    all: test@echo "hello all"
    test:@echo "hello test"
    
     
    • make默认执行第一个目标(all),依赖test,因此先执行test再执行all
  • 复杂编译流程
    通过变量和自动变量简化多文件编译,例:

    CC = gcc
    SRCS = main.c foo.c
    OBJS = $(SRCS:.c=.o)
    EXE = simple$(EXE): $(OBJS)$(CC) -o $@ $^%.o: %.c$(CC) -c $< -o $@
    

二、CMake 核心内容

1. CMake 概述
  • 定位:跨平台构建工具,通过编写CMakeLists.txt生成 Makefile 或其他项目文件(如 VS 工程),简化多平台编译配置。
  • 优势:相比 Makefile,语法更简洁,支持模块化设计,适合大型项目。
2. 基础语法与流程
  • 核心命令
    • cmake_minimum_required:指定 CMake 最低版本。
    • project:定义项目名称和语言(如CCXX)。
    • add_executable:添加可执行文件,关联源文件。
    • add_library:生成库文件(SHARED动态库,STATIC静态库)。
    • target_link_libraries:链接库文件到可执行文件。
  • 编译流程
    1. 在项目根目录创建CMakeLists.txt
    2. 创建build目录,进入后执行cmake ..生成 Makefile。
    3. 执行make编译。
3. 实战场景
  • 单文件编译

    cmake_minimum_required(VERSION 2.8)
    project(0voice)
    set(SRC_LIST main.c)
    add_executable(0voice ${SRC_LIST})
    
  • 多目录与库管理

    • 子目录编译为库
      # 根目录CMakeLists.txt
      add_subdirectory(src/dir1)  # 添加子目录
      add_subdirectory(src/dir2)
      add_executable(main main.c)
      target_link_libraries(main dir1 dir2)  # 链接库
      
       
      # src/dir1/CMakeLists.txt
      add_library(dir1 SHARED dir1.c)  # 生成动态库
      
    • 强制使用静态库
      target_link_libraries(main libdir1.a)  # 指定静态库文件名
      
  • 安装与编译选项

    • 指定安装路径
      cmake -DCMAKE_INSTALL_PREFIX=/usr/local ..  # 安装到/usr/local
      make install  # 安装库、头文件等到目标路径
      
    • Debug/Release 模式
      if(${CMAKE_BUILD_TYPE} MATCHES "Release")set(CMAKE_CXX_FLAGS "-O3 -Wall")  #  Release优化
      else()set(CMAKE_CXX_FLAGS "-O0 -g")     # Debug调试符号
      endif()
      
特性MakefileCMake
学习难度较高(语法灵活但复杂)较低(模块化命令,易上手)
跨平台支持依赖平台特定语法(如 GNU Make)原生支持多平台(生成对应平台构建文件)
大型项目管理手动维护依赖关系,易出错支持子目录、库管理,自动处理依赖
适用场景简单项目或需要精细控制编译流程的场景复杂多模块项目、跨平台开发

建议

  • 小型项目或需要深入理解编译原理时,使用 Makefile。
  • 中大型项目或跨平台开发时,优先选择 CMake,搭配make执行编译。

三、Makefile和CMake的具体案例

案例 1:Makefile 跨目录编译

项目结构
project_make/
├── include/           # 头文件目录
│   └── utils.h
├── src/               # 源文件目录
│   ├── main.c
│   └── utils/
│       └── util.c
├── build/             # 输出目录(存放目标文件和可执行文件)
└── Makefile

代码文件内容
  1. include/utils.h(头文件)
 
#ifndef UTILS_H
#define UTILS_Hint add(int a, int b);#endif
 
  1. src/utils/util.c(功能实现)
#include "utils.h"int add(int a, int b) {return a + b;
}
 
  1. src/main.c(主函数)
#include <stdio.h>
#include "utils.h"int main() {int sum = add(3, 5);printf("3 + 5 = %d\n", sum);return 0;
}
Makefile 实现
# 变量定义
CC = gcc
CFLAGS = -Wall -I./include  # -I 指定头文件路径
SRCS = $(wildcard src/*.c src/utils/*.c)  # 匹配所有源文件
OBJS = $(patsubst %.c, build/%.o, $(SRCS))  # 目标文件路径(build目录下保持原目录结构)
EXE = build/app  # 最终可执行文件路径# 生成可执行文件(默认目标)
all: $(EXE)# 链接目标文件生成可执行文件
$(EXE): $(OBJS)@mkdir -p $(dir $@)  # 创建输出目录(若不存在)$(CC) $^ -o $@# 模式规则:编译 .c 文件为 .o(保持目录结构)
build/%.o: %.c@mkdir -p $(dir $@)  # 创建目标文件所在目录(如 build/src/utils/)$(CC) $(CFLAGS) -c $< -o $@# 清理生成文件
.PHONY: clean
clean:rm -rf build/
操作说明
  1. 执行 make,会自动:
    • 在 build 目录下生成 src/main.osrc/utils/util.o 目标文件。
    • 链接生成可执行文件 build/app
  2. 运行 ./build/app,输出 3 + 5 = 8

案例 2:CMake 跨目录编译

项目结构
project_cmake/
├── include/           # 头文件目录
│   └── utils.h
├── src/               # 源文件目录
│   ├── main.c
│   └── utils/
│       ├── util.c
│       └── CMakeLists.txt  # 子目录 CMake 配置
├── build/             # 编译目录(手动创建)
└── CMakeLists.txt     # 根目录 CMake 配置
代码文件内容

头文件 include/utils.hsrc/utils/util.csrc/main.c 与 Makefile 案例完全相同。

CMake 配置文件
  1. 根目录 CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(MyProject)# 指定 C 标准(可选)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)# 包含头文件目录(全局生效)
include_directories(include)# 添加子目录(会执行 src/utils/CMakeLists.txt)
add_subdirectory(src/utils)# 定义主程序源文件(仅主函数)
set(MAIN_SRC src/main.c)# 生成可执行文件(链接子目录生成的库)
add_executable(app ${MAIN_SRC})
target_link_libraries(app utils)  # 链接子目录生成的库
 
  1. 子目录 src/utils/CMakeLists.txt
# 定义当前目录的源文件(仅功能实现)
set(UTIL_SRC util.c)# 生成静态库(库名:utils)
add_library(utils STATIC ${UTIL_SRC})# 可选:设置库的输出目录(例如放到 build/lib)
set_target_properties(utils PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
操作说明
  1. 进入 build 目录(需手动创建):
    mkdir build && cd build
    
  2. 执行 cmake .. 生成构建文件(会自动处理跨目录依赖)。
  3. 执行 make 编译,生成:
    • 静态库 build/lib/libutils.a(子目录生成)。
    • 可执行文件 build/app(根目录生成)。
  4. 运行 ./app,输出 3 + 5 = 8

0voice · GitHub 

http://www.xdnf.cn/news/6436.html

相关文章:

  • AI大模型应用:17个实用场景解锁未来
  • 软件设计师考试《综合知识》CPU考点分析(2019-2023年)——求三连
  • 让AI帮我写一个word转pdf的工具
  • 从《西游记》到微调大模型:一场“幻觉”与“认知”的对话20250515
  • 在 VMware 中挂载 U 盘并格式化为 ext4 文件系统的完整指南
  • 企业在蓝海市场有哪些推进目标?
  • 操作系统学习笔记第3章 内存管理(灰灰题库)
  • 嵌入式学习--江科大51单片机day7
  • Metagloves Pro+Manus Core:一套组合拳打通虚拟制作与现实工业的任督二脉
  • 题海拾贝:P4017 最大食物链计数
  • 399. 除法求值
  • 自然资源和空间数据应用平台
  • 深度学习框架---TensorFlow概览
  • 【vue】【环境配置】项目无法npm run serve,显示node版本过低
  • 【2025最新】VSCode Cline插件配置教程:免费使用Claude 3.7提升编程效率
  • Unity光照笔记
  • 解决Mawell1.29.2启动SQLException: You have an error in your SQL syntax问题
  • Java EE初阶——线程安全
  • 死锁(Deadlock)知识点详解
  • 青少年气胸术后护理要点清单
  • Cursor安全漏洞事件深度解析:当AI编程工具成为供应链攻击的新战场
  • WebGL 3着色器和GLSL
  • Elasticsearch性能调优全攻略:从日志分析到集群优化
  • C++多态实现的必要条件剖析
  • 架构进阶:企业流程框架设计思路【附全文阅读】
  • 微信小程序van-dialog确认验证失败时阻止对话框的关闭
  • Spring 模拟转账开发实战
  • 什么是红海战略?了解红海战略的竞争目标
  • (面试)Handler消息处理机制原理
  • 基于Deeplearning4j的多源数据融合预测模型实现:从设计到落地全解析