news 2026/6/10 21:14:33

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/10 3:52:53

Open-AutoGLM核心技术揭秘:AI驱动下的公积金提取效率革命

第一章&#xff1a;Open-AutoGLM公积金提取辅助在智能化政务场景不断演进的背景下&#xff0c;Open-AutoGLM作为一款面向自动化任务处理的大语言模型辅助系统&#xff0c;为公积金提取流程提供了高效、准确的解决方案。该系统通过自然语言理解与结构化数据交互能力&#xff0c;…

作者头像 李华
网站建设 2026/6/10 10:57:19

你还在手动找加油站?Open-AutoGLM自动化查询方案全公开

第一章&#xff1a;你还在手动找加油站&#xff1f;Open-AutoGLM自动化查询方案全公开在智能出行场景中&#xff0c;频繁手动搜索加油站不仅耗时&#xff0c;还影响驾驶安全。Open-AutoGLM 是一款基于自然语言理解与地理信息融合的自动化查询框架&#xff0c;能够实时解析用户意…

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

揭秘Open-AutoGLM自动社保查询系统:如何3分钟完成百人参保数据采集

第一章&#xff1a;揭秘Open-AutoGLM自动社保查询系统的核心价值Open-AutoGLM 是一款基于大语言模型驱动的自动化社保信息查询系统&#xff0c;致力于在保障数据安全的前提下&#xff0c;提升政务信息获取的效率与准确性。该系统融合自然语言理解、自动化流程执行与多源数据对接…

作者头像 李华
网站建设 2026/6/10 6:09:42

【Open-AutoGLM实战指南】:5大场景全覆盖,轻松实现零材料提取

第一章&#xff1a;Open-AutoGLM公积金提取辅助概述Open-AutoGLM 是一款基于开源大语言模型&#xff08;LLM&#xff09;技术构建的智能辅助系统&#xff0c;专为优化公积金提取流程而设计。该系统融合自然语言理解、自动化表单填充与政策规则引擎&#xff0c;帮助用户高效完成…

作者头像 李华
网站建设 2026/6/10 10:57:21

ClickHouse 数据去重技术:大数据处理的常见问题解决方案

ClickHouse 数据去重技术:大数据处理的破局点 某电商平台技术团队发现,实时分析面板中显示的“独立访客数”(UV)总比预期高出20%,直接影响了促销活动效果评估和流量分析。深入排查时,他们惊讶地发现——问题根源竟是看似简单却深藏陷阱的数据重复问题!当你的数据仓库每天涌…

作者头像 李华
网站建设 2026/6/10 11:01:15

5个关键步骤教你部署Open-AutoGLM自动购票机器人,错过等于浪费一小时

第一章&#xff1a;Open-AutoGLM自动购票机器人概述Open-AutoGLM 是一款基于大语言模型驱动的智能自动化购票工具&#xff0c;旨在通过自然语言理解与网页交互技术&#xff0c;实现对票务平台的高效、精准操作。该系统结合了浏览器自动化框架与语义解析能力&#xff0c;能够根据…

作者头像 李华