rpm本地打包之spec文件(linux)
rpm本地打包的回顾
- 在Linux之rpm本地打包中分析了rpm的常用指令、rpmbuild的常用命令以及参数的配置说明,详细情况请进入链接详细查看。接下来重点是分析spec文件。
spec文件
- 能够熟悉在以上链接中的操作命令以及配置参数的含义,管理日常的rpm软件包也就不成问题了。
- 然而,随着linux操作系统越来越流行于世界各地,越来越多的开发者更喜欢采用RPM格式来发布自己的软件包。那么RPM软件包是怎么样制作的了?对于大多数的开发者来说还是比较陌生的。
其实,制作RPM软件包并不是一件复杂的工作,其中最为关键在于编写
SPEC
软件包描述文件。要想制作一个rpm软件包就必须写一个软件包描述文件(SPEC
)。这个文件中包含了软件包的诸多信息,如软件包的名字、版本、类别、说明摘要、创建时要执行什么指令、安装时要执行什么操作、以及软件包所要包含的文件列表等等。描述文件如下:
文件头:
- 一般的spec文件头包含以下几个域:
Name:
- 软件包的名字,最终RPM软件包是用该名字与版本号,释出号及体系号来命名软件包的。
Name(example):
Name: python-django-horizon
Epoch:
- 软件包的系列,也就是软件包的体系号,表示软件包的系列
Epoch(example):
Epoch: 1
Version:
- 软件包的版本号。仅当软件包比以前有较大改变时才增加版本号。
Version(example):
Version: 4.0.2
Release:
- 软件包释出号。一般我们对该软件包做一些小的补丁的时候就应该释出号出1。
Relase(example):
Release: 4.0.2
注意: 其中
Epoch:Version:Release
表示了rpm包的新旧,优先级依次降低,打出的rpm包也是以${package}-${Version}-${Release}
命名
Summary:
- 一句话概括该软件包尽量多的信息。
Summary(example):
Summary: Django application for talking to Openstack
Group:
- 软件包所属类别,具体类别有:
Amusements/Games
(娱乐/游戏)Amusements/Graphics
(娱乐/图形)Applications/Archiving
(应用/文档)Applications/Communications
(应用/通讯)Applications/Databases
(应用/数据库)Applications/Editors
(应用/编辑器)Applications/Emulators
(应用/仿真器)Applications/Engineering
(应用/工程)Applications/File
(应用/文件)Applications/Internet
(应用/因特网)Applications/Multimedia
(应用/多媒体)Applications/Productivity
(应用/产品)Applications/Publishing
(应用/印刷)Applications/System
(应用/系统)Applications/Text
(应用/文本)Development/Debuggers
(开发/调试器)Development/Languages
(开发/语言)Development/Libraries
(开发/函数库)Development/System
(开发/系统)Development/Tools
(开发/工具)Documentation
(文档)System Environment/Base
(系统环境/基础)System Environment/Daemons
(系统环境/守护)System Environment/Kernel
(系统环境/内核)System Environment/Libraries
(系统环境/函数库)System Environment/Shells
(系统环境/接口)User Interface/Desktops
(用户界面/桌面)User Interface/X
(用户界面/X窗口)User Interface/X Hardware Support
(用户界面/X硬件支持)
Group(example):
Group: Development/Libraries
License:
- 软件包的发行许可证,也就是软件的授权方式,通常是GPL。
License(example)
# Code in horizon/horizon/utils taken from django which is BSD
License: ASL 2.0 and BSD
URL:
- rpm软件包的主页链接地址。
URL(example):
URL: http://horizon.openstack.org/
Source:
- 源程序软件包的名称。例如:
horizon-9.0.1.tar.gz
注意: Source[0-n]是rpm软件包打包时的源代码
Source(example):
Source0: https://tarballs.openstack.org/horizon/horizon-%{upstream_version}.tar.gz
Source2: openstack-dashboard-httpd-2.4.conf
Source3: python-django-horizon-systemd.conf
# demo config for separate logging
Source4: openstack-dashboard-httpd-logging.conf
# logrotate config
Source5: python-django-horizon-logrotate.conf
BuildArch:
- 指编译的目标处理器架构,noarch标识不指定,但通常都是以
/usr/lib/rpm/marcros
中的内容为默认值。
BuildArch(example):
BuildArch: noarch
BuildRequires:
- rpm软件包构建过程中所依赖的软件包名称,可以使用>=或者<=表示大于或者小于某一特定版本。
BuildRequires(example):
BuildRequires: python-setuptools
BuildRequires: python-pbr >= 2.0.0
BuildRequires: git
BuildRequires: python-six >= 1.10.0
BuildRequires: gettext
Requires:
- rpm软件包所依赖的软件包名称,可以使用>=或者<=表示大于或者小于某一特定版本。
Requires(example):
Requires: python-django
Requires: pytz
Requires: python-six >= 1.10.0
Requires: python-pbr
Provides:
- 指明rpm软件包提供一些特定的功能,以便其他rpm识别。
Provides(example):
# additional provides to be consistent with other django packages
Provides: django-horizon = %{epoch}:%{version}-%{release}
Obsoletes:
- 过时的,废弃的软件包
Obsoletes(example):
Obsoletes: python-django-openstack-auth < 4.0.0-1
Obsoletes: python2-django-openstack-auth < 4.0.0-1
%description:
- rpm软件包的详细说明。
%description(example):
%description
Horizon is a Django application for providing Openstack UI components.
It allows performing site administrator (viewing account resource usage,
configuring users, accounts, quotas, flavors, etc.) and end user
operations (start/stop/delete instances, create/restore snapshots, view
instance VNC console, etc.)
%package:
- 定义一个子包
%package(example):
%package -n openstack-dashboard
Summary: Openstack web user interface reference implementation
Group: Applications/System
Requires: httpd
%description doc
Documentation for the Django Horizon application for talking with Openstack
%files
段:
- 本段是文件段,用于定义软件包所包含的文件,分为三类–说明文档(doc),配置文件(config)及执行程序,还可定义文件存取权限,拥有者及组别。
%files
段(example):
%files -n openstack-dashboard -f dashboard.lang
...
注意: 当需要去定义一个子包时,必须至少包含Summary;Group;%description选项,任何没有指定的选项将使用父包的选项,如版本等。
%package -n openstack-dashboard
表示定义一个子包,子包的名称是openstack-dashboard
如果在%package
中使用了-n
选项,那么在使用%description
时也要加上,如:%package -n openstack-dashboard
以及它的%description -n openstack-dashboard
如果在%package
中使用了-n
选项,那么在使用%files
时也要加上,如:%package -n openstack-dashboard
以及它的%files -n openstack-dashboard -f dashboard.lang
%prep段:
- 这个段是预处理段,通常用来执行一些解开源程序包的命令,为下一步的编译安装作准备。%prep和下面的%build,%install段一样,除了可以执行RPM所定义的宏命令(以%开头)以外,还可以执行SHELL命令,命令可以有很多行,如我们常写的tar解包命令。
%prep段(example):
%prep
%autosetup -n horizon-%{upstream_version} -S git
# drop config snippet
cp -p %{SOURCE4} .
# customize default settings
# WAS [PATCH] disable debug, move web root
sed -i "/^DEBUG =.*/c\DEBUG = False" openstack_dashboard/local/local_settings.py.example
sed -i "/^WEBROOT =.*/c\WEBROOT = '/dashboard/'" openstack_dashboard/local/local_settings.py.example
sed -i "/^.*ALLOWED_HOSTS =.*/c\ALLOWED_HOSTS = ['horizon.example.com', 'localhost']" openstack_dashboard/local/local_settings.py.example
sed -i "/^.*LOCAL_PATH =.*/c\LOCAL_PATH = '/tmp'" openstack_dashboard/local/local_settings.py.example
sed -i "/^.*POLICY_FILES_PATH =.*/c\POLICY_FILES_PATH = '/etc/openstack-dashboard'" openstack_dashboard/local/local_settings.py.example
sed -i "/^BIN_DIR = .*/c\BIN_DIR = '/usr/bin'" openstack_dashboard/settings.py
sed -i "/^COMPRESS_PARSER = .*/a COMPRESS_OFFLINE = True" openstack_dashboard/settings.py
# set COMPRESS_OFFLINE=True
sed -i 's:COMPRESS_OFFLINE.=.False:COMPRESS_OFFLINE = True:' openstack_dashboard/settings.py
%build段:
- 本段是建立段,所要执行的命令为生成软件包服务,如make 命令。
%build段(example):
%build
# compile message strings
cd horizon && django-admin compilemessages && cd ..
cd openstack_dashboard && django-admin compilemessages && cd ..
# Dist tarball is missing .mo files so they're not listed in distributed egg metadata.
# Removing egg-info and letting PBR regenerate it was working around that issue
# but PBR cannot regenerate complete SOURCES.txt so some other files wont't get installed.
# Further reading why not remove upstream egg metadata:
# https://github.com/emonty/python-oslo-messaging/commit/f632684eb2d582253601e8da7ffdb8e55396e924
# https://fedorahosted.org/fpc/ticket/488
echo >> horizon.egg-info/SOURCES.txt
ls */locale/*/LC_MESSAGES/django*mo >> horizon.egg-info/SOURCES.txt
%{__python} setup.py build
# compress css, js etc.
cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py
# get it ready for compressing later in puppet-horizon
%{__python} manage.py collectstatic --noinput --clear
%{__python} manage.py compress --force
# build docs
export PYTHONPATH=.
sphinx-build -b html doc/source html
# undo hack
cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py
# Fix hidden-file-or-dir warnings
rm -fr html/.doctrees html/.buildinfo
%install段:
- 本段是安装段,其中的命令在安装软件包时将执行,如make install命令。
%install段(example):
%install
%{__python} setup.py install -O1 --skip-build --root %{buildroot}
# drop httpd-conf snippet
install -m 0644 -D -p %{SOURCE2} %{buildroot}%{_sysconfdir}/httpd/conf.d/openstack-dashboard.conf
install -d -m 755 %{buildroot}%{_datadir}/openstack-dashboard
install -d -m 755 %{buildroot}%{_sharedstatedir}/openstack-dashboard
install -d -m 755 %{buildroot}%{_sysconfdir}/openstack-dashboard
# create directory for systemd snippet
mkdir -p %{buildroot}%{_unitdir}/httpd.service.d/
cp %{SOURCE3} %{buildroot}%{_unitdir}/httpd.service.d/openstack-dashboard.conf
# Copy everything to /usr/share
mv %{buildroot}%{python_sitelib}/openstack_dashboard \
%{buildroot}%{_datadir}/openstack-dashboard
cp manage.py %{buildroot}%{_datadir}/openstack-dashboard
rm -rf %{buildroot}%{python_sitelib}/openstack_dashboard
# remove unnecessary .po files
find %{buildroot} -name django.po -exec rm '{}' \;
find %{buildroot} -name djangojs.po -exec rm '{}' \;
# Move config to /etc, symlink it back to /usr/share
mv %{buildroot}%{_datadir}/openstack-dashboard/openstack_dashboard/local/local_settings.py.example %{buildroot}%{_sysconfdir}/openstack-dashboard/local_settings
ln -s ../../../../..%{_sysconfdir}/openstack-dashboard/local_settings %{buildroot}%{_datadir}/openstack-dashboard/openstack_dashboard/local/local_settings.py
mv %{buildroot}%{_datadir}/openstack-dashboard/openstack_dashboard/conf/*.json %{buildroot}%{_sysconfdir}/openstack-dashboard
mv %{buildroot}%{_datadir}/openstack-dashboard/openstack_dashboard/conf/cinder_policy.d %{buildroot}%{_sysconfdir}/openstack-dashboard
mv %{buildroot}%{_datadir}/openstack-dashboard/openstack_dashboard/conf/nova_policy.d %{buildroot}%{_sysconfdir}/openstack-dashboard
%find_lang django --all-name
grep "\/usr\/share\/openstack-dashboard" django.lang > dashboard.lang
grep "\/site-packages\/horizon" django.lang > horizon.lang
# copy static files to %{_datadir}/openstack-dashboard/static
mkdir -p %{buildroot}%{_datadir}/openstack-dashboard/static
cp -a openstack_dashboard/static/* %{buildroot}%{_datadir}/openstack-dashboard/static
cp -a horizon/static/* %{buildroot}%{_datadir}/openstack-dashboard/static
cp -a static/* %{buildroot}%{_datadir}/openstack-dashboard/static
# create /var/run/openstack-dashboard/ and own it
mkdir -p %{buildroot}%{_sharedstatedir}/openstack-dashboard
# create /var/log/horizon and own it
mkdir -p %{buildroot}%{_var}/log/horizon
# place logrotate config:
mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d
cp -a %{SOURCE5} %{buildroot}%{_sysconfdir}/logrotate.d/openstack-dashboard
需要特别注意的是:
%install
部分使用的是绝对路径,而%files
使用的则是相对路径,虽然描述的是同一个地方,但是路径的书写格式千万不要出错
%files -n openstack-dashboard -f dashboard.lang
中的-f dashboard.lang
分析: 生成一个名为dashboard.lang
的文件,内容是所有的dashboard.mo
,-f
参数是将其后边接的文件合并到%files
的文件列表。
开始把软件安装到虚拟的根目录中,本段是安装段,其中的命令在安装软件包时将执行,如: make install命令、cp、mv、install、ln。
%check段:
- rpm软件包的测试。
%check段(example):
%check
%{__python2} manage.py test horizon --settings=horizon.test.settings
%post段:
- rpm软件包安装之后执行的脚本。
%post段(example):
%post -n openstack-dashboard
# ugly hack to set a unique SECRET_KEY
sed -i "/^from horizon.utils import secret_key$/d" /etc/openstack-dashboard/local_settings
sed -i "/^SECRET_KEY.*$/{N;s/^.*$/SECRET_KEY='`openssl rand -hex 10`'/}" /etc/openstack-dashboard/local_settings
# reload systemd unit files
systemctl daemon-reload >/dev/null 2>&1 || :
%postun段:
- rpm软件包卸载之后执行的脚本。
%postun段(example):
%postun
# update systemd unit files
%{systemd_postun}
课外知识: rpm还提供了一种信号机制:不同的操作会返回不同的信息,并放到默认变量$1中。
引用
0代表卸载,1代表安装,2代表升级
%doc段:
- 表示这是文档文件,因此如果安装时使用
--excludedocs
将不会安装此类文件。
%doc段(example):
%doc README.rst openstack-dashboard-httpd-logging.conf
%dir段:
- 表示将dir目录路径下的内容打进rpm软件包里。
%dir段(example):
%dir %{_datadir}/openstack-dashboard/
%config(noreplace):
- 该配置文件不会覆盖已经存在文件(RPM包中文件会以
.rpmnew
存在于系统,卸载时系统中的该配置文件会以.rpmsave
保存下来,如果没有这个选项,安装时RPM包中文件会以.rpmorig
存在于系统)覆盖已经存在文件(没被修改),创建新的文件加上扩展后缀.rpmnew
(被修改)。
%config(noreplace):
%config(noreplace) %{_sysconfdir}/httpd/conf.d/openstack-dashboard.conf
...
%attr(mode, user, group) filename
- 控制文件权限
%attr(mode, user, group) filename(example):
%dir %attr(0750, root, apache) %{_sysconfdir}/openstack-dashboard
%dir %attr(0750, root, apache) %{_sysconfdir}/openstack-dashboard/cinder_policy.d/
%dir %attr(0750, apache, apache) %{_sharedstatedir}/openstack-dashboard
%dir %attr(0750, apache, apache) %{_var}/log/horizon
%changelog段:
- 本段是修改日志段。你可以将软件的每次修改记录到这里,保存到发布的软件包中,以便查询之用。每一个修改日志都有这样一种格式:第一行是:* 星期 月 日 年 修改人 电子信箱。其中:星期、月份均用英文形式的前3个字母,用中文会报错。接下来的行写的是修改了什么地方,可写多行。一般以减号开始,便于后续的查阅。
%changelog段(example):
%changelog
宏定义字段
%{_sysconfdir} /etc
%{_prefix} /usr
...
- 所有的宏定义都可以在
/usr/lib/rpm/macros
中进行查看。
JackDan Thinking