Linux中的make与Makefile详解

发布于:2025-03-22 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、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

生活中的项目例子

建造一座房子:

  1. 准备阶段:规划设计、申请许可证
  2. 基础工程:打地基、搭建框架
  3. 主体工程:砌墙、安装门窗、铺设管道
  4. 装修阶段:刷墙、铺地板、安装设备
  5. 最终检查:确保一切符合标准

每个阶段都有进度显示,让你知道整个建造过程完成了多少。

总结

make和Makefile是Linux系统中强大的自动化构建工具,通过定义规则和依赖关系,可以大大简化复杂项目的编译过程。进度条的实现可以直观地了解编译进度。