Linux 如何编写makefile详解

文章目录

  • 前言
  • 一、makefile三要素
  • 二、写法
    • 1.makefile 1.1版本
    • 2.makefile 1.2版本
    • 3.makefile 1.3版本
    • 4.makefile 最终版本
  • 总结


前言

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 
#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 文件得到目标结果,分解图如下:
Linux 如何编写makefile详解_第1张图片
根据分解图描述,修改 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 

你可能感兴趣的:(Linux,C/C++,gcc,c++,makefile)