uncleremus
uncleremus

Reputation: 383

RPM: invoking a macro with arguments from another macro with arguments

I would like to invoke an RPM macro taking an argument from another macro. In m4, this would be done trivially like this (just an example, not what I really want to do):

define(`path', `/mnt/$1/lib')
define(`mkd', `mkdir path($1)/stuff')
mkd(`usr')
=> mkdir /mnt/usr/lib/stuff

Or in CPP syntax:

#define path(x) /mnt/x/lib
#define mkd(x) mkdir path(x)/stuff
mkd(usr)
=> mkdir /mnt/usr/lib/stuff

The obvious equivalent in RPM (broken!) would be:

# THIS IS BROKEN
%define path() /mnt/%1/lib
%define mkd() mkdir %{path %1}/stuff
%mkd usr

But this does not work, RPM prints "error: Too many levels of recursion in macro expansion. It is likely caused by recursive macro declaration."

I have made it "work" with the following code:

%define path() /mnt/%1/lib
%define mkd() mkdir %{expand:%%{path %1}}/stuff
%mkd usr

However I can't believe that this clumsy code would be the recommended way for doing this. Another approach that appears to work is using option arguments:

%define path(d:) /mnt/%{-d*}/lib
%define mkd(p:) mkdir %{path -d%{-p*}}/stuff
%mkd -p usr

But this works only because I used different option characters; if I use '-d' for both macros, it fails like the example above. I don't like this solution much either, because using options seems non-intuitive here.

What would be the preferred way to achieve the desired effect?

Upvotes: 0

Views: 1280

Answers (3)

uncleremus
uncleremus

Reputation: 383

It seems that the problem reported in this question is solved in recent versions of rpm, between 4.11 and 4.14.

~> rpm --version
RPM version 4.14.1
~> rpm --eval '
%define path() /mnt/%1/lib
%define mkd() mkdir %{path %1}/stuff
%define foo bar
%mkd %foo'

=> mkdir /mnt/bar/lib/stuff

With rpm 4.11, this fails with "too many levels of recursion", and the workaround of the previous answer (%global mkd() mkdir %{path %%1}/stuff) is necessary.

Upvotes: 0

matejcik
matejcik

Reputation: 2072

RPM handling of recursive macros is broken. As far as I was able to find out, there is no "recommended" way; this simply doesn't work and the workarounds you listed are as close as you're going to get.

Within RPM macro sets I've seen (including some within RPM itself), this is usually solved through conditional arguments: use -p option if supplied, %1 otherwise.

%define path(p:) /mnt/%{-p:%{-p*}}%{!-p:%1}/lib
%define mkd(d:)  mkdir %{path -p %1}/stuff
echo "making path at %{path hello}"
%{mkd hello}

You can also use "non-function" definitions -- this is sort of what is going on in the %define/%global case.

%define _path /mnt/%1/lib
# note no parentheses at end of `_path`
%define path() %_path
%define mkd()  mkdir %{_path}/stuff

This way, the %_path macro is inlined into the path() and mkd() definitions and no recursion is happening.

This would not work if you wanted to use %2 for the path, but because that is a different name, you can use it the old way ......... o_O

%define maketwo() mkdir %1; mkdir %{path %2}/stuff

Upvotes: 1

uncleremus
uncleremus

Reputation: 383

I found a more elegant solution. I don't understand why it works, and I'm not sure if it's generally applicable:

%define path() /mnt/%1/lib
%global mkd() mkdir %{path %%1}/stuff
%mkd usr
=> mkdir /mnt/usr/lib/stuff

It works only if mkd is define with %global. path can be define either with %define or %global. Also node the quoted percent character.

Upvotes: 1

Related Questions