Makefile研究 (一)—— 必备语法

摘自:http://blog.csdn.net/jundic/article/details/17535445

参考文档:http://blog.csdn.net/wrx1721267632/article/details/52840355

参考文档:http://wiki.ubuntu.org.cn/%E8%B7%9F%E6%88%91%E4%B8%80%E8%B5%B7%E5%86%99Makefile:%E9%9A%90%E5%90%AB%E8%A7%84%E5%88%99

  以前在windows上学习单片机、ARM的时候都是用IDE去管理编译代码,很多时候都不知道各个程序之间是怎么编译成可执行文件的,只要点下鼠标就可以了。虽然自己也有学习linux以及uboot 但都是看看改改程序。对于makefile认识也是单文件小工程的。认为makefile就是个编译工具,没必要深究。随着阅读的代码量的增多,发现,对于理解一些源码框架比如u-boot、inux 、andriod,要是看不懂makefile理解起来比较困难。而且在嵌入式系统开发中,考虑功能的模块化和可移植性,makefile是一个绕不过的东西。最近一次工作经历深深地刺激了我。是这样,我之前已经完整编译了整个工程,后面修改了某个配置文件,执行make 总是提示那个配置的东西不存在。即使make clean后再make还是会报一堆不知名的错误,我意识到makefile某些编译项用的是未改前的配置,现在有冲突了,当然会报错,这个系统是我们公司开发的有十几万个文件,每次编译都要二十多分钟,我当天一直查这个问题到11:00,伤心地回了家。第二天我默默地从服务器上重新下载新的code,直接修改配置文件,直接make正如我所料一次性编译通过。

        果然是该死的makefile第一次编译ok,修改配置文件,第二次编写就把代码编坏了。这种情况我相信很多搞程序的都遇到过,其实在此前就发现makefile不怎么完美,我经常添加模块还要去修改多个makefile才能使得添加的模块编译进去。跟进的项目终于接近尾声,决心好好研究下。

makefile 其实也是编程语言,他只是编译工具语言,也有语法,个人觉得我们实际应用中掌握常用的语法就好了下面是我总结的常用的Makefile语法。

一、Makefile 中的赋值方法

=    递归赋值 可以向后引用变量

:= 简单扩展 只能引用前面的变量

?= 如果没有赋值 则赋值一次

+=  在原来的基础上添加赋值变量

一个例子说明

 1 x = before  
 2 y = $(x)  
 3 x = later  
 4   
 5 xx = before  
 6 yy := $(xx)  
 7 xx = later  
 8   
 9 xxx = before  
10 xxx ?= later  
11   
12 yyy ?= before  
13 yyy = later  
14   
15 x += last  
16   
17 all :  
18     @echo "x=" $(x)  
19     @echo "y=" $(y)  
20     @echo "xx=" $(xx)  
21     @echo "yy=" $(yy)  
22     @echo "xxx=" $(xxx)  
23     @echo "yyy=" $(yyy)  

输出是

 x= later last

y= later last

xx=later

yy=before

xxx=before

yyy=later

 

二、Makefile 自动变量

所谓自动化变量,就是这种变量会把模式中所定义的一系列的文件自动地挨个取出,直至所有的符合模式的文件都取完了。这种自动化变量只应出现在规则的命令中。

下面是所有的自动化变量及其说明:

$@  

  表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。

$%

  仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是 "bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。

$<

  依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。

$^

  所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。

$?

  所有比目标新的依赖目标的集合。以空格分隔。

$+
这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。
$*
这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有用。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以," $*"的值就是"foo"。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$*"就是空值。
1 test.o : test1.c test2.c  
2           @echo $@  
3           @echo $<  
4           @echo $^ 

结果为

test.o

test1.c

test1.c test2.c

三、Makefile中的常用函数

1、文本替换 函数 $(subst ma,   wo   ,hello world!!)

将hello world!! 中间的wo 替换成ma

结果为  hello marld!!

 

2、格式替换函数 $(patsubst %.c ,%.o , test.c test1.c test2.c)

将.c 的格式替换成.o

结果为 test.o test1.o test2.o

 

3、去掉空格函数 $(strip a    b               c)

结果 a b c

4、字符串查找函数

$(findstring a,a b c d)

在字符串a b c d 中查找 a  ,找到就返回查到的值 ,否则为 "  "

上面结果为 a

 

5、格式匹配过滤函数 $(filter %.c %.s , mod1.c mod2.o mod3.s mod4.h)

查找格式是%.c 和 %.s 的文件

结果为 mod1.c mod2.s

 

6、格式不匹配函数 $(filter-out %.c %.s, mod1.c mod2.o mod3.s mod4.h)

它的和filter相反

结果为 mod2.o mod4.h

 

7、$(sort  hello  world branck)

按字母排列

结果为  branck hello world

 

8 、抽取文件目录函数$( dir source/hello.c inc/hello.h mksh)

结果: source/  inc/  ./

 

9、抽取文件名函数 $(nodir source/hello.c inc/hello.h mksh)

结果: hello.c hello.h mksh

 

10、提取文件名后缀函数 $(suffix source/hello.c inc/hello.h mksh)

结果: .c  .h

 

11、去除后缀名函数 $(basename sourc/hello.c inc/hello.h mksh)

结果: source/hello   inc/hello mksh

 

12、添加后缀名函数 $(addsuffix .c ,hello fun)

结果: hello.c fun.c

 

13、添加前缀函数 $(addprefix source/ , mod1.c mod2.c mod3.c)

结果: source/mod1.c source/mod2.c source/mod3.c

 

14、格式匹配函数 $(wildcard  source/*.c)

查找source 目录下所有.c 文件

 

15、格式匹配用法

SRCS = mod1.c mod2.c mod3.c

$(SRCS : %.c = %.o)

结果为mod1.o mod2.o mod3.o

 

16、目录循环搜索

dirs :=  dir1 dir2 dir3 dir4

files := $( foreach dir ,$(dirs) ,$(wildcard $(dir)/*)

查找 目录 dir1 dir2 dir3 dir4 目录下所有的文件。

 

17、在Makefile 中执行shell 脚本的方法

$(shell command)

SRCS := $(shell ls *.c)

则srcs 中所有的c程序。

 

四、Makefile 中的编译基础知识

1、编译时 指定头文件路径 用 -I

2、编译时 指定库路径用 -L 指定函数库名 用 -l (小写L )

3、在Makefile 指定宏定义 配置到程序中 -D

 

五、条件判断语句

1、ifdef/ifndf  (param)

       endif    

#    param  是否定义

2、ifeq/ifneq (a,b)

       endif  

#  a和b是否相等

我想Makefile 的基础知识掌握这些就ok了,关键是应用。

 

posted @ 2017-09-24 23:01  西门吹雪~~~  阅读(508)  评论(0编辑  收藏  举报