• Makefile入门


    Makefile是什么?

    • 可以自动将项目涉及的所有源文件编译成可执行文件的工具
    • makefile写好之后,一键make就可以全部自动编译,非常的快捷和方便
    • make是一个命令工具,用来解释和执行makefile
    • 基本上所有的编译工具链都自带make命令

    源文件如何变成可执行文件?

    1. 源文件 ------(预处理)------.i文件
    2. .i文件 ------(编 译)------.S文件
    3. .S文件 ------(汇 编)------.o文件
    4. .o文件 ------(链 接)------.elf文件(elf文件只能在OS下运行,由操作系统解析成bin文件然后运行)
    5. .elf文件------(objcopy)------.bin文件(bin文件比elf文件更小)

    Makefile都包含什么内容?

    • 规则:
    target : prerequisites...
    	command
    	...
    	...
    

      target:目标,最终需要生成的文件

      prerequisites:依赖,生成最终文件需要的依赖文件

      command:命令,在依赖文件的基础上,执行命令,生成目标文件

    • Makefile是如何工作:
      • make会在当前目录下寻找名称为“makefile”和“Makefile”的文件
      • 找到文件中的第一个目标,并把这个文件作为最终的目标
      • 使用command将依赖变成目标,如果依赖不存在,就先生成依赖,递归执行
    • 综述:
      • 规则(显式规则和隐式规则)
      • 变量
      • 条件判断
      • 函数
      • 注释
      • 命令

     变量

    • 在 Makefile 中的定义的变量,就像是 C/C++语言中的宏一样,直接展开,其与 C/C++所不同的是,你可以在 Makefile 中改变其值
    • 变量在声明时需要给予初值,而在使用时,需要给在变量名前加上“$”符号,但最好用小括号“ () ”或是大括号“{}”把变量给包括起来。如果你要使用真实的“$”字符,那么你需要用“$$”来表示
    • 几种赋值符号的差别
      • = 是最基本的赋值,make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值
      • := 是覆盖之前的值,变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值
      • ?= 是如果没有被赋值过就赋予等号后面的值
      • += 是添加等号后面的值(类似字符串的接续)
    • 变量值的替换
    foo := a.o b.o c.o 
    bar := $(foo:.o=.c) # 以“.o”字串“结尾”全部替换成“.c”
    
    foo := a.o b.o c.o  
    bar := $(foo:%.o=%.c) # 以“.o”字串“结尾”全部替换成“.c”
    
    • “把变量的值再当成变量”
    x = y 
    y = z 
    a := $($(x))
    #在这个例子中,$(x)的值是“y” ,所以$($(x))就是$(y),于是$(a)的值就是“z”
    

      

    条件判断

    • 类似C语言中的条件判断
    <conditional-directive> 
        <text-if-true> 
    else 
        <text-if-false> 
    endif
    
    • conditional-directive表示关键字,共四种
      • ifeq,参数值是否相同
      • ifneq,参数值是否不同
      • ifdef,参数是否非空
      • ifndef,参数是否为空
    • 例子
    #判断$(CC)变量是否“gcc”,如果是的话,则使用GNU函数编译目标
    
    libs_for_gcc = -lgnu
    normal_libs =
    foo: $(objects)
    ifeq ($(CC),gcc)
        $(CC) -o foo $(objects) $(libs_for_gcc)
    else
        $(CC) -o foo $(objects) $(normal_libs)
    endif
    
    #判断变量的值是否为空
    bar =
    foo = $(bar)
    ifdef foo
        frobozz = yes
    else
        frobozz = no
    endif
    

      

    函数

    • 在 Makefile 中可以使用函数来处理变量,从而让我们的命令或是规则更为的灵活和具有智能
    • 函数调用后,函数的返回值可以当做变量来使用
    • 函数调用语法:
      • $(<function> <arguments>)
      • <function>就是函数名,make 支持的函数不多
      • <arguments>是函数的参数,参数间以逗号“,”分隔
      • 函数名和参数之间以“空格”分隔,函数调用以“$”开头,以圆括号或花括号把函数名和参数括起。
    • 字符串处理函数:
      • $(subst <from>,<to>,<text>)  把字串<text>中的<from>字符串替换成<to>。
    #将"feet on the street"中的"ee"替换成"EE",结果是"fEEt on the strEEt"
    
    $(subst ee,EE,feet on the street)
    
      • $(patsubst <pattern>,<replacement>,<text>)  模式字符串替换函数,将符合模式的字符串替换掉。
    #把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o”
    
    $(patsubst %.c,%.o,x.c.c bar.c)
    
      • $(strip <string>)  去掉<string>字串中开头和结尾的空字符。
      • $(findstring <find>,<in>)  在字串<in>中查找<find>字串,如果找到,那么返回<find>,否则返回空字符串。
    #第一个函数返回“a”字符串,第二个返回“”字符串(空字符串)
    
    $(findstring a,a b c)
    
    $(findstring a,b c)
    
      • $(filter <pattern...>,<text>)  以<pattern>模式过滤<text>字符串中的单词,保留符合模<pattern>的单词。可以有多个模式,返回符合模式<pattern>的字串。
    #保留符合格式的字符串
    
    sources := foo.c bar.c baz.s ugh.h
    foo: $(sources)
    cc $(filter %.c %.s,$(sources)) -o foo
    $(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”
    
      • $(filter-out <pattern...>,<text>)  反过滤函数,和filter 相反。
      • $(sort <list>)  给字符串<list>中的单词排序(升序) ,$(sort foo bar lose)返回“bar foo lose”,备注:sort函数会去掉<list>中相同的单词。
      • $(word <n>,<text>)  取字符串<text>中第<n>个单词。(从一开始) 示例:$(word 2, foo bar baz)返回值是“bar”。
      • $(wordlist <s>,<e>,<text>) 从字符串<text>中取从<s>开始到<e>的单词串。<s><e>是一个数字,示例: $(wordlist 2, 3, foo bar baz)返回值是“bar baz”。
      • $(words <text>)  统计<text>中字符串中的单词个数,$(words, foo bar baz)返回值是“3”。
      • $(firstword <text>) 取字符串<text>中的第一个单词。
    • 文件名操作函数
      • $(dir <names...>) 从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠( “/”)之前的部分。如果没有反斜杠,那么返回“./”。示例: $(dir src/foo.c hacks)返回值是“src/ ./”。
      • $(notdir <names...>) 从文件名序列<names>中取出非目录部分。非目录部分是指最后一个反斜(“/”)之后的部分
      • $(suffix <names...>) 从文件名序列<names>中取出各个文件名的后缀,示例:$(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”。
      • $(basename <names...>) 从文件名序列<names>中取出各个文件名的前缀部分,示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar hacks”。
      • $(addsuffix <suffix>,<names...>) 把后缀<suffix>加到<names>中的每个单词后面,示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。
      • $(addprefix <prefix>,<names...>) 把前缀<prefix>加到<names>中的每个单词前面,示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”。
      • $(join <list1>,<list2>) 把<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单个数要比<list2>的多,那么,<list1>中的多出来的单词将保持原样。如果<list2>的单词个数要比<list1>多,那么,<list2>多出来的单词将被复制到<list2>中。
      • $(foreach <var>,<list>,<text>)  这个函数的意思是,把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环过程中,<text>的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值
    #$(files)的值是“a.o b.o c.o d.o”
    
    names := a b c d
    files := $(foreach n,$(names),$(n).o)
    
      • $(if <condition>,<then-part>) 和ifeq很像
      • $(if <condition>,<then-part>,<else-part>)
      • $(call <expression>,<parm1>,<parm2>,<parm3>...)  唯一一个可以用来创建新的参数化的函数
    reverse =  $(1) $(2) 
    foo = $(call reverse,a,b)
    #foo的值就是“a b”
    
    reverse =  $(2) $(1) 
    foo = $(call reverse,a,b)
    #foo的值就是“b a”
    
    • 调试打印函数
      • $(error <text ...>) 打印信息 make会停止
      • $(warning <text ...>) 打印信息 make不会停止
      • $(info <text ...>) 和waring类似,级别更低

    注释

      Makefile没有多行注释,只有单行注释,注释标记是:#

    命令

    • 所有的shell命令都可以在Makefile中使用
    • 命令必须是以tab开头
    • 同事急需要执行多条命令时,需要写在同一行,使用接续符 链接

    总结

      Makefile只是帮助编译大型工程的工具,有自己独特的语法(可以将Makefile理解为一种小型的脚本语言),通常工程中使用的Makefile都很类似,参考已有的Makfile就可以写出自己需要的Makefile,难度不是很大,需要的就是熟练使用,如果要写出通用性很强,且高效的Makfile就需要一定的技术积累。

  • 相关阅读:
    你有犯错的权利
    面对人生这道程序,该如何编码?
    如何面对失败?
    知行:成长的迭代之路
    一份软件工程行业生存指南
    知行:程序员如何保持二者的平衡
    花钱的习惯
    互联网金融涌动下的冲动与借债
    docker 常用命令
    java 静态变量 静态代码块 加载顺序问题
  • 原文地址:https://www.cnblogs.com/chusiyong/p/11383127.html
Copyright © 2020-2023  润新知