RPM包的制作
目录
扫盲
rpm包的安装
spec文件说明
rpm包构建过程
tips
扫盲
rpmdev-setuptree 在用户的home目录生成rpm工作目录,工作目录默认名rpmbuild,下面默认生成几个子目录:BUILD BUILDROOT RPMS SOURCES SPECS SRPMS
制作用rpm包用rpmbuild,它以SPECS目录下的xxx.spec文件为输入,这个spec文件是说明rpm构建过程及安装过程的文本文件。
rpm其实就是一个压缩包,包含了软件目录下的各种文件和目录,也包括了一些脚本,这些脚本是安装软件的时候执行的,所以在安装rpm包的时候,有时候会看到,不仅仅是把软件复制到了安装目录下,还会创建一些用户,注册服务等等,这些都是执行了rpm中的脚本,而rpm中的脚本就是shell脚本。
在很久很久以前一个很遥远的地方,rpmbuild并不仅仅是用来打个包,人们还用它解压源码压缩包,然后编译源码,把编译的结果放到一个目录下,把编译结果打成rpm包,还会把安装时要执行的脚本也加到这个包中,最后得到rpm包。
我的需求是:我自己已经编译并打包好了软件的二进制版本,并不需要rpmbuild 的编译安装过程,只需要把我自己的二进制软件目录打包成rpm包,然后用rpm安装时再执行一些依赖检查和脚本。
rpm包的安装
众所周知,rpm包需要root用户来安装,这是因为rpm包的安装目标路径,在打包的时候就已经设置好了(在spec中设置),虽然也可以在安装时用--relocate修改,不过大多数人不会修改,因此,安装路径通常只有root用户才有权限。
在构建rpm包时,可以在spec文件中设置安装前和安装后执行的shell命令,也可以设置卸载前和卸载后的shell命令。
除此以外rpm包安装时,还会查看依赖包是否存在,依赖包的名称和版本号,也是在spec里设置。rpm是一个操作系统中安装和卸载软件的管理系统,它有数据库会记录已经安装的软件的名称和版本号,安装时,rpm查询这个数据库,判断所需版本的依赖包是否已经安装。
可用 rpm -qa |grep mysql 查询某个包是否安装了。
spec文件说明
下面是一个spec文件的例子:
Name: opengauss-hadb
Version: 5.0.0
Release: 1
Group: Development/Libraries
License: ASL 2.0
URL: http://www.newland.com.cn/
Source0: opengauss-hadb-5.0.0-2.tar.gz
Summary: opengauss & patroni package
#BuildRequires:
Requires: pam >= 1.4.0
Requires: libffi >= 3.3
Requires: libaio >= 0.3.112
Requires: ncurses >= 6.2
Requires: python3 >= 3.7.9
Requires: openssl >= 1.1.1t
Requires: glibc-common >= 2.28
Requires: glibc-devel >= 2.28
Requires: glibc >= 2.28
%description
opengauss & patroni package
%prep
%setup -q
tar zcf opengauss.tar.gz opengauss
tar zcf python3.tar.gz python3
tar zcf netdata.template.tar.gz netdata.template
rm -rf opengauss python3 netdata.template
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}/usr/lib/opengauss-hadb-5.0.0
cp -fr ./* %{buildroot}/usr/lib/opengauss-hadb-5.0.0
%files
/usr/lib/opengauss-hadb-5.0.0
%defattr(-,root,root,-)
%pre
useradd postgres
%post
#!/bin/sh
tar xf /usr/lib/opengauss-hadb-5.0.0/opengauss.tar.gz -C /usr/lib/opengauss-hadb-5.0.0/
tar xf /usr/lib/opengauss-hadb-5.0.0/python3.tar.gz -C /usr/lib/opengauss-hadb-5.0.0/
tar xf /usr/lib/opengauss-hadb-5.0.0/netdata.template.tar.gz -C /usr/lib/opengauss-hadb-5.0.0/
chown root:root -R /usr/lib/opengauss-hadb-5.0.0/
rm -rf /usr/lib/opengauss-hadb-5.0.0/{opengauss.tar.gz,python3.tar.gz,netdata.template.tar.gz}
%preun
touch /usr/lib/opengauss-hadb-5.0.0/{opengauss.tar.gz,python3.tar.gz,netdata.template.tar.gz}
%postun
#!/bin/sh
#userdel -r postgres
rm -rf /usr/lib/opengauss-hadb-5.0.0
echo "Uninstall opengauss-hadb-5.0.0 successfully."
Name: opengauss-hadb
Version: 5.0.0
Release: 1
Group: Development/Libraries
License: ASL 2.0
URL: http://www.newland.com.cn/
Source0: opengauss-hadb-5.0.0-2.tar.gz
Summary: opengauss & patroni package
这些变量是rpm包的信息,其中Name、Version、Release会用来构造rpm包的文件名,卸载rpm包的时候需要指定Name,如果这个包被别的rpm包依赖,版本是否符合,就根据Version判断,Version的格式可以是x.x.x或x.x,也可以有字母例如 1.1.1t,判断版本大小是根据版本号和字母的ascii码(参考 rpm: Dependencies)
Source0 是源码或者我的二进制文件目录的tar包,要事先放到SOURCE目录下,执行构建的命令 rpmbuild -bb SPEC/my.spec 时,他会吧SOURCE目录下Source0指向的包,解压到BUILD目录下。这里有个隐含的要求:这个tar包解压后,应该生成一个名称由 Name-Version 组成目录,也就是说 opengauss-hadb-5.0.0-2.tar.gz 解压后应该是名称为 opengauss-hadb-5.0.0的目录,其中压缩包的名字是啥无所谓,只要和Source0指的是一样就可以了,但是解压缩后的目录名,必须由Name-Version组成。
Release 是必要的,但是没有限制,Group、License、URL、Summary可以根据需要随便填。
#BuildRequires:
Requires: pam >= 1.4.0
Requires: libffi >= 3.3
Requires: libaio >= 0.3.112
Requires: ncurses >= 6.2
Requires: python3 >= 3.7.9
Requires: openssl >= 1.1.1t
Requires: glibc-common >= 2.28
Requires: glibc-devel >= 2.28
Requires: glibc >= 2.28%description
opengauss & patroni package
指定依赖的报名和版本,这些包在rpm安装时会检查安装机器上是否由符合条件的包。
%description 随便写
%prep
%setup -q
tar zcf opengauss.tar.gz opengauss
tar zcf python3.tar.gz python3
tar zcf netdata.template.tar.gz netdata.template
rm -rf opengauss python3 netdata.template
%prep #是预处理阶段,是复制 SOURCE 下的 Source0指向的压缩包,到BUILD目录下
%setup -q #是解压BUILD目录下的压缩包,并cd 到 Name-Version目录下
之后的shell脚本都是在BUILD/Name-Version目录下执行的。
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}/usr/lib/opengauss-hadb-5.0.0
cp -fr ./* %{buildroot}/usr/lib/opengauss-hadb-5.0.0
这个%install很有迷惑性,它其实指的是源码解压到BUILD目录下后,make & make install,编译结果复制到BUILDROOT目录下,准确的说是复制到 [name]-[version]-[release].[arch] 目录下,这个目录又称作“假根”,意思是这个目录的下的结构,就是rpm安装时软件相对于根目录的部署,理解这一点非常重要,其中%{buildroot}表示当前这个构建过程的假根。
由于我们没有make install的过程,所以我们直接在%{buildroot}下创建目录结构,并且把我们的二进制软件复制到目录结构中,这里我们复制到了 %{buildroot}/usr/lib/opengauss-hadb-5.0.0 下,意味着,安装这个rpm时,软件将安装到目标机器的 /usr/lib/opengauss-hadb-5.0.0 目录下。
%files
/usr/lib/opengauss-hadb-5.0.0
%defattr(-,root,root,-)
%file 是指将哪些目录及其下面的文件、子目录加入到rpm包,%defattr(-,root,root,-) 是目录下的文件及子目录的owner和权限,使用这种方法,虽然不是以root用户执行rpmbuild的,打包时也可以指定root所有者和权限,否则rpm安装后这些目录和文件是执行rpmbuild用户的owner和权限。
%pre
useradd postgres
%post
#!/bin/sh
tar xf /usr/lib/opengauss-hadb-5.0.0/opengauss.tar.gz -C /usr/lib/opengauss-hadb-5.0.0/
tar xf /usr/lib/opengauss-hadb-5.0.0/python3.tar.gz -C /usr/lib/opengauss-hadb-5.0.0/
tar xf /usr/lib/opengauss-hadb-5.0.0/netdata.template.tar.gz -C /usr/lib/opengauss-hadb-5.0.0/
chown root:root -R /usr/lib/opengauss-hadb-5.0.0/
rm -rf /usr/lib/opengauss-hadb-5.0.0/{opengauss.tar.gz,python3.tar.gz,netdata.template.tar.gz}
%pre 是指rpm安装前执行的shell 脚本
%post 是指rpm安装后执行的shell 脚本
以root用户安装rpm,因此执行这些命令的用户都是root
%preun
touch /usr/lib/opengauss-hadb-5.0.0/{opengauss.tar.gz,python3.tar.gz,netdata.template.tar.gz}%postun
#!/bin/sh
#userdel -r postgres
rm -rf /usr/lib/opengauss-hadb-5.0.0
echo "Uninstall opengauss-hadb-5.0.0 successfully."
%preun 卸载前执行的命令
%postun 卸载后执行的命令
注意,userdel -r postgres 删除用户时会删除它的home目录。
rpm包构建过程
可能前面已经说过了,构建的命令是:
rpmbuild -bb SPEC/abc.spec
它会自动寻找与SPEC同一层目录下的,BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS这些目录,把中间结果或最终结果放在里面。在%prep阶段(使用%setup -q)它会首先解压SOURCES目录下Source0指向的tar包到BUILD目录下,在%install阶段,在BUILRROOT目录下构建与安装时相同的目录结构,只不过根目录是“假根”,然后将BUILD下的文件目录等,复制到假根下面的目录结构,然后通过%file指定哪些目录下的内容包含到rpm包中,最后(如果有的话)编写rpm安装前后执行的脚本,和卸载前后执行的脚本。最后生成的rpm包在RPMS目录下。
tips
构建rpm包时,也可以不用rpmdev-setuptree在家目录下创建rpmbuild,而是可以在任意目录下创建工作目录结构,然后修改宏变量_topdir,这个_topdir的路径就是rpmbuild的路径,例如:
rpmbuild -bb --define "_topdir /mnt/disk03/rpmbuild-opengauss-5.0.0" /mnt/disk03/rpmbuild-opengauss-5.0.0/SPECS/opengauss-client-5.0.0.spec
rpm安装的时候也可以指定非默认目录,但是这样做的前提是,制作rpm时要能relocate,但是我发现,这也有它的局限性,例如rpm安装后的脚本不能获得改变后的安装目录,无法做相应设置,因此可以先不考虑这个特性。
Prefix : /usr/lib
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}/usr/lib/opengauss-client-5.0.0
cp -fr ./* %{buildroot}/usr/lib/opengauss-client-5.0.0
%files
%defattr(-,root,root,-)
%attr(0755,root,root) /usr/lib/opengauss-client-5.0.0
rpm -ivh abc.rpm --prefix=/opt # 就会把spec里的/usr/lib,在安装时替换成/opt,但是有个问题是在%post的安装后脚本里不知如何获取新的prefix。
还有一种安装的配置,不需要为安装的软件创建用户,可以给所有用户使用。方法就是以root用户安装,可执行文件权限设置为 755,PATH和LD_LIBRARY_PATH环境变量设置到 /etc/profile,这样对所有用户就可用了。
设置和删除环境变量的命令可以用这个:
%post
echo "export LD_LIBRARY_PATH=/usr/lib/opengauss-client-5.0.0/lib64:\$LD_LIBRARY_PATH" >> /etc/profile
echo "export PATH=/usr/lib/opengauss-client-5.0.0/bin:\$PATH" >> /etc/profile
%postun
# delete the line with pattern
sed '/^export .*opengauss-client-5.0.0.*/d' -i /etc/profile
rpmbuild过程中有个步骤是CHECK_RPATHS,就是检查可执行文件或者so文件中的rpath,rpath是寻找依赖的so文件的路径,一般是编译时确定的,在编译时可以通过-rpath选项控制,一般设置为空。rpmbuild发现找不到可执行文件或者so内嵌的rpath就会报错,解决办法就是将~/.rpmmacros中的下面注释掉:
关于rpath,参考 RPATH是什么 - 邱明成 - 博客园
参考:
这是一个关于RPM的很好的教程:Maximum RPM
rpmbuild打包遇到问题汇总_rpmbuild contains an invalid rpath-CSDN博客
linux下如何完全删除用户_linux删除用户-CSDN博客
RPM打包原理、示例、详解及备查 - yipianchuyun - 博客园
rpmbuild打包任意文件及目录制作为rpm文件_rpmbuild指定编译目录-CSDN博客
error: Installed (but unpackaged) file(s) found_error: installed (but unpackaged) file(s) found:-CSDN博客
RPM包管理与操作-CSDN博客