make是一条计算机指令,是在安装有GNU Make的计算机上的可执行指令。该指令是读入一个名为
makefile的文件,然后执行这个文件中指定的指令。
Make
定义
GNU Make是一个用来控制可执行文件和其他一些从源文件来的非源代码文件版本的软件。
Make可以从一个名为makefile的文件中获得如何构建程序的依赖关系,Makefile中列出了每个目标文件以及如何由其他文件来生成它。 当编写一个程序时,可以为它编写一个makefile文件,这样就可以使用Make来编译和安装这个程序。
功能
规则目标
Makefile告诉Make怎样执行一系列的指令去依靠源文件生成一个目标文件。Makefile中声明了一个依赖关系的列表,这个列表应当包含所有文件(无论是源文件或者目标)作为输入。
以下是个简单的规则示例:
当执行make的时候可以指定特定的目标去更新,否则Make会更新Makefile目标列表中第一个目标。当然作为这个目标输入的其他目标会先更新。make 用makefile确定的目标文件应该被更新,然后目标文件实际上需要更新。如果一个目标文件比他的所有依赖都新,这样他就是最新的了,不需要再去重新生成了。其他文件确实需要更新,要按照正确的顺序,在用来生成其他文件之前先更新自己。
约定
GNU制定了一些如何编写Makefiles的约定,所用的GNU软件包必须遵守。即使不打算成为GNU软件,在程序中遵循这些约定也是一个好主意,这样用户就可以像其他许多软件包一样构建软件包,而且不需要学习任何特殊的东西。
这些约定可以在章节``Makefile conventions'' (147 k characters)和章节GNU Coding Standards (147 k characters)中看到。
关系
Android.mk和Application.mk是基于makefile装了一层封装,编写Android.mk和Application.mk之后可以通过ndk-build执行so的编译,Android.mk中新增了一些对固定文件 固定的环境变量的的读取
如APP_ABI := armeabi-v7a定义在Application.mk中然后执行ndk-build将只编译armeabi-v7a的so在 当前目录的上级目录。
具体参考
解释
当写一个简单的程序,只有一到两个
源文件的时候,输入
% cc file1.c file2.c
就没什么问题,但如果有很多源文件就会很烦人──编译的时间也会很长。
一个方法就是使用
目标文件,只在源文件有改变的情况下才重新编译源文件。因此可以这样做:
% cc file1.o file2.o ... file37.c ...
上次编译后,file37.c 发生了改变,但其他文件没有。这样做可以让编译过程快很多,但是也不能解决累人的输入问题。
或者可以使用一个
shell script来解决输入问题,但是也需要重新编译所有文件,在大型项目上很没有效率。
如果有成百上千的
源文件的话怎么办。如果再与很多人合作写程序,别人对源文件进行了修改,又没有告诉共享,该怎么办。
也许可以把以上两种方法结合,写一种像
shellscript 一样的东西。这种文件包含某种技巧可以决定什么时候该对
源文件进行编译。如今要的就是一个程序可以懂得这种技巧,因为要懂得这种技巧,shell 还没那么大的能耐。
这个程序就叫 make。它读入一个文件,叫
makefile,这个文件决定了源文件之间的依赖关系。而且决定了源文件什么时候该编译什么时候不应该编译。例如,某个规则可以说 “ 如果 fromboz.o 比 fromboz.c 要旧,意思就是有人修改了 fromboz.c,因此我们需要重新编译这个文件。”这个
makefile还有规则通知 make 该 怎么 重新编译源文件,因此 make 是一个强大得多的工具。
makefile通常和相关的
源文件保存在同一个目录下,可以叫做 makefile,Makefile 或者 MAKEFILE。大多数程序员会使用 Makefile 这个名字,因为这样可以让这个文件被放在目录列 表的顶端,可以很容易得看见。
make命令
这是一个非常简单的 make 文件:
foo: foo.c
cc -o foo foo.c
包含两行,一行是依赖关系,一行是执行动作。
依赖关系的那一行包含了程序的名字 (叫做 target),紧跟着一个冒号,然后是空格,最后是源文件的 名字。当 make读入这一行的时候,会检查 foo 是否存在;如果存在,就比较 foo 和 foo.c 最后的修改时间有什 么不同。如果 foo 不存在,或者比 foo.c 要旧,就检查执行动作那一行看看该怎么做。换句话 说,就是 foo.c 需要重新编译的时候该怎么办。
执行动作那一行以一个 tab (按下 tab) 开始,然后在命令行下产生 foo 所执行的命令。如果 foo 过期了,或者不存在,make 就会 执行这个命令来产生
foo。换句话说,这就是重新编译 foo.c 的规则。
因此,当输入 make 时,它会确定 foo 和 foo.c 在修改时间上是否同步。这个原则可以在 Makefile 里扩展到成百上千的目标文件上──实际上,在 FreeBSD 里,只要在合适的目录下输入 make world 就可以编译整个操作系统。
makefile另一个有用的特点就是目标文件不一定就是程序。例如,可以 有这样的 make 文件。
foo: foo.c
cc -o foo foo.c
install:
cp foo /home/me
可以输入如下的命令告诉 make 该执行哪个目标:
% make target
make 会只执行这个目标而忽略其他的目标。例如,如果输入 make foo,就只有 foo 被执行,必要的情况下重新编译 foo 而不会继续执行 install 这个目标。
如果只是输入 make 这个命令,make 总会寻找第一个目标,并且在执行完以后就不管其他的目标了。例如,如果输入 make foo,make 就会转到 foo 这个目标,在必要的情况下重新编译 foo,而不会执行 install 目标, 然后就停止了。
一定要注意,install 这个目标不依赖任何其他的东西。这意味着一旦输入 make install,这个目标下的所有命令都将被执行。这种情况下,foo 将被安装到用户的家目录下。应用程序的
makefile正是这样写的,以便程序在正确编译后可以被安装到正确的目录。
要尝试解释的话会比较容易让人混淆。如果不太懂 make 是如何工作的,最好的办法就是先写一个简单的程序例如 “hello world” 以及和上面的例子相同的 make 文件再去实验。然后 再进一步,使用多个
源文件,或者让源文件包含一个头文件。 touch 命令在这里就非常有用了──它能让在不改变文件内容的情况下改变文件的日期。
想进一步学习如何编写makefile请参考引用4.
参考资料
Make.Make李岩的博客.2013-06-07