接上篇~
yocto生成rootfs的流程 (DEB为例):
生成DEB ----> 生成本地源 -----> 配置apt ------> apt update --------> apt install packages。
最后两步,是在rootfs之外执行的,看一下配置:
Dir "/"
{
State "var/lib/apt/"
{
Lists "/home/zhangsong/work/temp/apt-conf/lists/";
status "/home/zhangsong/work/temp/rootfs/var/lib/dpkg/status";
extended_states "/home/zhangsong/work/temp/rootfs/var/lib/apt/extended_states";
};
Cache "var/cache/apt/"
{
Archives "/home/zhangsong/work/temp/apt-conf/lists/";
pkgcache "";
srcpkgcache "";
};
Bin "/usr/bin/"
{
methods "/usr/lib/apt/methods/";
gzip "gzip";
dpkg "dpkg";
dpkg-source "dpkg-source";
dpkg-buildpackage "dpkg-buildpackage";
apt-get "apt-get";
apt-cache "apt-cache";
};
Etc "/home/zhangsong/work/temp/apt-conf"
{
Preferences "preferences";
};
Log "/home/zhangsong/work/temp/rootfs/var/log/apt";
};
APT
{
Install-Recommends "true";
Immediate-Configure "false";
Architectures {"amd64";};
Architecture "amd64";
Get
{
Assume-Yes "true";
};
};
Acquire
{
AllowInsecureRepositories "true";
};
DPkg::Options {"--instdir=/home/zhangsong/work/temp/rootfs";"--root=/home/zhangsong/work/temp/rootfs";"--admindir=/home/zhangsong/work/temp/rootfs/var/lib/dpkg";"--force-all";"-D=2000";"--no-debsig"};
DPkg::Path "/usr/sbin:/usr/bin:/sbin:/bin";
这是我模仿yocto创建的配置,但是使用的是host上的apt、dpkg等。
正常情况下,apt install 最终会调用dpkg将deb包解压到rootfs中并且会chroot
到rootfs中执行configure动作(执行preinst、postinst等);即configure动作时root为rootfs而不是当前shell的 /
。
但是yocto中提供的apt、dpkg,据观察(未在代码中确认)只做了解压动作,而未执行configure;configure动作是放在后面单独处理了:
158 ¦ bb.note("fucking life *** apt update ***")
159 ¦ self.pm.update()
165 ¦ for pkg_type in self.install_order:
166 ¦ ¦ if pkg_type in pkgs_to_install:
167 ¦ ¦ ¦ self.pm.install(pkgs_to_install[pkg_type],
168 ¦ ¦ ¦ ¦ ¦ ¦ ¦ [False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY])
169 ¦ ¦ ¦ self.pm.fix_broken_dependencies()
170
184 ¦ bb.note("fucking life *** apt fix_broken_dependencies ***")
185 ¦ self.pm.fix_broken_dependencies()
186
187 ¦ bb.note("fucking life *** apt mark_packages ***")
188 ¦ self.pm.mark_packages("installed")
189
190 ¦ bb.note("fucking life *** apt run_pre_post_installs ***")
191 ¦ self.pm.run_pre_post_installs()
192
193 ¦ bb.note("fucking life *** execute_pre_post_process ***")
194 ¦ execute_pre_post_process(self.d, deb_post_process_cmds)
241 ¦ os.environ['D'] = self.target_rootfs
242 ¦ os.environ['OFFLINE_ROOT'] = self.target_rootfs
243 ¦ os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
244 ¦ os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
245 ¦ os.environ['INTERCEPT_DIR'] = self.intercepts_dir
246 ¦ os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
247
248 ¦ for pkg_name in installed_pkgs:
249 ¦ ¦ for control_script in control_scripts:
250 ¦ ¦ ¦ p_full = os.path.join(info_dir, pkg_name + control_script.suffix)
251 ¦ ¦ ¦ if os.path.exists(p_full):
252 ¦ ¦ ¦ ¦ try:
253 ¦ ¦ ¦ ¦ ¦ bb.note("Executing %s for package: %s ..." %
254 ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦(control_script.name.lower(), pkg_name))
255 ¦ ¦ ¦ ¦ ¦ output = subprocess.check_output([p_full, control_script.argument],
256 ¦ ¦ ¦ ¦ ¦ ¦ ¦ stderr=subprocess.STDOUT).decode("utf-8")
257 ¦ ¦ ¦ ¦ ¦ bb.note(output)
258 ¦ ¦ ¦ ¦ except subprocess.CalledProcessError as e:
259 ¦ ¦ ¦ ¦ ¦ bb.warn("%s for package %s failed with %d:\n%s" %
260 ¦ ¦ ¦ ¦ ¦ ¦ ¦ (control_script.name, pkg_name, e.returncode,
261 ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ e.output.decode("utf-8")))
262 ¦ ¦ ¦ ¦ ¦ failed_postinsts_abort([pkg_name], self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
yocto这么做的原因(目前能看到的好处):
- 不用root权限。
- 不用chroot。
小结论:
这就确定了yocto原生逻辑不支持直接使用非yocto style package(即按照yocto规则生成的package)组成的源来制作rootfs。(准确的说是不保证做出来的rootfs没问题)
除非是按照yocto的规则改造,比如meta-debian中的做法,从新生成deb。
原因就是 postinst、preinst脚本的执行需要处于一个/
指向目标rootfs的环境,比如chroot rootfs。
现在我的做法
目前简单处理,直接使用dbootstrap了
17 do_rootfs[depends] += "virtual/fakeroot-native:do_populate_sysroot pbzip2-native:do_populate_sysroot"
18 fakeroot python do_rootfs () {
19 import subprocess, time
20
21 rootfs_dir = d.getVar('IMAGE_ROOTFS')
22
23 def exec_debootstrap():
24 ¦ newenv = os.environ.copy()
25 ¦ newenv['PATH'] = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin'
26 ¦ newenv['IMAGE_ROOTFS'] = rootfs_dir
27 ¦ cmd = "debootstrap --arch=amd64 --foreign --no-check-gpg --keep-debootstrap-dir --variant=minbase --components=main,restricted,universe,multiverse 10.1 {} http://172.29.220.242/kylin/KYLIN-ALL gutsy".format
28
30 ¦ bb.note("fucking life : %s" % cmd)
31 ¦ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,shell=True,env=newenv)
32
33 ¦ while True:
34 ¦ ¦ for line in iter(proc.stdout.readline, b''):
35 ¦ ¦ ¦ bb.note(line.decode('utf-8'))
36
37 ¦ ¦ try:
38 ¦ ¦ ¦ proc.wait(timeout=0.1)
39 ¦ ¦ ¦ break
40 ¦ ¦ except subprocess.TimeoutExpired:
41 ¦ ¦ ¦ pass
42
43
44 bb.note('fucking life : begin kylin::do_rootfs')
45
46 if not os.path.exists(rootfs_dir):
47 ¦ exec_debootstrap()
48 else:
49 ¦ bb.note("fucking life : rootfs already exists, skip exec_debootstrap !")
50
51 bb.note('fucking life : exit kylin::do_rootfs')
52 }
创建了kylinos-image类,修改了do_rootfs的逻辑,在$WORKDIR/rootfs目录中使用debootstrap生成一个rootfs —— 目前只用了debootstrap的第一阶段(已经是一个完整的可chroot环境)。
后续的处理沿用yocto image.bbclass,用来生成镜像。
延伸思考
其实是否用debootstrap不重要,重要的是:
- 提供一个chroot环境
- 该环境还要能够支撑后续要安装的包进行configure动作,这个是难点。
目前debootstrap第一阶段完成后,即使删除了cache,也有200M以上,太大。关键是很难裁减,因为 第二条
原因。
满足这2个条件的决定性因素实际上是源。