openSUSE:RPM 条件构建

跳转到:导航搜索
此页面展示了 RPM 条件的含义,以及如何在 openSUSE 软件包中运用它们。
Icon-warning.png
警告: 此处几乎所有内容都基于经验法则。请谨慎使用!


什么是条件?我们为什么需要它们?

软件包可以通过将布尔条件传递给 rpmbuild 来提供对第三方/商业/特殊/不常用功能的支持,这些条件看起来像 --with(out) "条件"。rpmbuild 是 RPM 打包的核心命令,您使用的 osc build 实际上是指向它的宏。

对于 autotools 构建系统,条件是常见的 configure --with-guiconfigure我们在这里或那里见过。对于 CMake 来说,它是 -DENABLE_GTK3_MODULE 之类的。 这样的 --with-gui 参数可以通过硬编码或通过定义布尔宏来实现。

我们定义的布尔值就是条件。要使用条件,您需要在前言中定义,修改 BuildRequires/Requires/Provides/Obsoletes,%package (-n)子软件包和%files文件部分。

条件 vs. 硬编码

使用条件的好处是,您可以在有效的 spec 代码中控制其开启/关闭状态,而硬编码值则需要添加/删除 spec 文件中的注释标记 (#) 来禁用/启用它们。布尔宏使您能够通过仅修改一个数字(0 或 1)来控制整个 spec 文件。您甚至可以离开 spec 文件,只需传递参数来控制。但是硬编码每次都需要更改 spec 文件中的许多位置,因此会增加构建失败的风险。

轻松安装软件包

如果您不在%build部分中使用条件,而是使用硬编码的 --with-gui,那么您需要添加BuildRequires: gtk2-devel到前言中。因此,输出的 RPM 默认情况下具有 GUI 支持,除非您使用一些技巧来避免它,例如引入一个子软件包 *-gui,并且不在其他子软件包和主软件包中“Requires”它。这个技巧的缺点是,即使您将选择权转移给最终用户,他们很可能不知道选择它。例如:

  • 不同的命名方案

如果用户想要安装pidgin并且需要 CLI 支持,如果他们不知道pidgin的 CLI 版本名为finch,他们将不容易找到 CLI 程序。如果他们运行 sudo zypper in pidgin,自动依赖解析器将不会选择finch.

  • 孤立软件包

如果用户使用一些自动孤立软件包检测器和清理器,例如rpmorphanYaST(但可能结果不同),他们可能会不小心删除它。

但是,如果您使用条件构建,即使您将条件设置为 true,当用户安装软件包时,如果无法满足先决条件,例如libgtk2未找到,RPM 包管理器将取消安装 GUI 相关子软件包并安装主软件包,除非您明确声明“Requires”*-gui 子软件包而不是“Recommends”它。

避免 构建服务黑名单 的明智方法

有时您必须使用条件而不是硬编码,否则我们的 构建服务 肯定无法托管您的应用程序。媒体播放器中有大量的实例,例如:clementine.

Clementine 提供 Spotify 在线音乐流媒体(仅限美国付费用户)通过一个商业但免费使用的库,名为libspotify。但是 OBS 无法托管所有专利。因此,您不能硬编码它(它会失败并抱怨“无法解析”libspotify)。

在这些情况下,条件可以完成“可选构建”任务,并在最大程度上取悦想要完整版本的用户。他们不需要构建一个libspotify16软件包本地,rpm -ivh 它及其-devel软件包,然后添加BuildRequires: libspotify-devel到您的 spec 文件的前言和 %files 部分来编译完整版本。

作为打包者,您可以添加条件并在文件中 BuildRequires%{_libdir}/libspotify.pc到您的前言中,然后在%files部分中可选地提供文件。然后用户可以轻松使用 configure && make && sudo make install 方法安装 libspotify,然后 sudo rpmbuild --rebuild --with libspotify 安装完整版本,而无需了解 spec 文件的规则,将source.tar.bz2和补丁放入SOURCES和 spec 文件放入SPECS

,然后他们可以运行 sudo rpmbuild -ba clementine.spec。他们这次可以得到 RPM 输出,而无需了解打包魔术。

这样,我们将传统的安装与 RPM 连接起来,从而节省了最终新手的精力和时间。

跨发行版

%if 0%{?suse_version} <= 1220 && 0%{?suse_version} > 1130
# Notice: it's not thread macros but parallel macros. macro can not thread.
BuildRequires: gtk2-devel
%endif

有时,由于发行版之间的差异,某些依赖项可能会达到“不需要但不会造成危害”的状态。通常我们使用

来解决这个问题。但是缺点是,该方法针对的是发行版之间命名差异的兼容性,它不是为处理“可能”软件包设计的;例如:您当然不能Requires: libktorrent3在 openSUSE 12.1 中,因为它libktorrent4;但另一个实例是digikam使用. 使用libkface的面部识别从未重命名,但在 openSUSE 11.4 之前,它不稳定,所以我们将其删除。但是,如果您安装它,也不会造成太大的危害。因此,我们可以定义一个条件with_libkfaceYaST并在 11.4 之前将其设置为 0。

默认情况下不发布此功能,但如果您想要它,rpmbuild --rebuild --with libkface 可能会帮助您;第三个实例是monopidgin第三个实例是中的支持。在 11.4 之后,我们不需要它,因为很少有插件,它的支持会使主程序不稳定,并且需要许多依赖项,所以我们计划将其删除。但是,由于我们只是在 11.3 中添加了支持,所以再次从用户那里夺走支持还为时过早,所以我们定义了一个with_mono第三个实例是条件并在 11.4 及以下版本中将其打开。要使用编写.net

插件,只需重建即可。

使用条件在使用这些说明的实际案例之前,您可能需要检查/usr/lib/rpm/macros

中的“Conditional build stuff.”部分。您的本地安装。

%bcond_with video
# Condition here is video; it's default off and needs to be activated with
# --with video command line switch.
# To get the default enabled variant use %bcond_without instead of %bcond_with.

首先,您需要在 spec 文件的前言中添加 %define 行

%if %{with video}
BuildRequires: v4l2-devel
%endif

并在 BuildRequires 部分添加这些%with() 扩展到 1 如果video

功能已启用,否则为 0。

%if %{with video}
%package video or %package -n libfoo-video
Summary: video plugin for package foo
Group: System/GUI/KDE
Provides: foo-visual = %{version}
Obsoletes: foo-visual < %{version}
%description video
blabla
%endif

如果您想拆分一个新的子软件包%build那么在

%configure \
       %{?with_video:--with-video} \
       --with-gui # Here we hard coded because we are sure this is not usable without gui

cmake -DCMAKE_INSTALL_PREFIX=%{_prefix} \
%if %{with video}
      -DENABLE_VIDEO \
%endif
      ..

部分

中配置它。%files

%files -n %{name}.lang
..
%if %{with video}
%{_libdir}/%{name}/libfoo-video.so.1.2.3
%dir %{_datadir}/%{name}/foo-video/
%{_datadir}/%{name}/foo-video/example 
(Can use %{_datadir}/%{name}/foo-video/ to replace those two lines above)
%dir %{_sysconfdir}/%{name}/
%config %{_sysconfdir}/%{name}/%{name}-video.conf
(Notice: sub and main package both need to declare to own %{_sysconfdir}/%{name} directory)
%endif
..

部分,使用:

%if %{with video}
%files -n libfoo-video
..
%endif

好极了!您现在设置了条件构建。

注意事项

  • %if 语句中,使用 %if %{with X}%if %{without X}。这将执行正确的操作,无论宏 X 是由 %bcond_with 还是 %bcond_without 引入的。
  • 没有必要添加额外的 0(例如 %if 0%{with video})。
  • 不要在 %if 语句中使用 %{with_x}
  • 在内联表达式中,使用 %{?with_X:value-if-yes}%{!?with_X:value-if-no}。例如:%{?with_debug:-g -O0}%{!?with_debug:-O3}
  • 不要在内联表达式中使用 %{with X}
  • 不要使用 %{without_X}
  • 不要在 spec 文件中定义 %define with_X 1%define without_X 1。而是分别使用 %bcond_without X%bcond_with X

备注

另一个重要的提示是,您必须注意,条件不像是“如果我定义了%bcond_with,那么%bcond_without也可用”。实际上,它们是两个完全不同的条件。也就是说,如果您定义了--with,现在您想使用--without,正确的方法是无操作,因为如果您定义了--with,默认值将保持为without。由于省略 --with foo 命令会导致 %{with foo} 扩展为 0(除非您手动with_foo 定义为某个值,当然)。

命令行选项 --with X 等效于 %define _with_X --with-X,同样适用于 --without。不建议使用这些扩展,尽管如此。与 %{_with_video} 相比,最好编写 %{?with_video:--with-video},因为后者(与前者不同)即使 spec 文件包含 %bcond_without video 并且命令行中未提供 --with video 也能工作。

条件变体

以下示例中的一些不正确。请阅读上面的注意事项部分。

还有一些常用的打包人员使用的条件变体,例如从%definepidgin:

%define with_mono 1
%if %with_mono
%endif

创建的条件。这种变体是通过 RPM 通用定义宏的方式实现的

%define something 0/1.

但是,这里只能是布尔值,0 或 1。随后的用法是 %if something。

第二个变体是通过 %with_something 0 定义的,然后使用

%if %{with_something} 
do something eg: --with-video
%endif

来使用。

以及没有在gegl:

%if 0%{?BUILD_ORIG}
%if 0%{?BUILD_ORIG_ADDON}
...
%endif
%endif

中声明/定义的条件。这种变体使用一种结合定义和使用的方法0表示默认布尔值,?表示“判断”。因此,整个语句意味着如果 BUILD_ORIG 条件满足,则将 if 引号内容的构建状态设置为 1,即构建它们;如果不满足,则使用默认值,即 0,不构建。

但是,我们不鼓励在可能对最终用户开放的软件包中使用这些变体(geglgimp的依赖库,最终用户不会编译它,如果他们这样做,他们就不再是最终用户了,因为它很难调整),因为它会给他们带来麻烦。他们必须使用

sudo rpmbuild -D 'BUILD_ORIG 1'

来重建。通常,这些变体用于判断构建环境而不是用户选项。

甚至,正如您可能发现的,宏,例如%{?suse_version}/%{?fedora_version}都是条件,但由 openSUSE 预定义的条件,并且它们更复杂,并捆绑在我们的 开放构建服务 中。

组合条件表达式

通常需要组合多个条件表达式。可以使用&&“与”和||“或”运算符组合条件。它们具有从左到右的结合性。&&优先于||。可以使用括号对表达式进行分组。!是否定运算符,它具有从右到左的结合性,并且具有最高的优先级。