目录
1.基本写法
2. 带上clean
3. 加上特殊字符、通配符等进行简写
4. 避免重复的进一步简化
5. 加入编程思想的工程通用模板
1.基本写法
Makefile的编写方便了在linux下编译大型的C/C++的程序,同时,Makefile的写法相对灵活,我们从最简单的方法入手。最简单的方式其实就是原始的gcc或者g++的语法,但是要明确makefile最最基本的语法规则。
Target … : prerequisites …
Command1
Command2
…
这个格式必须使用,即
编译的目标,冒号,编译目标的依赖
执行的操作
下面我们简单写一个main函数,其调用了加法和减法器中的函数。具体如下:
这里实现很简单,重点是理清楚逻辑依赖关系。一共存在5个文件,main.cpp, add.cpp, add.h, minus.cpp, minus.h。
具体的写法如所示,这个简单的makefile已经可以运行,要注意的要点是:
这里的目标是生成可执行的名称为main的文件,其依赖编译形成的main.o, add.o, minus.o这三个文件, 如果makefile都能够找到这三个依赖文件,那么就执行下面的执行命令。g++将几个.o文件链接成可执行文件main。类似的,在任何涉及更多文件的工程中,makefile中的第一行应该将所有需要用到的.o文件都写上,不管是直接的依赖还是间接的依赖。
在缺省目标之外,还有其他三个目标,即缺省目标的三个依赖。这三个目标也写明了其对应的依赖和执行命令。其实makefile文件的编写关键之一就是要写明依赖。
2. 带上clean
通过上面的方法,可以达到生成可执行文件的目的了。但是这个过程会生成很多中间过程的对象文件,如add.o这样的文件。在所有过程执行完成之后,我们很可能不想保留这些文件,甚至不想保留最后的可执行文件,只保留所有的源文件。这里就可以使用一个新的目标,通常命名为clean,通过rm命令来删除这些不想保留的文件。执行时可以用make命令指定要执行的目标,就可以不执行缺省的目标。如make clean。
需要注意的是,这里clean的写法依然保持1中基本写法模式,目标是clean,没有依赖,执行删除的相应命令。
这里还有一个.PHONY: clean的用法,实际上是防止目录中真的有一个没有依赖的clean文件,但是一般情况下是不这样写的。
3. 加上特殊字符、通配符等进行简写
其实makefile的写法相当的灵活,这里简单介绍两个符号,可以达到简化的目的。$^和$@。$^表示当前规则中的所有依赖条件。$@表示当前规则中的目标。所以上面的makefile文件可以写成如下的形式。
另外除了缺省目标外,其他的目标也都是中间过程,这里涉及到隐藏规则的技巧。但是其实也不需要明白隐藏技巧到底是什么,只需要记住makefile重要的是依赖关系。将除了缺省目标之外的其他目标,只写明其依赖的头文件即可,不需要写其本身的源文件和执行命令。重点还是依赖关系。
4. 避免重复的进一步简化
以上是熟悉makefile的编写过程,实际上真实工程中的makefile不会写成上面那样。首先,在makefile中写出依赖的头文件是不太明智的。第一是我们实际上在规范的源文件中书写中,都会用#include写明依赖的头文件名称。而且真实情况下,这种头文件会非常的多。所以第一步是依赖并不写成.o文件(对象文件)和.h文件(头文件)之间的,而是写成.o文件和.cpp文件(源文件)的。在规范的代码写法中,通常一个源文件对应生成一个对象文件。第二,如果我们不在源文件中写明头文件,而在makefile中写,也是可以的。但是这会造成一个问题,就是我们需要再makefile中明确各种复杂的头文件依赖,并且如果在源文件中有#include依赖,那么就会造成重复。这种属于手工维护重复的行为不止在写程序中,在很多工程应用问题上都需要避免。会产生这边改了,那边没有同步的情况。比如数据库里就极力避免这种数据。我们希望的是修改一个地方,所有需要用到该处的内容就都修改了。
那么可以写成如下的形式。
5. 加入编程思想的工程通用模板
写程序的一个经典思维就是模板化,模板化的好处是方便根据具体情况进行局部的改动,而大部分内容可以通用。Makefile也不例外,由于每个程序都需要经过编译的过程,所以makefile是典型的需要反复使用的文档。另外根据上面改进写法,很多.o和.cpp之间的依赖是同一个格式,很容易用通配符之类的符号来代替。于是可以写成如下的形式。这里除了下图中的注释解释,还有一个单独的“$”符号没有解释,这个符号非常的常见,其实意思很简单,就是将后面括号里的变量展开。比如$(TARGET)展开后就是要形成的目标文件——main。
在这种写法下,makefile文件不拘泥于某个特定的工程,具有较强的适用性。而且很多东西可以局部修改,比如编译器,头文件的路径,生成可执行文件的路径等。具体的解释在语句后的注释,使用起来较为方便。