makefile笔记

最近在看makefile相关内容,写这篇文章记录总结最近的学习内容。

首先创建测试目录makefileTest以及相关文件:

makefile中比较重要的内容就是依赖目标生成规则,指明了要生成的目标以及如何生成,指定好以后makefile便会根据指定的规则去做。

编写第一个版本的makefile,写出每个生成目标及其依赖规则。由于刚安装centos7,所以显示g++用不了,搜索进行安装,发现刚安装好的系统处于安全性考虑是没有联网的,导致获取安装包失败,那首先联网吧。网上搜索,发现需要修改系统的配置文件,进入以下目录:

cd /etc/sysconfig/network-scripts/

 

找到配置文件名,此处是ifcfg-ens33,打开此文件将ONBOOT=no修改为yes,重新启动系统,发现已经可以联网下载安装包。

使用命令 yum install gcc-c++ 下载G++安装包,这一步操作需要使用su - 命令切换到root用户获取权限进行操作。

写出了第一个版本的makefile:

1 testobj:main.o student.o dog.o family.o
  2     g++ -o testobj main.o student.o dog.o family.o
  3 main.o:main.cpp student.h dog.h family.h
  4     g++ -c main.cpp
  5 student.o:student.cpp student.h
  6     g++ -c student.cpp
  7 dog.o:dog.cpp dog.h
  8     g++ -c dog.cpp
  9 family.o:family.cpp family.h
 10     g++ -c family.cpp
 11 
 12 clean:
 13     rm -rf testobj *.o

生成的最终可执行目标文件是testobj,依赖于所有的.o文件,在写最终目标生成规则的时候,需要指定最终可执行文件名以及依赖的文件名,在生成.o文件的时候,只需要指出依赖的文件名即可,makefile会自动生成与源文件同名的.o文件。

在终端执行make,输出如下:

可以看到make将执行的命令回显在终端,在当前目录下生成了源文件同名的.o文件以及终极目标,执行此目标文件可以看到测试输出:

 

虽然这个简单版本的makefile能够正确的编译,但是有许多地方出现了同名的文件列表重复,例如第一个依赖规则中的.o文件,如果添加一个源文件,就需要修改很多地方的文件列表,很麻烦。在makefile中可以使用变量,因此可以将重复的部份用变量表示,在使用的地方引用这个变量即可。修改如下:

1 OBJECTS:=main.o student.o dog.o family.o
  2 
  3 testobj:$(OBJECTS)
  4     g++ -o testobj $(OBJECTS)
  5 main.o:main.cpp student.h dog.h family.h
  6     g++ -c main.cpp
  7 student.o:student.cpp student.h
  8     g++ -c student.cpp
  9 dog.o:dog.cpp dog.h
 10     g++ -c dog.cpp
 11 family.o:family.cpp family.h
 12     g++ -c family.cpp
 13 
 14 clean:
 15     rm -rf testobj *.o

可以看到将所有的.o文件用变量OBJECTS来表示,使用的时候用$(OBJECTS)来引用即可。

接下来考虑makefile中每添加一个源文件就需要写出其生成规则,也是很麻烦的,那么可以考虑使用make的隐含生成规则,只写出目标.o文件依赖的.h文件,让make自己去推导生成规则找对应的源文件,修改如下:

 OBJECTS:=main.o student.o dog.o family.o
  2 
  3 testobj:$(OBJECTS)
  4     g++ -o testobj $(OBJECTS)
  5 main.o:student.h dog.h family.h
  6 student.o:student.h
  7 dog.o:dog.h
  8 family.o:family.h
  9 
 10 clean:
 11     rm -rf testobj *.o

至此,看着makefile变得简单多了,那么每添加一个源文件,只需要在OBJECTS变量中添加对应的.o文件,写出对应的.o文件的依赖规则即可。

到这里,还是觉得一个文件的添加就需要改东西很麻烦,这些依赖规则能不能交给强大的make来自己实现呢?当然可以,于是接下来就要使用makefile的自动生成依赖规则了,到这里好喜欢makefile了。在开始之前需要学习几个系统函数,因为接下来会用到这些函数。

(1)wildcard

  用法:$(wildcard PATTREN...)      输出与给定模式匹配的所有文件列表

       那么我们可以用此函数获取当前目录下的所有源文件:$(wildcard *.cpp)

       此函数会列出当前目录下所有的.cpp源文件。

  (2) patsubst
  用法:$(patsubst PATTERN,REPLACEMENT,TEXT)    模式替换函数

       用此函数我们可以根据.cpp文件得到.o文件:$(patsubts %.cpp,%.o,$(wildcard *.cpp))  

       Tips:在这里*.cpp使用了通配符,表示后缀为.cpp的所有文件

那么,用上系统函数的makefile修改如下:

1 SRCFILE:=$(wildcard *.cpp)
  2 OBJECTS:=$(patsubst %.cpp,%.o,$(SRCFILE))
  3 
  4 testobj:$(OBJECTS)
  5     g++ -o testobj $(OBJECTS)
  6 main.o:student.h dog.h family.h
  7 student.o:student.h
  8 dog.o:dog.h
  9 family.o:family.h
 10 
 11 clean:
 12     rm -rf testobj *.o

接下来考虑自动生成依赖规则。首先可以使用g++编译器的-M选项来列出文件的依赖关系,使用-M选项会输入所有的文件依赖关系,包括标准库文件,因此这里使用-MM选项只输出我们自己需要的依赖关系。

要让makefile自动生成依赖规则,需要使用上述的命令,在makefile中为每个源文件.cpp,为其生成一个同名.d文件,用来描述此源文件的依赖关系,例如对于main.cpp,为其生成main.d文件,描述了main.o文件的依赖关系。还需要学习一些自动化变量:

(1)$< 规则的第一个依赖文件名

(2)$@ 规则的目标文件名

(3)$^ 规则的所有依赖文件

修改为自动生成依赖关系后的makefile如下:

 1 SRCFILE:=$(wildcard *.cpp)
  2 OBJECTS:=$(patsubst %.cpp,%.o,$(SRCFILE))
  3 G++:=g++
  4 
  5 testobj:$(OBJECTS)
  6     g++ -o testobj $(OBJECTS)
  7 
  8 %.d:%.cpp
  9     $(G++) -MM $< > $@.$$$$; 
 10     sed 's,($*).o[ :]*,1.o $@ :,g' < $@.$$$$ > $@; 
 11     rm -rf $@.$$$$
 12 
 13 sinclude $(SRCFILE:.cpp=.d)
 14 
 15 clean:
 16     rm -rf testobj *.o

首先使用g++ -MM 将每一个.cpp文件的依赖关系输出重定向到一个文件中,这个文件为依赖目标文件名.进程号,特殊变量$$$$表示当前进程号,举个具体的例子展开就是:g++ -MM main.cpp > main.d.123456。接下来使用sed做字符替换,将第一行命令生成的文件进行处理,加入.d文件,比如将main.o:main.cpp student.h dog.h family.h处理为 main.o mina.d:main.cpp student.h dog.h family.h,接下来最后一步删除产生的临时文件。在使用时,需要将这些.d文件包含进当前的makefile中,sinclude $(SRCFILE:.cpp=.d)命令,表示将当前的.cpp源文件替换问.d文件包含进来,这样就可以使用生成的.d依赖文件了。

执行make后的结果如下:

可以看到对于每个.o文件有同名.d文件生成,打开任意一个.d文件查看其内容:

可以看到main.d中是main.o的依赖关系。

最后介绍伪目标,伪目标只是一个标签,没有依赖任何文件,可以将clean写成伪目标,声明一个伪目标的方法是将其作为特殊目标.PHONY的依赖,当输入make clean时,一些定义的指令会被执行,现修改makefile如下:

1 SRCFILE:=$(wildcard *.cpp)
  2 OBJECTS:=$(patsubst %.cpp,%.o,$(SRCFILE))
  3 G++:=g++
  4 
  5 testobj:$(OBJECTS)
  6     g++ -o testobj $(OBJECTS)
  7 
  8 %.d:%.cpp
  9     $(G++) -MM $< > $@.$$$$; 
 10     sed 's,($*).o[ :]*,1.o $@ :,g' < $@.$$$$ > $@; 
 11     rm -rf $@.$$$$
 12 
 13 sinclude $(SRCFILE:.cpp=.d)
 14 
 15 .PHONY:clean
 16 clean:
 17     rm -rf testobj *.o

可以看到定义了clean伪目标,当输入make clean时,clean定义的指令会被执行,删除终极目标以及所有的.o文件。有时候在执行伪目标的时候不想关注是否执行成功,也不关注出错的提示消息,这时候就可以在命令前面加-来告诉make你只管执行,结果我不关注,比如:

15 .PHONY:clean
 16 clean:
 17     -rm -rf testobj *.o

以上就是近期对于makefile的学习笔记,没有涉及到不同目录下文件的编译以及高级用法,算是初级学习吧。

参考资料:

(1)GUN make中文手册:https://pan.baidu.com/s/1BwlD910FXWA9mOrL9uAlSA

(2)makefile教程(中文版):https://pan.baidu.com/s/1EITITRS6P0Dk41YwlMD5_g

原文地址:https://www.cnblogs.com/kks170716/p/9216038.html