查看: 176|回复: 0

[经验] 飞凌嵌入式ElfBoard ELF1板卡-Makefile介绍

[复制链接]

该用户从未签到

发表于 2025-1-3 09:17:08 | 显示全部楼层 |阅读模式
分享到:
前面我们编译内核时,那么多.c文件最终生成一个zImage镜像。其实是make工具通过解析Makefile文件进行一系列编译操作,最终生成我们想要的镜像文件。Makefile文件中描述了个工程所有文件的编译顺序、编译规则以及依赖关系,决定了工程中文件是否需要编译,以及这些文件的编译顺序。Make工具还可以通过比较文件最后修改时间,来决定哪些文件需要更新哪些不需要更新,更改了某个文件之后,只对依赖此文件的目标文件进行重新编译更新,这就大大减少了编译时间。
打开源码目录可以看到,Makefile文件不仅存在于源码根目录下,在其他的子目录下也基本都有Makefile文件。在执行编译时,Make工具会解析根目录下Makefile 文件进行编译,而根目录的Makefile会调用子目录下的Makefile,子目录下又有子目录,层层调用。
Makefile需要按照一定的格式语法规则进行书写。如果你是做Linux应用开发的人员,那么写Makefile就是必备技能,就需要深谙Makefile语法规则。对于我们Linux平台初级开发人员,很少需要我们去写一个复杂的Makefile文件,只是在做平台移植的时候可能需要去简单修改或者阅读Makefile文件,所以呢,下面我们就简单了解一下Makefile基本格式规则。
目标和依赖
目标就是我们需要生成的文件,依赖就是生成目标文件所需要的其他文件,称为依赖文件。基本语法规则如下:
  1. targets … :dependent_files …

  2. (tap)command

复制代码
举例一
我们先创建一个简单的app.c文件:
  1. elf@ubuntu:~/work/tmp$ touch app.c
复制代码
在文件中输入如下代码:
  1. #include <stdio.h>

  2. int main(void)

  3. {

  4.         printf("my first app !!! \r\n");

  5.         return 0;

  6. }
复制代码
按照基本规则建立一个简单的Makefile文件,app为我们需要的目标文件,app.c为所要生成的app的依赖文件,gcc –o app app.c就是生成目标app需要执行的命令。
建立一个Makefile文件:
  1. elf@ubuntu:~/work/tmp$ touch makefile
复制代码
文件中输入如下内容:
11-1.png
注意:复制粘贴过程中可能会出现格式问题
  1. app:app.c

  2.         gcc -o app app.c
复制代码
使用make命令,可以看到执行了命令gcc -o app app.c,ls命令查看文件,发现在该目录下生成了app目标文件:
  1. elf@ubuntu:~/work/tmp$ make

  2. gcc -o app app.c

  3. elf@ubuntu:~/work/tmp$ ls

  4. app app.c makefile
复制代码
执行app,即可看到app.c中我们写的打印信息:
  1. elf@ubuntu:~/work/tmp$ ./app

  2. my first app !!!
复制代码
然后我们再执行make命令,会发现出现如下信息,‘app’ is up to date,说明app已经是最新的,没必要再重新生成:
  1. elf@ubuntu:~/work/tmp$ make

  2. make: ‘app’ is up to date.
复制代码
我们做一下稍微的改动,修改app.c文件中的内容或者使用touch命令更改一下app.c的时间属性,然后再进行make看看:
  1. elf@ubuntu:~/work/tmp$ touch app.c

  2. elf@ubuntu:~/work/tmp$ make

  3. gcc -o app app.c
复制代码
可以看到又重新执行了gcc -o app app.c命令,重新生成了app。也就是说,make工具会判断依赖文件的时间戳是否比目标文件时间戳更新,来决定是否重新生成目标文件。
举例二
假如我们有多个文件需要编译,比如我们创建多个文件fun1.c,fun1.h,fun2.c,fun2.h,app.c。
fun1.c:
  1. #include <stdio.h>

  2. #include "fun1.h"

  3. void fun1(char *s)

  4. {

  5.         printf("%s\r\n",s);

  6. }
复制代码
fun1.h:
  1. #ifndef _FUN1_H

  2. #define _FUN1_H

  3. void fun1(char *s);

  4. #endif
复制代码
fun2.c:
  1. #include "fun2.h"

  2. int fun2(int x,int y)

  3. {

  4.         return x+y;

  5. }
复制代码
fun2.h:
  1. #ifndef _FUN2_H

  2. #define _FUN2_H

  3. int fun2(int x,int y);

  4. #endif
复制代码
创建app.c文件,该文件引用fun1.c和fun2.c中定义的函数:
  1. #include <stdio.h>

  2. #include "fun1.h"

  3. #include "fun2.h"

  4. int main(void)

  5. {

  6.         fun1("I am fun1 !");

  7.         printf("fun2 return value=%d \n",fun2(1,2));

  8.         return 0;

  9. }
复制代码
最后创建makefile文件,其中app是我们的最终目标文件,app的依赖文件为app.o,fun1.o,fun2.o,这几个.o文件称为中间目标文件,它们又有各自所依赖的.c和.h文件,最后的clean也是一个目标文件,但是这个目标没有任何依赖,也不会生成真正文件,只是执行一条命令,我们称之为伪目标。我们可以看到clean执行的是删除.o和app的命令。
11-2.png
注意:复制粘贴过程中可能会出现格式问题
  1. app:app.o fun1.o fun2.o

  2.         gcc -o app app.o fun1.o fun2.o

  3. app.o:app.c

  4.         gcc -c app.c

  5. fun1.o:fun1.c fun1.h

  6.         gcc -c fun1.c

  7. fun2.o:fun2.c fun2.h

  8.         gcc -c fun2.c

  9. clean:

  10.          rm -rf *.o app<span style="font-family: Arial; background-color: rgb(255, 255, 255);">                  </span>
复制代码
创建完成之后,我们直接make:
  1. elf@ubuntu:~/work/tmp$ make

  2. gcc -c app.c

  3. gcc -c fun1.c

  4. gcc -c fun2.c

  5. gcc -o app app.o fun1.o fun2.o

  6. elf@ubuntu:~/work/tmp$ ls

  7. app  app.c  app.o  fun1.c  fun1.h  fun1.o  fun2.c  fun2.h  fun2.o  makefile           
复制代码
上图可以看到生成最终目标app和一些中间目标文件,这个过程经过了以下四个步骤:
  1. gcc -c app.c

  2. gcc -c fun1.c

  3. gcc -c fun2.c

  4. gcc -o app app.o fun1.o fun2.o
复制代码
执行app,可以看到执行成功:
  1. elf@ubuntu:~/work/tmp$ ./app

  2. I am fun1 !

  3. fun2 return value=3
复制代码
现在修改其中一个文件fun2.h后,比如更改一下fun2.h的时间属性,然后再次编译:
  1. elf@ubuntu:~/work/tmp$ touch fun2.h

  2. elf@ubuntu:~/work/tmp$ make

  3. gcc -c fun2.c

  4. gcc -o app app.o fun1.o fun2.o
复制代码
可以看到只用了两个步骤,app的其他依赖文件并没有被重新编译生成。
最后,执行make clean清除中间文件和app:
  1. elf@ubuntu:~/work/tmp$ make clean

  2. rm -rf *.o app

  3. elf@ubuntu:~/work/tmp$ ls

  4. app.c  fun1.c  fun1.h  fun2.c  fun2.h  makefile
复制代码
变量定义
变量是在Makefile中定义的名字,用来代替一个文本字符串,该文本字符串称为该变量的值。变量名是不包括“:”、“#”、“=”结尾空格的任何字符串。变量名是大小写敏感的。推荐在Makefile内部使用小写字母作为变量名。
变量赋值方式有以下几种:
递归赋值
变量的名称 = 变量值
变量的值将会是整个Makefile中最后被指定的值。
举例,建立一个makefile文件,定义两个变量a和b,引用变量使用$(),使用@echo进行打印:
11-3.png
注意:复制粘贴过程中可能会出现格式问题
  1. a = 123

  2. b = $(a) string

  3. a = 678

  4. target:

  5.         @echo "a = $(a)"

  6.         @echo "b = $(b)"
复制代码
可以看到b的值为678 string,而不是123 string
  1. elf@ubuntu:~/work/tmp$ make

  2. a = 678

  3. b = 678 string
复制代码
简单赋值
变量的名称 := 变量值
变量的值决定于它在Makefile中的位置,而不是整个Makefile展开后最终的值。
11-4.png
注意:复制粘贴过程中可能会出现格式问题
  1. a := 123

  2. b := $(a) string

  3. a := 678

  4. target:

  5.         @echo "a = $(a)"

  6.         @echo "b = $(b)"
复制代码
make后可以看到b的值为123 string:
  1. elf@ubuntu:~/work/tmp$ make

  2. a = 678

  3. b = 123 string
复制代码
条件赋值
变量的名称 ?= 变量值
如果变量未定义,则使用该变量值定义变量。如果该变量已经被定义赋值,则该赋值语句无效,使用原有值。
11-5.png
注意:复制粘贴过程中可能会出现格式问题
  1. a ?= 123

  2. b ?= $(a) string

  3. a ?= 678

  4. target:

  5.         @echo "a = $(a)"

  6.         @echo "b = $(b)"
复制代码
make后,a的值是第一次被赋予的值:
  1. elf@ubuntu:~/work/tmp$ make

  2. a = 123

  3. b = 123 string
复制代码
追加赋值
变量的名称 += 变量值
举例:
11-6.png
注意:复制粘贴过程中可能会出现格式问题
  1. a := 123

  2. b := $(a) string

  3. a += $(b)

  4. target:

  5.         @echo "a = $(a)"

  6.         @echo "b = $(b)"
复制代码
make之后结果:
  1. elf@ubuntu:~/work/tmp$ make

  2. a = 123 123 string

  3. b = 123 string
复制代码
系统变量
系统变量或者叫环境变量,包含了常见编译器、汇编器的名称及其编译选项,我们在编译之前使用source /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi主要就是设置这些系统环境变量的值。
下表列出了Makefile中常见系统变量及其部分默认值:
AR:库文件维护程序的名称,默认值为ar;
AS:汇编程序的名称,默认值为as;
CC:C编译器的名称,默认值为cc;
CPP:C预编译器的名称,默认值为$(CC) –E;
CXX:C++编译器的名称,默认值为g++;
FC:FORTRAN编译器的名称,默认值为f77;
RM:文件删除程序的名称,默认值为rm –f;
ARFLAGS:库文件维护程序的选项,无默认值;
ASFLAGS:汇编程序的选项,无默认值;
CFLAGS:C编译器的选项,无默认值;
CPPFLAGS:C预编译的选项,无默认值;
CXXFLAGS:C++编译器的选项,无默认值;
FFLAGS:FORTRAN编译器的选项,无默认值;
条件表达式的语法
(1)比较arg1和arg2值是否相同,如果相同则执行text-if-true,否则执行text-if-false。
  1. ifeq (<arg1>, <arg2> )

  2. text-if-true

  3. else

  4. text-if-false

  5. endif
复制代码
比较arg1和arg2值是否相同,如果不同则执行text-if-true,否则执行text-if-false,与上面的ifeq相反。
  1. ifneq (<arg1>, <arg2> )

  2. text-if-true

  3. else

  4. text-if-false

  5. endif
复制代码
(2)判断variable是否为空,如果非空则为真,执行text-if-true,否则执行text-if-false。
  1. ifdef <variable >

  2. text-if-true

  3. else

  4. text-if-false

  5. endif
复制代码
判断variable是否为空,如果为空则为真,执行text-if-true,否则执行text-if-false。
  1. ifndef <variable >

  2. text-if-true

  3. else

  4. text-if-false

  5. endif
复制代码


回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条



手机版|小黑屋|与非网

GMT+8, 2025-1-20 03:33 , Processed in 0.111648 second(s), 16 queries , MemCache On.

ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

苏公网安备 32059002001037号

Powered by Discuz! X3.4

Copyright © 2001-2024, Tencent Cloud.