Linux嵌入式学习——Makefile

一、引入

多个文件编译生成可执行程序时,有两种方法:

  • 法一:gcc -o test a.c b.c(内部实现机制:a.c和b.c都需要经历预处理、编译、汇编等过程后链接)
  • 法二:gcc -c -o a.o a.c  ,  gcc -c -o b.o b.c ,  gcc -o test a.o b.o  (先各自编译不链接,最后一起链接)

当我们修改其中某个.c文件时,比如只修改a.c,那么法一需要所有文件都重新编译后链接,但其实b.c没有修改,再次编译是浪费时间和资源,这就是法一的缺点:每次修改都整体重新编译

而法二就解决了法一的这个弊端,如只修改a.c文件,那么只需要执行gcc -c -o a.o a.c  ,  gcc -o test a.o b.o即可,不需要重新编译b.c文件。

但是,当文件少的时候可以一个一个人为操作,一旦文件数量多了,手动操作就相当繁琐了,这就引入了Makefile,帮助我们自动编译。

那么,就产生一个问题,用Makefile如何实现法二在修改某个文件时的编译过程?

:这个问题的本质就是Makefile如何判断哪些文件需要重新编译——比较时间。若.c文件时间比.o文件新,或者.o文件还没有生成,那么需要重新编译.c文件。这里.o文件就是目标文件,.c文件就是依赖文件。

二、Makefile规则

1.Makefile文件编写规则

目标(target)…:    依赖(prerequiries)…

<tab>命令(command)

如果“依赖文件”比“目标文件”更加新,那么执行“命令”来重新生成“目标文件”。

命令被执行的2个条件:依赖文件比目标文件,或是 目标文件还没生成

Makefile参考文档:GNU Make 使用手册(于凤昌中译版)_51CTO博客_gnu make 于凤昌

官方文档:https://www.gnu.org/software/make/manual/

2.make使用规则

  • make程序需要一个所谓的Makefile文件来告诉它干什么。执行make的时候,系统回去当前目录下找Makefile文件。
  • make [目标名] :若五目标,默认第一个目标

3.举例

以a.c, b.c文件为例:

test:a.o b.o
    gcc -o a.o b.o
a.o:a.c
    gcc -c -o a.o a.c
b.o:b.c
    gcc -c -o b.o b.c

4.语法

4.1 通配符:%  和其他符号:$@ , $< , $^

上面的例子中,如果有1000个.c文件,若是每个文件都写一遍,那真的要累死。但仔细看第3行至第6行发现,其实格式都一样,只是因为文件名不同而写了多个。通配符%就可以解决这种重复但工作内容相似的问题:%.o代表所有满足这种格式的.o文件,%.c同理。

$@:表示目标文件

$<:表示第一个依赖文件

$^:表示所有依赖文件

所以,上面那个例子可以优化为:

test:a.o b.o
    gcc -o test $^
%.o:%.c
    gcc -c -o $@ $<

4.2 假想目标:.PHONY:

上面的例子中只有一个目标test,执行make的时候可以写make,也可以写make test。一般Makefile里面不止一个目标。

这里假设需要一个清除所有编译生成的文件的操作,那么需要在Makefile里增加clean目标,如下:

test:a.o b.o
    gcc -o test $^
%.o:%.c
    gcc -c -o $@ $<

clean:
    rm -f *.o test

执行make clean就可以执行清楚操作了。

但其实这个有一个弊端:若该目录下存在clean文件,但在Makefile中clean没有依赖文件,所以就没有办法通过比较时间来判断rm指令需不需要执行,这时候即使我们make clean,系统会提示clean文件已经是最新,不回去执行rm操作。

解决办法就是将clean作为假想目标,方法如下:

test:a.o b.o
    gcc -o test $^
%.o:%.c
    gcc -c -o $@ $<

clean:
    rm -f *.o test

.PHONY:clean

这样,即使目录下有clean文件也不用担心啦。

4.3 一些变量::= , = , ?= , +=

  • :=    :即时变量(简单变量),变量的值在定义时即刻确定
  • =     :延时变量,变量的值在使用时才确定
  • ?=   :延时变量,如果是第一次定义才起效,如果在前面该变量已定义则忽略这句
  • +=   :附加变量,它是即时变量还是延时变量取决于前面的变量定义

举例:

A:=$(C)
B=$(C)
C:=123
D?=7788

all:
        @echo A=$(A)
        @echo B=$(B)
        @echo D=$(D)

C+=123

make 结果:A=   B=123 123   D=7788

make D=000结果:A=   B=123 123   D=000

make C=000结果:A=000   B=000   D=7788

make C=000 D=000结果:A=000   B=000   D=000

5.函数

5.1 $(foreach var,list,test)

功能:对list中的每一个变量var,执行text操作

5.2 $(filter pattern...,text)

功能:在text中取出符合pattern格式的值

5.3 $(filter-out pattern...,text)

功能:在text中取出不符合pattern格式的值

5.4 $(wildcard pattern)

功能:pattern定义了文件名的格式,wildcard取出其中存在的文件

5.5 $(patsubst pattern,replacement,text)

功能:从list中取出每一个值,如果符合pattern,则替换为replacement,其他不符合的就不替换

示例:

A = a b c
B = $(foreach t,$(A),$(t).o)

C = a b c d/ /e
D = $(filter %/,$(C))
E = $(filter /%,$(C))
F = $(filter-out %/,$(C))

files = $(wildcard *.c)
files2 = a.c b.c c.c d.c e.c a.o b.o c.o d.o e.o
files3 = $(wildcard $(files2))

dep_file = $(patsubst %.c,%.d,$(files2))

all:
        @echo B=$(B)
        @echo D=$(D)
        @echo E=$(E)
        @echo F=$(F)
        @echo files=$(files)
        @echo files3=$(files3)
        @echo dep_file=$(dep_file)

运行结果:

B=a.o b.o c.o
D=d/
E=/e
F=a b c /e
files=c.c a.c b.c
files3=a.c b.c c.c a.o b.o c.o
dep_file=a.d b.d c.d d.d e.d a.o b.o c.o d.o e.o

6.头文件依赖

参考文档:Linux Makefile 生成 *.d 依赖文件以及 gcc -M -MF -MP 等相关选项说明_Jerry.yl的博客-CSDN博客

include讲解:

https://www.cnblogs.com/cuckoos/articles/5049984.html

gcc指令含义
gcc -M c.c打印出依赖
gcc -M -MF c.d c.c生成c.d依赖文件
gcc -c -o c.o c.c -MD -MF c.d编译出c.o,且生成c.d依赖文件

举例:

已有文件a.c , b.c , c.c , c.h,文件内容如下:

//a.c文件内容:
#include <stdio.h>

int main(int argc,char* argv[])
{
        func_b();
        func_c();
        return 0;
}

//b.c文件内容:
#include <stdio.h>

void func_b()
{
        printf("This is B\n");
}


//c.c文件内容:
#include <stdio.h>
#include "c.h"

void func_c()
{
        printf("This is C:%d\n",NUMC);
}

//c.h文件内容:
#define NUMC 1

之前的Makefile内容如下:

test:a.o b.o c.o
    gcc -o test $^
%.o:%.c
    gcc -c -o $@ $<

clean:
    rm -f *.o test

.PHONY:clean

当我修改c.h的时候,make不会重新编译test。这就是上面的Makefile文件的弊端:不支持自动检测头文件。

①增加c.h头文件

test:a.o b.o c.o
    gcc -o test $^

%.o:%.c
    gcc -c -o $@ $<

c.o:c.c c.h

clean:
    rm -f *.o test

.PHONY:clean

②如包含的依赖文件很多或者自己不知道,可不可以自动获取

test:a.o b.o c.o
    gcc -o test $^

%.o:%.c
    gcc -c -o $@ $< -MD -MF .$@.d

clean:
    rm -f *.o test

.PHONY:clean

③自动获取了头文件依赖,并将其存到.c.o.d文件中,但是没有将其包含进来,修改c.h文件还是不能重新生成test

objs := a.o b.o c.o
dep_files := $(foreach f,$(objs),.$(f).d)
dep_files := $(wildcard $(dep_files))


test:$(objs)
        gcc -o test $^
#       @echo dep_files=$(dep_files)

%.o:%.c
        gcc -c -o $@ $< -MD -MF .$@.d

ifneq ($(dep_files),)
include $(dep_files)
endif

clean:
        rm -f *.o test

disclean:
        rm -vf $(dep_files) *.o test

.PHONY:disclean
.PHONY:clean

该Makefile文件就可以实现自动检测头文件了,当c.h文件修改时,会重新编译c.o文件。

7.CFLAGS

CFLAGS = -Werror

功能:将所有的警告都变成error,推荐使用

objs := a.o b.o c.o
dep_files := $(foreach f,$(objs),.$(f).d)
dep_files := $(wildcard $(dep_files))

CFLAGS = -Werror

test:$(objs)
        gcc -o test $^
#       @echo dep_files=$(dep_files)

%.o:%.c
        gcc $(CFLAGS) -c -o $@ $< -MD -MF .$@.d

ifneq ($(dep_files),)
include $(dep_files)
endif

clean:
        rm -f *.o test

disclean:
        rm -vf $(dep_files) *.o test

.PHONY:disclean
.PHONY:clean

CFLAGS = -I.

功能:指定头文件目录为当前目录

一般我们习惯将所有头文件都放到include文件夹中,所以用CFLAGS = -Iinclude

三、注意

1.Makefile中的%标记和系统通配符*的区别在于:*是应用在系统中的,%是应用在这个Makefile文件中的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很高兴回答你关于野火嵌入Linux学习的问题!以下是一些学习笔记的建议: 1. 了解嵌入系统:首先,你需要了解嵌入系统是什么以及它们与桌面操作系统的区别。嵌入系统通常用于特定的应用领域,例如智能家居、汽车电子和工业控制等。 2. 学习Linux基础知识:野火嵌入Linux是基于Linux内核的操作系统,所以你需要掌握Linux的基础知识,包括文件系统、进程管理、设备驱动程序等。 3. 硬件平台了解:野火嵌入Linux有不同的硬件平台,例如野火开发板。你需要学习如何操作和配置这些硬件平台,并了解它们的特性和限制。 4. 交叉编译环境设置:为了在PC上开发嵌入系统,你需要设置一个交叉编译环境,以便能够编译和调试嵌入应用程序。这涉及到安装和配置交叉编译工具链。 5. 内核定制和驱动程序开发:学习如何定制Linux内核以满足特定需求,并开发设备驱动程序以支持外部硬件。 6. 应用程序开发:掌握嵌入应用程序的开发技术,包括使用C/C++语言、Makefile和调试工具。 7. 调试和故障排除:学会使用调试工具和技术来定位和解决嵌入系统中的问题。 8. 实际项目经验:通过参与实际的嵌入项目或完成一些小型项目来应用你的知识和技能。 这些只是一些学习笔记的建议,野火嵌入Linux学习需要不断的实践和探索。希望这些对你有帮助!如果你有任何进一步的问题,欢迎继续提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值