Makefile简介

📚 简介
make命令执行时,需要一个makefile文件,以告诉make命令需要怎么样的去编译和链接程序。

TO DO:

  • 目前基本涵盖了主要内容,后续考虑会在Github根据内容生成一点对应练习题,不然看完了没实际上手总感觉差点什么

目标与规则

首先,我们用一个示例来说明makefile的书写规则。这个示例来源于GNU的make使用手册,在这个示例中,我们的工程有8个C文件和3个头文件。

编译规则

我们要遵循以下规则:

  1. 首次编译:如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接
  2. 增量编译:如果某几个C文件被修改,只编译被修改的C文件,并链接目标程序
  3. 头文件更新:如果头文件被改变,需要编译引用了这些头文件的C文件,并链接目标程序

💡 智能编译
只要makefile写得够好,所有的这一切,我们只用一个make命令就可以完成。make命令会自动智能地根据当前的文件修改情况来确定哪些文件需要重编译。


Makefile 基本语法

规则格式

target ... : prerequisites ...
    recipe
    ...
    ...
组件 说明
target 可以是目标文件(.o)、可执行文件或标签(label)
prerequisites 生成该target所依赖的文件和/或target
recipe 该target要执行的命令(任意的shell命令)

⚠️ 重要提示
prerequisites中如果有一个以上的文件比target文件要新,recipe所定义的命令就会被执行。


实战示例

基础版本

edit : main.o kbd.o command.o display.o \
        insert.o search.o files.o utils.o
    cc -o edit main.o kbd.o command.o display.o \
        insert.o search.o files.o utils.o

main.o : main.c defs.h
    cc -c main.c
kbd.o : kbd.c defs.h command.h
    cc -c kbd.c
command.o : command.c defs.h command.h
    cc -c command.c
display.o : display.c defs.h buffer.h
    cc -c display.c
insert.o : insert.c defs.h buffer.h
    cc -c insert.c
search.o : search.c defs.h buffer.h
    cc -c search.c
files.o : files.c defs.h buffer.h command.h
    cc -c files.c
utils.o : utils.c defs.h
    cc -c utils.c
clean :
    rm edit main.o kbd.o command.o display.o \
        insert.o search.o files.o utils.o

📌 使用方法

  • 保存为 makefileMakefile
  • 执行 make 生成可执行文件
  • 执行 make clean 清理文件

Make 工作原理

执行流程

  1. 查找文件:在当前目录下找名为 "Makefile" 或 "makefile" 的文件
  2. 确定目标:找到文件中的第一个目标文件作为最终目标
  3. 检查依赖:如果目标文件不存在或依赖文件更新,执行相应命令
  4. 递归处理:对于不存在的依赖文件,递归查找生成规则
  5. 层层构建:从最底层开始,逐步构建到最终目标

🎯 核心原则
make只管文件的依赖性。如果找不到依赖文件,make就会停止工作。


使用变量优化

问题分析

在基础版本中,.o 文件列表重复出现多次,维护困难。

解决方案

objects = main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o

edit : $(objects)
    cc -o edit $(objects)

main.o : main.c defs.h
    cc -c main.c
kbd.o : kbd.c defs.h command.h
    cc -c kbd.c
command.o : command.c defs.h command.h
    cc -c command.c
display.o : display.c defs.h buffer.h
    cc -c display.c
insert.o : insert.c defs.h buffer.h
    cc -c insert.c
search.o : search.c defs.h buffer.h
    cc -c search.c
files.o : files.c defs.h buffer.h command.h
    cc -c files.c
utils.o : utils.c defs.h
    cc -c utils.c

clean :
    rm edit $(objects)

🎉 优势
添加新的 .o 文件时,只需修改 objects 变量即可。


自动推导规则

隐式规则

GNU make 具有强大的自动推导功能:

  • 看到 .o 文件时,自动添加对应的 .c 文件为依赖
  • 自动推导出 cc -c whatever.c 命令

简化版本

objects = main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o

edit : $(objects)
    cc -o edit $(objects)

main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

.PHONY : clean
clean :
    rm edit $(objects)

📝 说明
.PHONY 表示 clean 是个伪目标文件。


另一种风格

依赖关系合并

objects = main.o kbd.o command.o display.o \
    insert.o search.o files.o utils.o

edit : $(objects)
    cc -o edit $(objects)

$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

.PHONY : clean
clean :
    rm edit $(objects)

风格对比

风格 优点 缺点
详细风格 依赖关系清晰 代码较长
简洁风格 代码简短 依赖关系不够清晰

清理规则

📏 基本规则

clean:
    rm edit $(objects)

稳健做法

.PHONY : clean
clean :
    -rm edit $(objects)

🔧 技巧说明

  • .PHONY 声明伪目标
  • - 前缀表示忽略错误继续执行
  • clean 规则通常放在文件末尾

Makefile 组成要素

五大组件

组件 描述 示例
显式规则 明确指定目标、依赖和命令 main.o : main.c defs.h
隐式规则 利用make的自动推导功能 自动推导 .c.o
变量定义 定义可重用的字符串 objects = main.o kbd.o
指令 include、条件编译等 include other.mk
注释 使用 # 字符 # 这是注释

⚠️ 格式要求
命令必须以 Tab 键开始,不能使用空格!


文件命名规范

查找顺序

make 按以下顺序查找文件:

  1. GNUmakefile
  2. makefile
  3. Makefile推荐

命名建议

文件名 推荐度 说明
Makefile ⭐⭐⭐⭐⭐ 通用性好,排序靠前
makefile ⭐⭐⭐⭐ 通用性好
GNUmakefile ⭐⭐ 仅GNU make支持

自定义文件名

# 使用自定义makefile
make -f Make.Linux
make --file Make.Solaris

包含其他文件

include 指令

include <filenames>...

使用示例

include foo.make *.mk $(bar)

等价于:

include foo.make a.mk b.mk c.mk bish bash

查找路径

  1. 当前目录
  2. -I 参数指定的目录
  3. 系统默认目录:
    • /usr/local/bin
    • /usr/gnu/include
    • /usr/local/include
    • /usr/include

错误处理

# 忽略错误继续执行
-include <filenames>...

# 兼容其他版本
sinclude <filenames>...

环境变量

MAKEFILES 变量

如果定义了 MAKEFILES 环境变量,make会自动包含其中指定的文件。

🚨 注意事项
不建议使用此环境变量,因为它会影响所有的Makefile执行。


Make 工作流程

执行步骤

第一阶段:分析阶段

  1. 📖 读入所有的Makefile
  2. 📥 读入被include的其他Makefile
  3. 🔧 初始化文件中的变量
  4. 🧠 推导隐式规则,并分析所有规则
  5. 🔗 为所有的目标文件创建依赖关系链

第二阶段:执行阶段

  1. 🎯 根据依赖关系,决定哪些目标要重新生成
  2. ⚡ 执行生成命令

💡 变量展开策略

🐌 延迟展开
make使用"拖延战术",变量仅在真正需要时才展开。


📋 总结

  • ✅ Makefile的基本语法和规则
  • ✅ 如何使用变量优化代码
  • ✅ 隐式规则的强大功能
  • ✅ 文件包含和组织方式
  • ✅ Make的工作原理和流程
posted @ 2025-06-02 20:15  通辽节度使  阅读(70)  评论(0)    收藏  举报