Skip to content

03 高级话题

1. 对包进行签名

防止已下载备用的包被第三方篡改。

三种方法:

  • 对已存在的包添加签名
  • 对已存在的包替换签名
  • 在构建的时候对包签名

1.1 对已存在的包添加签名

大部分情况下都是先构建好包,再进行签名(可以进行多次和重复签名),最后发布。

在阅读本章之前,需要先了解GPG工具生成密钥,简单的使用说明可参考github:

https://help.github.com/en/articles/managing-commit-signature-verification

使用gpg生成密钥之后,需要在~/.rpmmacros中编写宏(%_gpg_name)指定密钥名称(也可以是用户名):

$ cat ~/.rpmmacros
%_gpg_name D2C6AF4B

1.1.1 官方文档说明

使用--addsign选项添加签名:

$ rpm --addsign blather-7.9-1.i386.rpm
            Enter pass phrase:

Pass phrase is good.
blather-7.9-1.i386.rpm:

使用--checksig选项验证签名:

$ rpm --checksig blather-7.9-1.i386.rpm
blather-7.9-1.i386.rpm: size pgp pgp md5 OK

两个pgp pgp代表被签名了两次。

1.1.2 centos 7.5说明

没有找到官方文档中提到的选项,使用rpmsignrpmkeys代替。

$ rpmsign --addsign cello-1.0-1.el7.x86_64.rpm
Enter pass phrase:
Pass phrase is good.
cello-1.0-1.el7.x86_64.rpm:

验证时需要先导入公钥,公钥的获取方法:

$ gpg --export -a -o pub-key D2C6AF4B 

导入公钥:

$ rpmkeys --import pub-key

验证签名:

$ rpmkeys -K cello-1.0-1.el7.x86_64.rpm
cello-1.0-1.el7.x86_64.rpm: RSA sha1 ((MD5) PGP) md5 NOT OK

1.2 对已存在的包替换签名

--resign选项:

$ rpm --resign b*.rpm
            Enter pass phrase:

Pass phrase is good.
blather-7.9-1.i386.rpm:
bother-3.5-1.i386.rpm:

1.3 在构建的时候对包签名

--sign选项(已废弃),只能与--bb--ba一起使用:

$ rpmbuild -ba --sign blather-7.9.spec
            Enter pass phrase:

Pass phrase is good.
* Package: blather
…
Binary Packaging: blather-7.9-1
Finding dependencies...
…
Generating signature: 1002
Wrote: /usr/src/redhat/RPMS/i386/blather-7.9-1.i386.rpm
…
Source Packaging: blather-7.9-1
…
Generating signature: 1002
Wrote: /usr/src/redhat/SRPMS/blather-7.9-1.src.rpm

还可同时构建多个包:

$ rpmbuild -ba --sign b*.spec

2. Mock

mock是构建包的工具。可以构建不同于构建主机的架构和版本的包。

centos需要从epel中安装。可以查看/etc/mock/目录下的配置文件看支持哪些操作系统及版本。

$ mock --r epel-6-x86_64 cello-1.0-1.el7.src.rpm

mock还会解决一些BuildRequires的依赖问题(比如gcc)。

3. 一些宏

3.1 自定义宏

RPM Official Documentation

%global <name>[(opts)] <body>

3.2 %setup

%setup可用于从源码压缩包构建RPM包。其执行的动作可以在rpmbuild --dubug的输出中看到。

[root@study SPECS]# rpmbuild --debug -bb cello.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.y404KC
+ umask 022
+ cd /home/study/rpmbuild/BUILD
+ cd /home/study/rpmbuild/BUILD
+ rm -rf cello-1.0
# -c的意思时输出到标准输出,不改变源文件
+ /usr/bin/gzip -dc /home/study/rpmbuild/SOURCES/cello-1.0.tar.gz
+ /usr/bin/tar -xvvf -
drwxr-xr-x root/root         0 2019-07-24 09:56 cello/
-rw-r--r-- root/root       612 2019-07-22 17:03 cello/LICENSE
-rw-r--r-- root/root        85 2019-07-23 10:40 cello/cello.c
-rw-r--r-- root/root       135 2019-07-23 10:49 cello/Makefile
-rw-r--r-- root/root       249 2019-07-24 09:56 cello/cello-output-first-patch.patch
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ cd cello-1.0
+ /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ exit 0

下面是改变 %setup 行为的选项。

3.2.1 %setup -q

限制该宏的输出,将tar -xvvf替换为tar -xf。必须首先使用此选项。

3.2.2 %setup -n

在某些情况下,解压的tar包的文件架的名字与%{name}-%{version}格式不同,会导致该宏错误。

此时可以指定文件夹的名字:-n directory_name

$ tar -zcf cello-1.0.tar.gz cello/

$ cat cello.spec
Name:           cello
Version:        1.0
Release:        1%{?dist}
...
%prep
%setup -n cello
3.2.3 %setup -c

适用于源码包不包含任何子目录的情况。即直接将文件打成源码包。

$ tar -tf cello-1.0.tar.gz
cello.c
cello-output-first-patch.patch
LICENSE
Makefile

$ cat cello.spec
%prep
%setup -n cello -c
$ rpmbuild -bb cello.spec --debug
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.Na0EvS
+ umask 022
+ cd /home/study/rpmbuild/BUILD
+ cd /home/study/rpmbuild/BUILD
+ rm -rf cello
+ /usr/bin/mkdir -p cello
+ cd cello
+ /usr/bin/gzip -dc /home/study/rpmbuild/SOURCES/cello-1.0.tar.gz
+ /usr/bin/tar -xvvf -
-rw-r--r-- root/root        85 2019-07-23 10:40 cello.c
-rw-r--r-- root/root       249 2019-07-24 09:56 cello-output-first-patch.patch
-rw-r--r-- root/root       612 2019-07-22 17:03 LICENSE
-rw-r--r-- root/root       135 2019-07-23 10:49 Makefile
+ STATUS=0
+ '[' 0 -ne 0 ']'
3.2.4 %setup -D and -T

-D选项表示不删除源码文件夹。若多次使用%setup宏的情况下很有用。在%prep阶段将不会执行下面的语句:

rm -rf 'cello-1.0'

-T选项表示禁止源码包的解压,将下面的语句从脚本中移除:

/usr/bin/gzip -dc '/builddir/build/SOURCES/cello-1.0.tar.gz' | /usr/bin/tar -xvvf -
3.2.5 %setup -a and -b
  • -b:在进入工作目录之前(before)扩展指定源。
  • -a:在进入工作目录之后(after)扩展指定源。

它们的参数是spec文件头部预定义的Source号码。

$ cat cello.spec
Source0:        http://www.sharkshark.xyz/%{name}/release/%{name}-%{version}.tar.gz
Source1:        cello-extra.tar.gz

%prep
#%setup -q -D -n cello -a 1
%setup -q -D -n cello -b 1

$ tree ../SOURCES/
../SOURCES/
├── bello-0.1.tar.gz
├── cello-1.0.tar.gz
├── cello-extra.tar.gz
├── cello-output-first-patch.patch
└── pello-0.1.1.tar.gz

-b选项效果:

$ rpmbuild -bb cello.spec --debug
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.jfuaQ1
+ umask 022
+ cd /home/study/rpmbuild/BUILD
+ cd /home/study/rpmbuild/BUILD
+ /usr/bin/gzip -dc /home/study/rpmbuild/SOURCES/cello-1.0.tar.gz
+ /usr/bin/tar -xf -
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ /usr/bin/gzip -dc /home/study/rpmbuild/SOURCES/cello-extra.tar.gz
+ /usr/bin/tar -xf -
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ cd cello
+ /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ exit 0

$ tree ../BUILD
../BUILD
├── cello
│   ├── cello
│   ├── cello.c
│   ├── cello-output-first-patch.patch
│   ├── debugfiles.list
│   ├── debuglinks.list
│   ├── debugsources.list
│   ├── elfbins.list
│   ├── LICENSE
│   └── Makefile
└── cello-extra
    └── test.sh

-a选项效果:

$ rpmbuild -bb cello.spec --debug
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.JcZtHp
+ umask 022
+ cd /home/study/rpmbuild/BUILD
+ cd /home/study/rpmbuild/BUILD
+ /usr/bin/gzip -dc /home/study/rpmbuild/SOURCES/cello-1.0.tar.gz
+ /usr/bin/tar -xf -
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ cd cello
+ /usr/bin/gzip -dc /home/study/rpmbuild/SOURCES/cello-extra.tar.gz
+ /usr/bin/tar -xf -
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ exit 0

$ tree ../BUILD
../BUILD
└── cello
    ├── cello
    ├── cello.c
    ├── cello-extra
       └── test.sh
    ├── cello-output-first-patch.patch
    ├── debugfiles.list
    ├── debuglinks.list
    ├── debugsources.list
    ├── elfbins.list
    ├── LICENSE
    └── Makefile

3.3 %files

%files部分中常见的高级宏:

定义
%license 指定作为 LICENSE 的文件,它将由RPM安装和标记。Example: %license LICENSE
%doc 指定作为 文档 的文件,它将由RPM安装和标记。 还用于代码示例和文档附带的各种项目。 如果包含代码示例,则应注意从文件中删除可执行模式。 Example: %doc README
%dir 标识该路径是该RPM应包含的目录。这很重要,以便RPM文件清单准确地知道卸载时要清理的目录。Example: %dir %{_libdir}/%{name}
%config(noreplace) 指定以下文件是配置文件,因此如果已从原始安装校验中修改文件,则不应在RPM包安装时或更新时覆盖(或替换)该文件。如果发生更改,将在升级或安装时使用.rpmnew附加到文件名末尾创建文件,以便不修改目标系统上预先存在或已修改的文件。Example: %config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf

3.4 覆盖与自定义宏

可以在~/.rpmmacros文件中覆盖宏和自定义宏(不推荐)。

%_topdir /opt/some/working/directory/rpmbuild
%_smp_mflags -l3

4. Scriptlets and Triggers

在RPM软件包中,有一系列指令可用于在RPM安装期间对系统进行必要或所需的更改。它们被称为scriptlets

一部分指令如下:

指令 定义
%pre 在目标操作系统安装RPM包之前执行的指令。
%post 在目标操作系统安装RPM包之后执行的指令。
%preun 在目标操作系统卸载RPM包之前执行的指令。
%postun 在目标操作系统卸载RPM包之后执行的指令。

另一个对RPM事务提供更细粒度控制的项目就是所谓的触发器。

这些与scriptlet实际上是相同的,但在RPM安装或升级事务期间以非常特定的操作顺序执行,允许对整个过程进行更细粒度的控制。

参考/usr/share/doc/rpm-4.*/triggers文件使用。

4.1 在spec文件中使用非shell脚本

scriptlet的-p选项运行使用指定的解释器代替默认的-p /bin/sh

$ cat cello.spec
# RPM包安装完成后使用python输出信息
%post -p /usr/bin/python
print "this is a scriplet by python"
$ rpm -Uvh ../RPMS/x86_64/cello-1.0-1.el7.x86_64.rpm --force
Preparing...                          ################################# [100%]
Updating / installing...
   1:cello-1.0-1.el7                  ################################# [100%]
this is a scriplet by python

5. RPM条件语句

条件语句一般适用于以下场景:

  • 特定于架构的部分

  • 特定于操作系统的部分

  • 各种版本的操作系统之间的兼容性问题

  • 宏的存在和定义

5.1 语法

%if expression
...
%endif
%if expression
...
%else
...
%endif

5.2 示例

%if 0%{?rhel} == 6
sed -i '/AS_FUNCTION_DESCRIBE/ s/^/#/' configure.in
sed -i '/AS_FUNCTION_DESCRIBE/ s/^/#/' acinclude.m4
%endif

表达式与上面的例子等价:

%if 0%{?el6}
%global ruby_sitearch %(ruby -rrbconfig -e 'puts Config::CONFIG["sitearchdir"]')
%endif
%if 0%{?fedora} >= 19
%global with_rubypick 1
%endif
%define ruby_archive %{name}-%{ruby_version}
%if 0%{?milestone:1}%{?revision:1} != 0
%define ruby_archive %{ruby_archive}-%{?milestone}%{?!milestone:%{?revision:r%{revision}}}
%endif

5.3 变种

%ifarch%ifnarch%ifos是条件语句的特殊变种。

%ifarch可以跟多个体系架构,每个用逗号或空格分开:

%ifarch i386 sparc
...
%endif

%ifnarch与%ifarch逻辑相反。

%ifos控制操作系统:

%ifos linux
...
%endif