禁止自动分析源码添加不应该加入的依赖 在spec文件中加入Autoreq: 0即可
opnvswitch源码打包需要加入,否则会解析代码中的依赖,在ISO中安装检测失败/bin/python3
%post 部分如果有shell 则依赖关系会自动增加/bin/sh , 如果合并入镜像是不能有/bin/sh /bin/python等
一、打包工具安装
执行命令安装所需的rpm包:
yum install rpmdevtools rpmlint make gcc -y
rpmdevtools包里包括了rpmbuild ,安装好工具后,使用命令rpmdev-setuptree
生成~/rpmbuild目录及其子目录
tree rpmbuild
rpmbuild
├── BUILD
├── RPMS
├── SOURCES
├── SPECS
└── SRPMS
或者手动生成目录,命令如下:
mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
Fedora的文档建议不使用root用户进行RPM打包
二、使用spec文件创建rpm包
切换路径至SPECS文件夹,使用命令创建spec文件(rpm打包的脚本规则文件),执行命令
rpmdev-newspec myrpm
在当前目录生成了myrpm.spec文件,内容如下(增加了说明和其他部分,源文件比较简单)
Name: myrpm
Version:
Release: 1%{?dist}
Summary:
License: # GPLBSDMITor
URL:
Source0: SOURCES/%{name}-%{version}.tar.gz
BuildRequires: # rpm
Requires: # rpm -ivh
%description
#
%prep
# %prep %setup -q SOURCES${Source0} BUILD %{name}-%{version}
%setup -q
# %patch0 -p1
%build
# %build %build
# cdBUILD/%{name}-%{version}()
%configure # %configureconfigureautoconfconfigure ,
make %{?_smp_mflags} # %{_smp_mflags} cpuCPUmake , ~/.rpmmacros
%install
# %install
# BUILD/%{name}-%{version}()
# BUILDROOTBUILDROOT
rm -rf $RPM_BUILD_ROOT #BUILDROOTcentos6 $RPM_BUILD_ROOT %{buildroot} BUILDROOT/%{name}-%{version}-%{release}.%{_arch}
%make_install # %make_installmake install
%clean
#RPMBUILD
rm -rf %{buildroot}
%files
# BUILDROOT/package/ , /usr/bin/test
%config(noreplace) %{_sysconfdir}/foo.conf # *.rpmsave *.rpmnew
%config %{_sysconfdir}/foo.conf # /etc
%attr(755, root, root) %{_sysconfdir}/foo.conf
%attr(mode, user, group) %{_sysconfdir}/foo.conf # ”-”
/usr/bin/test
%doc
%changelog
## rpm -q package --changelog
开始部分里开头是大写字母的字段,可以被当做变量引用 %{name} %{version} %{release} ...
rpmlint xx.spec可以检测spec语法正确性
spec 中不需要的部分直接删除即可
%build和%install部分都是先cd到BUILD/包名的目录下, 在执行其他命令
三、 SPEC规则注解
rpmbuild
├── BUILD
├── BUILDROOT
├── RPMS
├── SOURCES
├── SPECS
└── SRPMS
rpmdev-setuptree 执行完之后缺少一个BUILDROOT目录, 实际执行 还要用到该目录, 但不需手动创建,在rpmbuild的过程中会 自动创建。
rpmbuild的规则里有一些内置宏变量,以%开头的, 查看方法rpmbuild --showrc 或者rpm --showrc 几种方式都可以查看;
要查看具体某个宏的值可以使用rpm --eval/-E , 比如 (规范宏写法应该是%{_name})$ rpm -E %{_topdir} /home/wanglin/rpmbuild
路径 | 宏 | 名称 | 用途 |
---|---|---|---|
~/rpmbuild/BUILD | %_builddir | 构建目录 | 源代码包从SOURCES目录解压缩后放到该目录,并在该目录进行编译即configure 和 make命令 |
~/rpmbuild/BUILDROOT | %_buildrootdir | 最终安装目录 | 保存%install阶段安装的文件,即make install执行后产生的文件 |
~/rpmbuild/RPMS | %_rpmdir | 标准rpm包目录 | 生成的二进制rpm包会存放在该目录 |
~/rpmbuild/SOURCES | %_sourcedir | 源代码目录 | 源码tar.gz包放到该目录 |
~/rpmbuild/SPECS | %_specdir | Spec文件目录 | Spec文件存放路径 |
~/rpmbuild/SRPMS | %_srcrpmdir | 源码RPM包目录 | 生成的srpm包存放在该目录 |
四、spec中的脚本
脚本片段 类似kickstart文件,使用shell编写
- %pre: 在rpm包安装之前执行
- %post: 在安装rpm包之后执行
- %preun: 在卸载rpm包之前执行
- %postun: 在卸载rpm包之后执行
- %pretrans: 在事务开始时执行
- %posttrans: 在事务结束时执行
通常所有安装脚本和触发器脚本都是使用 /bin/sh 来执行的,如果想使用其他脚本,如 perl,可使用 -p /usr/bin/perl 来告诉 rpm 使用perl解释器
如果仅执行一个命令,则 “-p” 选项会直接执行,而不启用 shell。然而,若有许多命令时,不要使用此选项,按正常编写 shell 脚 本即可。
%pre、%post、%preun 和 %postun 提供 $1 参数,表示动作完成后,系统中保留的此名称的软件包数量。
因此可用于检查软件安装情况,不过不要比较此参数值是否等于 2,而是比较是否大于等于 2。对于%pretrans 和 %posttrans,$1 的值恒为 0。
- | install | upgrade | uninstall |
---|---|---|---|
%pretrans | $1 == 0 | $1 == 0 | (N/A) |
%pre | $1 == 1 | $1 == 2 | (N/A) |
%post | $1 == 1 | $1 == 2 | (N/A) |
%preun | (N/A) | $1 == 1 | $1 == 0 |
%postun | (N/A) | $1 == 1 | $1 == 0 |
%posttrans | $1 == 0 | $1 == 0 | (N/A) |
示例1:
%post
if [ "$1" = 1 ]; then
$1 == 0 $1 == 2 $1 == 2 $1 == 1 $1 == 1 $1 == 0
(N/A) (N/A) (N/A) $1 == 0 $1 == 0 (N/A)
# rpm1 if
if [ ! -f %{_sysconfdir}/shells ] ; then
echo "%{_bindir}/foo" > %{_sysconfdir}/shells
echo "/bin/foo" >> %{_sysconfdir}/shells
else
grep -q "^%{_bindir}/foo$" %{_sysconfdir}/shells || echo "%{_bindir}/foo" >> %{_sysconfdir}/shells
grep -q "^/bin/foo$" %{_sysconfdir}/shells || echo "/bin/foo" >> %{_sysconfdir}/shells
fi
%postun #0
if [ "$1" = 0 ] && [ -f %{_sysconfdir}/shells ] ; then
sed -i '\!^%{_bindir}/foo$!d' %{_sysconfdir}/shells
sed -i '\!^/bin/foo$!d' %{_sysconfdir}/shells
fi
示例2:
例如,如果软件包安装了一份 info 手册,那么可以用 info 包提供的 install-info 来更新 info 手册索引。 首先,我们不保证系统已安装 info 软件包,除非明确声明需要它;其次,我们不想在 install-info 执行失败时,使软件包安装失败:
Requires(post): info
Requires(preun): info
... %post
/sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || :
%preun
if [ $1 = 0 ] ; then
/sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || :
fi
上边的示例中还有一个安装 info 手册时的小问题需要解释一下。install-info 命令会更新 info 目录,所以我们应该在 %install 阶段删 除 %{buildroot} 中无用的空目录:
rm -f %{buildroot}%{_infodir}/dir
一般最后一条命令的exit状态就是脚本的exit状态,除一些特殊情况,一般脚本都是以exit 0状态退出所以大部分脚本片段都会使用 "||:" 退出
五、执行打包命令生成rpm
SPEC 编写完毕,执行以下命令来构建 SRPM 和 RPM 包:
rpmbuild -ba myrpm.spec
如果成功,RPM 会保存至 ~/rpmbuild/RPMS,SRPM 会保存至 ~/rpmbuild/SRPMS。
如果失败,请查看 BUILD 目录的相应编译日志。为了帮助调试,可以用 --short-circuit 选项来忽略成功的阶段。例如,若想要(略过更早的阶段)重新从 %install 阶段开始,请执行:
rpmbuild -bi --short-circuit myrpm.spec
如果只想创建 RPM,请执行:
rpmbuild -bb myrpm.spec
如果只想创建 SRPM(不需要执行 %prep 或 %build 或其他阶段),请执行:
rpmbuild -bs myrpm.spec
rpmlint检查
为避免常见错误,请先使用 rpmlint 查找 SPEC 文件的错误:
rpmlint program.spec
如果返回错误/警告,使用 “-i” 选项查看更详细的信息。
也可以使用 rpmlint 测试已构建的 RPM 包,检查 SPEC/RPM/SRPM 是否存在错误。你需要在发布软件包之前,解决这些警告。此 页面 提供一些常见问题的解释。如果你位于 SPEC 目录中,请执行:
rpmlint myrpm.spec ../RPMS/*/NAME*.rpm ../SRPMS/NAME*.rpm
进入 ~/rpmbuild/RPMS 下的特定架构目录中,您会发现有许多二进制 RPM 包。使用以下命令快速查看 RPM 包含的文件和权限:
rpmls *.rpm
六、部分宏变量
%{_sysconfdir} /etc
%{_prefix} /usr
%{_exec_prefix} %{_prefix}
%{_bindir} %{_exec_prefix}/bin
%{_libdir} %{_exec_prefix}/%{_lib}
%{_libexecdir} %{_exec_prefix}/libexec
%{_sbindir} %{_exec_prefix}/sbin
%{_sharedstatedir} /var/lib
%{_datarootdir} %{_prefix}/share
%{_datadir} %{_datarootdir}
%{_includedir} %{_prefix}/include
%{_infodir} /usr/share/info
%{_mandir} /usr/share/man
%{_localstatedir} /var
%{_initddir} %{_sysconfdir}/rc.d/init.d
%{_var} /var
%{_tmppath} %{_var} /tmp
%{_usr} /usr
%{_usrsrc} %{_usr}/src
%{_lib} lib (lib64 on 64bit multilib systems)
%{_docdir} %{_datadir}/doc
%{buildroot} %{_buildrootdir}/%{name}-%{version}-%{release}.%{_arch}
$RPM_BUILD_ROOT %{buildroot}
%{_unitdir} /usr/lib/systemd/system
%define Macro Value Macro=Value
七、一个只打包文件的spec实例
Name: cvk_tools
Version: 1.1
Release: 1%{?dist}
Summary: cvk_tools by wanglin
License: GPLv3
URL:
Source: %{name}-%{version}.tar.gz
BuildArch: %{_arch}
Autoreq: 0
%prep
%setup -q
%description
init cvk
%define pack_path root/%{name}-%{version}
%define ca_path etc/pki/CA
%install
install -d %{buildroot}/%{pack_path}
install -d %{buildroot}/%{ca_path}
for i in `ls ./`;do
if [ -f $i ];then
install ./$i %{buildroot}/%{pack_path}
elif [ -d $i ];then
if [[ $i =~ "ca" ]];then
install -m 444 -o root -g root ./ca/cacert.pem %{buildroot}/%{ca_path}
else
install -d %{buildroot}/%{pack_path}/$i
for f in `ls $i`;do
install $i/$f %{buildroot}/%{pack_path}/$i
done
fi
fi
done
# Upgarde${conf}.old
%pre
conf=/%{pack_path}/tools.conf
if (( $1 == 2 ));then
[ -f $conf ] && mv $conf ${conf}.old || :
fi
# Upgrade
%post
conf=/%{pack_path}/tools.conf
if (( $1 == 2 ));then
[ -f ${conf}.old ] && mv ${conf}.old ${conf} || :
fi
%postun
if (( $1 == 0 ));then
[ -d /%{ca_path} ] && rm -rf /%{ca_path} || :
[ -d $(dirname /%{ca_path}) ] && rm -rf $(dirname /%{ca_path}) || :
[ -d /%{pack_path} ] && rm -rf /%{pack_path} || :
fi
%files
%attr(-, root, root)
# first method
/%{pack_path}
/%{ca_path}
#----second method
#%dir /%{pack_path}
#/%{pack_path}/*
#%dir /%{ca_path}
#/%{ca_path}/*
%doc
%changelog
* Fri Jul 1 2022 cvk_tools Release - 1.1-1
- Second Version release
* Thu Nov 25 2021 cvk_tools Release - 1.0-1
- Initial first version
附录: 打包遇到的问题
- fle段缺少文件
Checking for unpackaged file(s): /usr/lib/rpm/check-files /root/rpmbuild/BUILDROOT/lldpad-1.1-1.el7.x86_64
error: Installed (but unpackaged) file(s) found:
/usr/etc/bash_completion.d/lldpad
/usr/etc/bash_completion.d/lldptool
/usr/include/lldpad/clif.h
/usr/include/lldpad/clif_msgs.h
/usr/include/lldpad/clif_sock.h
...
RPM build errors:
Installed (but unpackaged) file(s) found:
/usr/etc/bash_completion.d/lldpad
/usr/etc/bash_completion.d/lldptool
/usr/include/lldpad/clif.h
/usr/include/lldpad/clif_msgs.h
/usr/include/lldpad/clif_sock.h
...
这个意思是说发现了制作RPM包的spec脚本中没有包含但又被安装的文件
如果显示很多文件,将这些文件你可以分门别类,用%doc、%config这些宏来指定,其实如果它报错的时候,只显示一两个文件,可 以直接把那个文件写在%file下面
vi /usr/lib/rpm/macros,找到%__check_files %{_rpmconfigdir}/check-files %{buildroot} 这一行,把这一行注释掉,然后重新编 译。
- file段重复包含
warning: File listed twice: /etc/pki/CA/cacert.pem
warning: File listed twice: /root/cvk_tools-1.1/README.md
warning: File listed twice: /root/cvk_tools-1.1/config_ca.sh
...
重复包含了文件
去掉file段重复目录或者文件的包含关系