嵌入式学习笔记之一:嵌入式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
在文件中输入如下代码
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/tmpS
up to date.
app' is
forunx@ubuntu:—/work/tmpS
make
我们做一下稍微的改动,修改 app.c 文件中的内容或者使用 touch 命令更改一下 app.c 的时间属性,然后再进行 make 看看:
forunx :—/work/tmpS touch app.C
forunx :—/work/tmpS make
gCC 一 0 app app•C
forunx :—/work/tmpS
可以看到又重新执行了 gcc -o app app.c 命令,重新生成了 app。也就是说,make 工具会判断依赖
文件的 时间戳 是否比目标文件时间戳更新, 来决定是否重新生成目标文件。
我们再举一例:
假如我们有多个文件需要编译,比如我们创建多个文件
fun1.c,fun1.h,fun2.c,fun2.h,app.c。
fun1.c:
void fun1(char *s)
{
printf("%s\r\n",s);
}
fun1.h:
void fun1(char *s);
fun2.c
int fun2(int x,int y)
{
return x+y;
}
fun2.h
int fun2(int x,int y);
创建 app.c 文件,该文件引用fun1.c 和 fun2.c 中定义的函数。
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.o
gcc -o app app.o fun1.o fun2.o
app.o: app.c
gcc -c app.c
fun1.o: fun1.c fun1.h
gcc -c fun1.c
fun2.o: fun2.c fun2.h
gcc -c fun2.c
clean:
rm -rf *.o app
创建完成之后,我们直接 make:
上图可以看到生成最终目标 app 和一些中间目标文件,这个过程经过了以下四个步骤:
gcc -c app.c
gcc -c fun1.c
gcc -c fun2.c
gcc -o app app.o fun1.o fun2.o
执行 app,可以看到执行成功:
现在修改其中一个文件 fun2.h 后,比如更改一下 fun2.h 的时间属性,然后再次编译:
可以看到只用了两个步骤,app 的其他依赖文件并没有被重新编译生成。
最后,执行 make clean 清除中间文件和 app:
创作不易,欢迎点赞、关注。--努力做一个善于发现并解决问题的人。