gnu实用工具
.h文件为头文件用来放函数和变量的声明。
.c或者.cpp文件为源文件用来放函数或变量的定义。
源文件编译之后生成目标文件(.o文件)
将目标文件链接之后生成可执行文件
将众多中间目标文件(.o文件)打包叫库文件(.a文件)
编译:编译器只检查语法错误,函数和变量是否声明。
链接:连接器将函数和全局变量链接,检查函数和变量的定义。
写好makefile/Makefile文件,一个make命令可以自动搞定一些。
makefile使用规则:
1.如果工程没有编译过,需要编译和链接所有c文件和目标文件。
2.如果工程里的某几个c文件被修改,只需要编译修改的c文件,并链接所有目标文件。
3.如果头文件被修改,只需要编译引用了被修改的头文件的c文件,并链接所有目标文件。
--------------------------------------------------
makefile的格式:
target . . . : prerequisites . . .
command
...
...
target:可以是.o文件、可执行文件、标签(lable),多个文件用空格分开。
prerequisites:生成target所需要的文件或目标(目标文件或头文件等),多个文件用空格分开。
command:跳格键tab开头,利用prerequisites生成target的shell命令(make需要执行的命令)
可以在makefile文件的结尾定义一些标签,比如打包,备份,清空等:
clean:
rm elf a.o b.o c.o d.o
在执行make clean命令后就会删除所有的目标文件。
可以使用续行符\
make命令:执行makefile文件,会比较target和prerequisites的时间戳,如果prerequisites比target新,或者target不存在就会执行command,否则会跳过command。
--------------------------------------------------
共用的头文件可以简化
a.o : a.h b.h c.h
b.o : b.h c.h d.h
c.0 : c.h d.h
d.o : d.h
等价于:
$(obj) : d.h
a.o b.o c.o : c.h
a.o b.o : b.h
a.o : a.h
--------------------------------------------------
一个makefile组成
obj = a.o b.o c.o d.o
CC = gcc
elf : $(obj)
$(CC) -o elf $(obj)
$(obj) : d.h
a.o b.o c.o : c.h
a.o b.o : b.h
a.o : a.h
.PHONY : clean
clean :
-rm elf $(obj)
Makefile中有哪些内容:
显式规则;
隐晦规则;
变量定义;
文件指示:makefile中的引用和根据条件指定有效部分。
#:使用#进行注释。
@:在commands前面加@就会取消在终端显示执行编译等命令的信息,eg:
@arm-linux-gcc ……
--------------------------------------------------
Make的工作顺序:
1. 读入所有makefile
2. 读入include的其他makefile
3. 初始化变量
4. 推导隐晦规则,分析所有规则
5. 为所有目标文件创建依赖关系链
6. 依据依赖关系,决定哪些目标需要重新生成
7. 执行生成的命令
--------------------------------------------------
在makefile中引用其他的makefile
-include a.makefile b.makefile c.makefile d.makefile
make会在当前目录寻找文件,也会在/usr/local/bin或/usr/include找,并加载到当前位置;
在include前面加-会忽略错误,继续执行。
-I
---------------------------------------------------
文件寻找
当有大量源文件时,make只会在当前目录中寻找,需要另外指令路径。
Makefile的特殊环境变量VPATH可以指定源文件路径,eg:VPATH=../mycode。
还可以使用makefile的关键字vpath来指定路径,有三种用法:
1.vpath #为符合patern模式的文件指定搜索目录
2.vpath #清除符合模式patern的文件的搜索目录
3.vpath #清除所有已经设置好的文件搜索目录
Patern要以%开头。
Eg:vpath %.h ../headers
vpath %.c ../srcs
--------------------------------------------------
伪目标
伪目标一般放在makefile文件结尾,在make命令中调用,在makefile中用.PHONY来指定,可同时定义多个伪目标,用空格分开,需要用make+伪目标名才能执行。
可以给伪目标指定依赖文件,如果要一次生成多个可执行文件可以将伪目标放在文件开头,作为默认目标。
all : elf1 elf2 elf3
.PHONY : all
这样make all命令就会生成三个可执行文件。
伪目标同样也可以作为其他目标的依赖。
.PHONY: cleanall cleanobj cleanelf
Cleanall: cleanobj cleanelf
makefile文件中加清空文件便于重新编译:
clean :
rm elf $(obj)
更好的写法是:
.PHONY : clean
clean :
-rm elf $(obj)
用.PHONY来表示clean是一个伪目标。
在rm前面加-表示忽略错误,继续向后执行。
--------------------------------------------------
多目标
Eg:
func-A-elf func-B-elf: same.h
Generate same.h -$(funcname) > $@
等价于:
func-A-elf: same.h
Generate same.h -func-A > func-A-elf
func-B-elf: same.h
Generate same.h -func-B > func-B-elf
--------------------------------------------------
静态模式
静态模式可以更容易的定义多目标规则。
语法格式:
: :
Targets:定义了一组目标文件,可以有通配符。
Target-pattern:指定了目标文件targets的模式。
Prereq-pattern:目标的依赖模式。
Eg:
obj = a.o b.o
all : $(objs)
$(objs) : %.o : %.c
$(CC) -c $(CFLAGS) $< -o $@
等价于:
a.o : a.c
$(CC) -c $(CFLAGS) a.c -o a.o
b.o : b.c
$(CC) -c $(CFLAGS) b.c -o b.o
--------------------------------------------------
自动生成依赖性
.c文件包含了很多.h文件,修改不方便,为了处理这种关系。
gcc -MM XXX.c 可以显示源文件和头文件的依赖关系。
makefile中的处理办法:为每一个.c文件都生成一个.d文件用来存放对应的.c文件的依赖关系。
产生.d文件的方法:
%.d: %.c
@set -e; rm -f $@; \ #删除所有的目标文件.d文件
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ #根据.c文件生成.d文件,$$$$是随机编号
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ #替换
rm -f $@.$$$$ #删除临时文件
--------------------------------------------------
makefile中的命令:
1.显示命令:
@echo XXXX #只显示XXX
make的参数:
make -n/--justprint #只显示命令,不执行命令
make -s/--slient #禁止命令显示
2.执行命令:
如果下一条命令的执行需要依赖上一条命令,那么两条命令写在一行,中间用分号隔开。
cd /home/canux;pwd
3.命令出错:
如果某个命令出错整个规则执行都出错,为了忽略某个错误,让整个规则运行完,在命令前加-。
make -i/--ignore-errors #某个命令出错,继续执行其他命令,忽略这个错误
make -k/--keep-going #某个命令出错了,不执行这个规则,继续执行其他规则
-S/--no-keep-going/--stop #禁止-k选项
4.嵌套执行make
在大的工程里,不同模块和功能的源文件放在不同的目录中,所以在每个目录中有一个Makefile文件,在根目录写一个总控的Makefile,便于维护。
smart210:
cd /board/sumsung/smart210 && $(MAKE)
总控Makefile中的变量可以通过申明传递到下级Makefile中,但是不会覆盖下级Makefile中的变量。
传递到下级Makefile的申明方法:
export
禁止传递到下级Makefile的申明方法:
unexport
传递所有变量:
export
等价于:
variable := value
export variableexport variable = value
等价于:
variable = value
export variable
等价于:
export variable := value
SHELL和MAKEFLAGS这两个变量总是要传递到下层Makefile中。
MAKEFLAGS是make的参数,但是有几个参数不往下传递。
5.定义命令包
可以给相同命令序列定义一个变量,以define开始,以endef结束。
define 变量名称
命令1
命令2
...
endef
--------------------------------------------------
使用变量
如果prerequisites包含的文件很多或者会重复用到,那么将这些文件当作一个字符串,给他取个别名:
定义:
obj = a.o b.o c.o d.o
使用:
elf : $(obj)
gcc -o target $(obj)
clean :
rm elf $(obj)
1.变量的基础
变量:字符、数据、下划线组成,不应该含有:#=空格回车,大小写敏感。
变量在定义是需要赋初值,在使用时需要$开头,最好还要用()或{}括起来。
2.变量中的变量
在定义变量时可以用其他变量来构造变量的值。
一种就是用等号,左侧是变量右侧是值,右侧可以是还没有定义的变量,也可以是已经定义的变量。
foo = $(bar)
bar = $(ugh)
ugh = huh
另一种是使用:=,前面的变量不能使用后面的变量。
x : = foo
y := $(x) bar
x := later
等价于:
y = foo bar
x = later
定义一个空格
nullstring:=
space:=$(nullstring)#end of the line
#用来表示变量的结束,前面的空格都属于变量的内容。
foo ?= bar
?=符号表示如果foo没有被定义过那么foo=bar,如果foo之前被定义过,那么这条语句无效。
3.变量的高级用法
变量值的替换:
格式:$(var:a=b)或 ${var:a=b}
将变量var中以a结尾的元素中的a替换成b,eg:
foo := a.o b.o
bar:=$(foo:.o=.c)
等价于:
bar := a.c b.c
静态模式定义的变量替换:
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
依赖于被替换的字符串中有相同的模式,所以可以用%来替换。
把变量的值再当成变量:
x=y
y=z
a:=$($(x))
等价于
a=z
通过给参数赋值得到变量的不同的值
a_objects := a.o b.o c.o
1_objects := 1.o 2.o 3.o
sources := $($(a1)_objects:.o=.c)
ifdef so_sort
func := sort
else
func := strip
endif
bar := a s g k s
foo := $($(func) $(bar))
把变量当变量也可以用在操作符的左边:
dir = foo
$(dir)_source := $(wildcard $(dir)/*.c)
define $(dir)_print
lpr $($(dir)_source)
endef
4.追加变量
可以使用+=操作符给变量追加值
obj = a.o b.o c.o
obj += d.o
还可以
obj = $(obj) d.o
如果变量没有定义过那么+=就等价于=,如果定义过就追加。
如果之前为:=那么+=就是:=
如果之前为=那么+=就是=
5.override指示符
如果在Makefile中已经定义的变量,又通过make的命令行参数来设置或是环境变量,那么Makefile对这些变量的赋值会被忽略,如果想在makefile中设置这类参数的值,那么可以使用override重定义。
override =
override :=
override +=
6.多行变量
使用define关键字来设置变量的值,可以换行。
define 变量名
命令
函数
文字
变量
endef
7.环境变量
系统的环境变量会在make开始运行时载入,如果在Makefile中定义了这个变量,或通过make命令传递个这个变量,那么默认系统变量会被覆盖。
make -e #系统环境变量会覆盖Makefile中定义的变量。
在嵌套调用的Makefile中,上层Makefile中定义的环境变量会以系统环境变量的方式传递到下层Makefile。变量需要用export申明才能传递。
8.目标变量
为某个目标设置局部变量,这个变量只在这条规则和连带规则中有效,这个局部变量也只在这个作用范围有效。局部变量可以和全局变量同名。
格式:
:
: override
prog : CFLAGS = -g #定义局部变量
prog : prog.o foo.o
prog.o : prog.c
$(CC) $(CFLAGS) prog.c
foo.o : foo.c
$(CC) $(CFLAGS) foo.c
不管全局变量CFLAGS是什么,在生成prog目标过程中都是-g。
9.模式变量
给定一种模式,然后把变量定义在所有符合这种模式的目标上。
格式:
: #自定义变量
: override #系统默认环境变量或make命令传入的变量
%.o : CFLAGS = -O
所有.o文件的参数都是-O。
--------------------------------------------------
使用条件判断
endif
else
endif
表示条件关键字,有四个:
ifeq:比较两个参数是否相等。
ifeq(,)
ifneq:比较两个参数是否不相等。
ifneq(,)
ifdef:变量值非空,表达式值为真。
ifdef
ifndef:变量值为空,表达式值为真。
ifndef
自动化变量不要放进条件表达式。
--------------------------------------------------
makefile能使用函数
1.函数的调用语法
$( )
${ }
function:make支持的函数名
arguments:函数参数,以,分割多个参数。
make调用函数像调用变量一样,需要$开头,然后用(){}括起来。
2.字符串处理函数
字符串替换函数:
$(subst ,,)
把字符串中的替换成
模式字符串替换函数:
$(patsubst ,,)
查找中的单词,单词用空格、tab、回车分割,如果单词符合模式,就以替换。pattern可以用通配符%来表示任意长度的单词,如果replacement中也有%,那么replacement中的%就是pattern中的%所表示的单词。
去空格函数:
$(strip )
去掉字符串开头和结尾的空字符
查找字符串函数:
$(findstring ,)
在字符串中查找字符串,如果找到然会,否则返回空。
过滤函数:
$(filter ,)
以模式过滤字符串中的单词,保留符合模式的单词,返回符合模式的字符串。
反过滤函数:
$(filter-out ,)
以模式过滤字符串中的单词,除去符合模式的单词,返回不符合模式的字串。
排序函数:
$(sort )
给字串中的单词排序,默认是升序,按照首字母从小到大。
取单词函数:
$(word ,)
取字符串中的第个单词,从1开始,如果超过字串大小就返回空。
取单词串函数:
$(wordlist ,,)
从字符串中取从到的单词字串,如果s超出就返回空,如果e超出就返回没有超出的部分。
单词个数统计函数:
$(words )
从中统计单词个数。
首单词函数:
$(firstword )
去中的第一个单词。
3.文件名操作函数
取目录函数
$(dir )
从names中取出目录部分,也就是/之前的部分,如果没有/就返回./;
总的来说就是除去单纯的文件名。
取文件函数
$(notdir )
从names中取出文件,除去目录部分。
取后缀函数
$(suffix )
从names中取出文件名的后缀,如果没有后缀就返回空串。
取前缀函数
$(basename )
从names中取出文件名的前缀部分,如果没有前缀就返回空串。
所谓前缀就是除了后缀剩下的部分。
加后缀函数
$(addsuffix ,)
把后缀加到中的每个单词后面。
加前缀函数
$(addprefix , )
把前缀加到中的每个单词前面。
连接函数
$(join ,)
就是把list2中的单词按照位置的对应顺序加到list1中的后面形成一个行的单词序列。
如果list1的单词多,多出来的就不变;如果list2单词多,多出来的就复制到list1后面。
4.foreach循环函数
语法:
$(foreach ,,)
var是一个变量;
list是一个表达式,由多个单词组成;
text是一般使用var这个变量来枚举list中的单词。
foreach每次从list中取一个单词赋值给var,text每次根据var的值得到一个结果,
最后返回text的所有结果组成的序列,每次的结果空格分开。
5.if条件函数
语法:
$(if ,)
$(if ,,)
condition是表达式,如果表达式返回非空字符串,那么表达式为真,执行then-part;
否则执行else-part;返回执行的结果。
6.call函数
$(call ,,...)
call会用parm 1等参数替换expression这个表达式原来的参数,expression表达式的返回值就是call的函数的返回值。
7.origin定位函数
$(origin )
定位variable这个变量的定义情况,variable只能是变量名。
返回结果:
undefined:未定义
default:默认定义
environment:环境变量
file:在Makefile中定义的变量
conmandline:在命令行定义的变量
override:用override重定义的变量
automatic:自动化变量
8.shell函数
$(shell )
执行shell命令command,返回命令执行结果。
9.控制make的函数
产生一个致命的错误:
$(error )
是错误信息;调用这个函数make会产生一个致命错误然后退出运行。
输出一段警告信息:
$(warning )
是错误信息,调用这个函数打印这个错误信息,make继续运行。
--------------------------------------------------
make命令的运行
1.make的退出码
0:成功执行
1:出错返回1
2:使用make的-q选项并且使得一些目标不需要更新,返回2.
2.指定Makefile
gnu的make命令默认在当前目录按顺序找三个文件:
GNUmakefile、makefile、Makefile,找到就执行。
make -f/--file/--makefile filename #可以用参数来指定另外的make文件。
3.指定目标
默认make的最终目标是Makefile中的第一个目标,其他目标都是第一个目标连带出来的。
make只执行终极目标和这个终极目标连带的目标,其他的目标不执行。
任何Makefile中的目标(包括伪目标)都可以被指定成终极目标:make 目标名称
环境变量MAKECMDGOALS存放了指定的终极目标的列表。
可以写一些伪目标:
all:编译所有目标
clean:删除所有make创建出来的文件
intall:安装已编译好的程序
print:列出改变过的源文件
tar:把源程序打包备份
dist:创建一个压缩文件
TAGS:更新所有目标
check和test:测试Makefile流程
4.检查规则
make -n/--just-print/--dry-run/--recon #不还行makefile中的规则,只检查命令或执行序列
只打印命令,把规则和连带规则下的命令打印出来,不执行命令。
make -t/--touch #把目标文件的时间更新,但不更改目标文件
就是假装编译目标,但是只是更新了时间没有真正更新。
make -W /--what-if=/--assume-new=/--new-file=
指定一个源文件或依赖文件,make会自动推导来运行依赖于这个文件的命令。
5.make的参数
-b/-m #忽略和其他版本make的兼容性
-B/--always-make #所有目标都重新编译
-C
/--directory=
#指定读取makefile的目录
-debug[=] #输出make的调试信息
options的取值:
a:输出所有调试信息
b:输出不需要重新编译的目标
v:输出更多不需要重新编译的文件
i:输出所有隐含规则
j:输出执行规则中命令的详细信息
m:输出make读取、更新、执行Makefile的信息
-d #相当于-debug=a
-e/--environment-overrides #指明环境变量的值覆盖Makefile中的变量的值
-j[]/--jobs[=] #指定同时运行的命令的个数
-l /--load-average[=]/-max-load[=] #指定make运行命令的负载
-o /--old-file=/==assume-old= #指定不重新生成的目标文件,即使依赖文件比目标文件新
-p/--print-data-base #输出Makefile中所有数据,包括规则和变量
-q/--question #仅检查指定的目标是否需要更新,0表示需要更新,2表示出错
-r/--no-builtin-rules #禁止make使用任何隐含规则
-R/--no-builtin-variabes #禁止make使用任何作用于变量上的隐含规则
-s/--silent/--quiet #在命令运行时不输出命令的输出
-w/--print-derectory #输出运行makefile之前和之后的信息。
--no-print-directory #禁止-w选项
--warn-undefined-variables #只要make发现没有定义的变量就输出警告信息
--------------------------------------------------
隐含规则
make的自动推导功能:只要make看到.o文件,就会自动关联.c文件,同时command中的gcc也会自动关联:
a.o : a.c a.h
gcc -c a.c a.h
等价于:
a.o : a.h
make有自己的隐含规则库,从前到后进行自动推导,如果自定义的规则省略了,那么就调用隐含规则。
1.编译c程序的隐含规则
.o会自动将依赖目标推导为.c,并且自动推导命令为$(CC) -c $(CPPFLAGS) $(CFLAGS)
2.编译c++程序的隐含规则
.o目标会自动推导依赖为.cc或.C,生成命令$(CXX) -c $(CPPFLAGS) $(CFLAGS)
3.汇编和汇编预处理的隐含规则
.o目标会自动推导依赖目标为.s,默认使用编译器as,生成命令$(AS) $(ASFLAGS).
.s会自动推导依赖为.S,默认使用c预编译器,生成命令为$(AS) $(ASFLAGS)
4.链接文件的隐含规则
目标依赖于.o,一般使用c编译器运行链接程序ld,生成命令为$(CC) $(LDFLAGS) .o $(LOADLIBES) $(LDLIBS)
-----------------------------
隐含规则使用的变量
如果你在Makefile中重新定义了隐含规则中的变量那么会使用用户定义的变量。
否则就使用默认的变量。
1.和命令相关的变量
AR 函数打包命令ar
AS 汇编语言编译程序as
LD 链接程序ld
CC c语言编译程序gcc
CXX c++语言编译程序g++
CPP c程序预处理器$(CC) -E
RM 删除文件命令rm -f
2.和命令参数相关的变量
ARFLAGS 函数库打包程序ar命令的参数
ASFLAGS 汇编编译器命令as参数
CFLAGS c语言编译器参数
CXXFLAGS c++语言编译器参数
CPPFLAGS c预处理器参数
LDFLAGS 连接器参数
-----------------------------
定义模式规则
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
依赖的%决定目标的%。
自动化变量
$^ 所有的依赖文件,除去了重复的依赖
$< 第一个依赖文件
$@ 目标文件
$* 不包含扩展名的目标文件名称
$+ 所有的依赖文件,用空格分开,包含重复的依赖
$? 所有时间戳比目标文件新的依赖文件,以空格分开
$% 如果目标是函数库成员,则表示目标的成员
自动化变量加上D或F可以获取文件的目录名或当前目录符合模式的文件名。
$(@D) 得到$@的目录
$(@F) 得到$@的文件名
其他同理
-----------------------------
makefile也兼容老风格的后缀规则
.c.o是双后缀等价于%.o:%.c
.c是单后缀等价于%:%.c
后缀规则不能有依赖文件,只能有目标
.c.o:
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<</div>
make能自动识别一些后缀,也可以用伪目标.SUFFIXES来定义后缀
.SUFFIXES : .hack .win #定义自己的后缀
.SUFFIXES #删除默认后缀
--------------------------------------------------
使用make更新函数库文件
函数库就是目标文件的打包文件,一般用ar来打包。
函数库文件和它的组成格式:archive(member1 member2...)
foolib(hack.o kludge.o) : hack.o kludge.o
ar cr foolib hack.o kludge.o
(%.o) : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o
$(AR) R $@ $*.o
$(RM) $*.o |