Makefile基础知识
文章目录
Makefile 的引入
a.c
#include <stdio.h>
extern void func_b();
int main(int argc, const char *argv[])
{
func_b();
return 0;
}
b.c
#include <stdio.h
void func_b()
{
printf("This is B\n");
}
Makefile:
test : a.o b.o
gcc -o test a.o b.o
a.o : a.c
gcc -c -o a.o a.c
b.o : b.c
gcc -c -o b.o b.c
a.c ==> APP 的过程:
1. 预处理
2. 编译
3. 汇编
4. 链接
a.c ===> xxx.S ===> xxx.o
=====> test
b.c ===> yyy.S ===> yyy.o
缺点: 对所有的文件都再处理一次
应该分别编译,最后链接
gcc -c -o a.o a.c
gcc -c -o b.o b.c
gcc -o test a.o b.o
问题: 如何判断哪个文件被更新了
答: 比较目标文件和依赖文件的时间戳
Makefile的规则:
目标: 依赖1 依赖2 依赖n
`TAB` 命令Makfile的执行原理:
执行条件
- 目标不存在
- 依赖已更新
注: 当依赖比目标新时,执行命令
test : a.o b.o
gcc -o test a.o b.o
a.o : a.c
gcc -o a.o -c a.c
b.o : b.c
gcc -o b.o -c b.c
Makefile的语法
通配符:
- $@ 表示目标
- $< 表示第一个依赖文件
- $^ 表示所有的依赖文件
假想目标: .PHONY: target
使用Makefile时:
make [目标]
若无目标,则默认第一个目标
变量
即时变量(简单变量)
A := XXX # A的值即刻确定,在定义是即确定
B = XXX # B的值使用到时才确定
:= # 即时变量
= # 延时变量
?= # 延时变量, 如果是第1次定义才起效, 如果在前面该变量已定义则忽略这句
+= # 附加, 它是即时变量还是延时变量取决于前面的定义
Makefile的函数
a. $(foreach var,list,text)
b. $(filter pattern...,text) # 在text中取出符合patten格式的值
$(filter-out pattern...,text) # 在text中取出不符合patten格式的值
c. $(wildcard pattern) # pattern定义了文件名的格式,
# wildcard取出其中存在的文件
d. $(patsubst pattern,replacement,$(var)) # 从列表中取出每一个值
# 如果符合pattern
# 则替换为replacement
Makefile实例
a. 改进: 支持头文件依赖
Linux Makefile 生成 *.d 依赖文件以及 gcc -M -MF -MP 等相关选项说明
gcc -M c.c // 打印出依赖
gcc -M -MF c.d c.c // 把依赖写入文件c.d
gcc -c -o c.o c.c -MD -MF c.d // 编译c.o, 把依赖写入文件c.d
温顾知新:
Makefile中foreach使用
foreach 函数和别的函数非常的不一样。因为这个函数是用来做循环用的,Makefile中的foreach函数几乎是仿照于Unix标准Shell (/bin/sh)中的for语句,或是C-Shell(/bin/csh)中的foreach语句而构建的。它的语法是:
$(foreach <var>,<list>,<text>)
这个函数的意思是,把参数;中的单词逐一取出放到参数;所指定的变量中,然后再执行<
text>;所包含的表达式。每一次;会返回一个字符串,循环过程中,;的所返回的每个字符串会以空格分隔,最后当整个循环结束时,;所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。
所以,;最好是一个变量名,;可以是一个表达式,而;中一般会使用;这个参数来依次枚举;中的单词。举个例子:
names := a b c d
files := $(foreach n,$(names),$(n).o)
上面的例子中, ( n a m e ) 中的单词会被挨个取出,并存到变量“ n ”中,“ (name)中的单词会被挨个取出,并存到变量“n”中,“ (name)中的单词会被挨个取出,并存到变量“n”中,“(n).o”每次根据“ ( n ) ”计算出一个值,这些值以空格分隔,最后作为 f o r e a c h 函数的返回,所以, (n)”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以, (n)”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,(files)的值是“a.o
b.o c.o d.o”。
注意,foreach中的;参数是一个临时的局部变量,foreach函数执行完后,参数;的变量将不在作用,其作用域只在foreach函数当中。
Makefile中filter使用
$(filter PATTERN…,TEXT)
函数名称:过滤函数—filter。
函数功能:过滤掉字串“TEXT”中所有不符合模式“PATTERN”的单词,保留所有符合此模式的单词。可以使用多个模式。模式中一般需要包含模式字符“%”。存在多个模式时,模式表达式之间使用空格分割。
返回值:空格分割的“TEXT”字串中所有符合模式“PATTERN”的字串。
函数说明:“filter”函数可以用来去除一个变量中的某些字符串,我们下边的例子中就是用到了此函数。
示例:
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
使用“ ( f i l t e r (filter %.c %.s, (filter(sources))”的返回值给 cc 来编译生成目标“foo”,函数返回值为“foo.c
bar.c baz.s”
Makefile中filter-out使用
$(filter-out PATTERN…,TEXT)
函数名称:反过滤函数—filter-out。
函数功能:和“filter”函数实现的功能相反。过滤掉字串“TEXT”中所有符合模式“PATTERN”的单词,保留所有不符合此模式的单词。可以有多个模式。存在多个模式时,模式表达式之间使用空格分割。。
返回值:空格分割的“TEXT”字串中所有不符合模式“PATTERN”的字串。 函数说明:
“filter-out”函数也可以用来去除一个变量中的某些字符串, (实现和“filter”函数相反)。
示例:
objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o$(filter-out ( m a i n s ) , (mains), (mains),(objects))
实现了去除变量“objects”中“mains”定义的字串(文件名)功能。它的返回值为“foo.o bar.o”