最详细的Makefile教程

Makefile 是一个用于管理软件项目中编译、链接和其他任务的工具。它使用 Make 工具来自动化构建过程,确保只有修改过的文件才会重新编译。以下是一个简单但详细的 Makefile 教程,帮助你入门。

1. Makefile 的基础结构

一个基本的 Makefile 包含规则(rules)、目标(targets)、依赖关系(dependencies)和命令(commands)。以下是一个简单的例子:

# 注释以 '#' 开头

# 定义变量
CC = gcc
CFLAGS = -Wall

# 第一个目标是默认目标
all: my_program

# 目标和依赖关系
my_program: main.o utils.o
    $(CC) $(CFLAGS) -o my_program main.o utils.o

# 编译规则
main.o: main.c
    $(CC) $(CFLAGS) -c main.c

utils.o: utils.c
    $(CC) $(CFLAGS) -c utils.c

# 清理规则
clean:
    rm -f my_program *.o

在这个例子中:

  • my_program 是目标,依赖于 main.outils.o
  • main.outils.o 分别是目标,依赖于对应的源文件和头文件。
  • 每个目标都有相应的规则,指定了如何生成它们的命令。

你可以通过执行 make my_program 来构建可执行文件 my_program。如果某个文件发生了更改,Make 工具会自动检测并重新构建相关的文件。

2. Makefile 中的关键概念

  • 目标(target): 是构建过程中的输出文件,可以是可执行文件、库文件等。 (my_program: main.o utils.o 中my_program就是依赖)
  • 依赖关系(dependencies): 是目标生成所依赖的文件或其他目标。(my_program: main.o utils.o 中main.o util.o就是依赖)
  • 规则(rule): 定义了如何生成目标的规则。(my_program: main.o utils.o 中my_program 是由main.o和util.o生成)。
  • 命令(command): 是实际执行的操作,通常是编译、链接等命令。(如$(CC) $(CFLAGS) -o my_program main.o utils.o 就是命令)
  • 变量(variable): 是用于存储值的名称,可以简化 Makefile。(如CC ,CFLAGS)

3.复杂的makefile

编写一个非常复杂的 Makefile 涉及到一个实际的项目,而一个完整的项目的 Makefile 往往更为庞大。以下是一个简化的示例,展示了如何使用上述所有的知识来构建一个具有多个源文件、目录结构和不同规则的项目。请注意,实际项目可能需要更多的规则和变量以满足复杂的构建需求。

假设有如下项目结构

project/
|-- src/
|   |-- main.c
|   |-- utils.c
|   |-- main.h
|   |-- utils.h
|-- lib/
|   |-- math/
|       |-- add.c
|       |-- subtract.c
|       |-- add.h
|       |-- subtract.h
|-- build/
|-- bin/

以下是一个复杂的 Makefile 示例:

# 定义变量
CC = gcc
CFLAGS = -Wall
SRC_DIR = src
LIB_DIR = lib
BUILD_DIR = build
BIN_DIR = bin

# 通配符匹配源文件
SRCS := $(wildcard $(SRC_DIR)/*.c)
OBJS := $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(SRCS))
LIB_SRCS := $(wildcard $(LIB_DIR)/*/*.c)
LIB_OBJS := $(patsubst $(LIB_DIR)/%.c, $(BUILD_DIR)/%.o, $(LIB_SRCS))

# 默认目标:构建可执行文件 my_program
all: $(BIN_DIR)/my_program

# 构建可执行文件规则
$(BIN_DIR)/my_program: $(OBJS) $(LIB_OBJS)
    $(CC) $(CFLAGS) -o $@ $^

# 通配符规则:编译所有源文件到目标文件
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
    $(CC) $(CFLAGS) -c $< -o $@

# 通配符规则:编译所有库源文件到目标文件
$(BUILD_DIR)/%.o: $(LIB_DIR)/%.c
    $(CC) $(CFLAGS) -c $< -o $@

# 清理规则
clean:
    rm -rf $(BUILD_DIR)/* $(BIN_DIR)/*

# 输出详细信息
info:
    @echo "Source files: $(SRCS)"
    @echo "Object files: $(OBJS)"
    @echo "Library source files: $(LIB_SRCS)"
    @echo "Library object files: $(LIB_OBJS)"

因为这个makefile包含了通配符,和内置变量,看起来有点困难,但是基本的规则,目标和依赖没有变化。

  • 通配符:

   *:匹配任意长度的字符,但不包括路径分隔符(如 /)。

# 匹配所有以 .c 结尾的源文件 
*.c: $(CC) $(CFLAGS) -c $< -o $@

    %:匹配任意长度的字符,包括路径分隔符。

# 匹配所有以 .o 结尾的目标文件,对应的源文件是同名的 .c 文件

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

 ?:匹配任意单个字符。

# 匹配所有以 a、b 或 c 开头,接着是一个字符,然后是 .c 结尾的源文件

[abc]?.c: $(CC) $(CFLAGS) -c $< -o $@

[]:匹配括号内的任意一个字符。

# 匹配所有以 a、b 或 c 开头,接着是 .c 结尾的源文件

[abc]*.c: $(CC) $(CFLAGS) -c $< -o $@
内置变量:

在 Makefile 中,有一些内置的自动变量用于方便地引用特定信息。以下是一些常用的内置变量:

   $@ 表示规则中的目标文件名

my_target: dependency
    command $@

    $< 表示规则中的第一个依赖文件名

my_target: dependency
    command $<

   $^ 表示规则中的所有依赖文件列表。

my_target: dependency1 dependency2
    command $^

    $? 表示规则中所有比目标文件更新的依赖文件列表

my_target: dependency1 dependency2
    command $?

    $(@D)$(@F) 分别表示目标文件所在的目录和文件名

my_target: dependency
    command $(@D)/$(@F)

这些内置变量使得在 Makefile 中引用文件名、目录名等信息更加方便。使用它们可以避免在规则中硬编码文件名,使得 Makefile 更加灵活和易维护。需要注意的是,这些变量只有在规则的执行过程中才会被正确赋值。

下面逐行解释每一部分:

  1. CC = gcc:定义变量 CC 为编译器的命令,使用 GCC。

  2. CFLAGS = -Wall:定义变量 CFLAGS 为编译选项,包括开启所有警告。

  3. SRC_DIR = src:定义变量 SRC_DIR 为源代码目录。

  4. LIB_DIR = lib:定义变量 LIB_DIR 为库目录。

  5. BUILD_DIR = build:定义变量 BUILD_DIR 为构建目录,用于存放生成的目标文件。

  6. BIN_DIR = bin:定义变量 BIN_DIR 为输出二进制文件目录,用于存放生成的可执行文件。

  7. SRCS := $(wildcard $(SRC_DIR)/*.c):使用通配符匹配源文件,生成源文件列表 SRCS。(wildcard 是一个函数,用于展开通配符,获取符合通配符模式的文件列表。)

  8. OBJS := $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(SRCS)):使用模式替换,生成对应的目标文件列表 OBJS。(patsubst 是 Makefile 中的一个函数,用于执行模式替换(pattern substitution))

  9. LIB_SRCS := $(wildcard $(LIB_DIR)/*/*.c):使用通配符匹配库源文件,生成库源文件列表 LIB_SRCS

  10. LIB_OBJS := $(patsubst $(LIB_DIR)/%.c, $(BUILD_DIR)/%.o, $(LIB_SRCS)):使用模式替换,生成对应的库目标文件列表 LIB_OBJS

  11. all: $(BIN_DIR)/my_program:定义默认目标 all,构建可执行文件 my_program

  12. $(BIN_DIR)/my_program: $(OBJS) $(LIB_OBJS):构建可执行文件规则,依赖于所有源文件和库文件的目标文件。

  13. $(CC) $(CFLAGS) -o $@ $^:构建可执行文件命令,使用变量引用。

  14. $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c:通配符规则,编译所有源文件到目标文件。

  15. $(CC) $(CFLAGS) -c $< -o $@:通配符规则的命令,使用自动变量 $<$@

  16. $(BUILD_DIR)/%.o: $(LIB_DIR)/%.c:通配符规则,编译所有库源文件到目标文件。

  17. $(CC) $(CFLAGS) -c $< -o $@:通配符规则的命令,使用自动变量 $<$@

  18. clean: rm -rf $(BUILD_DIR)/* $(BIN_DIR)/*:定义清理规则,删除构建目录和输出目录下的所有文件。

  19. info: @echo "Source files: $(SRCS)" ...:输出详细信息规则,显示源文件、目标文件等详细信息。使用 @echo 避免输出规则本身。

这个 Makefile 结合了之前提到的变量、通配符、自动变量等概念,用于构建包含多个源文件和库文件的项目。

你可能感兴趣的:(深入浅出C语言,linux,运维,服务器,c语言)