RPM Spec 文件中的 %bcond_with和 %bcond_without这两个非常有用的条件宏。
核心概念:什么是 bcond?
bcond是 "build condition" 的缩写,即“构建条件”。它允许软件包维护者在构建(build)RPM 包时,通过命令行参数来启用或禁用某些功能、依赖项或代码路径。这极大地增强了 Spec 文件的灵活性和可配置性。
%bcond_with和 %bcond_without是定义这些构建条件的两种主要方式,它们互为镜像。
1. %bcond_with
这个宏用于定义一个默认关闭的选项。用户需要显式地使用 --with <option>来开启它。
语法和工作原理
在 Spec 文件中,你这样定义:
%bcond_with option_name
- 默认状态:
option_name是未定义的(%{with_option_name}会展开为空)。 - 检查方式:在 Spec 中,使用
%{with option_name}来判断该选项是否被启用。如果启用了,这个宏会展开为1,否则为空。 - 启用方法:在构建时,使用
--with option_name参数。rpmbuild -ba --with option_name mypackage.spec # 或者在 mock, koji 等工具中设置
典型使用场景
当你要添加的是一个非必需、可能带来额外依赖或仅部分用户需要的功能时,使用 %bcond_with。例如,一个程序有图形界面和纯文本模式,而图形界面需要安装 X11 库。
示例:构建一个带 GUI 功能的包
# 1. 定义条件:默认不构建 GUI
%bcond_with guiName: myapp
Version: 1.0
Release: 1%{?dist}
Summary: A useful application# 2. 根据条件处理依赖
# 如果启用了 GUI,则添加图形相关的依赖
%if %{with gui}
BuildRequires: libX11-devel, libXtst-devel
Requires: libX11, libXtst
%endif%description
A useful application with optional GUI.%prep
%autosetup%build
# 3. 将条件传递给 configure 脚本
%configure %{?_with_gui:--enable-gui} # 注意:这里传递的是 _with_gui
# 或者更常见的写法是直接让 configure 自己检测,但有时需要强制
./configure --prefix=%{_prefix} %{!?_without_gui:--enable-gui} # 另一种写法,与 bcond_without 配合%install
%make_install%files
%{_bindir}/myapp
%{_mandir}/man1/myapp.1.gz
# 4. 根据条件安装文件
%if %{with gui}
%{_datadir}/applications/myapp.desktop
%{_datadir}/icons/hicolor/...
%endif
构建命令:
- 构建无 GUI 版本(默认):
rpmbuild -ba myapp.spec - 构建有 GUI 版本:
rpmbuild -ba --with gui myapp.spec
2. %bcond_without
这个宏用于定义一个默认开启的选项。用户需要显式地使用 --without <option>来关闭它。
语法和工作原理
在 Spec 文件中,你这样定义:
%bcond_without option_name
- 默认状态:
option_name是已定义的(%{with_option_name}会展开为1)。 - 检查方式:同样使用
%{with option_name}来判断。如果没有用--without关闭,则展开为1;如果使用了--without,则展开为空。 - 禁用方法:在构建时,使用
--without option_name参数。rpmbuild -ba --without option_name mypackage.spec
典型使用场景
当你要添加的是一个核心、常用、但少数用户可能想排除的功能时,使用 %bcond_without。最常见的例子是 Python 模块 和 文档。大多数用户需要 Python 绑定,但有些最小化安装的环境可能不需要。
示例:构建一个可选的 Python 模块
# 1. 定义条件:默认构建 Python 模块
%bcond_without pythonName: mylib
Version: 1.0
Release: 1%{?dist}
Summary: A core library# 2. 根据条件处理依赖
# 如果 *不* 禁用 Python (即默认或 --with python),则添加 Python 依赖
%if %{with python}
BuildRequires: python3-devel
%py3_build
%endif%description
A core library with an optional Python module.%package -n python3-mylib
Summary: Python bindings for %{name}
Requires: %{name} = %{version}-%{release}
%description -n python3-mylib
This package provides the Python bindings for %{name}.%prep
%autosetup%build
%configure
%make_build
# 3. 根据条件构建 Python 模块
%if %{with python}
%py3_build
%endif%install
%make_install
# 4. 根据条件安装和创建子包
%if %{with python}
%py3_install
%files -n python3-mylib
%license LICENSE
%{python3_sitelib}/mylib/
%{python3_sitelib}/mylib-*.egg-info/
%endif%files
%{_libdir}/libmylib.so*
%{_includedir}/mylib.h
构建命令:
- 构建包含 Python 模块的版本(默认):
rpmbuild -ba mylib.spec - 构建不包含 Python 模块的版本(例如,为了最小化依赖):
rpmbuild -ba --without python mylib.spec
关键区别与使用指南
| 特性 | %bcond_with <option> |
%bcond_without <option> |
|---|---|---|
| 默认状态 | 关闭 (%{with ...}为空) |
开启 (%{with ...}为 1) |
| 如何启用 | --with <option> |
(默认即启用,无需操作) |
| 如何禁用 | (默认即禁用,无需操作) | --without <option> |
| 设计哲学 | “默认精简,按需添加” | “默认完整,按需移除” |
| 适用场景 | 附加功能、可选依赖、实验性特性 | 核心功能、常见依赖、文档、Python/Perl 模块 |
高级技巧和注意事项
- 在
rpmmacros中设置默认值 你可以修改用户的~/.rpmmacros或系统的/etc/rpm/macros文件来永久改变某个条件的默认行为,而无需每次都在命令行指定。# 在 ~/.rpmmacros 中 %_with_gui 1 # 将 %bcond_with gui 的默认行为改为开启 %_without_python 1 # 将 %bcond_without python 的默认行为改为关闭 - 与
%{?_with_foo:...}和%{!?_without_bar:...}配合使用 在./configure行或其他地方,你经常看到这种简写:%{?_with_foo:--enable-foo}:如果_with_foo被定义(即用户用了--with foo),则展开为--enable-foo,否则为空。%{!?_without_bar:--enable-bar}:如果_without_bar没有被定义(即用户没有用--without bar,也就是默认或用了--with bar),则展开为--enable-bar,否则为空。这在使用%bcond_without时非常方便。 - 检查条件是否定义 你可以用
%{defined with_...}来检查一个with_变量是否被定义,这比检查它是否为空更明确。%if %{defined with_gui} echo "GUI is defined, but is it enabled?" %if %{with_gui} echo "Yes, GUI is enabled." %else echo "No, GUI is defined but disabled (this shouldn't happen with bcond_with)." %endif %endif
总结
%bcond_with:为默认不构建的功能提供开关。用--with打开。%bcond_without:为默认构建的功能提供开关。用--without关闭。
选择哪一个取决于你对“正常”或“默认”构建的理解。对于 Fedora/RHEL 官方源中的软件包,%bcond_without在需要处理 Python/Perl 模块和文档时更为常见,因为确保这些组件可用是默认预期。而对于用户自己的或第三方仓库的包,如果功能很专业且非必需,%bcond_with是更好的选择。
熟练掌握这两个宏是编写专业、灵活、可维护的 RPM Spec 文件的关键一步。