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

make 中的路径搜索(十二)_第1张图片

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

make 中的路径搜索(十二)_第2张图片

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

图片.png

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

make 中的路径搜索(十二)_第3张图片

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

make 中的路径搜索(十二)_第4张图片

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

OBJS := func.o main.o
INC := inc
SRC := src
VPATH := $(INC) $(SRC)

hello.out : $(OBJS)
    @gcc -o $@ $^
    @echo "Target File ==> $@"

$(OBJS) : %.o : %.c func.h
    @gcc -o $@ -c $^

        我们来编译试试看

图片.png

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

make 中的路径搜索(十二)_第5张图片

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

make 中的路径搜索(十二)_第6张图片

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

make 中的路径搜索(十二)_第7张图片

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

make 中的路径搜索(十二)_第8张图片

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

make 中的路径搜索(十二)_第9张图片

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

图片.png

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

OBJS := func.o main.o
INC := inc
SRC := src
CFLAGS := -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 $<

        编译结果如下

图片.png

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

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

make 中的路径搜索(十二)_第10张图片

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

make 中的路径搜索(十二)_第11张图片

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

VPATH := src1
CFLAGS := -I inc

vpath %.c src2
vpath %.h inc

app.out : func.o main.o
    @gcc -o $@ $^
    @echo "Target File ==> $@"

$(OBJS) : %.o : %.c func.h
    @gcc $(CFLAGS) -o $@ -c $<

        编译结果如下

make 中的路径搜索(十二)_第12张图片

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

make 中的路径搜索(十二)_第13张图片

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

图片.png

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

make 中的路径搜索(十二)_第14张图片

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

make 中的路径搜索(十二)_第15张图片

        我们来编译看看结果

make 中的路径搜索(十二)_第16张图片

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

图片.png

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

make 中的路径搜索(十二)_第17张图片

        我们来看看编译结果

make 中的路径搜索(十二)_第18张图片

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

图片.png

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

make 中的路径搜索(十二)_第19张图片

        我们看到重新生成了一个 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 := src
PATH := src
CFLAGS := -I inc

app.out : func.o main.o
    @gcc -o $@ $^
    @echo "Target File ==> $@"

 %.o : %.c inc/func.h
    @gcc $(CFLAGS) -o $@ -c $<

        编译结果如下        

make 中的路径搜索(十二)_第20张图片

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


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