注: 以下内容来自朱老师物联网大讲堂课件

<>1. 为什么需要Makefile

工程项目中c文件太多管理不方便,因此用Makefile来做项目管理,方便编译链接过程。
在一个正式的软件项目中,由很多个.c和.h文件构成,此时如果直接在命令行编译,就会像这样:gcc a.c b.c c.c d.c e.c f.c g.c
-o exe 每次编译都要输入一堆东西很麻烦,这个问题严重影响工作效率,所以我们就使用Makefile来进行管理

makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual
C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。

<>2. 一个简单的Makefile示例

<>2.1 Makefile中的一些基本概念

目标:目标定格写(如上例:led.bin),后面是冒号(冒号后面是依赖),目标就是我们要去make xxx的那个xxx,就是我们最终要生成的东西。

依赖:依赖是用来产生目标的原材料,也就是(目标:)后面的部分。

命令:命令前面一定是Tab,不能是空格,也不能说多个空格。命令就是要生成那个目标需要做的动作。

Makefile中的注释用#
Makefile中注释使用#,和shell一样。

<>2.2 Makefile的基本工作原理
其一,当我们执行 make xx 的时候,Makefile会自动执行xx这个目标下面的命令语句。 其二,当我们make
xx的时候,是否执行命令是取决于依赖的。依赖如果成立就会执行命令,否则不执行。 其三,我们直接执行make 和make 第一个目标
效果是一样的。(第一个目标其实就是默认目标)
make的依赖性决定了make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。

<>2.3 Makefile的执行过程

在默认的方式下,也就是我们只输入make命令。那么,

* make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
* 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“led.bin”这个文件,并把这个文件作为最终的目标文件。
* 如果led.bin文件不存在,或是led.bin所依赖的后面的 .o
文件的文件修改时间要比led.bin这个文件新,那么,他就会执行后面所定义的命令来生成led.bin这个文件。
*
如果led.bin所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)
* 当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件声明make的终极任务,也就是执行文件led.bin了。
<>3.一个稍微复杂的Makefile示例

CC = arm-linux-gcc LD = arm-linux-ld OBJCOPY = arm-linux-objcopy OBJDUMP = arm-
linux-objdump AR = arm-linux-ar INCDIR := $(shell pwd) # C
预处理器的flag,flag就是编译器可选的选项CPPFLAGS := -nostdlib -nostdinc -I$(INCDIR)/include # C
编译器的flagCFLAGS := -Wall -O2 -fno-builtin #导出这些变量到全局,其实就是给子文件夹下面的Makefile使用
export CC LD OBJCOPY OBJDUMP AR CPPFLAGS CFLAGS objs := start.o sdram_init.o led
.o uart.o main.o objs += lib/libc.a uart.bin: $(objs) $(LD) -Tlink.lds -o uart.
elf $^ $(OBJCOPY) -O binary uart.elf uart.bin $(OBJDUMP) -D uart.elf > uart_elf.
dis gcc mkv210_image.c -o mkx210 ./mkx210 uart.bin 210.bin lib/libc.a: cd lib;
make; cd .. %.o : %.S $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c %.o : %.c $(CC) $(
CPPFLAGS) $(CFLAGS) -o $@ $< -c .PHONY:clean clean: rm *.o *.elf *.bin *.dis
mkx210-f cd lib; make clean; cd ..
<>3.1 Makefile中的通配符和Makefile自动推导

(1)%
%是Makefile中的通配符,代表一个或几个字母。也就是说%.o就代表所有以.o为结尾的文件。
(2)"~"
波浪号(“~ ”)字符在文件名中也有比较特殊的用途。如果是“ ~/test
”,这就表示当前用户的$HOME目录下的test目录。而“~hchen/test”则表示用户hchen的宿主目录下的test目录。(这些都是Unix下的小知识了,make也支持)而在Windows或是MS-DOS下,用户没有宿主目录,那么波浪号所指的目录则根据环境变量“HOME”而定。
(3)" * "
通配符代替了你一系列的文件,如“ * .c”表示所以后缀为c的文件。一个需要我们注意的是,如果我们的文件名中有通配符,如:“ *
”,那么可以用转义字符“\”,如“\ * ”来表示真实的“*”字符,而不是任意长度的字符串。

所谓自动推导其实就是Makefile的规则。当Makefile需要某一个目标时,他会把这个目标去套规则说明,一旦套上了某个规则说明,则Makefile会试图寻找这个规则中的依赖,如果能找到则会执行这个规则用依赖生成目标。

<>3.2 Makefile中定义和使用变量

Makefile中定义和使用变量,和shell脚本中非常相似。相似是说:都没有变量类型,直接定义使用,引用变量时用$(CC)

<>3.3 伪目标(.PHONY)

(1)伪目标意思是这个目标本身不代表一个文件,执行这个目标不是为了得到某个文件或东西,而是单纯为了执行这个目标下面的命令。
(2)伪目标一般都没有依赖,因为执行伪目标就是为了执行目标下面的命令。既然一定要执行命令了那就不必加依赖,因为不加依赖意思就是无条件执行。
(3)伪目标可以直接写,不影响使用;但是有时候为了明确声明这个目标是伪目标会在伪目标的前面用.PHONY来明确声明它是伪目标。
.PHONY:clean clean: rm *.o *.elf *.bin *.dis mkx210 -f cd lib; make clean; cd .
.
<>3.4 Makfile中引用其他Makefile(include指令)

(1)有时候Makefile总体比较复杂,因此分成好几个Makefile来写。然后在主Makefile中引用其他的,用include指令来引用。

在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是:
include< filename >filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符)

注意:在include前面可以有一些空字符,但是绝不能是[Tab]键开始。include和可以用一个或多个空格隔开。
例如:
include $(srctree)/arch/$(SRCARCH)/Makefile
<>3.5 命令前面的@用来静默执行

(1)在makefile的命令行中前面的@表示静默执行。
(2)Makefile中默认情况下在执行一行命令前会先把这行命令给打印出来,然后再执行这行命令。
(3)如果你不想看到命令本身,只想看到命令执行就静默执行即可。
未添加@
all: echo "hello"
执行结果
root@ubuntu:/mnt/hgfs/winshare/uboot_practice# make echo "hello" hello
root@ubuntu:/mnt/hgfs/winshare/uboot_practice#
添加@
all: @echo "hello"
执行结果
root@ubuntu:/mnt/hgfs/winshare/uboot_practice# make hello root@ubuntu:/mnt/hgfs
/winshare/uboot_practice#
<>3.6 Makefile中几种变量赋值运算符

(1)= 最简单的赋值
(2):= 一般也是赋值
以上这两个大部分情况下效果是一样的,但是有时候不一样。
用=赋值的变量,在被解析时他的值取决于最后一次赋值时的值,所以你看变量引用的值时不能只往前面看,还要往后面看。
用:=来赋值的,则是就地直接解析,只用往前看即可。

=赋值练习
var1=abc var2=$(var1)de var1=gh all: @echo $(var1) @echo $(var2)
执行结果
root@ubuntu:/mnt/hgfs/winshare/uboot_practice# make gh ghde root@ubuntu:/mnt/
hgfs/winshare/uboot_practice#
:= 练习
var1=abc var2:=$(var1)de var1=gh all: @echo $(var1) @echo $(var2)
执行结果
root@ubuntu:/mnt/hgfs/winshare/uboot_practice# make gh abcde root@ubuntu:/mnt/
hgfs/winshare/uboot_practice#
(3)?= 如果变量前面并没有赋值过则执行这条赋值,如果前面已经赋值过了则本行被忽略。(实验可以看出:所谓的没有赋值过其实就是这个变量没有被定义过)
var = Donke var ?= Dong all: @echo $(var)
执行结果:Donke
var ?= Dong all: @echo $(var)
执行结果:Dong

(4)+= 用来给一个已经赋值的变量接续赋值,意思就是把这次的值加到原来的值的后面,有点类似于strcat。(在shell
makefile等文件中,可以认为所有变量都是字符串,+=就相当于给字符串stcat接续内容)(注意一个细节,+=续接的内容和原来的内容之间会自动加一个空格隔开)
var = Donke var += Dong all: @echo $(var)
输出结果:Donke Dong

注意:Makefile中并不要求赋值运算符两边一定要有空格或者无空格,这一点比shell的格式要求要松一些。

<>3.7 Makefile的环境变量

(1)makefile中用export导出的就是环境变量。一般情况下要求环境变量名用大写,普通变量名用小写。
#导出这些变量到全局,其实就是给子文件夹下面的Makefile使用 export CC LD OBJCOPY OBJDUMP AR CPPFLAGS
CFLAGS

(2)环境变量和普通变量不同,可以这样理解:环境变量类似于整个工程中所有Makefile之间可以共享的全局变量,而普通变量只是当前本Makefile中使用的局部变量。所以要注意:定义了一个环境变量会影响到工程中别的Makefile文件,因此要小心。

(3)Makefile中可能有一些环境变量可能是makefile本身自己定义的内部的环境变量或者是当前的执行环境提供的环境变量(譬如我们在make执行时给makefile传参。make
CC=arm-linux-gcc,其实就是给当前Makefile传了一个环境变量CC,值是arm-linux-gcc。我们在
make时给makefile传的环境变量值优先级最高的,可以覆盖makefile中的赋值)。这就好像C语言中编译器预定义的宏__LINE__
__FUNCTION__等一样。

<>3.8 Makefile中使用通配符

(1)* 若干个任意字符
(2)? 1个?表示一个任意字符,n个?表示输出n个字符
(3)[] 将[]中的字符依次去和外面的结合匹配,如果有则输出,无则不输出

练习
all: echo *.c echo ?.c echo ??.c echo *.h echo [abcdef].c
执行结果:
root@ubuntu:/mnt/hgfs/winshare/uboot_practice/2.14.makefile# make echo *.c 12.
c 234.c a.c b.c d.c echo ?.c a.c b.c d.c echo ??.c 12.c echo *.h a.h b.h echo
[abcdef].c a.c b.c d.c root@ubuntu:/mnt/hgfs/winshare/uboot_practice/2.14.
makefile#
(4)% 也是通配符,表示任意多个字符,和 * 很相似,但是%一般只用于规则描述中,又叫做规则通配符。
%.o : %.S $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c %.o : %.c $(CC) $(CPPFLAGS) $
(CFLAGS) -o $@ $< -c
<>3.9 Makefile的自动变量

(1)为什么使用自动变量。在有些情况下文件集合中文件非常多,描述的时候很麻烦,所以我们Makefile就用一些特殊的符号来替代符合某种条件的文件集,这就形成了自动变量。
(2)自动变量的含义:预定义的特殊意义的符号。就类似于C语言编译器中预制的那些宏__FILE__一样。
(3)常见自动变量:
$@ 规则的目标文件名
$< 规则的依赖文件名
$^ 依赖的文件集合

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

make使用变量CC来定义编译器,并且传递变量CFLAGS(编译器参数)、CPPFLAGS(C语言预处理器参数)、TARGET_ARCH(目标机器的结构定义)给编译器然后加上参数-c,后面跟变量$
<(第一个依赖文件名),然后是参数-o加变量$@(目标文件名)。综上所述,一个C编译的具体命令将会是:
$ {CC} $ {CFLAGS} $ {CPPFLAGS} $ {TARGET_ARCH} –c $< -o $@
更多Makefile 资料
《跟我一起学makefile》
《葵花宝典》

技术
©2019-2020 Toolsou All rights reserved,
份额已超宁德时代!LG化学确认将分拆电池业务部门Qt学习7——模态和非模态对话框数字滚动抽奖小程序vue 监听 Treeselect 选择项的改变 dedecms网站被黑 劫持到其他网站如何解决内存溢出和内存泄漏的区别、产生原因以及解决方案SQL Server 数据库词汇表1190 反转每对括号间的子串 leetcode蝗灾虫群上亿只很少发生碰撞 蝗虫要成自动驾驶功臣最优化方法总结:公式解、数值优化、求解思想