RPM
Macros

RPM Macros

Technically, there are 3 types of "macros":

  • %{rpm_macro}: Normal RPM macros
  • %(echo "hi" | sed 's/hi/hello/'): Shell macros that expands to the output of the commands
  • %[v"1.2.3~1" < v"1.2.3"]: RPM expression

This document only covers the first type.

If you are new to RPM packaging, you should first check out the macros with the πŸ”° icon. On a computer, the right hand side of this page shows you the list of macros.

If you would like to learn more about the RPM macro syntax, check out Macro syntax.

Legend

  • πŸ”’ : Parameterized macro.
  • πŸ”¦ : Relatively uncommon.
  • πŸ”° : Relatively common.
  • 🚸 : Not recommended in Terra.
  • πŸ› οΈ : Advanced.
  • πŸ”© : Usually used with rpm -E but not in spec files.
  • 🏷️ : Usually defined but not used directly.

RPM Built-in Macros

Macro Manipulation

This list is obtained from rpmspec-rs (opens in a new tab), which is obtained from RPM docs (opens in a new tab).

%define πŸ”°

Defines a macro locally.

Macros defined with %define will not last to another RPM section. For example, if used in preamble section, the macro will be automatically undefined in %prep.

To make sure a macro lasts for the entire spec file, use %global instead.

example.spec
%build
%define my_macro Hello, World!
echo %my_macro
 
%install
echo %my_macro
expand.spec
%build
echo Hello, World!
 
%install
echo %my_macro

%global πŸ”°

Defines a macro globally.

As oppose to %define, macros defined with %global will last for the entire spec file.

example.spec
%global cat Meow!!!
 
%prep
echo nya %cat
 
%build
echo %cat
expand.spec
%prep
echo nya Meow!!!
 
%build
echo Meow!!!

%undefine πŸ”¦

Undefine a macro.

Technically this does not always undefine the macro specified. Instead, this pops the latest definition from the macro stack.

example.spec
%global cat Meow!
 
%prep
%global cat Nyaa!
echo %cat
 
%build
%undefine cat
echo %cat
 
%install
%undefine cat
echo %cat
expand.spec
%prep
echo Nyaa!
 
%build
echo Meow!
 
%install
echo %cat

%dnl πŸ”°

Comments.

Similar to # …, this can comment out anything in %{dnl:…} or after %dnl …. The difference with # … is that %dnl prevents macro expansions, while macros in # … are actually expanded.

example.spec
	%define meow a
	# comment %meow
	%dnl comment %meow
expand.spec
# comment a

%{load:…} πŸ”¦πŸšΈ

Load a macro file.

%{expr:…} πŸ”¦

Expand all macros in the body, then evaluate the body as an expression.

$ rpm -D 'foo 1 + 2' -E '%[%foo]'
error: macro expansion did not return an integer: %foo
error:                                            ^
error: expanded string: 1 + 2
 
$ rpm -D 'foo 1 + 2' -E '%{expr:%foo}'
3

%{expand:…} πŸ› οΈ

Double-expand the macro.

example.spec
%define nya foo
%define meow %{expand:%%nya}
 
echo %meow
expand.spec
echo foo

%{lua:…} πŸ› οΈ

Embed lua code.

⚠️

Since RPM expands macros when the source RPM (SRPM) is created, the lua code is executed during that stage.

This means that the lua code is executed not during the stage it is defined in, but way before even %prep.

⚠️

Do NOT comment out lua code using # …, %dnl … or %{dnl:…}.

Lua comments must start with -- ….

In rpmspec-rs, a space must be present after --.

Documentations for the embedded Lua interpreter, including specific functions available, are available at https://rpm-software-management.github.io/rpm/manual/lua.html (opens in a new tab).

example.spec
%global my_macro Hello
%{lua:
  -- this lua block expands to the input
  -- to all `print()` statements
  print("%prep\necho " ..
    rpm.expand("%my_macro from lua!") ..
    "\n")
}
expand.spec
%prep
echo Hello from lua!

%{macrobody:…} πŸ”©

Expand to the definition of the given macro.

$ rpm -E '%dist'
.um41
 
$ rpm -E '%macrobody dist'
%{?distprefix}.um41%{?with_bootstrap:~bootstrap}

%{quote:…} πŸ”¦πŸ› οΈ

Does nothing. You heard me, this macro literally does nothing.

But because of that it's very useful. This can be used to pass arguments with spaces to parameterized macros:

example.spec
echo %gsub %{quote:hello hello} %{quote:^hello} bye
expand.spec
echo bye hello

String Operations

%gsub … πŸ”’πŸ”¦πŸ› οΈ

Equivalent to %{lua: string.gsub("%1", "%2", %3, %?4)}, where %n represents the n-th argument to %gsub.

In short, this replaces %?4 occurrences of %2 in %1 to %3. %4 is optional and defaults to infinity.

%2 is treated as a lua pattern (opens in a new tab).

example.spec
echo %gsub %{quote:hello hello} %{quote:^hello} bye
expand.spec
echo bye hello

%{len:…}

Obtains the length of the given text.

example.spec
%define foo meow now!
echo %{len:%foo}
expand.spec
echo 9

%{lower:…}

Turn the input into ASCII-lowercase.

%{upper:…}

ASCII-uppercase.

%rep … πŸ”’πŸ”¦

Repeat the first input (%1) n-times, n given by %2 (lua string.rep()).

%{reverse:…}

%sub … πŸ”’πŸ”°

Expand to substring (lua string.sub()).

$ rpm -E '%sub abc 2 3'
bc

%{shescape:…}

Wrap the input with single quotes and also escape preexisting single-quotes correctly, so that the input can be safely passed as arguments to shell commands.

$ rpm -E "echo %{shescape:this is some 'text'}"
echo 'this is some '\''text'\'''
 
$ rpm -E "%(echo %{shescape:this is some 'text'})"
this is some 'text'

%{shrink:…}

Trim leading and trailing ASCII-whitespace, and reduce intermediate whitespace to a single space.

File and Path Operations

%{basename:…} πŸ”¦

basename(1) macro analogue

%{dirname:…} πŸ”¦

dirname(1) macro analogue

%{exists:…} πŸ”¦

test file existence, expands to 1/0

%{suffix:…} πŸ”¦

expand to suffix part of a file name

%{url2path:…} πŸ”¦

convert url to a local path

%{uncompress:…}

expand to a command for outputting argument file to stdout, uncompressing as needed

This macro is used in %setup … for uncompressing source files.

Environment info

%{getncpus:…}

When used as %{getncpus} with no arguments, expand to the number of available CPU cores.

Also accept extra arguments:

accepts arguments total, proc and thread, additionally accounting for available memory (e.g. address space limitations for threads)

However, %getncpus total, %getncpus proc and %getncpus thread produce the same results as far as concerned. It is suspected that the arguments in most cases have no effect to the output.

%getconfdir πŸ”¦

expand to rpm "home" directory (typically /usr/lib/rpm)

%{getenv:...} πŸ”¦

getenv(3) macro analogue

%rpmversion πŸ”¦

expand to running rpm version

Output

%{echo:…} πŸ”¦

print ... to stdout

⚠️

The printing happens when the macro is evaluated and expanded, not at the section/position it is written.

%{warn:…} πŸ”¦

print warning: ... to stderr

Produce an RPM warning, which is immediately shown in console stderr, and displayed again at the end of the rpmbuild execution (after Executing(rmbuild)).

⚠️

The printing happens when the macro is evaluated and expanded, not at the section/position it is written.

%{error:…} πŸ”¦

print error: ... to stderr and return an error

Produce an RPM warning, which is immediately shown in console stderr, then immediately perform Executing(rmbuild) and exit with a summary of all warnings and errors.

⚠️

The printing happens when the macro is evaluated and expanded, not at the section/position it is written.

%{verbose:…} πŸ”¦

%verbose: expand to 1 if rpm is in verbose mode, 0 if not

%{verbose:...}: expand to ... if rpm is in verbose mode, the empty string if not

Spec-specific Macros

%{S:…}

expand ... to <source> file name

Expand to the path to the source file. This is just a fancy macro that transforms %{S:123} to %SOURCE123.

example.spec
Source0: https://repos.fyralabs.com/um42/comps.xml
 
%prep
cat %{S:0}
echo %{S:1}
expand.spec
Source0: https://repos.fyralabs.com/um42/comps.xml
 
%prep
cat %{_sourcedir}/comps.xml
echo %SOURCE1

%{P:…}

expand ... to <patch> file name

As you may have guessed, this is basically the Patch: version of %{S:…}.

Diagnostics

%trace πŸ”¦πŸ› οΈ

toggle print of debugging information before/after expansion

%dump πŸ”©

print the active (i.e. non-covered) macro table

When you want to find a macro that has the string x86_64_v3, you can run:

rpm -E '%dump' 2>&1 | grep x86_64_v3

Or you can just use less instead of grep.

%__file_name πŸ”¦

current file (if parsing a file)

%__file_lineno πŸ”¦

line number in current file (if parsing a file)

Common Macros

Miscellaneous

%nil πŸ”°

Expand to nothing.

Defined as %{!?nil}.

In Preamble

%forgemeta … πŸ”’πŸ”°

See https://docs.fedoraproject.org/en-US/packaging-guidelines/SourceURL/#_using_forges_hosted_revision_control (opens in a new tab).

Parameterized as %forgemeta(z:isva):

  • -v: verbose
  • -i: informative
  • -s: silent
  • -a: process all source archives (when you have %forgeurl0, %forgeurl1, …)
  • -z n: process the n-th source archive

%forgeautosetup … πŸ”’

Somehow pretty rare.

Parameterized as %forgeautosetup(z:vNS:p:q).

%forgesetup … πŸ”’πŸ”°

Parameterized as %forgesetup(z:va).

%forgeversion … πŸ”’πŸ”¦

Parameterized as %forgeversion(pz:ab:).

%debug_package πŸ”°πŸ·οΈ

When you see:

error: Empty %files file %_builddir/%buildsubdir/debugsourcefiles.list

This means RPM is trying to generate a %{name}-debuginfo/%{name}-debugsource package. You can disable this automatic behavior by setting:

%define debug_package %nil

Then RPM will not strip debug symbols.

%_build_id_links 🏷️

When you see problems with /usr/lib/.build-id, try setting this to none.

In /usr/lib/rpm/macros:

#
# Defines how and if build_id links are generated for ELF files.
# The following settings are supported:
#
# - none
#   No build_id links are generated.
#
# - alldebug
#   build_id links are generated only when the __debug_package global is
#   defined. This will generate build_id links in the -debuginfo package
#   for both the main file as /usr/lib/debug/.build-id/xx/yyy and for
#   the .debug file as /usr/lib/debug/.build-id/xx/yyy.debug.
#   This is the old style build_id links as generated by the original
#   find-debuginfo.sh script.
#
# - separate
#   build_id links are generate for all binary packages. If this is a
#   main package (the __debug_package global isn't set) then the
#   build_id link is generated as /usr/lib/.build-id/xx/yyy. If this is
#   a -debuginfo package (the __debug_package global is set) then the
#   build_id link is generated as /usr/lib/debug/.build-id/xx/yyy.
#
# - compat
#   Same as for "separate" but if the __debug_package global is set then
#   the -debuginfo package will have a compatibility link for the main
#   ELF /usr/lib/debug/.build-id/xx/yyy -> /usr/lib/.build-id/xx/yyy
%_build_id_links compat

%_missing_build_ids_terminate_build 🏷️

Still having problems with /usr/lib/.build-id? Try setting this to 0.

In /usr/lib/rpm/macros:

#
# Should an ELF file processed by find-debuginfo.sh having no build ID
# terminate a build?  This is left undefined to disable it and defined to
# enable.
#
%_missing_build_ids_terminate_build    1

%__strip 🏷️

In some cases (e.g. JavaScript binaries) stripping the binaries may break the executable. To disable stripping, override the strip call:

%global __strip /bin/true

%__requires_exclude 🏷️

Since rpmbuild automatically generates Requires: by looking at what things the binaries are linked to, you may disable that by adding a regex to this macro:

example.spec
# https://github.com/terrapkg/packages/blob/7f0fd6b6a8993d17832403960d4343899cfb0474/anda/apps/youtube-music/youtube-music.spec#L5
%define debug_package %nil
 
# Exclude private libraries since this is bundled with electron
%global __provides_exclude ^((libffmpeg[.]so.*)|(lib.*\\.so.*))$
%global __requires_exclude ^((libffmpeg[.]so.*)|(lib.*\\.so.*))$
If you want to disable all automatic Requires:, set AutoReq: 0.

%__requires_exclude_from 🏷️

example.spec
# https://github.com/terrapkg/packages/blob/7f0fd6b6a8993d17832403960d4343899cfb0474/anda/system/opentabletdriver/opentabletdriver.spec#L6
%global __requires_exclude_from ^/usr/lib/opentabletdriver/.*$

%__provides_exclude 🏷️

Similarly, since rpmbuild automatically generates Provides:, you may disable that:

example.spec
# https://github.com/terrapkg/packages/blob/7f0fd6b6a8993d17832403960d4343899cfb0474/anda/apps/youtube-music/youtube-music.spec#L4
%define debug_package %nil
 
# Exclude private libraries since this is bundled with electron
%global __provides_exclude ^((libffmpeg[.]so.*)|(lib.*\\.so.*))$
%global __requires_exclude ^((libffmpeg[.]so.*)|(lib.*\\.so.*))$
If you want to disable all automatic Provides:, set AutoProv: 0.

%__provides_exclude_from 🏷️

example.spec
# https://github.com/terrapkg/packages/blob/7f0fd6b6a8993d17832403960d4343899cfb0474/anda/devs/asar/asar.spec#L7
%define debug_package %nil
%define __strip /bin/true
%global _build_id_links none
 
# Exclude private libraries
%global __requires_exclude libffmpeg.so
%global __provides_exclude_from %{_datadir}/%{name}/.*\\.so

%__brp_mangle_shebangs_exclude_from πŸ”°πŸ·οΈ

RPM tries to mangle shebangs (#!) by default (see %__brp_mangle_shebangs). If this breaks, you can ask it to exclude the files that matches a given regex.

example.spec
# https://github.com/terrapkg/packages/blob/frawhide/anda/devs/zed/nightly/zed-nightly.spec#L9
 
%bcond_with check
 
# Exclude input files from mangling
%global __brp_mangle_shebangs_exclude_from ^/usr/src/.*$
 
%global crate zed
%global app_id dev.zed.Zed-Nightly
 
%global rustflags_debuginfo 0

%__brp_mangle_shebangs_exclude 🏷️

Similar to above, but takes in regex for shebangs to exclude instead.

%__brp_mangle_shebangs_exclude_file 🏷️

Similar to above, but takes in path to file that contains the regexes.

%__brp_mangle_shebangs_exclude_from_file 🏷️

%__brp_mangle_shebangs 🏷️

As a last resort to fixing bugs related to shebangs mangling shenanigans, undefine this macro. You really should try using %__brp_mangle_shebangs_exclude_from first though.

%bcond_with … πŸ”’πŸ”°

DISABLE a "conditional".

Parameterized as %bcond_with(). Takes in 1 argument: the name of the "conditional".

For more information: https://rpm-software-management.github.io/rpm/manual/conditionalbuilds.html (opens in a new tab)

%bcond_without … πŸ”’πŸ”°

ENABLE a "conditional".

Parameterized as %bcond_without(). Takes in 1 argument: the name of the "conditional".

For more information: https://rpm-software-management.github.io/rpm/manual/conditionalbuilds.html (opens in a new tab)

In %prep

%autosetup … πŸ”’πŸ”°

Uncompress the specified sources. By default, uncompress %{SOURCE0} (i.e. %{S:0}).

Parameterized as %autosetup(a:b:cCDn:TvNS:p:):

  • -n …: specify the top-level directory (i.e. root folder) name in the archive (default %{name}-%{version})
  • -p n: specify the -p n argument to %autopatch …
  • -a n: unpack %{S:n} after changing directory
  • -b n: unpack %{S:n} before changing directory
  • -c: create directory, cd into it, then unpack the source (useful if archive lacks top-level directory)
  • -C: I've no idea what this does! β€” Mado
  • -D: do not delete folder before unpacking sources
  • -T: disable unpacking %SOURCE0
  • -v: verbose. If specified, %autosetup will omit the -q argument to %setup
  • -S …: specify the SCM, equivalent to %global __scm … then %__scm_setup_git (replace git with the argument). With this argument, the specified SCM will be set up in the uncompressed directory
  • -N: do not run %autopatch …

This is actually a wrapper around %setup and %autopatch ….

If you need to unpack multiple sources, see: http://ftp.rpm.org/max-rpm/s1-rpm-inside-macros.html (opens in a new tab)

%setup … πŸ”’πŸšΈ

Uncompress the specified sources. By default, uncompress %{SOURCE0} (i.e. %{S:0}).

⚠️

Only available in a spec file.

In most cases you should use %autosetup … instead. If you would like to use %setup …, we recommend adding the -q flag.

Parameterized as %setup(a:b:cCDn:Tq): (need confirmation)

  • -n …: specify the top-level directory (i.e. root folder) name in the archive (default %{name}-%{version})
  • -a n: unpack %{S:n} after changing directory
  • -b n: unpack %{S:n} before changing directory
  • -c: create directory, cd into it, then unpack the source (useful if archive lacks top-level directory)
  • -C: I've no idea what this does! β€” Mado
  • -D: do not delete folder before unpacking sources
  • -T: disable unpacking %SOURCE0
  • -q: quiet, because by default %setup … is verbose

If you need to unpack multiple sources, see: http://ftp.rpm.org/max-rpm/s1-rpm-inside-macros.html (opens in a new tab)

The %setup … macro does 3 things:

  • prepare the destination
    • %global %{buildsubdir} to the argument given in -n …
    • mkdir -p %{builddir}
      • note: %{builddir} is defined only during rpmbuild
      • see %{_builddir}
    • cd %{builddir}
  • uncompress the archive
  • cd %{?buildsubdir}

%autopatch … πŸ”’

Apply patches.

Wrapper around %__apply_patch. No, this does not wrap around %patch!

Parameterized as %autopatch(vqp:m:M:):

  • -v: verbose
  • -q: don’t warn if there are no matching patches
  • -p n: number of top-level path components to strip (default 0)
    For example, if the patch uses the path a/b/hello.txt, passing in -p1 will strip it to b/hello.txt, and passing in -p2 will strip it to just hello.txt.
  • -m n: apply patches starting from n (default 0)
  • -M n: apply patches up to n (default to the last patch number for Patch:)

Examples:

  • %autopatch -m 100: apply patches with number >= 100
  • %autopatch -M 400: apply patches with number <= 400
  • %autopatch -m 80 -M 99: apply patches 80 to 99 inclusive
  • %autopatch 1 4 6: apply patches 1, 4 and 6

Reference: https://rpm-software-management.github.io/rpm/manual/autosetup.html#autopatch (opens in a new tab)

%patch … πŸ”’πŸšΈ

Apply a patch.

⚠️

Only available in a spec file.

We recommend %autopatch over this.

Parameterized as %patch(b:p:P:REz:).

Paths

This list is NOT exhaustive. Some macros are not built-in. If your machine does not come with them, try dnf install 'rpm_macro(macro_name)'.

/
┣╸boot
┃ β”—β•Έefi
┃   β”—β•ΈEFI             β‡’ %{efi_esp_efi}
┃     β”—β•Έfedora        β‡’ %{efi_esp_dir}
┣╸etc                 β‡’ %{_sysconfdir}
┃ ┣╸fonts             β‡’ %{_fontconfig_masterdir}
┃ ┃ β”—β•Έconf.d          β‡’ %{_fontconfig_confdir}
┃ ┣╸java              β‡’ %{_javaconfdir}
┃ ┣╸jvm-common        β‡’ %{_jvmcommonsysconfdir}
┃ ┣╸jvm               β‡’ %{_jvmsysconfdir}
┃ ┣╸pam.d             β‡’ %{_pam_confdir}
┃ ┣╸rc.d
┃ ┃ β”—β•Έinit.d          β‡’ %{_initddir} %{_initrddir}
┃ β”—β•Έsecurity          β‡’ %{_pam_secconfdir}
┣╸run                 β‡’ %{_rundir} %{_runstatedir}
┣╸usr                 β‡’ %{_usr} %{_prefix} %{_exec_prefix}
┃ ┣╸bin               β‡’ %{_bindir} (%{fedora}>=42: %{_sbindir})
┃ ┣╸sbin              β‡’ %{_sbindir} (older fedora versions)
┃ ┣╸src               β‡’ %{_usrsrc}
┃ ┣╸tmp               β‡’ %{_tmppath}
┃ ┣╸include           β‡’ %{_includedir} %{_oldincludedir}
┃ ┃ β”—β•Έd               β‡’ %{_d_includedir}
┃ ┣╸lib64             β‡’ %{_libdir} %{_pam_libdir} %{_usr}/%{_lib}
┃ ┃ ┣╸gfortran
┃ ┃ ┃ β”—β•Έmodules       β‡’ %{_fmoddir}
┃ ┃ ┣╸lua
┃ ┃ ┃ β”—β•Έ%{lua_version} β‡’ %{lua_libdir}
┃ ┃ ┣╸ocaml           β‡’ %{ocamldir}
┃ ┃ β”—β•Έsecurity        β‡’ %{_pam_moduledir}
┃ ┣╸lib               β‡’ %{_libdir} ONLY ON `noarch` OR 32-BIT PLATFORMS
┃ ┃ ┣╸binfmt.d        β‡’ %{_binfmtdir}
┃ ┃ ┣╸clang
┃ ┃ ┃ β”—β•Έ%{clang_major_version} β‡’ %{clang_resource_dir}
┃ ┃ ┣╸environment.d   β‡’ %{_environmentdir}
┃ ┃ ┣╸java            β‡’ %{_jnidir}
┃ ┃ ┣╸jvm             β‡’ %{_jvmdir} %{_jvmlibdir}
┃ ┃ ┣╸jvm-common      β‡’ %{_jvmcommonlibdir}
┃ ┃ ┣╸jvm-private     β‡’ %{_jvmprivdir}
┃ ┃ ┣╸kernel
┃ ┃ ┃ β”—β•Έinstall.d     β‡’ %{_kernel_install_dir}
┃ ┃ ┣╸modprobe.d      β‡’ %{_modprobedir}
┃ ┃ ┣╸modules-load.d  β‡’ %{_modulesloaddir}
┃ ┃ ┣╸mono            β‡’ %{_monodir}
┃ ┃ ┃ β”—β•Έgac           β‡’ %{_monogacdir}
┃ ┃ ┣╸rpm             β‡’ %{_rpmconfigdir} %{getconfdir}
┃ ┃ ┃ ┣╸fileattrs     β‡’ %{_fileattrsdir}
┃ ┃ ┃ ┣╸lua           β‡’ %{_rpmluadir}
┃ ┃ ┃ β”—β•Έmacros.d      β‡’ %{_rpmmacrodir} %{rpmmacrodir}
┃ ┃ ┣╸sysctl.d        β‡’ %{_sysctldir}
┃ ┃ ┣╸sysusers.d      β‡’ %{_sysusersdir}
┃ ┃ ┣╸swidtag
┃ ┃ ┃ β”—β•Έfedoraproject.org β‡’ %{_swidtagdir}
┃ ┃ ┣╸systemd         β‡’ %{_systemd_util_dir}
┃ ┃ ┃ ┣╸catalog       β‡’ %{_journalcatalogdir}
┃ ┃ ┃ ┣╸user          β‡’ %{_userunitdir}
┃ ┃ ┃ ┣╸user-environment-generators   β‡’ %{_systemd_user_env_generator_dir}
┃ ┃ ┃ ┣╸user-preset   β‡’ %{_userpresetdir}
┃ ┃ ┃ ┣╸user-generators   β‡’ %{_systemdusergeneratordir}
┃ ┃ ┃ ┣╸system        β‡’ %{_unitdir}
┃ ┃ ┃ ┣╸system-environment-generators β‡’ %{_systemd_system_env_generator_dir}
┃ ┃ ┃ ┣╸system-generators β‡’ %{_systemdgeneratordir}
┃ ┃ ┃ β”—β•Έsystem-preset β‡’ %{_presetdir}
┃ ┃ ┣╸tmpfiles.d      β‡’ %{_tmpfilesdir}
┃ ┃ β”—β•Έudev
┃ ┃   ┣╸hwdb.d        β‡’ %{_udevhwdbdir}
┃ ┃   β”—β•Έrules.d       β‡’ %{_udevrulesdir}
┃ ┣╸libexec           β‡’ %{_libexecdir}
┃ ┃ β”—β•Έperl5-tests     β‡’ %{perl_testdir}
┃ β”—β•Έshare             β‡’ %{_datadir} %{_datarootdir}
┃   ┣╸bash-completion
┃   ┃ β”—β•Έcompletions   β‡’ %{bash_completions_dir}
┃   ┣╸cargo
┃   ┃ β”—β•Έregistry      β‡’ %{cargo_registry}
┃   ┃   β”—β•Έ%{crate}-%{version_no_tilde} β‡’ %{crate_instdir}
┃   ┣╸color           β‡’ %{_colordir} %{_syscolordir}
┃   ┃ ┣╸cmms          β‡’ %{_cmmscolordir}
┃   ┃ ┣╸icc           β‡’ %{_icccolordir}
┃   ┃ β”—β•Έsettings      β‡’ %{_settingscolordir}
┃   ┣╸doc             β‡’ %{_docdir} %{_defaultdocdir}
┃   ┃ β”—β•Έ%{name}      β‡’ %{_pkgdocdir}
┃   ┣╸elvish
┃   ┃ β”—β•Έlib
┃   ┃   β”—β•Έcompletions β‡’ %{elvish_completions_dir}
┃   ┣╸fish
┃   ┃ β”—β•Έvendor_completions.d β‡’ %{fish_completions_dir}
┃   ┣╸fonts           β‡’ %{_fontbasedir}
┃   ┣╸fontconfig
┃   ┃ β”—β•Έconf.avail    β‡’ %{_fontconfig_templatedir}
┃   ┣╸icons           β‡’ %{_iconsdir}
┃   ┣╸info            β‡’ %{_infodir}
┃   ┣╸ivy-xmls        β‡’ %{_ivyxmldir}
┃   ┣╸java            β‡’ %{_javadir}
┃   ┣╸javadoc         β‡’ %{_javadocdir}
┃   ┣╸jvm             β‡’ %{_jvmdatadir}
┃   ┣╸jvm-common      β‡’ %{_jvmcommondatadir}
┃   ┣╸licenses        β‡’ %{_defaultlicensedir}
┃   ┣╸lua
┃   ┃ β”—β•Έ%{lua_version} β‡’ %{lua_pkgdir}
┃   ┣╸man             β‡’ %{_mandir}
┃   ┣╸maven-poms      β‡’ %{_mavenpomdir}
┃   ┣╸metainfo        β‡’ %{_metainfodir}
┃   ┣╸pam.d           β‡’ %{_pam_vendordir}
┃   ┣╸pkgconfig
┃   ┃ β”—β•Έpersonality.d β‡’ %{pkgconfig_personalitydir}
┃   ┣╸systemtap
┃   ┃ β”—β•Έtapset        β‡’ %{_systemtap_tapsetdir}
┃   ┣╸user-tmpfiles.d β‡’ %{_user_tmpfilesdir}
┃   ┣╸zsh
┃   ┃ β”—β•Έsite-functions β‡’ %{zsh_completions_dir}
┃   β”—β•Έ%{python_wheel_pkg_prefix}-wheels β‡’ %{python_wheel_dir}
β”—β•Έvar                 β‡’ %{_var} %{_localstatedir}
  β”—β•Έlib               β‡’ %{_sharedstatedir}
/home/mado/rpmbuild   β‡’ %{_topdir}
┣╸BUILD               β‡’ %{_builddir}
┃ β”—β•Έ%{buildsubdir}
┃   ┣╸.pyproject-builddir%{_pyproject_files_pkgversion} β‡’ %{_pyproject_builddir}
┃   β”—β•Έpyproject-wheeldir%{_pyproject_files_pkgversion}  β‡’ %{_pyproject_wheeldir}
┣╸RPMS                β‡’ %{_rpmdir}
┣╸SOURCES             β‡’ %{_sourcedir}
┣╸SPECS               β‡’ %{_specdir}
β”—β•ΈSRPMS               β‡’ %{_srcrpmdir}

Common Paths πŸ”°

/etc                                   β‡’ %{_sysconfdir}
/run                                   β‡’ %{_rundir}
/usr                                   β‡’ %{_usr}
/usr/bin                               β‡’ %{_bindir}
/usr/sbin                              β‡’ %{_sbindir} maybe?
/usr/src                               β‡’ %{_usrsrc}
/usr/include                           β‡’ %{_includedir}
/usr/lib64                             β‡’ %{_libdir}
/usr/lib                               β‡’ %{_libdir} if `noarch` or 32-bit platforms
/usr/lib/sysctl.d                      β‡’ %{_sysctldir}
/usr/lib/sysusers.d                    β‡’ %{_sysusersdir}
/usr/lib/systemd/user                  β‡’ %{_userunitdir}
/usr/lib/systemd/user-preset           β‡’ %{_userpresetdir}
/usr/lib/systemd/system                β‡’ %{_unitdir}
/usr/lib/systemd/system-preset         β‡’ %{_presetdir}
/usr/libexec                           β‡’ %{_libexecdir}
/usr/share                             β‡’ %{_datadir}
/usr/share/doc                         β‡’ %{_docdir}
/usr/share/icons                       β‡’ %{_iconsdir}
/usr/share/info                        β‡’ %{_infodir}
/usr/share/licenses                    β‡’ %{_defaultlicensedir}
/usr/share/man                         β‡’ %{_mandir}
/usr/share/metainfo                    β‡’ %{_metainfodir}
/var                                   β‡’ %{_var}
/var/lib                               β‡’ %{_sharedstatedir}

/usr/share/bash-completion/completions β‡’ %{bash_completions_dir}
/usr/share/elvish/lib/completions      β‡’ %{elvish_completions_dir} (Terra only)
/usr/share/fish/vendor_completions.d   β‡’ %{fish_completions_dir}
/usr/share/zsh/site-functions          β‡’ %{zsh_completions_dir}

In %install

%buildroot πŸ”°

⚠️

Only available in a spec file.

Path to a specific directory where files, that get packaged into the final RPM, should live. This is the same as $pkgdir in Arch Linux PKGBUILDs.

%find_lang πŸ”°

From /usr/lib/rpm/find-lang.sh:

automagically generate list of language specific files
for inclusion in an rpm spec file.
This does assume that the *.mo files are under .../locale/...

Expand to exactly /usr/lib/rpm/find-lang.sh %{buildroot}. Used with the -f flag for %files. Useful for packaging /usr/share/locale/*/LC_MESSAGES/%{app_id}.mo files.

$ /usr/lib/rpm/find-lang.sh

Usage: /usr/lib/rpm/find-lang.sh TOP_DIR PACKAGE_NAME [prefix]

where TOP_DIR is
the top of the tree containing the files to be processed--should be
$RPM_BUILD_ROOT usually. TOP_DIR gets sed'd out of the output list.
PACKAGE_NAME is the %{name} of the package. This should also be
the basename of the .mo files.  the output is written to
PACKAGE_NAME.lang unless $3 is given in which case output is written
to $3.
Additional options:
  --with-gnome          find GNOME help files
  --with-mate           find MATE help files
  --with-kde            find KDE help files
  --with-qt             find Qt translation files
  --with-html           find HTML files
  --with-man            find localized man pages
  --all-name            match all package/domain names
  --without-mo          do not find locale files
  --generate-subpackages move language files in one sub package per language
example.spec
# https://github.com/terrapkg/packages/blob/2635988e63fa5a05bd2c42d34c893d9e2bb13580/anda/apps/mugshot/mugshot.spec#L41-L46
%install
%pyproject_install
%pyproject_save_files -L 'mugshot*'
install -Dm644 data/glib-2.0/schemas/%{lower:%app}.gschema.xml %buildroot%_datadir/glib-2.0/schemas/
 
%find_lang mugshot
 
%files -f %{pyproject_files} -f mugshot.lang
%doc README.md NEWS
…

%fdupes … πŸ”’

Hard-link (or symlink) files to save space.

Parameterized as %fdupes(s):

  • -s: use symlinks instead of hard links

Take in exactly 1 argument: a glob path that lists out files to be deduplicated.

example.spec
# https://github.com/terrapkg/packages/blob/2635988e63fa5a05bd2c42d34c893d9e2bb13580/anda/themes/fluent-theme/fluent-theme.spec#L31
BuildRequires:  fdupes
# or BuildRequires:  rpm_macro(fdupes)
 
…
 
%install
mkdir -p %{buildroot}%{_datadir}/themes
./install.sh -i simple -t all -d %{buildroot}%{_datadir}/themes
./install.sh -i simple -t all --tweaks round float -d %{buildroot}%{_datadir}/themes
 
%fdupes %buildroot%_datadir
 
%files
%license COPYING
%doc README.md
 
%{_datadir}/themes/Fluent*/
πŸ’‘

Use %{quote:…} to feed in multiple paths:

%fdupes %{quote:%buildroot%_datadir %buildroot%_sysconfdir}

Per Language

If your language / buildsystem is not listed here, go to https://docs.fedoraproject.org/en-US/packaging-guidelines/ (opens in a new tab) and find them on the navigation rail on the left.

C and C++

%configure

Expand to ./configure with the correct flags and arguments. If you need to execute ./configure, in most cases you should use this macro instead.

In some cases where ./configure is not directly available, you may need to run autoreconf -fi or -fiv.

%make_build πŸ”°

β‡’ %__make -O -j${RPM_BUILD_NCPUS} V=1 VERBOSE=1, where %{__make} β‡’ /usr/bin/make. Further arguments to make can be attached.

%make_install πŸ”°

β‡’ %__make install DESTDIR=%?buildroot INSTALL="%__install -p".

%makeinstall 🚸

Deprecated. Use %make_install instead.

%cmake πŸ”°

Expand to %{__cmake} (β‡’ /usr/bin/cmake) with the correct flags, such as -B "%__cmake_builddir" (β‡’ -B "redhat-linux-build").

%cmake_build πŸ”°

β‡’ %__cmake --build "%__cmake_builddir" %?_smp_mflags --verbose.

%cmake_install πŸ”°

β‡’ DESTDIR="%buildroot" %__cmake --install "%__cmake_builddir".

%meson πŸ”°

β‰ˆ %__meson setup with correct flags.

%meson_build πŸ”°

β‡’ %__meson compile -C "%_vpath_builddir" -j %_smp_build_ncpus %{?__meson_verbose:--verbose}

β‡’ /usr/bin/meson compile -C redhat-linux-build -j 20 --verbose (on my machine).

%meson_install πŸ”°

β‡’ DESTDIR=%buildroot %__meson install -C %_vpath_builddir --no-rebuild. Used in %install.

%meson_test πŸ”°

β‡’ %__meson test -C %_vpath_builddir --no-rebuild %{!?__meson_verbose:--quiet}. Used in %check.

%ninja_build

β‡’ %__ninja %__ninja_common_opts

β‡’ %__ninja -v -j${RPM_BUILD_NCPUS}.

%ninja_install

β‡’ DESTDIR=%buildroot %__ninja install %__ninja_common_opts.

%ninja_test

β‡’ %__ninja test %__ninja_common_opts.

%build_cflags 🏷️

Flags to be passed to the C compiler.

β‡’ %{__build_flags_lang_c} %{?_distro_extra_cflags}

%_distro_extra_cflags 🏷️

For example, if the compiler is emitting errors that could be disabled with some -Wno-… flags, you can define this macro with extra flags.

This macro is not defined by default on Fedora.

%build_cxxflags 🏷️

Flags to be passed to the C++ compiler.

β‡’ %{__build_flags_lang_cxx} %{?_distro_extra_cxxflags}

%_distro_extra_cxxflags 🏷️

For example, if the compiler is emitting errors that could be disabled with some -Wno-… flags, you can define this macro with extra flags.

This macro is not defined by default on Fedora.

D

%ldc_arches

%_d_optflags

Java

https://docs.fedoraproject.org/en-US/java-packaging-howto/ (opens in a new tab)

Nim

This section is only applicable to Terra (because Fedora does not ship Nim).

%nim_prep

Set mold as the linker and run nimble setup -y.

%nim_build

Same as %nim_c.

%nim_c

β‡’ nim c -d:release -t:"%nim_tflags" -l:"%nim_lflags".

Python

https://docs.fedoraproject.org/en-US/packaging-guidelines/Python/ (opens in a new tab)

Rust

https://docs.fedoraproject.org/en-US/packaging-guidelines/Rust/ (opens in a new tab)

%rustflags_debuginfo 🏷️

If gdb crashes in find-debuginfo, you might want to lower the value to 1 or even 0.

The default value is 2.

example.spec
https://github.com/terrapkg/packages/blob/2df55dad60a58ecf5f8fbe962f877f2834bdfa25/anda/devs/zed/nightly/zed-nightly.spec#L14
 
%bcond_with check
 
# Exclude input files from mangling
%global __brp_mangle_shebangs_exclude_from ^/usr/src/.*$
 
%global crate zed
%global app_id dev.zed.Zed-Nightly
 
%global rustflags_debuginfo 0