32-Makefile 工程管理 与 make

32-Makefile 工程管理 与 make

一、工程管理器 make

make 是一个工程管理器,它根据 makefile 文件中的指令来编译和链接程序。makefile 文件定义了如何编译和链接程序,以及如何生成目标文件和可执行文件

当我们需要编译一个比较大的项目时,编译命令会变得越来越复杂,需要编译的文件越来越多。其次就是项目中并不是每一次编译都需要把所有文件都重新编译,比如没有被修改过的文件则不需要重新编译。工程管理器就帮助我们来优化这两个问题。
MakeFile就类似于make工程管理的工作的脚本。用来告诉工程管理器如何正确的编译我们的程序。

二、Makefile 和 Make 的详细讲解

1.1 Make 和 Makefile 简介

make 是一个用于自动化构建工程的工具,可以根据 Makefile 中定义的规则自动完成编译、链接等操作。Makefile 则是 make 工具使用的脚本文件,定义了目标(target)、依赖(dependency)和命令(command)的关系。

1.2 Makefile 基本语法

目标: 依赖
[TAB]命令
  • 目标:要生成的文件或执行的任务。
  • 依赖:目标文件生成所需的文件或其他目标。
  • 命令:生成目标的具体操作,必须以TAB开头。

==注意=:
目标必须存在,依赖可以没有,命令前面必须是一个制表符"TAB"
Makefile 文件的命名一般是 Makefile没有后缀也没有前缀

在这里插入图片描述

1.3 Makefile 示例

假设我们有一个简单的 C 项目,包含以下文件:

  • main.c
  • foo.c
  • foo.h

我们希望将这些源文件编译为可执行文件 main

1.4 简单的 Makefile 示例

# 定义目标和依赖
main: main.o foo.o
 # 使用 gcc 编译
 gcc -o main main.o foo.o

# 定义 main.o 的规则
main.o: main.c foo.h
 gcc -c main.c

# 定义 foo.o 的规则
foo.o: foo.c foo.h
 gcc -c foo.c

# 清理生成的文件
clean:
 rm -f main main.o foo.o

1.5 Makefile 中的变量

Makefile 中的变量可以使脚本更易读、更易维护。
在Makefile 中变量属于弱类型,在Makefile中变量就是一个名字(像是C语言中的宏),代表一个文本字符串(变量的值),在Makefile的目标、依赖、命令中引用一个变量的地方。

在 Makefile 中,变量的特征和使用方式有以下几点:

  1. 变量和函数的展开

    • 变量和函数的展开(除规则的命令行以外)是在 make 读取 Makefile 文件时进行的。这里的变量包括了使用 = 定义和使用指示符 define 定义的变量。
  2. 变量的用途

    • 变量可以用来代表文件名列表、编译选项列表、程序运行的选项参数列表、搜索源文件的目录列表、编译输出的目录列表以及所有我们能够想到的事物。
  3. 变量名的限制

    • 变量名不能包括 :#=、前置空白和尾空白的任何字符串。尽管在 GNU make 中没有对变量的命名有其它的限制,但定义包含除字母、数字和下划线以外字符的变量是不推荐的。这是因为这些字符可能在以后的 make 版本中被赋予特殊含义,并且这种命名的变量对于一些 Shell 来说不能作为环境变量使用。
  4. 大小写敏感

    • 变量名是大小写敏感的。变量 fooFooFOO 是三个不同的变量。传统上,Makefile 中的变量名是全大写的。推荐的做法是对内部定义的一般变量(例如:目标文件列表 objects)使用小写方式,而对一些参数列表(例如:编译选项 CFLAGS)采用大写方式。对一个工程来说,Makefile 中的变量命名应保持一致的风格,以避免混乱。
  5. 自动化变量

    • 有一些变量名只包含一个或几个特殊的字符,被称为自动化变量。这些变量的值会“自动地”发生变化。例如:<@?#*@D%F^D 等。
  6. 变量引用

    • 变量的引用与 Shell 脚本类似,使用美元符号和圆括号。例如,有个变量叫 A,那么对它的引用是 $(A);有个自动化变量 @,对它的引用是 $(@);有个系统变量 CC,对其引用的格式是 $(CC)。对于单字符变量,引用时的括号可以省略,写成 $A$@

定义变量的方法如下:

CC = gcc
CFLAGS = -I.

main: main.o foo.o
 $(CC) -o main main.o foo.o

main.o: main.c foo.h
 $(CC) $(CFLAGS) -c main.c

foo.o: foo.c foo.h
 $(CC) $(CFLAGS) -c foo.c

clean:
 rm -f main main.o foo.o

系统预定义变量
在这里插入图片描述

1.6 自动化变量

自动化变量使得 Makefile 更加简洁、灵活。例如:

  • $@ 表示目标文件
  • $< 表示第一个依赖文件
  • $^ 表示所有依赖文件
    在这里插入图片描述

在这里插入图片描述

使用自动化变量的示例:

CC = gcc
CFLAGS = -I.

main: main.o foo.o
 $(CC) -o $@ $^

main.o: main.c foo.h
 $(CC) $(CFLAGS) -c $<

foo.o: foo.c foo.h
 $(CC) $(CFLAGS) -c $<

clean:
 rm -f main main.o foo.o

1.7 通用规则

Makefile 支持通用规则,可以避免为每个文件单独定义规则。例如:

CC = gcc
CFLAGS = -I.
SRC = $(wildcard *.c)
OBJ = $(SRC:.c=.o)

main: $(OBJ)
 $(CC) -o $@ $^

%.o: %.c
 $(CC) $(CFLAGS) -c $<

clean:
 rm -f main $(OBJ)

在这个例子中:

  • $(wildcard *.c) 用于获取当前目录下所有 .c 文件。
  • $(SRC:.c=.o) 用于将 .c 文件扩展名替换为 .o
  • 通用规则 %.o: %.c 用于定义如何从 .c 文件生成 .o 文件。

1.8 伪目标

伪目标(Phony Target)不会生成文件,只是执行一组命令。例如 clean 目标。使用 .PHONY 声明伪目标:

.PHONY: clean

clean:
 rm -f main $(OBJ)

伪目标 clean 用于清理生成的文件,它不会生成实际文件,只是执行清理命令。

1.9 依赖管理

Make 会根据依赖关系和文件的==时间戳来判断是否需要重新编译目标。如果依赖文件比目标文件更新,Make 会重新执行规则。

1.10 递归定义和条件定义

Makefile 中支持递归定义和条件定义变量:

  • 递归定义:变量可以引用尚未定义的其他变量。

    A = apple
    B = $(A) tree
    

    在这个例子中,B 的值是 apple tree,即使 A 是在 B 之后定义的。

  • 条件定义:仅当变量尚未定义时赋值。

    A ?= I love China
    

    只有当 A 未被定义时,它才会被赋值为 I love China

1.11 函数

Makefile 支持使用函数,例如替换字符串和获取文件列表:

  • $(subst FROM,TO,TEXT):将字符串 TEXT 中的字符 FROM 替换为 TO。

    A = $(subst pp,PP,apple)
    

    在这个例子中,A 的值是 aPPle

  • $(wildcard PATTERN):获取匹配模式为 PATTERN 的文件名。

    SRC = $(wildcard src/*.c)
    

    在这个例子中,SRC 包含 src 目录下所有 .c 文件的列表。

示例 Makefile

结合以上所有内容,下面是一个示例 Makefile:

CC = gcc
CFLAGS = -I./inc
SRC = $(wildcard src/*.c)
OBJ = $(SRC:.c=.o)
TARGET = bin/main

all: $(TARGET)

$(TARGET): $(OBJ)
 $(CC) -o $@ $^

%.o: %.c
 $(CC) $(CFLAGS) -c $< -o $@

.PHONY: clean
clean:
 rm -f $(TARGET) $(OBJ)

在这个例子中:

  • CCCFLAGS 定义了编译器及其选项。
  • SRCOBJ 使用了函数来获取源文件和目标文件列表。
  • all 是默认目标,依赖于 $(TARGET)
  • $(TARGET) 依赖于 $(OBJ),并使用 $(CC) 编译生成可执行文件。
  • %.o: %.c 是通用规则,用于从 .c 文件生成 .o 文件。
  • clean 是一个伪目标,用于清理生成的文件。
    在这里插入图片描述

总结
Makefile 是管理大型项目编译过程的强大工具。通过定义目标、依赖和命令,以及使用变量、自动化变量、通用规则和伪目标,可以大大简化编译过程,提高工作效率。

  • 22
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写的什么石山代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值