一、make命令简介
make是Linux系统中一个强大的自动化构建工具,它通过读取名为"Makefile"的文件来执行一系列命令,完成程序的编译和链接等工作。
1. make命令的基本用法
make [选项] [目标]
如果不指定目标,make会默认执行Makefile中的第一个目标。
2. make命令常用选项
- -f filename:指定Makefile文件名(默认为Makefile)
- -j N:同时运行N个任务(并行编译)
- -n:只打印命令,不执行
- -s:静默模式,不打印命令
3. 生活中的例子
想象一下做饭的过程:
- 如果你想做一顿完整的晚餐,你需要准备食材、烹饪主菜、准备配菜等多个步骤
- 你可能有一本"食谱"(相当于Makefile),上面写着每道菜的做法和步骤
- 当你说"我要做晚餐"(相当于运行make dinner),你会按照食谱上的步骤一步步完成
make工具就像是一个厨师助手,它会查看你的"食谱",了解各个步骤之间的依赖关系,然后按照正确的顺序执行这些步骤。
二、Makefile基础
Makefile是make命令执行的依据,它定义了一系列规则,告诉make如何编译和链接程序。
1. Makefile的基本结构
makefile
目标: 依赖项
<Tab>命令
- 目标:要生成的文件或执行的动作
- 依赖项:生成目标所需的文件或条件
- 命令:生成目标需要执行的Shell命令(必须以Tab键开头)
2. 一个简单的Makefile示例
makefile
hello: hello.c
gcc -o hello hello.c
clean:
rm -f hello
这个Makefile定义了两个目标:
- hello:依赖于hello.c,执行gcc命令编译生成可执行文件
- clean:没有依赖项,执行rm命令删除生成的文件
3. 生活中的例子
做蛋糕的例子:
- 目标:蛋糕
- 依赖项:面粉、鸡蛋、糖、黄油
- 命令:混合材料,放入烤箱烘烤30分钟
用Makefile表示:
makefile
cake: flour eggs sugar butter
mix_ingredients
bake_in_oven 30
三、Makefile中的变量
1. 变量定义和使用
makefile
# 定义变量
CC = gcc
CFLAGS = -Wall -g
# 使用变量
hello: hello.c
$(CC) $(CFLAGS) -o hello hello.c
2. 常用的预定义变量
- $@:当前目标
- $<:第一个依赖项
- $^:所有依赖项
- $*:不包含扩展名的目标名称
3. 生活中的例子
烹饪中的变量:
- 烹饪工具(锅)= 不粘锅
- 烹饪温度 = 中火
使用这些变量:
煎鸡蛋: 鸡蛋 油
用$(烹饪工具)加热
用$(烹饪温度)煎鸡蛋
四、Makefile中的规则
1. 隐式规则
make有一些内置的规则,例如从.c文件生成.o文件:
makefile
# 不需要明确指定如何从.c生成.o
hello: hello.o
gcc -o hello hello.o
2. 模式规则
makefile
%.o: %.c
gcc -c $< -o $@
这条规则告诉make如何从任何.c文件生成对应的.o文件。
3. 生活中的例子
厨房规则:
- 任何需要煮的食材都放入锅中加水煮沸
- 任何需要烤的食物都放入预热好的烤箱
五、Makefile中的函数
1. 常用函数
makefile
# 查找所有.c文件
SRCS = $(wildcard *.c)
# 将.c替换为.o
OBJS = $(SRCS:.c=.o)
# 获取文件列表的单词数
COUNT = $(words $(SRCS))
2. 生活中的例子
准备派对:
- 查找所有朋友的联系方式(wildcard)
- 将每个朋友的名字转换为邀请函(替换函数)
- 计算总共需要准备多少份食物(words函数)
六、实现简单的进度条
下面是一个简单的进度条实现:
makefile
# 简单的进度条Makefile
# 源文件和目标文件
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
TARGET = myapp
# 总文件数和计数器
TOTAL = $(words $(SRCS))
COUNTER_FILE = .counter
# 初始化计数器
$(shell echo 0 > $(COUNTER_FILE))
# 更新进度的函数
define update_progress
@count=`cat $(COUNTER_FILE)` && \
count=$$((count+1)) && \
echo $$count > $(COUNTER_FILE) && \
percent=$$((count*100/$(TOTAL))) && \
echo "编译进度: [$$count/$(TOTAL)]
$$percent% - $<"
endef
# 默认目标
all: $(TARGET)
# 链接目标文件
$(TARGET): $(OBJS)
gcc -o $@ $^
@echo "编译完成!"
# 编译源文件
%.o: %.c
gcc -c $< -o $@
$(call update_progress)
# 清理
clean:
rm -f $(OBJS) $(TARGET) $
(COUNTER_FILE)
@echo "清理完成!"
.PHONY: all clean
生活中的进度条例子
想象你在做一个10道菜的宴会:
- 总共需要做10道菜
- 每完成一道菜,你会在清单上打勾并计算完成百分比
- "宴会准备进度:[3/10] 30% - 正在准备红烧肉"
七、带图形的进度条
下面是一个使用ASCII字符显示图形进度条的例子:
makefile
# 带图形的进度条Makefile
# 源文件和目标文件
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
TARGET = myapp
# 总文件数和计数器
TOTAL = $(words $(SRCS))
COUNTER_FILE = .counter
BAR_LENGTH = 20
# 初始化计数器
$(shell echo 0 > $(COUNTER_FILE))
# 更新进度条的函数
define update_progress_bar
@count=`cat $(COUNTER_FILE)` && \
count=$$((count+1)) && \
echo $$count > $(COUNTER_FILE) && \
percent=$$((count*100/$(TOTAL))) && \
completed=$$((percent*$(BAR_LENGTH)/
100)) && \
remaining=$$(($(BAR_LENGTH)
-completed)) && \
echo -ne "\r[" && \
for i in `seq 1 $$completed`; do echo
-ne "#"; done && \
for i in `seq 1 $$remaining`; do echo
-ne " "; done && \
echo -ne "] $$percent% - $<"
endef
# 默认目标
all: $(TARGET)
# 链接目标文件
$(TARGET): $(OBJS)
@echo -e "\n链接目标文件..."
gcc -o $@ $^
@echo "编译完成!"
# 编译源文件
%.o: %.c
gcc -c $< -o $@
$(call update_progress_bar)
# 清理
clean:
rm -f $(OBJS) $(TARGET) $
(COUNTER_FILE)
@echo "清理完成!"
.PHONY: all clean
生活中的图形进度条例子
装修房子的进度:
- [########## ] 50% - 正在铺地板
- [############### ] 75% - 正在刷墙面
- [####################] 100% - 装修完成!
八、实际应用:编译一个小项目
下面是一个实际的小项目编译示例,包含进度显示:
makefile
# 小项目编译Makefile
# 项目设置
CC = gcc
CFLAGS = -Wall -g
SRCS = $(wildcard src/*.c)
OBJS = $(SRCS:.c=.o)
TARGET = myapp
# 进度设置
TOTAL = $(words $(SRCS))
COUNTER_FILE = .counter
BAR_LENGTH = 30
# 初始化计数器
$(shell echo 0 > $(COUNTER_FILE))
# 更新进度条的函数
define update_progress_bar
@count=`cat $(COUNTER_FILE)` && \
count=$$((count+1)) && \
echo $$count > $(COUNTER_FILE) && \
percent=$$((count*100/$(TOTAL))) && \
completed=$$((percent*$(BAR_LENGTH)/
100)) && \
remaining=$$(($(BAR_LENGTH)
-completed)) && \
echo -ne "\r[" && \
for i in `seq 1 $$completed`; do echo
-ne "#"; done && \
for i in `seq 1 $$remaining`; do echo
-ne " "; done && \
echo -ne "] $$percent% - 编译 $<"
endef
# 默认目标
all: prepare $(TARGET)
# 准备阶段
prepare:
@echo "准备编译环境..."
@mkdir -p bin
# 链接目标文件
$(TARGET): $(OBJS)
@echo -e "\n链接目标文件..."
$(CC) $(CFLAGS) -o bin/$@ $^
@echo "编译完成!"
# 编译源文件
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
$(call update_progress_bar)
# 清理
clean:
@echo "清理项目..."
rm -f $(OBJS) bin/$(TARGET) $
(COUNTER_FILE)
@echo "清理完成!"
.PHONY: all prepare clean
生活中的项目例子
建造一座房子:
- 准备阶段:规划设计、申请许可证
- 基础工程:打地基、搭建框架
- 主体工程:砌墙、安装门窗、铺设管道
- 装修阶段:刷墙、铺地板、安装设备
- 最终检查:确保一切符合标准
每个阶段都有进度显示,让你知道整个建造过程完成了多少。
总结
make和Makefile是Linux系统中强大的自动化构建工具,通过定义规则和依赖关系,可以大大简化复杂项目的编译过程。进度条的实现可以直观地了解编译进度。