RPM包的制作
前言
按照其軟件包的格式來划分,常見的Linux發行版主要可以分為兩類,類ReadHat系列和類Debian系列,這兩類系統分別提供了自己的軟件包管理系統和相應的工具。
類RedHat系統中軟件包的后綴是rpm,提供了同名的rpm命令來安裝、卸載、升級rpm軟件包; 類Debian系統中軟件包的后綴是deb,同樣提供了dpkg命令來對后綴是deb
rpm的全稱是Redhat Package Manager,常見的使用rpm軟件包的系統主要有Fedora、CentOS、openSUSE、SUSE企業版、PCLinuxOS以及Mandriva Linux、Mageia等。 使用deb軟件包后綴的類Debian系統最常見的有Debian、Ubuntu、Finnix等。
無論是rpm命令還是dpkg命令在安裝軟件包時都存在軟件包依賴關系問題。
Fedora/CentOS 系統 提供了 yum 來自動解決軟件包的安裝依賴; openSUSE/SUSE 系統 提供了 zypper 來自動解決軟件包的安裝依賴; Mandriva Linux/Mageia 系統 提供了 urpmi 來自動解決軟件包的安裝依賴; Debian/Ubuntu 系統 提供了 apt-* 命令。
這些工具本質上最終還是調用了 rpm (或者dpkg)來進行安裝,但是在安裝前會自動幫用戶解決了軟件包的安裝依賴。
從軟件運行的結構來說,一個軟件主要可以分為三個部分:可執行程序、配置文件和動態庫。 當然還有可能會有相關文檔、手冊、供二次開發用的頭文件以及一些示例程序等等。其他部分都是可選的,只有可執行文件是必須的。
制作rpm軟件包,常用的方法是使用 rpmbuild 這個命令行工具。
安裝rpmbuild工具
可以執行yum命令安裝工具包(能夠聯網的情況):
yum install rpm* rpm-build rpmdev*
或者使用rpm包直接接安裝(有rpm-build安裝包的情況):
rpm -ivh rpm-build-4.8.0-37.el6.x86_64.rpm
如果還需要安裝rpmdevtools工具,稍微麻煩一些,需要查看依賴自己來安裝。
rpmbuid打包目錄
rpm打包目錄有一些嚴格的層次上的要求。
rpm的版本<=4.4.x,rpmbuid工具其默認的工作路徑是 /usr/src/redhat因為權限的問題,普通用戶不能制作rpm包,制作rpm軟件包時必須切換到 root 身份才可以。
rpm從4.5.x版本開始,將rpmbuid的默認工作路徑移動到用戶家目錄下的rpmbuild目錄里,即 $HOME/rpmbuild ,並且推薦用戶在制作rpm軟件包時盡量不要以root身份進行操作。
rpmbuild默認工作路徑的確定,通常由在 /usr/lib/rpm/macros 這個文件里的一個叫做 %_topdir 的宏變量來定義。如果用戶想更改這個目錄名,rpm官方並不推薦直接更改這個目錄,而是在用戶家目錄下建立一個名為 .rpmmacros 的隱藏文件(Linux下隱藏文件,前面的點不能少),然后在里面重新定義 %_topdir,指向一個新的目錄名。這樣就可以滿足某些用戶的差異化需求了。通常情況下.rpmmacros文件里一般只有一行內容,比如:
%_topdir $HOME/myrpmbuildenv
在%_topdir目錄下一般需要建立6個目錄
目錄名 | 說明 | macros中的宏名 |
---|---|---|
BUILD | 編譯rpm包的臨時目錄 | %_builddir |
BUILDROOT | 編譯后生成的軟件臨時安裝目錄 | %_buildrootdir |
RPMS | 最終生成的可安裝rpm包的所在目錄 | %_rpmdir |
SOURCES | 所有源代碼和補丁文件的存放目錄 | %_sourcedir |
SPECS | 存放SPEC文件的目錄 | %_specdir |
SRPMS | 軟件最終的rpm源碼格式存放路徑 | %_srcrpmdir |
如果有安裝rpmdevtools,可以使用 rpmdev-setuptree命令在當前用戶home/rpmbuild目錄里自動建立上述目錄。
SPEC文件詳解
注:
liunx環境可以使用vi來編輯spec文件,windows環境可以可以使用sublime編輯spec文件。 需要注意的是,絕對不能使用記事本來編輯,這是因為windows的默認換行符是
\r\n
,而 liunx 的換行符是\n
。如果spec文件中包含多余的\r
會導致rpm包創建失敗。
文件使用liunx換行符風格,也就是換行是 \n
,所以建議使用 VI 來編輯spec文件文件,因為如果
當打包目錄建立好之后,將所有用於生成rpm包的源代碼、shell腳本、配置文件都拷貝到SOURCES目錄里,注意通常情況下源碼的壓縮格式都為*.tar.gz格式。然后,將最最最重要的SPEC文件,命名格式一般是“軟件名-版本.spec”的形式,將其拷貝到SPECS目錄下,切換到該目錄下執行:
rpmbuild -bb 軟件名-版本.spec
如果系統有rpmdevtools工具,可以用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
如果沒有安裝rpmdevtools,也可以自己手動創建一個spec文件
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
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root,-)
%doc
%changelog
SPEC文件的核心是它定義了一些“階段”(%prep、%build、%install和%clean),當rpmbuild執行時它首先會去解析SPEC文件,然后依次執行每個“階段”里的指令。
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包每個階段說明:
制作rpm包有 %prep,%build,%install,%files,%clean 幾個關鍵階段。
%prep 階段
這個階段里通常情況,主要完成對源代碼包的解壓和打補丁,而解壓時最常見到的就是一句指令:
%setup -q
將 %sourcedir 目錄下的源代碼解壓到 %builddir 目錄下。
%build 階段
這個階段會在 %_builddir 目錄下執行源碼包的編譯。一般是執行執行常見的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里對應的目錄里。
當用戶最終用 rpm -ivh name-version.rpm
安裝軟件包時,這些文件會安裝到用戶系統中相應的目錄里。
這個階段最常見的兩條指令是:
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
其中$RPMBUILDROOT也可以換成我們前面定義的BuildRoot變量,不過要寫成%{buildroot}才可以,必須全部用小寫,不然要報錯。
如果軟件有配置文件或者額外的啟動腳本之類,就要手動用copy命令或者install命令你給將它也拷貝到%{buildroot}相應的目錄里。用copy命令時如果目錄不存在要手動建立,不然也會報錯,所以推薦用install命令。
%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/目錄里。
軟件包制作階段
這個階段是自動完成的,所以在SPEC文件里面是看不到的,這個階段會將 %buildroot 目錄的相關文件制作成rpm軟件包最終放到 %rpmdir 目錄里。
%clean階段
編譯完成后一些清理工作,主要包括對%{buildroot}目錄的清空(這不是必須的),通常執行諸如make clean之類的命令。
%changelog 階段
這是最后一個階段,主要記錄的每次打包時的修改變更日志。標准格式是:
* date +"%a %b %d %Y" 修改人 郵箱 本次版本x.y.z-p
- 本次變更修改了那些內容
rpm打包流程總結
- 安裝 rpmbuild
- 構建rpm的編譯目錄結構: rpmbuild/ ├ BUILD ├ RPMS ├ SOURCES ├ SPECS └ SRPMS
- 將源碼放到到 rpmbuild/SOURCES
- 在rpmbuild/SPECS目錄創建spec文件
- 在rpmbuild/SPECS目錄下執行打包編譯:
rpmbuild -bb xxxxx.spec
- 最終生成的rpm包
SPEC文件模板
以下給出一個完整的,包含了打補丁、安裝、卸載特性的SPEC文件模板
Name: test
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
卸載軟件之后要做的事情,比如刪除備份、配置文件等。
附錄:
rpmbuild相關命令
基本格式:rpmbuild [options] [spec文檔|tarball包|源碼包]
-
從spec文檔建立有以下選項: -bp #只執行spec的%pre 段(解開源碼包並打補丁,即只做准備) -bc #執行spec的%pre和%build 段(准備並編譯) -bi #執行spec中%pre,%build與%install(准備,編譯並安裝) -bl #檢查spec中的%file段(查看文件是否齊全) -ba #建立源碼與二進制包(常用) -bb #只建立二進制包(常用) -bs #只建立源碼包
-
從tarball包建立,與spec類似 -tp #對應-bp -tc #對應-bc -ti #對應-bi -ta #對應-ba -tb #對應-bb -ts #對應-bs
-
從源碼包建立 --rebuild #建立二進制包,通-bb --recompile #同-bi
-
其他的一些選項 --buildroot=DIRECTORY #確定以root目錄建立包 --clean #完成打包后清除BUILD下的文件目錄 --nobuild #不進行%build的階段 --nodeps #不檢查建立包時的關聯文件 --nodirtokens --rmsource #完成打包后清除SOURCES --rmspec #完成打包后清除SPEC --short-cricuit --target=CPU-VENDOR-OS #確定包的最終使用平台
spec文檔的編寫
Name: 軟件包的名稱,后面可使用%{name}的方式引用,具體命令需跟源包一致 Summary: 軟件包的內容概要 Version: 軟件的實際版本號,具體命令需跟源包一致 Release: 發布序列號,具體命令需跟源包一致
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硬件支持)
License: 軟件授權方式,通常就是GPL Source: 源代碼包,可以帶多個用Source1、Source2等源,后面也可以用%{source1}、%{source2}引用 BuildRoot: 這個是安裝或編譯時使用的“虛擬目錄”,考慮到多用戶的環境,一般定義為:%{_tmppath}/%{name}-%{version}-%{release}-root1
或%{_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 zlib ※“>=”號兩邊需用空格隔開,而不同軟件名稱也用空格分開 還有例如PreReq、Requires(pre)、Requires(post)、Requires(preun)、Requires(postun)、BuildRequires等都是針對不同階段的依賴指定 Provides: 指明本軟件一些特定的功能,以便其他rpm識別 Packager: 打包者的信息 scription 軟件的詳細說明
spec腳本主體
spec腳本的主體中也包括了很多關鍵字和描述,下面會一一列舉。我會把一些特別需要留意的地方標注出來。 %prep 預處理腳本 %setup -n %{name}-%{version} 把源碼包解壓並放好 注:可根據你的源碼的名字格式,來確認解壓后名字的格式,否則可能導致install的時候找不到對應的目錄 通常是從/usr/src/redhat/SOURCES里的包解壓到/usr/src/redhat/BUILD/%{name}-%{version}中。 一般用%setup -c就可以了,但有兩種情況:一就是同時編譯多個源碼包,二就是源碼的tar包的名稱與解壓出來的目錄不一致,此時,就需要使用-n參數指定一下了。 %patch 打補丁 通常補丁都會一起在源碼tar.gz包中,或放到SOURCES目錄下。一般參數為: %patch -p1 使用前面定義的Patch補丁進行,-p1是忽略patch的第一層目錄 %Patch2 -p1 -b xxx.patch 打上指定的補丁,-b是指生成備份文件
◎補充 %setup 不加任何選項,僅將軟件包打開。 %setup -n newdir 將軟件包解壓在newdir目錄。 %setup -c 解壓縮之前先產生目錄。 %setup -b num 將第num個source文件解壓縮。 %setup -T 不使用default的解壓縮操作。 %setup -T -b 0 將第0個源代碼文件解壓縮。 %setup -c -n newdir 指定目錄名稱newdir,並在此目錄產生rpm套件。 %patch 最簡單的補丁方式,自動指定patch level。 %patch 0 使用第0個補丁文件,相當於%patch ?p 0。 %patch -s 不顯示打補丁時的信息。 %patch -T 將所有打補丁時產生的輸出文件刪除。
%build 開始構建包 在/usr/src/redhat/BUILD/%{name}-%{version}目錄中進行make的工作 ,常見寫法: make %{?smpmflags} OPTIMIZE="%{optflags}" 都是一些優化參數,定義在/usr/lib/rpm/marcros中
%install 開始把軟件安裝到虛擬的根目錄中 在/usr/src/redhat/BUILD/%{name}-%{version}目錄中進行make install的操作。這個很重要,因為如果這里的路徑不對的話,則下面%file中尋找文件的時候就會失敗。 常見內容有: %makeinstall 這不是關鍵字,而是rpm定義的標准宏命令。也可以使用非標准寫法: make DESTDIR=$RPMBUILDROOT install 或 make prefix=$RPMBUILDROOT install
需要說明的是,這里的%install主要就是為了后面的%file服務的。所以,還可以使用常規的系統命令:
install -d $RPMBUILDROOT/ #建立目錄 cp -a * $RPMBUILDROOT/
%clean 清理臨時文件 通常內容為: 引用 [ "$RPMBUILDROOT" != "/" ] && rm -rf "$RPMBUILDROOT" rm -rf $RPMBUILDDIR/%{name}-%{version}
※注意區分$RPM_BUILD_ROOT和$RPM_BUILD_DIR: $RPM_BUILD_ROOT是指開頭定義的BuildRoot,而$RPM_BUILD_DIR通常就是指/usr/src/redhat/BUILD,其中,前面的才是%file需要的。
%pre rpm安裝前執行的腳本 %post rpm安裝后執行的腳本 %preun rpm卸載前執行的腳本 %postun rpm卸載后執行的腳本 %preun %postun 的區別是:前者在升級的時候會執行,后者在升級rpm包的時候不會執行
%files 定義那些文件或目錄會放入rpm中 這里會在虛擬根目錄下進行,千萬不要寫絕對路徑,而應用宏或變量表示相對路徑。如果描述為目錄,表示目錄中除%exclude外的所有文件。 fattr (-,root,root) 指定包裝文件的屬性,分別是(mode,owner,group),-表示默認值,對文本文件是0644,可執行文件是0755
%exclude 列出不想打包到rpm中的文件 ※小心,如果%exclude指定的文件不存在,也會出錯的。 %changelog 變更日志
spec文檔中常用的幾個宏(變量)
- RPM_BUILD_DIR: /usr/src/redhat/BUILD
- RPM_BUILD_ROOT: /usr/src/redhat/BUILDROOT
- %{_sysconfdir}: /etc
- %{_sbindir}: /usr/sbin
- %{_bindir}: /usr/bin
- %{_datadir}: /usr/share
- %{_mandir}: /usr/share/man
- %{_libdir}: /usr/lib64
- %{_prefix}: /usr
- %{_localstatedir}: /usr/var
rpm常用命令
-
手動安裝 rpm 包
rpm-ivh xxxxx.rpm
-
查看 rpm 包信息
rpm-qpi xxxxx.rpm
-
查看 rpm 包依賴
rpm -qpR xxxxx.rpm
-
查看 rpm 包中包含那些文件
rpm -qlp xxxxx.rpm
可以加grep搜索rpm -qlp xxxxx.rpm|grep spec
-
使用工具rpm2cpio提取文件:
rpm2cpio xxxxx.rpm |cpio -ivd xxx.jpg
-
用rpm2cpio將rpm文件轉換成cpio文件
rpm2cpio xxxxxx.rpm >xxxxx.cpio
-
用cpio解壓cpio文件
cpio -i --make-directories
-
提取所有文件:
rpm2cpio xxx.rpm | cpio -vi
rpm2cpio xxx.rpm | cpio -idmv
rpm2cpio xxx.rpm | cpio --extract --make-directories
-
cpio 參數說明: i 和 extract 表示提取文件 v 表示指示執行進程 d 和 make-directory 表示根據包中文件原來的路徑建立目錄 m 表示保持文件的更新時間
-
查看rpm包里的pre和post install腳本:
rpm -qp --scripts xxxxx.rpm
-
查看安裝的過程中,代碼的執行過程:
rpm -ih -vv xxxxx.rpm