Makefile简介及如何指定头文件和库文件
原创
已于 2023-05-28 12:58:06 修改
·
1.3w 阅读
·
8
·
54
·
CC 4.0 BY-SA版权
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
文章标签:
#ubuntu
#linux
#运维
于 2023-02-19 13:23:34 首次发布
开发语言
同时被 2 个专栏收录
63 篇文章
订阅专栏
cmake
8 篇文章
订阅专栏
Makefile是用于自动化编译的工具,当C/C++项目源文件较多时,通过编写Makefile预设编译规则,执行make命令即可批量编译。相对于直接使用命令行,Makefile节省时间,尤其是大型项目。文章介绍了Makefile的基本语法,包括目标、依赖和规则命令,并通过不同版本的示例展示其使用,包括如何简化Makefile、处理源文件变化以及添加新文件的情况。此外,还提及了cmake作为更现代的替代选择。
Makefile简介
初学C/C++时,我们编译源文件时,通常直接敲命令去进行编译。但在实际项目中,源文件非常多,直接敲命令编译就不现实了,这时候就需要用到Makefile,Makefile是一个文本文件,我们只需要提前在Makefile中写好源文件的编译规则,然后直接执行make命令,就可以自动编译。现在使用cmake的比较多,cmake相比于makefile,语法更加简洁,学习门槛更低,实现相同的编译功能,Makefile可能需要几十行脚本来完成,cmake只需要几行脚本就可以搞定。但鉴于可能有些老项目或者部分公司依旧使用makefile,所以我们还是得对此熟悉。
Makefile语法
Makefile有三大要素:目标、依赖、规则命令
目标:我们要生成的最终文件依赖:生成目标文件要依赖的文件规则命令:要生成目标文件所执行的命令
语法
目标:依赖
规则命令
注:规则命令前面必须要有Tab键
简单使用
目前有以下源文件
├── main.cpp
├── Makefile
├── myadd.cpp
├── myadd.h
├── myminus.cpp
└── myminus.h
版本一
# 生成最终可执行文件的文件名和需要的依赖文件
res:main.o myadd.o myminus.o
# 生成最终可执行文件需要执行的命令
g++ main.o myadd.o myminus.o -o res
# 生成中间文件main.o需要的源文件
main.o:main.cpp
# 生成中间文件main.o需要执行的命令
g++ -c main.cpp
myadd.o:myadd.cpp
g++ -c myadd.cpp
myminus.o:myminus.cpp
g++ -c myminus.cpp
# 执行清理操作
clean:
rm *.o
直接执行make命令就可以生成最终可执行文件res
root@ubuntu:/home/lng/makefiledir# make
g++ -c main.cpp
g++ -c myadd.cpp
g++ -c myminus.cpp
g++ main.o myadd.o myminus.o -o res
root@ubuntu:/home/lng/makefiledir# ls
main.cpp main.o Makefile myadd.cpp myadd.h myadd.o myminus.cpp myminus.h myminus.o res
root@ubuntu:/home/lng/makefiledir#
一般*.o文件是不需要的,可以执行make clean命令来清除*.o文件
root@ubuntu:/home/lng/makefiledir# make clean
rm *.o
root@ubuntu:/home/lng/makefiledir# ls
main.cpp Makefile myadd.cpp myadd.h myminus.cpp myminus.h res
root@ubuntu:/home/lng/makefiledir#
备注
可能有人会有疑问,为什么一定要生成中间文件*.o, 我们平常使用命令编译时,都是直接用源文件生成可执行文件。当然可以不用生成*.o文件,使用以下命令,直接用源文件生成可执行文件
res:main.cpp myadd.cpp myminus.cpp
g++ main.cpp myadd.cpp myminus.cpp -o res
但这样就会存在一个问题,一旦我们修改任何一个源文件,所有的文件都重新编译。在一些大型项目中,编译是非常耗时的,每修改一个源文件,都去重新编译,这将大大降低工作效率。因此我们通常都是先生成中间文件*.o, 当我们修改其中一个源文件时,只需要编译修改的文件,其他文件都不需要重新编译。
版本二
在makefile中允许我们使用变量,可以对Makefile内容进行简化
# 对变量赋值
Target=res
Objs=main.o myadd.o myminus.o
Src=main.cpp myadd.cpp myminus.cpp
# 取出变量中的值
$(Target):$(Objs)
g++ $(Objs) -o $(Target)
$(Objs):$(Src)
g++ -c $(Src)
clean:
rm *.o
这个版本的Makefile与版本一的效果完全一样,但代码量更少,也更简洁。
版本三
目前还存在一个问题,就是我们一旦要添加新的源文件,必须修改Makefile,有没有一种写法,在添加源文件时不需要修改Makefile?当然也有。首先需要介绍两个函数和四个变量函数
wildcard:进行文件匹配patsubst:内容替换
变量
$@ : 目标$^ : 全部依赖$< : 第一个依赖$? : 第一个变化的
Target=res
# 获取当前目录下所有源文件
Src:=$(wildcard *.cpp)
# 将所有*.cpp文件替换为*.o文件。Objs变量中就保存了所有*.o文件名
Objs:=$(patsubst %.cpp,%.o, $(Src))
$(Target):$(Objs)
g++ $(Objs) -o $(Target)
*.o:*.cpp
# 相当于 g++ -c *.cpp -o *.o
g++ -c $< -o $@
clean:
rm *.o
通过变量和函数我们就可以自动获取文件名,不用再去手动进行赋值。
指定头文件和库文件
目录结构如下
├── include
│ ├── myadd.h
│ ├── myminus.h
│ └── mymult.h
├── lib
│ └── libmymult.so
├── Makefile
└── src
├── main.cpp
├── myadd.cpp
└── myminus.cpp
Makefile
# 指定编译器
CC=g++
#指定编译选项
CFLAGS=-Wall -g
Target=res
Src:=$(wildcard ./src/*.cpp)
Objs:=$(patsubst %.cpp,%.o, $(Src))
#指定头文件位置
INCLUDES = -I ./include
#指定库文件
LIBS=-L ./lib -lmymult
$(Target):$(Objs)
echo $(Objs)
@mkdir -p output
$(CC) $(Objs) $(LIBS) -o output/$(Target)
%.o:%.cpp
$(CC) $(INCLUDES) $(CFLAGS) -c $< -o $@
clean:
rm $(Objs)