makefile的介绍与使用
1. 简单示例:
1.1. 代码结构:
main.cpp
#include <iostream>
#include "functions.h"using namespace std;int main()
{printhello();cout << "here is main" << endl;cout << "factorial 5 is : " << factorial(5) << endl;return 0;
}
functions.h
#pragma once
// 输出hello
void printhello();
// 获得阶乘
int factorial(int n);
printhello.cpp
#include <iostream>
#include "functions.h"using namespace std;void printhello()
{int i = 1;cout << "hello!" << endl;
}
factorial.cpp
#include "functions.h"
int factorial(int n)
{if (n == 1)return 1;elsereturn n * factorial(n - 1);
}
1.2. 最简单的编译命令:
g++ main.cpp printhello.cpp factorial.cpp -o main
1.3. 与最简单的编译命令相同的Makefile
:
main: main.cpp printhello.cpp factorial.cppg++ -o main main.cpp printhello.cpp factorial.cpp
1.4. 普通版本的Makefile
版本:
所有文件名称都手写,且每次都写两个过程:
- 可执行文件的编译命令
- 每一个链接文件的生成命令
## 指定编译器版本
CXX = g++
## 目标文件,即最后的可执行文件名称
TARGET = main
## 目标文件的依赖文件变量定义
OBJ = main.o printhello.o factorial.o
## 目标文件的依赖文件关系
## 如果需要重新生成依赖文件,那么需要执行的命令
$(TARGET): $(OBJ)$(CXX) $(OBJ) -o $(TARGET)
## 每一个依赖文件的依赖文件关系和重新生成需要执行的命令
main.o: main.cpp$(CXX) -c main.cpp -o main.o
printhello.o: printhello.cpp$(CXX) -c printhello.cpp -o printhello.o
factorial.o: factorial.cpp$(CXX) -c factorial.cpp -o factorial.o
好处:每次只会重新编译修改了的那一个文件,而不像1.3
一样每次都需要将项目的每一个文件都重新编译一次。极大的提高了编译效率。
1.5. 优化1.4链接文件生成的Makefile
版本:
优化内容:
又少了一些需要固定写死的内容。
比如之前的链接文件每一个都需要写出来,现在只需要一行就能写出来了
## 指定编译器版本
CXX = g++
## 目标文件,即最后的可执行文件名称
TARGET = main
## 目标文件的依赖文件变量定义
OBJ = main.o printhello.o factorial.o
## 编译命令的可配参数
CXXFLAGS = -c -Wall
## 目标文件的依赖文件关系
## 如果需要重新生成依赖文件,那么需要执行的命令
$(TARGET): $(OBJ)$(CXX) $^ -o $@
## 每一个依赖文件的依赖文件关系和重新生成需要执行的命令
%.o: %.cpp$(CXX) $(CXXFLAGS) $< -o $@
## 如果当前目录下不存在一个名称为clean可执行文件,那么给make添加一个clean命令
.PHONY: clean
# clean命令的内容
clean:rm -f *.o $(TARGET)
参数解释:
- $^ 是冒号后面的内容,即 $(OBJ)
- $@ 是冒号前面的内容,即 $(TARGET)
- $< 是冒号后面的后面的内容,即 %.cpp
- $^ 与 < 的区别是: < 的区别是: <的区别是:^ 取的是字符串整个文本,而 $< 取的是字符串内的第一个元素。比如字符串为
main.cpp hello.cpp func.cpp
,那么使用 $^ 取的就是:main.cpp hello.cpp func.cpp
,而使用 $< 取的就是main.cpp
- % :通配符
- %.o : 当前代码【Makefile】中每一个以
.o
结尾的变量名称 - %.cpp : 取前面
.o
的名称
- %.o : 当前代码【Makefile】中每一个以
- 但是 % 会将每一个变量分别执行一次,而不是说 % 是将所有变量都填写的这里。
- .PHONY:判断当前文件的目录下是否存在该可执行文件
1.6. 最灵活的Makefile
版本
## 指定编译器版本
CXX = g++
## 目标文件,即最后的可执行文件名称
TARGET = main
## 获取当前文件夹下的所有*.cpp文件名称
SRC = $(wildcard *.cpp)
## 将所有*.cpp名称替换为*.o
## 目标文件的依赖文件变量定义
OBJ = $(patsubst %.cpp, %.o, $(SRC))
## 编译命令的可配参数
CXXFLAGS = -c -Wall
## 目标文件的依赖文件关系
## 如果需要重新生成依赖文件,那么需要执行的命令
$(TARGET): $(OBJ)$(CXX) $^ -o $@
## 每一个依赖文件的依赖文件关系和重新生成需要执行的命令
%.o: %.cpp$(CXX) $(CXXFLAGS) $< -o $@
## 如果当前目录下不存在一个名称为clean可执行文件,那么给make添加一个clean命令
.PHONY: clean
# clean命令的内容
clean:rm -f *.o $(TARGET)
- wildcard:获取当前目录下的内容
- patsubst:进行替换操作
2. 问题解决:
2.1. Makefile: *** missing separator. Stop.
原因:Vim可能默认会将 Tab 换成 空格
解决办法:
- 修改 /etc/vimrc
- 添加一行内容:
set noexpandtab
- 然后重新回到Makefile文件中,删除空格重新打Tab即可。
3. 借鉴文章:
Makefile 20分钟入门,简简单单,展示如何使用Makefile管理和编译C++代码_哔哩哔哩_bilibili
3W字讲解:详细讲解