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说明⚓
没有找到官方文档中提到的选项,使用rpmsign
和rpmkeys
代替。
$ 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 自定义宏⚓
%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