[原创]OpenEmbedded 中文手册

8.11 打包:定义包类及其内容

一个bitbake"配方"就是一组创建一个或者多个在目标设备上安装的包的命令集合.典型的包就是

.ipkg或者.deb包(尽管bitbake本身并没有跟任何特殊的包格式相关联).

默认情况下会自动生成一些包,这些包并没有包含任何"配方"作者要求的特定动作.上面的helloworld

例子的包输出信息就说明了这些:

[NOTE: package helloworld-0.1-r0: task do_package_write: started

NOTE: Not creating empty archive for helloworld-dbg-0.1-r0

Packaged contents of helloworld into /home/lenehan/devel/oe/build/titan-glibc-25/tmp/deploy/ipk/sh4/helloworld_0.1-r0_sh4.ipk

Packaged contents of helloworld-doc into /home/lenehan/devel/oe/build/titan-glibc-25/tmp/deploy/ipk/sh4/helloworld-doc_0.1-r0_sh4.ipk

NOTE: Not creating empty archive for helloworld-dev-0.1-r0

NOTE: Not creating empty archive for helloworld-locale-0.1-r0

NOTE: package helloworld-0.1-r0: task do_package_write: completed

我们可以看到上面的打包动作做了如下事情:

* 创建主包,helloworld_0.1-r0_sh4.ipk.这个包包含了helloworld的二进制文件

/usr/bin/helloworld.

*创建了文档包, helloworld-doc_0.1-r0_sh4.ipk.这个包包含了readme文件

/usr/share/doc/helloworld/README.txt.

*尝试创建调试包,helloworld-dbg-0.1-r0_sh4.ipk,一个开发包helloworld-dev-0.1-r0_sh4.ipk

和一个本地化(locale)包helloworld-locale-0.1-r0_sh4.ipk.但是这些包最终并没有创建,因为没有找到任何

应该属于该包的文件.

这里发生了几件需要重点理解的事情:

1.有一类默认的包会被尝试去创建.这些包在变量PAKAGES里控制.

2.对于每个包会默认认为有些文件是属于它的.比如文档包就会包含任何在/usr/share/doc里面的文件.这些文件和目录

在变量FILES_<packge-name>里控制.

3.默认情况下,不包含任何文件的空包是不会被创建的,也不会产生错误.要不要产生空包可以在变量ALLOW_EMPTY里来设置.

8.11.1 基本规则

 尽可能的分离包是OE的设计的一个高明之处.许多设备的内存和存储空间是有限的,对于发行者和用户来说可以选择不安装一个包的

 不需要的部分可以节省可观的存贮空间.

 比如没有哪个PC发行版会包含用户不需要使用的文档和开发库包(译者注:当然这个不怎么正确,文档包一般都有,lfs还默认安装开发库),

 因为用户的日常使用根本用不到这些东西.特别的,如果你的包提供多种二进制格式,用户可能一般会只选择其中一种,所以你应该分离这些包.

 默认情况下文件将会被自动分组而分离,包括:

 开发包(dev)

 任何开发才会需要的文件.这会包含头文件,静态链接库,动态链接库等.这些只会是想要在设备上编译程序的人才需要的.

 但是通常情况这种需要并不多,所以这些文件就会自动移动到分离的包(开发包).

 文档包(doc)

 任何文档相关文件,包括man手册页.这些文件只是处于提供帮助信息的目的.对于大多数嵌入式设备并没有让用户去查看文档

 的方式或者需要.不过文档文件比较占用空间.分离出来这些就可以节省空间,如果用户需要查看也可以选择自己安装.

 本地化包(locale)

 本地化信息是软件包的翻译信息.许多用户不需要这些翻译,许多设备只是想提供用户界面相关的组件的翻译信息但不是系统

 程序的,分离这部分出来然后用户可以选择他们到底需不需要.

 8.11.2.默认包类和文件

默认的打包设置在conf/bitbake.conf里面定义,而且对于大多数"配方"来说这些设置是很合适的,下面的列表展示了一些默认的

   打包相关变量的值:

   PACKAGES

  这个变量列出了每个软件包都需要创建那些包:

  PACKAGES = "${PN}-dbg ${PN} ${PN}-doc ${PN}-dev ${PN}-locale"

  注意,包的顺序是很重要的:这些包是按照数序来创建的,所以如果两种包同时指定了一个文件,那么按照顺序的第一种包将包含这个文件.

  这在使用通配符来指定包内容的时候就很重要了.

  例如,如果主包${PN}包含了/usr/bin/*(/usr/bin下面的所有文件),但是你又希望/usr/bin/tprogram到一个独立的包

  ${PN}-tpackage里面,你需要把在PACKAGES变量里设置的${PN}-tpackage安排在${PN}的前面,或者编辑FILES_${PN}

  的通配符叫它不要匹配到/usr/bin/tprogram文件.

  注意-dbg包包含了从二进制和库文件里过滤出来的调试信息.这个类型的包应该永远设置优先与其他的包以保证调试信息能正确的

  提取出来.

   FILES_${PN}

  主包,包含了一切在设备上运行程序需要的东西.

  FILES_${PN} = "\

    ${bindir}/* \

    ${sbindir}/* \

    ${libexecdir}/* \

${libdir}/lib*.so.* \

    ${sysconfdir} \

    ${sharedstatedir} \

    ${localstatedir} \

    /bin/* \

    /sbin/* \

    /lib/*.so* \

    ${datadir}/${PN} \

    ${libdir}/${PN}/* \

    ${datadir}/pixmaps \

    ${datadir}/applications \

    ${datadir}/idl \

    ${datadir}/omf \

    ${datadir}/sounds \

    ${libdir}/bonobo/servers"

    FILES_${PN}-dbg

 从为过滤的库和可执行文件里提取的调试信息.OE会自动的提取调试信息到.debug文件夹里然后过滤原文件.

    FILES_${PN}-dbg = "\

    ${bindir}/.debug \

    ${sbindir}/.debug \

    ${libexecdir}/.debug \

    ${libdir}/.debug \

    /bin/.debug \

    /sbin/.debug \

  /lib/.debug \

    ${libdir}/${PN}/.debug"

    FILES_${PN}-doc

 文档相关文件.所有文档将会自动的被分离到它自己的文档包里,除非需要,文档默认将不会被安装.

    FILES_${PN}-doc = "\

    ${docdir} \

    ${mandir} \

    ${infodir} \

    ${datadir}/gtk-doc \

    ${datadir}/gnome/help"

    FILES_${PN}-dev

    开发相关的文件.任何头文件,库文件或者支持开发用的文件.

    FILES_${PN}-dev = "\

    ${includedir} \

    ${libdir}/lib*.so \

    ${libdir}/*.la \

    ${libdir}/*.a \

    ${libdir}/*.o \

    ${libdir}/pkgconfig \

    /lib/*.a \

    /lib/*.o \

    ${datadir}/aclocal"

    FILES_${PN}-locale

    本地化相关文件.

    FILES_${PN}-locale = "${datadir}/locale"

8.11.3.通配符

 FILES变量的通配符是通过python的fnmatch函数来处理的.下面是这个函数的一些相关注意事项:

    * /<dir>/*: 这个会匹配到所有在dir下面的目录和文件.

    * /<dir>/a*: 这个只会匹配到文件,不包含目录.

    * /dir: 这个会包含dir目录,当然会包含dir下面的一切子目录和文件.

注意,包类的前后顺序将会影响通配符的匹配效果,假设我们有三个二进制文件在/usr/bin/目录下面,

   然后我们想要test程序到一个单独的包里面:

   /usr/bin/programa /usr/bin/programb /usr/bin/test

   所以,我们定义一个新的包然后告诉bitbake在这个包里包含/usr/bin/test.

   FILES-${PN}-test = "${bindir}/test"

   PACKAGES += "FILES-${PN}-test"

   这样打包时其实${PN}-test 包不会产生,原因是 PACKAGES变量现在看起来是这样了:

   {PN}-dbg ${PN} ${PN}-doc ${PN}-dev ${PN}-locale ${PN}-test

注意到${PN}在${PN}-test的前面,然后我们看到FILES-${PN}里面定义了${bindir}/*通配符,

所以,${PN}会第一个匹配到它然后它会被移动到${PN}包里而不是${PN}-test包.

   想要做到预期的效果我们有两种选择:

  1.编辑${PN}的设置不让它匹配到test程序.

   比如我们可以这样做

   FILES-${PN} = "${bindir}/p*"

   所以,现在这将只会匹配到p开头的文件,不会包含test程序.注意,FILES-${PN}通常

   会包含很多通配符的设置项,然后会包含很多其他的文件进来,这个例子里我们并没有其

   他文件,所以这么简单的定义是可行的.

  2. 编辑${PN}-test包以使它在前面.

   最显而易见的办法是把${PN}-test包"前加"(作为前缀)到PACKAGES变量里面:

   PACKAGES =+ "FILES-${PN}-test"

   有时候这样会工作的很好,但是这里有了一个问题,这个包现在处于-dbg包的前面,这将

   导致.debug目录包含进包里面(译者注:官方文档这里貌似弄错了,其实test包如果在前面,

   意味着在过来debug信息之前test程序已经被移动了,所以只会导致对应的.debug目录没有

   被创建而不是包含到了里面,可能是exclude被误写为include了.)

   通常情况下最常用的办法可能是重新定义PACKAGES变量了:

   PACKAGES = "${PN}-dbg ${PN}-test ${PN} ${PN}-doc ${PN}-dev ${PN}-locale"

8.11.4.查看包

在开发"配方"的过程中如果能检查和确认那个文件进了那个包,或者那个包没有创建,那个包里没有文件之类的应该

是非常之有用的.

其中之一最简单的办法就是在install目录里面运行find命令.在install目录里面每个包都会有一个子目录,然后

文件也被移动到了相关匹配的包里.下面的例子展示了这些:

$ find tmp/work/helloworld-0.1-r0/install

tmp/work/helloworld-0.1-r0/install

tmp/work/helloworld-0.1-r0/install/helloworld-locale

tmp/work/helloworld-0.1-r0/install/helloworld-dbg

tmp/work/helloworld-0.1-r0/install/helloworld-dev

tmp/work/helloworld-0.1-r0/install/helloworld-doc

tmp/work/helloworld-0.1-r0/install/helloworld-doc/usr

tmp/work/helloworld-0.1-r0/install/helloworld-doc/usr/share

tmp/work/helloworld-0.1-r0/install/helloworld-doc/usr/share/doc

tmp/work/helloworld-0.1-r0/install/helloworld-doc/usr/share/doc/helloworld

tmp/work/helloworld-0.1-r0/install/helloworld-doc/usr/share/doc/helloworld/README.txt

tmp/work/helloworld-0.1-r0/install/helloworld

tmp/work/helloworld-0.1-r0/install/helloworld/usr

tmp/work/helloworld-0.1-r0/install/helloworld/usr/bin

tmp/work/helloworld-0.1-r0/install/helloworld/usr/bin/helloworld

$

上面的信息说明-local,-dbg,-dev包是空包,然后-doc和主包都各自包含了一个文件.使用"-type f"选项

可以只显示文件,这样看起来更清晰.

相比较之下,image目录(也就是所谓的目标目录D)将会包含任何没有打包的文件:

$ find tmp/work/helloworld-0.1-r0/image

tmp/work/helloworld-0.1-r0/image

tmp/work/helloworld-0.1-r0/image/usr

tmp/work/helloworld-0.1-r0/image/usr/bin

tmp/work/helloworld-0.1-r0/image/usr/share

tmp/work/helloworld-0.1-r0/image/usr/share/doc

tmp/work/helloworld-0.1-r0/image/usr/share/doc/helloworld

$

这样一来,所有被打包了的文件都不会留到这里,使用"-type f"更清晰些:

$ find tmp/work/helloworld-0.1-r0/image -type f

$

bitbake也会在打包过程中警示没有被打包进的文件:

NOTE: package helloworld-0.1-r0: task do_package: started

NOTE: the following files were installed but not shipped in any package:

NOTE:   /usualdir/README.txt

NOTE: package helloworld-0.1-r0: task do_package: completed

除非是不正常的情况,否则,一个"配方"应该不留下任何没有被打包的文件.

8.11.5.不需要的文件

OE里没有明确的支持不包含文件的方法.你可以简单的留到包外,但是你会得到警告或者错误(如果

你做完整的包检查的话),而且其他人或许会知道你存心没有打包某个文件.

在某些时候或许在安装任务结束后手动删除不需要的文件要相对容易些.下面从samba配方里截取的例子

显示了在用autotools类安装任务完成之后删除一些文件.这里是在autotools产生的install任务之后

运行do_install_append命令:

do_install_append() {

   ...

   rm -f ${D}${bindir}/*.old

   rm -f ${D}${sbindir}/*.old

   ...

}

8.11.6.debian命名机制

打包的时候应用一种特殊的debian库命名规则.如果启用了,包会被按照debian的习惯重新命名.

debian命名方式可以通过在local.conf文件或者您的发行版的配置文件里包含debian类来实现:

INHERIT += "debian"

根据规则,包会被根据查看共享库的名字和版本自动重命名为<libname><lib-major-version>

的样子.比如,包名为foo,然后这个包里包含了一个libfoo.so.1.2.3这样的文件,那么包将会被

自动重命名为libfoo1.

如果我们查看lzo_1.08.bb的配方,当前的发布号14,它产生了一个单个共享库文件:

$ find tmp/work/lzo-1.08-r14/install/

tmp/work/lzo-1.08-r14/install/lzo

tmp/work/lzo-1.08-r14/install/lzo/usr

tmp/work/lzo-1.08-r14/install/lzo/usr/lib

tmp/work/lzo-1.08-r14/install/lzo/usr/lib/liblzo.so.1

tmp/work/lzo-1.08-r14/install/lzo/usr/lib/liblzo.so.1.0.0

如果不使用debian命名方式,包名字会叫做lzo_1.08-r14_sh4.ipk(然后对应的dev和dbg包就会

是lzo-dbg_1.08-r14_sh4.ipk 和 lzo-dev_1.08-r14_sh4.ipk),但是,如果使用了debian

命名机制,根据liblzo.so.1.0.0包就lzo会被重命名为liblzo1:

$ find tmp/deploy/ipk/ -name '*lzo*'

tmp/deploy/ipk/sh4/liblzo1_1.08-r14_sh4.ipk

tmp/deploy/ipk/sh4/liblzo-dev_1.08-r14_sh4.ipk

tmp/deploy/ipk/sh4/liblzo-dbg_1.08-r14_sh4.ipk

这里有一些变量会影响debian重命名类的具体操作:

LEAD_SONAME

如果一个包实际包含了多重共享库,那么其中一个会被自动选择,然后产生一个警告.

这个变量是一个正则表达式,用来选择那些共享库是debian重命名可用的.

DEBIAN_NOAUTONAME_<pkgname>

如果这个变量的值设置为1,那么该包就不会自动使用debian命名机制.

AUTO_LIBNAME_PKGS

设置那些包会应用debian命名机制.这个选项用来设置不需要所有包都使用debian

命名机制的情况.

8.11.7.空包

默认情况下空包是被忽略的.或许您可能希望去创建空包,典型的应用就是你可能需要创建一个虚拟包

然后里面只有依赖关系的情况.ALLOW_EMPTY变量是用来控制产不产生空包的:

ALLOW_EMPTY

控制产不产生空包.默认这个设置是0,空包不会被创建,设置为1就允许创建不包含文件的空包.

8.12任务:用“任务“工作

bitbake构建(运行)"配方"的时候是一系列的"任务".有时候你需要明确地定义一个类到底做什么,

比如提供do_install函数来替换"配方"里默认的install"任务",有时候这些由最常用的类来提供,比如,

autotools类就会有configure,compile,install"任务"的默认替代实现.

有几种方式可以改写要运行的任务:

重载默认的任务实现:

定义你自己的任务实现会覆盖(重载)任何默认的或者类里面的原有实现.

比如,你可以定义你自己的compile任务实现来重载默认的:

do_compile() {

      oe_runmake DESTDIR=${D}

}

如果你是希望阻止一个任务的执行,你可以定义你自己的空的实现,通常就是定义一个

使用冒号的任务:

do_configure() {

    :

}

前加或者追加到任务:

有时候你需要默认的实现,不过你需要一些额外的功能.这个就可以通过在任务的实现上

前加或者追加你附加的功能来达到.

下面的例子是想在autotool默认install任务后安装一个可能因为什么原因没有安装的附加文件的情况:

do_install_append() {

       install -d ${D}${datadir}

       install -m 0655 units.dat ${D}${datadir}

}

下面的这个从cherokee配方里提取的例子说明了如何在install任务之前增加某个功能的情况.在这个例子

中它编译了一个一个在主机(host)上运行的本地测试程序.如果没有这个程序,后面autotools类里的install

任务会失败,因为默认的安装会在主机上运行原本给目标(target)机器编译的程序.

do_install_prepend () {

        # It only needs this app during the install, so compile it natively

        $BUILD_CC -DHAVE_SYS_STAT_H -o cherokee_replace cherokee_replace.c

}

定义一个新的任务:

另外一个选择是定义一个全新的任务,然后用bitbake注册这个任务,把它放到两个已存在的任务之间.

下面的示例情况是需要从一个解压的目录拷贝cvs的树,然后这个必须在添加任何本地补丁之前完成,

所以我们定义了一个新的任务来完成这件事情,然后这个任务被注册到了已存在的unpack和patch任务之间.

do_unpack_extra(){

    cp -pPR ${WORKDIR}/linux/* ${S}

}

addtask unpack_extra after do_unpack before do_patch

注意:

addtask里面并没有在unpack_extra前面加do,但是任务名称一定要加do,如果addtask里的也加了

do或者错了,不会产生任何错误,然后也不会实际运行.

使用重载:

重载在上面已经讲述过了.不过重载还允许对于特定的目标机器,发行版,平台来实现.

这个不怎么常用.下面从udev里来的例子是想要只针对h2200机器来给install任务追加一个附加安装的文件:

do_install_append_h2200() {

    install -m 0644 ${WORKDIR}/50-hostap_cs.rules ${D}${sysconfdir}/udev/rules.d/50-hostap_cs.rules

}

8.13类:分离常用的功能

通常一个特定的模式会在不止一个"配方"里使用,或者有时候需要用基于python的复杂的函数功能来达到某种需要.通常

这就是通过类来实现的.类是的实现可以在OE代码的classes子目录里找到.

对类及其功能要引起足够的重视,因为类可以:

* 节省开发者的时间,要不然这些事情就需要自己来做了;

* 因为太多的东西在后台执行,好多配方就理解起来很困难了,除非你自己很清楚类也知道它们如何工作;

* 更多事情是如何继续的细节可以通过了解类的实现来理解.

类是通过继承(inherit)方法使用的.下面从curl"配方"来的例子就使用了3个类:

inherit autotools pkgconfig binconfig

这里使用了三个单独的类服务:

autotools

autotools 类用于使用GNU配置(autoconf)工具来进行配置和编译的软件.

pkgconfig

pkgconfig 类用于处理.pc文件,这些程序会使用pkg-config来为想要链接它的软件提供信息.

binconfig

binconfig 类用于处理<name>-config文件,这些程序使用这个程序来对要链接它的软件提供信息.

每个类都是在classes子目录里实现的,名字为<classname>.bbclass,这些类的特定细节需要进一步讨论,不过

有时候想要理解整个类并不那么容易.一些类的细节在本手册中有讲述.

8.14暂存:为编译配置头文件和库

暂存是对是对一些文件的中间处理以让它们对于其他"配方"来说可用,比如头文件和库.这个跟安装不同,因为安装是为了

打包准备的.暂存是为了在主机上准备好东西以备后面编译程序之用.

这里以bzip2为例,你可以看到它暂存了头文件和库:

do_stage () {

    install -m 0644 bzlib.h ${STAGING_INCDIR}/

    oe_libinstall -a -so libbz2 ${STAGING_LIBDIR}

}

oe_libinstall方法在方法一节来描述.它用来安装库文件(这里是到暂存目录).暂存相关的变量会自动定义为正确的

暂存位置.这里我们使用的是主暂存变量:

STAGING_INCDIR

这里是暂存头文件要安装到的位置.这相当于标准的/usr/include目录.

STAGING_LIBDIR

这个是暂存库文件安装的位置.相当于标准的/usr/lib目录.

其他的暂存相关变量在 9章的暂存目录一节里有描述.

在tmp下面查看staging目录你可以看到bzip2配方执行后的效果:

$ find tmp/staging -name '*bzlib*'

tmp/staging/sh4-linux/include/bzlib.h

$ find tmp/staging -name '*libbz*'

tmp/staging/sh4-linux/lib/libbz2.so

tmp/staging/sh4-linux/lib/libbz2.so.1.0

tmp/staging/sh4-linux/lib/libbz2.so.1

tmp/staging/sh4-linux/lib/libbz2.so.1.0.2

tmp/staging/sh4-linux/lib/libbz2.a

stage(暂存)的相关变量在编译其他软件包的时候也会使用,我们来看看gnupg配方历史用bzip2的相关项.

DEPENDS = "zlib bzip2"

...

EXTRA_OECONF = "--disable-ldap \

        --with-zlib=${STAGING_LIBDIR}/.. \

        --with-bzip2=${STAGING_LIBDIR}/.. \

        --disable-selinux-support"

bzip2在这里两处都有涉及:

DEPENDS

你应该还记得DEPENDS定义的是构建时依赖.这里bzip2暂存的头文件和库在编译gnupg时就要用到.

因此我们就需要确保bzip2配方运行过了而且相关文件也进了暂存目录,在DEPENDS里添加bzip2依赖

就会让这些事情就绪.

EXTRA_OECONF

这个变量会被autotools 类使用给configure脚本提供额外的配置参数.在gnupg里就是告诉bzip2

的头文件和目录在哪里.这个是通过--with-bzip2来实现的.这里就是指向头文件和库所在的目录.如果

没有这个选项,编译的时候gnupg的配置脚本就会尝试搜寻主机目录了.

记住,暂存是用来准备头文件,库给其他配方使用的,库和头文件是最常见的要暂存的东西了,有时候pkgconfig文件也要

暂存,对于本地包,二进制文件也需要暂存.

8.15自动配置:autotools主题

这一节需要等待完成:

* 关于构建autoconf软件包

* EXTRA_OECONF

* /usr/include,/usr/lib的问题.

* 配置以搜寻暂存区域.

* -L${STAGING_LIBDIR} vs ${TARGET_LDFLAGS}

* 网络资源

8.16安装脚本:运行脚本安装和移除包

打包系统比如.ipkg和.deb支持在软件安装或者移除的时候执行安装前和安装后,移除前和移除后脚本.

定义这些脚本就可以在合适的时间执行一些动作或者任务.通常的用法有:在安装的时候启动守护进程,

卸载的时候停止进程,在安装的时候创建用户和组.registering and unregistering

alternative implementations of commands and registering the need for volatiles.

支持的脚本包括:

preinst (安装前)

preinstall脚本是在安装包的内容前安装东西.在安装前包里的内容显然是不可用的.

安装前脚本通常不怎么使用.

postinst (安装后)

postinst脚本在安装了包的内容之后执行.在postinst期间,包的内容是可用的.通常

安装后脚本用来创建目录,注册守护进程,启动守护进程,修正SUID二进制文件等.

prerm

prerm在卸载安装包之前执行.这个时候包的内容仍然是可用的.

postrm

postrm在卸载包之后执行.这个时候包的内容已经不存在了,所以不能在脚本里面使用.postrm

通常用来做升级或者替换.(告知升级替换系统这个包不可用了,应该选择另外的包).

脚本的注册使用如下形式的函数:

pkg_<脚本名>_<包名>

在下面ndisc6的例子中,为ndisc6创建的三个包注册了posinst脚本:

# Enable SUID bit for applications that need it

pkg_postinst_${PN}-rltraceroute6 () {

    chmod 4555 ${bindir}/rltraceroute6

}

pkg_postinst_${PN}-ndisc6 () {

    chmod 4555 ${bindir}/ndisc6

}

pkg_postinst_${PN}-rdisc6 () {

    chmod 4555 ${bindir}/rdisc6

}

注意:

这些脚本是通过目标设备上的/bin/sh来执行的,通常就是典型的busybox sh,但是也可以是

bash或者其他sh兼容的shell.无论何时你都不应该在你的脚本里使用bash扩展特性.

注意有些类也会注册脚本.你声明的任何脚本都会在前面包含已经在类里面定义了的脚本.下面的类会产生

特殊的脚本内容:

update-rc.d

这个类是守护程序用来注册他们的启动和初始化脚本的.

具体细节在initscipts一节介绍.

module

这个类由linux内核模块使用.这个类在安装和卸载内核模块的时候调用depmod和update-modules

命令.

kernel

这个类由linux内核使用.在安装和卸载内核的时候有很多事情要做.这个类正是产生这些脚本的.

qpf

这个类用两个安装和卸载qpf字体.这个类注册了一些在安装和卸载字体时候升级字体路径和字体缓存信息的脚本.

update-alternatives

这个类在一个文件可以由几个包来提供的时候使用.它告诉系统有另外的一个包可以用.替换系统将会建立一个到

已有的正确替代品的链接.

gtk-icon-cache

这个类用来安装新的gtk图标.在安装和卸载包之后需要更新图标缓存.

gconf

<官方文档空白>

package

这个基础类由打包(packaging)类,比如.ipkg和.deb使用.这个类可能创建一些更新

动态链接缓存的脚本.

下面p3scan的示例说明了postinst脚本的用法.脚本创建了需要的用户和组,然后创建了一些临时目录.

最后脚本使用了update-rc.d类注册了一个启动脚本.然后启动对应的守护进程.(调用update-rc.d的

方法在alternatives一节里有描述)

inherit autotools update-rc.d

...

# Add havp's user and groups

pkg_postinst_${PN} () {

        grep -q mail: /etc/group || addgroup --system havp

        grep -q mail: /etc/passwd || \

            adduser --disabled-password --home=${localstatedir}/mail --system \

                    --ingroup mail --no-create-home -g "Mail" mail

        /etc/init.d/populate-volatile.sh update

}

一些脚本有如下的形式:

if [ x"$D" = "x" ]; then

    ...

fi

这是在测试安装目录D有没有定义,如果没有定义那么任何动作都不会执行.这种状况下安装目录就不会

创建.这个用法的主要目的是阻止程序在生成根文件系统的时候被安装.这种情况下脚本就不能运行因为

生成根文件系统是在主机上进行而不是在目标机器上进行的.如果一个包在安装根文件系统开始时候进行

那么就需要使用alterntive方法来执行一些特定的动作(比如在passwd和group文件里面包含

需要的用户和组).

8.17配置文件

作为包的一部分被安装的配置文件需要特殊的对待和处理.如果不做处理的话,一个包的新版本安装的时候会

覆盖原来的配置文件,这样原来用户自己做的配置将会丢失.

如果不希望发生这样的事情你需要告知包管理系统那些文件是配置文件.这些文件在安装覆盖的时候将会

询问用户做处理,比如下面的例子:

    Configuration file '/etc/p3scan/p3scan.conf'

    ==> File on system created by you or by a script.

    ==> File also in package provided by package maintainer.

       What would you like to do about it ?  Your options are:

        Y or I  : install the package maintainer's version

        N or O  : keep your currently-installed version

          D     : show the differences between the versions (if diff is installed)

     The default action is to keep your current version.

    *** p3scan.conf (Y/I/N/O/D) [default=N] ?

为了声明一个文件是配置文件.你需要在CONFFILES_<包名>变量里面用空格作为间隔列出那些配置文件.下面的clamav例子

里有两个文件被标记为配置文件.

CONFFILES_${PN}-daemon = "${sysconfdir}/clamd.conf \

                          ${sysconfdir}/default/clamav-daemon"

注意${PN}-daemon包名的用法.${PN}这里会展开为clamav,所以这些配置文件是在clamav-daemon包里面的.

8.18包之间的关系

软件包格式ipkg,deb支持显式的包依赖关系.这包括冲突的包和需要的包.

RRECOMMENDS

用来指定其他那些包都需要在安装这个包之前安装.Generally this means while the recommended

packages are not required they provide some sort of functionality which users would usually want.

RCONFLICTS

用来指定冲突于该包的软件包.两个冲突的包不可能被同时安装的上.

RREPLACES

用来指定当前包替换了一个使用不同名称的旧包.在安装包的时候被替换的包会被卸载掉,因为那个包

不会再有用了.

RSUGGESTS

指定建议安装的包.这些包会跟要安装的包有些关系或者比较有用,但是并不强制安装.

RPROVIDES

用来指定一个包为运行时提供了什么.比如热插拔的支持由几个包支持的,例如udev,linux-hotplug.

两者都为运行时提供了"hotplug".所以任何需要"hotplug"支持的包简单的在它的RDEPENDS里声明

"hotplug"即可.这项特性通常在发行版级别提供"virtual/hotplug"虚拟包的支持.

PROVIDES

用来显式的指定这个包在编译时提供了什么.这个通常在两个或者更多包提供相同功能的时候使用.

比如在OE里我们有几个不同的X server,然后每个都声明提供"virtual/xserver"功能.因此,

一个依赖于X server的包可以简单的在DEPENDS里声明"virtual/xserver".这个在发行版级别

指定了那个"virtual/xserver"的实现会被使用.

8.19 Fakeroot: 需要 "root"身份

有些时候包需要root权限来做一些事情,比如改变用户和组或者创建节点设备.Since OpenEmbedded will

not keep the user and group information it's usually preferable to remove that

from the makefiles. For device nodes it's usually preferable to create them from

the initial device node lists or via udev configuration.

不过你如果确实需要root身份才能做事情,那么你可以使用fakeroot来模拟一个root环境.

使用fakeroot执行动作可以在任务之前加上fakeroot:

fakeroot do_install() {

因为这需要fakeroot首先存在,所以你应该添加一个fakeroot-native依赖:

DEPENDS = "fakeroot-native"

你可以参考fuse的配方.关于fakeroot的进一步信息你可以参照参考手册:fackeroot.

8.20本地:为主机编译的包

这节还需要完成:

* 什么是一个本地包

* 使用所需的非本地包

8.21开发:开发“配方”的方法策略

这节还需要完成:

* 如何开发配方

* 如何处理繁多的补丁

* 如何处理网络文件的事宜

* autotools事宜

8.22高级的版本号:如何处理rc和per版本

在指定一个包的rc和pre版本的时候需要注意一些事情.

假设我们已经有了一个软件的1.5版本,然后你想添加一个新的1.6-rc1.

* 1.5 : 已有版本

* 1.6-rc1 : 新版本.

如果新包被命名为1.6-rc1,开始可能一切ok,不过最后这个包正式发布的时候版本会是1.6.现在

如果你创建包的1.6版本你会发现包的顺序是这样的:

  1. 1.5
  1. 1.6
  1. 1.6-rc

这会导致一些包管理器比如ipkg认为1.6是老于rc的版本.在OE里正确命名一个包的pre和rc版本的

方式是在先前的版本号后面用+加上新的版本号.所以1.6-rc1发布版将会是这样的版本号:

* 1.5+1.6-rc1

这样顺序就会变为:

  1. 1.5
  1. 1.5+1.6-rc1
  1. 1.6

这是包管理系统所期待的正确的版本号.

8.23 require/include:重用“配方”的内容

在很多包里你都需要维护包的多个版本,不过这些配方经常是相同的或者只有一些少量的地方不同.

require和include宏可以用来在一个文件中包含另外一个文件的内容.你应该多注意和尝试使用这种

方式来添加一个配方的新版本.

注意:

require和include具有相同的功能--包含另外一个文件的内容到配方中来.这两个命令

的不同之处在于require在要包含的文件不存在的时候将会产生一个错误.因此include

不应该在新的配方里使用.

例如 clamav配方:

require clamav.inc

PR = "r0"

注意,所有配方功能都在clamav.inc文件里提供.仅仅配方的发布号在配方里定义.每个配方都包含

了clamav.inc文件以免重复编写那些功能.这也意味着一个新版本的发布仅仅是拷贝了配方的内容

然后把发布号重改了.

下面的iproute2例子里,配方添加了一个附加的补丁文件.这个并没有在include的文件里定义.

这些补丁仅仅是针对新版本软件的,所以只在它自己的配方里添加:

PR = "r1"

            file://new-flex-fix.patch"

require iproute2.inc

DATE = "060323"

下面从cherokee来的例子里,同样也有为此版本添加的补丁,不过这个还示例了为这个版本定义了

一个configure任务.这样这个任务就会重载include文件里的.

PR = "r7"

SRC_URI_append = "file://configure.patch \

                  file://Makefile.in.patch \

                  file://Makefile.cget.patch \

                  file://util.patch"

require cherokee.inc

do_configure() {

        gnu-configize

        oe_runconf

        sed -i 's:-L\$:-L${STAGING_LIBDIR} -L\$:' ${S}/*libtool

}

8.24Python语言:python的高级功能

8.25初始化脚本:怎么处理守护进程

8.26两者选一:怎样处理多个包使用相同的命令

8.26.1.例子

8.26.2.使用新的

8.27转瞬即逝:如何处理/var目录

8.27.1.声明

8.27.2.写日志和日志文件

8.27.3.摘要

8.28其他杂项

剩余章节翻译中。。。。。。。。。