1128 字
6 分钟
Linux下规范性Makefile规则

文件目录#

  • bsp
    • clk (时钟驱动)
      • bsp_clk.c
      • bsp_clk.h
    • delay (延时驱动)
      • bsp_delay.c
      • bsp_delay.h
    • led (LED驱动)
      • bsp_led.c
      • bsp_led.h
  • imx6ul
    • cc.h (变量类型声明)
    • fsl_common.h (NXP官方SDK通用宏定义文档)
    • fsl_iomuxc.h (NXP官方SDK寄存器地址定义文档)
    • imx6ul.h (常用头文件)
    • MCIMX6Y2.h (NXP官方SDK寄存器结构体和相关位定义文档)
  • obj .o文件目标地址
  • project
    • main.c (主函数)
    • start.S (启动文件)

Makefile#

CROSS_COMPILE ?= arm-linux-gnueabihf-
TARGET ?= bsp
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdump
INCDIRS := imx6ul \
bsp/clk \
bsp/led \
bsp/delay
SRCDIRS := project \
bsp/clk \
bsp/led \
bsp/delay
INCLUDE := $(patsubst %, -I %, $(INCDIRS))
SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))
SFILENDIR := $(notdir $(SFILES))
CFILENDIR := $(notdir $(CFILES))
SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS := $(SOBJS) $(COBJS)
VPATH := $(SRCDIRS)
.PHONY: clean
$(TARGET).bin : $(OBJS)
$(LD) -Timx6ul.lds -o $(TARGET).elf $^
$(OBJCOPY) -O binary -S $(TARGET).elf $@
$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis
$(SOBJS) : obj/%.o : %.S
$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<
$(COBJS) : obj/%.o : %.c
$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<
clean:
rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

细节#

工具配置#

CROSS_COMPILE ?= arm-linux-gnueabihf- # 交叉编译工具前缀(ARM架构)
TARGET ?= bsp # 目标程序名称,默认输出文件名为 `bsp`
CC := $(CROSS_COMPILE)gcc # C 编译器
LD := $(CROSS_COMPILE)ld # 链接器
OBJCOPY := $(CROSS_COMPILE)objcopy # 二进制转换工具
OBJDUMP := $(CROSS_COMPILE)objdump # 反汇编工具

目录与文件管理#

INCDIRS := imx6ul \ # 头文件搜索目录列表,"\"为换行符
bsp/clk \
bsp/led \
bsp/delay
SRCDIRS := project \ # 源码搜索目录列表,"\"为换行符
bsp/clk \
bsp/led \
bsp/delay
INCLUDE := $(patsubst %, -I %, $(INCDIRS)) # 将 INCDIRS 转换为 gcc 的 `-I` 选项

patsubst:

  • 函数用法:$(patsubst <pattern>,<replacement>,<text>

  • 名称:模式字符串替换函数——patsubst。

  • 功能:查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括通配符“%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么<replacement>中的这个“%”将是<pattern>中的那个“%”所代表的字串。(可以用“\”来转义,以“%”来表示真实含义的“%”字符)

  • 返回:函数返回被替换过后的字符串。

  • 示例:$(patsubst %.c,%.o, a.c b.c),把字串“a.c b.c”符合模式[%.c]的单词替换成[%.o],返回结果是“a.o b.o”

源码文件收集#

SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S)) # 所有 .S 汇编文件
CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c)) # 所有 .c C 文件
SFILENDIR := $(notdir $(SFILES)) # 去除路径,保留文件名(如 `start.S`)
CFILENDIR := $(notdir $(CFILES)) # 去除路径,保留文件名(如 `main.c`)
SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o)) # 汇编文件对应的 .o 文件(放在 obj 目录)
COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o)) # C 文件对应的 .o 文件(放在 obj 目录)
OBJS := $(SOBJS) $(COBJS) # 所有目标文件
VPATH := $(SRCDIRS) # 指定源码搜索路径,Make 会在这些目录中查找依赖文件

foreach

  • 用法:$(foreach <var>, <list>, <expr>)

  • 功能:遍历 <list> 中的每个元素,将元素赋值给 <var>,然后展开 <expr>,最终将所有展开结果合并成一个空格分隔的字符串。

  • 示例中的用法:遍历 SRCDIRS 中的每个目录,收集所有 .S.c 文件的完整路径。

    1. dir 依次取 SRCDIRS 中的值(如 project, bsp/clk 等)
    2. $(wildcard $(dir)/*.S) 展开为 dir 目录下所有 .S 文件的路径(如 project/start.S, bsp/clk/clk.S)。
    3. 最终 SFILES 是所有 .S 文件的路径列表,CFILES 是所有 .c 文件的路径列表。

wildcard

  • 用法:$(wildcard <pattern>)
  • 功能:匹配符合 <pattern> 的文件路径,支持通配符 * 和 ?。
  • 示例中的用法:在 foreach 循环中,为每个 dir 目录生成文件列表。

生成二进制文件#

$(TARGET).bin : $(OBJS) # 依赖所有 .o 文件
$(LD) -Timx6ul.lds -o $(TARGET).elf $^ # 1. 链接生成 ELF 文件
$(OBJCOPY) -O binary -S $(TARGET).elf $@ # 2. 提取二进制文件
$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis # 3. 生成反汇编文件

编译规则#

$(SOBJS) : obj/%.o : %.S # 编译 .S 汇编文件(需预处理的汇编)
$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<
  • -nostdlib:不链接标准库,适用于裸机程序
  • -O2:优化等级为2
  • $(INCLUDE):包含头文件目录的-I选项
$(COBJS) : obj/%.o : %.c # 编译 .c C 文件
$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<

清理规则#

clean:
rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

完整流程:#

  1. 收集源码路径:
    • SFILES = project/start.S bsp/clk/clk.S
    • CFILES = project/main.c bsp/clk/clk.c
  2. 提取文件名:
    • SFILENDIR = start.S clk.S
    • CFILENDIR = main.c clk.c
  3. 生成目标文件列表
    • SOBJS = obj/start.o obj/clk.o
    • COBJS = obj/main.o obj/clk.o
    • OBJS = obj/start.o obj/clk.o obj/main.o obj/clk.o
  4. 编译时
    • Make 根据 VPATHprojectbsp/clk 目录中查找 start.Sclk.c 等文件。
Linux下规范性Makefile规则
https://arkmostima.pages.dev/posts/linux下规范性makefile规则/
作者
Aki
发布于
2025-03-02
许可协议
CC BY-NC-SA 4.0