前言
Linux c/c++ 开发少不了编写 makefile 文件,一次编写,终身受益,哈哈!另外,gcc编译基础知识可参考我这篇博客:Linux C gcc编译基础知识详解
一、makefile三要素
目标:最终想要得到的
依赖:通过什么文件去生成
规则命令:怎么去生成
二、写法
先贴上我整个工程环境,如下所示:
ls
include src
[lzy@localhost Calc]$ ls include lib src
include:
head.h
lib:
src:
add.c main.c sub.c
[lzy@localhost Calc]$ cat include/*.h src/*.c
#ifndef __HEAD_H__
#define __HEAD_H__
int add(int, int);
int sub(int, int);
#endif
#include "head.h"
int add(int iNum1, int iNum2)
{
int iResult = iNum1 + iNum2;
return iResult;
}
#include <stdio.h>
#include "head.h"
int main(void)
{
int iSum = add(6, 3);
int iSub = sub(6, 3);
printf("Sum = %d\n", iSum);
printf("Sub = %d\n", iSub);
return 0;
}
#include "head.h"
int sub(int iNum1, int iNum2)
{
int iResult = iNum1 - iNum2;
return iResult;
}
makefile写法如下:
目标:依赖
tab键 规则命名
先在 src 目录下创建一个 makefile 空文件,填入如下内容:
[lzy@localhost src]$ ls
add.c main.c makefile sub.c
[lzy@localhost src]$ cat makefile
app:*.c
gcc -o app -I ../include *.c
[lzy@localhost src]$ make
gcc -o app -I ../include *.c
[lzy@localhost src]$ ls
add.c app main.c makefile sub.c
[lzy@localhost src]$ ./app
Sum = 9
Sub = 3
就这?这不就把 gcc 那条命令放在里面吗?太简单了把!别急,一步一步来
1.makefile 1.1版本
接上,修改其中一个文件,再执行一下 makefile,如下:
[lzy@localhost src]$ ls
add.c app main.c makefile sub.c
[lzy@localhost src]$ make
make: “app”是最新的。
[lzy@localhost src]$ touch add.c
[lzy@localhost src]$ make
gcc -o app -I ../include *.c
[lzy@localhost src]$
发现,如果更改其中一个文件,所有源码都重新编译,这可不太好;因此,可以考虑编译过程分解,先生成 .o 文件,然后使用 .o 文件得到目标结果,分解图如下:
根据分解图描述,修改 makefile 文件如下:
[lzy@localhost src]$ cat makefile
app:main.o add.o sub.o
gcc -o app -I ../include main.o add.o sub.o
main.o:main.c
gcc -c main.c -I ../include
add.o:add.c
gcc -c add.c -I ../include
sub.o:sub.c
gcc -c sub.c -I ../include
[lzy@localhost src]$ make
gcc -c main.c -I ../include
gcc -c add.c -I ../include
gcc -c sub.c -I ../include
gcc -o app -I ../include main.o add.o sub.o
[lzy@localhost src]$ ls
add.c add.o app main.c main.o makefile sub.c sub.o
[lzy@localhost src]$ ./app
Sum = 9
Sub = 3
[lzy@localhost src]$ touch add.c
[lzy@localhost src]$ make
gcc -c add.c -I ../include
gcc -o app -I ../include main.o add.o sub.o
虽说解决了编译前面说的那个问题,但是变复杂了,如果工程文件很多的话,那得写长啊;别急,往下走
2.makefile 1.2版本
makefile 可以定义和使用变量,同时还可以使用函数,可以用来代替很多重复的工作;
函数:
wildcard:可以进行文件匹配
patsubst:内容替换
makefile变量:
$@ :代表目标
$^ :代表全部依赖
$< :第一个依赖
$? :第一个变化的依赖
makefile修改如下:
[lzy@localhost src]$ cat makefile
# SrcFiles 定义资源文件
# get all *.c files
SrcFiles=$(wildcard *.c)
# ObjFiles 定义目标文件
# all *.c files --> *.o files
ObjFiles=$(patsubst %.c,%.o,$(SrcFiles)) # %是通配符
# 变量用法 $(var)
app:$(ObjFiles)
gcc -o app -I ../include $(ObjFiles)
# app查找的时候依赖某个*.o,转换的时候发现没有,
# 所以去找对应的生成规则,但只有%.o:%c,%是通配符,
# 所以app将依赖的main.o、add.o和sub.o分别进行匹配,
# 而main.o、add.o和sub.o的依赖分别匹配成main.c、add.c和sub.c
%.o:%.c
# 模式匹配规则,%@、$<等变量,只能在规则中出现,
# 我这边一个*.o依赖一个*.c,所以$<
gcc -c $< -I ../include
# 这是一个目标测试,依赖和规则可以不写,唯独不能没有目标
# 因为makefile隐含规则,故测试需要指定目标:make test
test:
echo $(SrcFiles)
echo $(ObjFiles)
[lzy@localhost src]$ make
gcc -c main.c -I ../include
gcc -c add.c -I ../include
gcc -c sub.c -I ../include
gcc -o app -I ../include main.o add.o sub.o
[lzy@localhost src]$ ./app
Sum = 9
Sub = 3
[lzy@localhost src]$ make test
echo main.c add.c sub.c
main.c add.c sub.c
echo main.o add.o sub.o
main.o add.o sub.o
补充说明:有没有发现,makefile里面定义了很多个目标,但只会会去处理 app,为什么呢?这是因为 makefile 存在一个隐含规则,默认处理第一个目标。
哈哈,现在看起来是不是牛逼一点了,还没完呢,继续完善
3.makefile 1.3版本
接上,继续完善makefile,方便清理工程,增加一个清理功能,用到了两个小功能:
@ 在规则前,代表不输出该条规则的命令
- 在规则前,代表该条规则报错,仍然继续执行
优化后的makefile如下:
[lzy@localhost src]$ cat makefile
....省略
test:
# 不想输出命令时,前面加@
@echo $(SrcFiles)
@echo $(ObjFiles)
clean:
# 前面加-号,当该条指令报错的时候,仍继续执行
-@rm -f *.o
-@rm -f app
[lzy@localhost src]$ ls
add.c add.o app main.c main.o makefile sub.c sub.o
[lzy@localhost src]$ make test
# 不想输出命令时,前面加@
main.c add.c sub.c
main.o add.o sub.o
[lzy@localhost src]$ make clean
[lzy@localhost src]$
应该差不多了吧?哈哈,不急,先做个小测试,如下:
[lzy@localhost src]$ ls
add.c main.c makefile sub.c
[lzy@localhost src]$ touch clean
[lzy@localhost src]$ ls
add.c clean main.c makefile sub.c
[lzy@localhost src]$ make clean
make: “clean”是最新的。
纳尼?我是要清理,不是要生成一个clean目标;怎么办,接着优化呗,如下:
[lzy@localhost src]$ cat makefile
....省略
#定义伪目标,防止有歧义
#指定clean是一个伪目标,不是想要得到的目标,只是makefile里面有的
.PHONY:clean
clean:
# 前面加-号,当该条指令报错的时候,仍继续执行
-@rm -f *.o
-@rm -f app
[lzy@localhost src]$ make clean
[lzy@localhost src]$
4.makefile 最终版本
想不到吧,还没到头😱,对于简单工程,前面几个版本已经完全够用了,但是如果工程比较大,可能需要生成多个目标,附上最终makefile:
[lzy@localhost src]$ cat makefile
# SrcFiles 定义资源文件
# get all *.c files
SrcFiles=$(wildcard *.c)
# ObjFiles 定义目标文件
# all *.c files --> *.o files
ObjFiles=$(patsubst %.c,%.o,$(SrcFiles)) # %是通配符
# all是一个伪目标
all:app app1
# 变量用法 $(var)
app:$(ObjFiles)
# $@代表目标,代替app
gcc -o $@ -I ../include $(ObjFiles)
app1:$(ObjFiles)
gcc -o $@ -I ../include $(ObjFiles)
# app查找的时候依赖某个*.o,转换的时候发现没有,
# 所以去找对应的生成规则,但只有%.o:%c,%是通配符,
# 所以app将依赖的main.o、add.o和sub.o分别进行匹配,
# 而main.o、add.o和sub.o的依赖分别匹配成main.c、add.c和sub.c
%.o:%.c
# 模式匹配规则,%@、$<等变量,只能在规则中出现,
# 我这边一个*.o依赖一个*.c,所以$<
gcc -c $< -I ../include
# 这是一个目标测试,依赖和规则可以不写,唯独不能没有目标
# 因为makefile隐含规则,故测试需要指定目标:make test
test:
# 不想输出命令时,前面加@
@echo $(SrcFiles)
@echo $(ObjFiles)
#定义伪目标,防止有歧义
#指定clean是一个伪目标,不是想要得到的目标,只是makefile里面有的
.PHONY:clean
clean:
# 前面加-号,当该条指令报错的时候,仍继续执行
-@rm -f *.o
-@rm -f app app1
[lzy@localhost src]$ make clean
[lzy@localhost src]$ ls
add.c main.c makefile sub.c
[lzy@localhost src]$ make
gcc -c main.c -I ../include
gcc -c add.c -I ../include
gcc -c sub.c -I ../include
gcc -o app -I ../include main.o add.o sub.o
gcc -o app1 -I ../include main.o add.o sub.o
[lzy@localhost src]$ ls
add.c add.o app app1 main.c main.o makefile sub.c sub.o
[lzy@localhost src]$ ./app
Sum = 9
Sub = 3
[lzy@localhost src]$ ./app1
Sum = 9
Sub = 3
总结
以上就是今天要讲的内容,写的有点啰嗦了,能坚持下来看到这里的都是勇士,来,敬勇士😄;最后,补充一下如何指定makefile文件进行make,如下:
[lzy@localhost src]$ ls
add.c main.c makefile sub.c
[lzy@localhost src]$ cp makefile makefile1
[lzy@localhost src]$ ls
add.c main.c makefile makefile1 sub.c
[lzy@localhost src]$ make -f makefile1
gcc -c main.c -I ../include
gcc -c add.c -I ../include
gcc -c sub.c -I ../include
gcc -o app -I ../include main.o add.o sub.o
gcc -o app1 -I ../include main.o add.o sub.o