赞
踩
一个简单的Makefile文件包含的一系列“规则”:
- 目标(target) ... : 依赖(prerequiries) ...
- <tab>命令(command)
如果“依赖文件”比“目标文件”更加新,那么执行“命令”来重新生成“目标文件”。
命令被执行的2个条件:依赖文件比目标文件新,或目标文件还没生成。
小生给大家推荐一个Linux内核学习交流地:869634926整理了一些个人觉得比较好的Linux内核学习书籍、视频资料分享给大家,有需要的可以自行添加哦!
A. $(foreach var,list,text)
含义是for each var in list, change it to text。
对于list中每个元素,取出来赋值给var,然后把var用text替换。
例如:
- objs := a.o b.o
- dep_files := $(foreach f, $(objs), .$(f).d) // 最终dep_files := .a.o.d .b.o.d
B. $(wildcard pattern)
pattern 所列出的文件是否存在,把存在的文件都列出来。
例如:
src_files := $(wildcard *.c) // src_files中列出了当前目录下的所有.c文件
第1个Makefile,简单,低效:
- test: main.c sub.c sub.h
- gcc -o test main.c sub.c
第2个Makefile,效率高,但相似规则太多太啰嗦,不支持检测头文件:
- test: main.o sub.o
- gcc -o test main.o sub.o
-
- main.o: main.c
- gcc -c -o main.o main.c
-
- sub.o: sub.c
- gcc -c -o sub.o sub.c
-
- clean:
- rm *.o test -f
第3个Makefile,效率高,精炼,不支持检测头文件:
- test : main.o sub.o
- gcc -o test main.o sub.o
-
- %.o : %.c
- gcc -c -o $@ $<
-
- clean:
- rm *.o test -f
注:Makefile变量(特殊变量)
∗不包含扩展名的文件名;∗不包含扩展名的文件名;
∗不包含扩展名的文件名;∗不包含扩展名的文件名;
+ 所有的依赖文件,以空格分开,以出现的先后顺序,可能包含重复的依赖文件;
<第一个依赖文件的名称;<第一个依赖文件的名称;
<第一个依赖文件的名称;<第一个依赖文件的名称;
? 所有时间戳比目标文件晚的依赖文件,并以空格分开;
@目标文件的完整名称;@目标文件的完整名称;
@目标文件的完整名称;@目标文件的完整名称;
^ 所有不重复的依赖文件,以空格分开;
$% 如果目标是归档成员,则该变量表示目标的归档成员名称;
第4个Makefile,效率高,精炼,支持检测头文件,但需要手工添加头文件规则:
- test : main.o sub.o
- gcc -o test main.o sub.o
-
- %.o : %.c
- gcc -c -o $@ $<
-
- sub.o : sub.h
-
- clean:
- rm *.o test -f
第5个Makefile,效率高,精炼,支持自动检测头文件:
- objs := main.o sub.o
-
- test : $(objs)
- gcc -o test $^
-
- # 需要判断是否存在依赖文件
- # .main.o.d .sub.o.d
- dep_files := $(foreach f, $(objs), .$(f).d)
- dep_files := $(wildcard $(dep_files))
-
- # 把依赖文件包含进来
- ifneq ($(dep_files),)
- include $(dep_files)
- endif
-
- %.o : %.c
- # 生成依赖文件$@.d
- gcc -Wp,-MD,.$@.d -c -o $@ $<
-
- clean:
- rm *.o test -f
-
- distclean:
- rm $(dep_files) *.o test -f
注:
ifneq(ARG1,ARG2) 判断参数是否不相等;
include 类似于C语言的#include,将内容原封不动包含进来;
GCC命令加"-Wp,-MD,@.d"会自动生成依赖文件@.d"会自动生成依赖文件
@.d"会自动生成依赖文件@.d"会自动生成依赖文件
@.d;
可用来编译应用程序,其特点:
1)支持多个目录、多层目录、多个文件;
2)支持给所有文件设置编译选项;
3)支持给某个目录设置编译选项;
4)支持给某个文件单独设置编译选项;
5)简单、易用;
A. make命令的使用
执行make命令时,会去当前目录下查找名为"Makefile"的文件,并根据它的指示执行操作,生成第一个目标。
可以使用"-f"选项指定要查找并执行的文件,而不再使用名为"Makefile"的文件,例如:
make -f Makefile.build
可以使用"-C"选项指定目录,切换到其他目录里去,例如:
make -C a/ -f Makefile.build
注:切换到目录"a/",指定查找文件Makefile.build
可以指定目标,不再默认生成第一个目标:
make -C a/ -f Makefile.build other_target
B. 立即变量、延时变量
变量定义的语法形式:
- A = xxx // 延时变量
- B ?= xxx // 延时变量,只有第一个定义时赋值才成功;如果曾定义过,此赋值无效
- C := xxx // 立即变量
- D += yyy // 如果D在前面是延时变量,那么现在还是延时变量;
- // 如果D在前面是立即变量,那么现在还是立即变量
延时变量的值,在使用时才展开、确定。例如:
- A = $@ # 目标文件完整名称
-
- test: # 此时才定义目标文件名称
- @echo $A
上面变量A在执行时才确定,值为test,是延时变量。
如果用"A := @",A是立即变量,而此时@",A是立即变量,而此时
@",A是立即变量,而此时@",A是立即变量,而此时
@为空,因此A值为空。
C. 变量的导出(export)
编译程序时,我们不断使用"make -C dir"切换到其他目录,执行其他目录的Makefile。如果想要某个变量值在所有目录都可见,需要将其export出来。
例如,"CC = $(CROSS_COMPIE)gcc",CC变量表示编译器,在整个makefile执行过程中都是一样的。定义它后,要用"export CC"将其导出。
D. Makefile中可以使用shell命令
例如:
TOPDIR := $(shell pwd)
立即变量TOPDIR等于shell命令pwd的执行结果,即当前目录(通常是执行make命令的目录)。
E. 在Makefile中怎么放置第1个目标
执行make命令时,如果不指定目标,那么它默认生成第1个目标。所以“第一个目标”位置很重要。有时不太方便将第1个目标完整地放在文件前面,此时可以做文件的前面直接放置目标,在后面再完善它的依赖和命令。
例如:
- first_target: // 这句话放前面
- ... // 其他代码,比如include其他文件后得到后面的xxx变量
- first_target: $(xxx) $(yyy) // 在文件的后面再来完善
- command
F. 假想目标(伪目标)
如果Makefile中有这样的目标:
- clean:
- rm -f $(shell find -name "*.o")
- rm -f $(TARGET)
如果当前目录下,恰好有名为"clean"的文件,那么执行"make clean"时就不会执行那些删除命令。
此时,我们需要把"clean"这个目标,设置为"假想目标",这样可以确保执行"make clean"时执行删除命令。
使用下面语句,将"clean"设置为假想目标:
.PHONY : clean
G. 常用的函数
1)foreach
用法:$(foreach var, list,text)
解释:for each var in list, change it to text
对于list中每个元素,取出来赋值给var,然后将var改为text所描述的形式
例如:
- objs := a.o b.o
- dep_files := $(foreach f, $(objs), .$(f).d) // 最终dep_files := .a.o.d .b.o.d
2)wildcard
用法:$(wildcard pattern)
解释:pattern所列出文件如果存在,就把存在的文件都列出来
例如:
src_files := $(wildcard *.c) // 最终src_files中列出了当前目录下所有.c文件
3) filter
用法:$(filter pattern...,text)
解释:把text中符合pattern格式的内容,filter(过滤)出来以留下来。
例如:
- obj-y := a.o b.o c/ d/
- DIR := $(filter %/, $(obj-y)) // 结果为:c/ d/
4)filter-out
用法:$(filter-out pattern..., text)
解释:把text中复合pattern格式的内容,filter-out(过滤)出来以丢弃。
例如:
- obj-y := a.o b.o c/ d/
- DIR := $(filter-out %/, $(obj-y)) // 结果为:a.o b.o
5)subst
用法:$(subst from,to,text)
解释:将text中的东西,从from替换为to
$(subst a,the,There is a big tree) // 结果为:There is the big tree
6)patsubst
用法:$(patsubst pattern, replacement, text)
解释:寻找"text"中符合格式"pattern"的字,用"replacement"替换它们。"pattern"和"replacement"中可以使用通配符。
例如:
- subdir-y := c/ d/
- subdir-y := $(patsubst %/, %, $(subdir-y)) // 结果为:c d
A. 在Makefile文件中确定要编译的文件、目录,比如obj-y += main.oobj-y += a/"Makefile"文件总是被"Makefile.build"包含的。
B. 在Makefile.build中设置编译规则,有3条:
1)怎么编译子目录?进入子目录编译:
- $(subdir-y):
- make -C $@ -f $(TOPDIR)/Makefile.build
2)怎么编译带你过去目录中的文件?
- %.o : %.c
- $(CC) $(CFLAGS) $(EXTRA_CLAGS) $(FLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $<
3)当前目录下的.o和子目录下的built-in.o要打包起来:
- built-in.o: $(cur_objs) $(subdir_objs)
- $(LD) -r -o $@ $^
C. 顶层Makefile中把顶层目录的built-in.o链接成APP
- $(TARGET) : built-in.o
- $(CC) $(LDFLAGS) -o $(TARGET) built-in.o
韦东山通用Makefile,是韦东山老师(百问网)为嵌入式Linux应用项目编写的工程构建makefile。支持多目录、单目标的项目。
根目录下,存在Makefile和Makefile.build两个文件。这两个文件非常重要,make命令能递归查找每个子目录,就是这2个Makefile文件的功劳。
在每个需要搜索源码的子目录下,都要添加一个子Makefile文件,便于递归搜索。
项目目录结构:
- $ tree
- .
- ├── bin
- │ └── led.sh
- ├── business
- │ ├── main.c
- │ └── Makefile
- ├── config
- │ ├── config.c
- │ └── Makefile
- ├── display
- │ ├── disp_manager.c
- │ ├── framebuffer.c
- │ └── Makefile
- ├── font
- │ ├── font_manager.c
- │ ├── freetype.c
- │ └── Makefile
- ├── include
- │ ├── common.h
- │ ├── config.h
- │ ├── disp_manager.h
- ...
- ├── Makefile
- ├── Makefile.build
- ├── page
- │ ├── main_page.c
- │ ├── Makefile
- │ └── page_manager.c
- ├── ui
- │ ├── button.c
- │ └── Makefile
- └── unittest
- ├── client.c
- ├── disp_test.c
- ...
根目录下的Makefile,主要有这几点作用:
- # 根目录下的Makefile
-
- # 延时变量, 只有第一次定义赋值才成功.而该变量在/etc/profile中. 已定义为arm-linux-gnueabihf-
- CROSS_COMPILE ?=
- # 定义延时变量
- # e.g. as = arm-linux-gnueabihf-as
- AS = $(CROSS_COMPILE)as
- LD = $(CROSS_COMPILE)ld
- CC = $(CROSS_COMPILE)gcc
- CPP = $(CC) -E
- AR = $(CROSS_COMPILE)ar
- NM = $(CROSS_COMPILE)nm
-
- STRIP = $(CROSS_COMPILE)strip
- OBJCOPY = $(CROSS_COMPILE)objcopy
- OBJDUMP = $(CROSS_COMPILE)objdump
-
- # export全局变量, 可供其他Makefile使用
- export AS LD CC CPP AR NM
- export STRIP OBJCOPY OBJDUMP
-
- # 定义编译选项
- CFLAGS := -Wall -O2 -g # 立即变量
- # -I 指定头文件目录
- # $(shell pwd) 将shell命令pwd输出(即当前目录)作为结果
- # 该句含义是为编译器指定头文件目录为当前目录(执行make命令的目录)下的include目录
- CFLAGS += -I $(shell pwd)/include # 追加赋值
-
- # 定义链接选项
- LDFLAGS := -lts -lpthread -lfreetype -lm
-
- # export 全局变量
- export CFLAGS LDFLAGS
-
- # 当前目录作为顶层目录TOPDIR
- TOPDIR := $(shell pwd)
- export TOPDIR
-
- # 定义立即变量, 目标名称, 也是最终生成的二进制目标文件名称
- TARGET := test
-
- # 定义变量记录要搜索的子目录(子目录必须包含一个makefile文件)
- # 注意: 由于一个目标只能包含一个main函数,因此unittest目录和business目录,只能加入一个
- obj-y += display/
- # obj-y += unittest/
- obj-y += input/
- obj-y += font/
- obj-y += ui/
- obj-y += page/
- obj-y += config/
- obj-y += business/
-
- # 第一个目标
- all : start_recursive_build $(TARGET)
- @echo $(TARGET) has been built!
-
- start_recursive_build:
- # 切换到目录 $(TOPDIR), 找Makefile.build文件, 并执行make命令
- # 我们查看Makefile.build文件
- @echo start_recursive_build
- @echo obj-y = $(obj-y)
- make -C ./ -f $(TOPDIR)/Makefile.build
-
- # 依赖built-in.o 由Makefile.build生成
- $(TARGET) : built-in.o
- $(CC) -o $(TARGET) built-in.o $(LDFLAGS)
-
- clean:
- rm -f $(shell find -name "*.o")
- rm -f $(TARGET)
- rm -f test
-
- distclean:
- rm -f $(shell find -name "*.o")
- rm -f $(shell find -name "*.d")
- rm -f $(TARGET)
- rm -f test
根目录Makefile.build,主要工作是:
- # Makefile.build
- # 立即变量, 用于记录伪目标
- PHONY := __build
- # 先声明目标, 等到文件后面完善
- __build:
-
- # 定义立即变量, 值为空
- obj-y :=
- subdir-y :=
- EXTRA_CFLAGS :=
-
- # include当前目录下的Makefile, 注意执行命令时, 是会通过make -C命令切换工作目录的
- # 也就是说, 不同make命令执行路径下, Makefile指的是不同的文件
- include Makefile
-
- # obj-y := a.o b.o c/ d/
- # $(filter %/, $(obj-y)) : c/ d/
- # __subdir-y : c d
- # subdir-y : c d
-
- # 定义立即变量
- # $(filter %/, $(obj-y)) 从变量obj-y中过滤出以"/"结尾的目录名
- # $(patsubst %/,%,$(filter %/, $(obj-y))) 去掉obj-y中以"/"结尾的目录名中的"/"
- __subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y)))
-
- # 追加赋值, subdir-y表示要子目录
- subdir-y += $(__subdir-y)
-
- # c/built-in.o d/built-in.o
-
- # foreach(var,list,text), 意为foreach var in list, change it to text
- # 将子目录列表subdir-y中, 每一项(每个文件名)f, 都修改为f/built-in.o
- # 也就是说, 每个子目录, 都会对应生成一个名为 "子目录名/built-in.o"的文件 (.o文件是链接文件)
- subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)
-
- # a.o b.o
-
- # 定义立即变量, 从obj-y中过滤掉目录名(名称以"/"结尾), 只剩下普通文件(.o文件)
- cur_objs := $(filter-out %/, $(obj-y))
- # 将cur_objs中的每个文件名f, 替换为f.d, 即加上".d"后缀, 代表该源码文件的依赖文件
- dep_files := $(foreach f,$(cur_objs),.$(f).d)
- # 如果依赖文件存在, 就列出来重新赋值给dep_files
- dep_files := $(wildcard $(dep_files))
-
- # 如果依赖文件列表不为空, 就直接包含(include)依赖文件列表
- ifneq ($(dep_files),)
- include $(dep_files)
- endif
-
- # 每个子目录名(不含"/")追加到伪目标
- PHONY += $(subdir-y)
-
- # 定义规则 (目标 : 依赖)
- __build : $(subdir-y) built-in.o
-
- # 以子目录每一项为目标, 而每一项都是一个目录名
- $(subdir-y):
- @echo subdir-y = $@
- # 进入到每个子目录($@代表目标名称), 查找并执行顶层目录的Makefile.build
- make -C $@ -f $(TOPDIR)/Makefile.build
-
- # 定义built-in.o依赖规则
- # cur_objs 从obj-y过滤出的普通文件(.o文件)
- # subdir_objs 子目录下的built-in.o
- built-in.o : $(cur_objs) $(subdir_objs)
- # LD 代表交叉编译器的链接器; -r 选项代表可重定位的输出, 一个输出文件可再次作为ld输入
- # -o 设置输出文件; $@ 目标名称; $^ 所有不重复的依赖文件
- $(LD) -r -o $@ $^
-
- # 定义延时变量, 单个目标的依赖文件: .目标名称.d
- dep_file = .$@.d
-
- # 模式规则, 所有没有显式规则的%.o目标, 会匹配该隐含规则
- # 实际不一定会调用
- %.o : %.c
- # 生成依赖文件dep_file (即.$@.d), 后面Makefile文件会用
- $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $<
-
- # 定义伪目标
- .PHONY : $(PHONY)
子目录makefile。比如font/ 子目录,如果根目录下的Makefile添加将"font/"添加进了obj-y,那么子目录必须包含一个子makefile。
- EXTRA_CFLAGS :=
- CFLAGS_file.o :=
-
- obj-y += font.o
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。