Makefile学习总结

作者: Stan_Z | 来源:发表于2018-07-11 11:53 被阅读40次

    相信做过rom开发的,尤其做过机型适配的,一定知道makefile的重要性。
    这里我开了个章节,专门针对makefile做入门学习,学习的内容就是陈皓老师的 跟我一起写makefile.个人感觉作为入门还是比较经典的,当然更详细的可以看看 《GNU make 学习手册》

    Makefile入门(一):概述
    Makefile入门(二):MakeFile介绍
    Makefile入门(三):书写规则
    Makefile入门(四):书写命令
    Makefile入门(五):使用变量
    Makefile入门(六):使用条件判断
    Makefile入门(七):使用函数
    Makefile入门(八):make运行
    Makefile入门(九):隐含规则
    Makefile入门(十):使用make更新函数库文件
    Android.mk入门
    理解 Android Build 系统

    针对十一篇的内容做一个简单的提炼:

    一、概述

    1.什么是makefile?
    一个工程中的源文件不计其数,并且按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个 Shell脚本一样,其中也可以执行操作系统的命令。

    我们可以简单的把makefile认为是一份定义了源文件间依赖关系、如何编译各个源文件并生成可执行文件的说明书。makefile关系到了整个工程的编译规则,makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。

    2.程序的编译和链接
    首先要把源文件编译成中间代码文件,这个动作叫做编译(compile),
    然后再把大量的中间代码文件合成执行文件,这个动作叫作链接(link)

    make命令执行时,需要一个 makefile 文件,以告诉make命令如何去编译和链接程序。一个示例:我们的工程有8个c文件,和3个头文件,我们要写一个makefile来告诉make命令如何编译和链接这几个文件
    编译链接规则:
    1)如果这个工程没有编译过,那么我们的所有c文件都要编译并被链接。
    2)如果这个工程的某几个c文件被修改,那么我们只编译被修改的c文件,并链接目标程序。
    3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的c文件,并链接目标程序

    二、makefile介绍

    1.makefile规则
    target ... : prerequisites ...
    command
    ...
    ...
    target可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label)。对于标签这种特性,在后续的“伪目标”章节中会有叙述。
    prerequisites就是,要生成那个target所需要的文件或是目标。
    command也就是make需要执行的命令。(任意的shell命令)
    另外command前使用tab

    2.make是如何工作的

    1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
    2. 如果找到,它会找文件中的第一个目标文件(target),并把这个文件作为最终的目标文件。
    3. 如果目标文件不存在,或是目标文件所依赖的文件的修改时间要比目标文件新,那么,他就会执行后面所定义的命令来生成目标文件。
    4. 如果目标文件所依赖的文件也不存在,那么make会在当前文件中以此依赖的文件为目标寻找它的依赖性,如果找到则再根据那一个规则生成当前的依赖文件。(这有点像一个堆栈的过程)

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

    3.makefile的工作方式:

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

    三、书写规则与书写命令

    一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。
    1.通配符
    make支持三个通配符:“*”,“?”和“~”。这是和Unix的B-Shell是相同的。

    2.伪目标
    .PHONY: clean
    clean:
    rm *.o temp

    “伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显示地指明这个“目标”才能让其生效。为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第一个。一个示例就是,如果你的 Makefile需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,并且,所有的目标文件都写在一个Makefile中,那么你可以使 用“伪目标”这个特性。

    3.显示命令
    当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来,最具代表性的例子是,我们用这个功能来像屏幕显示一些信息。如:
    @echo 正在编译XXX模块......
    当make执行时,会输出“正在编译XXX模块......”字串,但不会输出命令,如果没有“@”,那么,make将输出:
    echo 正在编译XXX模块......
    正在编译XXX模块......

    4.命令执行
    当依赖目标新于目标时,也就是当规则的目标需要被更新时,make会一条一条的执行其后的命令。需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令
    例如:
    exec:
    cd /home/hchen; pwd
    会先执行cd 再执行pwd

    四. 使用变量

    1.变量的定义与使用
    举例:
    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
    

    “\”是换行的意思
    .o文件字符串被重复了两次,可以使用变量来定义:
    objects = main.o kbd.o command.o display.o \

                insert.o search.o files.o utils.o
    

    使用变量:$(objects)

    2.变量的赋值方式

    = 是最基本的赋值
    := 是覆盖之前的值
    ?= 是如果没有被赋值过就赋予等号后面的值
    += 是添加等号后面的值

    "=":递归赋值,即赋值后并不马上生效,等到使用时才真正的赋值,此时通过递归找出当前的值,所有在使用是很有可并不是开始赋的值,所有使用时有应
    ":=":直接赋值,这就是我们常规的那种赋值方式,一赋值马上有效。在没赋值是为空字符。前面的变量不能使用后面的变量,只能使用前面已定义好的变量
    "?=":仅仅在变量还没赋值的情况下才有效,所有一般用在第一次赋值。
    "+=": 在变量后加上字符

    五、条件判断

    条件表达式的语法为:
    <conditional-directive>
    <text-if-true>
    endif
    以及:
    <conditional-directive>
    <text-if-true>
    else
    <text-if-false>
    endif
    关键字分为两对:每一对都是相反的意思,所以挑一个讲就行
    ifeq 、ifneq
    ifdef 、ifndef

    1.ifeq <variable-name>比较参数的值是否相等

    ifeq (<arg1>, <arg2>)
    ifeq '<arg1>' '<arg2>'
    ifeq "<arg1>" "<arg2>"
    ifeq "<arg1>" '<arg2>'
    ifeq '<arg1>' "<arg2>"
    

    比较参数“arg1”和“arg2”的值是否相同。当然,参数中我们还可以使用make的函数。如:
    ifeq ((strip(foo)),)
    <text-if-empty>
    endif
    2.ifdef <variable-name> 如果变量<variable-name>的值非空,那到表达式为真。否则,表达式为假
    示例一:
    bar =
    foo = $(bar)
    ifdef foo
    frobozz = yes
    else
    frobozz = no
    endif
    示例二:
    foo =
    ifdef foo
    frobozz = yes
    else
    frobozz = no
    endif

    六、使用函数

    函数调用,很像变量的使用,也是以“”来标识的,其语法如下:(<function> <arguments>)
    或是
    { } 这里,就是函数名,make支持的函数不多。为函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。函数调用以“公式输入有误(subst a,b,公式输入有误(subst a,b,{x})”的形式。因为统一会更清楚,也会减少一些不必要的麻烦。

    具体函数可以看函数篇,不在此总结了。

    相关文章

      网友评论

        本文标题:Makefile学习总结

        本文链接:https://www.haomeiwen.com/subject/fadqpftx.html