004 【编译神器】Makefile:最常用编译方法详解
一种常用于编译的脚本语言,可以更好更方便地管理项目的代码编译。在文件中定义一系列的规则来指定哪些文件先编译,一旦写好后,只需要一个 make 命令就可以编译
一、Makefile 三要素
- 一般在 makefile 文件中会使用
gcc
命令对源代码进行编译 - makefile 的编写规则:
目标:依赖命令
// 目标:要生成的目标文件
// 依赖:目标文件由哪些文件生成
// 命令:执行该命令由依赖文件生成目标
二、Makefile 工作原理
Makefile 的 工作原理 可以概括为两步:
- 检查依赖是否 存在
- 检查是否需要 更新
- 可以用以下伪代码来进行表述
// 检查依赖是否存在
if(依赖存在)//执行规则中的命令生成依赖文件的目标文件if(目标的修改时间 > 依赖的修改时间)不更新目标文件else更新目标文件
else向下搜索,查找是否有生成该依赖文件的规则if(没有规则能生成该依赖文件)ERROR!
三、Makefile 基本使用方法
我们以经典的 TCP 通讯为例来说明,当我们编写了 TCP 的服务端和客户端两个程序后,要进行编译的话需要运行两次 gcc
指令,比较繁琐,尤其是当我们项目工程具有更多的源文件时,使用 makefile 实现一键编译,将非常的方便。
- makefile编写,生成 TCP_Server、TCP_Client 两个可执行文件。
all:TCP_Server TCP_Client
TCP_Server:tcp_server.cgcc tcp_server.c -o TCP_Server
TCP_Client:tcp_client.cgcc tcp_client.c -o TCP_Client
编写完成后,我们只需在当前目录下运行
make all
即可同时编译生成 TCP_Server、TCP_Client 两个目标文件。具体的 Makefile 变量、指令、参数将在下文展开。
- 运行可执行文件(以 TCP 程序为例)
./TCP_Server 192.168.0.109 1234
./TCP_Client 192.168.0.109 1234
四、Makefile 变量
1、变量类型
- 自定义变量
var = abc // 变量定义
- 系统变量
CC = gcc-3.4 // 指定编译器的类型
CPPFLAGS = -I./ // 指定预处理的选项
CFLAGS = -Wall -O2 // 指定编译器选项(例如调试信息、优化等)
LDFLAGS = -L // 指定链接器选项
SRC_FILES = main.c foo.c bar.c // 指定源文件列表
- 自动变量
$@ 表示规则中的目标
$^ 表示规则中的所有条件, 组成一个列表, 以空格隔开, 如果这个列表中有重复的项则消除重复项
$< 表示规则中的第一个条件
$? 第一变化的依赖
2、赋值方式
makefile 的赋值方式有简单赋值、递归赋值、条件赋值、追加赋值。本文只介绍最为常用的赋值方式,即递归赋值 =
。(其余赋值方式可参考文章:Makefile基础教程(变量的介绍和使用)_makefile 使用变量 编译-CSDN博客)
x = abc // 定义变量并赋值
bar = $(x) // 使用变量, $(变量名)
五、Makefile 工程应用
1、编译操作
makefile 在工程上的应用示例如下:
- 编译 main.c 、 fun1.c 、 func2.c 生成对应的目标文件
main.o、func1.o、func2.0
- 链接所有的目标文件,生成最后的可执行文件
- 基础写法
CC := gcc
TARGET := hello# 生成可执行文件
TARGET : hello.o func.o$(CC) -o TARGET hello.o func.o# 生成目标文件
hello.o : hello.c$(CC) -c -o hello.o hello.c
func.o : func.c$(CC) -c -o func.o func.call : TARGET
gcc 编译器参数大全可参考:20个最常用的GCC编译器参数
常用参数:
-o 指定输出文件名
-c 只编译源代码,生成目标文件,但不会生成最终可执行文件
- 进阶写法
TARGET = hello
object = hello.o func1.o func2.o
CC = gcc
CFLAGS = -Wall -O2TARGET : $(object)$(CC) -o $@ $^ # 规则中的目标 规则中的依赖%.o : %.c $(CC) -o $@ -c $< # 规则中的目标 规则中的第一个条件
2、清理操作
清除目标文件,在 makefile 中使用 rm 指令删除文件,然后在终端中输入 make clean= 清除生成的目标文件
- 基础写法
# 清除所有 .o 后缀文件及可执行文件
clean :rm *.o TARGET
参数:
rm -f
强制执行删除操作。
- 进阶写法
clean: -rm -f $(target) $(object)