Makefile进阶

来源:互联网 发布:智能机器人编程 编辑:程序博客网 时间:2024/06/15 23:57


再也不能忍受自己写的无力的Makefile了,我昨天写的是这样子的:


PS:为什么图中的字都是斜的,难道最近代码敲多了,近视了?

虽然以前还学过一点依赖,但是忘了。我用上图中的水平编译上面几个文件,经常出现undefined reference,所以我今天就来治治它。


下面我要分步骤进阶了:

1.让make自动推导

make很强大,他可以自动推导文件以及文件依赖关系后面的命令,于是我们没必要再每一个.o文件后都写上类似的命令,因为,我们的make会自动识别,并自己推导命令。

如下图,这是我今天连接mysql写的第一个makefile,要注意的是如果有链接库,只用在一处加就可以了,比如图中第4行。


现在干净多了好吧,这是第一个版本。

.PHONY:表示clean是个伪目标文件,下面还会细说。


2.另类风格的Makefile

既然make可以自动推导命令,那么我看到一大堆.o和.h的依赖就感到不爽,那么多重复的.h,能不能将其收拢起来,OK,看下面。


把共同依赖同一个头文件的.o放在一堆就可以了,虽然我这里只是两句变成了一句,看起来没什么效果。不过如果文件变多了的话,那么效果是很明显的。


3.清空目标的规则

每个Makefile文件中都应该写一个清空目标文件(.o和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。
一般都是:
.PHONY: clean
  clean
  -rm main $(objects)
.PHONY的意思是表示clean是一个伪目标。而在rm命令前面加一个‘-’的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。当然clean的规则不要放在文件的开头,不然这就会编程make的默认目标。伪目标的是为了防止当前目录中存在一个名为clean的文件,当make clean是,由于clean文件没有依赖关系,所以makefile会什么都不做,我们定义一个伪目标就是为了防止这种情况。当我们make clean时,不管有没有clean文件,都会执行-rm main $(objects)

4.引入其他Makefile

举例:
主Makefile,文件名就是Makefile:

从makefile,文件名为,other.mk

OK,这就可以正常运行了。不要把inlude放在Makefile第一句,Makefile的默认规则你懂的,第一句执行完了,可能直接歇火不干了。

5.Makefile的工作方式

GNU的make工作时执行步骤如下:
1.读入所有的Makefile
2.读入被include的其他Makefile
3.初始化文件中的变量
4.推导隐晦规则,并分析所有规则
5.为所有目标文件创建依赖关系链
6.根据依赖关系,决定哪些目标要重新生成
7.执行生成命令

书写规则:
规则包含两个部分,一个是依赖关系,一个是生成目标的方法
在Makefile中,规则的顺序是很重要的,因为,Makefile中只应该有一个最终目标,其他目标都是被这个目标连带出来的。所以一定要让make知道你的最终目标是深恶。
一般来说,定义在Makefile中的目标可能会有很多,所以一定要让make知道你的最终目标是什么。Makefile中第一条规则中的目标将被确立为最终的目标。如果第一条规则中的目标有多个,那么,第一个目标会成为最终的目标,make所完成的也就是这个目标。
举例:
foo.o:foo.c defs.h
cc -c -g foo.c
这个例子告诉我们两件事:1.文件的依赖关系,foo.o依赖于foo.c和defs.h文件,如果foo.c和defs.h的文件日期要比foo.o的文件日期要新,或是foo.o不存在,那么依赖关系发生。2.如果生成foo.o文件,使用cc命令。一般我们使用C++是的cc用g++。

规则中使用通配符:
make支持三种通配符:"*",“?" 和 ”[...]",和shell相同。所以我们常见的用法在clean中使用:rm -rf *.o


6.静态模式

Makefile规则中的目标可以不止一个,其支持多目标,有可能我们的多目标同时依赖一个文件,并且其生成的命令大体相似,我们可以把它们合并起来。当然多个目标的生成规则的执行命令是同一个,这可能会带来麻烦。这时候就是自动化变量上场的时候了,“$@”,这个变量表示目前规则中所有目标的集合,"$<"表示所有依赖目标集。
举例:
现在,这个方法比之前所有的方法都吊,虽然也是一句 :),是我例子选的不好,%.o和%.cpp可以通配有OBJ中的相关文件。试想,如果我们"%.o"有几百个,那么只要用这种很简单的静态模式规则,就可以写完一堆规则,非常有效率。
上面例子中,指明了我们目标从OBJ中国获取,%.o表明要所有以“.o"结尾的目标,也就是mysql_manager.o和test_mysql_manager.o,而依赖模式”%.c"则取模式"%.o"的%,也
就是mysql_magager和test_mysql_manager,并为其添加“.cpp"的后缀。”$<"表示所有依赖,“$@”表示所有目标。执行了就可以make了。


7.自动生成依赖性

如果是较大型的工程,你必须要清除C文件包含了哪些头文件,这样修改很麻烦。我们可以使用C/C++编译的一个功能,大多数C/C++编译器都支持一个"-M“的选项,即自动寻找源文件中包含的头文件,并生成一个依赖关系。例如,如我们执行下面的命令:
cc -M main.c
其输出是:
main.o: main.c defs.h
于是由编译器自动生成依赖关系,就不必动手写若干文件了。如果使用GNU的C/C++编译器,需要使用"-MM”参数,不然,它会把标准库的头文件也包含进来。
那么编译器的这个共鞥如何与我们的Makefile联系在一起呢。
GNU组织建议编译器为每一个源文件自动生成的依赖关系放到一个文件中,为每一个“name.c"的文件都生成一个”name.d"的Makefile文件,.d文件就存放对应的.c文件的依赖关
系。
于是我们可以写出.c文件和.d文件的依赖关系,并让make自动更新或生成.d文件,并将其包含在我们的主Makefile中,这样我们就可以自动化的生成每个文件的依赖关系了。
 %.d: %.c              
@set -e; rm -f $@; \ 
$(CC) -M $(CPPFLAGS) $< >; $@.$$$$; \  
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ >; $@; \             
rm -f $@.$$$$ 
这个规则的意思是,所有的[.d]文件依赖于[.c]文件,“rm -f $@”的意思是删除所有的目标,也就是[.d]文件,第二行的意思是, 为每个依赖文件“$<”,也就是[.c]文件生成依赖文件,“$@”表示模式“%.d”文件,如果有一个C文件是name.c,那么“%”就是 “name”,“$$$$”意为一个随机编号,第二行生成的文件有可能是“name.d.12345”,第三行使用sed命令做了一个替换,关于sed命 令的用法请参看相关的使用文档。第四行就是删除临时文件。
总来言之,这个模式要做的事就是在编译器生成的依赖关系中加入.d文件的依赖。
我们现在可以使用Makefile的include命令来引入它:
sources = foo.c bar.c
include $(sources:.c=.d)
0 0
原创粉丝点击