【Embedded System】Makefile

💘ARM Embedded System,欢迎关注我的Gitee

💦Makefile介绍

在这里插入图片描述
  Makefile 或 makefile: 告诉make维护一个大型程序,该做什么。Makefile说明了组成程序的各模块间的相互
关系及更新模块时必须进行的动作,make按照这些说明自动地维护这些模块。

💦Makefile 是什么

  Makefile的宗旨就是:让编译器知道要编译一个文件需要依赖其他的哪些文件。当那些依赖文件有了改变,编译器会自动的发现最终的生成文件已经过时,而重新编译相应的模块。
  makefile带来的好处就是—“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。

💦Makefile的文件名

  默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解释这个文件。在这三个文件名中,最好使用“Makefile”这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用“GNUmakefile”,这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感,但是基本上来说,大多数的make都支持“makefile”和“Makefile”这两种默认文件名。
  当然,你可以使用别的文件名来书写Makefile,比如:“Make.Linux”,“Make.Solaris”,“Make.AIX”等,如果要指定特定的Makefile,你可以使用make的“-f”和“–file”参数,如:make -f Make.Linux或make --file Make.AIX。
  执行make命令时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序。
  Makefile是一个文本形式的数据库文件,其中包含一些规则来告诉make处理哪些文件以及如何处理这些文件。这些规则主要是描述哪些文件(称为target目标文件,不要和编译时产生的目标文件相混淆)是从哪些别的文件(称为dependency依赖文件)中产生的,以及用什么命令(command)来执行这个过程。

💦Makefile内容

  Makefile里主要包含了五个部分内容:显式规则、隐式规则、变量定义、文件指示和注释。
  1、显式规则。显式规则说明了如何生成一个或多个目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
  2、隐晦规则。由于make有自动推导的功能,所以隐式规则可以让我们比较粗糙地简略地书写Makefile,这是由make支持的。
  3、变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上
  4、文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样。
  5、注释。和UNIX的Shell脚本一样Makefile中只有行注释, 其注释是用“#”字符,这个就像C/C++中的“//”或者“ /*”。

在Makefile中的命令,必须要以[Tab]键开始!!!
Makefile的默认文件名为GNUmakefile、makefile或Makefile,多数Linux程序员使用第三种。
执行make命令时,会对磁盘上的文件进行检查,如果目标文件的生成或被改动时的时间至少比它的一个依赖文件还旧的话,make就执行相应的命令,以更新目标文件。目标文件不一定是最后的可执行文件,可以是任何一个中间文件并可以作为其他目标文件的依赖文件。

💦引用其它的Makefile

  在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是:include <filename>,filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符)
  Make支持三种通配符: “*”、“?”、“[…]”
  include foo.make *.mk $(bar)等价于:include foo.make a.mk b.mk c.mk e.mk f.mk

💦Makefile的规则

  Makefile文件主要含有一系列的规则,每条规则包含以下内容:
  一个目标(target),即make最终需要创建的文件,如可执行文件和目标文件;目标也可以是要执行的动作,如“clean”。
  一个或多个依赖文件(dependency)列表,通常是编译目标文件所需要的其他文件。
  一系列命今(command),是make执行的动作,通常是把指定的相关文件编译成目标文件的编译命令,每个命令占一行,且每个命令行的起始字符必须为TAB字符。
  target ... : dependencies ... command...
  target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label)。  dependencies就是,要生成那个target所需要的文件或是目标。
  command也就是make需要执行的命令。(任意的Shell命令,可以有若干行)
  Command必须以Tab键开头。否则,make就会显示出错信息。如果某一命令行太长可以分作两行,用反斜杠()连接

💦Linux GCC常用命令

💦简介

GCC 的意思也只是 GNU C Compiler 而已。经过了这么多年的发展,GCC 已经不仅仅能支持 C 语言;它现在还支持 Ada 语言、C++ 语言、Java 语言、Objective C 语言、Pascal 语言、COBOL语言,以及支持函数式编程和逻辑编程的 Mercury 语言,等等。而 GCC 也不再单只是 GNU C 语言编译器的意思了,而是变成了 GNU Compiler Collection 也即是 GNU 编译器家族的意思了。另一方面,说到 GCC 对于操作系统平台及硬件平台支持,概括起来就是一句话:无所不在。

💦GCC的使用

  基本使用格式 :
  gcc [options] [filenames]

  其中:
  options:编译器所需要的编译选项 filenames:要编译的文件名

💦GCC的常用选项

在这里插入图片描述
在这里插入图片描述

💦GCC文件扩展名规范

在这里插入图片描述

💦简单编译

  示例程序如下:

//test.c
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}

  这个程序,一步到位的编译指令是:
  gcc test.c -o test

💦预处理

  gcc -E test.c -o test.i gcc -E test.c
  可以输出test.i文件中存放着test.c经预处理之后的代码。打开test.i文件,看一看,就明白了。后面那条指令,是直接在命令行窗口中输出预处理后的代码.
  gcc的-E选项,可以让编译器在预处理后停止,并输出预处理结果。在本例中,预处理结果就是将stdio.h 文件中的内容插入到test.c中了。

💦编译为汇编代码(Compilation)

  预处理之后,可直接对生成的test.i文件编译,生成汇编代码:
  gcc -S test.i -o test.s
  gcc的-S选项,表示在程序编译期间,在生成汇编代码后,停止,-o输出汇编代码文件。

💦汇编(Assembly)

  对于上一小节中生成的汇编代码文件test.s,gas汇编器负责将其编译为目标文件,如下:
  gcc -c test.s -o test.o

💦连接(Linking)

  gcc连接器是gas提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。
  对于上一小节中生成的test.o,将其与C标准输入输出库进行连接,最终生成程序test
  gcc test.o -o test

💦多个程序文件的编译

  通常整个程序是由多个源文件组成的,相应地也就形成了多个编译单元,使用GCC能够很好地管理这些编译单元。假设有一个由test1.c和 test2.c两个源文件组成的程序,为了对它们进行编译,并最终生成可执行程序test,可以使用下面这条命令:
  gcc test1.c test2.c -o test
  如果同时处理的文件不止一个,GCC仍然会按照预处理、编译和链接的过程依次进行。如果深究起来,上面这条命令大致相当于依次执行如下三条命令:
  gcc -c test1.c -o test1.o
  gcc -c test2.c -o test2.o
  gcc test1.o test2.o -o test
例如,有以下的Makefile文件:

# 一个简单的Makefile的例子
# 以#开头的为注释行
test:prog.o code.o
		gcc –o test prog.o code.o
prog.o:prog.c prog.h code.h
		gcc –c prog.c –o prog.o
code.o:code.c code.h
		gcc –c code.c –o code.o
clean:
		rm –f *.o 

  上面的Makefile文件中共定义了四个目标:test、prog.o、code.o和clean。目标从每行的最左边开始写,后面跟一个冒号(:),如果有与这个目标有依赖性的其他目标或文件,把它们列在冒号后面,并以空格隔开。然后另起一行开始写实现这个目标的一组命令。在Makefile中,可使用续行号(\)将一个单独的命令行延续成几行。但要注意在续行号(\)后面不能跟任何字符(包括空格键)。
  一般情况下,调用make命令可输入:
  # make target
  target是Makefile文件中定义的目标之一,如果省略target,make就将生成Makefile文件中定义的第一个目标。对于上面Makefile的例子,单独的一个“make”命令等价于:
  # make test
  因为test是Makefile文件中定义的第一个目标,,所有make首先将其读入,然后从第一行开始执行,把第一个目标test作为它的最终目标后面的目标的更新都会影响到test的更新。第一条规则说明只要文件test的时间戳比文件prog.o或code.o中的任何一个旧,下一行的编译命令将会被执行。
  但是,在检查文件prog.o和code.o的时间戳之前,make会在下面的行中寻找以prog.o和code.o为目标的规则,在第三行中找到了关于prog.o的规则,该文件的依赖文件是prog.c、prog.h和code.h。同样,make会在后面的规则行中继续查找这些依赖文件的规则,如果找不到,则开始检查这些依赖文件的时间戳,如果这些文件中任何一个的时间戳比prog.o的新,make将执行“gcc –c prog.c –o prog.o”命令,更新prog.o文件。
  以同样的方法,接下来对文件code.o做类似 的检查,依赖文件是code.c和code.h。当make执行完所有这些套嵌的规则后,make将处理最顶层的test规则。如果关于prog.o和code.o的两个规则中的任何一个被执行,至少其中一个.o目标文件就会比test新,那么就要执行test规则中的命令,因此make去执行gcc命令将prog.o和code.o连接成目标文件test。
  在上面Makefile的例子中,还定义了一个目标clean,它是Makefile中常用的一种专用目标,即删除所有的目标模块。
  现在来看一下make做的工作:
  首先make按顺序读取makefile中的规则,然后检查该规则中的依赖文件与目标文件的时间戳哪个更新,如果目标文件的时问戳比依赖文件还早,就按规则中定义的命令更新目标文件。如果该规则中的依赖文件又是其他规则中的目标文件,那么依照规则链不断执行这个过程,直到Makefile文件的结束,至少可以找到一个不是规则生成的最终依赖文件,获得此文件的时间戳,然后从下到上依照规则链执行目标文件的时间戳比此文件时间戳旧的规则,直到最顶层的规则。

💦Makefile中的变量

  Makefile里的变量就像一个环境变量。事实上,环境变量在make中也被解释成make的变量。这些变量对大小写敏感,一般使用大写宇母。几乎可以从任何地方引用定义的变量,变量的主要作用如下:
  保存文件名列表。在前面的例子里,作为依赖文件的一些目标文件名出现在可执行文件的规则中,而在这个规则的命令行里同样包含这些文件并传递给gcc做为命令参数。如果使用一个变量来保存所有的目标文件名,则可以方便地加入新的目标文件而且不易出错。
  保存可执行命令名,如编译器。在不同的Linux系统中存在着很多相似的编译器系统,这些系统在某些地方会有细微的差别,如果项目被用在一个非gcc的系统里,则必须将所有出现编译器名的地方改成用新的编译器名,比如编译器的版本不同,arm-linux 3.4.1、4.3.2等。如果使用一个变量来代替编译器名,那么只需要改变该变量的值。其他所有地方的命令名就都改变了。
  保存编译器的参数。在很多源代码编译时,gcc需要很长的参数选项,在很多情况下,所有的编译命令使用一组相同的选项,如果把这组选项使用一个变量代表,那么可以把这个变量放在所有引用编译器的地方。当要改变选项的时候,只需改变一次这个变量的内容即可。

💦定义变量

  Makefile中的变量是用一个文本串在Makefile中定义的,这个文本串就是变量的值。只要在一行的开始写下这个变量的名字,后面跟一个“=”号,以及要设定这个变量的值即可定义变量,下面是定义变量的语法:
  VARNAME=string
  使用时,把变量用括号括起来,并在前面加上KaTeX parse error: Expected 'EOF', got '&' at position 15: 符号,就可以引用变量的值: &̲emsp;&emsp;```{VARNAME} ```
  make解释规则时,VARNAME在等式右端展开为定义它的字符串。变量一般都在Makefile的头部定义。按照惯例,所有的Makefile变量都应该是大写。如果变量的值发生变化,就只需要在一个地方修改,从而简化了Makefile的维护。
  现在利用变量把前面的Makefile重写一遍:

	OBJS=prog.o code.o
	CC=gcc
 	test:${ OBJS }
		${ CC } –o test ${ OBJS }
	prog.o:prog.c prog.h code.h
		${ CC } –c prog.c –o prog.o
	code.o:code.c code.h
		${ CC } –c code.c –o code.o
 	clean:
		rm –f *.o 

  如:源程序为
在这里插入图片描述
  经过变量替换过后的makefile文件:
在这里插入图片描述

💦Makefile的隐含规则

  在上面的例子中,几个产生目标文件的命令都是从“.c”的C语言源文件和相关文件通过编译产生“.o”目标文件,这也是一般的步骤。实际上,make可以使工作更加自动化,也就是说,make知道一些默认的动作,它有一些称作隐含规则的内置的规则,这些规则告诉make当用户没有完整地给出某些命令的时候,应该怎样执行。
  例如,把生成prog.o和code.o的命令从规则中删除,make将会查找隐含规则,然后会找到并执行一个适当的命令。由于这些命令会使用一些变量,因此可以通过改变这些变量来定制make。象在前面的例子中所定义的那样,make使用变量CC来定义编译器,并且传递变量CFLAGS(编译器参数)、CPPFLAGS(C语言预处理器参数)、TARGET_ARCH(目标机器的结构定义)给编译器,然后加上参数-c,后面跟变量$<(第一个依赖文件名),然后是参数-o加变量$@(目标文件名)。综上所述,一个C编译的具体命令将会是:
  $ {CC} $ {CFLAGS} $ {CPPFLAGS} $ {TARGET_ARCH} –c $< -o $@
在这里插入图片描述
  在上面的例子中,利用隐含规则,可以简化为:

	OBJS=prog.o code.o
	CC=gcc
	test:${ OBJS }
		${ CC } –o $@ $^
	 prog.o:prog.c prog.h code.h
	code.o:code.c code.h
	 clean:
		rm –f *.o
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZoomToday

给作者倒一杯卡布奇诺

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值