当前位置: 首页 > news >正文

buildroot Makefile include *.mk 的玄机.


author: hjjdebug
date: 2026年 06月 12日 星期五 21:57:59 CST
descrip: buildroot Makefile include *.mk 的玄机.


文章目录

  • 1. 文件结构
  • 2. Makefile
  • 3. Makefile.in
  • 4. demo.mk
  • 5. foo.mk
  • 6 测试结果
  • 7. buildroot 高仿项目

读buildroot 中Makefile, 在每个package目录的.mk文件中,都有一句
$(eval $(host-autotools-package));
我知道这是Makefile 语句的动态展开. 进一步分析一下宏命令 host-autotools-package
它在 ./package/pkg-autotools.mk 中定义
319:host-autotools-package =( c a l l i n n e r − a u t o t o o l s − p a c k a g e , h o s t − (call inner-autotools-package,host-(callinnerautotoolspackage,host(pkgname),( c a l l U P P E R C A S E , h o s t − (call UPPERCASE,host-(callUPPERCASE,host(pkgname)),( c a l l U P P E R C A S E , (call UPPERCASE,(callUPPERCASE,(pkgname)),host)
那每个目录下的.mk又是怎样被包含的呢?

顶层Makefile 543 行有包含
543 include $(sort $(wildcard package//.mk))

我当时有点犯迷糊, 它这一股脑的把各个.mk 文件包含进来, pkgname 怎么来设置呢? 在哪里设置的?
带着这个疑问,我进行了简化实验, 终于得出了结论.
原来pkgname 并不需要我们自己去设置, 它是一个变量,通过一定的计算方法得到.
而这个方法就借助了make 每include 一个.mk 文件, 它会把.mk这个文件名加入到MAKEFILE_LIST变量中,
用(lastword $(MAKEFILE_LIST)) 可以取到文件路径

其精妙处在与,你include 一个.mk 文件, pkgname会被即时计算(这就是变量延时展开),从文件路径中导出包名.
可见,include *.mk, 与把一大堆.mk 合并成一个大的.mk文件再include 进来是不一样的.
前者借助与make变量MAKEFILE_LIST 可以得到包含的文件名称,从而可以进行一堆makefile 扩展
下面用一个简单的例子,几十行代码,来演示一下这种技巧.

1. 文件结构

$ tree
.
├── Makefile
└── package
├── demo
│ └── demo.mk
├── foo
│ └── foo.mk
└── Makefile.in

2. Makefile

hjj@hjj-laptop:~/test/make3$ cat Makefile #1.先引入 package/Makefile.in,定义延迟展开的 pkgname include package/Makefile.in # 让all 成为第一个目标,默认目标 PHONY:all all:#2.关键行,原封不动:展开并 include 所有 package/*/*.mk include $(sort $(wildcard package/*/*.mk))#3.给all 设定依赖,两处声明all 规则会合并.all:host-foo host-demo @echo"===== 全部规则加载完成 ====="

3. Makefile.in

hjj@hjj-laptop:~/test/make3/package$ cat Makefile.in # 用=定义延迟展开的 pkgname,每次使用时才重新计算 pkgname=$(basename $(notdir $(lastword $(MAKEFILE_LIST))))

4. demo.mk

hjj@hjj-laptop:~/test/make3/package/demo$ cat demo.mk DEMO_VER:=1.0.0define PKG_TPL # 每次宏展开时,都会打印当前 pkgname,证明它是正确的 $(info[DEBUG]包含文件:$(lastword $(MAKEFILE_LIST)))$(info[DEBUG]当前 pkgname=$(pkgname))host-$(pkgname):@echo">>> 运行目标 host-$(pkgname) , 版本: $(DEMO_VER)"endef $(eval $(PKG_TPL))

5. foo.mk

hjj@hjj-laptop:~/test/make3/package/foo$ cat foo.mk FOO_VER:=2.0.0define PKG_TPL $(info[DEBUG]包含文件:$(lastword $(MAKEFILE_LIST)))$(info[DEBUG]当前 pkgname=$(pkgname))host-$(pkgname):@echo">>> 运行目标 host-$(pkgname) , 版本: $(FOO_VER)"endef $(eval $(PKG_TPL))

6 测试结果

hjj@hjj-laptop:~/test/make3$ make[DEBUG]包含文件:package/demo/demo.mk[DEBUG]当前 pkgname=demo[DEBUG]包含文件:package/foo/foo.mk[DEBUG]当前 pkgname=foo>>>运行目标 host-foo,版本:2.0.0>>>运行目标 host-demo,版本:1.0.0=====全部规则加载完成=====hjj@hjj-laptop:~/test/make3$ make host-demo[DEBUG]包含文件:package/demo/demo.mk[DEBUG]当前 pkgname=demo[DEBUG]包含文件:package/foo/foo.mk[DEBUG]当前 pkgname=foo>>>运行目标 host-demo,版本:1.0.0hjj@hjj-laptop:~/test/make3$ make host-foo[DEBUG]包含文件:package/demo/demo.mk[DEBUG]当前 pkgname=demo[DEBUG]包含文件:package/foo/foo.mk[DEBUG]当前 pkgname=foo>>>运行目标 host-foo,版本:2.0.0

这里makefile 相当于脚本语言,make相当于解释器. pkgname变量相当于一个函数返回值(字符串型的)
include xxx.mk 文件, 在xxx.mk 文件中,可以利用make的MAKEFILE_LIST 变量获取到文件名称.
这是自动获取,就不用你手工设置相关变量了.有了这点自动化,差点没把我搞蒙.

再浏览代码,发现还可以简化, 就是现在demo,foo中都定义了PKG_TPL, 形成了一种函数覆盖. 这是不好的,应该把它放到Makefile.in 中定义, 求同存异,把版本号作为函数的参数传进来. 这样就只有一处PKG_TPL, demo.mk,fool.mk进一步简化. 经实验也同样符合要求.代码完美.自己练一下吧,这里就不给代码了.

7. buildroot 高仿项目

送礼要送大礼,先看看结果吧.
就跟真的一样. 流程都到了.
每个包都有 download → extract → configure → build → install 的完整依赖

$ make[DEBUG]包含文件:package/demo/demo.mk[DEBUG]处理包:demo,版本:1.0.0[DEBUG]包含文件:package/foo/foo.mk[DEBUG]处理包:foo,版本:2.0.0>>>下载 foo:https://example.com/foo/foo-2.0.0.tar.gz>>>解压 foo:foo-2.0.0.tar.gz>>>配置 foo:./configure--prefix=/usr>>>编译 foo:make-j4>>>安装 foo:make install ✅ foo 构建完成!>>>下载 demo:https://example.com/demo/demo-1.0.0.tar.gz>>>解压 demo:demo-1.0.0.tar.gz>>>配置 demo:./configure--prefix=/usr>>>编译 demo:make-j4>>>安装 demo:make install ✅ demo 构建完成!=====全部规则加载完成=====

而这一些,仅仅有这么多代码:
$ find . -name “.mk" -o -name "Makefile”|xargs wc -l
4 ./package/foo/foo.mk
7 ./package/demo/demo.mk
42 ./package/Makefile.in
13 ./Makefile
66 总用量

Makefile.in, 调试,书写它花了我一点点时间, 值的. 由此对make有了进一步理解.
希望你由此爱上模板编程.
它能够根据模板,生成实际的Makefile 中的目标规则.
更多知识自己体会.
下面把整个工程倾囊相送,放到一起了,由名称可以区分开

hjj@hjj-laptop:~/test/make3$ cat./package/foo/foo.mk FOO_VER:=2.0.0FOO_SITE:=https://example.com/fooFOO_SOURCE:=foo-$(FOO_VER).tar.gz $(eval $(PKG_BUILD_TPL))hjj@hjj-laptop:~/test/make3$ cat./package/demo/demo.mk # 包自定义变量 DEMO_VER:=1.0.0DEMO_SITE:=https://example.com/demoDEMO_SOURCE:=demo-$(DEMO_VER).tar.gz # 执行宏,生成所有目标 $(eval $(PKG_BUILD_TPL))hjj@hjj-laptop:~/test/make3$ cat./package/Makefile.in # 用=定义延迟展开的 pkgname,每次使用时才重新计算 pkgname=$(basename $(notdir $(lastword $(MAKEFILE_LIST))))# 当Makefile 没有uppercase 内置函数时,自己写一个 define uppercase $(shell echo $(1)|tr[:lower:][:upper:])endef VER=$($(call uppercase,$(pkgname))_VER)SITE=$($(call uppercase,$(pkgname))_SITE)SOURCE=$($(call uppercase,$(pkgname))_SOURCE)# 定义构建流程宏(依赖链:download → extract → configure → build → install) define PKG_BUILD_TPL # 调试信息 $(info[DEBUG]包含文件:$(lastword $(MAKEFILE_LIST)))$(info[DEBUG]处理包:$(pkgname),版本:$(VER))#1.download 目标 host-$(pkgname)-download:@echo">>> 下载 $(pkgname): $(SITE)/$(SOURCE)"#2.extract 目标(依赖 download) host-$(pkgname)-extract:host-$(pkgname)-download @echo">>> 解压 $(pkgname): $(SOURCE)"#3.configure 目标(依赖 extract) host-$(pkgname)-configure:host-$(pkgname)-extract @echo">>> 配置 $(pkgname): ./configure --prefix=/usr"#4.build 目标(依赖 configure) host-$(pkgname)-build:host-$(pkgname)-configure @echo">>> 编译 $(pkgname): make -j4"#5.install 目标(依赖 build) host-$(pkgname)-install:host-$(pkgname)-build @echo">>> 安装 $(pkgname): make install"#6.主目标(依赖 install) host-$(pkgname):host-$(pkgname)-install @echo"✅ $(pkgname) 构建完成!"endef hjj@hjj-laptop:~/test/make3$ cat./Makefile #1.先引入 package/Makefile.in,定义延迟展开的 pkgname include package/Makefile.in # 让all 成为第一个目标,默认目标 PHONY:all all:#2.关键行,原封不动:展开并 include 所有 package/*/*.mk include $(sort $(wildcard package/*/*.mk))#3.给all 设定依赖,两处声明all 规则会合并.all:host-foo host-demo @echo"===== 全部规则加载完成 ====="
http://www.zskr.cn/news/1516979.html

相关文章:

  • 3步快速解决线缆依赖问题:NoCableLauncher的完整使用指南
  • 遇到一个ORA-01017错误,解决方法
  • 主流 MP3 音频转换工具大全,免费软件适配音频剪辑日常使用 - 软件工具教程方法
  • 微信私域机器人开发:iPad协议API实战指南
  • YaeAchievement:3分钟搞定原神成就数据导出,告别手动记录的烦恼
  • 考研数学积分题总丢分?掌握这3个对称区间和三角函数的‘秒杀’性质,计算速度翻倍
  • 嵌入式设备日志自动备份:用Dropbear+SCP免密传输,5分钟搞定脚本配置
  • YimMenu:GTA5终极防护与增强菜单完全指南
  • Java 标准 JAXP(Java API for XML Processing),JDK 内置,无需额外引入第三方依赖
  • netstat命令和ss命令详解
  • PythonVista:突破系统限制,为老旧Windows重新定义Python兼容性边界
  • 2026运城旧金铂银回收黄金回收高信誉门店汇总 5 家线下实体回收商家实地评测与联络渠道整理 - 中业金奢再生回收中心
  • 硬件工程师踩过的坑:Buck电路PCB布局的10个细节(附AD/嘉立创实战案例)
  • 路灯智能控制模块怎么选型?看光控时控经纬度远程四大功能
  • FPGA实战(07): Verilog 实现带符号输出的 0~99 循环计数器(tops 模块)设计与仿真
  • PyTorch-NPU/stable-diffusion-2-1:华为NPU优化的AI绘画模型完全指南 [特殊字符]
  • 基于plc的楼宇供电控制系统及综合防雷设计23(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码
  • 2026大同市卡地亚+GP芝柏表手表专业回收,26年精选回收店铺排行榜推荐 - 莘州文化
  • 团队API文档难维护?怎么用 Claude 快速生成 Markdown?一文看懂选型与实战指南
  • 2026年北京财务管理公司对比测评,代理记账机构哪家强? - 互联百晓生
  • 2026伊犁市百达翡丽+宝珀手表专业回收,26年精选回收店铺排行榜推荐 - 莘州文化
  • 基于PLC控制的铺丝机总体及其控制系统设计23(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码
  • 数据分析师的肌肉记忆:原始数据诊断四层校验法
  • 告别信号衰减!手把手教你制作7/8馈线接头(附工具清单与防短路技巧)
  • M68000处理器数据格式详解:从整数到浮点数的底层表示与对齐优化
  • 嵌入式以太网驱动深度解析:从ENET硬件到SDK实战
  • FPGA实战(10):FPGA全流水复数乘法器设计及自动化验证(Verilog)
  • 终极指南:三步快速解锁原神60FPS限制,享受丝滑游戏体验
  • 经验分享:2026京东 E 卡回收常见骗局拆解与安全交易方案 - 京卡收卡券回收
  • 2026年上海采购新人CPPM报名前需要准备什么?众智商学院官网入门条件与资料清单确认 - 众智商学院职业教育