news 2026/6/9 18:30:20

20251127 - 韦东山Linux - 通用Makefile解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
20251127 - 韦东山Linux - 通用Makefile解析

韦东山Linux - 通用Makefile解析

# Makefile文件 CROSS_COMPILE = AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld CC = $(CROSS_COMPILE)gcc CPP = $(CC) -E AR = $(CROSS_COMPILE)ar NM = $(CROSS_COMPILE)nm STRIP = $(CROSS_COMPILE)strip OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump export AS LD CC CPP AR NM export STRIP OBJCOPY OBJDUMP CFLAGS := -Wall -O2 -g CFLAGS += -I $(shell pwd)/include LDFLAGS := export CFLAGS LDFLAGS TOPDIR := $(shell pwd) export TOPDIR TARGET := test obj-y += main.o obj-y += sub.o obj-y += a/ all : start_recursive_build $(TARGET) @echo $(TARGET) has been built! start_recursive_build: make -C ./ -f $(TOPDIR)/Makefile.build $(TARGET) : start_recursive_build $(CC) -o $(TARGET) built-in.o $(LDFLAGS) clean: rm -f $(shell find -name "*.o") rm -f $(TARGET) distclean: rm -f $(shell find -name "*.o") rm -f $(shell find -name "*.d") rm -f $(TARGET)
PHONY := __build __build: obj-y := subdir-y := EXTRA_CFLAGS := include Makefile # obj-y := a.o b.o c/ d/ # $(filter %/, $(obj-y)) : c/ d/ # __subdir-y : c d # subdir-y : c d __subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y))) subdir-y += $(__subdir-y) # c/built-in.o d/built-in.o subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o) # a.o b.o cur_objs := $(filter-out %/, $(obj-y)) dep_files := $(foreach f,$(cur_objs),.$(f).d) dep_files := $(wildcard $(dep_files)) ifneq ($(dep_files),) include $(dep_files) endif PHONY += $(subdir-y) __build : $(subdir-y) built-in.o $(subdir-y): make -C $@ -f $(TOPDIR)/Makefile.build built-in.o : $(subdir-y) $(cur_objs) $(LD) -r -o $@ $(cur_objs) $(subdir_objs) dep_file = .$@.d %.o : %.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $< .PHONY : $(PHONY)

这是一个经典的Kbuild 风格(Linux 内核构建系统)的Makefile架构。这个系统将项目的配置信息(Makefile) 与通用构建规则(Makefile.build) 彻底分离,并实现了递归编译自动依赖追踪

下面将对MakefileMakefile.build进行详细的逐行解析。


📄 一、顶层Makefile(配置与入口)

这个文件位于项目的顶层,负责定义编译工具、全局选项、目标列表,并作为启动递归构建的入口 1。

  1. 工具链变量定义
CROSS_COMPILE = AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld # ... (其他工具变量如 CC, CPP, AR, NM, STRIP, OBJCOPY, OBJDUMP)
  • 定义工具链前缀CROSS_COMPILE。如果为空,则使用本地工具(如gcc,as)。
  • 基于CROSS_COMPILE定义所有工具的完整名称(如CCgcc)。
  • 变量导出(Export)
export AS LD CC CPP AR NM export STRIP OBJCOPY OBJDUMP
  • export:将这些变量(如CC)导出到Shell 环境。这样,在Makefile规则中执行的任何命令(如gcc)都能访问这些变量的值,确保所有子目录和递归调用都使用相同的工具链。
  • 编译与链接选项
CFLAGS := -Wall -O2 -g CFLAGS += -I $(shell pwd)/include LDFLAGS := export CFLAGS LDFLAGS
  • CFLAGS:定义全局 C 编译选项。
    • := -Wall -O2 -g:使用即时变量定义-Wall(所有警告)、-O2(优化级别)、-g(生成调试信息)。
    • += -I $(shell pwd)/include:添加一个搜索头文件的路径,即项目根目录下的include文件夹。
  • LDFLAGS:定义链接选项(当前为空)。
  • export:将全局选项导出,确保在所有递归子目录中,编译和链接都使用这些统一的选项。
  • 路径与目标定义
TOPDIR := $(shell pwd) export TOPDIR TARGET := test obj-y += main.o obj-y += sub.o obj-y += a/
  • TOPDIR:定义项目的顶层目录的绝对路径,并导出。这对于子目录调用Makefile.build时查找文件路径至关重要。
  • TARGET:定义最终可执行文件名为test
  • obj-y:定义了本层目录的构建配置:
    • main.o,sub.o:需要编译的文件。
    • a/:需要递归进入的子目录。
  • 核心规则与入口
all : start_recursive_build $(TARGET) @echo $(TARGET) has been built! start_recursive_build: make -C ./ -f $(TOPDIR)/Makefile.build $(TARGET) : start_recursive_build $(CC) -o $(TARGET) built-in.o $(LDFLAGS)
  • all:默认目标,依赖于start_recursive_build$(TARGET)
  • start_recursive_build递归构建的入口
    • make -C ./ -f $(TOPDIR)/Makefile.build:启动一个新的make进程。它告诉make切换到当前目录 (./),并使用位于$(TOPDIR)Makefile.build作为规则文件。这将触发Makefile.build开始执行本层和子目录的编译。
  • $(TARGET)(test):最终的链接目标。
    • 依赖于start_recursive_build(确保所有.o文件都已编译,并被打包到built-in.o中)。
    • 命令:将built-in.o(由Makefile.build聚合而成) 链接成最终的test可执行文件。
  • 清理目标
clean: rm -f $(shell find -name "*.o") rm -f $(TARGET) distclean: rm -f $(shell find -name "*.o") rm -f $(shell find -name "*.d") rm -f $(TARGET)
  • clean:删除所有.o文件和最终目标test
  • distclean:更彻底的清理,还删除了自动生成的依赖文件 (.d文件)。

📄 二、通用规则文件Makefile.build

这个文件包含了所有通用的编译逻辑、递归规则和文件聚合机制,它不包含任何特定于项目的配置

  1. 变量初始化与包含
PHONY := __build __build: obj-y := subdir-y := EXTRA_CFLAGS := include Makefile
  • PHONY__build__buildMakefile.build中的核心目标,它代表“构建当前目录的所有内容”,被声明为伪目标 (PHONY)。
  • 变量清空obj-y,subdir-y,EXTRA_CFLAGS被清空,以确保它们仅包含当前目录 (Makefile) 中定义的值。
  • include Makefile关键步骤。此时make停止,转而读取当前目录下的Makefile(即顶层Makefile或子目录下的Makefile)。这个include会导入obj-y等配置变量。
    • 例如,在顶层运行时,obj-y变为main.o sub.o a/
  • 目录和文件分离
__subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y))) subdir-y += $(__subdir-y) subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o) cur_objs := $(filter-out %/, $(obj-y))
  • __subdir-y:将obj-y中所有以/结尾的项(如a/)筛选出来 (filter %/, ...),并去除斜杠 (patsubst %/,%, ...),得到子目录名列表 (a)。
  • subdir-y:存储需要递归的子目录列表 (a)。
  • subdir_objs:生成子目录构建完成后的聚合目标文件列表(a/built-in.o)。
  • cur_objs:将obj-y/结尾的项筛选出来,得到本层需要编译的.o文件列表(main.o sub.o)。
  • 自动依赖追踪 (Auto-Dependency)
dep_files := $(foreach f,$(cur_objs),.$(f).d) dep_files := $(wildcard $(dep_files)) ifneq ($(dep_files),) include $(dep_files) endif
  • 生成依赖文件名的理论列表 (.main.o.d,.sub.o.d)。

  • 使用wildcard查找实际存在的依赖文件。

  • 如果找到任何.d文件 (ifneq), 则将其include进来,实现了自动依赖追踪:一旦头文件有改动,make就能通过.d文件中的规则知道哪些.o文件需要重编译。

    递归与聚合规则

__build : $(subdir-y) built-in.o $(subdir-y): make -C $@ -f $(TOPDIR)/Makefile.build built-in.o : $(subdir-y) $(cur_objs) $(LD) -r -o $@ $(cur_objs) $(subdir_objs)
  • __build:核心目标,它依赖于所有子目录的完成 ($(subdir-y)) 和本层聚合文件 (built-in.o)。
  • $(subdir-y)(a):递归规则。对于每个子目录a
    • make -C $@ -f $(TOPDIR)/Makefile.build递归调用make。切换到子目录 (-C $@,即a/),并要求子目录再次使用Makefile.build进行构建。
  • built-in.o聚合规则
    • 依赖于所有子目录的聚合文件 ($(subdir-y)) 和本层.o文件 ($(cur_objs))。
    • 命令:$(LD) -r -o $@ ...:使用链接器 (LD) 的-r选项(可重定位输出),将本层所有编译出的.o文件 ($(cur_objs)) 以及所有子目录的聚合文件 ($(subdir_objs))打包成一个大的聚合文件built-in.o
  • 编译模式规则

Makefile

dep_file = .$@.d %.o : %.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $<
  • dep_file:定义了当前目标.o对应的.d文件名 (.$@.d)。
  • %.o : %.c:通用的编译规则。
    • 调用gcc($(CC))。
    • 使用全局 ($(CFLAGS)), 额外的 ($(EXTRA_CFLAGS)), 局部 ($(CFLAGS_$@)) 选项。
    • -Wp,-MD,$(dep_file):这是 GCC 产生依赖的另一种写法。-MD标志生成依赖,并将其写入$(dep_file)中。
    • -c -o $@ $<:正常的编译命令。

总结工作流

  1. 顶层Makefile启动make -C ./ -f Makefile.build

  2. Makefile.build读取Makefile中的配置 (obj-y = main.o sub.o a/)

  3. Makefile.build编译main.o,sub.o(通过%.o : %.c规则)

  4. Makefile.build发现a/,执行递归调用make -C a/ -f Makefile.build

  5. 子目录重复步骤 2-4,并最终生成a/built-in.o.

  6. 回到顶层,Makefile.buildmain.o,sub.o, 和a/built-in.o打包成顶层的built-in.o

  7. 顶层Makefile将顶层的built-in.o链接成最终的可执行文件test

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 16:07:19

Open-AutoGLM核心技术解析:如何用自然语言理解破局政务“办事难”困局

第一章&#xff1a;Open-AutoGLM政务办理辅助的背景与意义随着数字化政府建设的不断推进&#xff0c;政务服务正从“可办”向“好办、智办”转型升级。传统的政务流程普遍存在手续繁琐、信息孤岛严重、跨部门协同效率低等问题&#xff0c;导致公众在办理业务时耗时长、体验差。…

作者头像 李华
网站建设 2026/6/9 16:10:38

你还在等客服回复?,掌握这3种自助预约方式秒杀90%用户

第一章&#xff1a;你还在等客服回复&#xff1f;掌握自助预约的必要性在数字化服务日益普及的今天&#xff0c;依赖人工客服处理预约请求不仅效率低下&#xff0c;还可能因响应延迟影响业务进度。掌握自助预约系统&#xff0c;意味着用户能够实时获取资源状态、自主安排时间&a…

作者头像 李华
网站建设 2026/6/4 20:38:41

多线程(0-0)

一、进程【理解】 1. 进程&#xff1a;操作系统(OS)中&#xff0c;每一个被执行的应用程序。 2. 注意&#xff1a;目前操作系统支持多进程&#xff0c;并发执行的任务。 3. 多进程并发执行的原理&#xff1a;微观上串行(一个一个的进程进行执行&#xff0c;获取cpu时间片的进程…

作者头像 李华
网站建设 2026/6/9 11:29:05

好写作AI:你的论文为什么总被批“不像英美学术体”?

当一篇论文被国际审稿人评价为“论证方式带有明显的区域性特征”&#xff0c;或是导师指出“这不像Nature/Science会接受的表达范式”时&#xff0c;问题往往不在你的英语语法&#xff0c;而在于你未能掌握目标学术圈的 “隐形写作规则” 。更令人沮丧的是&#xff0c;许多AI工…

作者头像 李华
网站建设 2026/6/8 8:32:20

车载AI革命来临(Open-AutoGLM赋能智能保养)

第一章&#xff1a;车载AI革命来临&#xff08;Open-AutoGLM赋能智能保养&#xff09;现代汽车行业正经历一场由人工智能驱动的深刻变革&#xff0c;其中&#xff0c;Open-AutoGLM作为专为车辆场景设计的大语言模型&#xff0c;正在重新定义智能保养的边界。该模型融合自然语言…

作者头像 李华