1. 引言
本文介绍如何通过解析Yocto系统中的组件包依赖关系,了解各组件的物理结构和工程结构。这对于理解某个组件的构建流程和依赖关系非常重要。
2. Dot文件基础知识
2.1 Dot文件基本结构
一个有效的DOT文件应具有以下基本结构:
digraph G {
"node1" [label="Node 1"];
"node2" -> "node1";
}
语法规则
- 图的声明: 使用
digraph
声明有向图。 - 节点定义: 格式为
"node_name" [label="Node Label"];
。 - 边的定义: 格式为
"node1" -> "node2";
。 - 分号: 每一行定义后必须以分号
;
结束。
2.2 语法检查工具
- Graphviz
dot -Tpng yourfile.dot -o output.png
- 在线验证工具
- Graphviz Online
- Graphviz Visual Editor
2.3 Dot示例
以下是一个有效的 DOT 文件示例:
digraph G {
"A" [label="Node A"];
"B" [label="Node B"];
"C" [label="Node C"];
"A" -> "B";
"B" -> "C";
"C" -> "A"; // 循环依赖
}
总结
通过遵循 DOT 文件的语法规则和使用 Graphviz 等工具进行检查,你可以确保 DOT 文件的正确性并避免常见错误。
3. 生成依赖图
首先,使用 bitbake -g <recipe>
命令生成依赖关系文件,这会生成 pn-buildlist
和 task-depends.dot
文件。这里以lib32-curl为例讲解整个过程。
此时在build下生成2个文件
文件名 | 内容 |
pn-buildlist | 包列表 |
task-depends.dot | yocto执行的task(如xxx.do_fetch)的依赖关系 |
task-depends.dot
文件通常采用 DOT 格式,用于描述图形结构,特别是节点和边之间的关系。对于 Yocto 项目中的依赖关系,文件的内容格式大致如下:
- 基本结构
digraph G {
"task1" [label="task1\n:version\n:path"]
"task1" -> "task2"
"task2" [label="task2\n:version\n:path"]
"task2" -> "task3"
...
}
- 具体组件
digraph G { ... }
:
- 指定这是一个有向图(directed graph),
G
是图的名称。
- 节点定义:
- 格式为
"task_name" [label="task_name\n:version\n:path"]
,其中:
"task_name"
: 任务的名称,例如acl-native.do_compile
。label
: 描述性标签,通常包括任务名称、版本和路径信息。使用换行符\n
分隔不同信息。
- 依赖关系:
- 格式为
"task1" -> "task2"
,表示task1
依赖于task2
。每个箭头表示一个任务对另一个任务的依赖。
- 示例内容
以下是一个具体的示例:
digraph G {
"acl-native.do_compile" [label="acl-native do_compile\n:2.3.1-r0\nvirtual:native:/path/to/acl_2.3.1.bb"]
"acl-native.do_compile" -> "acl-native.do_configure"
"acl-native.do_configure" [label="acl-native do_configure\n:2.3.1-r0\nvirtual:native:/path/to/acl_2.3.1.bb"]
"acl-native.do_configure" -> "acl-native.do_patch"
"acl-native.do_patch" [label="acl-native do_patch\n:2.3.1-r0\nvirtual:native:/path/to/acl_2.3.1.bb"]
"acl-native.do_patch" -> "acl-native.do_fetch"
}
- 总结
task-depends.dot
文件通过 DOT 格式清晰地表示了任务之间的依赖关系,便于可视化工具(如 Graphviz)进行解析和展示。理解该格式有助于更好地分析 Yocto 构建过程中的各个任务及其依赖。
通过解析task-depends.dot文件,既可以知道我依赖谁以及谁依赖我的问题。
4.我有哪些依赖
这个问题已经有解决方案,可以使用https://github.com/openembedded/openembedded-core/blob/master/scripts/oe-depends-dot 脚本来实现。
First generate the .dot file:
bitbake -g core-image-minimal
To find out why a package is being built:
%(prog)s -k <package> -w ./task-depends.dot
To find out what a package depends on:
%(prog)s -k <package> -d ./task-depends.dot
Reduce the .dot file packages only, no tasks:
%(prog)s -r ./task-depends.do
该脚本支持的如下功能:
- 查找某个package为什么会被编译
- 查找某个package依赖了什么
- 从dot文件中移除task
python3 oe-depends-dot.py -r ./task-depends.dot
执行之后,会生成一个精简版本的dot文件task-depends-reduced.dot
,将原始的task依赖转变为package依赖,且将有些复杂的依赖关系进行了简化,比如 如果A->B, B->C, A->C, 那么 A->C 可以直接移除。这样大大减小了dot文件的大小,加速了后续dot转化为png的过程。
如下是简化后的文件的部分内容
digraph depends {
...
"acl-native" -> "attr-native"
"gtk-doc-native" -> "pkgconfig-native"
"lib32-binutils-cross-arm" -> "zlib-native"
"lib32-binutils-cross-arm" -> "bison-native"
"lib32-binutils-cross-arm" -> "pkgconfig-native"
"lib32-ca-certificates" -> "openssl-native"
"lib32-ca-certificates" -> "opkg-utils-native"
"lib32-ca-certificates" -> "lib32-openssl"
"lib32-ca-certificates" -> "debianutils-native"
"lib32-curl" -> "lib32-ca-certificates"
"lib32-curl" -> "lib32-libidn2"
"lib32-curl" -> "lib32-zlib"
"lib32-gcc-cross-arm" -> "zstd-native"
"lib32-gcc-cross-arm" -> "libmpc-native"
...
}
精简后的dot文件生成图片的效果。
dot -Tpng task-depends-reduced.dot -o task-depends-reduced-output.png
这里生成的图形依赖比较复杂,包含了诸多我们不需要关注的部分,比如一些native组件。移除dot文件中的native的行,结果:
sed '/native/d' task-depends-reduced.dot > task-depends-reduced1.dot
对于一个较小的组件,通过这种方式,可以得到清晰准确的package依赖链,但是对于依赖复杂的组件,通常生成的dot复杂,以至于不能渲染成图片。因此开发了一个新的工具,在以上的基础上做了一些精简:
- 使用package黑名单,移除不需要关注的package
- 改变链表渲染方向,改为从左到右
- 使用一个集合来记录已添加的依赖,确保每个 package 只被添加一次。构建依赖关系时,优先添加靠近根节点的依赖。
使用脚本
python3 pn-depends.py
自动生成lib32-curldependency_chain.png
最终效果为
5. 我被谁依赖
经常在项目中,遇到有些非期望的目标,我们想知道它是因为怎样的依赖关系,才被添加到编译系统的。此时可以通过这种方式
%(prog)s -k <package> -w ./task-depends.dot
但是需要注意的是,这里的task-depends.dot
需要编译最终的目标来生成
# python3 oe-depends-dot.py -k lib32-curl -w ./task-depends.dot
Because: lib32-socl-system-server lib32-dtvkit-release-prebuilt lib32-gstreamer1 lib32-packagegroup-amlogic-baserootfs lib32-playready lib32-oem-generic-mediaclient-image lib32-socl-platformserver lib32-cobalt-browser lib32-gst-agmplayer lib32-gst-socl-drm-plugins lib32-gst-socl-drmbufferpool-plugins lib32-gst-plugin-socl-asink lib32-gst-plugin-socl-demux lib32-gst-plugin-socl-subtitlesink lib32-gst-plugin-socl-v4l2dec lib32-gst-plugin-video-sink lib32-socl-player-service lib32-socl-appmanager lib32-socl-dial lib32-socl-system-app lib32-libvideorender
lib32-oem-generic-mediaclient-image -> lib32-packagegroup-amlogic-baserootfs -> lib32-socl-system-app -> lib32-socl-system-server -> lib32-curl
lib32-oem-generic-mediaclient-image -> lib32-packagegroup-amlogic-baserootfs -> lib32-dtvkit-release-prebuilt -> lib32-curl
lib32-oem-generic-mediaclient-image -> lib32-packagegroup-amlogic-baserootfs -> lib32-socl-dial -> lib32-socl-appmanager -> lib32-socl-platformserver -> lib32-gst-plugin-socl-asink -> lib32-gstreamer1 -> lib32-curl
lib32-oem-generic-mediaclient-image -> lib32-packagegroup-amlogic-baserootfs -> lib32-cobalt-browser -> lib32-socl-appmanager -> lib32-socl-platformserver -> lib32-gst-plugin-socl-asink -> lib32-gstreamer1 -> lib32-curl
lib32-oem-generic-mediaclient-image -> lib32-packagegroup-amlogic-baserootfs -> lib32-libvideorender -> lib32-socl-appmanager -> lib32-socl-platformserver -> lib32-gst-plugin-socl-asink -> lib32-gstreamer1 -> lib32-curl
lib32-oem-generic-mediaclient-image -> lib32-packagegroup-amlogic-baserootfs -> lib32-socl-player-service -> lib32-gst-agmplayer -> lib32-gst-socl-drm-plugins -> lib32-gstreamer1 -> lib32-curl
lib32-oem-generic-mediaclient-image -> lib32-packagegroup-amlogic-baserootfs -> lib32-gst-plugin-socl-demux -> lib32-gst-socl-drm-plugins -> lib32-gstreamer1 -> lib32-curl
lib32-oem-generic-mediaclient-image -> lib32-packagegroup-amlogic-baserootfs -> lib32-gst-plugin-socl-v4l2dec -> lib32-gst-socl-drmbufferpool-plugins -> lib32-gstreamer1 -> lib32-curl
lib32-oem-generic-mediaclient-image -> lib32-packagegroup-amlogic-baserootfs -> lib32-gst-plugin-video-sink -> lib32-gst-socl-drmbufferpool-plugins -> lib32-gstreamer1 -> lib32-curl
lib32-oem-generic-mediaclient-image -> lib32-packagegroup-amlogic-baserootfs -> lib32-gst-plugin-socl-subtitlesink -> lib32-gstreamer1 -> lib32-curl
lib32-oem-generic-mediaclient-image -> lib32-packagegroup-amlogic-baserootfs -> lib32-playready -> lib32-curl
生成图片
dot -Tpng dependency_graph.dot -o dependency_graph-output.png