8.makefile
8.makefile
学习目标:
熟练使用规则编写简单的makefile文件
熟练使用makefile中的变量
熟练使用makefile中的函数
1.makefile
makefile文件中定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
makefile文件中会使用gcc编译器对源代码进行编译,最终生成可执行文件或者是库文件。
makefile文件的命名:makefile或者Makefile
1.1 makefile的基本规则
makefile由一组规则组成,规则如下:
目标: 依赖
(tab)命令
makefile基本规则三要素:
▶目标: 要生成的目标文件
▶ 依赖: 目标文件由哪些文件生成
▶ 命令: 通过执行该命令由依赖文件生成目标
下面以具体的例子来讲解:
当前目录下有main.c fun1.c fun2.c sum.c,根据这个基本规则编写一个简单的makefile文件,生成可执行文件main。
第一个版本的makefile:
缺点:效率低,修改一个文件,所有的文件会全部重新编译。
main: main.c fun1.c fun2.c sum.c
gcc -o main main.c fun1.c fun2.c sum.c
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include "head.h"
int main(int argc, char *argv[])
{
printf("this is main!\n");
int i = 0;
for(i=0; i<argc; i++)
{
printf("[%d]:[%s]\n", i, argv[i]);
}
fun1();
fun2();
printf("sum(10)==[%d]\n", sum(10));
return 0;
}
head.h
void fun1();
void fun2();
int sum(int len);
fun1.c
#include <stdio.h>
void fun1()
{
printf("this is fun1\n");
}
fun2.c
#include <stdio.h>
void fun2()
{
printf("this is fun2\n");
}
sum.c
#include <stdio.h>
int sum(int len)
{
int i = 0;
int sum = 0;
for(i=0; i<len; i++)
{
sum += i;
}
return sum;
}
cmt@cmt-VMware-Virtual-Platform:~/C_Program/day3$ make
gcc -o main main.c fun1.c fun2.c sum.c
cmt@cmt-VMware-Virtual-Platform:~/C_Program/day3$ ls -ltr
总计 44
-rw------- 1 cmt cmt 66 9月 30 20:40 fun2.c
-rw------- 1 cmt cmt 67 9月 30 20:40 fun1.c
-rw------- 1 cmt cmt 44 9月 30 20:40 head.h
-rw------- 1 cmt cmt 121 9月 30 20:40 sum.c
drwx------ 4 cmt cmt 4096 9月 30 20:40 linux系统编程第3天
-rw------- 1 cmt cmt 333 9月 30 20:45 main.c
-rw-rw-r-- 1 cmt cmt 73 9月 30 20:49 makefile
-rwxrwxr-x 1 cmt cmt 16184 9月 30 20:49 main
cmt@cmt-VMware-Virtual-Platform:~/C_Program/day3$ ./main
this is main!
[0]:[./main]
this is fun1
this is fun2
sum(10)==[45]
1.2 makefile工作原理
基本原则:
☀若想生成目标,检查规则中的所有的依赖文件是否都存在:
●如果有的依赖文件不存在,则向下搜索规则,看是否有生成该依赖文件的规则:
如果有规则用来生成该依赖文件,则执行规则中的命令生成依赖文件;
如果没有规则用来生成该依赖文件,则报错。
■如果所有依赖都存在,检查规则中的目标是否需要更新,必须先检查它的所有依赖,依赖中有任何一个被更新,则目标必须更新(检查的规则是哪个时间大哪个最新)
▶ 若目标的时间 > 依赖的时间,不更新
▶ 若目标的时间 < 依赖的时间,则更新
总结:
▶ 分析各个目标和依赖之间的关系
▶ 根据依赖关系自底向上执行命令
▶ 根据依赖文件的时间和目标文件的时间确定是否需要更新
▶ 如果目标不依赖任何条件,则执行对应命令,以示更新(如:伪目标)
第二个版本:
缺点: 冗余,若.c文件数量很多,编写起来比较麻烦。
如果对程序某个文件做了修改,如加入一个空白行,就需要重新编译,否则不需要并且出现下面的提示信息
make: “main”已是最新。
main: main.o fun1.o fun2.o sum.o
gcc - o main main.o fun1.o fun2.o sum.o
main.o : main.c
gcc - c main.c - I. /
fun1.o : fun1.c
gcc - c fun1.c
fun2.o : fun2.c
gcc - c fun2.c
sum.o : sum.c
gcc - c sum.c
cmt@cmt-VMware-Virtual-Platform:~/C_Program/day3$ rm main
cmt@cmt-VMware-Virtual-Platform:~/C_Program/day3$ ls
fun1.c fun2.c head.h linux系统编程第3天 main.c makefile sum.c
cmt@cmt-VMware-Virtual-Platform:~/C_Program/day3$ make
gcc -c main.c -I./
gcc -c fun1.c
gcc -c fun2.c
gcc -c sum.c
gcc -o main main.o fun1.o fun2.o sum.o
1.3 makefile中的变量
在makefile中使用变量有点类似于C语言中的宏定义,使用该变量相当于内容替换,使用变量可以使makefile易于维护,修改起来变得简单。
makefile有三种类型的变量:
▶ 普通变量
▶ 自带变量
▶ 自动变量
普通变量
▶ 变量定义直接用 =
▶ 使用变量值用 $(变量名)
如:下面是变量的定义和使用
foo = abc // 定义变量并赋值
bar = $(foo) // 使用变量,$(变量名)
定义了两个变量: foo、bar,其中bar的值是foo变量值的引用。
除了使用用户自定义变量,makefile中也提供了一些变量(变量名大写)供用户直接使用,我们可以直接对其进行赋值:
CC = gcc #arm-linux-gcc
CPPFLAGS : C预处理的选项 -I
CFLAGS: C编译器的选项 -Wall -g -c
LDFLAGS : 链接器选项 -L -l
自动变量
▶ $@: 表示规则中的目标
▶ $<: 表示规则中的第一个条件
▶ $^: 表示规则中的所有条件,组成一个列表,以空格隔开,如果这个列表中有重复的项则消除重复项。
特别注意:自动变量只能在规则的命令中使用。
模式规则
至少在规则的目标定义中要包含’%’,‘%’表示一个或多个,在依赖条件中同样可以使用’%’,依赖条件中的’%’的取值取决于其目标:
比如: main.o:main.c fun1.o: fun1.c fun2.o:fun2.c,说的简单点就是: xxx.o:xxx.c
makefile的第三个版本:
自定义变量
target = main
objects = main.o fun1.o fun2.o sum.o
$(target) : $(objects)
gcc - o main $(objects)
main.o : main.c
gcc - c main.c - I. /
fun1.o : fun1.c
gcc - c fun1.c
fun2.o : fun2.c
gcc - c fun2.c
sum.o : sum.c
gcc - c sum.c
自带变量
target = main
objects = main.o fun1.o fun2.o sum.o
CC = gcc
CPPFLAGS = -I. /
$(target) : $(objects)
$(CC) - o main $(objects)
main.o : main.c
$(CC) - c main.c $(CPPFLAGS)
fun1.o : fun1.c
$(CC) - c fun1.c
fun2.o : fun2.c
$(CC) - c fun2.c
sum.o : sum.c
$(CC) - c sum.c
自动变量
target = main
objects = main.o fun1.o fun2.o sum.o
CC = gcc
CPPFLAGS = -I. /
$(target) : $(objects)
$(CC) - o main $(objects)
main.o : main.c
$(CC) - c $ < $(CPPFLAGS)
fun1.o : fun1.c
$(CC) - c $ <
fun2.o : fun2.c
$(CC) - c $ <
sum.o : sum.c
$(CC) - c $<
target = main
objects = main.o fun1.o fun2.o sum.o
CC = gcc
CPPFLAGS = -I. /
$(target) : $(objects)
$(CC) - o $@ $^
main.o : main.c
$(CC) - c $ < $(CPPFLAGS)
fun1.o : fun1.c
$(CC) - c $ <
fun2.o : fun2.c
$(CC) - c $ <
sum.o : sum.c
$(CC) - c $<
target = main
objects = main.o fun1.o fun2.o sum.o
CC = gcc
CPPFLAGS = -I. /
$(target) : $(objects)
$(CC) - o $@ $ ^
% .o: % .c
$(CC) - c $ < $(CPPFLAGS)
#main.o: main.c
# $(CC) - c $ < $(CPPFLAGS)
#
#fun1.o: fun1.c
# $(CC) - c $ <
#
#fun2.o: fun2.c
# $(CC) - c $ <
#
#sum.o: sum.c
# $(CC) - c $<
1.4makefile函数
makefile中的函数有很多,在这里给大家介绍两个最常用的。
1.wildcard – 查找指定目录下的指定类型的文件
src=$(wildcard *.c) //找到当前目录下所有后缀为.c的文件,赋值给src
- patsubst – 匹配替换
obj=$(patsubst %.c,%.o, $(src)) //把src变量里所有后缀为.c的文件替换成.o
在makefile中所有的函数都是有返回值的。
当前目录下有main.c fun1.c fun2.c sum.c
src=$(wildcard *.c)
等价于src=main.c fun1.c fun2.c sum.c
obj=$(patsubst %.c,%.o, $(src))
等价于obj=main.o fun1.o fun2.o sum.o
makefile的第四个版本:
在makefile中所有的函数都是有返回值的。
当前目录下有main.c fun1.c fun2.c sum.c
src=$(wildcard *.c)
等价于src=main.c fun1.c fun2.c sum.c
obj=$(patsubst %.c,%.o, $(src))
等价于obj=main.o fun1.o fun2.o sum.o
makefile的第四个版本:
缺点: 每次重新编译都需要手工清理中间.o文件和最终目标文件
1.5 makefile的清理操作
用途: 清除编译生成的中间.o文件和最终目标文件
make clean 如果当前目录下有同名clean文件,则不执行clean对应的命令, 解决方案:
▶ 伪目标声明:
.PHONY:clean
■声明目标为伪目标之后, makefile将不会检查该目标是否存在或者该目标是否需要更新
- l clean命令中的特殊符号:
▶ “-”此条命令出错,make也会继续执行后续的命令。如:“-rm main.o”
rm -f: 强制执行, 比如若要删除的文件不存在使用-f不会报错
▶ “@”不显示命令本身, 只显示结果。如:“@echo clean done”
- 其它
– make 默认执行第一个出现的目标, 可通过make dest指定要执行的目标
– make -f : -f执行一个makefile文件名称, 使用make执行指定的makefile: make -f mainmak
makefile的第5个版本:
在makefile的第5个版本中, 综合使用了变量, 函数, 模式规则和清理命令,是一个比较完善的版本。
target = main
src = $(wildcard * .c)
objects = $(patsubst % .c, % .o, $(src))
CC = gcc
CPPFLAGS = -I. /
$(target) : $(objects)
$(CC) - o $@ $ ^
% .o: % .c
$(CC) - c $ < $(CPPFLAGS)
.PHONY : clean
clean :
-rm - f xxxxxx
rm - f $(objects) $(target)
#main.o: main.c
# $(CC) - c $ < $(CPPFLAGS)
#
#fun1.o: fun1.c
# $(CC) - c $ <
#
#fun2.o: fun2.c