基础知识:篇5-Makefile示例

说明
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
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)

版权声明:本文为weixin_42645653原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。