Skip to content

Instantly share code, notes, and snippets.

@contyk

contyk/use-macros.md

Last active Apr 27, 2020
Embed
What would you like to do?
Fedora Feature Macros

Fedora Feature Macros

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.

Reasoning

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).

Usage

%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 to 0 or 1, or 0 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 the ncurses 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.

Compatibility with RPM's --with & --without options

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.

Implementation

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.

global.yaml

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.<package>.yaml

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

Maintanance

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.

Pros and cons

As with everything, adopting this idea has advantages and disadvantages.

Pros

  • 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

Cons

  • More packaging work for maintainers who opt-in
  • Requires extra steps to redefine local features
  • Potentially confusing to casual contributors

Try it out

I made a COPR package that defines some sample global features. Adding local features should be straightforward after examining the source package.

Ideas

This concept & implementation are drafts. Here are some ideas moving forward.

Initial list of global features

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.

Handling undefined features

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.

USE provides for packages that utilize them

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.

General availability

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}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.