makefile(GNU make) learn detail ---First

这是一遍学习makefile的笔记,环境在linux上.详情参考:https://seisman.github.io/how-to-write-makefile/overview.html#

以及一些实例:https://gitlab.com/stewartweiss/Make-Tutorial,以及GNU make中文文档:链接: https://pan.baidu.com/s/1veIuHHa_6_18rlmTRrzQsQ 提取码: u461 .

 

Table of Contents

 

 

关于程序的编译和链接

makefile简单介绍及使用

makefile的规则

make 如何工作

指定变量

自动推导规则

另类风格的makefile

清除工作目录过程文件


关于程序的编译和链接

C ,C++ ,首先要通过编译(compile)生成Object File,即.o文件,然后再把大量的.o文件链接(link)合成执行文件.编译时,编译器需要的是语法的正确,函数与变量的声明的正确;链接时,主要是链接函数和全局变量.链接器并不管函数所在的源文件,只管函数的中间目标文件 (Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显 地指出中间目标文件名,这对于编译很不方便。所以,我们要给中间目标文件打个包,在Windows下这种包 叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。详细请见:Lunix系统C语言编译

makefile简单介绍及使用

make命令执行时,需要一个makefile文件,以告诉make命令需要怎么样的去编译和链接程序。我将在这里简要介绍一下。Makefile传统上被命名为“ makefile”或“ Makefile”。该make命令将在当前工作目录中查找具有任一名称的文件。GNU版本make按顺序尝试以下名称:

   GNUmakefile
   makefile
   Makefile  

你不应该命名你的makefile GNUmakefile; 这是误导性的,不便于携带。我更喜欢大写版本,即,Makefile因为我的所有源文件都是小写名称,而其他信息文件(例如this README)是大写的。在某些系统上,ls默认情况下,该命令将以小写形式显示大写,因此makefile名称将位于目录列表的开头附近。

通常,make 工具主要被用来进行工程编译和程序链接。当使用 make 工具进行编译时,工程中以下几种文件在执行 make 时将会被编译(重新编译):

  • 所有的源文件没有被编译过,则对各个 C 源文件进行编译并进行链接,生成最后的可执行程序;
  • 每一个在上次执行 make 之后修改过的 C 源代码文件在本次执行 make 时将会被重新编译;
  • 头文件在上一次执行 make 之后被修改。则所有包含此头文件的 C 源文件在本次执行 make 时将会被重新编译。

makefile的规则

target ... : prerequisites ...
    command
    ...
    ...
  • target:可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label). 
  • prerequisites:生成该target所依赖的文件和/或target.
  • command:该target要执行的命令(任意的shell命令).
prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。

注意:一个规则可以有多个命令行,每一条命令占一行。每一个命令行必须以[Tab]字符开始,[Tab]字符告诉 make 此行是一个命令行。make 按照命令完成相应的动作。这也是书写 Makefile 中容易产生,而且比较隐蔽的错误。

make 如何工作

#示例Makefile
demo: main.o utils.o interface.o
	gcc -o demo main.o utils.o interface.o

main.o: main.c utils.h interface.h
	gcc -c main.c

utils.o: utils.c utils.h defs.h
	gcc -c utils.c

interface.o: interface.c interface.h defs.h
	gcc -c interface.c

当在 shell 提示符下输入“make”命令以后。make 读取当前目录下的 Makefile 文件,并将 Makefile 文件中的第一个目标作为其执行的“终极目标”,开始处理第一个规则(终极目标所在的规则)。在我们的例子中,第一个规则就是目标“demo”所在的规则。规则描述了“demo”的依赖关系,并定义了链接.o 文件生成目标“edit”的命令; make在执行这个规则所定义的命令之前,首先处理目标“demo”的所有的依赖文件(例子中的那些.o 文件)的更新规则(以这些.o 文件为目标的规则)。对这些.o 文件为目标的规则处理有下列三种情况:

  1. 目标.o 文件不存在,使用其描述规则创建它;
  2. 目标.o 文件存在,目标.o 文件所依赖的.c 源文件、 .h 文件中的任何一个比目标.o文件“更新”(在上一次 make 之后被修改)。则根据规则重新编译生成它;
  3. 目标.o 文件存在,目标.o 文件比它的任何一个依赖文件(的.c 源文件、.h 文件)“更新”(它的依赖文件在上一次 make 之后没有被修改),则什么也不做。

这些.o 文件所在的规则之所以会被执行,是因为这些.o 文件出现在“终极目标”的依赖列表中。在 Makefile 中一个规则的目标如果不是“终极目标”所依赖的(或者“终极目标”的依赖文件所依赖的),那么这个规则将不会被执行,除非明确指定执行这个规则(可以通过 make 的命令行指定重建目标,那么这个目标所在的规则就会被执行,例如 “make clean”)。在编译或者重新编译生成一个.o 文件时,make 同样会去寻找它的依赖文件的重建规则(是这样一个规则:这个依赖文件在规则中作为目标出现),在这里就是.c 和.h 文件的重建规则。

创建或者更新每一个规则依赖文件的过程都是这样的一个过程(类似于 c 语言中的递归过程)。对于任意一个规则执行的过程都是按照依赖文件列表顺序,对于规则中的每一个依赖文件,使用同样方式(按照同样的过程)去重建它,在完成对所有依赖文件的重建之后,最后一步才是重建此规则的目标。
更新(或者创建)终极目标的过程中,如果任何一个规则执行出现错误 make 就立即报错并退出。整个过程 make 只是负责执行规则,而对具体规则所描述的依赖关系的正确性、规则所定义的命令的正确性不做任何判断。就是说,一个规则的依赖关系是否正确、描述重建目标的规则命令行是否正确,make 不做任何错误检查。因此,需要正确的编译一个工程。需要在提供给 make 程序的 Makefile 中来保证其依赖关系的正确性、和执行命令的正确性。

To run the make command, make sure your working directory has a makefile in it. If it is not one of the standard names described above you can use the -f option to the make command. If the makefile is named MAKEFILE, you would type

#上面英文是说如果你的文件没有按上述所说的三个名字命名你可以使用-f参数来使用其他名字
make -f MAKEFILE

to use that file instead. If you do not want make to do anything but just want to see what make might do, use the -n option, which does not execute any commands but just displays which ones it would do:

#-n参数可以用来提前看一下make命令将要做什么,并不执行
make -n

指定变量

为避免以来的.o文件只在依赖文件列表(prerequisites)或规则的命令中(command)出现一次而引发的问题,在实际工作中大家都比较认同的方法是,使用一个变量“objects”、“OBJECTS”、“objs”、“OBJS”、“obj”或者“OBJ”来作为所有的.o 文件的列表的替代。在使用到这些文件列表的地方,使用此变量来代替.在上例的 Makefile中我们可以添加这样一行:

#示例Makefile
objects=main.o utils.o interface.o
demo: $(objects)
	gcc -o demo $(objects)

main.o: main.c utils.h interface.h
	gcc -c main.c

utils.o: utils.c utils.h defs.h
	gcc -c utils.c

interface.o: interface.c interface.h defs.h
	gcc -c interface.c

自动推导规则

在使用make编译.c源文件时,编译.c源文件规则的命令可以不用明确给出。这是因为make本身存在一个默认的规则,能够自动完成对.c文件的编译并生成对应的.o文件。在Makefile中我们只需要给出需要重建的目标文件名(一个.o文件),make会自动为这个.o文件寻找合适的依赖文件(对应的.c文件--对应是指:文件名除后缀外,其余都相同的两个文件),而且使用正确的命令来重建这个目标文件。这样,在书写 Makefile 时,我们就可以省略掉描述.c 文件和.o 依赖关系的规则,而只需要给出那些特定的规则描述(.o 目标所需要的.h 文件)。

demo: main.o utils.o interface.o
	gcc -o demo main.o utils.o interface.o

main.o: utils.h interface.h  
utils.o: utils.h defs.h
interface.o: interface.h defs.h

另类风格的makefile

上一节中我们提到过, Makefile 中,所有的.o 目标文件都可以使用隐含规则由 make自动重建,我们可以根据这一点书写更加简洁的 Makefile。而且在这个 Makefile 中,我们是根据依赖而不是目标对规则进行分组。

demo: main.o utils.o interface.o
	gcc -o demo main.o utils.o interface.o
main.o utils.o: utils.h 
utils.o interface.o: defs.h
interface.o main.o: interface.h

这种风格的 Makefile 并不值得我们借鉴。问题在于:同时把多个目标文件的依赖放在同一个规则中进行描述(一个规则中含有多个目标文件),这样导致规则定义不明了,比较混乱。建议大家不要在 Makefile 中采用这种方式了书写。否则后期维护将会是一件非常痛苦的事情。

书写规则建议的方式是:单目标,多依赖。就是说尽量要做到一个规则中只存在一个目标文件,可有多个依赖文件。尽量避免使用多目标,单依赖的方式。这样书写的好处是后期维护会非常方便,而且这样做会使 Makefile 会更清晰、明了。

清除工作目录过程文件

规则除了完成源代码编译之外,也可以完成其它任务。clean所定义的命令用来删除 make 过程产生的中间文件(进行清理工作)。

.PHONY: all
all: demo

demo: main.o utils.o interface.o
	gcc -o demo main.o utils.o interface.o

main.o: main.c  utils.h interface.h
utils.o: utils.c utils.h defs.h
interface.o: interface.c interface.h defs.h

# The rule to remove object files is:
.PHONY: clean
clean:
	\rm *.o

通过“.PHONY”特殊目标将“clean”目标声明为伪目标。避免当磁盘上存在一个名为“clean”文件时,目标“clean”所在规则的命令无
法执行.在rm前加"\"确保没有与interactive, rm -i冲突,确保在rm命令在系统的绝对路径.PHONY是一个伪目标,在下面将讨论.

 

小结:持续更新,先来一点应付考试-_-

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值