说明:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
QQ 群 号:513683159 【相互学习】
内容来源:
例说makefile 索引博文
根据前篇所得实际还有很多可测试的点,这边仅做些许简单示例,有时间在进行补充。
上一篇:基础知识:篇4-make工具与Makefile文件概念
下一篇:基础知识:篇6-cmake工具与CMakeLists.txt文件
目录:
实验一:头文件与源文件放在同一文件夹下
0、创建文件
与该篇《基础知识:篇2-多源文件编译过程》示例一致。
1️⃣mymath.c文件
#include<stdio.h>
#include "add.h"
#include "sub.h"
int main(int argc, char **argv)
{
int a = 10 ,b = 5;
printf("%d + %d = %d\n",a,b,add(a,b));
printf("%d - %d = %d\n",a,b,sub(a,b));
return 0;
}
2️⃣add.c文件
#include "add.h"
int add(int a, int b)
{
return a + b;
}
3️⃣add.h文件
#ifndef _ADD_H_
#define _ADD_H_
int add(int a, int b);
#endif
4️⃣sub.c文件
#include "sub.h"
int sub(int a, int b)
{
return a - b;
}
5️⃣sub.h文件
#ifndef _SUB_H_
#define _SUB_H_
int sub(int a, int b);
#endif
1、文件树结构
.
├── add.c
├── add.h
├── Makefile
├── mymath.c
├── mymath.o
├── sub.c
└── sub.h
2、编译
终端输入:gcc mymath.c add.c sub.c -o mymath
,得到可执行文件mymath。
这是直接使用指令实现,接下来将演示使用Makefile文件进行编译
3、Makefile文件
版本一:(显示规则标准版)
mymath:mymath.o add.o sub.o
gcc mymath.o add.o sub.o -o mymath
mymath.o:mymath.c add.h sub.h
gcc -c mymath.c -o mymath.o
add.o:add.c add.h
gcc -c add.c -o add.o
sub.o:sub.c sub.h
gcc -c sub.c -o sub.o
clean:
rm -f *.o mymath
PS:
严格按照如下结构编写:
目标:依赖列表
命令
①注意命令必须以【Tab】开头
②依赖列表用于表示与目标的联系,只有写在依赖列表上,当依赖文件发生变化时,目标才会重新编译,若没写在依赖列表上,则即使依赖文件发生变化也会认为已经是最新版本。
版本二:(变量版)
TARGET = mymath #最终目标
OBJECT = mymath.o add.o sub.o #中间的目标文件
CC = gcc #编译器
CFLAGS = -c
$(TARGET):$(OBJECT)
$(CC) $(OBJECT) -o $(TARGET)
mymath.o:mymath.c add.h sub.h
$(CC) $(CFLAGS) mymath.c -o mymath.o
add.o:add.c add.h
$(CC) $(CFLAGS) add.c -o add.o
sub.o:sub.c sub.h
$(CC) $(CFLAGS) sub.c -o sub.o
clean:
rm -f *.o $(TARGET)
定义变量并使用变量能够程序更简化并易于修改等优点。
版本三:(自动推导版)
GNU的make工具很强大,可自动推导文件及其文件依赖关系后命令,如:
1️⃣看到【.o文件】自动将【.c文件】加入依赖关系,如:add.o
= add.o:add.c
2️⃣并推导出对应的编译指令,如:$(CC) -c add.c
TARGET = mymath #最终目标
OBJECT = mymath.o add.o sub.o #中间的目标文件
CC = gcc #编译器
$(TARGET):$(OBJECT)
$(CC) $(OBJECT) -o $(TARGET)
mymath.o: add.h sub.h
add.o: add.h
sub.o: sub.h
clean:
rm -f *.o $(TARGET)
注意依赖列表中其他文件仍需放在依赖列表而不可省,若省略则会造成改变对应文件而不会重新编译。
版本四:(自动变量版)
TARGET = mymath #最终目标
OBJECT = mymath.o add.o sub.o #中间的目标文件
CC = gcc #编译器
$(TARGET):$(OBJECT)
$(CC) $^ -o $@
mymath.o: add.h sub.h
add.o: add.h
sub.o: sub.h
clean:
rm -f *.o $@
$@
:规则的目标文件
$^
:所有的依赖文件
实验二:头文件与源文件没放在同一文件夹下
1、文件树结构
.
├── include
│ ├── add.h
│ └── sub.h
├── Makefile
├── mymath.c
├── add.c
└── sub.c
2、编译
终端输入:gcc mymath.c add.c sub.c -I ./include/ -o mymath
,得到可执行文件mymath。
这是直接使用指令实现,接下来将演示使用Makefile文件进行编译
3、Makefile文件
版本一:(终端指令版)
mymath:
gcc mymath.c add.c sub.c -I ./include/ -o mymath
clean:
rm mymath
该版本与直接在终端调用指令相差无几,需注意:
1️⃣终极目标必须要有。
2️⃣命令行必须以【Tab】开始。
版本二:(显示规则标准版)
mymath:mymath.o add.o sub.o
gcc mymath.o add.o sub.o -o mymath
mymath.o:mymath.c ./include/add.h ./include/sub.h
gcc -c mymath.c -I ./include/ -o mymath.o
add.o:add.c ./include/add.h
gcc -c add.c -I ./include/ -o add.o
sub.o:sub.c ./include/sub.h
gcc -c sub.c -I ./include/ -o sub.o
clean:
rm -f *.o mymath
为啥画风突然变得复杂了?这边就需要好好解释一番:
Makefile文件的结构:
目标:依赖列表
命令
可看出该版本是严格遵循该结构的,那么这样写有什么好处呢?
mymath是第一个目标,即最终目标,该目标有三个依赖文件:mymath.o add.o sub.o
,
有人说我把这三个文件删了应该也可以吧,这是不可以的,因为这三个文件是gcc mymath.o add.o sub.o -o mymath
所需,
若删除,则下面的目标是不会执行的,只有存在才会执行对应的目标
总结:最终目标是由三个目标文件链接而成的可执行文件:mymath,而三个目标文件作为最终目标的依赖才会去实现下面的三个目标。
可将其想象为树,父与子断开(没放在依赖列表中)后,则父没有原材料(如:add.o
)无法自己生产,需要子生产进行提供。
下面目标其实是可以省略依赖列表的(原材料找的到,如add.c
),但为什么不省略的?
因为若不添加到依赖列表,就无法实时检测对应文件是否更新,造成更改对应文件而不知导致不会重新编译。
一试便知,若删除依赖列表对应文件,你去修改后重新make它不会重新编译,若在依赖列表中则会重新编译。
注意依赖列表中文件一定要写对位置,否则会导致找不到对应文件,如:./include/add.h
不能写成add.h
下面的命令行必须是在指出所需头文件路径,-I ./include/
,若不知处则找不到头文件。
版本三:(变量版)
TARGET = mymath #最终目标
OBJECT = mymath.o add.o sub.o #中间的目标文件
CC = gcc #编译器
INCLUDE = -I ./include/ #头文件路径
$(TARGET):$(OBJECT)
$(CC) $(OBJECT) -o $(TARGET)
mymath.o:mymath.c ./include/add.h ./include/sub.h
$(CC) -c mymath.c $(INCLUDE) -o mymath.o
add.o:add.c ./include/add.h
$(CC) -c add.c $(INCLUDE) -o add.o
sub.o:sub.c ./include/sub.h
$(CC) -c sub.c $(INCLUDE) -o sub.o
clean:
rm -f *.o $(TARGET)
版本四:(自动推导版)
TARGET = mymath #最终目标
OBJECT = mymath.o add.o sub.o #中间的目标文件
CC = gcc #编译器
INCLUDE = -I ./include/ #头文件路径
$(TARGET):$(OBJECT)
$(CC) $(OBJECT) -o $(TARGET)
mymath.o:mymath.c ./include/add.h ./include/sub.h
$(CC) -c mymath.c $(INCLUDE)
add.o:add.c ./include/add.h
$(CC) -c add.c $(INCLUDE)
sub.o:sub.c ./include/sub.h
$(CC) -c sub.c $(INCLUDE)
clean:
rm -f *.o $(TARGET)