【Makefile】学习笔记 (1) 基本使用

文章介绍了Makefile的基本使用,包括如何编译多个cpp文件,以及如何通过编写简单的Makefile脚本来自动化编译过程。进一步讨论了Makefile的优化版本,如使用变量简化规则。接着,文章转向Cmake,解释了Cmake作为跨平台构建系统的优点,以及Cmake的基本工作流程,包括编写CMakeLists.txt文件和执行cmake命令。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Makefile和Cmake入门


B站于仕琪老师课程 + 动手实操 学习记录

Makefile入门

两个函数的实现在两个不同的 Cpp 文件中: factorial.cpp 和 printhello.cpp。
两个函数在头文件 functions.h 中进行声明。
主函数中调用这两个函数,使用 #include 包含头文件 functions.h 。

这个时候有三个 cpp 文件,一个.h文件,如何执行进行编译呢?

最直接的方法是使用如下编译指令

g++ main.cpp  factorial.cpp printhello.cpp -o main.out

编译成功,并且可以运行,如下:
在这里插入图片描述
这里的 .cpp 文件只有三个,直接这样编译没有问题,但是如果有很多文件,这样一起编译的话会花费很多时间,这个时候可以选择逐个编译,如下:
在这里插入图片描述
g++ mian.cpp -c的意思是只编译,不链接。 编译完成之后,可以看到生成了很多 .o 文件。
将这些 .o 文件链接在一起生成可执行文件就可以执行程序了,如下:
在这里插入图片描述
为了减少编译时间,选择了逐个编译。如果修改了某个 cpp文件,只需要对修改的cpp文件执行编译即可,没有修改的文件不需要再次编译。

但是问题是,如果文件非常多,每次手动输入这些命令也是很麻烦的。

这个时候,可以把这些编译命令写到脚本文件中,这个脚本文件有一个固定的格式,那就是makefile。
makefile 会很多写法,最简单makefile的写法如下:

## VERSION 1
hello: main.cpp printhello.cpp factorial.cpp
	g++ -o hello main.cpp printhello.cpp factorial.cpp

说明:

  1. #开头表示注释
  2. 第一行 hello 是目标(可执行文件),目标的生成依赖于main.cpp printhello.cpp factorical.cpp三个文件
  3. 第二行 g++ -o 前面必须要有一个 tab 符号,不能是空格,表示使用 g++ -o命令生成目标 hello。第一行只是说了目标文件的生成依赖什么,但是没有说怎么生成,这里就是说基于所以依赖的文件,以何种方式生成目标文件。

写完 makefile 文件之后,直接使用make命令,该命令会自动寻找当前文件夹下面的makefile(或Makefile)文件。如果makefile文件的名字不是不是makefile,可以使用 -f 选项指定文件名称,如下所示:

	make -f filename

使用make命令后,makefile文件的执行过程如下,
在这里插入图片描述
执行过程中,寻找目标文件hello,如果没有就生成该文件。
执行完成之后,在没有任何改动的情况下个,再次执行make,会出现make: "hello" is up to date
也就是说,执行一次make之后,目标文件hello所以依赖的三个 cpp文件没有修改的话,再次使用make是不会重新编译的。具体的实现是根据文件的保存时间判断的,只有hello的保存时间早于三个cpp文件的任意一个,使用make命令才会再次编译全部文件。

这个makefile文件的问题同样也是,文件很多的时候,编译时间很长,只修改其中一个文件之后,使用make命令,会编译全部文件。

所以给出第二个版本的makefile文件,如下:

## VERSION 2
# 定义变量
CXX = g++
TARGET = hello
OBJ = main.o printhello.o factorial.o
# 编译过程 (和version1一样,只是这里使用变量表示)
$(TARGET) : $(OBJ)
	$(CXX) -o $(TARGET) $(OBJ)
main.o: main.cpp
	$(CXX) -c main.cpp
printhello.o: printhello.cpp
	$(CXX) -c printhello.cpp
factorial.o: factorial.cpp
	$(CXX) -c factorial.cpp

使用make命令后,执行过程如下:
在这里插入图片描述
执行过程中,从头开始,先去找第一个TARGET文件,没有就要生成,因为生成该文件依赖OBJ文件,所以这个时候就要去往下找OBJ对应的文件,找到之后,在回头生成TARGET文件。

这个时候,如果修改了cpp文件,再次编译的时候,只会对修改过的cpp文件进行编译, 而不是全部编译。
如下,只有main.cpp的时间戳是晚于hello文件的,所以只对main.cpp再次编译,然后链接形成hello文件。
在这里插入图片描述
这个版本的makefile已经是可以用的,当然还有更好的写法,如下:

## VERSION 3
CXX = g++
TARGET = hello
OBJ = main.o printhello.o factorial.o

CXXFLAGS = -c -Wall # 编译选项,-Wall的的意思是报出所有的warning

$(TARGET) : $(OBJ)
	$(CXX) -o $@ $^ # $@代表$(TARGET),$^代表$(OBJ)
	
%.o: %.cpp
	$(CXX) $(CXXFLAGS) $< -o $@
	# %表示通配符  $< 表示所依赖的文件的第一个($^是全部)
.PHONY: clean
clean:
	rm -f *.o $(TARGET)
# clean是一个新的目标,不需要依赖对象,执行rm -f命令,强制删除所有.o 文件和 $(TARGET) 文件

使用make clean会在目标文件中寻找clean目标,然后执行,如下:
在这里插入图片描述
.PHONY: clean的作用:
因为clean文件(目标文件)的生成不依赖于任何文件(冒号后面的内容为空),所以如果当前文件夹中已经存在一个 clean 文件的话,就会出现make: 'clean' is up to date,clean文件存在,不需要生成。
.PHONY: clean表示有一个不存在的目标依赖clean,这个时候就会启动 clean 的生成。此时,就算有clean的情况下,make clean命令也能够正常执行预定的功能。
(phony的意思是:伪造的,假的)

version3 的makefile文件执行过程如下:
在这里插入图片描述
因为编译选项中添加了 -Wall ,所以报出了程序中所有的warning。

虽然v3已经很不错了,但是还是在OBJ的地方还是出现了文件名,文件很多的情况下,还是要自己一个个去写,所以最后一个版本的makefile写法,如下。

## VERSION 4
CXX = g++
TARGET = hello
SRC = $(wildcard *.cpp) # 当前目录下的所有CPP文件都放到变量SRC中
OBJ = $(patsubst %.cpp, %.o, $(SRC)) # 替换 把所有的.cpp替换为 .o

CXXFLAGS = -c -Wall

$(TARGET) : $(OBJ)
	$(CXX) -o $@ $^
	
%.o: %.cpp
	$(CXX) $(CXXFLAGS) $< -o $@
	
.PHONY: clean
clean:
	rm -f *.o $(TARGET)

Cmake的使用过程

makefile的配置是和和当前的系统强相关的,编译器、路径等都是不同的。如果编写一个跨平台的内容,是很不方便的。
Cmake就可以很好的解决这个问题。

Cmake基本过程:

  1. 编写一个Cmake文件,文件名称为CMakeLists.txt
  2. 执行cmake . 命令
  3. 执行make命令

CMakeLists.txt文件内容如下:
在这里插入图片描述

具体执行过程如下:
在这里插入图片描述
这个时候的一个问题是,使用cmake生成了很多文件,如果如果想要删除这些文件,需要一个一个的删除,很麻烦,这个时候是可以建一个文件夹的,这个文件夹的名称一般为build
在这里插入图片描述
需要注意的时候,这个时候使用的是cmake ..。原因如下:
.是当前目录,..是上一级目录,cmake其实就是在一个指定的目录下面寻找CMakeLists.txt文件,我们这里是进入了build文件夹,而CMakeLists.txt是在上一级文件夹,所以需要使用cmake ..
这个时候,如果想要删除,直接删除build文件夹就可以了。(删除文件夹的命令:rm -rf build/)
(这也是为什么好多工具有一个build文件夹的原因。)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值