首页 > 体检 > 正文

嵌入式linux基础之makefile

日期:2023-03-23 19:01:54 来源 : 哔哩哔哩

在linux中我们使用gcc来编译单个文件,然而我们在工作中可不单单只有一个文件,因此面对大工程我们可以使用makefile来对整个项目工程进行编译。

那什么是makefile,其实就是定义了一整套编译规则的一个文件,然后使用make才启动这项规则,下面先来介绍一下make 这个命令

执行make命令的时候,首先CPU会去搜索这三个文件


(资料图片仅供参考)

“GNU-makefile”、“makefile”和“Makefile”。其按顺序找这三个文件,一旦找到,就开始读取这个文件并执行

还要记住make的环境变量是MAKEFILES

如果你的当前环境中定义了环境变量 MAKEFILES ,那么,make 会把这个变量中的值做一个类似于include 的动作,把这个MAKEFILES的变量指包含进去;

make的工作步骤

读入所有的 Makefile。

读入被 include 的其它 Makefile。

初始化文件中的变量。

推导隐晦规则,并分析所有规则。

为所有的目标文件创建依赖关系链。

根据依赖关系,决定哪些目标要重新生成。

执行生成命令

make的参数

make命令的参数有许多种,然而我们也没必要全部记住,重要的2个先记住

主要记住参数-f和-C

-f:   指定需要执行的 makefile文件

-C: 指定读取 makefile 的目录。如果有多个“-C”参数,make 的解释是

后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。如:“make -C ~hchen/test

-C prog”等价于“make -C ~hchen/test/prog”。

合并使用实例:

make -C a/ -f Makefile.build

意思是到a目录里面找到Makefile.build,并执行

makefile基础

介绍完make之后,接下来,来看一下makefile的规则,其实整个规则是围绕着这三个概念

目标:依赖

规则

当依赖时间  >  目标,说明有需要更改,makefile才会执行规则(规则有些地方也叫命令)

目标就是我们需要生成的文件,比如可执行文件;目标也有伪目标

伪目标用.PHONY来定义

.PHONY  XXX文件 定义为假想目标,就不会去判断目标是否存在

依赖就是生成目标需要的文件

规则也叫命令,就是执行这项规则去生成这个目标,其实大部分的规则其实也就是采用上一篇GCC编译规则去生成目标

实例:

main: main.o input.o calcu.o

gcc -o main main.o input.o calcu.o

规则有分显示规则和隐示规则

显示规则:代码中有写出来的,可直接根据代码推导;

隐示规则:代码中没写,是make指令自己的规则,隐示规则是重点,在介绍隐示规则之前

先读一下这张表,名为“自动化变量表”

隐含规则:

重点记住这几条即可

1、只要 make 看到一个 .o 文件,它就会自动的把 .c (或者.cpp,根据其设置的环境变量而定)文件加在依赖关系中,并根据环境变量自动推导其相关规则

2、%的作用,比如当sub.o找到不到依赖的时候,回来这一条做匹配;

例如:

上面找不到.o的规则的会去第6行执行

3、同一个目标,一条规则有命令,另一个没有,他们的依赖会合并在一起

例如:

第5行和第8行合并

重点记住这三条,其他的有遇到在临时去查

重点记住:makefile会想尽办法生成最终目标,然后选择是否删除中间件(这个用户可以定义)

重要的知识点汇总

当然作为编程脚本,不仅仅这些,把它和C语言作对比,也有以下这些同样的功能,

引用其它的文件文件的功能

include filename

make 命令开始时,会找寻 include 所指出的其它的文件,并把其内容安置在当前的位置

make的寻找路径顺序

1、当前路径

2、如果 make 执行时,有 -I 或 --include-dir 参数,在这个参数所指定的目录下去寻找

3、/usr/local/bin 或 /usr/include

如果有文件没有找到的话,make 会生成一条警告信息,继续载入其它的文件,一旦完成 makefile 的读取,make 会再重试这些没有找到,或是不能读取的文件make 才会出现一条致命信息;

如果你想让 make 不理那些无法读取的文件,而继续执行,你可以在include 前加一个减号“-”。如:-include

文件搜寻功能:

大写的VPATH

VPATH:

如果没有指明这个变量,make 只会在当前的目录中去找寻依赖文件和目标文件。

如果定义了这个变量,那么,make 就会在当前目录找不到的情况下,到所指定的目录中去找寻文件了

小写的vpath

vpath:

主要目的是搜索,搜索什么东西,在哪里搜索?

例如vpath %.h ../headers

搜索.h文件,在headers目录下搜错

如遇到连续的vpath

例如

vpath %.c foo

vpath % blish

vpath %.c bar

搜索.c文件,在foo目录下搜索,然后这blish,最后在bar目录下搜索

多目标的时候

bigoutput littleoutput : text.g

generate text.g -$(subst output,,$@) > $@

等同于

bigoutput : text.g

generate text.g -big > bigoutput

littleoutput : text.g

generate text.g -little > littleoutput

@ 字符的作用

@ 字符在命令行前,那么,这个命令将不被 make 显示出来

例如 @echo 1234

如果没有@的话,会把整条命令都打印出来,有的话只会打印1234

分号的作用

如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令

例如

示例一

exec:

cd /home/hchen

pwd

示例二:

exec:

cd /home/hchen; pwd

当我们执行 make exec 时,第一个例子中的 cd 没有作用,pwd 会打印出当前的 Makefile 目录,而

第二个例子中,cd 就起作用了,pwd 会打印出“/home/hchen”

错误命令忽略:

有些时候,命令的出错并不表示就是错误的,为了忽略错误,我们可以在 Makefile 的命令行前加一个减号 - (在 Tab 键之

后),标记为不管命令出不出错都认为是成功的

还有一个全局的办法是,给 make 加上 -i 或是 --ignore-errors 参数,那么,Makefile 中所有命

令都会忽略错误

make 的参数的是 -k 或是 --keep-going ,这个参数的意思是,如果某规则中

的命令出错了,那么就终止该规则的执行,但继续执行其它规则。

export  的作用

传递上级变量给子级的makefile用

“嵌套执行”中比较有用的参数,-w 或是 --print-directory 会在 make 的过程中输出

一些信息,让你看到目前的工作目录

有两个变量,一个是 SHELL ,一个是 MAKEFLAGS ,这两个变量不管你是否 export,

其总是要传递到下层 Makefile 中,特别是 MAKEFLAGS 变量,其中包含了 make 的参数信息

定义命令包

定义一系列命令的集合

定义这种命令序列的语法以 define 开始,以 endef 结束

define run-yacc

yacc $(firstword $^)

mv y.tab.c $@

endef

使用:

foo.c : foo.y

$(run-yacc)

要使用这个命令包,我们就好像使用变量一样,make在执行命令包时,命令包中的每个命令会被依次独立执行

override指示符

我们可能会有这样的需求;可以通过命令行来指定一些附加的编译参数,

对一些通用的参数或者必需的编译参数在Makefile中指定,而在命令行中指定一些特殊的参数。

对于这种需求,我们就需要使用指示符“override”来实现

EXEF = foo

override CFLAGS += -Wall –g

.PHONY : all debug test

all : $(EXEF)

foo : foo.c

………..

………..

$(EXEF) : debug.h

$(CC) $(CFLAGS) $(addsuffix .c,$@) –o $@

debug :

@echo ”CFLAGS = $(CFLAGS)”

执行:make CFLAGS=-O2 将显式编译“foo”的过程是“cc –O2 –Wall –g foo.c –o foo”。

执行“make CFLAGS=-O2 debug”可以查看到变量“CFLAGS”的值为“–O2 –Wall –g”。

另外,这个例子中,如果把变量“CFLAGS”之前的指示符“override”去掉,

使用相同的命令将得到不同的结果

如果make 指定了“-e”参数,那么,系统环境变量将覆盖 Makefile 中定义的变量

变量

变量的使用

$(变量名)

变量的赋值

•“=”延时赋值,该变量只有在调用的时候,才会被赋值

•“:=”直接赋值,与延时赋值相反,使用直接赋值的话,变量的值定义时就已经确定了。

•“?=”若变量的值为空,则进行赋值,通常用于设置默认值。

•“+=”追加赋值,可以往变量后面增加新的内容。

变量高级用法

$(var:a=b) 或是 ${var:a=b}

把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”

实例

foo := a.o b.o c.o

bar := $(foo:.o=.c)

在foo这个变量的值中,把.o结尾的换成.c

$(bar) 的值就是“a.c b.c c.c”

另外一种变量替换的技术是以“静态模式”(参见前面章节)定义

foo := a.o b.o c.o

bar := $(foo:%.o=%.c)

目标变量

首先在makefile中定义的变量都是全局变量

prog : CFLAGS = -g

定义在目标之后的变量,只在目标作用域范围内有效

模式变量

我们可以给定一种“模式”,可以把变量定义

在符合这种模式的所有目标上

%.o : CFLAGS = -O

同样,模式变量的语法和“目标变量”一样

意思是说只有目标为.o的文件才能使用这变量

条件判断

有变量,当然也有类似于C语言的 if .....else......条件判断

条件判断的关键字有四个

第一个ifeq

比较参数 arg1 和 arg2 的值是否相同

第二个ifneq

ifneq和ifeq相反

第三个条件关键字是 ifdef

ifdef

如果变量的值非空,那到表达式为真。否则,表达式为假

第四个条件关键字是 ifndef

ifndef

和ifdef意思相反

后缀规则:

后缀规则不允许任何的依赖文件,如果有依赖文件的话,那就不是后缀规则,那些后缀统统被认为是文件名

单后缀:

例如:.c 相当于 % : %.c

双后缀:

.c.o:(相当于%.o : %.c)

$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<

.c.o 那么其就是双后缀规则,意义就是 .c 是源文件的后缀,.o 是目标文件的后缀

函数库文件的后缀规则

.c.a:

$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $.o$(AR) r $@ $.o

$(RM) $.o

其等效于:

(%.o) : %.c$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $.o

$(AR) r $@ $.o$(RM) $.o

函数

最后介绍一下函数:

函数包括函数名和参数

语法:

$()或者${}

函数和变量的括号最好一样,

如使用 $(subst a,b,$(x)) 这样的形式,而不是 $(subst a,b, ${x}) 的形式

记住格式语法就行,不需要去背,用到的时候这临时去查

总结:

上一篇: 下一篇:
x

推荐阅读