RPM 中宏的简单介绍

这篇文章是 极简 RPM 打包指南 的补充文档。

宏 (Macro) 是 RPM .spec 文件中使用的一种特殊的语法,和 C 语言中的宏类似,它被嵌入到.spec 文件中,经过 rpmbuild 预处理之后,转换为真正的 shell 脚本。

宏的作用

宏包含两种,简单宏和包含参数的宏。简单的宏可以当作字符串变量使用。复杂的宏可以接受参数,可以类比为函数。

宏是递归定义的,也即宏可以包含宏。

宏的使用

宏的使用语法有两种,比如:

$<name>

或者

${<name>}

<name>是宏的名字。

这两种语法各有优劣。第二种语法可以安全地与嵌入到 bash 脚本之中,比如:

cd %{_prefix}/bin/

它等价于:

cd /usr/bin

第一种语法则在调用宏函数(可以接受参数的宏)的时候很有用。假如我定义了一个宏:

%define mymacro() (echo -n "My arg is %1" ; sleep %1 ; echo done.)

那么我可以这样调用它:

%mymacro 5

内置宏

在安装有 rpm 的系统中,有很多内置的宏可以直接使用。下面的命令可以告诉你这些内置宏都来自哪里:

rpm --showrc|grep "^Macro path"

如果你想知道这些内置宏是如何定义的,你可以:

rpmbuild --eval "%{macro_name}"

最后,下面是最常见的内置宏和它们的作用:

  %define ...         定义一个宏
  %undefine ...       删除一个宏
  %global ...         类似于编程语言中的 global 标记,使一个宏全局可用,而不只是作用于它的作用域

需要注意,%define 和%global 除了作用域,还有第二个区别。因为宏可以嵌套定义,所以展开时就分为两种:使用时展开,定义时展开。

%define 定义的宏是在使用时才展开的,%global 则在定义时就展开了。

编写宏

正如前面所说,宏分为两种,变量宏(简单不接受参数的宏)和函数宏(接收参数的宏),它们的定义也各不相同。

变量宏:

%define

比如:

%define maintainer example@example.com

函数宏:

%define (opts)

比如:

%define mymacro() (echo -n "My arg is %1" ; sleep %1 ; echo done.)

函数宏的参数处理,与 bash 脚本编程中的 getopt 完全一致。在编写函数宏时,下面的变量(宏)可以直接使用:

    %0      the name of the macro being invoked
    %*      all arguments (unlike shell, not including any processed flags)
    %**     all arguments (including processed flags)
    %#      the number of arguments
    %{-f}   if present at invocation, the flag f itself
    %{-f*}  if present at invocation, the argument to flag f
    %1, %2  the arguments themselves (after getopt(3) processing)

逻辑判断

RPM 宏支持一些简单的条件判断语句。

%if…%else…%endif

%if %{my_macro} 如果宏 my_macro 存在
%define my_new_macro 12345 定义一个新宏
%undefine my_macro 删掉旧宏
%else 否则
%define my_macro 54321 定义 my_macro
%endif 结束 if 语句

只有%if 和%endif 是必须的

!

%if !%{my_macro} 如果宏 my_macro 不存在

?

fedora@localhost ~> rpm --eval "%{macro_to_test}" 当访问一个宏,但宏不存在时,会返回整个语句本身
%{macro_to_test}
fedora@localhost ~> rpm --eval "%{?macro_to_test}" 使用?语法之后,如果宏不存在,返回空

fedora@localhost ~> rpm --eval "%{?_prefix}" 如果宏存在,则返回宏的值
/usr
fedora@localhost ~> rpm --eval "%{?_prefix:hello world}" 甚至可以自定义宏的返回值
hello world

这里只是一些简单的语法,实际打包时会用到其他更具体的宏,功能都是类似的。

参考

这篇文章的大部分内容来自 RPM 官方文档 Macro syntax

3赞