Outdated advice regarding %global vs. %define #1449

Closed
opened 2025-04-01 19:08:39 +00:00 by ferdnyc · 17 comments
Member

While its advice has been softened somewhat through the addition of some disclaimer language, the thrust of the section on how to define spec variables is still communicated by its title:

%global Preferred Over %define

Use %global instead of %define, unless you really need only locally defined submacros within other macro definitions (a very rare case).

Rationale: The two macro defining statements behave the same when they are at the top level of rpm’s nesting level.

But when they are used in nested macro expansions (like in %{!?foo: ... } constructs, %define theoretically only lasts until the end brace (local scope), while %global definitions have global scope.

Note that %define and %global differ in more ways than just scope: the body of a %define’d macro is lazily expanded (i.e., when used), but the body of %global is expanded at definition time. It’s possible to use %%-escaping to force lazy expansion of %global.

This is, in the words of RPM core developer Panu Matilainen, "bad advice that spread far and wide". Accompanied by a fuller explanation of how scoping (now) works for %define variables:

That used to be the case in rpm < 4.14 (prior to rhel-8 if you like). The worst issue with it was that %define appeared to worked in those situations but then an unrelated spec change could break suddenly break it. It was bad, but the "always use global" cure is worse than the disease.

Since 4.14 only parametric macros have locally scoped macros.

We are strongly encouraged to stop telling people that they should be using %global everywhere, or really anywhere, unless it's absolutely necessary. (Meaning, unless immediate expansion of the variable is required.) For most specfile needs, the lazy expansion provided by %define is preferred.

While its advice has been softened somewhat through the addition of some disclaimer language, the thrust of the section on how to define spec variables is still communicated by its title: > ## [`%global` Preferred Over `%define`](https://docs.fedoraproject.org/en-US/packaging-guidelines/#_global_preferred_over_define) > > Use `%global` instead of `%define`, unless you really need only locally defined submacros within other macro definitions (a very rare case). > > Rationale: The two macro defining statements behave the same when they are at the top level of rpm’s nesting level. > > But when they are used in nested macro expansions (like in `%{!?foo: ... }` constructs, `%define` theoretically only lasts until the end brace (local scope), while `%global` definitions have global scope. > > Note that %define and %global differ in more ways than just scope: the body of a %define’d macro is lazily expanded (i.e., when used), but the body of %global is expanded at definition time. It’s possible to use %%-escaping to force lazy expansion of %global. This is, in [the words of RPM core developer Panu Matilainen](https://github.com/rpm-software-management/rpm/issues/3661#issuecomment-2728634266), "bad advice that spread far and wide". Accompanied by a fuller explanation of how scoping (now) works for `%define` variables: > That used to be the case in rpm < 4.14 (prior to rhel-8 if you like). The worst issue with it was that %define _appeared_ to worked in those situations but then an unrelated spec change could break suddenly break it. It was bad, but the "always use global" cure is worse than the disease. > > Since 4.14 only parametric macros have locally scoped macros. We are strongly encouraged to stop telling people that they should be using `%global` everywhere, or really **anywhere**, unless it's absolutely necessary. (Meaning, unless immediate expansion of the variable is required.) For most specfile needs, the lazy expansion provided by `%define` is preferred.
Owner

+1 to drop this rule

+1 to drop this rule
Owner

So the recommendation should be flipped, or what? From

use %global always unless you really need %define

to

use %define always unless you really need %global

?

So the recommendation should be flipped, or what? From > use `%global` always unless you really need `%define` to > use `%define` always unless you really need `%global` ?
Owner

Yes, pretty much.

Yes, pretty much.
Member

Yes - that's how it really should've been all along. Even in the old days, %global was needed in one case where it no longer is (that %{!?foo:%define foo bar} type construct), it didn't need this big hammer recommendation.

Yes - that's how it really should've been all along. Even in the old days, %global was needed in *one* case where it no longer is (that `%{!?foo:%define foo bar}` type construct), it didn't need this big hammer recommendation.
Author
Member

How does this sound, for a new section? If we want I can open a PR and we can iterate on the wording there. (The two commands would be defined using a description list in asciidoc format.)

Defining variables (macros) in spec files

RPM supports two commands that can be used in .spec files to define new %-variables. (Called 'macros' in RPM terminology, since every %-expansion is a macro expansion. But macros can also be used for simple variable replacement.)

%define::
Expanded at time of use (lazy expansion).
A macro created with %define can use other macros in its definition,
and all macros will be replaced with their current value
at the time the %define macro is expanded.
This makes it very easy to use macros in loops,
or to change their definition in different parts of the spec file.

%global::
Expanded at time of creation (immediate expansion).
A macro created with %global is expanded as soon as it is created,
so that any %-macros used in the definition
are substituted with their value
at the time the %global statement is processed.
Because a macro defined with %global
no longer contains the %-macros used in its definition,
only their expanded values,
later modifications to those macros
will not affect the expansion of the global macro.

Packagers SHOULD use %define to create macros in spec files.
Spec files MAY use %global instead,
if immediate expansion is needed (a rare occurrence).

[NOTE]
=====
Previous versions of these guidelines
advised using `%global` in preference to `%define`.
Due to changes in RPM,
the rationale behind that advice no longer holds,
and this section has been updated to reflect current best practices.
However, because the previous advice was in place for many years,
you will encounter `%global` macros in existing spec files,
as well as in examples provided by these guidelines,
that have not been updated to the current recommendations.
====
How does this sound, for a new section? If we want I can open a PR and we can iterate on the wording there. (The two commands would be defined using a description list in asciidoc format.) ## Defining variables (macros) in spec files RPM supports two commands that can be used in `.spec` files to define new `%`-variables. (Called 'macros' in RPM terminology, since every `%`-expansion is a macro expansion. But macros can also be used for simple variable replacement.) `%define`:: Expanded at time of use (lazy expansion). A macro created with `%define` can use other macros in its definition, and all macros will be replaced with their current value at the time the `%define` macro is expanded. This makes it very easy to use macros in loops, or to change their definition in different parts of the spec file. `%global`:: Expanded at time of creation (immediate expansion). A macro created with `%global` is expanded as soon as it is created, so that any `%`-macros used in the definition are substituted with their value at the time the `%global` statement is processed. Because a macro defined with `%global` no longer contains the `%`-macros used in its definition, only their expanded values, later modifications to those macros will not affect the expansion of the global macro. Packagers **SHOULD** use `%define` to create macros in spec files. Spec files **MAY** use `%global` instead, if immediate expansion is needed (a rare occurrence). ```adoc [NOTE] ===== Previous versions of these guidelines advised using `%global` in preference to `%define`. Due to changes in RPM, the rationale behind that advice no longer holds, and this section has been updated to reflect current best practices. However, because the previous advice was in place for many years, you will encounter `%global` macros in existing spec files, as well as in examples provided by these guidelines, that have not been updated to the current recommendations. ==== ```
Author
Member

I can't decide if it should still get into the whole topic of lazy expansion in global macros using %%.

I can't decide if it should still get into the whole topic of lazy expansion in global macros using `%%`.
Author
Member

And now I'm thinking the [NOTE] should even be a [CAUTION].

And now I'm thinking the `[NOTE]` should even be a `[CAUTION]`.
Owner

We discussed this at today's FPC meeting. We're generally in favor, so please send a pull request to implement it. Ideally we'd like to get @pmatilai's +1 on the exact wording in that PR.

We discussed this at today's FPC meeting. We're generally in favor, so please send a pull request to implement it. Ideally we'd like to get @pmatilai's +1 on the exact wording in that PR.
Author
Member

I'll whip something up today (hopefully).

I'll whip something up today (hopefully).
Author
Member

Hey @pmatilai, a question that came up while working on this:

Are there any differences in the handling of things like subshell calls, when using %define vs. %global?

There are a lot of %global uses in our packaging templates, so in addition to updating the guidelines I wanted to also update (at least some of) those. But then I ran into these macros from the TCL packaging guidelines:

%{!?tcl_version: %global tcl_version %(echo 'puts $tcl_version' | tclsh)}
%{!?tcl_sitelib: %global tcl_sitelib %{_datadir}/tcl%{tcl_version}}

...And that got me wondering. If this macro definition:

%global tcl_version %(echo 'puts $tcl_version' | tclsh)

is replaced with this macro definition:

%define tcl_version %(echo 'puts $tcl_version' | tclsh)

...will every use of %{tcl_version} be re-executing that echo 'puts $tcl_version' | tclsh shell command?

If the answer is yes (though I doubt it is), we may want to continue recommending %global for macros that invoke subshells or do other complex processing.

Hey @pmatilai, a question that came up while working on this: Are there any differences in the handling of things like subshell calls, when using `%define` vs. `%global`? There are a _lot_ of `%global` uses in our packaging templates, so in addition to updating the guidelines I wanted to also update (at least some of) those. But then I ran into these macros from [the TCL packaging guidelines](https://docs.fedoraproject.org/en-US/packaging-guidelines/Tcl/#_noarch_packages): ``` %{!?tcl_version: %global tcl_version %(echo 'puts $tcl_version' | tclsh)} %{!?tcl_sitelib: %global tcl_sitelib %{_datadir}/tcl%{tcl_version}} ``` ...And that got me wondering. If this macro definition: `%global tcl_version %(echo 'puts $tcl_version' | tclsh)` is replaced with this macro definition: `%define tcl_version %(echo 'puts $tcl_version' | tclsh)` ...will every use of `%{tcl_version}` be re-executing that `echo 'puts $tcl_version' | tclsh` shell command? If the answer is yes (though I doubt it is), we may want to continue recommending `%global` for macros that invoke subshells or do other complex processing.
Author
Member

First draft PR now opened as #1454.

First draft PR now opened as #1454.
Member

Shell expansion isn't special in any way, so yes it's re-executed at each use of such macro. Sometimes it's what you need, sometimes not.

The real problem with the existing "use %global everywhere" is the "everywhere", and switching to "use %define elsewhere" just leads to different problems. One should use whichever is appropriate for a given situation. And to make that call, people will need to understand the difference.

Shell expansion isn't special in any way, so yes it's re-executed at each use of such macro. Sometimes it's what you need, sometimes not. The real problem with the existing "use %global everywhere" is the "everywhere", and switching to "use %define elsewhere" just leads to different problems. One should use whichever is appropriate for a given situation. And to make that call, people will need to understand the difference.
Author
Member

Thanks, @pmatilai!, that's a big help! I'll have to make some adjustments to #1454.

Thanks, @pmatilai!, that's a big help! I'll have to make some adjustments to #1454.
Owner

RPM 6.1 will have %define -g with the same behavior as %global which confuses this further. And RPM has documentation in https://rpm.org/docs/latest/man/rpm-macros.7#Macro_manipulation that pretty clearly explains the difference between %define and %global. I wonder if we can just... remove this section from the Guidelines entirely since it's inaccurate, and our goal isn't to duplicate RPM documentation in the Guidelines.

RPM 6.1 will have `%define -g` with the same behavior as `%global` which confuses this further. And RPM has documentation in https://rpm.org/docs/latest/man/rpm-macros.7#Macro_manipulation that pretty clearly explains the difference between `%define` and `%global`. I wonder if we can just... remove this section from the Guidelines entirely since it's inaccurate, and our goal isn't to duplicate RPM documentation in the Guidelines.
gotmax23 changed title from [master] Outdated advice regarding %global vs. %define to Outdated advice regarding %global vs. %define 2026-05-18 05:12:22 +00:00
Member

RPM 6.1 will have %define -g with the same behavior as %global which confuses this further.

Careful - that's NOT the same as %global. %define -g -e is. I'd say most people nowadays use %global only for the -e side-effect, or because that's what they were told to use. The need to declare a global macro inside a parametric function exists, but it's quite rare I would say.

So if our updated documentation on the matter seems sufficiently clear on this, I tend to concur that just eliminating the distro-specific guidance might well be the best course of action. Packagers are far better off learning to use what they actually need than being instructed to use one thing or the other just because some ancient RPM version did something cooky with it.

> RPM 6.1 will have %define -g with the same behavior as %global which confuses this further. Careful - that's NOT the same as `%global`. `%define -g -e` is. I'd say most people nowadays use `%global` only for the `-e` side-effect, or because that's what they were told to use. The need to declare a global macro inside a parametric function exists, but it's quite rare I would say. So if our updated documentation on the matter seems sufficiently clear on this, I tend to concur that just eliminating the distro-specific guidance might well be the best course of action. Packagers are far better off learning to use what they actually need than being instructed to use one thing or the other just because some ancient RPM version did something cooky with it.
Owner

+1 to removing this section from the Guidelines entirely

+1 to removing this section from the Guidelines entirely
Owner

pmatilai, thanks for the point of information and also for the nice new RPM manpages.

I opened #1541 to propose removing the guideline.

pmatilai, thanks for the point of information and also for the nice new RPM manpages. I opened https://forge.fedoraproject.org/packaging/guidelines/pulls/1541 to propose removing the guideline.
james closed this issue 2026-05-28 16:35:55 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
7 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
packaging/guidelines#1449
No description provided.