在yocto中使用kylin源生成rootfs(准备篇)

yocto生成rootfs的流程

总体.png
第一阶段.png
第二阶段.png

头一张图是rootfs生成的总体流程;后两张图是按阶段描述:

  • 第一阶段 —— 生成deb包(只考虑deb);生成的deb包(们)被称为Feeds。这阶段与这关系不大,不细看了。
  • 第二阶段 —— 使用Feeds,生成rootfs。细看着一块。图上包含了do_image相关内容,这块与这关系不大,也不看;只看do_rootfs部分。

不贴太多代码,拣关键的贴:

_create的实现

123 class PkgRootfs(DpkgOpkgRootfs):
124     def __init__(self, d, manifest_dir, progress_reporter=None, logcatcher=None):
125     ¦   super(PkgRootfs, self).__init__(d, progress_reporter, logcatcher)

132     ¦   bb.utils.remove(self.image_rootfs, True)
133     ¦   bb.utils.remove(self.d.getVar('MULTILIB_TEMP_ROOTFS'), True)
134     ¦   self.manifest = PkgManifest(d, manifest_dir)
135     ¦   self.pm = DpkgPM(d, d.getVar('IMAGE_ROOTFS'),
136     ¦   ¦   ¦   ¦   ¦   ¦d.getVar('PACKAGE_ARCHS'),
137     ¦   ¦   ¦   ¦   ¦   ¦d.getVar('DPKG_ARCH'))

140     def _create(self):
141     ¦   pkgs_to_install = self.manifest.parse_initial_manifest()
144 
145     ¦   alt_dir = self.d.expand("${IMAGE_ROOTFS}/var/lib/dpkg/alternatives")
146     ¦   bb.utils.mkdirhier(alt_dir)
147 
148     ¦   # update PM index files
149     ¦   self.pm.write_index()
152 
158     ¦   self.pm.update()
162 
163     ¦   for pkg_type in self.install_order:
164     ¦   ¦   if pkg_type in pkgs_to_install:
165     ¦   ¦   ¦   self.pm.install(pkgs_to_install[pkg_type],
166     ¦   ¦   ¦   ¦   ¦   ¦   ¦   [False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY])
167     ¦   ¦   ¦   self.pm.fix_broken_dependencies()
173 
174     ¦   self.pm.install_complementary()
178 
179     ¦   self._setup_dbg_rootfs(['/var/lib/dpkg'])
180 
181     ¦   self.pm.fix_broken_dependencies()
182 
183     ¦   self.pm.mark_packages("installed")
184 
185     ¦   self.pm.run_pre_post_installs()

主要是2个对象:

  1. self.manifest = PkgManifest
  2. self.pm = DpkgPM(

manifest的作用是得到需要安装的包列表 pkgs_to_install = self.manifest.parse_initial_manifest()

剩下基本就是pm的操作:

  1. self.pm.write_index()
  2. self.pm.update()
  3. self.pm.install(pkgs_to_install[pkg_type], [False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY])
  4. self.pm.fix_broken_dependencies()
  5. self.pm.install_complementary()
  6. self.pm.mark_packages("installed")
  7. self.pm.run_pre_post_installs()

这是抽象类中的实现流程,确认看到 self.pm.install就可以了;该流程中最重要的对象就是DpkgPM,下面重点看这个类。

indexer

self.pm.write_index()

166 class DpkgPM(OpkgDpkgPM):
167     def __init__(self, d, target_rootfs, archs, base_archs, apt_conf_dir=None, deb_repo_workdir="oe-rootfs-repo", filterbydependencies=True):
....
189     ¦   self.indexer = DpkgIndexer(self.d, self.deploy_dir)

336          This function creates the index files 
337     def write_index(self):                                                                                                                                                                              
338     ¦   self.deploy_dir_lock()
339     
340     ¦   result = self.indexer.write_index()
341 
342     ¦   self.deploy_dir_unlock()
343 
344     ¦   if result is not None:
345     ¦   ¦   bb.fatal(result)

这里看出,DpkgIndexer是一个很重要的类,在看PM之前先看这个

  9 class DpkgIndexer(Indexer):
 10     def _create_configs(self):
 11     ¦   bb.utils.mkdirhier(self.apt_conf_dir)
 12     ¦   bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "lists", "partial"))
 13     ¦   bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "apt.conf.d"))
 14     ¦   bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "preferences.d"))
 15     
 16     ¦   with open(os.path.join(self.apt_conf_dir, "preferences"),
 17     ¦   ¦   ¦   "w") as prefs_file:
 18     ¦   ¦   pass
 19     ¦   with open(os.path.join(self.apt_conf_dir, "sources.list"),
 20     ¦   ¦   ¦   "w+") as sources_file:
 21     ¦   ¦   pass
 22     
 23     ¦   with open(self.apt_conf_file, "w") as apt_conf:
 24     ¦   ¦   with open(os.path.join(self.d.expand("${STAGING_ETCDIR_NATIVE}"),
 25     ¦   ¦   ¦   "apt", "apt.conf.sample")) as apt_conf_sample:
 26     ¦   ¦   ¦   for line in apt_conf_sample.read().split("\n"):
 27     ¦   ¦   ¦   ¦   line = re.sub(r"#ROOTFS#", "/dev/null", line)
 28     ¦   ¦   ¦   ¦   line = re.sub(r"#APTCONF#", self.apt_conf_dir, line)
 29     ¦   ¦   ¦   ¦   apt_conf.write(line + "\n")

 31     def write_index(self):
 32     ¦   self.apt_conf_dir = os.path.join(self.d.expand("${APTCONF_TARGET}"),
 33     ¦   ¦   ¦   "apt-ftparchive")
 34     ¦   self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
 35     ¦   self._create_configs()
 36 
 37     ¦   os.environ['APT_CONFIG'] = self.apt_conf_file
 38 
 39     ¦   pkg_archs = self.d.getVar('PACKAGE_ARCHS')
 40     ¦   if pkg_archs is not None:
 41     ¦   ¦   arch_list = pkg_archs.split()
 42     ¦   sdk_pkg_archs = self.d.getVar('SDK_PACKAGE_ARCHS')
 43     ¦   if sdk_pkg_archs is not None:
 44     ¦   ¦   for a in sdk_pkg_archs.split():
 45     ¦   ¦   ¦   if a not in pkg_archs:
 46     ¦   ¦   ¦   ¦   arch_list.append(a)
 47 
 48     ¦   all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
 49     ¦   arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in arch_list)
 50 
 51     ¦   apt_ftparchive = bb.utils.which(os.getenv('PATH'), "apt-ftparchive")
 52     ¦   gzip = bb.utils.which(os.getenv('PATH'), "gzip")
 53 
 54     ¦   index_cmds = []
 55     ¦   deb_dirs_found = False
 56     ¦   for arch in arch_list:
 57     ¦   ¦   arch_dir = os.path.join(self.deploy_dir, arch)
 58     ¦   ¦   if not os.path.isdir(arch_dir):
 59     ¦   ¦   ¦   continue
 60 
 61     ¦   ¦   cmd = "cd %s; PSEUDO_UNLOAD=1 %s packages . > Packages;" % (arch_dir, apt_ftparchive)
 62 
 63     ¦   ¦   cmd += "%s -fcn Packages > Packages.gz;" % gzip
 64 
 65     ¦   ¦   with open(os.path.join(arch_dir, "Release"), "w+") as release:
 66     ¦   ¦   ¦   release.write("Label: %s\n" % arch)
 67 
 68     ¦   ¦   cmd += "PSEUDO_UNLOAD=1 %s release . >> Release" % apt_ftparchive
 69 
 70     ¦   ¦   index_cmds.append(cmd)
 71 
 72     ¦   ¦   deb_dirs_found = True
 73 
 74     ¦   if not deb_dirs_found:
 75     ¦   ¦   bb.note("There are no packages in %s" % self.deploy_dir)
 76     ¦   ¦   return
 77 
 78     ¦   oe.utils.multiprocess_launch(create_index, index_cmds, self.d)
 79     ¦   if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
 80     ¦   ¦   raise NotImplementedError('Package feed signing not implementd for dpkg')

这段看完,知道 是在目录 self.deploy_dir/arch创建apt本地仓库(其中deploy_dir后续再说)。即self.pm.write_index()就是在初始化apt本地仓库。

DpkgPM

166 class DpkgPM(OpkgDpkgPM): 
167     def __init__(self, d, target_rootfs, archs, base_archs, apt_conf_dir=None, deb_repo_workdir="oe-rootfs-repo", filterbydependencies=True):
168     ¦   super(DpkgPM, self).__init__(d, target_rootfs)
169     ¦   self.deploy_dir = oe.path.join(self.d.getVar('WORKDIR'), deb_repo_workdir)
170     
171     ¦   create_packages_dir(self.d, self.deploy_dir, d.getVar("DEPLOY_DIR_DEB"), "package_write_deb", filterbydependencies)
172 
173     ¦   if apt_conf_dir is None:
174     ¦   ¦   self.apt_conf_dir = self.d.expand("${APTCONF_TARGET}/apt")
175     ¦   else:
176     ¦   ¦   self.apt_conf_dir = apt_conf_dir
177     ¦   self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
178     ¦   self.apt_get_cmd = bb.utils.which(os.getenv('PATH'), "apt-get")
179     ¦   self.apt_cache_cmd = bb.utils.which(os.getenv('PATH'), "apt-cache")
180 
181     ¦   self.apt_args = d.getVar("APT_ARGS")
182 
183     ¦   self.all_arch_list = archs.split()
184     ¦   all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
185     ¦   self.all_arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in self.all_arch_list)
186 
187     ¦   self._create_configs(archs, base_archs)
188 
189     ¦   self.indexer = DpkgIndexer(self.d, self.deploy_dir)


380     def _create_configs(self, archs, base_archs):
381     ¦   base_archs = re.sub(r"_", r"-", base_archs)
382 
383     ¦   if os.path.exists(self.apt_conf_dir):
384     ¦   ¦   bb.utils.remove(self.apt_conf_dir, True)
385 
386     ¦   bb.utils.mkdirhier(self.apt_conf_dir)
387     ¦   bb.utils.mkdirhier(self.apt_conf_dir + "/lists/partial/")
388     ¦   bb.utils.mkdirhier(self.apt_conf_dir + "/apt.conf.d/")
389     ¦   bb.utils.mkdirhier(self.apt_conf_dir + "/preferences.d/")
390 
391     ¦   arch_list = []

414     ¦   arch_list.reverse()
415 
416     ¦   with open(os.path.join(self.apt_conf_dir, "sources.list"), "w+") as sources_file:
417     ¦   ¦   for arch in arch_list:
418     ¦   ¦   ¦   sources_file.write("deb [trusted=yes] file:%s/ ./\n" %
419     ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦  os.path.join(self.deploy_dir, arch))
420 
421     ¦   base_arch_list = base_archs.split()
422     ¦   multilib_variants = self.d.getVar("MULTILIB_VARIANTS");
423     ¦   for variant in multilib_variants.split():
424     ¦   ¦   localdata = bb.data.createCopy(self.d)
425     ¦   ¦   variant_tune = localdata.getVar("DEFAULTTUNE:virtclass-multilib-" + variant, False)
426     ¦   ¦   orig_arch = localdata.getVar("DPKG_ARCH")
427     ¦   ¦   localdata.setVar("DEFAULTTUNE", variant_tune)
428     ¦   ¦   variant_arch = localdata.getVar("DPKG_ARCH")
429     ¦   ¦   if variant_arch not in base_arch_list:
430     ¦   ¦   ¦   base_arch_list.append(variant_arch)
431                                                                                                                                                                                                         
432     ¦   with open(self.apt_conf_file, "w+") as apt_conf:
433     ¦   ¦   with open(self.d.expand("${STAGING_ETCDIR_NATIVE}/apt/apt.conf.sample")) as apt_conf_sample:
434     ¦   ¦   ¦   for line in apt_conf_sample.read().split("\n"):
435     ¦   ¦   ¦   ¦   match_arch = re.match(r"  Architecture \".*\";$", line)
436     ¦   ¦   ¦   ¦   architectures = ""
437     ¦   ¦   ¦   ¦   if match_arch:
438     ¦   ¦   ¦   ¦   ¦   for base_arch in base_arch_list:
439     ¦   ¦   ¦   ¦   ¦   ¦   architectures += "\"%s\";" % base_arch
440     ¦   ¦   ¦   ¦   ¦   apt_conf.write("  Architectures {%s};\n" % architectures);
441     ¦   ¦   ¦   ¦   ¦   apt_conf.write("  Architecture \"%s\";\n" % base_archs)
442     ¦   ¦   ¦   ¦   else:
443     ¦   ¦   ¦   ¦   ¦   line = re.sub(r"#ROOTFS#", self.target_rootfs, line)
444     ¦   ¦   ¦   ¦   ¦   line = re.sub(r"#APTCONF#", self.apt_conf_dir, line)
445     ¦   ¦   ¦   ¦   ¦   apt_conf.write(line + "\n")
446 
447     ¦   target_dpkg_dir = "%s/var/lib/dpkg" % self.target_rootfs
448     ¦   bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "info"))
449 
450     ¦   bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "updates"))

264     def update(self):
265     ¦   os.environ['APT_CONFIG'] = self.apt_conf_file
266 
267     ¦   self.deploy_dir_lock()
268 
269     ¦   cmd = "%s update" % self.apt_get_cmd
270 
271     ¦   try:
272     ¦   ¦   subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
273     ¦   except subprocess.CalledProcessError as e:
274     ¦   ¦   bb.fatal("Unable to update the package index files. Command '%s' "
275     ¦   ¦   ¦   ¦   ¦"returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
276 
277     ¦   self.deploy_dir_unlock()
278 
279     def install(self, pkgs, attempt_only=False):
280     ¦   if attempt_only and len(pkgs) == 0:
281     ¦   ¦   return
282 
283     ¦   os.environ['APT_CONFIG'] = self.apt_conf_file
284 
285     ¦   cmd = "%s %s install --allow-downgrades --allow-remove-essential --allow-change-held-packages --allow-unauthenticated --no-remove %s" % \
286     ¦   ¦   ¦ (self.apt_get_cmd, self.apt_args, ' '.join(pkgs))
287 
288     ¦   try:
289     ¦   ¦   bb.note("Installing the following packages: %s" % ' '.join(pkgs))
290     ¦   ¦   output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
291     ¦   ¦   bb.note(output.decode("utf-8"))
292     ¦   except subprocess.CalledProcessError as e:
293     ¦   ¦   (bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
294     ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦ "Command '%s' returned %d:\n%s" %
295     ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦ (cmd, e.returncode, e.output.decode("utf-8")))
296 
297     ¦   # rename *.dpkg-new files/dirs
298     ¦   for root, dirs, files in os.walk(self.target_rootfs):
299     ¦   ¦   for dir in dirs:
300     ¦   ¦   ¦   new_dir = re.sub(r"\.dpkg-new", "", dir)
301     ¦   ¦   ¦   if dir != new_dir:
302     ¦   ¦   ¦   ¦   bb.utils.rename(os.path.join(root, dir),
303     ¦   ¦   ¦   ¦   ¦   ¦   ¦ os.path.join(root, new_dir))
304 
305     ¦   ¦   for file in files:
306     ¦   ¦   ¦   new_file = re.sub(r"\.dpkg-new", "", file)
307     ¦   ¦   ¦   if file != new_file:
308     ¦   ¦   ¦   ¦   bb.utils.rename(os.path.join(root, file),
309     ¦   ¦   ¦   ¦   ¦   ¦   ¦ os.path.join(root, new_file))

这个大流程,简单的说就是配置apt-get apt-cache,其中source.list指向上面Indexer创建的本地apt仓库。
然后 apt-get update, apt-get intsall

本地仓库中的deb来自哪里。

def create_packages_dir(d, subrepo_dir, deploydir, taskname, filterbydependencies):
这个方法比较复杂,主要是里面的数据结构比较麻烦。
代码逻辑大致明白,结合着加log的方法,弄明白了这个方法到底完成了什么。

首先,传入的参数

  1. deploydir指向一个目录,该目录中存放了编译好的package;当前例子中该目录的值为/home/zhangsong/work/about_yocto/build_dir/build6/tmp/deploy/deb
  2. subrepo_dir指向一个目录,该目录构建了一个本地库(apt库);但是这个方法并未创建仓库,而是将仓库中所有的deb都方进来。当前例子中该目录的值为/home/zhangsong/work/about_yocto/build_dir/build6/tmp/work/raspberrypi4_64-poky-linux/core-image-minimal/1.0-r0/oe-rootfs-repo
    然后:这个方法是被do_rootfs调用的(简单的这么说),后续的逻辑简单的描述就是——do_rootfs所依赖的包(包含do_package_write_deb的recipe)编译打包产生的deb,都放在subrepo_dir中。效果如下:
zhangsong@zhangsong-ThinkPad-W541:~/work/about_yocto/build_dir/build6/tmp/work/raspberrypi4_64-poky-linux/core-image-minimal/1.0-r0/oe-rootfs-repo$ tree all/
all/
├── autoconf-archive_2021.02.19-r0_all.deb
├── autoconf-archive-doc_2021.02.19-r0_all.deb
├── ca-certificates_20211016-r0_all.deb
├── ca-certificates-dbg_20211016-r0_all.deb
├── ca-certificates-dev_20211016-r0_all.deb
├── ca-certificates-doc_20211016-r0_all.deb
├── Packages
├── Packages.gz
├── Release
├── run-postinsts_1.0-r10_all.deb
├── run-postinsts-dbg_1.0-r10_all.deb
├── run-postinsts-dev_1.0-r10_all.deb
├── tzdata_2021e-r0_all.deb
├── tzdata-africa_2021e-r0_all.deb
├── tzdata-americas_2021e-r0_all.deb
├── tzdata-antarctica_2021e-r0_all.deb
  ..............

总结

在创建rootfs的过程中,Package management起到了决定性作用(可以说PM创建了rootfs)。
分为以下几个步骤:

  1. 创建本地仓库目录以及将deb包放入目录中(分arch)——见create_packages_dir
  2. 使用indexer创建apt本地仓库。
  3. 创建apt-get配置文件,重点是sources.list指向上面创建的apt-get本地仓库。
  4. apt-get update
  5. apt-get install pkgs

再结合着开头的3张图:
1 创建Feeds —— 编译出deb包
2 PM开始工作。

思路设计

目的是,(全部/大部分)使用kylin源中的deb生成rootfs。
结合上面的分析,简单的说,将apt-get的本地仓库,换成kylinos的仓库即可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容