如果需要的文件是存在于不同的路径下(即源文件与 Makefile 文件不在同一个路径下),在编译的时候就用到了 Makefile 中为我们提供的目录搜索文件的功能。
常见的搜索的方法的主要有两种:
- VPATH 是变量,使用时需要指定文件的路径,是搜索路径下所有的文件;
- vpath 是关键字,按照模式搜索,也可以说成是选择搜索。搜索的时候不仅需要加上文件的路径,还需要加上相应限制的条件;
在 Makefile 中可以这样写:
VPATH := dir
我们可以这样理解,把 dir 的值赋值给变量 VPATH,所以在执行 make 的时候会从 dir 目录下找我们需要的文件。
当存在多个路径的时候我们可以这样写:
VPATH := dir dir2
或者是
VPATH := dir:dir2
多个路径之间要使用空格或者是冒号隔开,表示在多个路径下搜索文件。搜索的顺序为我们书写时的顺序,应该先搜索 dir 目录下的文件,再搜索 dir2 目录下的文件。
注意:无论定义了多少路径,make 执行的时候会先搜索当前路径下的文件,当前目录下没有我们要找的文件,才去 VPATH 的路径中去寻找。如果当前目录下有我们要使用的文件,那么 make 就会使用我们当前目录下的文件。
代码目录结构如下:
wohu@ubuntu:~/cpp/demo$ tree
.
├── header
│ └── name.h
├── Makefile
└── source
├── main.cpp
└── name.cpp
2 directories, 4 files
wohu@ubuntu:~/cpp/demo$
Makefile 放在 header 和 source 目录同级目录。编写 Makefile 如下:
main: main.o name.o
g++ main.o name.o -o main
执行 make 会报错:
make: *** No rule to make target 'main.o', needed by 'main'. Stop.
重建最终目标文件 main 的时候我们需要 main.o 文件,但是没有找到 main.o 文件,这是错误的根本原因。
加上路径搜索
VPATH = source header
main: main.o name.o
g++ main.o name.o -o main
输入 make 即可正常运行
wohu@ubuntu:~/cpp/demo$ make
g++ -c -o main.o source/main.cpp
g++ -c -o name.o source/name.cpp
g++ main.o name.o -o main
wohu@ubuntu:~/cpp/demo$
具体用法:
vpath PATTERN DIRECTORIES
vpath PATTERN
vpath
其中:
- PATTERN:为要寻找的条件;
- DIRECTORIES:寻找的路径;
首先是用法一,命令格式如下:
vpath main.cpp dir
可以这样理解,在 dir 路径下搜索文件 main.cpp。多路径的书写规则如下:
vpath main.cpp dir dir2 或者是 vpath main.cpp dir : dir2
多路径的用法其实和 VPATH 差不多,都是使用空格或者是冒号分隔开,搜索路径的顺序是先 dir 目录,然后是 dir2 目录。
其次是用法二,命令格式如下:
vpath main.cpp
用法二的意思是清除符合文件 main.cpp 的搜索目录。
最后是用法三,命令格式如下:
vpath
vpath 单独使的意思是清除所有已被设置的文件搜索路径。
另外在使用 vpath 的时候,搜索的条件中可以包含模式字符 %,这个符号的作用是匹配一个或者是多个字符,例如 %.cpp表示搜索路径下所有的 .cpp 结尾的文件。如果搜索条件中没有包含 % ,那么搜索的文件就是具体的文件名称。
使用 VPATH 的情况是前路径下的文件较少,或者是搜索的文件不能使用通配符表示,这些情况下使用 VPATH 最好。
如果存在某个路径的文件特别的多或者是可以使用通配符表示的时候,就不建议使用 VPATH 这种方法,为什么呢?
因为 VPATH 在去搜索文件的时没有限制条件,所以它回去检索这个目录下的所有文件,每一个文件都会进行对比,搜索和我们目录名相同的文件,不仅速度会很慢,而且效率会很低。
我们在这种情况下就可以使用 vpath 搜索,它包含搜索条件的限制,搜索的时候只会从我们规定的条件中搜索目标,过滤掉不符合条件的文件,当然查找的时候也会比较的快。
使用示例:
vpath %.cpp source
main: main.o name.o
g++ main.o name.o -o main
所谓的隐含规则就是系统自动完成一些动作。在某些时候其实不需要给出重建目标文件的命令,有的甚至可以不需要给出规则。
例如:
vpath %.cpp source
main: main.o name.o
g++ main.o name.o -o main
执行 make 后,输出结果:
wohu@ubuntu:~/cpp/demo$ make
g++ -c -o main.o source/main.cpp
g++ -c -o name.o source/name.cpp
g++ main.o name.o -o main
wohu@ubuntu:~/cpp/demo$
系统自动执行了一下两行语句
g++ -c -o main.o source/main.cpp
g++ -c -o name.o source/name.cpp
注意:隐含条件只能省略中间目标文件重建的命令和规则,但是最终目标的命令和规则不能省略。
下面这个 makefile 是和上面等价的。
vpath %.cpp source
main: main.o name.o
g++ main.o name.o -o main
main.o: main.cpp
g++ -c ./source/main.cpp -o main.o
name.o: name.cpp
g++ -c ./source/name.cpp -o name.o
执行 make 后,输出结果:
wohu@ubuntu:~/cpp/demo$ make
g++ -c ./source/main.cpp -o main.o
g++ -c ./source/name.cpp -o name.o
g++ main.o name.o -o main
wohu@ubuntu:~/cpp/demo$
需要注意的是:通过 VPATH/vpath 告知文件搜寻路径是告知的是 make,这利于它隐式推导时的文件搜索,而不是告知的 gcc,所以还是得通过 -I 指定 gcc 预编译时头文件搜索路径。
书写 Makefile 可以直接写 shell 时用这些变量。下面只列出一些 C/C++ 相关的:
变量名 | 含义 |
---|---|
RM | rm -f |
AR | ar |
CC | cc |
CXX | g++ |
示例:
all:
@echo $(RM)
@echo $(AR)
@echo $(CC)
@echo $(CXX)
执行 make, 显示各个变量的值
wohu@ubuntu:~/cpp/func$ make
rm -f
ar
cc
g++
wohu@ubuntu:~/cpp/func$
变量名 | 含义 |
---|---|
ARFLAGS | AR命令的参数 |
CFLAGS | C语言编译器的参数 |
CXXFLAGS | C++语言编译器的参数 |
示例: 下面以 CXXFLAGS 为例演示
#include
int main(int argc, char *argv[])
{
std::cout << "hello,world" << std::endl;
return 0;
}
Makefile 内容
CXXFLAGS += -Wall
main: demo.o
$(CXX) demo.o -o main
执行 make
wohu@ubuntu:~/cpp/func$ make
g++ -Wall -c -o demo.o demo.cpp <-- 这个是隐式规则自动推导的
g++ demo.o -o main
wohu@ubuntu:~/cpp/func$