原文位址:一堂課玩轉rpm包的制作 作者:wjlkoorey258
常見的Linux發行版主要可以分為兩類,類ReadHat系列和類Debian系列,這裡我們是以其軟體包的格式來劃分的,這兩類系統分别提供了自己的軟體包管理系統和相應的工具。類RedHat系統中軟體包的字尾是rpm;類Debian系統中軟體包的字尾是deb。另一方面,類RedHat系統提供了同名的rpm指令來安裝、解除安裝、更新rpm軟體包;類Debian系統同樣提供了dpkg指令來對字尾是deb的軟體包進行安裝、解除安裝和更新等操作。
rpm的全稱是Redhat Package Manager,常見的使用rpm軟體包的系統主要有Fedora、CentOS、openSUSE、SUSE企業版、PCLinuxOS以及Mandriva Linux、Mageia等。使用deb軟體包字尾的類Debian系統最常見的有Debian、Ubuntu、Finnix等。
無論是rpm指令還是dpkg指令在安裝軟體包時都存在一個讓人非常頭疼的問題,那就是軟體包的依賴關系。這一點很多人應該深有體會,這也使初學者在接觸Linux系統時覺得很不友善的地方。慶幸的是,很多發行版都考慮到了這問題,于是Fedora和CentOS提供了yum來自動解決軟體包的安裝依賴,同樣的openSUSE提供了zypper,類Debian系統提供了apt-*指令。也就是說這些工具本質上最終還是調用了rpm(或者dpkg)是不過安裝前自動幫使用者解決了軟體包的安裝依賴。如下表所示:
分類 | 發行版 | 手動安裝指令 | 自動安裝指令 | 軟體包字尾 |
類RedHat | Fedora/CentOS | rpm | yum | *.rpm |
openSUSE/SUSE | zypper | |||
Mandriva Linux/Mageia | urpmi | |||
類Debian | Debian/Ubuntu | dpkg | apt-get | *.deb |
小技巧:執行rpmdev-setuptree會在目前使用者家目錄下的rpmbuild目錄(如果該目錄不存在也會被自動建立)裡自動建立上述目錄。
當上述目錄建立好之後,将所有用于生成rpm包的源代碼、shell腳本、配置檔案都拷貝到SOURCES目錄裡,注意通常情況下源碼的壓縮格式都為*.tar.gz格式(當然還可以為其他格式,但那就是另外一種方式,這裡先不介紹)。然後,将最最最重要的SPEC檔案,命名格式一般是“軟體名-版本.spec”的形式,将其拷貝到SPECS目錄下,切換到該目錄下執行:
- rpmbuild -bb 軟體名-版本.spec
最終我們想要的rpm軟體包就安安穩穩地躺在RPMS目錄下了。對,就這麼簡單。
這裡的關鍵就是上面的SPEC檔案的寫法,我們可以用rpmdev-newspec -o Name-version.spec指令來生成SPEC檔案的模闆,然後在上面修改就可。例如:
- [root@localhost ~]# rpmdev-newspec -o myapp-0.1.0.spec
- Skeleton specfile (minimal) has been created to "myapp-0.1.0.spec".
- [root@localhost ~]# cat myapp-0.1.0.spec
- Name: myapp-0.1.0
- Version:
- Release: 1%{?dist}
- Summary:
- Group:
- License:
- URL:
- Source0:
- BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
- BuildRequires:
- Requires:
- %description
- %prep
- %setup -q
- %build
- %configure
- make %{?_smp_mflags}
- %install
- rm -rf $RPM_BUILD_ROOT
- make install DESTDIR=$RPM_BUILD_ROOT
- %clean
- %files
- %defattr(-,root,root,-)
- %doc
- %changelog
其實SPEC檔案的核心是它定義了一些“階段”(%prep、%build、%install和%clean),當rpmbuild執行時它首先會去解析SPEC檔案,然後依次執行每個“階段”裡的指令。
接下來,我們來簡單了解一下SPEC檔案的頭部。假如,我們的源碼包名字是myapp-0.1.0.tar.gz,那麼myapp-0.1.0.spec的頭部一般如下的樣子:
- Name: myapp <===軟體包的名字(後面會用到)
- Version: 0.1.0 <===軟體包的版本(後面會用到)
- Release: 1%{?dist} <===釋出序号
- Summary: my first rpm <===軟體包的摘要資訊
- Group: <===軟體包的安裝分類,參見/usr/share/doc/rpm-4.x.x/GROUPS這個檔案
- License: GPL <===軟體的授權方式
- URL: <===這裡本來寫源碼包的下載下傳路徑或者自己的部落格位址或者公司網址之類
- Source0: %{name}-%{version}.tar.gz <===源代碼包的名稱(預設時rpmbuid回到SOURCES目錄中去找),這裡的name和version就是前兩行定義的值。如果有其他配 置或腳本則依次用Source1、Source2等等往後增加即可。
- BuildRoot: %{_topdir}/BUILDROOT <=== 這是make install時使用的“虛拟”根目錄,最終制作rpm安裝包的檔案就來自這裡。
- BuildRequires: <=== 在本機編譯rpm包時需要的輔助工具,以逗号分隔。假如,要求編譯myapp時,gcc的版本至少為4.4.2,則可以寫成gcc >=4.2.2。還有其他依賴的話則以逗号分别繼續寫道後面。
- Requires: <=== 編譯好的rpm軟體在其他機器上安裝時,需要依賴的其他軟體包,也以逗号分隔,有版本需求的可以
- %description <=== 軟體包的詳細說明資訊,但最多隻能有80個英文字元。
下面我們來看一下制作rpm包的幾個關鍵階段,以及所發生的事情:
階段 | 動作 |
将%_sourcedir目錄下的源代碼解壓到%_builddir目錄下。如果有更新檔的需要在這個階段進行打更新檔的操作 | |
在%_builddir目錄下執行源碼包的編譯。一般是執行./configure和make指令 | |
将需要打包到rpm軟體包裡的檔案從%_builddir下拷貝%_buildrootdir目錄下。當使用者最終用rpm -ivh name-version.rpm安裝軟體包時,這些檔案會安裝到使用者系統中相應的目錄裡 | |
制作rpm包 | 這個階段是自動完成的,是以在SPEC檔案裡面是看不到的,這個階段會将%_buildroot目錄的相關檔案制作成rpm軟體包最終放到%_rpmdir目錄裡 |
編譯後的清理工作,這裡可以執行make clean以及清空%_buildroot目錄等 |
每個階段的詳細說明如下:
- %prep階段
這個階段裡通常情況,主要完成對源代碼包的解壓和打更新檔(如果有的話),而解壓時最常見到的就是一句指令:
- %setup -q
當然,這句指令可以成功執行的前提是你位于SOURCES目錄下的源碼包必須是name-version.tar.gz的格式才行,它還會完成後續階段目錄的切換和設定。如果在這個階段你不用這條指令,那麼後面每個階段都要自己手動去改變相應的目錄。解壓完成之後如果有更新檔檔案,也在這裡做。想了解的童鞋可以自己去查查如何實作,也不難,這裡我就不展開了。
- %build階段
這個階段就是執行常見的configure和make操作,如果有些軟體需要最先執行bootstrap之類的,可以放在configure之前來做。這個階段我們最常見隻有兩條指令:
它就自動将軟體安裝時的路徑自動設定成如下約定:
- 可執行程式/usr/bin
- 依賴的動态庫/usr/lib或者/usr/lib64視作業系統版本而定。
- 二次開發的頭檔案/usr/include
- 文檔及手冊/usr/share/man
注意,這裡的%configure是個宏常量,會自動将prefix設定成/usr。另外,這個宏還可以接受額外的參數,如果某些軟體有某些進階特性需要開啟,可以通過給%configure宏傳參數來開啟。如果不用 %configure這個宏的話,就需要完全手動指定configure時的配置參數了。同樣地,我們也可以給make傳遞額外的參數,例如:
- make %{?_smp_mflags} CFLAGS="" …
- %install階段
這個階段就是執行make install操作。這個階段會在%_buildrootdir目錄裡建好目錄結構,然後将需要打包到rpm軟體包裡的檔案從%_builddir裡拷貝到%_buildrootdir裡對應的目錄裡。這個階段最常見的兩條指令是:
- rm -rf $RPM_BUILD_ROOT
其中$RPM_BUILD_ROOT也可以換成我們前面定義的BuildRoot變量,不過要寫成%{buildroot}才可以,必須全部用小寫,不然要報錯。
如果軟體有配置檔案或者額外的啟動腳本之類,就要手動用copy指令或者install指令你給将它也拷貝到%{buildroot}相應的目錄裡。用copy指令時如果目錄不存在要手動建立,不然也會報錯,是以推薦用install指令。
- %clean階段
編譯完成後一些清理工作,主要包括對%{buildroot}目錄的清空(當然這不是必須的),通常執行諸如make clean之類的指令。
- 制作rpm軟體包的階段
這個階段必須引出下面一個叫做%files的階段。它主要用來說明會将%{buildroot}目錄下的哪些檔案和目錄最終打包到rpm包裡。
在%files階段的第一條指令的文法是:
- %defattr(檔案權限,使用者名,組名,目錄權限)
如果不牽扯到檔案、目錄權限的改變則一般用%defattr(-,root,root,-)這條指令來為其設定預設權限。所有需要打包到rpm包的檔案和目錄都在這個地方列出,例如:
- %{_bindir}/*
- %{_libdir}/*
- %config(noreplace) %{_sysconfdir}/*.conf
在安裝rpm時,會将可執行的二進制檔案放在/usr/bin目錄下,動态庫放在/usr/lib或者/usr/lib64目錄下,配置檔案放在/etc目錄下,并且多次安裝時新的配置檔案不會覆寫以前已經存在的同名配置檔案。
這裡在寫要打包的檔案清單時,既可以以宏常量開頭,也可以為“/”開頭,沒任何本質的差別, 都表示從%{buildroot}中拷貝檔案到最終的rpm包裡;如果是相對路徑,則表示要拷貝的檔案位于%{_builddir}目錄,這主要适用于那 些在%install階段沒有被拷貝到%{buildroot}目錄裡的檔案,最常見的就是諸如README、LICENSE之類的檔案。如果不想将% {buildroot}裡的某些檔案或目錄打包到rpm裡,則用:
- %exclude dic_name或者file_name
但是關于%files階段有兩個特性要牢記:
- %{buildroot}裡的所有檔案都要明确被指定是否要被打包到rpm裡。什麼意思呢?假如,%{buildroot}目錄下有4個目錄a、b、c和d,在%files裡僅指定a和b要打包到rpm裡,如果不把c和d用exclude聲明是要報錯的;
- 如果聲明了%{buildroot}裡不存在的檔案或者目錄也會報錯。
關于%doc宏,所有跟在這個宏後面的檔案都來自%{_builddir}目錄,當使用者安裝rpm時,由這個宏所指定的檔案都會安裝到/usr/share/doc/name-version/目錄裡。
- %changelog階段
這是最後一個階段,主要記錄的每次打包時的修改變更日志。标準格式是:
- * date +"%a %b %d %Y" 修改人 郵箱 本次版本x.y.z-p
- - 本次變更修改了那些内容
說了這麼多,我們實戰一下。網上很多教程都是拿Tomcat或者Nigix開頭,這裡我就先從簡單的mp3解碼庫libmad入手,将它打成一個rpm包,具體步驟如下:
(如果自己系統上沒有rpmbuild指令就安裝之:yum install rpm* rpm-build rpmdev*)
1、建構rpm的編譯目錄結構:
2、下載下傳libmad源碼到rpmbuild/SOURCES目錄下,可以從 http://downloads.sourceforge.net/mad/libmad-0.15.1b.tar.gz這裡下載下傳。
3、在rpmbuild/SPECS目錄下執行rpmdev-newspec -o libmad-0.15.1b.spec,會在目前目錄下生成名為libmad-0.15.1b.spec的模闆檔案。
4、将libmad-0.15.1b.spec修改成如下的樣子:
5、在rpmbuild/SPECS目錄下執行打包編譯:
- rpmbuild -bb libmad-0.15.1b.spec
最終生成的rpm包如下:
因為我是64位系統,是以編譯出的libmad适用于CentOS6.0-64。
如果我們将libmad的源碼和spec檔案拷貝32位系統上,再執行rpm打包,看看結果:
結果如下:
後記:
關于SPEC檔案,還有一些其他特性,諸如安裝軟體包之前、之後要做的事情,以及解除安裝軟體包之前之後要做的事情,包括給源碼打更新檔,關于這些特性感興趣的童鞋自己去摸索吧。最後給出一個完整的,包含了打更新檔、安裝、解除安裝特性的SPEC檔案模闆:
- Name: test
- …
- #==================================SPEC頭部====================================
- %patch <==== 在這裡打包
- 要打包到rpm包裡的檔案清單
- #==================================SPEC主體====================================
- %pre
- 安裝或者更新軟體前要做的事情,比如停止服務、備份相關檔案等都在這裡做。
- %post
- 安裝或者更新完成後要做的事情,比如執行ldconfig重構動态庫緩存、啟動服務等。
- %preun