深入解析Linux编程:使用make命令与Makefile文件编译大型程序

网课:(课程查看)

深入解析Linux编程:使用make命令与Makefile文件编译大型程序

论坛:(学术问答)

开发板:(淘宝)

(天猫)

交流群一:QQ群:869222007(鸿蒙开发/Linux/嵌入式/驱动/资料下载)

交流群二:QQ群:536785813(单片机嵌入式)

公众号:百文科技

版本日期 作者备注

V1

2020年

魏东山

技术文档

Linux中使用make命令来编译程序,特别是大型程序; make 命令执行的操作取决于 Makefile 文件。最简单的Makefile如下:

hello: hello.c
gcc -o hello hello.c
clean:
rm -f hello

将以上4行保存为Makefile文件(注意第2行和第4行必须用Tab键缩进,不能用空格键),放到01_hello目录下,然后直接执行make命令编译程序,执行“make clean”清除编译结果。

make命令根据文件更新时间戳来确定哪些文件需要重新编译。这可以让你避免编译已经编译过并且没有改变的程序,并且可以大大提高编译效率。

要完全理解Makefile的规则,请参阅《GNU Make用户手册》。以下仅作粗略介绍。

3.1 支持视频内容大纲 3.1.1 Makefile 规则和示例

参考文档:gunmake.htm

① 为什么需要Makefile

如何高效地编译程序?

你想达到什么效果?请参考Visual Studio:修改源文件或头文件,只需重新编译涉及的文件即可。

您可以重新生成APP

② Makefile其实很简单

一个简单的 Makefile 包含一系列以下格式的“规则”:

目标(target)…: 依赖(prerequiries)…
<tab>命令(command)

如果“依赖文件”比“目标文件”新,则执行“命令”重新生成“目标文件”。该命令的执行有两个条件:依赖文件比目标文件新,或者目标文件尚未生成。

③ 首先介绍一下Makefile的两个作用

A.$(foreach var,列表,文本)

简单地说,对于列表中的每个变量,将其更改为文本。对于列表中的每个元素,将其取出并赋值给var,然后将var更改为文本描述的形式。

例子:

objs := a.o b.o
dep_files := $(foreach f, $(objs), .$(f).d) // 最终 dep_files := .a.o.d .b.o.d

B.$(通配符模式)

按pattern列出的文件是否存在,列出所有存在的文件。

例子:

src_files := $( wildcard *.c) // 最终 src_files 中列出了当前目录下的所有.c` 文件

④ 逐步完善Makefile

第一个 Makefile 简单、粗暴且低效:

test : main.c sub.c sub.h
gcc -o test main.c sub.c

第二个Makefile效率很高,类似规则太多且冗长,并且不支持头文件检测:

test : main.o sub.o
gcc -o test main.o sub.o
main.o : main.c
gcc -c -o main.o main.c
sub.o : sub.c
gcc -c -o sub.o sub.c
clean:
rm *.o test -f

第三个Makefile高效精炼,不支持检测头文件:

test : main.o sub.o
gcc -o test main.o sub.o
%.o : %.c
gcc -c -o $@ $<
clean:
rm *.o test -f

第四个Makefile高效、精致,支持头文件检测(但需要手动添加头文件规则):

test : main.o sub.o
gcc -o test main.o sub.o
%.o : %.c
gcc -c -o $@ $<
sub.o : sub.h
clean:
rm *.o test -f

第五个Makefile高效、精致,并且支持自动检测头文件:

objs := main.o sub.o
test : $(objs)
gcc -o test $^
# 需要判断是否存在依赖文件
# .main.o.d .sub.o.d
dep_files := $(foreach f, $(objs), .$(f).d)
dep_files := $(wildcard $(dep_files))
# 把依赖文件包含进来
ifneq ($(dep_files),)
 include $(dep_files)
endif
%.o : %.c
gcc -Wp,-MD,.$@.d -c -o $@ $<
clean:
rm *.o test -f
distclean:
rm $(dep_files) *.o test -f

3.1.2 通用Makefile的使用

我参考Linux内核Makefile写了一个通用的Makefile,可以用来编译应用程序:

① 支持多目录、多层目录、多文件;

② 支持设置所有文件的编译选项;

③ 支持设置某个目录的编译选项;

④ 支持对某个文件单独设置编译选项;

⑤ 简单易用。

使用 git 下载本教程的文档后,可以在以下目录中找到说明和示例:

01_all_series_quickstart\
04_嵌入式 Linux 应用开发基础知识\source\05_general_Makefile

3.1.3 通用Makefile分析

①零星知识点

A、make命令的使用:

执行make命令时,会在当前目录下查找名为“Makefile”的文件,并根据其指令进行操作,生成第一个目标。

我们可以使用“-f”选项来指定文件,而不是使用名为“Makefile”的文件,例如:

make -f Makefile.build

我们可以使用“-C”选项来指定目录并切换到其他目录,例如:

make -C a/ -f Makefile.build 

我们可以指定一个目标,不再默认生成第一个目标:

make -C a/ -f Makefile.build other_target

B. 立即变量和延迟变量:

定义变量的语法如下:

A = xxx // 延时变量
B ?= xxx // 延时变量,只有第一次定义时赋值才成功;如果曾定义过,此赋值无效
C := xxx // 立即变量
D += yyy // 如果 D 在前面是延时变量,那么现在它还是延时变量;
// 如果 D 在前面是立即变量,那么现在它还是立即变量

GNU make中有两种给变量赋值的方式:延迟变量和立即变量。

上图中,变量A是一个延迟变量,只有在使用时才对其值进行扩展和确定。例如:

A = $@
test:
@echo $A

在上面的Makefile中,变量A的值仅在执行期间确定。它等于test,是一个延迟变量。

如果使用“A := @”,这是一个立即变量,然后@”,这是一个立即变量,然后@”,这是一个立即变量,然后@为空,所以A的值为空。

C. 变量导出:

在编译程序时,我们会不断地使用“make -C dir”切换到其他目录,执行其他目录中的Makefile。如果您希望变量的值在所有目录中可见,请将其导出。

例如“CC = $(CROSS_COMPILE)gcc”,这个CC变量代表编译器,在整个过程中都是一样的。定义好后,使用“export CC”将其导出。

D. Makefile 中可以使用 Shell 命令:

例如:

TOPDIR := $(shell pwd)

这是一个立即变量,TOPDIR 等于 shell 命令 pwd 的结果。

E. 如何将第一个目标放入 Makefile 中:

如果执行make命令时不指定目标,则默认生成第一个目标。

所以“目标1”,位置很重要。有时将第一个目标完全放在文件前面并不方便。在这种情况下,您可以将目标直接放在文件前面,然后再完善其依赖项和命令。例如:

First_target: // 这句话放在前面
.... // 其他代码,比如 include 其他文件得到后面的 xxx 变量
First_target : $(xxx) $(yyy) // 在文件的后面再来完善
command

F. 假设目标:

我们的 Makefile 中有这个目标:

clean:
rm -f $(shell find -name "*.o")
rm -f $(TARGET)

如果当前目录下正好有一个名为clean的文件,那么执行make clean时就不会执行这些删除命令。

这时,我们需要将“clean”目标设置为“假想目标”,以保证执行“make clean”时能够执行那些删除命令。

使用以下语句将“clean”设置为假设目标:

.PHONY : clean

G、常用功能:

我。 $(foreach var,列表,文本)

简单地说,对于列表中的每个变量,将其更改为文本。对于列表中的每个元素,将其取出并赋值给var,然后将var更改为文本描述的形式。

例子:

objs := a.o b.o
dep_files := $(foreach f, $(objs), .$(f).d) // 最终 dep_files := .a.o.d .b.o.d

二. $(通配符模式)

按pattern列出的文件是否存在,列出所有存在的文件。

例子:

src_files := $( wildcard *.c) // 最终 src_files 中列出了当前目录下的所有.c 文件

三. $(过滤模式...,文本)

过滤掉并保留文本中符合模式格式的内容。

例子:

obj-y := a.o b.o c/ d/
DIR := $(filter %/, $(obj-y)) //结果为:c/ d/

四. $(过滤掉模式...,文本)

过滤掉文本中符合模式格式的内容。

例子:

obj-y := a.o b.o c/ d/
DIR := $(filter-out %/, $(obj-y)) //结果为:a.o b.o

六. $(patsubst模式,替换,文本)

在文本'中查找与格式模式匹配的单词'并将其替换为替换'。通配符可以用在“pattern”和“replacement”中。

例如:

subdir-y := c/ d/
subdir-y := $(patsubst %/, %, $(subdir-y)) // 结果为:c d

② 通用Makefile的设计思路

A、在Makefile中确定需要编译的文件和目录,如:

obj-y += main.o
obj-y += a/

“Makefile”文件始终包含在“Makefile.build”中。

B、在Makefile.build中设置编译规则。有3条编译规则:

我。如何编译子目录?进入子目录进行编译:

$(subdir-y):
make -C $@ -f $(TOPDIR)/Makefile.build

二.如何编译当前目录下的文件?

%.o : %.c
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $<

三.当前目录下的.o和子目录下的built-in.o必须打包:

built-in.o : $(cur_objs) $(subdir_objs)
$(LD) -r -o $@ $^

C、在顶层Makefile中,将顶层目录中的built-in.o链接到APP中:

$(TARGET) : built-in.o
$(CC) $(LDFLAGS) -o $(TARGET) built-in.o

③ 情景解读

无需阅读本节下面的内容。为编写《嵌入式Linux应用开发完全手册升级版》一书而准备。只需结合第 3.0 节观看视频即可。

3.2 Makefile规则

一个简单的 Makefile 包含一系列以下格式的“规则”:

目标(target)…: 依赖(prerequiries)…
<tab>命令(command)

目标通常是要生成的文件的名称,可以是可执行文件或OBJ文件,也可以是要执行的操作的名称,例如clean。

依赖项是用于生成目标的材料(例如源文件)。一个目标通常有多个依赖项。

命令是生成目标时执行的操作。一条规则可以包含多个命令,每个命令占一行。

注意:每个命令行前面必须有一个Tab字符,即命令行的第一个字符是Tab。这就是事情很容易出错的地方。

通常,如果依赖项发生更改,则需要规则来调用命令来更新或创建目标。但并非所有目标都具有依赖性。例如,目标“clean”用于清理文件,它没有依赖关系。

规则通常解释如何以及何时重建目标。在创建或更新目标之前,make 首先调用命令来处理依赖关系。

当然,规则也可以用来解释如何以及何时执行一个动作,即打印一条提示消息。

Makefile 可以包含规则以外的文本,但简单的 Makefile 只需包含规则。虽然实际规则比此处显示的示例更复杂,但格式完全相同。

对于上述Makefile,执行“make”命令时,只有当hello.c文件比hello文件新时,才会执行命令“armlinux-gcc –o hello hello.c”,生成可执行文件hello;如果还没有hello文件,该命令也会被执行。

当运行“make clean”时,由于目标clean没有依赖关系,因此它的命令“rm -f hello”将被强制执行。

3.3 Makefile中的赋值方法

定义变量的语法如下:

immediate = deferred
immediate ?= deferred
immediate := immediate
immediate += deferred or immediate
define immediate
deferred
endef

GNU make中有两种给变量赋值的方式:延迟变量和立即变量。区别在于它们的定义方式和扩展方式。前者是在使用变量时展开的,也就是说变量的值在实际使用时就确定了;后者的值在定义时就已经确定了。使用=、?= 定义的变量或使用define 指令定义的变量是延迟变量;使用 := 定义的变量是直接变量。需要注意的一点是,?= 仅在变量尚未定义时才有效,即 ?= 用于定义延迟变量第一次出现的情况。

对于附加运算符+=,如果前面使用(:=)将右侧变量定义为立即变量,则它也是立即变量,否则它是延迟变量。

3.4 Makefile常用函数

函数调用的格式如下:

$(function arguments) 

这里的function是函数名,arguments是函数的参数。参数和函数名称由空格或制表符分隔。

如果有多个参数,请用逗号分隔。这些空格和逗号不是参数值的一部分。

内核Makefile中使用了大量的函数。现在我们就介绍一些常用的。

3.4.1 字符串替换和分析函数

(1)$(替换自、至、文本)

使用 to 替换文本中出现的所有 from。

例如:

$(subst ee,EE,feet on the street) 

结果是街上的 fEEt。

(2)$(patsubst 模式,替换,文本)

查找文本中与格式模式匹配的单词,并将其替换为替换。模式和替换

通配符可以用在 .

例如:

$(patsubst %.c,%.o,x.c.c bar.c)

结果是:xco bar.o。

(3)$(剥离字符串)

删除前导和尾随空格,并将中间的多个空格压缩为单个空格。

例如:

$(去掉 abc )

结果是abc。

(4)$(findstring 查找,in)

在字符串in中查找find。如果找到则返回值为find,否则返回值为空。

例如:

$(查找字符串 a,abc)

$(查找字符串a,bc)

将分别产生值 a 和 `` (空字符串)。

(5)$(过滤模式...,文本)

返回文本中以空格分隔且与格式模式匹配的单词...,并删除与格式模式不匹配的单词...。

例如:

$(filter %.c %.s,foo.c bar.c baz.s ugh.h) 

结果是 foo.c bar.c baz.s。

(6)$(过滤掉模式...,文本)

返回文本中与格式模式不匹配的以空格分隔的单词...,并删除与格式模式匹配的单词...。它是函数filter的反函数。

例如:

$(filter %.c %.s,foo.c bar.c baz.s ugh.h) 

结果是呃.h。

(7)$(排序列表)

按字母顺序对列表中的单词进行排序并删除重复的单词。输出由单个空格分隔的单词列表。

例如:

$(sort foo bar lose) 

返回值是bar foo loss。

3.4.2 文件名功能

(1)$(目录名称...)

提取名称中每个文件名的路径部分...文件名的路径部分包括从文件名的第一个字符到最后一个斜杠。

破折号之前的所有字符(包括斜杠)。

例如:

$(dir src/foo.c hacks)

结果是 src/./。

(2)$(不是目录名称...)

提取names...中每个文件名中除路径部分(真实文件名)之外的所有字符。

例如:

$(notdir src/foo.c hacks) 

结果就是 foo.c 被黑客攻击。

(3)$(后缀名...)

提取names中每个文件名的后缀....

例如:

$(suffix src/foo.c src-1.0/bar.c hacks) 

结果是.c.c。

(4)$(基本名称...)

提取名称中每个文件名中除后缀之外的所有字符....

例如:

$(basename src/foo.c src-1.0/bar hacks) 

结果是 src/foo src-1.0/bar hacks。

(5)$(addsuffix 后缀,名称...)

参数name...是一系列文件名,用空格分隔; suffix是后缀名。将后缀(suffix)的值附加到每个独立文件名的末尾。完成后,文件名将连接起来,并用一个空格分隔。

例如:

$(addsuffix .c,foo bar) 

结果是 foo.c bar.c。

(6)$(addprefix 前缀,名称...)

参数名称是一系列文件名,以空格分隔; prefix 是前缀名称。 will 前缀(前缀)

的值附加到每个单独文件名的前面,完成后,文件名将连接起来,并用一个空格分隔。

例如:

$(addprefix src/,foo bar) 

结果是 src/foo src/bar。

(7)$(通配符模式)

参数pattern是包含通配符的文件名格式(通配符的使用方式与shell中相同)。函数通配符

结果是与格式匹配的实际文件名称列表,以空格分隔。

例如,当前目录下有文件1.c、2.c、1.h、2.h,则:

c_src := $(wildcard *.c) 

结果是 1.c 2.c。

3.4.3 其他功能

(1)$(foreach 变量,列表,文本)

前两个参数 var 和 list 将首先展开。注意,此时最后一个参数文本并未展开;然后,将列表扩展得到的每个单词赋值给var变量;那么text引用的是要扩展的变量,所以每次text的扩展名都是不同的。

该函数的结果是由空格分隔的文本。对列表进行多次扩展后,得到新的列表,即:text。多次展开的单词连接在一起,单词之间用空格分隔。这创建了一个函数。 foreach的返回值。

下面是一个简单的示例,它将变量 files 的值设置为 dirs 中所有目录中的所有文件的列表:

dirs := a b c d
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))

这里的text为$(通配符$(dir)/*),其扩展过程如下:

① 赋给变量dir的第一个值为a,扩展结果为$(通配符a/*);

② 赋给变量dir的第二个值为b,扩展结果为$(通配符b/*);

③ 赋给变量dir的第三个值为c,扩展结果为$(通配符c/*);

④ 如此继续扩大。

此示例与以下示例具有相同的结果:

files := $(wildcard a/* b/* c/* d/*) 

(2)$(if条件,则部分[,else部分])

首先删除第一个参数“condition”的前导和尾随空格,然后展开它。如果扩展为非空字符串,则条件“condition”为“true”;如果扩展为空字符串,则条件“条件”为“假”。

如果条件'condition'为'true',则计算第二个参数'then-part'的值并将其用作整个函数if的值。

如果条件'condition'为'false'且第三个参数存在,则计算第三个参数'else-part'的值,并将该值作为整个函数if的值;如果第三个参数不存在,则该函数将不计算任何内容并返回空值。

注意:只能计算“then-part”和“else-part”之一,不能同时计算两者。这可能会产生副作用(例如函数 shell 调用)。

(3) (originvariable) 变量“variable”是查询变量的名称,而不是对该变量的引用。因此,不能使用 '(origin 变量)。变量“variable”是查询变量的名称,而不是对该变量的引用。因此,不能使用 '(originvariable) 变量。 “variable”是查询变量的名称,而不是对该变量的引用。因此,变量不能写成''加括号的格式。当然,如果需要使用非常量的文件名,可以在文件名中使用变量引用。

函数origin的结果是一个字符串,字符串变量定义如下:

'undefined':如果变量'variable'从未被定义过;

'default':变量'variable'是默认定义;

'environment':变量'variable'定义为环境变量,选项'-e'不打开;

'environment override':变量'variable'被定义为环境变量,选项'-e'打开;

'file':变量'variable'在Makefile中定义;

‘command line’:变量‘variable’在命令行中定义;

'override':变量'variable'是在Makefile中使用override指令定义的;

'automatic':变量'variable'是一个自动变量

(4)$(shell 命令参数)

函数shell是make与外部环境之间的通信工具。执行函数 shell 的结果与在控制台上执行“命令参数”的结果类似。但是,如果“命令参数”的结果包含换行符(和回车符),它们将在函数 shell 的返回结果中被处理为单个空格。如果返回结果以换行符(和回车符)结尾,它将被删除。 。

例如,当前目录下有文件1.c、2.c、1.h、2.h,则:

c_src := $(shell ls *.c)

结果是“1.c 2.c”

“Makefile简介”的这一部分可以作为阅读内核、引导加载程序和应用程序的Makefile文件时的参考手册。下面以options程序的Makefile为例进行演示。 Makefile的内容如下:

File: Makefile
01 src := $(shell ls *.c)
02 objs := $(patsubst %.c,%.o,$(src))
03 
04 test: $(objs)
05 gcc -o $@ $^
06
07 %.o:%.c
08 gcc -c -o $@ $<
09 
10 clean:
11 rm -f test *.o

在上面的Makefile中,@、@、@、^、

你可能想看:
« 上一篇
下一篇 »

相关推荐

FreeBasic:强大免费开源的64位basic编译器及应用优势

2025年05月10日

82阅读

脊柱侧弯矫正方法大盘点:运动康复、支具矫正等多种方式

2025年05月09日

87阅读

如何在winForm标题栏去掉标题文字且任务栏图标正常显示

2025年05月09日

86阅读

十一黄金周促销广告词大全,国庆购物狂欢等你来

2025年05月09日

78阅读

关闭快速编辑模式及插入模式,设置控制台指定文字颜色举例

2025年05月09日

63阅读

黄金搭档低糖高钙饮品包装及成分功效全解析,适合人群要知晓

2025年05月09日

91阅读