vlambda博客
学习文章列表

嵌入式学习笔记之一:嵌入式linux中混合编译入门

编译原理

编译工具将我们的源码文件最终编译成可执行文件,并不是一步到位的,其中要经过一个类似工厂流水线的过程,交叉编译工具中包含负责处理各个环节的功能工具,所以叫做交叉编译工具链。

编译的一般过程分为:

预编译->编译->汇编->链接->生成 elf 文件->转换为二进制可支持 bin 文件。

预编译 Pre-compile 阶段:主要是对头文件#include 、宏定义#define 等进行展开。

编译 compile 阶段:主要是将我们的.c 文件编译成汇编.s 文件。

汇编 Assembly 阶段:将汇编文件.s 编译成为目标文件.o。

链接 Link 阶段:将生成.o 文件链接生成.elf 文件。

转换阶段:将生成的.elf 文件转换成为可执行二进制.bin 文件。

而这些编译步骤是通过 make 工具解析 Makefile 文件来执行。Makefile 文件中描述了整个工程所有文件的编译顺序、编译规则、依赖关系等。

MAKEFILE

前面我们提到过,Make 工具通过解析Makefile 文件进行一系列编译操作,最终生成我们想要的镜像文件。Makefile 文件中描述了各工程所有文件的编译顺序、编译规则以及依赖关系,决定了工程中文件是否需要编译,以及这些文件的编译顺序。Make 工具还可以通过比较文件最后修改时间,来决定哪些文件需要更新哪些不需要更新,更改了某个文件之后,只对依赖此文件的目标文件进行重新编译更新,这就大大减少了编译时间。

打开源码目录可以看到,Makefile 文件不仅存在于源码根目录下,在其他的子目录下也基本都有

Makefile 文件。在执行编译时,Make工具会解析根目录下 Makefile 文件进行编译,而根目录的Makefile会调用子目录下的 Makefile,子目录下又有子目录,层层调用。

Makefile 需要按照一定的格式语法规则进行书写。如果你是做 Linux 应用开发的人员,那么写 Makefile就是必备技能,就需要深谙 Makefile 语法规则。对于我们 Linux 平台初级开发人员,很少需要我们去写一个复杂的 Makefile 文件,只是在做平台移植的时候可能需要去简单修改或者阅读Makefile 文件。

下面我们就简单了解一下 Makefile 基本格式规则。

1 、 目标和依赖

目标就是我们需要生成文件,依赖就是生成目标文件所需要的其他文件,称为依赖文件。基本语法规则如下:

targets … :dependent_files … (tap)command 


举例一:我们先创建一个简单的 app.c 文件。

forlinx@ubuntu:~/work/tmp$ touch app.c

在文件中输入如下代码

#include <stdio.h>int main(void){printf("my first app !!! \r\n");return 0;}


按照基本规则建立一个简单的 Makefile 文件,app 为我们需要的目标文件,app.c 为所要生成的 app 的依赖文件,gcc –o app app.c 就是生成目标 app 需要执行的命令。

建立一个 Makefile 文件:

forlinx@ubuntu:~/work/tmp$ touch makefile

文件中输入如下内容:

app:app.c

gcc -o app app.c

使用 make 命令,可以看到执行了命令 gcc -o app app.c,ls 命令查看文件,发现在该目录下生成了app 目标文件:

forlinx@ubuntu:~/work/tmp$ make

执行 app,即可看到 app.c 中我们写的打印信息:

forlinx@ubuntu:—/work/tmpS ./app 

my first app !! ! 

forlinx@ubuntu:—/work/tmpS

然后我们再执行 make 命令,会发现出现如下信息,’app’ is up to date,说明 app 已经是最新的,没必要再重新生成:

forunx@ubuntu:—/work/tmpSup to date.app' isforunx@ubuntu:—/work/tmpSmake


我们做一下稍微的改动,修改 app.c 文件中的内容或者使用 touch 命令更改一下 app.c 的时间属性,然后再进行 make 看看:

forunx@ubuntu:—/work/tmpS touch app.Cforunx@ubuntu:—/work/tmpS makegCC 一 0 app app•Cforunx@ubuntu:—/work/tmpS


可以看到又重新执行了 gcc -o app app.c 命令,重新生成了 app。也就是说,make 工具会判断依赖

文件的 时间戳 是否比目标文件时间戳更新, 来决定是否重新生成目标文件。

我们再举一例:

假如我们有多个文件需要编译,比如我们创建多个文件

fun1.c,fun1.h,fun2.c,fun2.h,app.c。fun1.c:#include <stdio.h>#include "fun1.h"void fun1(char *s){printf("%s\r\n",s);}fun1.h:#ifndef _FUN1_H#define _FUN1_Hvoid fun1(char *s);#endiffun2.c#include "fun2.h"int fun2(int x,int y){return x+y;}fun2.h #ifndef _FUN2_H#define _FUN2_Hint fun2(int x,int y);#endif

创建 app.c 文件,该文件引用fun1.c 和 fun2.c 中定义的函数。

#include <stdio.h>#include "fun1.h"#include "fun2.h"int main(void){fun1("I am fun1 !");printf("fun2 return value=%d\n",fun2(1,2));return 0;}


最后创建 makefile 文件,其中app 是我们的最终目标文件,app 的依赖文件为 app.o、fun1.o、fun2.o,

这几个.o 文件称为中间目标文件,它们又有各自所依赖的.c 和.h 文件,最后的 clean 也是一个目标文件,

但是这个目标没有任何依赖,也不会生成真正文件,只是执行一条命令,我们称之为伪目标。我们可以看

到 clean 执行的是删除.o 和 app 的命令。

app: app.o fun1.o fun2.ogcc -o app app.o fun1.o fun2.oapp.o: app.cgcc -c app.cfun1.o: fun1.c fun1.hgcc -c fun1.cfun2.o: fun2.c fun2.hgcc -c fun2.cclean:rm -rf *.o app


创建完成之后,我们直接 make:

嵌入式学习笔记之一:嵌入式linux中混合编译入门

上图可以看到生成最终目标 app 和一些中间目标文件,这个过程经过了以下四个步骤:

gcc -c app.cgcc -c fun1.cgcc -c fun2.cgcc -o app app.o fun1.o fun2.o


执行 app,可以看到执行成功:

嵌入式学习笔记之一:嵌入式linux中混合编译入门

现在修改其中一个文件 fun2.h 后,比如更改一下 fun2.h 的时间属性,然后再次编译:

可以看到只用了两个步骤,app 的其他依赖文件并没有被重新编译生成。

最后,执行 make clean 清除中间文件和 app:

创作不易,欢迎点赞、关注。--努力做一个善于发现并解决问题的人。