Makefile学习入门

女生的网名这么多〃 提交于 2020-04-07 10:47:37

1、学习时遇到的问题

        在学习Makefile时,首先是对其的语法不太了解,造成对项目的Makefile文件阅读很吃力;其次是在寻找资源上,着实的浪费了很大的精力;最后就是对makefile的工作原理一知半解,导致走了很多弯路。

        下面我将自己对Makefile的理解和认为好的资料分享给大家,希望大家在接触它时,可以少走弯路。

2、为什么需要Makefile

        1、在大型的项目中,源文件和目录不计数,文件间的依赖关系及其所在路径非常复杂;

        2、文件间的编译顺序,哪些需要先编译,哪些需要后编译,哪些需要重新编译,我们不能通过简单的编译命令来完成;     

        3、当我们仅仅修改了部分文件的代码,我们不能通过只编译更新过的文件来更快的完成编译或者说是模块化编译。

这些问题,Makefile都可以解决,  一旦写好,只需要一个make的命令,整个工程就会完全自动编译,极大的提高了软件开发的效率。

3、Makefile是什么

        可以理解为一种自动化编译的工具,里面包含了整个工程项目的编译规则,并且可以将项目分成多个模块进行分别编译,而且里面可以包含执行操作系统的命令。当我们需要编译代码时,只需要一个命令就可完成整个工程项目的编译或者某个模块的编译。

4、Makefile的工作原理

        大家都编译过工程项目,对编译过程应该了解,简单说就是:源文件首先会生成中间目标文件,再由中间目标文件生成执行文件具体说就是:在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成 Object File。而在链接程序时,链接器会在所有的 Object File 中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error)。如果向具体了解编译过程,可以参考http://my.oschina.net/u/1783725/blog/680722

    Makefile格式

       格式1: target ... : prerequisites ...
                              command   (命令需要tab键)
                              ...
                              ...

        格式2:targets : prerequisites ; command
                            command  (命令需要tab键)
                            ...

        target 也就是一个目标文件,可以是 Object File,也可以是执行文件。还可以是一个标签(Label)

        prerequisites 就是要生成那个 target 所需要的文件或是目标。

        command 也就是 make 需要执行的命令。 (任意的 Shell 命令)

    工作原理:    target 这一个或多个的目标文件,依赖于prerequisites 中的文件,其生成规则定义在 command 中

                           通俗的说:target 目标的生成,需要满足prerequisites 条件,条件满足才能执行command 来生成目标。 

                           具体的说:如果prerequisites 中如果有一个以上的文件比 target 文件要新(更新日期要新)的话,command 所定义的命令就会被执行。这就是 Makefile 的规则。在编译时,make会比较 targets 文件和 prerequisites 文件的修改日期, 如果 prerequisites 文件的日期要比 targets 文件的日期要新,或者 target 不存在的话,那么,make 就会执行后续定义的命令。

    例子

edit : main.o kbd.o command.o display.o \
		insert.o search.o files.o utils.o
	cc -o edit main.o kbd.o command.o display.o \
		insert.o search.o files.o utils.o
main.o : main.c defs.h
	cc -c main.c
kbd.o : kbd.c defs.h command.h
	cc -c kbd.c
command.o : command.c defs.h command.h
	cc -c command.c
display.o : display.c defs.h buffer.h
	cc -c display.c
insert.o : insert.c defs.h buffer.h
	cc -c insert.c
search.o : search.c defs.h buffer.h
	cc -c search.c
files.o : files.c defs.h buffer.h command.h
	cc -c files.c
utils.o : utils.c defs.h
	cc -c utils.c
clean :
	rm edit main.o kbd.o command.o display.o \
		insert.o search.o files.o utils.o

5、Makefile如何工作

        1、make 会在当前目录下找名字叫“Makefile”或“makefile”的文件。
        2、如果找到,它会找文件中的第一个目标文件(target) ,在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。
        3、如果 edit 文件不存在,或是 edit 所依赖的后面的[.o]文件的文件修改时间要比edit 这个文件新,那么,他就会执行后面所定义的命令来生成 edit 这个文件。
        4、如果 edit 所依赖的.o 文件也存在,那么 make 会在当前文件中找目标为.o 文件的依赖性,如果找到则再根据那一个规则生成.o 文件。 (这有点像一个堆栈的过程)
        5、当然,你的 C 文件和 H 文件是存在的啦,于是 make 会生成 .o 文件,然后再用 .o文件生命 make 的终极任务,也就是执行文件 edit 了。

    注意:make 会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件,在找寻的过程中, 如果出现错误, 比如最后被依赖的文件找不到, 那么 make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make 根本不理。make 只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。

            通过上述分析,我们知道,像 clean 这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要 make 执行。即命令——“make clean” ,以此来清除所有的目标文件,以便重编译。

6、工程中make 工作时的执行步骤  

        第5点说的是一个make文件中makefile的执行流程,而第6点说的是整个项目中(多个make文件)makefile的执行流程。

        1、读入所有的 Makefile。
        2、读入被 include 的其它 Makefile。
        3、初始化文件中的变量。
        4、推导隐晦规则,并分析所有规则。
        5、为所有的目标文件创建依赖关系链。
        6、根据依赖关系,决定哪些目标要重新生成。
        7、执行生成命令。  

        通过上面的总结,大家对Makefile应该有一个大体的了解了吧,下面将会对其细节做一下总结。

7、Makefile中的五中规则

        1、显示规则。显示规则说明了,如何生成一个或多的的目标文件。这是由 Makefile 的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
        2、隐晦规则。由于我们的 make 有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写 Makefile,这是由 make 所支持的。
        3、变量的定义。在 Makefile 中我们要定义一系列的变量,变量一般都是字符串,这个有点像 C 语言中的宏,当 Makefile 被执行时,其中的变量都会被扩展到相应的引用位置上。
        4、文件指示。其包括了三个部分,一个是在一个 Makefile 中引用另一个 Makefile,就像 C 语言中的 include 一样;另一个是指根据某些情况指定 Makefile 中的有效部分,就像 C 语言中的预编译#if 一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
        5、注释。Makefile 中只有行注释,和 UNIX 的 Shell 脚本一样,其注释是用“#”字符,这个就像 C/C++中的“//”一样。如果你要在你的 Makefile 中使用“#”字符,可以用反斜框进行转义,如: “\#” 
    最后,还值得一提的是,在 Makefile 中的命令,必须要以[Tab]键开始。

8、Makefile中基础

(1)伪目标和.PHONY

 伪目标:“伪目标”并不是一个文件,只是一个标签。 make 无法生成它的依赖关系和决定它是否要执行,必须显示地指明这个“目标”才能让其生效
                 “伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了,可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是 “伪目标” 
                 伪目标同样可以作为“默认目标” ,只要将其放在第一个,也可以为伪目标指定所依赖的文件。

                  使用“.PHONY”标记,放在文件首部的第一个目标,不会被make作为执行目标。

(2)通配符

    make 支持通配符: “*” , “?”和“[...]”,通配符可以用在变量中,起作用必须用到wildcard   如objects := $(wildcard *.o)

    注意:1、%.o: %.c                表示的是目标集为当前目录下所有.o结尾的文件,依赖集为当前目录下所有.c结尾的文件

    如某目录下有 test01.c   test02.c   test03.c文件,则%.o:%.c就表示3个,分别为 test01.o:test01.c      test02.o:test02.c        test03.o:test03.c 。具体是否会生成.o文件,还需要看makefile文件中的依赖关系,需不需要生成.o文件。

   2、.c.o           是老式风格的"后缀规则"——双后缀,意义就是".c"是源文件的后缀,".o"是目标文件的后缀

   后缀规则不允许任何的依赖文件,如果有依赖文件的话,那就不是后缀规则,那些后缀统统被认为是文件名

.c.o:
    $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<

(3)指定特定的makefile执行文件

     Makefile    make -f Make.Linux 或 make --file Make.AIX

(4)引用其它的makefile文件

    格式     include <filename>      

     如:    include foo.make a.mk b.mk c.mk e.mk f.mk

(5)一些常用变量    

    1、“$@”表示目标的集合,就像一个数组, “$@”依次取出目标,并执于命令,如:

bigoutput   littleoutput : text.g
generate   text.g   -$(subst output,,$@) > $@
等价于:
bigoutput : text.g
      generate text.g -big > bigoutput
littleoutput : text.g
           generate text.g -little > littleoutput

  2、“$<”表示第一个依赖文件,依次取出目标,并执于命令

%.o:%.c
	$(CC) $(CFLAGS) -c  -o $@  $<

    依次取出.o的目标文件和.c的第一个依赖文件进行编译。有多少个.o文件,就进行多少次编译。 

3、”$^“ 表示所有的依赖文件

  4、“@”字符在命令行前通常, make 会把其要执行的命令行在命令执行前输出到屏幕上。当我们用,那么,这个命令将不被 make 显示出来

9、最后给大家推荐一下资料吧

(1)、《跟我一起写makefile》,一部很精辟的书,简明扼要,全书无尿点

(2)、http://www.cnblogs.com/wang_yb/p/3990952.html (Makefile 使用总结 ),里面的有关makefile的函数很全,可以当做API来用

沙米的能力有限,希望大家留言,一起交流。

   

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!