千家信息网

make 中的路径搜索(十二)

发表于:2024-11-27 作者:千家信息网编辑
千家信息网最后更新 2024年11月27日,我们在实际的工程项目中,所有的源文件和头文件都放在同一个文件夹中吗?如果是比较小的项目,当然是可以的。但如果是成千上万的源文件,当然必须得分开存放。常用的源码管理方式如下那么下面的 makefile
千家信息网最后更新 2024年11月27日make 中的路径搜索(十二)

我们在实际的工程项目中,所有的源文件和头文件都放在同一个文件夹中吗?如果是比较小的项目,当然是可以的。但如果是成千上万的源文件,当然必须得分开存放。常用的源码管理方式如下

那么下面的 makefile 能够编译成功吗?

我们来试试,编译结果如下

那么结果肯定会是这样的,因为我们在 makefile 中就没有指定路径,它在当前目录下没有找到源文件。接下来我们就得来介绍一个特殊的预定义变量 VPATH (全大写),VAPTH 变量的值用于指示 make 如何查找文件,不同文件夹可作为 VPATH 的值同时出现,文件夹的名字之间需要使用分隔符进行区分。格式如下

make 对于 VAPTH 值的处理方式:1、当前文件夹找不到需要的文件时,VAPTH 会被使用;2、make 会在 VAPTH 指定的文件夹中依次搜索文件;3、当多个文件夹存在同名文件时,选择第一次搜索到的文件。注意事项:1、VAPTH 只能决定 make 的搜索路径,无法决定命令的搜素路径;2、对于特定的编译命令(gcc),需要独立指定编译搜索路径。如下

下来我们还是以代码为例来进行说明,在上面的 makefile 基础上进行改写

OBJS := func.o main.oINC := incSRC := srcVPATH := $(INC) $(SRC)hello.out : $(OBJS)    @gcc -o $@ $^    @echo "Target File ==> $@"$(OBJS) : %.o : %.c func.h    @gcc -o $@ -c $^

我们来编译试试看

我么你看到编译还是报错,原因就是我们在 gcc 的时候依赖文件中还包含有 func.h,下来我们将最后一行的 $^ 改为 $<。再来试试看

我们在编译的时候,编译器又报错了,说是找不到 func.h。我们的 func.h 文件在 inc 文件中放着,编译器并不知道去这个文件夹下找,只是在当前文件下进行查找。现在就用到了我们上面说的为特定的编译命令(gcc)独立的指定编译路径。加上 CFLAGS := -I $(INC),并将 CFLAGS 变量加在 gcc 编译的命令中。再来看看编译结果

我们看到已经正确编译了,并且可执行程序也完美运行。看似问题完美的解决了,但其实 VPATH 也存在一定的问题,当 inc 文件夹中意外出现源文件(c/cpp 文件),那么可能就会产生编译错误。我们如果将 func.c 误放在 inc 文件夹中,这个 func.c 文件中打印的是 this is from inc ... ,看看会发生什么

我们看到输出的竟然会是 inc 文件中的 func.c 的内容,按照预想应该是 hello makefile 啊。那么这时的解决方案便是利用 vpath 关键字(全小写),它是为不同类型的文件指定不同的搜索路径。语法是在 Directory 中搜索符合 Pattern 的规则的格式,格式如下


我们再次用 vpath 来进行设置,看看编译结果是怎样的

我们看到已经正确实现了。那么既然有设置这个规则,是否也可以有取消此规则的设置呢?当然有了。在进行某个具体搜索规则的取消时,是 vpath Pattern(如下图);取消所有的设置规则时,直接 vpath 即可

我们来试试,直接在下面加上 vpath %.h,代码如下

OBJS := func.o main.oINC := incSRC := srcCFLAGS := -I $(INC)vpath %.h $(INC)vpath %.c $(SRC)hello.out : $(OBJS)    @gcc -o $@ $^    @echo "Target File ==> $@"vpath %.h$(OBJS) : %.o : %.c func.h    @gcc $(CFLAGS) -o $@ -c $<

编译结果如下

我们看到编译标错了,说找不到 func.h 头文件啦。当然找不到了,我们取消了 .h 头文件的搜索路径了。

下来我们来看看如果当 VPATH 和 vpath 同时出现时,make 会如何处理呢?如下

下面的项目中,会选择哪一个文件夹进行编译?

我们还是以代码为例来进行编译看看

VPATH := src1CFLAGS := -I incvpath %.c src2vpath %.h incapp.out : func.o main.o    @gcc -o $@ $^    @echo "Target File ==> $@"$(OBJS) : %.o : %.c func.h    @gcc $(CFLAGS) -o $@ -c $<

编译结果如下

我们看到它是按照 vpath 关键字指定的路径下进行编译的。我们如果将 src2 文件夹中的 func.c 换成 func.cpp 呢,看看结果


我们看到:make 首先在当前文件夹搜索需要的文件,如果失败的话,make 优先在 vpath 指定的文件夹中搜索目标文件;当 vpath 搜索失败时,转而搜索 VPATH 指定的文件夹。如下

下来我们看看当使用 vpath 对同一个 Pattern 指定多个文件夹时,make 会如何处理?如下

下面的项目中,会选择哪一个文件夹进行编译?

我们来编译看看结果

我们看到编译的是第一个文件夹 src1 。make 首先在当前文件夹搜索需要的文件,如果失败:make 以自上而下的顺序搜索 vpath 指定的文件夹,当找到目标文件,搜索结束。如下

下面来看看通过 VPATH 指定搜索路径后,make 如何决定目标文件的最终位置?下面的项目中,会选择哪一个文件夹进行编译?

我们来看看编译结果

我们看到生成可执行程序 app.out。并且成功运行。下来我们将 app.out 放入 src 文件夹中,重新 make,编译器应该是重新生成一个 app.out

它提示 app.out 是最新的,为什么没有重新生成 app.out 呢?仔细分析下,在当前文件夹下没找到,但是编译器不死心,继续在 VPATH 变量指定的路径下进行寻找,终于在 src 文件夹下找到了 app.out,所以直接就不更新了。那么我们在 func.c 源文件中改变输出的字符串为 hello D.T.Software 呢?看看结果

我们看到重新生成了一个 app.out,在 src 文件夹下的 app.out 是没有改变的。但是在当前目录下又重新生成了一个 app.out 可执行程序。当 app.out 完全不存在时,make 在当前文件下创建 app.out。当 src 文件夹下存在 app.out 时,所有目标和依赖的新旧关系不变,make 不会重新创建 app.out;当依赖文件被更新,make 在当前文件夹下创建 app.out。

那么问题就来了,当依赖改变时,如何使得 src 下的 app.out 也被更新呢?解决方案便是使用 GPATH 特殊变量指定目标文件夹;GPATH := src ,当 app.out 完全不存在时,make 默认在当前文件夹创建 app.out;当 app.out 存在于 src,且依赖文件被更新,make 在 src 中创建 app.out。下来还是以代码为例来进行说明

GPATH := srcPATH := srcCFLAGS := -I incapp.out : func.o main.o    @gcc -o $@ $^    @echo "Target File ==> $@" %.o : %.c inc/func.h    @gcc $(CFLAGS) -o $@ -c $<

编译结果如下

在工程项目中:1、尽量使用 vpath 为不同文件指定搜索路径;2、不要在源码文件夹中生成目标文件;3、为编译得到的结果创建独立的文件夹;4、避免 VPATH 和 GPATH 特殊变量的使用。 通过对 make 中的路径搜索的学习,总结如下:1、VPATH 变量用于指示 make 如何查找文件;2、make 会在 VPATH 指定的文件夹中依次搜索文件;3、vpath 关键字可以为不同类型的文件指定不同的搜索路径;4、vpath 比 VPATH 更灵活易用,可动态设置/取消搜索路径。


欢迎大家一起来学习 makefile,可以加我QQ:243343083

0