Makefile 是一个非常强大的构建自动化工具,用于管理项目的编译、链接和其他构建任务。以下是一个详细的 Makefile 使用文档,包括基本概念、语法、示例和常见任务。
1. 基本概念
•目标 (Targets):在 Makefile 中,目标是要生成的文件或执行的操作的名称。目标可以是文件名,也可以是伪目标,用于执行特定任务而不生成文件。•依赖项 (Dependencies):依赖项是与目标相关联的文件或其他目标,它们在目标生成之前必须存在或已经生成。•规则 (Rules):规则定义了如何生成目标以及生成目标所需的命令。•命令 (Commands):命令是在生成目标时要执行的操作。命令必须以 Tab 键开头。•变量 (Variables):变量用于存储文本或命令,并可以在整个 Makefile 中重复使用。•伪目标 (Phony Targets):伪目标是不代表实际文件的目标,而是用于执行特定操作的标记。
2. Makefile 语法
一个基本的 Makefile 规则的语法如下:
代码语言:javascript复制target: dependencies
command
•target
:要生成的目标的名称。•dependencies
:生成目标所需的文件或其他目标的列表。•command
:生成目标的命令,必须以 Tab 键开头。
3. 示例 Makefile
以下是一个简单的示例 Makefile,用于编译一个 C 程序:
代码语言:javascript复制CC = gcc
CFLAGS = -Wall
TARGET = myprogram
SOURCES = main.c utils.c
$(TARGET): $(SOURCES)
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCES)
clean:
rm -f $(TARGET)
这个 Makefile 使用了变量 CC
、CFLAGS
、TARGET
和 SOURCES
,并定义了一个 all
目标用于编译程序,以及一个 clean
目标用于清理生成的文件。
4. 常见任务
4.1 编译项目
代码语言:javascript复制all: $(TARGET)
$(TARGET): $(SOURCES)
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCES)
4.2 清理生成的文件
代码语言:javascript复制clean:
rm -f $(TARGET)
4.3 使用变量
代码语言:javascript复制CC = gcc
CFLAGS = -Wall
TARGET = myprogram
SOURCES = main.c utils.c
$(TARGET): $(SOURCES)
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCES)
4.4 伪目标
代码语言:javascript复制.PHONY: clean
clean:
rm -f $(TARGET)
5. 高级用法
除了基本用法外,还支持一些高级用法,可以用来处理更复杂的构建需求。以下是一些高级用法的详细介绍:
5.1 条件语句和函数
条件语句
Makefile 支持条件语句,可以根据条件来执行不同的规则或命令。通常使用 ifeq
和 ifdef
这两个条件语句。
ifeq ($(VARIABLE), value)
# 条件为真时的规则和命令
else
# 条件为假时的规则和命令
endif
例如,可以根据是否定义了 DEBUG
变量来设置不同的编译选项:
ifeq ($(DEBUG), 1)
CFLAGS = -g
else
CFLAGS = -O2
endif
函数
Makefile 还提供了一些内置函数,用于处理文本和文件列表。以下是一些常见的函数:
•(shell command):执行 shell 命令并返回结果。•(wildcard pattern):匹配文件名模式并返回符合条件的文件列表。•(foreach var, list, text):对列表中的每个元素执行指定的操作。•(strip string):删除字符串开头和结尾的空白字符。•(subst find,replace,text):替换文本中的字符串。•(filter pattern, text):从文本中筛选出匹配指定模式的字符串。•(patsubst pattern,replacement,text):用指定字符串替换文本中的模式。•(notdir names):从文件路径中提取文件名。
这些函数可以在 Makefile 中用于各种目的,例如文件操作、文本处理和条件判断。
5.2 自动化依赖关系生成
通常,Makefile 中的依赖关系需要手动维护。但是,对于 C/C 项目,您可以使用编译器提供的 -M
选项来自动生成依赖关系。例如:
SOURCES = main.c utils.c
DEPS = $(SOURCES:.c=.d)
%.d: %.c
$(CC) -M $< -o $@
-include $(DEPS)
在这个示例中,%.d: %.c
规则用于自动生成 .d
文件,其中包含了 .c
文件的依赖关系。然后使用 -include
指令来包含这些 .d
文件,以自动跟踪依赖关系。
5.3 多目录项目
对于大型项目,通常需要将 Makefile 拆分成多个子目录,每个子目录都有自己的 Makefile。然后,可以使用递归或变量传递来管理这些子目录之间的依赖关系。例如:
代码语言:javascript复制SUBDIRS = dir1 dir2
all: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@
clean:
for dir in $(SUBDIRS); do
$(MAKE) -C $$dir clean;
done
在这个示例中,SUBDIRS
变量包含了子目录的列表。$(MAKE)
是一个 Makefile 中的特殊变量,用于启动另一个 Makefile。
5.4 高级变量操作
Makefile 支持高级的变量操作,包括字符串操作、条件赋值、变量展开等。以下是一些示例:
字符串操作
代码语言:javascript复制STR1 = hello
STR2 = world
STR3 = $(STR1) $(STR2)
在这个示例中,STR3
的值将是 "hello world"
。
条件赋值
代码语言:javascript复制FOO ?= default_value
如果 FOO
变量未定义,则将其赋值为 default_value
。
变量展开
代码语言:javascript复制VAR1 = $(VAR2)
VAR2 = value
在这个示例中,VAR1
的值将是 value
,因为 Make 会递归地展开变量。
声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)[1]进行许可,使用时请注明出处。 Author: mengbin[2] blog: mengbin[3] Github: mengbin92[4] cnblogs: 恋水无意[5]
References
[1]
署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0): https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh
[2]
mengbin: mengbin1992@outlook.com
[3]
mengbin: https://mengbin.top
[4]
mengbin92: https://mengbin92.github.io/
[5]
恋水无意: https://www.cnblogs.com/lianshuiwuyi/