Makefile文件的编写规则

欢迎大家关注笔者,你的关注是我持续更博的最大动力


Makefile文件编写规则

文章目录:

1 makefile文件介绍

makefile是一个项目代码管理工具,是管理我们程序的源代码的 ,makefile只是一个文件,在这个文件中记录了我们程序编译的步骤(和gcc命令编译一样,只是方便管理),

为什么需要makefile呢?

例如我们有一个项目,项目中有100个文件,项目做完之后需要编译成一个可执行文件,这样在编译程序的时候gcc就会写的很长,如果你只修改某个文件中的一个符号,就要gcc重新编译,这样就很容易出错,而且找错不太好找。因此,我们可以使用代码管理工具makefile,把代码的所有编译命令都写到makefile中,然后执行一些makefile中的命令,程序就会编译生成一个可执行文件

2 makefile文件编写

一、makefile文件命名的两种方式:

  • 全部小写:makefile
  • 首字母大写:Makefile

二、makefile里命令的规则

1、规则中的三要素

  • 目标:最终生成可执行程序的名字
  • 依赖:生成可执行程序的条件(即源代码
  • 命令:使用依赖生成对应的可执行程序

2、第一个版本的makefile

目标:依赖条件
	命令 (前面有一个Tab空格)


# 文件的目录结构
├── add.c
├── head.c
├── main.c
├── makefile
├── mul.c
└── sub.c

# 1、先新建一个makefile文件,里面的内容如下
# (make编译的时候,默认是在当前目录下找这些.c文件,如果有不在当前目录下的需要制定路径位置)
myapp:main.c add.c sub.c mul.c
	gcc main.c add.c sub.c -o myapp

# 编辑好makefile内容,保存退出,然后用make进行编译
>>>make makefile

3、第二个版本的makefile

上面的这种makefile是最low的,也存在一个问题,如果我们有一个.c文件发生了修改,那么所有的文件都要重新编译一次,如果文件特别多这样就会浪费很多的时间,效率非常低。想要提高效率:就是只对修改的文件进行重新编译,没有修改的就不编译,就对.c文件分开来编译,就需要生成.o文件

# myapp终极目标:可执行程序,因为依赖是一些 .o文件, 而我们是没有这些文件
# 因此就需要在下面的子目标中生成这些依赖的 .o文件
myapp:main.o add.o sub.o mul.o
	gcc main.o add.o sub.o mul.o -o myapp

# main.o是子目标
main.o:main.c
	gcc -c main.c

add.o:add.c
	gcc-c add.c

mul.o:mul.c
	gcc -c mul.c

sub.o:sub.c
	gcc -c sub.c

解释:

当生成可执行程序myapp时,查找依赖发现没有main.o,add.o等文件,就会向下查找规则,有没有生成这些.o文件规则编译生成这些.o文件依赖的命令),找到如果有就会执行下面的规则,全部执行完之后,再执行我们生成终极目标的规则。(下面的子目标规则,都是为了生成终极目标可执行文件服务的

注意:

  • 1)写在开头的是终极目标
  • 2)makefile子目标有没有更新,为什么可以只编译修改的,是根据文件的修改时间对比来实现的(这也是makefile的工作原理
  • 3)makefile中的注释用#

4、makefile文件简化

上面的文件有两个地方可以进行简化:

  • main.o add.o sub.o mul.o 上下都出现了写文件,对于这种多次使用的可以用变量赋值,makefile不需要变量类型
  • 下面的四个子目标的的规则一样,可以用

修改如下:

obj=main.o add.o sub.o mul.o
target=app
$(target):$(obj)
	gcc $(obj) -o $(target)

# main.o是子目标
%.o:%.c
	gcc -c $< -o $@

在vi底行模式下的替换 : 3,4s/app/$(target) 把第3,4行的app 替换成$(target)

说明,模式规则的三个自动变量使用:

  • %.o:相当于是对上面依赖的一个占位天空,当需要依赖main.o它就会变成main.o,后面与前面匹配就是main.c
  • $<:规则中的第一个依赖,如果把其套到第一个规则中,第一个依赖就是main.o,如果套到第一个子规则中,它的第一个依赖就是main.c
  • $@:规则中的目标 ,如果套到第一个子规则中,其目标就是main.o
  • $^:规则中所有的依赖

上面的三个模式规则只能在规则的命令中使用,即只能写到第二行中

eg:根据上面的规则:

  • gcc main.o add.o sub.o mul.o -o myapp 可以修改如下
  • gcc $^ -o $@

5、makefile中有一些系统自己维护的变量

makefile中有一些系统自己维护的变量,一般大写,例如:CC=cc, CC的默认值就是cc,cc其实就是gcc,你也可以修改为CC=gcc

常用的一些系统维护变量:

  • CC=gcc : 编译命令gcc,用户可以修改这些默认变量的值
  • CPPFLAGS=-I :编译命令的预编译参数 -I
  • CFLAGS=-WALL -g -c :编译的时候使用的参数
  • LDFALGS=-L -l :链接库使用的选项

6、makefile中函数

makefile中函数调用都是有返回值的,我们函数调用的目的就是为了拿到返回值,然后获取一些信息。

上面我们的所有.o文件的名字都是我们手动指定的obj=main.o add.o sub.o mul.o,如果有很多.o文件,我们不可能全部手动指定,这样会很繁琐。此时我们就可以通过makefile中的一个函数获取当前目录中的.o文件,但是这些.o文件在程序没有执行的时候是不存在的,是我们在编译程序的时候生成的中间文件,但是这个.o文件是通过.c文件生成的,因此需要获取指定目录下的.c文件,这样才能够生成对应的.o文件,因此,这里需要用到两个函数:

  • 获取指定目录下的.c文件

src=$(wildcard ./*.c)wildcard函数名,表示从某目录下查找文件,./*.c函数参数,表示在当前目录下查找所有的.c文件,然后获取返回值,再函数前面加一个$符号即可(加括号就是为了使用$符号取返回值)(返回的结果就是:m ain.c add.c sub.c mul.c)。

  • 把对应的.c文件换成.o文件

obj=$(patsubst ./%.o, ./%.c, $(src))patsubst是函数名,该函数是一个匹配替换函数。把当前目录下的所有.c文件替换为同名的.o文件,.c文件来自$(src)

使用上面的两个函数之后,不管当前目录下有多少文件,它会自动搜索所有的.c文件,然后做一个字符串替换成对应的.o文件,返回到obj。

#obj=main.o add.o sub.o mul.o
target=app

#src返回的内容就是我们从指定目录下查找的.c文件
src=$(wildcard ./*.c)
#把所有的.c替换成.o,这样得到的obj和我们手动指定的一样
obj=$(patsubst ./%.o, ./%.c, $(src))
# makefile自己维护的变量
CC = gcc
CPPFLAGS = -I
$(target):$(obj)
	$(CC) $(obj) -o $(target)

# main.o是子目标
%.o:%.c
	gcc -c $< -o $@

7、清除makefile编译生成的一些文件

当我们需要重新编译的时候,可以删除之前生成的一些中间文件。

target=app

src=$(wildcard ./*.c)
obj=$(patsubst ./%.o, ./%.c, $(src))
# makefile自己维护的变量
CC = gcc
CPPFLAGS = -I
$(target):$(obj)
	$(CC) $(obj) -o $(target)

# main.o是子目标
%.o:%.c
	gcc -c $< -o $@
	
# 删除我们生成的中间.o文件和可执行文件,当我们需要重新编译的时候使用
clean:
	rm $(obj) $(target)

hello:
	echo "hello, makefile"

然后在命令行中使用:

>>>make clean:即可清除之前编译生成的中间文件。此时只会执行clean这个子目标。

当要删除的文件在不存在的时候就会在命令行中提示:无法删除,或没有那个文件/目录,此时可以在后面加一个-f参数,表示无论存在与否,都会强制删除,也不会提示不存在信息。

clean:
	rm $(obj) $(target) -f

注意:

  • clean是要生成的目标
  • clean没有依赖
  • 在命令行下直接输入make编译生成的是终极目标
  • 在命令行中输入make 子目标,此时只会执行子目标中的命令 。例如:上面我们随便定义一个hello子目标,也没有依赖,此时可以直接在命令行中输入make hello,此时只会执行(编译)该子目标中的命令

8、声明伪目标

我们在执行make clean的时候并不会在当前目录下生成一个clean文件,当我们在当前目录创建一个叫clean名字的文件的时候,此时的clean文件就是最新的,当我们在去执行make clean的时候,就会提示clean是最新的,因为子目标会的clean会与clean文件的时间做对比。解决方式就是不让其做对比,把clean子目标声明为一个伪目标

# 使用.PHONY:clean 把clean声明为一个伪目标
.PHONY:clean
clean:
	rm $(obj) $(target)

声明为伪目标之后,再做make clean的时候就不会再做更新比较了。

9、makefile的一些其他细节

命令执行失败,直接忽略

clean:
	mkdir /aa   
	rm $(obj) $(target)

作为普通用户再根目录下创建mkdir /aa一个文件肯定会失败,要先获取权限才可以。

解决方式,执行的命令前加-,当命令执行失败之后会忽略,然后继续向下执行,如下:

clean:
	-mkdir /aa   
	rm $(obj) $(target)

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述
♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠

猜你喜欢

转载自blog.csdn.net/weixin_41010198/article/details/105437952