Makefileѧϰ
以下是一些推荐的学习Makefile的网站和资源,可以帮助你快速掌握Makefile的常用规则、结构和示例:
- 廖雪峰的官方网站
廖雪峰的Makefile教程从基础概念讲起,通过简单易懂的例子逐步深入,非常适合作为入门学习。
网址:Makefile基础 - Makefile教程 - 廖雪峰的官方网站 - 腾讯云技术文档
腾讯云上有一篇关于Makefile的详细解释和示例,涵盖了从基础到进阶的内容,包括如何编写Makefile以及一些高级用法。
网址:超清晰的makefile解释、编写与示例 - 腾讯云 - 博客园 - Makefile语法总结
这篇文章总结了Makefile的常用语法和规则,并通过具体示例进行解析,适合有一定基础的读者深入学习。
网址:Makefile语法详细总结及示例解析(快速掌握) - CSDN博客 - Makefile教程
CSDN上有许多关于Makefile的教程和示例,其中一些文章提供了详细的规则和实际项目中的Makefile模板。
网址:makefile常用的命令总结及简单示例 - CSDN博客 - GitHub Pages - Makefile教程
这是一个开源的Makefile教程,提供了从入门到高级的详细内容,并且包含了许多实用的示例。
网址:Makefile 教程 - GitHub Pages - CSDN博客 - 通用Makefile模板
这篇文章提供了一个通用的Makefile模板,适用于C++项目的编译和链接,是一个很好的参考。
网址:makeFile基本介绍, 语法, 示例,通用makefile - CSDN博客
Makefile 的基本概念
- 定义:Makefile 是一个用于自动化编译和构建项目的文件,它描述了工程的编译、链接等规则,包括哪些源文件需要编译、如何编译、如何生成目标文件等。
- 作用:通过编写 Makefile,可以使用
make
命令工具来自动化编译过程,避免手动输入繁琐的编译命令,提高开发效率。
Makefile 的基本结构
- 变量定义:用于简化和统一文件名或命令的书写。例如:
CC = gcc
:定义编译器为 gcc。CFLAGS = -Wall -g
:定义编译选项,-Wall
显示所有警告,-g
启用调试信息。TARGET = my_program
:定义目标文件名为my_program
。OBJECTS = main.o utils.o
:定义目标文件列表。
- 规则定义:指定目标文件、依赖文件和生成目标文件的命令。基本格式如下:
target: dependencies
:目标文件和依赖文件。command
:生成目标文件的命令,必须以 Tab 键开始。
- 伪目标:用于执行一些特定的操作,如清理编译生成的文件。例如:
.PHONY: clean
:声明clean
是一个伪目标。clean:
:定义清理操作的命令。
Makefile 的基本语法
目标和依赖:目标是要生成的文件,依赖是生成目标所需要的文件。基本格式如下:
target: dependencies
:目标文件和依赖文件。command
:生成目标文件的命令,必须以 Tab 键开始。
内置变量:Makefile 提供了一些内置变量,常用的包括:
$@
:目标文件的名称。$^
:所有依赖文件的名称。$<
:第一个依赖文件的名称。
通配符:用于匹配文件名或文件路径中的多个字符,以便在规则中批量处理文件。常见的通配符有:
*
:匹配零个或多个字符。?
:匹配一个任意字符。[...]
:匹配方括号内的任意一个字符。[!...]
:匹配除了方括号内的字符之外的任意一个字符。
模式规则:用于定义一种模式,告诉 Make 工具如何将一类文件转换成另一类文件。例如:
%.o: %.c
:表示所有以.c
结尾的源文件都可以生成对应的.o
目标文件。
自动化变量:在规则的命令中使用,代表了与规则相关联的文件名。常用的自动化变量包括:
$@
:表示规则中的目标文件名。$<
:表示规则中的第一个依赖文件名。$^
:表示规则中的所有依赖文件名,以空格分隔。
Makefile 的实际应用
- 简单示例:
- 假设有一个简单的 C 项目,包含
main.c
和utils.c
两个源文件 ,目标是生成可执行文件my_program
。Makefile 内容如下:CC = gcc CFLAGS = -Wall -g TARGET = my_program OBJECTS = main.o utils.o all: $(TARGET) $(TARGET): $(OBJECTS) $(CC) $(CFLAGS) -o $(TARGET) $(OBJECTS) main.o: main.c $(CC) $(CFLAGS) -c main.c utils.o: utils.c $(CC) $(CFLAGS) -c utils.c .PHONY: clean clean: rm -f $(TARGET) $(OBJECTS)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38- 在项目目录中执行 `make` 命令即可编译项目,执行 `make clean` 命令可以清理编译生成的文件。
- **复杂项目**:
- 对于复杂的嵌入式 Linux 项目,可能包含多个源文件、头文件、汇编文件等,需要更复杂的 Makefile 来管理。例如:
- ```makefile
CROSS_COMPILE ?= arm-linux-gnueabihf-
TARGET ?= bsp
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdump
INCDIRS := imx6ul \
bsp/clk \
bsp/led \
bsp/delay
SRCDIRS := project \
bsp/clk \
bsp/led \
bsp/delay
INCLUDE := $(patsubst %, -I %, $(INCDIRS))
SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))
SFILENDIR := $(notdir $(SFILES))
CFILENDIR := $(notdir $(CFILES))
SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS := $(SOBJS) $(COBJS)
VPATH := $(SRCDIRS)
.PHONY: clean
$(TARGET).bin : $(OBJS)
$(LD) -Timx6ul.lds -o $(TARGET).elf $^
$(OBJCOPY) -O binary -S $(TARGET).elf $@
$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis
$(SOBJS) : obj/%.o : %.S
$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<
$(COBJS) : obj/%.o : %.c
$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<
clean:
rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)
- 该 Makefile 使用了交叉编译工具链,支持多个源文件和汇编文件的编译,生成二进制可执行文件、ELF 格式文件和反汇编文件。
- 假设有一个简单的 C 项目,包含
Makefile 的调试
- 查看
make
过程:使用make -n
命令可以显示将会执行的命令,但不会实际执行。 - 增加详细输出:使用
make --debug
命令可以提供详细的调试信息,帮助排查问题。
Makefile 的高级功能
条件语句:可以根据不同的条件执行不同的规则或设置变量。常见的条件语句有
ifeq
、ifneq
、ifdef
、ifndef
。- 示例:根据操作系统设置不同的编译选项。
CC = gcc CFLAGS = -Wall -g ifeq ($(OS),Windows_NT) CFLAGS += -DWIN32 else CFLAGS += -DUNIX endif TARGET = my_program SRCS = main.c utils.c all: $(TARGET) $(TARGET): $(SRCS) $(CC) $(CFLAGS) -o $(TARGET) $(SRCS) clean: rm -f $(TARGET)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- **使用函数**:Makefile 提供了一些内建函数,帮助处理字符串和文件操作等任务。常用函数包括:
- `$(wildcard pattern)`:返回匹配模式的所有文件。
- `$(patsubst pattern,replacement,text)`:用指定的替换文本替换模式。
- `$(basename names)`:去除文件名的扩展名部分。
- `$(dir names)`:返回文件的目录路径。
- 示例:使用 `wildcard` 和 `patsubst`。
---
### **Makefile基础笔记**
#### **1. Makefile概述**
- **Makefile的作用**:
- 在Linux环境下,`make`命令会查找当前目录下的`Makefile`文件。
- 根据Makefile中定义的规则,`make`可以自动化地执行命令,例如编译源代码、生成目标文件等。
- Makefile的核心思想是通过定义规则来描述如何生成目标文件。
- **基本逻辑示例**:
- 假设有文件`a.txt`、`b.txt`和`c.txt`,需要先合并`a.txt`和`b.txt`生成中间文件`m.txt`,再将`m.txt`与`c.txt`合并生成最终文件`x.txt`。
- Makefile通过规则定义这种依赖关系和生成过程。
#### **2. Makefile规则的基本格式**
- **规则格式**:
目标文件: 依赖文件1 依赖文件2 …
命令1
命令2
…1
2
3
4
5
6
7
8
9
10
11
12- **目标文件**:需要生成的文件。
- **依赖文件**:生成目标文件所需的文件。
- **命令**:用于生成目标文件的具体命令,必须以Tab键开头。
- **示例**:
```makefile
m.txt: a.txt b.txt
cat a.txt b.txt > m.txt
x.txt: m.txt c.txt
cat m.txt c.txt > x.txtm.txt
依赖于a.txt
和b.txt
,通过cat
命令合并生成。x.txt
依赖于m.txt
和c.txt
,同样通过cat
命令生成。
- 示例:根据操作系统设置不同的编译选项。
3. Makefile的执行逻辑
增量编译:
make
会检查目标文件和依赖文件的修改时间。- 如果目标文件的修改时间晚于所有依赖文件,则认为目标文件是最新的,不会重新生成。
- 如果依赖文件中的任何一个被修改,则会重新执行规则生成目标文件。
默认规则:
make
默认执行Makefile中的第一条规则。- 例如,如果要生成
x.txt
,make
会先检查m.txt
是否存在,如果不存在则先生成m.txt
,然后再生成x.txt
。
4. 伪目标(Phony Target)
定义:
- 伪目标不是实际的文件名,而是用于执行特定任务的规则。
- 例如,
clean
规则通常用于删除生成的文件。
示例:
1
2clean:
rm -f m.txt x.txt- 执行
make clean
会删除m.txt
和x.txt
。
- 执行
避免冲突:
- 如果存在名为
clean
的文件,make clean
可能不会执行。 - 可以使用
.PHONY
声明伪目标:1
2
3
clean:
rm -f m.txt x.txt
- 如果存在名为
5. 执行多条命令
独立命令:
- 每条命令默认在独立的Shell环境中执行。
- 例如,
cd
命令不会影响后续命令的执行环境。
多条命令的写法:
- 使用
;
分隔命令:1
2cd_ok:
pwd; cd ..; pwd - 使用
\
换行:1
2
3
4cd_ok:
pwd; \
cd ..; \
pwd - 使用
&&
确保命令顺序执行:1
2cd_ok:
pwd && cd .. && pwd
- 使用
6. 控制命令输出
- 隐藏命令输出:
- 在命令前加
@
可以隐藏命令的打印输出,但命令仍然会执行。 - 示例:
1
2
3no_output:
@echo 'not display'
echo 'will display'
- 在命令前加
7. 错误处理
默认行为:
- 如果命令返回非0值,
make
会中断执行并报错。
- 如果命令返回非0值,
忽略错误:
- 在命令前加
-
可以忽略错误,继续执行后续命令。 - 示例:
1
2
3ignore_error:
-rm zzz.txt
echo 'ok'
- 在命令前加
8. 总结
Makefile通过定义规则来自动化生成目标文件,核心在于描述目标文件与依赖文件之间的关系以及生成目标文件的具体命令。掌握以下要点:
- 规则的基本格式:目标文件、依赖文件和命令。
- 增量编译的逻辑:基于文件的修改时间。
- 伪目标的使用:如
clean
。 - 多条命令的写法:使用
;
、\
或&&
。 - 控制命令输出:使用
@
。 - 错误处理:使用
-
忽略错误。
编译C程序 - Makefile教程笔记
1. C程序编译的基本步骤
编译C程序通常分为两步:
- 编译阶段:将每个
.c
文件编译为.o
文件(目标文件)。 - 链接阶段:将所有
.o
文件链接为最终的可执行文件。
2. 示例项目结构
假设一个简单的C项目,包含以下文件:
hello.c
:定义了一个hello
函数。hello.h
:声明了hello
函数。main.c
:主程序,调用了hello
函数。
文件内容:
- **
hello.c
**:1
2
3
4
5
6
int hello() {
printf("hello, world!\n");
return 0;
} - **
hello.h
**:1
int hello();
- **
main.c
**:1
2
3
4
5
6
7
8
9
int main() {
printf("start...\n");
hello();
printf("exit.\n");
return 0;
}
3. Makefile编写
根据上述项目结构,Makefile可以定义如下规则:
1 | # 生成可执行文件 |
4. Makefile执行逻辑
增量编译:
make
会根据文件的修改时间来判断是否需要重新编译。- 如果
hello.c
被修改,make
会重新编译hello.c
生成hello.o
,并重新链接生成world.out
。 - 如果
hello.h
被修改,make
会重新编译所有依赖hello.h
的文件(如main.c
),并重新链接生成world.out
。
执行过程:
- 初始运行
make
时,会依次执行以下步骤:- 编译
hello.c
生成hello.o
。 - 编译
main.c
生成main.o
。 - 链接
hello.o
和main.o
生成world.out
。
- 编译
- 如果修改了
hello.c
,再次运行make
时,只会重新编译hello.c
并重新链接world.out
。 - 如果修改了
hello.h
,make
会重新编译main.c
并重新链接world.out
。
- 初始运行
5. 清理规则
clean
规则:- 用于删除所有生成的文件,包括
.o
文件和可执行文件。 - 执行命令:
make clean
。 - 示例:
1
2clean:
rm -f *.o world.out
- 用于删除所有生成的文件,包括
6. 规则优化
随着项目规模的扩大,手动维护Makefile中的规则会变得繁琐。后续可以学习如何使用变量、模式规则等高级特性来简化Makefile的编写。
7. 小结
- Makefile的作用:通过定义规则,
make
可以自动化编译C程序。 - 规则的基本格式:
1
2目标文件: 依赖文件
命令 - 增量编译:
make
根据文件的修改时间来决定是否重新编译。 - 清理规则:使用
clean
规则删除生成的文件,方便重新编译。
以下是根据廖雪峰的《使用隐式规则》教程整理的笔记,主要介绍了Makefile中隐式规则的概念、使用方法以及其潜在问题。
使用隐式规则 - Makefile教程笔记
1. 隐式规则的概念
隐式规则(Implicit Rule):
- Makefile中的一种特殊规则,用于自动推导目标文件的生成规则。
- 当Makefile中没有明确定义某个目标文件的规则时,
make
会尝试使用内置的隐式规则来生成该目标文件。
内置规则的作用:
- 为了简化Makefile的编写,
make
为常见的编译任务(如C、C++、ASM等)提供了默认的隐式规则。 - 例如,对于C程序,
make
会自动应用以下隐式规则:1
2xyz.o: xyz.c
cc -c -o xyz.o xyz.c
- 为了简化Makefile的编写,
2. 示例:隐式规则的应用
假设有一个C项目,包含hello.c
、main.c
和hello.h
,目标是生成可执行文件world.out
。
项目结构:
hello.c
:定义了一个hello
函数。hello.h
:声明了hello
函数。main.c
:主程序,调用了hello
函数。
Makefile:
1 | # 只保留生成 world.out 的规则 |
执行过程:
- 执行
make
命令时,make
会自动推导出hello.o
和main.o
的生成规则:hello.o
依赖于hello.c
,使用cc -c -o hello.o hello.c
生成。main.o
依赖于main.c
,使用cc -c -o main.o main.c
生成。
- 最后,
make
会链接hello.o
和main.o
生成world.out
。
输出:
1 | $ make |
3. 隐式规则的优势
- 减少重复规则:
- 隐式规则可以减少Makefile中大量重复的编译规则。
- 例如,对于多个
.c
文件,无需为每个文件单独编写.o
文件的生成规则。
4. 隐式规则的潜在问题
- 无法跟踪头文件的修改:
- 隐式规则的一个主要问题是无法自动跟踪头文件(如
.h
文件)的修改。 - 例如,如果修改了
hello.h
,隐式规则main.o: main.c
不会自动检测到hello.h
的修改,导致main.c
不会被重新编译。 - 这可能导致生成的可执行文件中包含过时的代码。
- 隐式规则的一个主要问题是无法自动跟踪头文件(如
5. 解决隐式规则的潜在问题
手动添加依赖:
- 为了确保头文件的修改能够触发重新编译,需要手动在Makefile中添加头文件的依赖关系。
- 例如:
1
2main.o: main.c hello.h
cc -c main.c
自动生成依赖文件:
- 在实际项目中,可以通过工具(如
gcc -M
)自动生成依赖文件,并将其包含在Makefile中。 - 例如,使用
gcc -M
生成依赖文件:1
gcc -M main.c > main.d
- 然后在Makefile中包含这些依赖文件:
1
-include main.d
- 在实际项目中,可以通过工具(如
6. 小结
- 隐式规则的作用:减少重复的编译规则,简化Makefile的编写。
- 隐式规则的潜在问题:无法自动跟踪头文件的修改,可能导致生成的可执行文件包含过时代码。
- 解决方法:手动添加头文件依赖或使用工具自动生成依赖文件。
以下是根据廖雪峰的《使用变量》教程整理的笔记,主要介绍了Makefile中变量的使用方法、内置变量和自动变量的概念。
使用变量 - Makefile教程笔记
1. 变量的作用
- 解决重复问题:
- 在Makefile中,文件名或命令可能会重复出现多次,手动修改容易出错。
- 使用变量可以简化Makefile的编写,提高可维护性。
2. 定义和使用变量
定义变量:
- 使用
变量名 = 值
或变量名 := 值
定义变量。 - 通常变量名使用全大写,例如
TARGET
、OBJS
等。 - 示例:
1
2TARGET = world.out
OBJS = hello.o main.o
- 使用
引用变量:
- 使用
$(变量名)
引用变量。 - 示例:
1
2$(TARGET): $(OBJS)
cc -o $(TARGET) $(OBJS)
- 使用
3. 动态生成变量
使用
wildcard
函数:wildcard
函数可以匹配当前目录下的文件模式。- 示例:
$(wildcard *.c)
会列出当前目录下所有.c
文件。 - 示例:
1
OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
使用
patsubst
函数:patsubst
函数用于模式替换。- 示例:
$(patsubst %.c,%.o,$(wildcard *.c))
会将所有.c
文件名替换为.o
文件名。 - 这样,每当添加新的
.c
文件时,OBJS
变量会自动更新,无需手动修改Makefile。
4. 内置变量
内置变量:
make
提供了一些内置变量,例如$(CC)
表示C编译器,默认值是cc
。- 示例:
1
2$(TARGET): $(OBJS)
$(CC) -o $(TARGET) $(OBJS)
修改内置变量:
- 可以重新定义内置变量的值。
- 示例:使用交叉编译器时,可以设置
CC = riscv64-linux-gnu-gcc
。
5. 自动变量
自动变量:
- 在规则中自动指向特定值的变量。
- 常用的自动变量:
$@
:目标文件名。$<
:依赖列表中的第一个文件。$^
:所有依赖文件。- 示例:
1
2
3
4
5world.out: hello.o main.o
@echo '$$@ = $@' # 目标文件名
@echo '$$< = $<' # 第一个依赖文件
@echo '$$^ = $^' # 所有依赖文件
$(CC) -o $@ $^
输出示例:
1
2
3
4$@ = world.out
$< = hello.o
$^ = hello.o main.o
$(CC) -o world.out hello.o main.o
6. 变量的调试
- 打印变量:
- 使用
@echo
打印变量的值,便于调试。 - 示例:
1
2
3
4
5world.out: hello.o main.o
@echo '$$@ = $@'
@echo '$$< = $<'
@echo '$$^ = $^'
$(CC) -o $@ $^
- 使用
7. 小结
- 变量的作用:简化Makefile的编写,减少重复,提高可维护性。
- 变量的定义和引用:使用
变量名 = 值
定义,使用$(变量名)
引用。 - 动态生成变量:使用
wildcard
和patsubst
函数自动生成文件列表。 - 内置变量:使用内置变量(如
$(CC)
)简化命令。 - 自动变量:使用自动变量(如
$@
、$<
、$^
)简化规则。