Makefile

第一次学习Makefile是云里雾里的,但是我们不要怕要有信心去搞定它。一步一步慢慢分析命令是怎么构成的!!!!

参考文章:Makefile入门(超详细一文读懂)-CSDN博客

一、工程管理器make

当我们需要编译一个比较大的项目时,编译命令会变得越来越复杂,需要编译的文件越来越多。其次就是项目中并不是每一次编译都需要把所有文件都重新编译,比如没有被修改过的文件则不需要重新编译。工程管理器就帮助我们来优化这两个问题。
MakeFile就类似于make工程管理的工作的脚本。用来告诉工程管理器如何正确的编译我们的程序。

二、Makefile和Make的详细讲解

make 是一个用于自动化构建工程的工具,可以根据 Makefile 中定义的规则自动完成编译、链接等操作。Makefile 则是 make 工具使用的脚本文件,定义了目标(target)、依赖(dependency)和命令(command)的关系。

2.1Makefile基本语法

2.1.1语法:

目标:依赖
    命令

 目标必须存在,依赖不是必须的,命令之前必须是"tab"键

Makefile 文件的命名一般是 Makefile没有后缀也没有前缀,命名首字母M

2.1.2验证:

1.当前文件目录下没有目标文件

2.当前文件目录下有目标文件 没有依赖

 make: 'Even' is up to date.

目标文件是最新的,并没有去执行规则

 3.当目标文件比依赖文件时间新,那么规则不执行,否则执行

依赖时间比目标时间新->执行规则

依赖时间比目标时间旧->不执行规则 

 2.1.3TAB之后跟的是一个shell指令

 执行了ls指令,再执行了删除jacy的指令

make执行Makefile文件的顺序

当我们输入make 的时候:
1. 工程管理器,先会默认在当前路径下寻找一个名为Makefile的文件
2. 确认Makefile文件中的目标(最终)
3. 检查最终目标是否已经存在
        a. 如果存在则检查规则中是否存在依赖
                i. 如果规则中没有依赖,则不运行规则
                ii. 如果有依赖则检查依赖是否真实存在
                        1. 有存在检查时间戳判断是否执行规则
                        2. 不存在则之直接报错
        b. 如果不存在,则直接运行规则

注意事项:

1、如果规则中没有写依赖,则无论如何该规则该规则都会执行
2、如果目标已经存在,然后也没有写依赖则不执行该规则

3、如果目标文件已经存在,规则中有写依赖并且依赖文件比目标文件更新,则规则会被执行

4、注意制表符表示后面紧接着的是一个Shell 命令

 2.2目标与依赖嵌套使用

根据一层一层的依赖关系,最先运行的指令是最底层的,类似于函数调用或者递归的操作

执行方式

make

make -jacy //告诉工程管理器 jacy是我们的最终目标 

2.3Makefile编译程序

我们以相同的程序使用Makefil的各种规则来实现不同版本相同功能的Makefile

2.3.1第一版本

./bin/main                         #main是我们的目标

./src/*.c                            #依赖是src里所有的c文件

gcc ./src/*.c -o ./bin/main -I./inc -L./lib -lmy_lib  #执行编译的shell指令,生成可执行的二进制文件main

 2.3.2第二版本

首先我们认识一下Makefile变量

在Makefile 中变量属于弱类型,在Makefile中变量就是一个名字(像是C语言中的宏),代表
一个文本字符串(变量的值),可以在Makefile的目标、依赖、命令中引用一个变量的地方
在Makefile中变量的特征有以下几点:
1. 变量和函数的展开(除规则的命令行以外),是在make读取Makefile文件时进行
的,这里的变量包括了使用“=”定义和使用指示符“define”定义的变量
2. 变量可以用来代表一个文件名列表、编译选项列表、程序运行的选项参数列表、搜
索源文件的目录列表、编译输出的目录列表和所有我们能够想到的事物

3. 变量名不能包括“:”、“#”、“=”、前置空白和尾空白的任何字符串。需要注意的是,
尽管在GNU make中没有对变量的命名有其它的限制,但定义一个包含除字母、数字和下
划线以外的变量的做法也是不可取的,因为除字母、数字和下划线以外的其它字符可能会在
以后的make版本中被赋予特殊含义,并且这样命名的变量对于一些Shell来说不能作为环
境变量使用。
4. 变量名是大小写敏感的。变量“foo”、“Foo”和“FOO”指的是三个不同的变量
Makefile传统做法是变量名是全采用大写的方式。推荐的做法是在对于内部定义的一般变
量(例如:目标文件列表objects)使用小写方式,而对于一些参数列表(例如:编译选项
CFLAGS)采用大写方式,这并不是要求的。但需要强调一点:对于一个工程,所Makefile
中的变量命名应保持一种风格,否则会显得你是一个蹩脚的开发者(就像代码的变量命名风
格一样),随时有被鄙视的危险。
5. 另外有一些变量名只包含了一个或者很少的几个特殊的字符(符号)。称它们为自
动化变量。像“<”、“@”、“?”、“*”、“@D”、“%F”、“^D”等等,后面会详
述之。

6. 变量的引用跟Shell脚本类似,使用美元符号和圆括号比如有个变量叫A,那么对
他的引用则是$(A),有个自动化变量叫@,则对他的引用是$(@),有个系统变量是CC则
对其引用的格式是$(CC)。对于前面两个变量而言,他们都是单字符变量,因此对他们引用
的括号可以省略,写成$A和$@。

三种变量类型
1、自定义变量

用户自己定义的变量

A = apple # 定义并赋值变量
B = I love China
C = $(A) tree # $() 则是对某一个变量进行引用

Even:
        @echo $(A)
        @echo $(B)
        @echo $(C)

使用变量修改第一版本

TAG=./bin/main
SRC=./src/*.c
CC=gcc
O=-o
CONFIG=-I./inc -L./lib -lmy_lib


$(TAG):$(SRC)
        $(CC) $(SRC) $(O) $(TAG) $(CONFIG)


clean:
        rm ./bin/*

通过更改定义的变量来实现对引用这个变量的所有指令进行修改(达到了c里实现宏的效果)

我们知道目标文件更新如果比依赖新那么Makefile就不会执行,因此我们可以在Makefile里添加其他的目标与依赖来分别执行这样就不用每次还要更新一下依赖文件Makefile才会执行。比如如添加删除bin目录下的文件、制作库文件的一些执行。

 2、系统预定义变量

3、自动化变量

        <、@、?、#等等,这些特殊的变量之所以称为自动化变量,是因为他们的值会“自动地”发生变化,本质就是运行规则的文件不同他们的指代也就不同罢了。例如

比如@当一套规则中目标文件改变了那么命令中的@也就变了,之后会有例子

 自动化变量表

例如可以将

改为

Makefile 中定义的变量的方式

Makefile 中定义的变量有4不同的方式:

1,递归定义方式:

A = I love $(B) # 在第一行使用到变量B但是还没有定义,因此管理器进行全文搜索找到B引用
B = China

2,直接定义方式:
B = China
A := I love $(B)#这个就是对A直接定义

如果B在A定义之前那么就执行B,如果其定义里出现有对其他变量的引用的话,只会其前面的语句进行搜寻(不包含自己所在的那一行),而不是搜寻整个文件

下面这种就不会调用B

A := I love $(B) # A在B之前引用B 则为空
B = China
3、条件定义方式:

有时我们需要先判断一个变量是否已经定义了,如果已经定义了则不作操作,如果没有
定义再来定义它的值,这时最方便的方法就是采用所谓的条件定义方式:

A = apple
A ?= I love China

此处对 A 进行了两次定义,其中第二次是条件定义,其含义是:如果 A 在此之前没有
定义,则定义为“I love China”,否则维持原有的值。

4,多行命令定义方式:
define commands
    echo “thank you!”
    echo “you are welcome.”
endef

此处定义了一个包含多行命令的变量commands,我们利用它的这个特点实现一个完整命令包的定义。注意其语法格式:以define开头,以endef结束,所要定义的变量名必须在指示符“define”的同一行之后,指示符define所在行的下一行开始一直到“end”所在行的上一行之间的若干行,是变量的值。这种方式定义的所谓命令包,可以理解为编程语言中的函数。

Makefile中的变量两种操作方式: 

1,追加变量的值,例如:

A = apple
A += tree

这样,变量A的值就是apple tree。
2,修改变量的值,例如:

A = srt.c string.c tcl.c
B = $(A:%.c=%.o)

B = $(A:%.c=%.o)->将A指令中的.c变成了.o

第三个版本

TAG=./bin/main		#目标
SRC=./src/hehe.c ./src/test.c		#依赖
#OBJ=./src/hehe.o ./src/test.o
OBJ=$(SRC:%.c=%.o)
CC=gcc
O=-o
CONFIG=-I./inc 


$(TAG):$(OBJ)
	$(CC) $(^) $(O) $(@) $(CONFIG)

%.o:%.c	#这是一条隐式规则,所有的.o是由.c文件构成
#方式是
#<(依赖列表第一个文件的完整名称)
#@(所在规则的目标完整名称)
#$(CONFIG)指定的文件路径
#-c生成.o文件停下来
	$(CC) $< -o $(@) $(CONFIG) -c 


clean:
	$(RM) ./bin/* ./src/*.o

 

看似复杂的东西其实就是编译代码的一些指令结合Makefile的规则来构成这个Makefile文件。

函数的使用

1、 $(subst FROM,TO,TEXT)

功能:
将字符串 TEXT 中的字符 FROM 替换为 TO。
返回:
替换之后的新字符串。

 2、 $(wildcard PATTERN)

功能:
获取匹配模式为 PATTERN 的文件名。
返回:
匹配模式为 PATTERN 的文件名。

例如:当src文件下只有hehe.c test.c时下面两句等效

#SRC=./src/hehe.c ./src/test.c      #依赖

SRC=$(wildcard src/*.c)
3、override 修饰变量

 override CFLAGS += -Wall

 修饰变量后,在执行make  CFLAGS="hello",CFLAGS不会被改变,当=之前加上+就会在原本值上加上这个新的值

例如:

 实战

我们通过对CONFIG进行override之后我们就可以在make时添加指令比如输出警告

4、  .PHONY 

.PHONY 来明确地告诉 Makefile,不要对 clean 运用任何隐式规则,不能运用隐式规则的目标被称为伪目标. 

比如有个clean文件,我们在make clean时就不会执行clean规则

.PHONY修饰clean后也可以只执行clean规则

第四版本通用版

TAG=./bin/main		#目标
SRC=$(wildcard src/*.c)
OBJ=$(SRC:%.c=%.o)
CC=gcc
O=-o
override CONFIG+=-I./inc 


$(TAG):$(OBJ)
	$(CC) $(^) $(O) $(@) $(CONFIG)

%.o:%.c	
	$(CC) $< -o $(@) $(CONFIG) -c 


clean:
	$(RM) ./bin/* ./src/*.o

 .PHONY:clean

 

 指定运行makefile

多个Makefile指定运行一套规则:make -f  要执行的Makefile文件名

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值