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就需要一定的技术积累。

原文地址:https://www.cnblogs.com/chusiyong/p/11383127.html