Makefile
Makefile简介
一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
Makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
make主要解决两个问题:
*1) 大量代码的关系维护*
大项目中源代码比较多,手工维护、编译时间长而且编译命令复杂,难以记忆及维护
把代码维护命令及编译命令写在makefile文件中,然后再用make工具解析此文件自动执行相应命令,可实现代码的合理编译
*2) 减少重复编译时间*
n 在改动其中一个文件的时候,能判断哪些文件被修改过,可以只对该文件进行重新编译,然后重新链接所有的目标文件,节省编译时间
Makefile文件命名规则
makefile和Makefile都可以,推荐使用Makefile。
make工具的安装
sudo apt install make
Makefile语法规则
规则
目标:依赖文件列表
命令列表
Makefile基本规则三要素:
1)目标:
-
通常是要产生的文件名称,目标可以是可执行文件或其它obj文件,也可是一个动作的名称
2)依赖文件:
-
用来输入从而产生目标的文件
-
一个目标通常有几个依赖文件(可以没有)
3)命令:
-
make执行的动作,一个规则可以含几个命令(可以没有)
-
有多个命令时,每个命令占一行
举例说明:
测试代码:
test: echo "hello world" test:test1 test2 echo "test" test1: echo "test1" test2: echo "test2"
Make命令格式
make是一个命令工具,它解释Makefile 中的指令(应该说是规则)。
make命令格式:
make -f file [ targets ]
1.[ -f file ]:
-
make默认在工作目录中寻找名为GNUmakefile、makefile、Makefile的文件作为makefile输入文件
-
-f 可以指定以上名字以外的文件作为makefile输入文件
l
2.[ options ]
-
-v: 显示make工具的版本信息
-
-w: 在处理makefile之前和之后显示工作路径
-
-C dir:读取makefile之前改变工作路径至dir目录
-
-n:只打印要执行的命令但不执行
-
-s:执行但不显示执行的命令
3.[ targets ]:
-
若使用make命令时没有指定目标,则make工具默认会实现makefile文件内的第一个目标,然后退出
-
指定了make工具要实现的目标,目标可以是一个或多个(多个目标间用空格隔开)。
Makefile工作原理
1)若想生成目标, 检查规则中的依赖条件是否存在,如不存在,则寻找是否有规则用来 生成该依赖文件
2) 检查规则中的目标是否需要更新,必须先检查它的所有依赖,依赖中有任一个被更新,则目标必须更新
总结:
-
分析各个目标和依赖之间的关系
-
根据依赖关系自底向上执行命令
-
根据修改时间比目标新,确定更新
-
如果目标不依赖任何条件,则执行对应命令,以示更新
Makefile示例
测试程序: test.c add.c sub.c mul.c div.c
最简单的Makefile
test:test.c add.c sub.c mul.c div.c gcc test.c add.c sub.c mul.c div.c -o test
缺点:效率低,修改一个文件,所有文件会被全部编译
Makefile中变量
在Makefile中使用变量有点类似于C语言中的宏定义,使用该变量相当于内容替换,使用变量可以使Makefile易于维护,修改内容变得简单变量定义及使用。
自定义变量
1)定义变量方法:
变量名=变量值
2)引用变量:
$(变量名)或${变量名}
3)makefile的变量名:
-
makefile变量名可以以数字开头
-
变量是大小写敏感的
-
变量一般都在makefile的头部定义
-
变量几乎可在makefile的任何地方使用
示例:
#变量 OBJS=add.o sub.o mul.o div.o test.o TARGET=test $(TARGET):$(OBJS) gcc $(OBJS) -o $(TARGET) add.o:add.c gcc -c add.c -o add.o sub.o:sub.c gcc -c sub.c -o sub.o mul.o:mul.c gcc -c mul.c -o mul.o div.o:div.c gcc -c div.c -o div.o test.o:test.c gcc -c test.c -o test.o clean: rm -rf $(OBJS) $(TARGET)
除了使用用户自定义变量,makefile中也提供了一些变量(变量名大写)供用户直接使用,我们可以直接对其进行赋值。
CC = gcc #arm-linux-gcc
CPPFLAGS : C预处理的选项 如:-I
CFLAGS: C编译器的选项 -Wall -g -c
LDFLAGS : 链接器选项 -L -l
自动变量
-
$@: 表示规则中的目标
-
$<: 表示规则中的第一个条件
-
$^: 表示规则中的所有条件, 组成一个列表, 以空格隔开,如果这个列表中有重复的项则消除重复项。
注意:自动变量只能在规则的命令中中使用
#变量 OBJS=add.o sub.o mul.o div.o test.o add.o TARGET=test CC=gcc #$@: 表示目标 #$<: 表示第一个依赖 #$^: 表示所有的依赖 $(TARGET):$(OBJS) #$(CC) $(OBJS) -o $(TARGET) $(CC) $^ -o $@ echo $@ echo $< echo $^ add.o:add.c $(CC) -c $< -o $@ sub.o:sub.c $(CC) -c $< -o $@ mul.o:mul.c $(CC) -c $< -o $@ div.o:div.c $(CC) -c $< -o $@ test.o:test.c $(CC) -c $< -o $@ clean: rm -rf $(OBJS) $(TARGET)
模式规则
模式规则示例:
%.o:%.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
Makefile第三个版本:
OBJS=test.o add.o sub.o mul.o div.o TARGET=test $(TARGET):$(OBJS) gcc $(OBJS) -o $(TARGET) %.o:%.c gcc -c $< -o $@
Makefile中函数
wildcard – 查找指定目录下的指定类型的文件
src = $(wildcard *.c) //找到当前目录下所有后缀为.c的文件,赋值给src
patsubst – 匹配替换
obj = $(patsubst %.c,%.o, $(src)) //把src变量里所有后缀为.c的文件替换成.o
第四个版本
SRC=$(wildcard *.c) OBJS=$(patsubst %.c, %.o, $(SRC)) TARGET=test $(TARGET):$(OBJS) gcc $(OBJS) -o $(TARGET) %.o:%.c gcc -c $< -o $@
Makefile中伪目标
clean用途: 清除编译生成的中间.o文件和最终目标文件
make clean 如果当前目录下有同名clean文件,则不执行clean对应的命令,解决方案:
伪目标声明: .PHONY:clean
clean命令中的特殊符号:
-
“-”此条命令出错,make也会继续执行后续的命令。如:“-rm main.o”
-
“@”不显示命令本身,只执行命令。如:“@echo clean done”
SRC=$(wildcard *.c) OBJS=$(patsubst %.c, %.o, $(SRC)) TARGET=test $(TARGET):$(OBJS) gcc $(OBJS) -o $(TARGET) %.o:%.c gcc -c $< -o $@ .PHONY:clean clean: rm -rf $(OBJS) $(TARGET)