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