一、制作规则
目标名:依赖
命令
目标名:最终生成得目标文件名
依赖:需要什么依赖来生成目标
命令:生成目标得指令(改行行需用tab键进行缩进)
例:app:main.c add.c sub.c
gcc main.c add.c sub.c -o app
二、制作makefile
1、准备工作
准备main.c、add.c、sub.c、head.h
1 #include <stdio.h>
2 #include "head.h"
3
4 int main(void)
5 {
6 int sum = add(1, 2);
7 printf("sum = %d\n", sum);
8 return 0;
9 }
1 #include "head.h"
2
3 int add(int a, int b)
4 {
5 int result = a + b;
6 return result;
7 }
1 #include "head.h"
2
3 int sub(int a, int b)
4 {
5 int result = a - b;
6 return result;
7 }
1 #ifndef _HEAD_H_
2 #define _HEAD_H
3
4 int add(int a, int b);
5 int sub(int a, int b);
6
7 #endif
2、编写makefile
- 入门式
1 app:main.c add.c sub.c
2 gcc main.c add.c sub.c -o app -I ./include
执行后:生成可执行文件app
- 进阶式
上述得makefile生成得目标文件,每次修改都会把所有文件都编译一遍,如果只修改一个文件,我们希望只重新编译修改得文件,这样可以节省时间;
对上述得makefile做如下修改:
1 app1:main.o add.o sub.o
2 gcc main.o add.o sub.o -o app1 -I ./include
3 main.o:main.c
4 gcc -c main.c -I ./include
5 add.o:add.c
6 gcc -c add.c -I ./include
7 sub.o:sub.c
8 gcc -c sub.c -I ./include
第一行得app1;是整个文件得终极目标,执行make得话,最终会生成app1出来;第一行后面得依赖main.o等文件如果没有会往下面查找对应得文件,找到第三行,main.o:main.c会生成main.o来完成第一行得依赖,其他得.o依赖以此类推;好处:这样单独文件编译得优点是每次修改单个文件得时候,make得时候只会编译修改得文件
上面可以看出,完成makefile文件后,执行make,第一次所有得文件都编译一遍,然后我们对add.c文件进行修改,加上一行空行,再次执行make得时候只会编译add.c文件,其他得.o文件还是第一次生成得;原理:系统会默认对比:修改的文件时间和以此文件为依赖生成目标文件的时间,如果生成的目标文件的时间小于修改的依赖文件,目标文件会重新生成。这里,修改了add.c文件,其对应的目标文件为add.o的生成时间是第一make生成的时间,修改add.c时间大于旧的add.o的时间,所有需要重新生成新的add.o文件,保证add.o目标文件的生成时间大于其所依赖的文件。
- 变量式
在makefile里面,跟编程类似,一类冗余的写法可以通过变量来进行优化,简捷写法
对进阶式的makefile更改如下:
1 obj=main.o add.o sub.o
2 target=app2
3 lib_path=./include
4 # makefile 中自己维护得变量,一般都是大写
5 CC = gcc
6 CPPLAGS = -I
7
8 $(target):$(obj)
9 $(CC) $(obj) -o $(target) $(CPPLAGS) $(lib_path)
10
11
12 %.o:%.c
13 gcc -c $< -o $@ $(CPPLAGS) $(lib_path)
这里对main.o等.o 文件用变量obj进行替代,获取obj的值,需要用$加上括号,类似:value = $(obj);自己定义的变量是小写,makefile有自己维护的变量,是大写字母的形式,获取方式和自己定义变量一样用$;
第12行的写法:
%o.%.c //同理,第8行会往下找对应的.o文件,找到第12行的时候,会根据%去自动匹配这里,会自动把%匹配成main;
第13行的写法:
$< :指定第一个依赖,例:上面的%.o:%.c 匹配成main.o:main.c;这里的第一个依赖就是main.c;($^:指所有的依赖)
$@:指定目标名,例:上面的%.o:%.c 匹配成main.o:main.c;这里的目标就是main.o
- 函数式
makefile中有一些自带的函数,这里简单介绍:wildcard 、patsubst的用法
wildcard:查找目录下文件;./*.c是作为参数,直接跟在wildcard后面的,即查找当前路径的所有.c文件
patsubst:替换文件,后面的参数,分别为:替换的原文件,替换的新文件,原文件的来源
1 #obj=main.o add.o sub.o
2 target=app2
3 lib_path=./include
4 # makefile 中自己维护得变量,一般都是大写
5 # makefile中的函数使用
6
7 src=$(wildcard ./*.c)
8 obj=$(patsubst ./%.c, ./%.o, $(src))
9
10 CC = gcc
11 CPPLAGS = -I
12
13 $(target):$(obj)
14 $(CC) $(obj) -o $(target) $(CPPLAGS) $(lib_path)
15
16
17 %.o:%.c
18 gcc -c $< -o $@ $(CPPLAGS) $(lib_path)
19
20 clean:
21 rm $(obj) $(target)
22
23 hello:
24 echo "hello makefile"
25
上面的写法还涉及到clean、和hello;这两个没有后面的依赖,之后跟的是命令,这样的写法,是便于make clean;make hello的操作,这样只执行对应后面的操作;如果直接make生成的是终极目标:app2;跟下面的clean、hello没关系,只有当make clean 、make hello才执行对应后面操作;
在执行make clean时,如果有个文件名为clean,make clean 里面的操作不会执行,而是报出当前文件是最新的,我们需要在makefile里面是把clean设置为伪目标,就不会跟做更新的比较(即不会比较目标文件的生成和依赖文件的时间);
修改的makefile:在clean加上.PHONY:clean
1 #obj=main.o add.o sub.o
2 target=app2
3 lib_path=./include
4 # makefile 中自己维护得变量,一般都是大写
5 # makefile中的函数使用
6
7 src=$(wildcard ./*.c)
8 obj=$(patsubst ./%.c, ./%.o, $(src))
9
10 CC = gcc
11 CPPLAGS = -I
12
13 $(target):$(obj)
14 $(CC) $(obj) -o $(target) $(CPPLAGS) $(lib_path)
15
16
17 %.o:%.c
18 gcc -c $< -o $@ $(CPPLAGS) $(lib_path)
19
20 #.PHONY:clean
21 clean:
22 rm $(obj) $(target) -f
23
24 hello:
25 echo "hello makefile"
上面再clean的操作后面加:-f;表示强制执行,不管当前存不存在删除的文件。