openSUSE:软件包依赖
软件包依赖类型
Requires
RPM 具有自动查找库、Perl 模块等依赖项的强大功能。通常,当依赖项已通过 RPM 以依赖于 libqt4 软件包中的共享库的形式被拾取时,无需显式列出“Requires:”(例如 Requires: libqt4)。
如果您的软件包拆分为子软件包,它们可能需要使用版本化的依赖项来需要基本软件包。
Requires: %{name} = %{version}
-devel 子软件包需要显式依赖于包含 devel 软件包具有符号链接的共享库的软件包。例如,libfoobar-devel 需要
Requires: libfoo2 = %{version}, libbar5 = %{version}
有关依赖项和可解析类型的列表,请参阅 Libzypp/Dependencies。
PreReq
PreReq 是一种技巧;它是一种高优先级的“Requires”,通常仅在涉及不宽容脚本时才需要解决循环依赖关系。几乎总是,常规的 Requires、Requires(pre)、Requires(post) 等有效,应该使用它们。
BuildArch
典型用法
BuildArch: noarch
如果您的文件列表中没有架构相关的文件,请与您的软件包一起使用。
请注意,如果您的主软件包是 noarch,则所有子软件包也必须是 noarch。
BuildRequires
您必须显式列出您的软件包成功构建所需的软件包。
与 Requires 不同,BuildRequires 没有自动查找依赖项的过程。拥有正确的构建要求可以节省所有开发人员和测试人员的时间,因为他们无需手动搜索缺少的构建要求。它还确保构建是可重现的并且始终使用相同的特性。例如,configure 脚本可能会根据 libpng 在构建系统中的可用性排除 PNG 支持。使用 BuildRequires: libpng-devel(或从 openSUSE 11.4 开始:BuildRequires: pkgconfig(libpng14),您可以确保如果 libpng 不存在,构建将失败。
OBS 注意事项
条件 BuildRequires 仅限于简单的变量。RPM 通常支持更复杂的结构,例如 %if 表达式
%if 0%(test "%something" = "enabled" && echo 1)
但是,在评估 BuildRequires 时,BuildService 的 RPM 解析器在禁用了子 shell 扩展的环境中运行。您会看到如下警告
Warning: spec file parser line 109: can't expand %(...)
%lua 脚本也不会被调度程序解释;不会发出警告。
以下示例仅使用一个简单的变量,该变量适用于 BuildRequires
%if ! %{with own_mpfr}
BuildRequires: mpfr-devel
%endif
例外情况
无需将以下软件包及其依赖项作为 BuildRequires 包含,因为它们会经常出现。这些软件包被认为是最小的构建环境。
还可以尝试此命令获取列表
Preinstall: aaa_base acl attr bash coreutils diffutils
Preinstall: filesystem fillup glibc grep insserv libacl libattr
Preinstall: libbz2-1 libgcc%{gcc_version} libxcrypt m4 libncurses5 pam
Preinstall: permissions libreadline6 rpm sed tar zlib libselinux1
Preinstall: liblzma5 libcap2 libpcre0
Preinstall: libpopt0 libelf1 liblua5_1
Required: gcc gcc%{gcc_version} glibc perl rpm tar patch
Support: autoconf automake binutils bzip2 gcc gcc%{gcc_version}
Support: gettext-runtime glibc libtool perl rpm zlib
Support: libncurses5
Support: libaudit1 cpio cpp cpp%{gcc_version} cracklib cvs
Support: file findutils gawk gdbm gettext-tools
Support: glibc-devel glibc-locale groff gzip info less
Support: libbz2-devel libdb-4_8
Support: libstdc++%{gcc_version}
Support: udev
Support: libxcrypt libzio
Support: linux-glibc-devel make man netcfg
Support: net-tools pam-modules patch perl-base sysvinit-tools
Support: texinfo timezone util-linux libmount1 login
Support: libgomp%{gcc_version} libuuid1 psmisc
Support: terminfo-base update-alternatives pwdutils build-mkbaselibs
Support: brp-check-suse post-build-checks rpmlint-Factory
Support: build-compare
Support: libunwind libunwind-devel
Conflicts
在可能的情况下,openSUSE 软件包应避免相互冲突。不幸的是,这并非总是可能的。有关 openSUSE Conflicts 策略的完整详细信息,请参阅 openSUSE:Packaging conflicts。
Recommends
SUSE 的 RPM 支持 Recommends 标签,但直到最近才在上游可用。因此,带有 Recommends 的软件包将无法为 OBS 目标 Fedora_18、RHEL_6 和 CentOS_6 构建。一种解决方法是从非 SUSE 目标中排除 Recommends 标签
%if 0%{?suse_version}
Recommends: foo
%endif
或者 Recommends 标签可以成为 Requires 标签,以便推荐的软件包 (foo) 在不支持 RPM Recommends 的系统上始终安装
%if 0%{?suse_version}
Recommends: foo
%else
Requires: foo
%endif
另请参阅 openSUSE:Build Service cross distribution howto
文件依赖
RPM 允许您依赖于文件而不是软件包。尽可能避免在 /etc、/bin、/sbin、/usr/bin 或 /usr/sbin 之外依赖于文件。在这些目录之外使用文件依赖项需要 yum(和其他使用 repomd 格式的 depsolvers)下载并解析大型 XML 文件以查找依赖项。通过依赖于软件包而不是文件来帮助 depsolvers 避免此处理,可以节省我们最终用户的很多时间。有时,其他技术考虑因素会超过这些考虑因素。一个具体的例子是安装到 %{_libdir}/mozilla/plugins 的软件包。在这种情况下,仅仅为了拥有此目录而强制使用特定的浏览器可能会拖入大量的无用软件包。要求目录以解析依赖项是更好的选择。
kmod 依赖
现在,用户空间软件包可以在安装时拉取特定的内核风味。这需要软件包的 specfile 包含新的 kmod Requires。
这对于 SLES 和 openSUSE Minimal-VM 镜像特别有用,因为它专注于虚拟环境(VM 和 Cloud),并且仔细关注镜像的大小。因此,对于这些镜像和其他发行版的变体,我们使用精简的内核(kernel-default-base),其中包含选定的模块,而 kernel-default 包含所有受支持的模块。
在 SLES 15 SP2 之前,如果用户安装了需要未安装在系统中的特定内核模块的用户空间软件包,则该服务/软件包将无法正常工作。这是我们的镜像中的一个常见场景,我们的用户必须手动安装完整的 kernel-default 才能解决此问题。
现在,软件包可以设置对内核模块或内核风味的依赖关系,因此它们将一起自动安装。
让我们举一个具体的例子
如果用户安装了 Minimal-VM 上的 multipath-tools,然后启动 multipath 服务,它将失败,因为所需的 dm_multipath 内核模块被排除在 kernel-default-base 之外,因此默认情况下未加载。将显示“Failed to find module 'dm_multipath'” systemd 错误消息,直到手动加载该模块 (bsc#1119414 - VMware: Failed to find modules scsi_dh_alua, scsi_dh_emc, scsi_dh_rdac, and dm_multipath)。
这些依赖项是为使用 /usr/lib/modules-load.d/ 机制配置模块加载的软件包自动生成的,但对于其他情况,需要手动放置。
另一个例子是 nfsd.ko 模块,NFS 服务器软件包需要它才能正确启动 NFS 服务。
因此,我们引入了对指定模块的“Requires”
Requires: (kmod(nfsd.ko) if kernel)
请注意,自动生成的 kmod Requires 包含条件 if kernel,以确保仅在安装了内核时才需要内核模块。这是为了避免安装镜像等问题,这些镜像构建带有软件包的 initrd,但内核本身存储在单独的位置。
强依赖和弱依赖
Requires 标签始终需要由求解器满足,Recommends 在不存在时会被忽略,并且也可以由软件包管理器(例如 zypper)故意忽略,例如,您可以使用“zypper in --no-recommends”,或者一个配置键(“solver.onlyRequires = true”)默认禁用建议和推荐软件包的安装,例如我们的 Minimal(以前的 JeOS)镜像的默认配置。
处理依赖时需要考虑的事项
如果您的软件调用外部工具,或者需要一些其他软件包或 RPM 在构建期间无法自动生成的其他功能,则需要显式地将依赖项添加到您的 spec 文件中。
在决定使用哪个依赖级别标签(Requires、Recommends、Suggests)时,需要考虑重要事项。软件包可以分解为几个组件,并具有不同级别的功能依赖关系
- 如果依赖项对于软件提供大量功能是必需的,则必须使用 Requires。
- 如果任何组件将在缺少依赖软件包时故意失败,则必须使用 Requires。
- Recommends 可用于表达其他功能。
- Suggests 可用于为用户或求解器提供提示,绝不能被视为硬性依赖项。
使用 Suggests 告诉包装系统和用户,列出的软件包与此软件包相关,并且可能可以增强其可用性,但如果不安装它们,也是完全合理的。
软件包中过多的 Requires 的一些担忧通常可以通过将软件包拆分为更小的组件来解决,这些组件然后可以具有适当的 Requires。例如,如果您的软件包具有默认情况下未使用的额外功能,例如 SSL 支持,您可以考虑将其拆分为 yourpackage-ssl,并在子软件包中执行所有适当的 Requires,这样您就不必在主软件包中推荐软件包。
对镜像构建和系统安装的影响。
目前,SUSE 使用两种机制来定义要安装的软件包列表,
1 - 安装程序使用的机制,其中模式软件包用于列出所有要安装的软件包
2 - Kiwi 描述文件,用于使用预定义的软件包集构建镜像
所有这些都应考虑软件包之间的关系(Requires、Recommends 等),我们应仔细考虑何时向现有软件包添加更多 Requires,尤其是在基本系统中。
虽然 openSUSE 中的模式可以用作软件安装的元软件包,但它们不能成组卸载,因此主要在安装和升级期间使用。
应该避免通过将软件包作为 Requires/Recommends 添加到模式来“修复”安装程序中的问题,而不是故意修改软件包以具有适当的依赖项。这些实际上可能会破坏其他类型的安装,例如 Minimal(以前的 JeOS)使用 --no-recommends 并且将与 YAST 安装不同的软件包集。
Suggests 在这里特别棘手,因为有时人们在模式中使用它来“提示”求解器(zypper 或 dnf)在多个软件包提供相同功能时应该查找哪些软件包,这也可以通过构建系统(OBS)中的“Prefer:” 标签来完成
这也应仔细考虑,因为其他类型的构建可能会由于构建的性质而假定不同的默认值。
如何进行分发升级
有两种更新模式需要不同的行为
- Bugfixes (Patches)
- 发行版升级
在第一种模式下,会修补已安装的软件包以修复错误或引入缺少的功能。但是,软件包版本通常保持不变,因为原始 tarball 相同;只有软件包发布号会更改。
此外,构建环境(编译器、库等)只会看到最少的更改。
分发升级将整个系统提升到一个新的水平,并且操作系统版本会增加。(如果系统是例如 openSUSE 10.2,它将增加到 openSUSE 10.3)允许在分发升级期间进行的软件包更改可能更加严重,包括
- 删除的软件包
- 新的软件包
- 重命名的软件包
- 合并的软件包
- 拆分的软件包
这些更改很难(甚至不可能)通过 RPM 依赖项来表达。因此,需要一种更具表现力的软件包依赖项形式,以便可以执行系统升级。
此 Wiki 页面记录了如何表达用于发行版升级的依赖关系。
如果您正在添加依赖项以实现无缝的系统升级,请不要忘记指定这些依赖项所需的 openSUSE 版本。这将允许在未来的版本中识别这些行已过时(很可能是在 SLE 版本 + 2)。
表达依赖关系的方式
每个软件包可以通过定义符号名称来表达对其他软件包的依赖关系。提供的功能 (Provides), Requires,和Conflicts.
这些符号名称通常是软件包的名称,但也可以是与软件包无关的“抽象”名称。
这些符号名称可用于表达能力,这些能力与特定软件包无关。例如,MTA(邮件传输代理)能力由多个软件包提供,sendmail和postfix是最主要的。
重要的是要记住,所有符号名称(无论是软件包名称还是抽象名称)都位于相同的命名空间中。在选择抽象名称(例如,用于能力)时,它不得与另一个软件包名称冲突。
为了表达更新依赖关系,以便用另一个(较新的)软件包替换一个(旧的)软件包,使用废弃 (Obsoletes)标签。
关于 RPM 依赖关系的事实
- 每个软件包隐式提供其名称。
无需提供软件包名称,但其他软件包可以需要(或与)软件包名称冲突。 - 一个软件包不能与其提供的符号标签冲突。
由于软件包名称是隐式提供的,因此软件包不能与其自身冲突。 - 已过时仅适用于真实的软件包名称。
不能废弃符号标签。 - 在Requires, 提供的功能 (Provides)和Conflicts区分大小写。
基本依赖关系
安装新软件包
| pac.spec | |
|---|---|
| 名称 | pac |
| 版本 | 1.0 |
| Requires | |
| 提供的功能 (Provides) | |
| 废弃 (Obsoletes) | |
| Conflicts |
更新软件包
| pac.spec | |
|---|---|
| 名称 | pac |
| 版本 | 1.1 |
| Requires | |
| 提供的功能 (Provides) | |
| 废弃 (Obsoletes) | |
| Conflicts |
匹配的名称和更高的版本使这成为对pac-1.0.
的更新。
| 重命名软件包 | ||
|---|---|---|
| 名称 | package.spec | |
| 版本 | 1.0 | |
| Requires | ||
| 提供的功能 (Provides) | pac = %version-%release | |
| 废弃 (Obsoletes) | pac < %version-%release | |
| Conflicts |
该提供的功能 (Provides)字段具有三个目的:它使用户能够通过旧名称找到软件包,它满足其他软件包依赖于旧名称的软件包依赖关系,并且它为 libzypp 中的求解器提供了一个提示,如果多个软件包废弃了旧名称,则优先选择此软件包。作为一项策略,要求软件包具有提供的功能 (Provides)标签。
该废弃 (Obsoletes)字段有两个目的:最重要的是,它告诉 rpm 仅通过指定的名称和版本号删除一个软件包,并且它为 libzypp 中的求解器提供了一个提示,表明重命名的软件包更新。
这两个标签都必须进行版本控制。版本必须包括版本和发布号,以增加新构建覆盖野外已安装旧软件包变体的可能性。
使用版本化的废弃 (Obsoletes)字段旨在处理旧名称的软件包重新进入发行版的情况。由于无法预测未来,因此必须假设当这种情况发生时,其版本号将更高。无法对该软件包将要服务的目的做出任何假设。它可以替换刚刚重命名的软件包(在这种情况下,此软件包重命名规则适用),或者它可能能够与重命名的软件包共存。如果出于某种原因,返回的软件包获得较低的版本号,则必须再次调整刚刚重命名的软件包,以不再声明对旧的、然后是新的软件包的所有权。然后两者都能够共存。
当一个软件包被重命名时,该软件包的版本可能保持不变。这意味着一个简单的Obsoletes: pac < %version在安装重命名的软件包时,不会删除旧软件包。最可能的结果是文件冲突,因为新软件包将与旧软件包并行安装,因此使用%version-%release.
的使用%version-%release将减少,但不能完全避免,新重命名的软件包仍然被视为比旧软件包旧的可能性。如果较旧的 codestream 接收到软件包的许多更改,这意味着它会获得较高的 %release 数字。另一个较新的 codestream 可能从低得多的版本号开始。因此,建议使用废弃 (Obsoletes)无法按预期工作,旧软件包将永远不会被删除,并且会发生文件冲突。如果遇到这种情况,可以接受未版本化的废弃 (Obsoletes),但代价是如果需要将旧名称的软件包添加到发行版,则必须完成额外的工作。请咨询发布经理以获取例外情况。
如果您使用与主软件包不同的版本号废弃子软件包,则不能使用%version在 Provides/Obsoletes 中。当然,您可以使用与版本子软件包中相同的版本字符串。
请还在 specfile 中添加一个简单的注释,例如“# pac 上次在 openSUSE 12.3 中使用”。每个软件包都必须照顾自己的遗产,这意味着 Provides 和 Obsoletes 标签将永久存在。
用具有相同功能的另一个软件包替换软件包
| 重命名软件包 | |
|---|---|
| 名称 | package.spec |
| 版本 | 1.1 |
| Requires | |
| 提供的功能 (Provides) | |
| 废弃 (Obsoletes) | pac < %version |
| Conflicts |
与其重命名软件包,pac将被替换为package.spec,尽管package.spec不提供pac的文件。所以package.spec 不提供 pac.
这只有在某个软件包废弃pac 仅限。如果有多个软件包,求解器将不会执行此更新。
拆分和合并
拆分子软件包
| pac.spec | pac-devel.spec | |||
|---|---|---|---|---|
| 名称 | pac | 名称 | pac-devel | |
| 版本 | 1.1 | 版本 | 1.1 | |
| Requires | Requires | |||
| 提供的功能 (Provides) | 提供的功能 (Provides) | pac:/file/from/pac | ||
| 废弃 (Obsoletes) | 废弃 (Obsoletes) | |||
| Conflicts | Conflicts |
将软件包拆分为两个
| firstpac.spec | secondpac.spec | |||
|---|---|---|---|---|
| 名称 | firstpac | 名称 | secondpac | |
| 版本 | 1.1 | 版本 | 1.1 | |
| Requires | Requires | |||
| 提供的功能 (Provides) | pac = 8.15 | 提供的功能 (Provides) | pac:/file/from/pac | |
| 废弃 (Obsoletes) | pac <= 8.15 | 废弃 (Obsoletes) | ||
| Conflicts | Conflicts |
所示的示例列出了pac和pac-devel的单独 spec 文件。(请记住,RPM 允许从单个.spec文件中提供相同的供应商和标识信息。
定义子软件包。)提供的功能 (Provides)依赖关系解析(安装两个新软件包)由 YaST 使用pac-devel.
中提到的split-alias完成。pac和pac-devel软件包维护者决定-devel之间的依赖关系。例如,如果想要将库软件包拆分为主软件包、和软件包定义子软件包,主软件包和和软件包,则主软件包和-devel软件包是独立的,但
软件包需要主软件包。
在更新期间,上述 split-alias 标签非常重要。YaST 仅更新已安装的软件包,在这种情况下仅更新主软件包。如果没有 split-alias,这将删除旧主软件包中包含但新主软件包中没有的开发所需的文件。提供的功能 (Provides)脚本pac给定的pac-devel现在触发此更新依赖关系,因为它告诉 YaST 也安装
如果一个软件包被拆分为多个软件包,则 split-alias 标签必须互斥。这是 YaST 正确处理此类更新依赖关系的一种方式。
如果一个软件包拆分为多个软件包,则 split-alias 标签必须互斥。这是 YaST 正确处理此类更新依赖关系的一种方式。
未来的重要增强-devel软件包始终是一个好主意。但并非在所有情况下都有意义——因为有时,-devel软件包太小或不包含任何内容。在这种情况下,只需添加一个Provides: pac-devel = %version在主软件包中,就可以实现其他软件包可以要求此虚拟-devel软件包。
请还在 specfile 中添加一个简单的注释,例如“# -devel 在 openSUSE 12.3 中拆分”。如果没有它,将很难确定何时可以删除这些行。否则,它可能会永远停留在那里。
请还在 spec 文件中添加一个简单的注释,例如“# -devel 在 openSUSE 12.3 中拆分”。如果没有它,将很难决定何时可以删除这些行。否则,它可能会永远保留在那里。
| 重命名软件包 | |
|---|---|
| 名称 | package.spec |
| 版本 | 1.1 |
| Requires | |
| 提供的功能 (Provides) | pac = 8.15 |
| 废弃 (Obsoletes) | pac <= 8.15 |
| Conflicts |
这与软件包重命名类似。新软件包还必须包含提供的功能 (Provides)和废弃 (Obsoletes)这与软件包重命名类似。新软件包还必须包含旧软件包的X.
标签。这对于从发行版之前正确更新至关重要。
| 重命名软件包 | |
|---|---|
| 名称 | package.spec |
| 版本 | 1.1 |
| Requires | |
| 提供的功能 (Provides) | pac1 = 8.15, pac2 = 2.3 |
| 废弃 (Obsoletes) | pac1 <= 8.15, pac2 <= 2.3 |
| Conflicts |
pac1 <= 8.15, pac2 <= 2.3
处理替代方案
| firstpac.spec | secondpac.spec | 不冲突 | |||||
|---|---|---|---|---|---|---|---|
| 名称 | firstpac | 名称 | secondpac | 名称 | thirdpac.spec | ||
| 版本 | 1.0 | 版本 | 1.0 | 版本 | 1.0 | ||
| Requires | Requires | Requires | |||||
| 提供的功能 (Provides) | thirdpac | 提供的功能 (Provides) | thirdpac | 提供的功能 (Provides) | thirdpac | ||
| 废弃 (Obsoletes) | 废弃 (Obsoletes) | 废弃 (Obsoletes) | |||||
| Conflicts | Conflicts | Conflicts |
Pac = 1.0
(Pac 中的大写字母是故意选择的,以区分此标签与软件包。此类能力必须在 openSUSE 社区内达成一致。在发明您自己的标签之前,请在 "opensuse-packaging" 邮件列表中询问!)
示例xdm和kdm提供“Display-Manager”能力。
部分冲突
| firstpac.spec | secondpac.spec | 不冲突 | |||||
|---|---|---|---|---|---|---|---|
| 名称 | firstpac | 名称 | secondpac | 名称 | thirdpac.spec | ||
| 版本 | 1.0 | 版本 | 1.0 | 版本 | 1.0 | ||
| Requires | Requires | Requires | |||||
| 提供的功能 (Provides) | thirdpac | 提供的功能 (Provides) | thirdpac | 提供的功能 (Provides) | thirdpac | ||
| 废弃 (Obsoletes) | 废弃 (Obsoletes) | 废弃 (Obsoletes) | |||||
| Conflicts | secondpac | Conflicts | firstpac | Conflicts |
该Conflicts表达只有 firstpac 或 secondpac 才能安装,但不能同时安装。
互斥
| firstpac.spec | secondpac.spec | 不冲突 | |||||
|---|---|---|---|---|---|---|---|
| 名称 | firstpac | 名称 | secondpac | 名称 | thirdpac.spec | ||
| 版本 | 1.0 | 版本 | 1.0 | 版本 | 1.0 | ||
| Requires | Requires | Requires | |||||
| 提供的功能 (Provides) | thirdpac | 提供的功能 (Provides) | thirdpac | 提供的功能 (Provides) | thirdpac | ||
| 废弃 (Obsoletes) | 废弃 (Obsoletes) | 废弃 (Obsoletes) | |||||
| Conflicts | secondpac thirdpac | Conflicts | firstpac thirdpac | Conflicts | firstpac secondpac |
只能安装 firstpac、secondpac 或 thirdpac 中的一个。









