前言
这里记录的是Makefile的编写,而不是CMakeLists,先学Makefile,然后再去看CMakeLists,或者直接去看CMakeLists。
1. 代码编译基本流程
1、预处理
宏定义展开、引入头文件、去除注释
2、汇编
生成汇编代码
3、编译
生成二进制文件
4、链接
生成可执行文件
gcc -* hello.c -o hello.*
-E :预处理生成.i gcc -E hello.c -o hello.i
-S :汇编生成.s gcc -S hello.i -o hello.S
-c :编译生成.o gcc -c hello.S -o hello.o
其中 -o :指定生成文件名
gcc hello.o -o hello.exe
很多文件时,可以一个一个去编译,且控制生成的文件类型(动态库or动态库或exe),但很麻烦。所以需要Makefile
2. Makefile 5层心法
2.1 显式规则
# '#'号是注释
1. 语法格式:
目标文件:依赖文件
(TAB)指令
hello.i:hello.c
gcc -E hello.c -o hello.i
hello.S:hello.i
gcc -s hello.i -o hello.S
hello.o:hello.S
gcc -c hello.S -o hello.o
hello.exe:hello.o
gcc hello.o -o hello.exe
2. 目标文件
#第一个目标文件是Makefile的最终目标,所以要放最前
hello.exe:hello.o
gcc hello.o -o hello.exe
hello.o:hello.S
gcc -c hello.S -o hello.o
hello.S:hello.i
gcc -s hello.i -o hello.S
hello.i:hello.c
gcc -E hello.c -o hello.i
上面的Makefile经过一次编译后hello.o等文件已经存在,当修改hello.c后想再次编译,则无法得到想要的结果,因为hello.o已经存在,所以需要伪目标 .PHONY
3. 伪目标 .PHONY:
hello.exe:hello.o
gcc hello.o -o hello.exe
hello.o:hello.S
gcc -c hello.S -o hello.o
hello.S:hello.i
gcc -s hello.i -o hello.S
hello.i:hello.c
gcc -E hello.c -o hello.i
.PHONY:
#删除过程文件
clear:
rm -rf hello.i hello.S hello.o
#删除所有文件
clearall:
rm -rf hello.i hello.S hello.o hello
写了上面的伪目标后,经过一次编译后,可以执行make clear清除中间文件.
make clear
make clearall
但有时我们会有许多文件,许多.cpp,需要怎样编译呢?
下面是一个可行的方法,但看起来还是较为混乱。
2.2 变量
= (替换赋值)
+= (追加)
:=(常量、立即展开赋值)
?= (询问赋值,若之前赋值过,则不会取后面的值)
TARGET = test.exe #下面所有test.exe都可以用TARGET替换
CC :=gcc
$(TARGET):circle.o cube.o main.o
$(CC) circle.o cube.o main.o -o $(TARGET)
cube.o:cube.c
$(CC) -c cube.c -o cube.o
circle.o:circle.c
$(CC) -c circle.c -o circle.o
main.o:main.c
$(CC) -c main.c -o main.o
.PHONY:
clear:
rm -rf circle.o cube.o main.o
更进一步的,我们可以使用Obj = .o
TARGET = test.exe #下面所有test.exe都可以用TARGET替换
CC :=gcc
Obj = main.o cube.o circle.o
$(TARGET):$(Obj)
$(CC) $(Obj) -o $(TARGET)
cube.o:cube.c
$(CC) -c cube.c -o cube.o
circle.o:circle.c
$(CC) -c circle.c -o circle.o
main.o:main.c
$(CC) -c main.c -o main.o
.PHONY:
clear:
rm -rf $(Obj)
2.3 隐含规则
上面编译各个.c
文件时,仍要一个一个写,费时费力,所以需要一种新的替换方式来避免每个都写一遍。
%.c 指任意的.c文件
%.o 指任意的.o文件
*.o 所有的.o
TARGET = test.exe #下面所有test.exe都可以用TARGET替换
CC :=gcc
Obj = main.o cube.o circle.o
$(TARGET):$(Obj)
$(CC) $(Obj) -o $(TARGET)
%.o:%.c
$(CC) -c %.c -o %.o
.PHONY:
clear:
rm -rf $(Obj)
2.4 通配符
使用通配符来表示一些类似于快捷键的东西
- $^ 当前依赖文件
- $@ 当前目标文件
- $< 当前所有依赖文件的第一个文件
- $$ 当前进程编号
在C语言中,只有.c文件才会参与编译