29 使用软件包

本节介绍以下软件包相关任务:

29 从镜像中排除软件包

有时需要阻止特定软件包安装到镜像。可以通过若干变量,忽略推荐软件包,或完全 禁止安装某个包。

以下变量仅适用于 IPK 和 RPM,不适用于 Debian 软件包。可以在发行版 configuration file 中设置,也可以通过配方名称覆盖将其限定到某个 镜像配方。详细说明见 Yocto Project 参考手册词汇表。

  • BAD_RECOMMENDATIONS:指定不希望安装、仅由推荐关系引入的软件包。

  • NO_RECOMMENDATIONS:禁止安装所有仅由推荐关系引入的软件包。

  • PACKAGE_EXCLUDE:无论是否属于推荐依赖,都禁止安装指定软件包。 如果已安装包必须依赖它,构建可能报错失败。

29 递增软件包版本

本节介绍二进制软件包版本管理的背景,以及相关服务、变量和术语。

需要区分以下概念:

  • 二进制软件包:最终构建并安装到镜像中的软件包。

  • 二进制软件包版本:由版本号和修订号两部分组成。

    备注

    技术上还有第三部分 epoch,即 PE,但这里大体忽略它。

    版本号来自 PV,修订号来自 PR。

  • PV:配方版本,代表被打包软件的版本,不要与二进制包版本混淆。

  • PR:配方修订号。

  • PR 服务:通过网络提供的服务,自动维护软件包源 与 RPM、APT、OPKG 等现有包管理器的兼容性。

二进制软件包内容变化时,包版本必须变化,通过递增 PR 或 PV 实现,有两种方式:

  • 使用软件包修订服务(PR 服务)自动递增。

  • 手动递增 PR 或 PV。

维护与 RPM、APT、OPKG 等兼容的软件包源,是构建系统及其用户的重要挑战, 因此更推荐自动化方式。无论哪种方法,关键要求都是二进制版本线性递增,且 版本组成部分能够支持这种递增。方法见 “自动递增软件包版本号”。

下面三节分别介绍 PR 服务、手动递增,以及确保版本线性增长的方法。

29 使用 PR 服务

在 Metadata 中手动维护修订号容易出错、不够准确,也增加配方提交者 的负担。PR 服务则自动生成递增数字,尤其是修订字段,消除人为影响。

备注

更多信息见 PR 服务 Wiki 页面。

Yocto Project 按优先级递减顺序使用 PE、PV 和 PR, 分别表示 epoch、版本和修订。其取值很大程度上取决于发行版和软件包源的策略。

构建系统通过特定构建的 “签名” 判断何时 重建软件包。任务所有输入都体现在签名中,签名变化可触发重建。因此,系统 本身不依赖 PR、PV 和 PE 触发重建,但可以利用签名 生成这些值。

PR 服务支持 OEBasic 和 OEBasicHash 生成器。校验和变化时递增 PR,不同生成机制会在不同条件下改变签名。

服务生成的值以 .x 形式附加到 PR,例如 r0 变成 r0.1、 r0.2。这允许继续保留原有 PR 的用途,包括必要时手动递增。

PR 服务默认未启用,因此生成的软件包仅保证“内部一致”。构建系统会增删包, 不保证升级路径,但镜像会正确包含最新变化。

最简单的情况是由单个开发主机构建软件包源。可在 Build Directory 的 build/conf/local.conf 设置 PRSERV_HOST,启用 本地服务:

PRSERV_HOST = "localhost:0"

服务启动后,软件包自动获得递增 PR,BitBake 负责服务器启停。

多个开发主机共享同一软件包源时,应运行单个 PR 服务,供每台构建系统连接。 使用 bitbake-prserv 启动:

bitbake-prserv --host ip --port port --start

手动启动后,还需按前述方法修改每台构建系统的 build/conf/local.conf,指向服务器地址和端口。

建议同时启用构建历史,对二进制包版本增加合理性检查。在各系统的 build/conf/local.conf 或发行版 configuration file 中添加:

# It is recommended to activate "buildhistory" for testing the PR service
INHERIT += "buildhistory"
BUILDHISTORY_COMMIT = "1"

构建历史详见 “使用 buildhistory 维护构建输出质量”。

备注

共享状态软件包不保存 PR 信息。如果维护共享状态源,所有向其 提供内容的构建系统应共同使用一个 PR 服务,或者全都不使用 PR 服务。

如果多台机器共享 PR 服务却不共享共享状态源,同样输出可能出现不同哈希。 PR 服务会将每个哈希视为新内容并递增修订号,导致大量不必要的软件包升级。

共享状态详见概述与概念手册的 “共享状态缓存”。

29 手动递增 PR

不使用 PR 服务时,可以手动递增 PR。

如果某次提交改变了软件包输出,应在同一提交中递增 PR。新配方应 显式设置初始值 "r0"。虽然它是默认值,写出来有助于在未来修改配方时记得 递增修订号。

通常只需递增二进制包版本。但如果 PV 发生变化却没有增大,可以递增 PE (Package Epoch),其默认值为 "0"。

二进制包版本尽量遵循 Debian 版本字段策略, 其中定义版本比较方式和“递增”的含义。

29 自动递增软件包版本号

获取仓库时,BitBake 用 SRCREV 确定要构建的源码修订。设为 AUTOREV 后,自动采用最新版本:

SRCREV = "${AUTOREV}"

还需在 PV 中包含 +,以便源码修订变化时自动更新版本,例如:

PV = "1.0+git"

构建系统自动在 PKGV 末尾追加版本控制信息,格式为:

AUTOINC+source_code_revision

AUTOINC 替换成的数字取决于 PR 服务:

  • 启用 PR 服务时,像 PR 一样递增,得到理想的线性增长版本。例如:

    hello-world-git_1.0+git0+b6558dd387-r0.0_armv7a-neon.ipk
    hello-world-git_1.0+git1+dd2f5c3565-r0.1_armv7a-neon.ipk
    

    这里有两个数字变化:

    • git0 变为 git1,因为源码修订 SRCREV 变化。

    • r0.0 变为 r0.1,因为 do_package 的哈希变化。

      原因可能有多种,可用以下命令调查:

      $ bitbake-diffsigs -t hello-world package
      
  • 未启用 PR 服务时,AUTOINC 替换为 "0"。由于版本包含源码修订,包 版本仍会变化,但不保证线性递增。例如:

    hello-world-git_1.0+git0+b6558dd387-r0_armv7a-neon.ipk
    hello-world-git_1.0+git0+dd2f5c3565-r0_armv7a-neon.ipk
    

构建系统本身不会为此跟踪二进制包版本历史。AUTOINC 与 PR 类似: 无 PR 服务时为 "0",启用服务后则跟踪版本,并在软件包修订变化时递增。

29 处理可选模块打包

许多软件将功能拆成可选模块或插件,实际构建哪些插件取决于配置。为了避免在 配方中重复判断模块可用性的逻辑,或逐个手动打包,构建系统支持动态模块打包。

需要处理两件事:

  • 确保实际执行模块打包。

  • 确保配方能满足其他配方对可选模块的依赖。

29 确保执行打包

在配方的 populate_packages Python 函数中调用 do_split_packages。 它在指定路径下匹配文件或目录,为每个匹配项生成软件包:追加 PACKAGES, 并设置 FILES:packagename、RDEPENDS:packagename、 DESCRIPTION:packagename 等。以下为 lighttpd 示例:

python populate_packages:prepend () {
    lighttpd_libdir = d.expand('${libdir}')
    do_split_packages(d, lighttpd_libdir, '^mod_(.*).so$',
                     'lighttpd-module-%s', 'Lighttpd module for %s',
                      extra_depends='')
}

调用指定了:

  • 在 do_install 已安装文件中搜索的目录。

  • 匹配模块文件的正则表达式,其中括号 () 标出用于提取模块名的部分。

  • 软件包命名模式。

  • 各包的描述。

  • 空 extra_depends,禁用对主 lighttpd 软件包的默认依赖。因此,若 ${libdir} 中存在 mod_alias.so,就创建 lighttpd-module-alias, 将 DESCRIPTION 设为 "Lighttpd module for alias"。

通常这样就够了。do_split_packages 还提供高级选项,可通过每个软件包 调用的钩子函数添加逻辑。若有多组模块,也可多次调用该函数。

更多示例见 OpenEmbedded-Core (OE-Core) 的 meta/recipes-connectivity/connman/ 中 connman.inc,以及 meta/classes-recipe/kernel.bbclass。

以下列出 do_split_packages 的必需和可选参数:

Mandatory arguments

root
   The path in which to search
file_regex
   Regular expression to match searched files.
   Use parentheses () to mark the part of this
   expression that should be used to derive the
   module name (to be substituted where %s is
   used in other function arguments as noted below)
output_pattern
   Pattern to use for the package names. Must
   include %s.
description
   Description to set for each package. Must
   include %s.

Optional arguments

postinst
   Postinstall script to use for all packages
   (as a string)
recursive
   True to perform a recursive search --- default
   False
hook
   A hook function to be called for every match.
   The function will be called with the following
   arguments (in the order listed):

   f
      Full path to the file/directory match
   pkg
      The package name
   file_regex
      As above
   output_pattern
      As above
   modulename
      The module name derived using file_regex
extra_depends
   Extra runtime dependencies (RDEPENDS) to be
   set for all packages. The default value of None
   causes a dependency on the main package
   (${PN}) --- if you do not want this, pass empty
   string '' for this parameter.
aux_files_pattern
   Extra item(s) to be added to FILES for each
   package. Can be a single string item or a list
   of strings for multiple items. Must include %s.
postrm
   postrm script to use for all packages (as a
   string)
allow_dirs
   True to allow directories to be matched -
   default False
prepend
   If True, prepend created packages to PACKAGES
   instead of the default False which appends them
match_path
   match file_regex on the whole relative path to
   the root rather than just the filename
aux_files_pattern_verbatim
   Extra item(s) to be added to FILES for each
   package, using the actual derived module name
   rather than converting it to something legal
   for a package name. Can be a single string item
   or a list of strings for multiple items. Must
   include %s.
allow_links
   True to allow symlinks to be matched --- default
   False
summary
   Summary to set for each package. Must include %s;
   defaults to description if not set.

29 满足依赖

另一项工作是确保配方满足其他配方对可选模块的依赖。可使用 PACKAGES_DYNAMIC。延续 lighttpd 示例:

PACKAGES_DYNAMIC = "lighttpd-module-.*"

正则表达式中的名称可以任意指定。这里使用 lighttpd-module- 前缀,确保 构建时满足对该前缀包名的 RDEPENDS 和 RRECOMMENDS。 如果使用 do_split_packages,PACKAGES_DYNAMIC 应与函数调用中 指定的包名模式相对应。

29 使用运行时软件包管理

构建期间,BitBake 总会将配方转换为一个或多个软件包。例如,bash 配方 生成 bash、bash-bashbug、bash-completion、bash-completion-dbg、 bash-completion-dev、bash-completion-extra、bash-dbg 等,并非 所有包都会进入镜像。

有时需要在目标运行期间更新、添加、删除或查询软件包,而不生成新镜像。例如:

  • 为已部署设备提供现场更新,如安全更新。

  • 加快设备上一个或多个应用程序的开发迭代。

  • 临时安装应用的调试包,以使用符号和源码调试。

  • 初始部署较少的软件包,再允许现场添加更多软件以定制设备。

这些场景类似传统 Linux 发行版:现场设备从服务器获取预编译包并安装或更新。 在已运行设备上执行这些操作,称为“运行时软件包管理”。

需要一台主机或服务器提供预编译包及元数据,并在目标上安装包操作工具。 构建机器可以兼任服务器,也可以将产物推送到面向互联网等用途的另一台服务器。 生产环境更适合后者,因为远离开发系统的 Build Directory 可避免 意外覆盖软件包。

即使只为一台设备构建,也会产生多个软件包数据库,按目标 CPU 架构、开发板、 C 库等划分。例如,qemux86 构建产生 noarch、i586 和 qemux86 三个数据库。要让设备识别全部可用包,需分别指向它们。这类似传统发行版配置 多个软件仓库。

运行时包管理完全可选,并非构建或部署成功的前提。要使用它,需要完成下文 介绍的额外配置。

29 构建注意事项

本节介绍支持运行时软件包管理所需的构建配置。

BitBake 生成软件包时,需通过 PACKAGE_CLASSES 指定格式:

  1. 在 build/conf/local.conf 或发行版 configuration file 中选择所需格式:

    PACKAGE_CLASSES ?= "package_packageformat"
    

    其中 packageformat 为受支持的 "ipk"、"rpm" 或 "deb"。

    备注

    可以指定多个格式,但创建镜像或 SDK 时,构建系统只使用第一个。

如果希望镜像初始就带有当前构建的软件包数据库和目标端管理工具,可以将 "package-management" 加入 IMAGE_FEATURES。组装镜像时便会包含 当前已知数据库及所需工具。但这并非强制要求:也可以只安装工具,不带数据库, 待系统运行后再初始化。例如,使用 IPK 时可将 "opkg" 加入 IMAGE_INSTALL。

任何可能生成或修改软件包的构建结束后,都建议重新生成索引:

$ bitbake package-index

不要试图用以下命令同时构建软件包和索引:

$ bitbake some-package package-index

BitBake 不保证索引任务在软件包构建完成后运行,因此新索引未必包含刚构建的包。 应始终先构建软件包,再单独更新索引。

可通过 PACKAGE_FEED_ARCHS、PACKAGE_FEED_BASE_PATHS 和 PACKAGE_FEED_URIS 为目标镜像预设软件包源。若未定义,需按后文手动 配置目标。要生成正确配置的镜像,应在构建前设置这些变量。

备注

镜像需要足够空闲存储来升级软件包,尤其是同时下载大量包时。请设置 IMAGE_ROOTFS_EXTRA_SPACE 预留空间。

构建完成后,软件包位于 ${TMPDIR}/deploy/packageformat。例如 ${TMPDIR} 为 tmp,格式为 RPM 时,路径为 tmp/deploy/rpm。

29 设置主机或服务器

虽然也能使用其他协议,通常通过 HTTP 提供软件包。可在服务器上配置 Apache 2、 lighttpd 或 Python Web 服务器。

本节用简单的 Python Web 服务器共享开发机器的软件包源。它未必适合生产环境, 但设置直接。若需要 Apache 2、Lighttpd 或 Nginx 等生产服务器,请相应配置。

在按 PACKAGE_CLASSES 选定格式构建过镜像的 Build Directory 中启动服务器。以下假定目录为 bitbake-builds/build,格式为 “package_rpm”:

$ cd bitbake-builds/build/tmp/deploy/rpm
$ python3 -m http.server

29 设置目标设备

目标配置因包管理系统而异,以下介绍 RPM、IPK 和 DEB。

29 使用 RPM

Dandified Packaging (DNF)负责 RPM 运行时管理。 如果当前运行镜像构建时未设置 PACKAGE_FEED_*,需要在目标上进行本节的 初始配置。

备注

相关变量见参考手册词汇表的 PACKAGE_FEED_ARCHS、 PACKAGE_FEED_BASE_PATHS 和 PACKAGE_FEED_URIS。

在目标创建 /etc/yum.repos.d/oe-packages.repo,定义 oe-packages, 告诉 DNF 哪些软件包数据库可用。

假设目标可使用 my.server 上的 all、i586 和 qemux86 数据库。 Web 服务器如何设置由你决定,关键是目标仓库配置中的 URI 正确指向远程软件包源。

备注

开发时可让 Web 服务器指向构建的 deploy 目录。生产时最好将包目录 复制到构建区之外,避免构建系统覆盖或修改部署内容。

可为各架构分别指定位置,或为所有架构指定统一位置,两种方式不能同时使用:

  • 显式列出架构: 分别指定每个数据库的 base URL:

    [oe-packages]
    baseurl=http://my.server/rpm/i586  http://my.server/rpm/qemux86 http://my.server/rpm/all
    

    此例分别告诉 DNF 三种架构的数据库位置。

  • 创建统一完整索引: 指定包含完整数据库的单个 base URL:

    [oe-packages]
    baseurl=http://my.server/rpm
    

    此例的单个数据库包含所有支持架构的索引信息。

指定位置后,获取数据库:

# dnf makecache

现在 DNF 就能从指定仓库查找、安装和升级软件包。

备注

更多信息见 DNF 文档。

29 使用 IPK

opkg 负责 IPK 运行时管理。如果 PACKAGE_FEED_ARCHS、 PACKAGE_FEED_BASE_PATHS 和 PACKAGE_FEED_URIS 未设置,或 镜像在设置前已构建,就需要在目标上初始化配置。

opkg 用配置文件查找可用数据库。在 /etc/opkg/ 中创建文件,列出 希望使用的仓库。

假设 HTTP 服务器 my.server 的 ipk/ 下有 i586、all 和 qemux86 数据库,在目标 /etc/opkg/ 中创建 my_repo.conf 等文件:

src/gz all http://my.server/ipk/all
src/gz i586 http://my.server/ipk/i586
src/gz qemux86 http://my.server/ipk/qemux86

然后让 opkg 获取仓库信息:

# opkg update

现在就能从指定仓库查找、安装和升级软件包。

29 使用 DEB

apt 负责 DEB 运行时管理,通过源列表文件查找数据库。如果 PACKAGE_FEED_ARCHS、PACKAGE_FEED_BASE_PATHS 和 PACKAGE_FEED_URIS 未设置,或镜像在设置前已构建,需要先在目标配置。

可在 /etc/apt/sources.list.d/ 创建 my_repo.list 等文件。假设 my.server 通过 HTTP 提供 deb/ 下的 i586、all 和 qemux86 数据库,列表内容为:

deb http://my.server/deb/all ./
deb http://my.server/deb/i586 ./
deb http://my.server/deb/qemux86 ./

再获取仓库信息:

$ sudo apt update

随后 apt 即可从指定仓库查找、安装和升级软件包。

29 生成和使用签名软件包

可以为构建使用的 RPM 软件包安全签名。签名通过验证后,构建系统才会使用该包; 签名安全检查失败时,构建停止。

本节介绍构建期间签署 RPM,以及构建时使用签名软件包源的方法。

29 签署 RPM 软件包

首先修改 rpm 配方配置,启用 OpenPGP 签名支持。可在其 .bbappend 中设置:

PACKAGECONFIG:append = " sequoia"

或在 Configuration File 中设置:

PACKAGECONFIG:append:pn-rpm-native = " sequoia"
PACKAGECONFIG:append:pn-rpm = " sequoia"

还需在 Configuration File 中添加:

# Inherit sign_rpm.bbclass to enable signing functionality
INHERIT += " sign_rpm"
# Define the GPG key that will be used for signing.
RPM_GPG_NAME = "key_name"
# Provide passphrase for the key
RPM_GPG_PASSPHRASE = "passphrase"

备注

为 key_name 和 passphrase 提供合适的值。

除 RPM_GPG_NAME 和 RPM_GPG_PASSPHRASE 外,还有两个可选变量:

  • GPG_BIN: 指定签名时执行的 gpg 二进制程序或包装脚本。

  • GPG_PATH: 指定签名时使用的 gpg 主目录。

29 处理软件包源

除了签署 RPM,还可以为 IPK 和 RPM 启用签名软件包源。

步骤与 RPM 签名类似。在 build/conf/local.conf 或发行版 configuration file 中定义:

INHERIT += "sign_package_feed"
PACKAGE_FEED_GPG_NAME = "key_name"
PACKAGE_FEED_GPG_PASSPHRASE_FILE = "path_to_file_containing_passphrase"

软件包源签名口令必须保存在独立文件中,由 PACKAGE_FEED_GPG_PASSPHRASE_FILE 指向。将明文口令放在配置之外更安全。

除 PACKAGE_FEED_GPG_NAME 和 PACKAGE_FEED_GPG_PASSPHRASE_FILE 外, 还有三个可选变量:

  • GPG_BIN: 指定签名时执行的 gpg 程序或包装脚本。

  • GPG_PATH: 指定签名时的 gpg 主目录。

  • PACKAGE_FEED_GPG_SIGNATURE_TYPE: 指定签名类型,仅适用于 RPM 和 IPK 软件包源。允许 "ASC"(默认,ASCII 装甲格式)或 "BIN"(二进制格式)。

29 使用 ptest 测试软件包

见测试环境手册的 使用 ptest 测试软件包。

29 创建 Node Package Manager(NPM)软件包

警告

NPM 获取器目前因安全问题禁用,详见 355cd22。

本节为未来可能重新启用而保留,目前已不适用。

NPM 是 JavaScript 包管理器。Yocto Project 支持 NPM 获取器, 可结合 devtool 创建生成 NPM 包的配方。

devtool 提供两种流程:NPM 注册表模块方式和 NPM 项目源码方式。

备注

虽然可以手写 NPM 配方,但 devtool 简单得多。

使用前还需了解以下要求和限制。

29 要求与注意事项

使用 devtool 创建 NPM 包前,请注意:

  • 注册表方式稍简单,但项目方式无需向公共 NPM 注册表 发布模块,可能更合适。

  • 熟悉 devtool。

  • NPM 主机工具需要 OpenEmbedded 环境中的本机 nodejs-npm 包。克隆 meta-openembedded 获取,并将本地层路径 加入 build/conf/bblayers.conf。

  • devtool 无法识别模块依赖中的本机库,必须手动将包加入配方。

  • 部署时,devtool 无法判断目标缺少哪些依赖,例如 nodejs 运行时。 需要自行查明并确保安装到目标。

  • 即使运行 Node 包不需要 NPM,在目标保留它仍很有用,包名为 nodejs-npm。

29 使用注册表模块方式

本例使用文件浏览 Web 应用 cute-files。

备注

必须知道 cute-files 的版本。

先用 devtool 和 NPM 获取器创建配方:

$ devtool add "npm://registry.npmjs.org;package=cute-files;version=1.0.2"

devtool add 调用 recipetool create,使用相同获取 URI 下载各依赖, 并尽可能收集许可证详情,最终生成配方。

运行可能较久,尤其需要构建 nodejs-native。最后应输出:

INFO: Recipe /home/.../build/workspace/recipes/cute-files/cute-files_1.0.2.bb has been automatically created; further editing may be required to make it fully functional

生成配方比较简单,包含 recipetool 找到的每个许可证,并在 LIC_FILES_CHKSUM 中记录。检查 LICENSE 中的 "unknown", 查明对应模块的许可证,并手动补入配方。

recipetool 还创建 shrinkwrap 文件,记录所有依赖模块的版本。许多包本身 没有该文件,工具会自动生成。

备注

每个子模块创建一个包,这是让镜像许可证清单体现所有依赖许可证的实际可行方式。

使用 devtool edit-recipe 查看配方:

$ devtool edit-recipe cute-files
# Recipe created by recipetool
# This is the basis of a recipe and may need further editing in order to be fully functional.
# (Feel free to remove these comments when editing.)

SUMMARY = "Turn any folder on your computer into a cute file browser, available on the local network."
# WARNING: the following LICENSE and LIC_FILES_CHKSUM values are best guesses - it is
# your responsibility to verify that the values are complete and correct.
#
# NOTE: multiple licenses have been detected; they have been separated with &
# in the LICENSE value for now since it is a reasonable assumption that all
# of the licenses apply. If instead there is a choice between the multiple
# licenses then you should change the value to separate the licenses with |
# instead of &. If there is any doubt, check the accompanying documentation
# to determine which situation is applicable.

SUMMARY = "Turn any folder on your computer into a cute file browser, available on the local network."
LICENSE = "BSD-3-Clause & ISC & MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=71d98c0a1db42956787b1909c74a86ca \
                    file://node_modules/accepts/LICENSE;md5=bf1f9ad1e2e1d507aef4883fff7103de \
                    file://node_modules/array-flatten/LICENSE;md5=44088ba57cb871a58add36ce51b8de08 \
...
                    file://node_modules/cookie-signature/Readme.md;md5=57ae8b42de3dd0c1f22d5f4cf191e15a"

SRC_URI = " \
    npm://registry.npmjs.org/;package=cute-files;version=${PV} \
    npmsw://${THISDIR}/${BPN}/npm-shrinkwrap.json \
    "

S = "${UNPACKDIR}/npm"

inherit npm

LICENSE:${PN} = "MIT"
LICENSE:${PN}-accepts = "MIT"
LICENSE:${PN}-array-flatten = "MIT"
...
LICENSE:${PN}-vary = "MIT"

该示例有三个要点:

  • SRC_URI 使用 NPM scheme,从而选择 NPM 获取器。

  • recipetool 收集全部许可证信息;若子模块许可证未知,会在注释中列出名称。

  • inherit npm 使 npm 打包所有模块。

构建 cute-files:

$ devtool build cute-files

安装该包前,目标必须先安装 nodejs。

假设目标 IP 为 192.168.7.2,部署命令为:

$ devtool deploy-target -s cute-files root@192.168.7.2

安装后,可测试应用并显示任意目录内容:

$ cd /usr/lib/node_modules/cute-files
$ cute-files

浏览器访问 http://192.168.7.2:3000,显示如下:

../_images/cute-files-npm-example.png

配方位于 workspace/recipes/cute-files,可以用于任何层。

29 使用 NPM 项目源码方式

打包注册表已有模块很有用,但开发者更常需要添加开发中的 node.js 项目。

这种方式与注册表方式很相似,只需向 devtool 提供源码 URL。

仍以 cute-files 为例:

$ devtool add https://github.com/martinaglv/cute-files.git

生成配方与前例类似,不过 SRC_URI 如下:

SRC_URI = " \
    git://github.com/martinaglv/cute-files.git;protocol=https;branch=master \
    npmsw://${THISDIR}/${BPN}/npm-shrinkwrap.json \
    "

主模块来自 Git,依赖来自 NPM 注册表。除此之外,两种方式的配方基本相同, 构建与部署步骤也完全一致。

29 向软件包添加自定义元数据

PACKAGE_ADD_METADATA 可向软件包添加额外元数据,体现在 control 或 spec 文件中。例如 IPK 内的 CONTROL 文件会增加对应行。

可通过后缀为特定包类型或特定包设置,优先级依次如下:

  • PACKAGE_ADD_METADATA_<PKGTYPE>:<PN>

  • PACKAGE_ADD_METADATA_<PKGTYPE>

  • PACKAGE_ADD_METADATA:<PN>

  • PACKAGE_ADD_METADATA

<PKGTYPE> 表示具体包类型:

  • IPK 对应 .ipk

  • DEB 对应 .deb

  • RPM 对应 .rpm

<PN> 表示软件包名称。

变量可包含多个单行元数据字段,以字面序列 '\n' 分隔。可通过变量标志 separator 重新指定分隔符。

以下示例为 IPK 添加两个自定义字段:

PACKAGE_ADD_METADATA_IPK = "Vendor: CustomIpk\nGroup:Applications/Spreadsheets"