本文主要是makefile——文件搜索相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
| Windows | windows11 |
| Ubuntu | Ubuntu16.04的64位版本 |
| VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
| SecureCRT | Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日 |
| 开发板 | 正点原子 i.MX6ULL Linux阿尔法开发板 |
| uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
| linux内核 | linux-4.15(NXP官方提供) |
| STM32开发板 | 正点原子战舰V3(STM32F103ZET6) |
点击查看本文参考资料
点击查看相关文件下载
点击查看文件搜索相关测试文件
各文件所在目录的结构如下:
1 2 3 4 5 6 7 8 9
| . ├── main.c ├── Makefile ├── test1.c ├── test1.h ├── test2.c └── test2.h
0 directories, 6 files
|
1 2 3 4 5 6 7 8 9 10 11
| #include <stdio.h> #include "test1.h" #include "test2.h"
int main(int argc, const char *argv[]) { printf("This is main file!\n"); test1Fun(); test2Fun(); return 0; }
|
1 2 3 4 5 6
| #include <stdio.h>
void test1Fun() { printf("This is test1.c file\n"); }
|
1 2 3 4 5 6
| #include <stdio.h>
void test2Fun() { printf("This is test2.c file\n"); }
|
1 2 3 4 5 6
| #ifndef __TEST1_H__ #define __TEST1_H__
void test1Fun();
#endif
|
1 2 3 4 5 6
| #ifndef __TEST2_H__ #define __TEST2_H__
void test2Fun();
#endif
|
一、源文件搜索
一般来说,一个大的工程下有大量的源文件,我们通常会把源文件分类,并存放在不同的目录中,便于查找和区分。所以,当 make 需要去找寻文件的依赖关系时,我们可以把一个路径告诉 make ,让 make 自动去找。
常见的搜索的方法的主要有两种:一般搜索 VPATH 和选择搜索 vpath 。表面上两者只是大小写的区别,其实两者在本质上也是不同的。
【注意】 VPATH 与 vpath 仅仅是对于 Makefile 来说搜索目标和依赖文件的路径,但是对于命令行来说是无效的,也就是说,在执行 g++ 或者 gcc 时不会自动从 VPATH 或者 vpath 中自动搜索要包含的头文件等信息文件,这个时候就需要在命令行的命令中添加 -I 参数,以便于 gcc 等编译器寻找头文件。
1. VPATH
1.1使用格式
VPATH 变量会告诉 make 去哪里寻找相应的文件,如果没有指明这个变量, make 只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么, make 就会在当前目录找不到的时候,到所指定的目录中去找寻文件了。一般使用格式如下:
1 2 3 4 5 6 7
| VPATH := src
VPATH := src1 ../src2
VPATH := src1 : ../src2
|
上边多个路径的写法指明了两个目录, src1 和 ../src2 。
【注意】
(1)当前目录永远是最高优先搜索的地方。如果当前目录下有我们要使用的文件,那么 make 就会使用我们当前目录下的文件。
(2)多个路径之间要使用空格或者是冒号隔开,表示在多个路径下搜索文件,搜索的顺序为我们书写时的顺序。
1.2使用实例
我们使用上边的文件搜索相关测试文件来测试,我们新建两个目录,并将两个相关文件移动到指定目录:
1 2 3
| mkdir test1 test2 mv test1.c test1 mv test2.c test2
|
所以现在的目录结构为:
1 2 3 4 5 6 7 8 9 10 11
| . ├── main.c ├── Makefile ├── test1 │ └── test1.c ├── test1.h ├── test2 │ └── test2.c └── test2.h
2 directories, 6 files
|
1 2 3 4 5 6 7 8 9 10 11
| OBJ := main.o test1.o test2.o
main: $(OBJ) gcc $(OBJ) -o main
%.o: %.c gcc -c $< -o $@
.PHONY: clean clean: rm -rf *.o main
|
此时不出意外的话,我们会得到一条这样的报错信息:
1 2
| gcc -c main.c -o main.o make: *** No rule to make target 'test1.o', needed by 'main'。 停止。
|
1 2 3 4 5 6 7 8 9 10 11
| OBJ := main.o test1.o test2.o VPATH := test1 test2 main: $(OBJ) gcc $(OBJ) -o main
%.o: %.c gcc -c $< -o $@
.PHONY: clean clean: rm -rf *.o main
|
此时我们再执行 make 命令,会得到如下的信息提示:
1 2 3
| gcc -c test1/test1.c -o test1.o gcc -c test2/test2.c -o test2.o gcc main.o test1.o test2.o -o main
|
可以发现,我们正常编译了所有文件,总的来说当 .c 源文件都位于 test1 和 test2 目录下的时候, VPATH 就成了必要了。
2. vpath
2.1使用格式
vpath 关键字它可以指定不同的文件在不同的搜索目录中。它有三种使用方法:
2.1.1设置搜索路径
1 2 3 4 5 6 7
| vpath <pattern> <dir>
vpath <pattern> <dir1> <dir2>
vpath <pattern> <dir1> : <dir2>
|
为符合模式 的文件指定搜索目录 。例如,
1
| vpath test.c src # 在 src 路径下搜索文件 test.c
|
【说明】
(1) vapth 使用方法中的可以包含 % 字符。 % 的意思是匹配零或若干字符。例如,
该语句表示,要求 make 在 ../headers 目录下搜索所有以 .h 结尾的文件。(如果某文件在当前目录没有找到的话)。
(2)可以连续地使用 vpath 语句,以指定不同搜索策略。如果连续的 vpath 语句中出现了相同的 <pattern> ,或是被重复了的 <pattern> ,那么, make 会按照 vpath 语句的先后顺序来执行搜索。例如,
1 2 3
| vpath %.c foo vpath % blish vpath %.c bar
|
表示搜索 .c 结尾的文件,先在 foo 目录,然后是 blish ,最后是 bar 目录搜索 .c 结尾的文件。
(3)还有一种搜索的形式
1 2
| vpath %.c foo:bar vpath % blish
|
表示搜索 .c 结尾的文件,先在 foo 目录,然后是 bar 目录,最后才是 blish 目录。
2.1.2清除搜索目录
清除符合模式 的文件的搜索目录。例如,
清除所有已被设置好了的文件搜索目录。
2.2使用实例
我们使用上边的文件搜索相关测试文件来测试,我们新建两个目录,并将两个相关文件移动到指定目录:
1 2 3
| mkdir test1 test2 mv test1.c test1 mv test2.c test2
|
所以现在的目录结构为:
1 2 3 4 5 6 7 8 9 10 11
| . ├── main.c ├── Makefile ├── test1 │ └── test1.c ├── test1.h ├── test2 │ └── test2.c └── test2.h
2 directories, 6 files
|
我们编写 Makefile 内容如下:
1 2 3 4 5 6 7 8 9 10 11 12
| OBJ = main.o test1.o test2.o vpath %.c test1 test2
main: $(OBJ) gcc $(OBJ) -o main
%.o: %.c gcc -c $< -o $@
.PHONY: clean clean: rm -rf *.o main
|
这样,在进行编译的时候 make 就会到设置的路径下寻找源文件。
二、头文件搜索
1.使用格式
我们并不会把所有的头文件都跟 Makefile 文件放在一个目录下,当我们的头文件和调用这个头文件的源文件以及 Makefile 不在同一个目录怎么办呢,他们有可能分布在不同的好几个目录,这个时候我们设置的 VPATH 或者是 vpath 是无法查找 .h 文件的,这时候我们就要靠 gcc 的 -I 参数啦。一般格式如下:
1
| gcc filename.c -o filename -I h_dir1 -I h_dir2 -I h_dir3 ...
|
- h_dir 就表示相关头文件路径,可以跟多个,用空格分开,但是每个目录都要有 -I 这个参数。
2.使用实例
我们使用上边的文件搜索相关测试文件来测试,我们新建两个目录,并将两个相关文件移动到指定目录:
1 2 3 4
| mkdir test1 test2 user mv test1.c test1.h test1 mv test2.c test2.h test2 mv main.c user
|
所以现在的目录结构为:
1 2 3 4 5 6 7 8 9 10 11 12
| . ├── Makefile ├── test1 │ ├── test1.c │ └── test1.h ├── test2 │ ├── test2.c │ └── test2.h └── user └── main.c
3 directories, 6 files
|
我们编写 Makefile 内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| OBJ := main.o test1.o test2.o
INCLUDE := -I test1 -I test2
vpath %.c user test1 test2
main: $(OBJ) gcc $(OBJ) -o main $(INCLUDE)
%.o: %.c gcc -c $< -o $@ $(INCLUDE)
.PHONY: clean clean: rm -rf *.o main
|
这样,在进行编译的时候 make 就会到设置的路径下寻找源文件,编译的时候也会到指定目录下寻找头文件,我们执行 make 命令的话,会有如下信息提示:
1 2 3 4
| gcc -c user/main.c -o main.o -I test1 -I test2 gcc -c test1/test1.c -o test1.o -I test1 -I test2 gcc -c test2/test2.c -o test2.o -I test1 -I test2 gcc main.o test1.o test2.o -o main -I test1 -I test2
|
三、生成文件路径
1.指定生成文件路径
我们每次生成的一大堆的文件都是存放在当前目录下的,文件多了之后,就会看起来很乱,我们能否指定生成文件路径呢?当然是可以的啦。这里直接以实例来说明。
1.1文件准备
首先我们新建一个 obj 文件夹用于存放生成的 .o 中间文件。
我们使用上边的文件搜索相关测试文件来测试,我们新建两个目录,并将两个相关文件移动到指定目录:
1 2 3 4
| mkdir test1 test2 user mv test1.c test1.h test1 mv test2.c test2.h test2 mv main.c user
|
所以现在的目录结构为:
1 2 3 4 5 6 7 8 9 10 11 12 13
| . ├── Makefile ├── obj ├── test1 │ ├── test1.c │ └── test1.h ├── test2 │ ├── test2.c │ └── test2.h └── user └── main.c
4 directories, 6 files
|
1.2两次试错
1.2.1测试1
我们编写 Makefile 内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| OBJ := main.o test1.o test2.o
INCLUDE := -I test1 -I test2 OBJDIR := ./obj vpath %.c user test1 test2
main: $(OBJ) gcc $(OBJ) -o main $(INCLUDE)
$(OBJ):$(OBJDIR)/%.o: %.c gcc -c $< -o $@ $(INCLUDE)
print: @echo "OBJ = $(OBJ)" .PHONY: clean clean: rm -rf $(OBJDIR)/*.o main
|
然后我们执行 make 命令,会发现报了下边的错误:
1 2 3 4 5 6 7 8
| Makefile:11: target 'main.o' doesn't match the target pattern Makefile:11: target 'test1.o' doesn't match the target pattern Makefile:11: target 'test2.o' doesn't match the target pattern gcc -c -o main.o -I test1 -I test2 gcc: fatal error: no input files compilation terminated. Makefile:12: recipe for target 'main.o' failed make: *** [main.o] Error 1
|
我们看报错的说明,就是生成 main 的时候,找不到它所依赖的 main.o 、 test1.o 和 test2.o ,并且在模式规则的匹配中,匹配也会有问题,我们来分析一下,我们拆开上边的模式规则,并将变量进行替换,可以得到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| OBJ := main.o test1.o test2.o
INCLUDE := -I test1 -I test2 OBJDIR := ./obj vpath %.c user test1 test2
main: main.o test1.o test2.o gcc main.o test1.o test2.o -o main $(INCLUDE)
main.o:./obj/%.o:%.c gcc -c $< -o $@ $(INCLUDE)
test1.o:./obj/%.o:%.c gcc -c $< -o $@ $(INCLUDE)
test2.o:./obj/%.o:%.c gcc -c $< -o $@ $(INCLUDE)
.PHONY: clean clean: rm -rf $(OBJDIR)/*.o main
|
我们会发现, % 匹配的时候,前边的目标文件格式与后边匹配的不太一致,并且我们生成的 .o 文件是存放在 obj 目录下的, gcc 编译的时候是找不到这几个文件的。
1.2.2测试2
我们可以修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| OBJ := main.o test1.o test2.o
INCLUDE := -I test1 -I test2 OBJDIR := ./obj vpath %.c user test1 test2
main: ./obj/main.o ./obj/test1.o ./obj/test2.o gcc ./obj/main.o ./obj/test1.o ./obj/test2.o -o main $(INCLUDE)
./obj/main.o:./obj/%.o:%.c gcc -c $< -o $@ $(INCLUDE)
./obj/test1.o:./obj/%.o:%.c gcc -c $< -o $@ $(INCLUDE)
./obj/test2.o:./obj/%.o:%.c gcc -c $< -o $@ $(INCLUDE)
.PHONY: clean clean: rm -rf $(OBJDIR)/*.o main
|
然后我们再执行 make 命令,会发现有如下提示:
1 2 3 4
| gcc -c user/main.c -o obj/main.o -I test1 -I test2 gcc -c test1/test1.c -o obj/test1.o -I test1 -I test2 gcc -c test2/test2.c -o obj/test2.o -I test1 -I test2 gcc ./obj/main.o ./obj/test1.o ./obj/test2.o -o main -I test1 -I test2
|
此时文件结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| . ├── main ├── Makefile ├── obj │ ├── main.o │ ├── test1.o │ └── test2.o ├── test1 │ ├── test1.c │ └── test1.h ├── test2 │ ├── test2.c │ └── test2.h └── user └── main.c
|
发现,生成的中间文件全部进入了 obj 目录。
1.3正确的格式
经过前边的试错,我们可以将 Makefile 写成如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| INCLUDE := -I test1 -I test2 OBJDIR := ./obj OBJ := $(OBJDIR)/main.o $(OBJDIR)/test1.o $(OBJDIR)/test2.o TARGET := main vpath %.c user test1 test2
$(TARGET): $(OBJ) gcc $(OBJ) -o $@ $(INCLUDE)
$(OBJ):$(OBJDIR)/%.o:%.c gcc -c $< -o $@ $(INCLUDE)
.PHONY: clean clean: rm -rf $(OBJDIR)/*.o main
|
这样的 Makefile 就简洁了很多了。
2.最终目标路径
上边我们已经把中间文件的路径设置过了,那还有最终生成的目标文件的,但其实也没什么必要,毕竟最终生成的文件只有一个,也不会很乱,不过这里还是记录一下吧,我们只需要在写目标文件的时候加上路径就可以了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| INCLUDE := -I test1 -I test2 OBJDIR := ./obj OBJ := $(OBJDIR)/main.o $(OBJDIR)/test1.o $(OBJDIR)/test2.o TARGET := ./bin/main vpath %.c user test1 test2
$(TARGET): $(OBJ) gcc $(OBJ) -o $@ $(INCLUDE)
$(OBJ):$(OBJDIR)/%.o:%.c gcc -c $< -o $@ $(INCLUDE)
.PHONY: clean clean: rm -rf $(OBJDIR)/*.o main
|
这样我们最终生成目标文件为 main ,并且,它将会被存放到 ./bin 目录下。