linux makefile教程怎么用?makefile教程视频

Linux Makefile 完整教程

目录

  1. 什么是 Make 和 Makefile
  2. 基本语法
  3. 变量
  4. 模式规则
  5. 自动变量
  6. 函数
  7. 条件判断
  8. 实战示例
  9. 最佳实践

什么是 Make 和 Makefile

Make 是一个自动化构建工具,Makefile 是它的配置文件。

  • 核心思想:只重新编译发生变化的源文件,提高构建效率
  • 应用场景:C/C++ 项目编译、大型工程构建、自动化部署等

基本工作流程

Makefile → make 命令 → 检查依赖关系 → 只编译有变化的文件

基本语法

1 基本结构

# 目标: 依赖
# <Tab> 命令(注意:必须用 Tab,不能用空格!)
target: dependency1 dependency2
    <command1>
    <command2>

2 第一个 Makefile 示例

# 编译 main.c 生成 main 可执行文件
main: main.o utils.o
    gcc -o main main.o utils.o
main.o: main.c
    gcc -c main.c
utils.o: utils.c
    gcc -c utils.c
# 清理生成的文件
clean:
    rm -f .o main

运行:

20分钟Makefile光速入门教程
加载中
20分钟Makefile光速入门教程
make          # 构建
make clean    # 清理

3 伪目标

.PHONY: clean all install
clean:
    rm -f .o main
all: main

.PHONY 声明伪目标,避免与同名文件冲突。


变量

1 变量定义

# 简单赋值(立即求值)
CC = gcc
CFLAGS = -Wall -g
# 递归赋值(延迟求值)
CC ?= gcc        # 如果未定义则赋值
CC := gcc        # 简单赋值,立即展开
CC += -std=c11   # 追加赋值
# 条件赋值
override CC = clang   # 覆盖命令行传入的值

2 常用内置变量

CC       # C 编译器
CXX      # C++ 编译器
CFLAGS   # C 编译选项
CXXFLAGS # C++ 编译选项
LDFLAGS  # 链接选项
LDLIBS   # 链接库
AR       # 归档工具
RM       # 删除命令

3 使用变量

CC = gcc
CFLAGS = -Wall -O2
TARGET = myapp
SRCS = main.c utils.c helper.c
OBJS = $(SRCS:.c=.o)
$(TARGET): $(OBJS)
    $(CC) $(LDFLAGS) -o $@ $^
%.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@
clean:
    rm -f $(OBJS) $(TARGET)

linux makefile教程怎么用?makefile教程视频

= 目标名,$^ = 所有依赖,$< = 第一个依赖


模式规则

1 通配符规则

# %.o: %.c 表示所有 .o 文件依赖于同名的 .c 文件
%.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@

2 多模式规则

# 同时匹配 .c 和 .cpp
%.o: %.c %.cpp
    $(CC) $(CFLAGS) -c $< -o $@

3 静态模式规则

OBJS = main.o utils.o helper.o
$(OBJS): %.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@

自动变量

变量 含义
目标文件名
$< 第一个依赖文件名
$^ 所有依赖文件名(去重)
所有依赖文件名(不去重)
目标中 匹配的部分
比目标更新的依赖文件列表
所有先决条件(包括 order-only)

示例

main.o: main.c main.h
    gcc -c $< -o $@    # 等价于 gcc -c main.c -o main.o
# 使用 $? 只编译更新的依赖
main: main.o utils.o
    gcc -o $@ $?       # 只链接更新的依赖

函数

1 常用函数

# wildcard: 获取匹配文件
SRCS = $(wildcard .c)
# patsubst: 模式替换
OBJS = $(patsubst %.c,%.o,$(SRCS))
# notdir: 去除路径
DIRS = $(notdir $(SRCS))
# addprefix: 添加前缀
PATHS = $(addprefix src/,$(SRCS))
# realpath: 获取绝对路径
REAL_PATHS = $(realpath $(DIRS))
# foreach: 循环
# $(foreach var,list,text)
OBJS = $(foreach src,$(SRCS),$(src:.c=.o))
# if: 条件判断
# $(if condition,true-part[,false-part])
RESULT = $(if $(CC),using gcc,using clang)
# warning/error: 输出警告/错误
$(warning This is a warning)
$(error This is an error)
# shell: 执行 shell 命令
DATE = $(shell date +%Y%m%d)

2 函数示例

# 获取所有 C 源文件
SRCS = $(wildcard src/.c)
# 转换为对象文件
OBJS = $(patsubst src/%.c,build/%.o,$(SRCS))
# 创建目录
build/%.o: src/%.c | build
    @mkdir -p $(@D)
    $(CC) $(CFLAGS) -c $< -o $@
build:
    mkdir -p build
clean:
    rm -rf build

条件判断

1 ifeq / ifneq

ifeq ($(CC),gcc)
    CFLAGS += -O3
else ifeq ($(CC),clang)
    CFLAGS += -O3 -Qunused-arguments
else
    CFLAGS += -O2
endif

2 ifdef / ifndef

linux makefile教程怎么用?makefile教程视频

ifdef DEBUG CFLAGS += -g -DDEBUG else CFLAGS += -O2 endif ifndef VERBOSE QUIET := @ endif

3 结合变量使用

ifeq ($(OS),Linux)
    LDFLAGS += -lpthread
else ifeq ($(OS),Darwin)
    LDFLAGS += -lpthread
endif

实战示例

1 简单 C 项目

# ==================== 配置区 ====================
CC = gcc
CFLAGS = -Wall -Wextra -std=c11 -g
LDFLAGS =
LDLIBS = -lm
TARGET = myapp
SRCDIR = src
OBJDIR = build
BINDIR = bin
SRCS = $(wildcard $(SRCDIR)/.c)
OBJS = $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(SRCS))
# ==================== 规则区 ====================
.PHONY: all clean install uninstall
all: $(TARGET)
$(TARGET): $(OBJS) | $(BINDIR)
    $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
$(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR)
    $(CC) $(CFLAGS) -c $< -o $@
$(OBJDIR):
    mkdir -p $(OBJDIR)
$(BINDIR):
    mkdir -p $(BINDIR)
clean:
    rm -rf $(OBJDIR) $(BINDIR) $(TARGET)
install: $(TARGET)
    cp $(TARGET) $(BINDIR)/
uninstall:
    rm -f $(BINDIR)/$(TARGET)

2 C++ 项目

# ==================== 配置区 ====================
CXX = g++
CXXFLAGS = -Wall -Wextra -std=c++17 -O2
LDFLAGS =
LDLIBS = -lpthread -lstdc++fs
TARGET = mycppapp
SRCDIR = src
OBJDIR = build
# 自动收集所有 .cpp 文件
SRCS = $(wildcard $(SRCDIR)/.cpp)
OBJS = $(patsubst $(SRCDIR)/%.cpp,$(OBJDIR)/%.o,$(SRCS))
# 自动收集头文件依赖
DEPS = $(OBJS:.o=.d)
# ==================== 规则区 ====================
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJS)
    $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS)
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
    @mkdir -p $(@D)
    $(CXX) $(CXXFLAGS) -MMD -MP -c $< -o $@
# 包含自动生成的依赖文件
-include $(DEPS)
clean:
    rm -rf $(OBJDIR) $(TARGET)

-MMD -MP 自动生成 .d 依赖文件,当修改头文件时会自动重新编译相关源文件。

3 多目标项目

# 构建多个可执行文件
TARGETS = app1 app2 app3
.PHONY: all clean
all: $(TARGETS)
app1: app1.o lib.o
    $(CC) -o $@ $^
app2: app2.o lib.o
    $(CC) -o $@ $^
app3: app3.o lib.o
    $(CC) -o $@ $^
%.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@
clean:
    rm -f $(TARGETS) .o

4 带测试的项目

CC = gcc
CFLAGS = -Wall -g
LDFLAGS =
LDLIBS = -lcheck -lm -lpthread
TARGET = mylib
TEST_TARGET = test_mylib
.PHONY: all clean test
all: $(TARGET) $(TEST_TARGET)
# 编译库
$(TARGET): main.o utils.o
    $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
# 编译测试
$(TEST_TARGET): test_main.o main.o utils.o
    $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
%.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@
test: $(TEST_TARGET)
    ./$(TEST_TARGET)
clean:
    rm -f $(TARGET) $(TEST_TARGET) .o

linux makefile教程怎么用?makefile教程视频


最佳实践

1 通用模板

# 1. 始终使用 .PHONY 声明伪目标
.PHONY: all clean install uninstall test
# 2. 使用变量而非硬编码
CC ?= gcc
CFLAGS ?= -Wall -Wextra
CXX ?= g++
CXXFLAGS ?= -Wall -Wextra
# 3. 使用自动变量
# 4. 使用模式规则减少重复
# 5. 提供 clean 目标
# 6. 使用 -include 包含依赖文件
# 7. 使用 @ 前缀抑制命令回显(可选)
# 8. 使用 - 前缀忽略错误(可选)
# 示例:安静模式
quiet_cmd = @echo "  CC      $<"
quiet_cmd = @$(CC) $(CFLAGS) -c $< -o $@

2 常见陷阱

# ❌ 错误:使用空格而不是 Tab
target: dep
    command    # 空格!会报错
# ✅ 正确:使用 Tab
target: dep
    command    # Tab
# ❌ 错误:变量名大小写敏感
CC = gcc
$(cc) ...    # cc 未定义!
# ✅ 正确
$(CC) ...
# ❌ 错误:忘记 .PHONY
clean:
    rm -f .o
# 如果当前目录有名为 clean 的文件,make 不会执行
# ✅ 正确
.PHONY: clean
clean:
    rm -f .o

3 调试技巧

# 显示执行的命令
make -n          # 预览命令(dry run)
make -d          # 详细调试信息
make VERBOSE=1   # 自定义 verbose 模式
# 在 Makefile 中调试
$(info CC is $(CC))
$(warning CFLAGS is $(CFLAGS))
$(error Stop here)

快速参考卡片

基本结构:  target: dependencies
                <tab> command
变量:       VAR = value
            $(VAR) 或 ${VAR}
自动变量:   $@ 目标  $< 第一个依赖  $^ 所有依赖
函数:       $(wildcard .c)
            $(patsubst %.c,%.o,$(SRCS))
条件:       ifeq / ifneq / ifdef / ifndef
伪目标:     .PHONY: clean all

概念 要点
核心 依赖关系 + 增量编译
语法 目标: 依赖 → Tab 命令
变量 $(VAR) 引用,善用自动变量
模式规则 %.o: %.c 减少重复
函数 wildcard, patsubst, foreach
最佳实践 使用 .PHONY、变量抽象、自动依赖

Makefile 是 Linux 开发中不可或缺的工具,掌握它能极大提升构建效率,建议从简单项目开始,逐步掌握高级特性。

首发原创文章,作者:王坚‌,如若转载,请注明出处:https://idctop.com/article/483362.html

(0)
费用中心合并是什么意思?企业财务软件费用中心合并怎么操作
上一篇 2026年7月11日 21:19
Linux课程论文怎么写?Linux系统安装配置教程
下一篇 2026年7月11日 21:22

相关推荐

  • Linux如何获取时钟?linux获取系统时间命令

    在Linux系统中获取高精度时钟,最推荐且通用的方法是使用clock_gettime()系统调用配合CLOCK_MONOTONIC或CLOCK_REALTIME时钟源,它能提供纳秒级精度并避免系统时间调整带来的跳变问题,时间对于操作系统而言,不仅仅是显示在屏幕上的数字,更是进程调度、日志记录、性能监控以及分布式……

    2026年7月6日
    20000
  • 2b2t手机版服务器号是多少,怎么进服务器

    2b2t并没有官方手机版服务器号,因为2b2t是Java版服务器,基岩版手机端无法直接加入原版2b2t,但可以通过第三方入口或替代服务器实现“手机版2b2t”体验,先搞清楚:2b2t手机版服务器号为什么不存在很多玩家在短视频平台看到“2b2t手机版”的截图或视频,误以为存在一个可以直接输入数字的服务器号,2b2……

    2026年8月13日
    200
  • 一个服务器上能配置多少个IP?,如何配置多个IP?

    一台服务器能配置的IP数量没有硬性上限,理论极限取决于子网掩码的计算规则,实际部署中则受限于操作系统、网卡性能以及服务商的分配策略,IPv4协议下,一个标准C类子网最多容纳254个可用地址,而通过CIDR聚合或虚拟接口技术,单台物理机完全可以承载数百甚至上千个IP,具体能配多少,答案分三层:协议层、系统层、业务……

    2026年8月13日
    300
  • 100G内存服务器多少钱一台,哪家性价比高?

    100G内存的服务器月租成本通常在800元至3000元区间,具体取决于CPU型号、存储类型与带宽规格,以及服务商是否持有自营机房和增值电信资质,100G内存服务器价格由哪些因素决定你看到的报价差异,核心来自硬件、基础设施和服务商三个层面,硬件配置CPU代数与核心数:至强E5、E5 v4、Gold系列之间价差可达……

    2026年8月26日
    100
  • 酷番云16核32G云服务器多少钱?,性价比高吗?

    16核32G云服务器在腾讯云的月租费用通常在1200元至2500元之间,具体取决于实例规格、带宽大小和购买时长,通过年付或新用户优惠可显著降低, 实际选型时,除了价格,还需关注服务商的资质和机房稳定性,持牌自营机房往往提供更可靠的服务,16核32G云服务器的典型应用场景16核32G的配置在云计算领域属于中高端机……

    2026年8月24日
    100
  • E5系列CPU必须要用服务器内存吗,多少钱

    E5处理器搭配普通内存确实能开机,但服务器内存才是稳定运行的保障,价格因规格和渠道差异从几十元到上千元不等,为什么E5平台对内存有特殊要求E5系列处理器是Intel为服务器和工作站设计的芯片组,它的内存控制器原生支持ECC纠错和Register(寄存器)缓冲技术,家用平台的内存控制器没有这些功能,所以普通内存条……

    2026年8月4日
    1500
  • Python GDAL在Linux上怎么安装?gdal库安装教程

    在Linux环境下使用Python进行GDAL开发,核心在于通过源码编译或Conda环境解决C++依赖库的链接问题,推荐优先使用Conda或Docker容器化方案以规避复杂的系统级配置,地理空间数据处理的基石往往建立在复杂的底层依赖之上,而Linux作为服务器端的主流操作系统,其环境配置的严谨性既是优势也是痛点……

    2026年7月4日
    19010
  • 服务器发送1T数据流量费要多少钱?,云服务器流量计费标准是什么

    服务器发送1TB数据,流量费通常在几十元到几百元之间,具体取决于云厂商定价、计费模式和带宽类型;若走传统IDC带宽,成本可能更高,而按固定带宽包月则费用弹性更大,先算一笔账:1TB流量到底值多少钱要搞清“服务器发送1T数据要多少流量费”,先要明白云服务器和物理机房的计费逻辑,目前主流方式有按流量计费、按固定带宽……

    2026年8月22日
    700
  • 2000G防御服务器被攻击要多少钱,防御费用怎么算?

    对于2000G级别的DDoS防御,租用一台具备2000G防御能力的服务器,月租成本通常在5万至20万元区间,具体取决于防护类型、带宽资源及服务商的基础设施,影响2000G防御价格的核心因素2000G防御价格并非固定标价,而是由多个技术参数和商业策略共同决定,理解这些因素,能帮你判断报价是否合理,防御能力真实性市……

    2026年8月11日
    400
  • 江西电信宽带DNS服务器地址是多少,如何设置才能提升网速?

    江西电信宽带的首选DNS服务器地址是202.101.224.68,备用DNS是202.101.224.69, 这是官方默认配置,适用于绝大多数家庭用户,如果你在使用中遇到网页提示错误或视频卡顿,从DNS排查常常能快速定位问题,为什么DNS设置影响上网体验DNS(域名系统)负责把网站域名转换成服务器IP地址,如果……

    2026年8月22日
    600

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注