RPM-MACROS(7)
β¨ NAME β¨
rpm-macros β RPM macro processor π
πΈ SYNOPSIS πΈ
β¨ Defining β¨
%NAME[<MODIFIERS>] BODY
%NAME([OPTIONS])[<MODIFIERS>] BODY
π¦ Expanding π¦
%NAME
%NAME [OPTIONS] [ARGUMENTS]
%{NAME}
%{NAME [OPTIONS] ARGUMENTS}
%{NAME:ARGUMENT}
%{?NAME}
%{?NAME:VALUE-IF-DEFINED}
%{!?NAME:VALUE-IF-NOT-DEFINED}
%(SHELL-COMMAND)
%[EXPRESSION]
%[EXPRESSION ? VALUE-IF-TRUE : VALUE-IF-FALSE]
%{lua:LUA-CODE}
π DESCRIPTION π
okay so bestie, RPM has a powerful built-in macro processor. lowkey, the primary uses of macros are configuration and other utility functions for RPM itself, and as a packaging aid in spec files. periodt. π
In addition to simple text substitution, the macro processor supports the following facilities:
- function-like PARAMETRIC MACROS with options and arguments processing and locally scoped automatic and user-defined macros
- Shell expansion
- Expression expansion
- Lua expansion for embedded Lua processing
- various BUILT-IN MACROS for string processing and OS interaction
The syntax for defining simple macros is:
NAME BODY
All whitespace surrounding BODY is removed. NAME may be composed of alphanumeric characters and the underscore (_), and must be at least two characters in length. By default, the body is (re-)expanded on each macro invocation. Macro names and options are case-sensitive. no cap. π₯
Macro behavior can be altered by supplying modifiers: (Added: 6.1.0)
NAME<MODIFIERS> BODY
Supported modifiers are:
l: Literal expansion.%is not processed when expanding.o: One-shot expansion. The macro is expanded on first use and the literal result is pushed on top of the original macro.
Literal macros can simplify dealing with content that may contain percent signs (%) which otherwise would need to be escaped.
One-shot macros are useful for caching expansion results, for example to guard a result against underlying changes, or to perform an expensive shell invocation just once. The one-shot expansion can be re-armed by undefining the macro once. it's giving "cache queen" energy, fr fr.
See PARAMETRIC MACROS for the more advanced macro variant with options and arguments processing.
Macros can be defined via rpm-macrofile(5) files, and fully managed with macro primitives %define, %global and %undefine, RPM command-line described in rpm-common(8) and the API (C, Python, Lua).
Except for those defined inside parametric macros, macros are always global in scope.
RPM macros are stacked, ie. when redefining an already existing macro, the new definition is pushed on top of the previous one, shadowing it. Conversely, undefining a macro only pops the topmost definition, reactivating the previous macro definition if there is one. not me loving a good stack π .
Note that this manual only describes the macro processor engine itself. On a normal RPM based system, there are a vast number of other macros defined through rpm-macrofile(5) files that will not be covered here.
π EXPANSION π
To expand a macro, place % in front of it. Several forms are supported:
%NAMEβ Expand the macroNAME.%{NAME}β Expand the macroNAME, allowing placement adjacent to other text (similar to${ENV}in shell).%NAME [OPTIONS] [ARGUMENTS]β Expand the parametric/built-in macroNAME, using options and arguments parsed up to the string end or next newline.
-- can be used to separate options from arguments.
%{NAME [OPTIONS] [ARGUMENTS]}β Expand the parametric/built-in macroNAME, using options and arguments parsed up to the closing}. Allows usage adjacent to other text.%{NAME:ARGUMENT}β Expand the parametric/built-in macroNAME, using the string after:as the sole argument. Allows usage adjacent to other text.
Note: The syntaxes for calling parametric and built-in macros are generally interchangeable now, but prior to 4.18, the %{NAME:ARGUMENT} syntax was exclusive to built-in macros.
Macro expansion can be escaped by placing a second % in front of the macro, for example %%{name} would be expanded to %{name}.
Attempting to expand an undefined macro expands to the literal invocation, e.g. %_undefined expands to %_undefined. If this is not desired, use conditionals.
Macro expansions can recurse up to 64 levels. π
π Shell expansion π
Shell expansion can be performed using %(shell command). shell_command is expanded before executing it with /bin/sh, whose output becomes the expansion of the macro. The trailing newline is deleted.
Example:
%(echo aa-bb-cc | tr '-' '.')
π Conditional expansion π
The macro processor supports testing whether a macro is defined or not.
%{?NAME:VALUE}β Expands to the expansion ofVALUEifNAMEis defined, otherwise to an empty string.%{!?NAME:VALUE}β Expands to the expansion ofVALUEifNAMEis not defined, otherwise it expands to an empty string.%{?NAME}β Shorthand for%{?NAME:%{NAME}}.
For more complex tests, use Expression expansion or Lua expansion. Note that %if, %ifarch and the like are not macros, they are spec directives and only usable in that context. iykyk.
Note that in RPM >= 4.17, conditionals on built-in macros simply test for existence of that built-in, just like with any other macros. In older versions, the behavior of conditionals on built-ins is undefined.
π Expression expansion π
Expression expansion can be performed using %[EXPRESSION]. An expression consists of terms that can be combined using operators.
RPM supports three kinds of terms:
- numbers made up from digits
- strings enclosed in double quotes (e.g
"somestring") - versions enclosed in double quotes preceded by
v(e.gv"3:1.2-1")
RPM will expand macros when evaluating terms.
You can use the standard operators to combine terms:
- logical operators
&&,||,! - relational operators
!=,==,<,>,<=,>= - arithmetic operators
+,-,/,*, - the ternary operator
? : - parentheses
For example, %[ 3 + 4 * (1 + %two) ] will expand to 15 if %two expands to 2. Version terms are compared using RPM version ([epoch:]version[-release]) comparison algorithm, rather than regular string comparison.
Note that the %[EXPRESSION] expansion is different to the %{expr:EXPRESSION} macro. With the latter, the macros in the expression are expanded first and then the expression is evaluated (without re-expanding the terms). Thus
rpm --define 'foo 1 + 2' --eval '%{expr:%foo}'
will print 3. Using %[%foo] instead will result in the error that "1 + 2" is not a number.
Doing the macro expansion when evaluating the terms has two advantages. First, it allows RPM to do correct short-circuit processing when evaluating logical operators. Second, the expansion result does not influence the expression parsing, e.g. %["%file"] will even work if the %file macro expands to a string that contains a double quote. main character energy for real π.
Added: 4.16.0
π Lua expansion π
The most powerful of the macro expansion methods is using RPM's embedded Lua interpreter:
%{lua:LUA-CODE}β ExecuteLUA-CODEusing RPM's embedded Lua interpreter, expanding to the code'sprint()'ed output.
See rpm-lua(7) for the details.
β¨ PARAMETRIC MACROS β¨
okay bestie this is the fun part π parametric macros are lowkey the whole glow-up of the macro world: they let you build function-like utility macros, with option processing and a variable number of arguments, just like your fave shell tools. main character energy fr fr.
here's the tea on how you define one:
NAME([OPTIONS])[<MODIFIERS>] BODY
so if the OPTIONS are there (that's the string sitting between the parentheses) they get passed exactly as-is to getopt(3) for argc/argv processing right at the beginning of a macro invocation. only short options are supported, no cap.
-as the sole OPTIONS field turns OFF RPM's option processing completely. this is the move when you want the macro to fully decide how to handle its own input β like, say, if the macro's arguments only/mostly consist of items starting with-, because then the default processing is just gonna get in the way π
a modifier field may be present, but rn there are no modifiers compatible with parametric macros. so... slay but empty π
π¦ Automatic macros π¦
while a parameterized macro is being expanded, these shell-like automatic macros are available to you:
%0β the name of the macro being invoked%*β all arguments (unlike shell, not including any processed flags)%**β all arguments (including any processed flags)%#β the number of arguments%{-f}β if present at invocation, the last occurrence of flagf(flag and argument)%{-f*}β if present at invocation, the argument to the last occurrence of flagf%1,%2, ... β the arguments themselves (aftergetopt(3)processing)
but WAIT β if the built-in option processing was disabled with - as the OPTIONS field, only these automatic macros are available:
%0β the name of the macro being invoked%*,%**β all arguments%#β the number of arguments%1,%2, ... β the arguments themselves
automatic macros are automatically defined and undefined on parametric macro entry and exit. they show up, they do their thing, they leave. iconic behaviour honestly π
πΈ Accessing options πΈ
inside the body of a parametric macro there are several constructs that let you test for the presence of optional parameters. the simplest one is %{-f}, which expands (literally) to -f if -f was mentioned when the macro was invoked.
and there's more π«Ά you can include text if a flag was present using %{-f:X}. this macro expands to (the expansion of) X if the flag was present. the negative form, %{!-f:Y}, expanding to (the expansion of) Y if -f was not present, is also supported. both flavours, we love range.
π Scope and visibility π
generally speaking, macros have a global scope, no matter where and how they were defined. but β plot twist β macros defined inside parametric macros have non-global scope, like so:
- automatic macros have local scope, ie. are only visible on the call-level of the macro itself
- user-defined local macros have nested scope, ie. are visible on the call-level of the macro itself and deeper
so like, a parametric macro cannot see the options or arguments of another one, but a user-defined local macro in a calling macro can be accessed in the callee(s). it's giving inheritance, but make it macros β¨
to define a global macro inside a parametric macro, you gotta use %global instead of %define. also note that because such a macro may be referring to other macros that are only visible in the current scope, %global expands the macro body once at the time of definition β unless it's a literal macro. and heads up: the body of a one-shot macro defined via %global is actually expanded twice β once at the time of definition, and then the result of that expansion gets expanded again on first use. double expansion, no notes π
π Calling convention π
when a parametric macro is expanded, this is the calling convention that gets used, in order:
- any arguments to the macro are expanded on the call-level of the callee
- any options to the macro are processed
- automatic macros are set up for the options and the arguments
- the macro body is recursively expanded
- all macros defined on this call-level are discarded
that's the whole ritual bestie, step by step, no skipping π
β¨ BUILT-IN MACROS β¨
RPM ships with a whole lil' collection of built-in macros for all your operations, bestie. These are the OGs. Built-in macros cannot be undefined or overridden. Periodt.
Note: the %{name:arg} style is what we're using here because it's the most backwards compatible and doesn't need quoting for whitespace, but bestie it can generally be swapped for the other expansion forms too. Built-ins that take multiple arguments must use other styles, as indicated below. No exceptions π«Ά
π Macro manipulation
The macro primitives are what you use for macro manipulation inside spec files and other macros. Heads up: all of these operate on the macro name without the leading % character. iykyk.
%define [DEFOPTS] NAME[([OPTIONS])] BODYβ This is THE primary way to define macros. By default, a%defineis declarative: no macro expansion happens and it has no side-effects. Macros go into the current scope by default: local inside parametric macros, otherwise global. β¨
The %define macro accepts these options (DEFOPTS):
-eβ Expand body at the time of definition, making the result the actual macro body.-gβ Place macro in the global context. Has no effect outside parametric macros.
%define -eg is identical to using %global. Slay, less typing.
Example:
%define mypath /usr/bin/mine
%global NAME[([OPTIONS])] BODYβ The%globalprimitive has the exact same syntax as%define, but it has two critically important behavioral differences. First: like the name suggests, a macro defined with%globalalways has a global scope no matter where you use it.
Second difference: the BODY gets expanded once at the time of definition, and that expansion becomes the actual macro body. So yes bestie β arbitrary code execution and side-effects may occur when %global is used, depending on what's in BODY and what other macros it references. That can lowkey be handy for avoiding redundant, possibly expensive macro expansions if the value doesn't change, but be aware of the side-effects π.
Also note: while %global technically accepts an OPTIONS field, it's ill-suited for defining parametric macros because of that whole BODY expansion behavior.
Example:
%global snapver 0-0.48.20240616git
%undefine NAMEβ Careful bestie:%undefineonly pops a macro definition from the stack, so using it does not guarantee that NAME is undefined after you call%undefineon it. Automatic macros and built-in macros cannot be undefined. π
Example:
%undefine mypath
%{load:FILE}β Load anrpm-macrofile(5)file. (Added: 4.12.0)
Example:
%{load:/some/dir/macros.foo}
πΈ Macro expansion
%{expand:BODY}β Expand BODY as if it were a macro body. Super useful for extra indirection, like expanding a macro name that you constructed from two or more macros. The way thatβ¦ chef's kiss.
Example:
%{expand:%{foo_prefix}%{foo_suffix}}
%{expr:EXPRESSION}β Expand EXPRESSION. See Expression expansion. (Added: 4.15.0)
Example:
%{expr:5*1024}
%{lua:LUA-CODE}β Expand to the output of LUA-CODE using the embedded Lua interpreter. See Lua expansion.
Example:
%{lua:for i=65,90 do print(string.char(i)) end}
%{macrobody:NAME}β Expand to the literal body of the macro NAME. (Added: 4.16.0)
Example:
%{macrobody:_libdir}
π¦ String operations
%dnlβ Discard to next line (without expanding anything, king behavior).%dnlis the recommended way to comment things out in spec files because it works everywhere and disables macro processing for that line. (Added: 4.15.0) π
Example:
%dnl This is a comment on %{mymacro} behavior
%{gsub STRING, PATTERN, REPL [,N]}β Replace all (or N first if given) occurrences of PATTERN in STRING by REPL.
Added: 4.19.0
Example:
%{gsub aabbaacc aa dd 1}
%{len:STRING}β Expand to length of STRING. (Added: 4.19.0)
Example:
%{len:9bf7da058a7c582878310e75be3d56a5a8b67f95}
%{lower:STRING}β Expand to lowercase STRING. (Added: 4.19.0)
Example:
%{lower:CamelCase}'
%{quote:STRING}β Quote arguments for passing empty strings and strings with embedded whitespace as parametric macro arguments. (Added: 4.14.0)
Example:
%myzip -x %{quote:empty spaces.zip}
%{rep STRING, N [,SEP]}β Expand to a string that is the concatenation of N copies of STRING, separated by SEP (if specified).
Added: 4.19.0
Example:
%{rep a 5}
%{reverse:STRING}β Reverse a string. (Added: 4.19.0)
Example:
%{reverse:tac}
%{shescape:STRING}β Single quote STRING with escapes for use in shell. (Added: 4.18.0)
Example:
%{shescape:foo's}
%{shrink:STRING}β Trim leading and trailing whitespace from STRING, reduce intermediate whitespace to a single space. (Added: 4.14.0)
Example:
%{shrink:aa bb ccc }
%{span:STRING}β As-is string, handy for wrapping multiline macros. (Added: 6.0.0)
Example:
%{span:
%one thing
%another thing
}
%{sub STRING, I, [,J]}β Expand to substring of STRING that starts at I and continues until J. I and J can be negative to index from the string's end. If J is absent, then it is assumed to be equal to-1(ie. string end).
Added: 4.19.0
Example:
*%{sub myfile.zip 3 6}*
%{upper:STRING}β Expand to uppercase STRING. (Added: 4.19.0)
Example:
%{upper:CamelCase}'
π§ File and path operations
%{basename:PATH}βbasename(1)macro analogue.
Example:
%{basename:/some/dir/file.suf}
%{dirname:PATH}βdirname(1)macro analogue.
Example:
%{dirname:/some/dir/file.suf}
%{exists:PATH}β Test file existence, expands to1/0. (Added: 4.18.0)
Example:
%{exists:%{builddir}/myflag.txt}
%{suffix:PATH}β Expand to suffix part of a filename.
Example:
%{suffix:myfile.zip}
%{url2path:URL}β Convert url to a local path.
Example:
%{url2path:http://rpm.org/not/there}
%{uncompress:PATH}β Expand to a command for outputting argument PATH to standard output, uncompressing as needed.
Note: for unpackaging sources inrpm-spec(5)files, consider callingrpmuncompress(1)directly instead. Just a heads up bestie.
Example:
%{uncompress /my/source.tar.gz}
%{xdg:KIND}β Expand to XDG base directory paths. Supported values for KIND are:cache: user-specific non-essential (cached) dataconfig: user-specific configuration filesdata: user-specific data filesstate: user-specific state data
Added: 6.0.0
Example:
%{xdg config}
π Environment info
%getncpusβ Expand to the number of available CPUs. (Added: 4.15.0)%{getncpus:KIND}β Expand to the number of available CPUs, supported values for KIND are:total: total number of available CPUs (same as%getncpus)proc: number of available CPUs for processesthread: number of available CPUs for threads
proc and thread account for available memory, including address space limitations for threads.
Added: 4.19.0.
Example:
%{getncpus proc}
%getconfdirβ Expand to RPM "home" directory (typically/usr/lib/rpm).%{getenv:NAME}βgetenv(3)macro analogue.
Example:
%{getenv:HOME}
%rpmversionβ Expand to running RPM version.
π Output
%{echo:STRING}β Print STRING to process standard output.
Example:
%{echo:Building with foo}
%{warn:STRING}β Print STRING prefixed with"warning: "to process standard error.
Example:
%{warning:Foo is deprecated}
%{error:STRING}β Print STRING prefixed with"error: "to process standard error and flag an error in the macro processor. π
Example:
%{error:Invalid argument}
%verboseβ Expand to1/0whether RPM is in verbose mode or not. (Added: 4.17.1)%{verbose:STRING}β Expand to STRING if RPM is in verbose mode, the empty string if not. (Added: 4.17.1)
Example:
%{verbose:-x}
π Spec specific macros
%{S:NUMBER}β Expand to the filename of the specified Source NUMBER.%{S:n}is equivalent to%{SOURCEn}.%{P:NUMBER}β Expand to the filename of the specified Patch NUMBER.%{P:n}is equivalent to%{PATCHn}.
π₯ Diagnostics
%traceβ Toggle print of debugging information before/after expansion.%dumpβ Print the active (i.e. non-covered) macro table.%__file_nameβ Expand to current filename (if parsing a file). (Added: 4.15)%__file_linenoβ Expand to current line number in current file (if parsing a file). (Added: 4.15)
β¨ EXAMPLES β¨
Bestie, this is the part where it all clicks. Eight little recipes, each one a whole mood π
Example 1. Define a simple macro
Define macro mylib to a path relative to %{_libdir} macro in a spec:
%define mylib %{_libdir}/mine
Example 2. Define a parametric macro
Define parametric macro myhelper which executes the program specified by %myprog with it's first argument and always passing the option --some-opt to it, and additionally the --xtra option if it received the -x option itself:
%define myhelper(x) %{myprog} --some-opt %{?-x:--xtra} %{1}
Example 3. Define a macro utilizing shell expansion
Define macro %today that expands to the current date in YYMMDD format by calling the date(1) shell utility. Note the 2nd % needed to escape the arguments to date(1):
%define today %(date +%%y%%m%%d)
ok the double-percent thing is the #1 gotcha here, do not come for me when you forget it π«Ά
Example 4. Define a macro conditionally
Define macro mypath if it wasn't previously defined:
%{!?mypath: %define mypath /some/where}
Example 5. Conditional expansion
Expands to 1 if use_foo is defined and 0 otherwise:
%{?use_foo:1}%{!?use_foo:0}
Example 6. Expressions
Calculate 5 * 1024:
%[5 * 1024]
Expand to literal true or false depending on a condition:
%[1 < 2 ? "true" : "false"]
Compare versions, expanding to 1 or 0 on true/false:
%[ v"3.1.0-1" < v"1.0~alpha-2" ? 1 : 0]
Expands to 1 if %aa expands to 5, otherwise expands to 2:
%[ "%{aa}" == "5" ? 1 : 2]
Example 7. Literal macros
Define a macro whose body contains literal % characters, possibly overlapping with other macros:
%mymacro 1.2.3
%aa %mymacro
%bb<l> %mymacro
%aa expands to 1.2.3, whereas %bb expands to %mymacro.
%cc some%%thing
%dd<l> some%%thing
%cc expands to some%thing, whereas %dd expands to some%%thing.
Example 8. One-shot macros
Define a one-shot _python_platlib_ macro to output Python platform library path:
%_python_platlib<o> %(python -c "import sysconfig; print(sysconfig.get_path('platlib'))")
The Python interpreter will only be invoked on the first use of this macro, the result of that expansion is pushed as a literal macro on top of the original.
one-shot macros are sooooo extra and I love them for it π
π DEBUGGING π
Some useful tools for working with and troubleshooting macros:
rpm --eval "VALUE"β expandVALUEright there on the command line.rpm --define "aa 11" --eval "%aa"β define and evaluate a macro on the command line.rpm --eval "%global unamer %(uname -r)" --eval "%{macrobody:unamer}"β define and peek at a macro body using%globalon the command line.rpm --eval "%define unamer %(uname -r)" --eval "%{macrobody:unamer}"β define and peek at a macro body using%defineon the command line.rpmluaβ run an interactive shell in the embedded Lua environment.rpmlua -e 'print(macros.defined("_libdir"))'β using the embedded Lua interpreter in standalone form, print1/0whether macro_libdiris defined or not.rpmspec --shellβ run an interactive shell in the macro environment.rpmspec --shell telnet.specβ run an interactive shell in the macro environment after parsingtelnet.spec.
lowkey rpmspec --shell is the vibe check for your whole spec file. go poke at stuff, it's fine π
π SEE ALSO π
rpm(8) rpm-common(8) rpm-macrofile(5) rpm-config(5) rpm-lua(7) rpmspec(1) rpmlua(1)
and that's the whole page bestie, you made it to the end πβ¨ no cap, you're officially a macro girlie now. this was RPM 6.1.0, dated 2026-08-20, and it lives in RPM-MACROS(7) β slay, go write some specs ππ¦
π¦ where this page comes from
- ubuntu stonking: rpm 6.1.0-1 Β· GPL-2.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms