Fedora feature macros (also dubbed USE macros) provide a simple way to configure common package build-time options, affecting available features, dependencies, or subpackages provided.
Packages are the main building blocks of our distribution. Introducing build-time, configurable options allows for additional packaging flexibility, greater potential testing, encourages cleaner dependencies and passing explicitly defined build-time options preventing unexpected build results when upstream defaults or the build environment change. Futhermore it gives our users, as well as downstream distributions or other variants, freedom to easily rebuild our packages without modification simply by adjusting the macros they provide.
USE macros define build features in a uniform manner for the entire buildroot and can be defined either globally or per package, enabling the usage of package-specific, non-standard features or explicit overrides of the default settings. The advantage of doing this on the distribution level, as opposed to macros set directly in the package, is the possibility to toggle these easily for end users and downstreams.
The concept and implementation are heavily inspired by Gentoo and their USE flags. It is completely optional and can be fully compatible even if the macros are unavailable (more on that in the Ideas section below).
%if %{use ssl}
BuildRequires: openssl-devel
%endif
%prep
%configure %{use_enable ssl openssl}
%check
make test %{?_use_ssl:-DSSL}
The snippet above adds a build dependency on openssl-devel
, calls ./configure --enable-openssl
, and tests with -DSSL
if the ssl
USE macro is set.
Features are defined by %_use_<feature>
binary macros and various helpers for common tasks are provided. These include:
%{use <feature>}
, expands to0
or1
, or0
if the feature isn't defined.%{use_enable <feature> [<configure name> [<configure option>]]}
, expands to--disable-<feature>
or--enable-<feature>
. If<configure name>
is provided,<feature>
is substituted for it. If<configure option>
is defined, it's passed as an argument. For example%{use_enable ncurses termcap tinfo}
would expand into--enable-termcap=tinfo
if thencurses
feature is set,--disable-termcap
otherwise.%{use_with <feature> [<configure name> [<configure option>]]}
, expands the same way as%use_enable
but uses the--with
and--without
keywords.
In addition to overriding the defaults with --define
, USE macros are fully compatible with RPM's --with
and --without
RPM options, making local overrides for rebuilds and testing fairly simple.
Feature macros as well as the helpers are defined in /usr/lib/rpm/macros.d/macros.use
. These need to be available in both RPM and SRPM buildroots to be effective. This is achieved by adding a runtime dependency to redhat-rpm-config
, for instance.
The macros file is provided by the use-macros
package which generates the file from data defined in YAML sources. These come in two variants that share the same format.
This file defines a common set of features with similar meaning across the package set and makes them available to all packages by default. An example global.yaml
would like like this:
- name: ncurses
description: Add support for ncurses.
enabled: true
- name: perl
description: Add support for Perl or Perl bindings.
enabled: true
- name: static_libs
description: Build static versions of dynamic libraries as well.
enabled: false
Local, per package definitions are used for special, package-specific features that don't necessarily make sense in the global context. They could also be used for package overrides of the global feature settings.
An example local file for the tinyfugue
package disables ncurses
support and defines its own feature, option102
:
- name: ncurses
description: Add support for ncurses.
enabled: false
- name: option102
description: Add support for Telnet OPTION 102.
enabled: true
Adding or changing macros consists entirely of editing or adding source YAML files in the use-macros
package and could be done directly or via pull requests.
Changes to the global set should require a blessing from a governing body. In Fedora good candidates would be either Fedora Packaging Committee or Fedora Engineering Steering Committee, however, the policy depends on the distribution.
As with everything, adopting this idea has advantages and disadvantages.
- End users, downstreams, spins, even modules can consume or provide different variants of packages without changing the sources
- Package builds are more flexible and robust
- Subjectively easy to use and understand
- Completely optional
- More packaging work for maintainers who opt-in
- Requires extra steps to redefine local features
- Potentially confusing to casual contributors
I made a COPR package that defines some sample global features. Adding local features should be straightforward after examining the source package.
This concept & implementation are drafts. Here are some ideas moving forward.
The initial list of global features should probably be limited. We could start with a couple of sources.
- Drawing from Gentoo again, we could adopt the most common flags used by the distribution that still make sense in our context.
- We could analyze Rawhide SPEC files and identify packages that use
%bcond
macros or conditions based on other, special macros such as%bootstrap
. - Going deeper, we could also look at the usage of
%fedora
and%rhel
and suggest features and ways to replace these with generic feature mechanisms.
Currently, querying undefined features with %use
returns zero and such features cannot be set using the --with
or --without
rpmbuild options. There are other possible ways this could behave.
- Still return zero but allow for
--with
and--without
overrides. - Throw an error or warn.
- Something else.
It might be useful for packages to provide something like use(<package>:<feature>) = true|false
so that packages that depend on some features being available can declare it in their dependencies.
It is common to use macros conditionally so that they still work even if they're not defined. Doing so makes SPEC files more complex and ugly. We could make these macros available in all Fedora and EPEL buildroots. Alternatively, to preserve compatibility, packagers would need to do something like this:
%if %{?use:%{use foo}}%{!?use:1}
BuildRequires: foo-devel
%endif
%prep
%configure %{?use_enable:%{use_enable foo}}
%check
make test %{?_use_foo:-DFOO}