Linux 如何编写makefile详解

  • Post author:
  • Post category:linux





前言


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 



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