打印

makefile的使用

[复制链接]
119|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
社畜一枚|  楼主 | 2018-10-5 20:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
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

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

397

主题

401

帖子

0

粉丝