背景: 常見的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)是不過安裝前自動幫用戶解決了軟件包的安裝依賴。如下表所示: 簡單點了說,如果你會在Fedora或者CentOS上用yum來自動安裝軟件包,那麼在Debian或者Ubuntu上你就會用apt-get自動安裝軟件,同理,在openSUSE上你就會用zypper自動安裝軟件包。
前期准備: 1.現在所有的打包都是基於源碼的基礎做打包,所有的源碼包都保存在192.168.0.101:/root目錄下。 2.使用yum安裝rpm有關的包 yum install rpm* rpm-build rpmdev* 3.查看rpm的版本 rpm --version 推薦使用4.5以上的版本(我們用的是4.11.3)
START(通過軟件包的源代碼構建rpm軟件安裝包) 1.從101上拿到**.src.rpm源碼並安裝
rpm -ivh ****.src.rpm
2.安裝源碼包後會在/root目錄下生成rpmbuild目錄
其中rpmbuild結構及各個文件夾的作用 目錄名 說明 macros中的宏名 BUILD 編譯rpm包的臨時目錄 %_builddir BUILDROOT 編譯後生成的軟件臨時安裝目錄 %_buildrootdir RPMS 最終生成的可安裝rpm包的所在目錄 %_rpmdir SOURCES 所有源代碼和補丁文件的存放目錄 %_sourcedir SPECS 存放SPEC文件的目錄(重要) %_specdir SRPMS 軟件最終的rpm源碼格式存放路徑 %_srcrpmdir
.
SPECS文件下存放了.spec文件(下面會重點講)
SOURCE文件下存放了剛剛安裝的rpm包源碼(patch、tar.gz文件)
3.我們可以看到其中有很多0001-0028的補丁文件,此時我們需要進入horizon * 文件夾,將一個一個補丁打上去 patch -p1 <../0001-disable-debug-move-web-root.patch patch -p1 <../000*
4. 完成之後我們需要刪除horizon*目錄中的AUTHORS Changelog文件,不然打包時會出現錯誤 之後刪除SOURCE目錄下所有的.patch文件
並且要將源碼文件下對應的文件清理干淨。 兩類文件:
a. py文件編譯文件pyc 命令: find ./ -name *.pyc -exec rm -rf {} \;
b. lock文件,key文件, 命令: find ./ -name *.lock -exec rm -rf {} \; find ./ -name *.key -exec rm -rf {} \;
並對horzion*文件夾進行tar打包 tar -zcvf horizon 2015.1.0.tar.gz /root/rpmbuild/SOURCES/horizon 2015.1.0
* rpm只支持tar.gz格式,並且tar.gz文件必須在SOURCES中才可以進行打包
5.進入剛剛的/root/rpmbuild/SPECS文件夾,執行rpmbuild命令進行rpm打包 rpmbuild -ba *.spec 生成的.rpm文件在/root/rpmbuild/RPMS 中
------------------------------------------------------以上是rpm打包操作流程-------------------------------------------------------------------
重點: 1.除了 rpmbuild/SOURCES文件中的tar.gz外,更重要的還是rpmbuild/RPMS中的.spec文件 #.spec腳本文件告訴RPM如何去構建和打包軟件,命名格式一般是“軟件名-版本.spec”
2.可以使用 rpmdev-newspec -o Name-version.spec 命令來生成spec文件模板
*3.spec文件詳解 a.SPEC文件的核心是它定義了一些“階段”(%prep、%build、%install和%clean),當rpmbuild執行時它首先會去解析SPEC文件,然後依次執行每個“階段”裡的指令。 我們來簡單了解一下SPEC文件的頭部。假如,我們的源碼包名字是ecloud-0.1.0.tar.gz,那麼myapp-0.1.0.spec的頭部一般如下的樣子:
b. spec關鍵字的具體形式及簡略解釋 Name: ecloud <===軟件包的名字(後面會用到) Version: 0.1.0 <===軟件包的版本(後面會用到) Release: 1%{?dist} <===發布序號 Summary: my first rpm <===軟件包的摘要信息 Group: <===軟件包的安裝分類,參見/usr/share/doc/rpm-4.x.x/GROUPS這個文件 License: GPL <===軟件的授權方式 URL: www.yunrongtech.com <===這裡本來寫源碼包的下載路徑或者自己的博客地址或者公司網址之類 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個英文字符。
關鍵字詳解 Name: 軟件包的名稱,後面可使用%{name}的方式引用
Summary: 軟件包的內容概要
Version: 軟件的實際版本號,例如:1.0.1等,後面可使用%{version}引用
Release: 發布序列號,例如:1linuxing等,標明第幾次打包,後面可使用%{release}引用
Group: 軟件分組,建議使用標准分組
License: 軟件授權方式,通常就是GPL
Source: 源代碼包,可以帶多個用Source1、Source2等源,後面也可以用%{source1}、%{source2}引用
BuildRoot: 這個是安裝或編譯時使用的“虛擬目錄”,考慮到多用戶的環境,一般定義為: %{_tmppath}/%{name}-%{version}-%{release}-root 或 %{_tmppath}/%{name}-%{version}-%{release}-buildroot-%(%{__id_u} -n} 該參數非常重要,因為在生成rpm的過程中,執行make install時就會把軟件安裝到上述的路徑中,在打包的時候,同樣依賴“虛擬目錄”為“根目錄”進行操作。後面可使用$RPM_BUILD_ROOT 方式引用。
URL: 軟件的主頁
Vendor: 發行商或打包組織的信息,例如RedFlag Co,Ltd
Disstribution: 發行版標識
Patch: 補丁源碼,可使用Patch1、Patch2等標識多個補丁,使用%patch0或%{patch0}引用
Prefix: %{_prefix} 這個主要是為了解決今後安裝rpm包時,並不一定把軟件安裝到rpm中打包的目錄的情況。這樣,必須在這裡定義該標識,並在編寫%install腳本的時候引用,才能實現rpm安裝時重新指定位置的功能
Prefix: %{_sysconfdir} 這個原因和上面的一樣,但由於%{_prefix}指/usr,而對於其他的文件,例如/etc下的配置文件,則需要用%{_sysconfdir}標識
Build Arch: 指編譯的目標處理器架構,noarch標識不指定,但通常都是以/usr/lib/rpm/marcros中的內容為默認值
Requires: 該rpm包所依賴的軟件包名稱,可以用>=或<=表示大於或小於某一特定版本,例如: libpng-devel >= 1.0.20 zlibpre)、Requires(post)、Requires(preun)、Requires(postun)、BuildRequires等都是針對不同階段的依賴指定
Provides: 指明本軟件一些特定的功能,以便其他rpm識別
Packager: 打包者的信息
%description 軟件的詳細說明
c.制作rpm包的幾個關鍵階段,以及所發生的事情: 階段 動作 %prep 將%_sourcedir目錄下的源代碼解壓到%_builddir目錄下。如果有補丁的需要在這個階段進行打補丁的操作
%build 在%_builddir目錄下執行源碼包的編譯。一般是執行./configure和make指令
%install 將需要打包到rpm軟件包裡的文件從%_builddir下拷貝%_buildrootdir目錄下。當用戶最終用 rpm -ivh name-version.rpm安裝軟件包時,這些文件會安裝到用戶系統中相應的目錄裡
制作rpm包 這個階段是自動完成的,所以在SPEC文件裡面是看不到的,這個階段會將%_buildroot目錄的相關文件制作成rpm軟件包最終放到%_rpmdir目錄裡
%clean 編譯後的清理工作,這裡可以執行make clean以及清空%_buildroot目錄等
d.spec腳本主體詳解 %prep階段 這個階段裡通常情況,主要完成對源代碼包的解壓和打補丁(如果有的話),而解壓時最常見到的就是一句指令:
%setup -q
當然,這句指令可以成功執行的前提是你位於SOURCES目錄下的源碼包必須是name-version.tar.gz的格式才行,它還會完成後續階段目錄的切換和設置。如果在這個階段你不用這條指令,那麼後面每個階段都要自己手動去改變相應的目錄。解壓完成之後如果有補丁文件,也在這裡做。
%build階段 這個階段就是執行常見的configure和make操作,如果有些軟件需要最先執行bootstrap之類的,可以放在configure之前來做。這個階段我們最常見只有兩條指令:
%configure make %{?_smp_mflags}
#它就自動將軟件安裝時的路徑自動設置成如下約定: 可執行程序/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 make install DESTDIR=$RPM_BUILD_ROOT
其中$RPM_BUILD_ROOT也可以換成我們前面定義的BuildRoot變量,不過要寫成%{buildroot}才可以,必須全部用小寫,不然要報錯。 如果軟件有配置文件或者額外的啟動腳本之類,就要手動用install命令你給將它也拷貝到%{buildroot}相應的目錄裡。
%clean階段 編譯完成後一些清理工作,主要包括對%{buildroot}目錄的清空,通常執行諸如make clean之類的命令。
制作rpm軟件包的階段 這個階段必須引出下面一個叫做%files的階段。它主要用來說明會將%{buildroot}目錄下的哪些文件和目錄最終打包到rpm包裡。
%files %defattr(-,root,root,-) %doc
在%files階段的第一條命令的語法是:
%defattr(文件權限,用戶名,組名,目錄權限)
如果不牽扯到文件、目錄權限的改變則一般用%defattr(-,root,root,-)這條指令來為其設置缺省權限。所有需要打包到rpm包的文件和目錄都在這個地方列出,例如:
%files %{_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 - 本次變更修改了那些內容
e.spec文件總覽 可以用rpmdev-newspec -o Name-version.spec命令來生成SPEC文件的模板,然後在上面修改就可
Name: ecloud Version: Requires: %description … #==================================SPEC頭部==================================== %prep %setup -q %patch <==== 在這裡打包 %build %configure make %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) 要打包到rpm包裡的文件清單 %doc %changelog #==================================SPEC主體==================================== %pre 安裝或者升級軟件前要做的事情,比如停止服務、備份相關文件等都在這裡做。 %post 安裝或者升級完成後要做的事情,比如執行ldconfig重構動態庫緩存、啟動服務等。 %preun 卸載軟件前要做的事情,比如停止相關服務、關閉進程等。 %postun 卸載軟件之後要做的事情,比如刪除備份、配置文件等。
f.example 以進行打包的虛擬機192.168.0.124:/root/rpm/ecloud/rpmbuild/SPECS/中的ecloud.spec文件
################################################################### Summary: System init. (Enterprise quality)
Name: ecloud #軟件包的名稱
Version: 1.0 #軟件的實際版本號
Release: 1.139 #發布序列號,後面的139是與系統包區分開
License: GPL #軟件授權方式,通常就是GPL
Group: Applications/System
Source: ecloud-%{version}.tar.gz #源代碼包
BuildRoot: %{_tmppath}/%{name}-%{version}-root #這個是安裝或編譯時使用的“虛擬目錄”
BuildRequires: libevent,libevent-devel #該rpm包所依賴的軟件包名稱
Url: http://www.yunrongtech.com
%description Ecloud system init
Memcached is an in-memory key-value store for smallchunks of arbitrary data (strings, objects) from results of database calls, APIcalls, or page rendering.
# -i, pre >> post, $1 = 1 # -U, pre >> post >> preun >> postun, pre and post are performed against new package, now $1 = 2,preun and postun are perform against old package, now $1 = 1 # -e, preun >> postun, $1 = 0 #為之後的更新刪除添加做准備
%prep #預處理腳本
%setup –q #提取源碼到BUILD 目錄; -q 指不顯示輸出(quietly) %install
rm -rf ${RPM_BUILD_ROOT} mkdir -p ${RPM_BUILD_ROOT}/usr/bin install -m 755 ecloud_i.sh ${RPM_BUILD_ROOT}%{_bindir} install -m 755 ecloud_e.sh ${RPM_BUILD_ROOT}%{_bindir} #將軟件安裝到虛擬目錄中
%pre # recover before Update if [ $1 == 2 ]; then ecloud_e.sh >/dev/null fi
%post #directly install ecloud_i.sh >/dev/null %preun #directoy uninstall if [ $1 == 0 ]; then ecloud_e.sh >/dev/null fi %clean rm -rf ${RPM_BUILD_ROOT} #清楚buildroot目錄下文件 %files #定義哪些些文件或目錄會放入rpm中 %defattr(-,root,root) #指定包裝文件的屬性 %attr(755,root,root) %{_bindir}/ecloud_i.sh #文件權限,用戶名,組名,目錄權限 %attr(755,root,root) %{_bindir}/ecloud_e.sh #ecloud_i.sh ecloud_e.sh為編寫好的shell輔助腳本 ############################################################################
總結
A.系統打包的動作過程
1)讀取並解析 filename.spec 文件 2)運行 %prep 部分來將源代碼解包到一個臨時目錄,並應用所有的補丁程序。 3)運行 %build 部分來編譯代碼。 4)運行 %install 部分將代碼安裝到構建機器的目錄中。 5)讀取 %files 部分的文件列表,收集文件並創建二進制和源 RPM 文件。 6)運行 %clean 部分來除去臨時構建目錄。
B.打rpm包的基本思路
1) 取回軟件的源代碼和相關的補丁 2) 測試所需的補丁,以便於順利的構建整個程序 3) 把源碼、配置文件等文件放到正確的位置 (SOUCES文件夾) 4) 編輯spec文件 5) 使用rpmbuild構建rpm包 (rpmbuild -ba *.spec) #補充 rpmbuild命令:rpmbuild命令會根據spec文件來生成rpm包,主要用到以下幾個參數:
rpmbuild --help -bp 執行到%prep階段結束 -bc 執行到%build階段結束 -bi 執行到%install階段結束 -bl 檢測%files文件是否有丟失 -ba 創建src.rpm和二進制包 -bs 只創建src.rpm源碼包 -bb 只創建二進制rpm包