2001 年 11 月 01 日 RPM 是廣泛使用的用于交付 Linux 軟件的工具,用戶可以輕松的安裝用 RPM 打包的產品。在本文中(系列文章的第 1 篇),IBM 軟件工程師 Dan Poirier 向您演示如何在 Red Hat Linux 7.1 系統(tǒng)上用 RPM 打包簡單軟件。 RPM(Red Hat Package Manager)是用于 Linux 分發(fā)版(distribution)的最常見的軟件包管理器。因為它允許分發(fā)已編譯的軟件,所以用戶只用一個命令就可以安裝軟件。 RPM 是 Linux“標準基本庫”版本 1.0.0 指定的安裝工具。在 Linux 分發(fā)版前 10 名中,有 8 個是基于 RPM(請參閱“Comparison of Linux Distributions”,它位于本文后面的 參考資料中列出的 distrowatch.com 上)。即使某些通常不使用 RPM 的分發(fā)版,如 Debian,也有可用工具將 RPM 轉換成它們自己的格式。在 Linux 上,對于除開發(fā)人員以外的任何人,RPM 也是用來打包軟件的最佳選擇。 不論您是自己開發(fā)軟件,還是通過提供代碼以從中創(chuàng)建 RPM 軟件包向開放源碼項目提供幫助,本文都會幫助您入門。順便要說的是,本系列的后續(xù)文章會涉及下列主題:構建 RPM 軟件包而未必是 root 用戶,在構建軟件之前為其打補丁,安裝和卸載時運行腳本以及在安裝或卸載 其它軟件包時運行腳本。 簡單實例 我將從簡單的實例開始,主要使用 RPM 缺省值。然后,我會添加上幾個可選特性。 RPM 軟件包從源文件形式的程序開始,作好了編譯準備。我不想創(chuàng)建一個沒有實際意義的示例,而是選擇使用 GNU Indent 程序(請參閱 參考資料)。 在 Linux 下創(chuàng)建 Indent 非常容易。 indent-2.2.6.tar.gz 文件位于當前目錄之中,所有您要做的就是: 手工構建 indent $ tar xzf indent.2.2.6.tar.gz $ cd indent-2.2.6 $ ./configure $ make $ make install | 如果您已經構建過很多開放源碼項目,那么這可能看起來很熟悉。 unpack;./configure;make;make install 序列是典型的使用 GNU 自動配置(autoconf)工具的軟件。因為這太普通了,所以我在這里描述的關于 indent 的大部分東西,幾乎不加改變就可以用于其它開放源碼項目。 現(xiàn)在假設您正在使用 Red Hat 7.1。在本文的后面,我將給出一些 在其它 Linux 分發(fā)版上使用 RPM 的建議。 在 Red Hat 7.1 上, 在繼續(xù)以前,請確保您已安裝了 rpm-build 軟件包。為檢查是否安裝,請運行 rpm -q rpm-build 。您應該看到一些類似于 rpm-build-4.0.2-8 的信息(版本可能會不同)。如果您看到的是 package rpm-build is not installed ,則將需要從 Red Hat 安裝 CD 上安裝它。 制作基本 RPM 軟件包 為了構建 RPM 軟件包,您需要寫一個名為 spec 文件的 RPM 輸入文件,該文件告訴 RPM 如何構建和打包您的軟件。編寫 spec 文件您需要: - 創(chuàng)建文件 indent-1.spec,如下所示。您可以任意地給它命名并把它放到任何地方;RPM 對這些沒有要求。
- 以 root 用戶登錄。
- 將 indent-2.2.6.tar.gz 文件復制到 /usr/src/redhat/SOURCES。
- 運行
rpm -ba indent-1.spec ,將 indent-1.spec 改為您使用的名字。 第一個 spec 文件:indent-1.spec Summary: GNU indent Name: indent Version: 2.2.6 Release: 1 Source0: %{name}-%{version}.tar.gz License: GPL Group: Development/Tools %description The GNU indent program reformats C code to any of a variety of formatting standards, or you can define your own. %prep %setup -q %build ./configure make %install make install %files %defattr(-,root,root) /usr/local/bin/indent %doc /usr/local/info/indent.info %doc %attr(0444,root,root) /usr/local/man/man1/indent.1 %doc COPYING AUTHORS README NEWS | 您應該看到 RPM 解包這個 tar 文件,編譯并安裝它。 在 Red Hat 7.1 上,工作目錄將是 /usr/src/redhat/BUILD。 最后,RPM 將創(chuàng)建兩個 RPM 文件。將在 /usr/src/redhat/SRPMS/indent-2.2.6-1.src.rpm 中創(chuàng)建一個源 RPM 文件,而在 /usr/src/redhat/RPMS/i386/indent-2.2.6-1.i386.rpm 中創(chuàng)建一個二進制 RPM 文件。 源 RPM 文件簡單地捆綁了 spec 文件和構建軟件包用到的所有源文件和補丁文件。如果您選擇分發(fā)它,則其他人可以很容易地用它重建您的軟件。二進制 RPM 文件僅包含已編譯的軟件和如何安裝的信息。 RPM 做什么 下面總結了在您運行 rpm -ba filename.spec 時,RPM 都做些什么: - 讀取并解析 filename.spec 文件
- 運行
%prep 部分來將源代碼解包到一個臨時目錄,并應用所有的補丁程序。 - 運行
%build 部分來編譯代碼。 - 運行
%install 部分將代碼安裝到構建機器的目錄中。 - 讀取
%files 部分的文件列表,收集文件并創(chuàng)建二進制和源 RPM 文件。 - 運行
%clean 部分來除去臨時構建目錄。 spec 文件的內容 spec 文件有幾個部分。第一部分是未標記的;其它部分以 %prep 和 %build 這樣的行開始。 頭 第一部分(未標記)定義了多種信息,其格式類似電子郵件消息頭。 Summary 是一行關于該軟件包的描述。 Name 是該軟件包的基名, Version 是該軟件的版本號。 Release 是 RPM 本身的版本號 ― 如果修復了 spec 文件中的一個錯誤并發(fā)布了該軟件同一版本的新 RPM,就應該增加發(fā)行版號。 License 應該給出一些許可術語(如:“GPL”、“Commercial”、“Shareware”)。 Group 標識軟件類型;那些試圖幫助人們管理 RPM 的程序通常按照組列出 RPM。您可以在 /usr/share/doc/rpm-4.0.2/GROUPS 文件看到一個 Red Hat 使用的組列表(假設您安裝的 RPM 版本是 4.0.2)。但是您還可以使用那些組名以外的名稱。 Source0 、 Source1 等等給這些源文件命名(通常為 tar.gz 文件)。 %{name} 和 %{version} 是 RPM 宏,它們擴展成為頭中定義的 rpm 名稱和版本。因此,在這個實例中, Source0 被設置為 indent-2.2.6.tar.gz 。 不要在 Source 語句中包含任何路徑。缺省情況下,RPM 會在 /usr/src/redhat/SOURCES 中尋找文件。請將您的源文件復制或鏈接到那里。(要使 spec 文件盡量可移植的話,應當盡量避免嵌入自己開發(fā)機器上的假想路徑。其他開發(fā)人員就可以指示 RPM 在別的目錄下查找源文件,而不用修改您的 spec 文件。) 描述 接下來的部分從 %description 行開始。您應該在這里提供該軟件更多的描述,這樣任何人使用 rpm -qi 查詢您的軟件包時都可以看到它。您可以解釋這個軟件包做什么,描述任何警告或附加的配置指令,等等。 Shell 腳本 下面幾部分是嵌入 spec 文件中的 shell 腳本。 %prep 負責對軟件包解包。在最常見情況下,您只要用 %setup 宏即可,它會做適當?shù)氖虑椋跇嫿夸浵陆獍?tar 文件。加上 -q 項只是為了減少輸出。 %build 應該編譯軟件包。該 shell 腳本從軟件包的子目錄下運行,在我們這個例子里是 indent-2.2.6 目錄,因而這常常與運行 make 一樣簡單。 %install 在構建系統(tǒng)上安裝軟件包。這似乎和 make install 一樣簡單,但通常要復雜些。我將在下面解釋這點。 文件列表
%files 列出應該捆綁到 RPM 中的文件,并能夠可選地設置許可權和其它信息。 在 %files 中,您可以使用 一次 %defattr 來定義缺省的許可權、所有者和組;在這個示例中, %defattr(-,root,root) 會安裝 root 用戶擁有的所有文件,使用當 RPM 從構建系統(tǒng)捆綁它們時它們所具有的任何許可權。 可以用 %attr(permissions,user,group) 覆蓋個別文件的所有者和許可權。 可以在 %files 中用一行包括多個文件。 可以通過在行中添加 %doc 或 %config 來標記文件。 %doc 告訴 RPM 這是一個文檔文件,因此如果用戶安裝軟件包時使用 --excludedocs ,將不安裝該文件。您也可以在 %doc 下不帶路徑列出文件名,RPM 會在構建目錄下查找這些文件并在 RPM 文件中包括它們,并把它們安裝到 /usr/share/doc/%{name}-%{version} 。以 %doc 的形式包括 README 和 ChangeLog 這樣的文件是個好主意。 %config 告訴 RPM 這是一個配置文件。在升級時,RPM 將會試圖避免用 RPM 打包的缺省配置文件覆蓋用戶仔細修改過的配置。 警告:如果在 %files 下列出一個目錄名,RPM 會包括該目錄下的所有文件。通常這不是您想要的,特別對于 /bin 這樣的目錄。
避免簡單實例中的問題 這個最基本的 spec 文件有幾個問題。最大的問題之一就是您最后在構建系統(tǒng)上實際安裝了該產品。而這可能只是一個軟件測試版本,您也許并不想在構建系統(tǒng)中安裝它。 RPM 用一個名為 構建根(build root)的特性來處理這個問題。它的想法是設置您的 spec 文件,以將所有安裝的文件復制到一個虛擬目錄樹(從構建根開始);然后 RPM 從那里得到文件。 但是,這需要一些軟件包的支持。在包括 indent 在內的很多 GNU 軟件包中,在 make install 的時候定義 DESTDIR 將會在所有安裝路徑之前添加 DESTDIR 值。 請注意 不要使用 ./configure --prefix=$RPM_BUILD_ROOT 。這會在假設整個軟件包文件的最終位置是構建根的情況下安裝整個軟件包。這對于 indent 可能沒有關系,但任何需要在運行時找到其安裝文件的程序都將失敗,因為當 RPM 最終安裝到用戶系統(tǒng)后,這些文件就不再位于構建根之下 ― 那只是您構建系統(tǒng)上的一個臨時目錄。 請參閱更新的文件 indent-2.spec,如下所示。 第二個 spec 文件:indent-2.spec Summary: GNU indent Name: indent Version: 2.2.6 Release: 2 Source0: %{name}-%{version}.tar.gz License: GPL Group: Development/Tools BuildRoot: %{_builddir}/%{name}-root %description The GNU indent program reformats C code to any of a variety of formatting standards, or you can define your own. %prep %setup -q %build ./configure make %install rm -rf $RPM_BUILD_ROOT make DESTDIR=$RPM_BUILD_ROOT install %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) /usr/local/bin/indent %doc /usr/local/info/indent.info %doc %attr(0444,root,root) /usr/local/man/man1/indent.1 %doc COPYING AUTHORS README NEWS | 更改說明 首先,我們增加了版本的發(fā)行號。無論何時,當您編輯 spec 文件時,都不要忘了這么做。 我們在頭中添加了 BuildRoot,以便告訴 RPM 這是在構建期間臨時安裝文件的地方。對于臨時文件,我們這里使用了兩個 RPM 宏,而不是假設某個特定位置。在 Red Hat 7.1 上, %{_builddir} 以類似于 /usr/src/redhat/BUILD 結束。 我們還需要告訴系統(tǒng)將 indent 安裝在那里。RPM 幫助我們用構建根的值定義一個 shell 變量 RPM_BUILD_ROOT ,因此在 make install 時,我們只需將它作為 DESTDIR 值傳入即可。 我們還在 %install 和 %clean 中添加了幾行,以便在開始安裝以前(為保險起見)和完成以后清除構建根。%clean 是一切都正常的情況下在 RPM 構建結束時運行的腳本,這樣臨時文件就不會一直保留。 最后,在 %files 中,請注意我們沒有在此處的路徑前包括 BuildRoot。我們使用了“真正”的路徑;RPM 將在構建根下尋找這些文件,因為您已經包括了 BuildRoot 定義。 這一次發(fā)生了什么 如果仔細觀察您會發(fā)現(xiàn),在 RPM 進行安裝部分以前,一切工作照舊。然后,文件將不直接安裝到 /usr/local/bin,而是安裝在(比如說)/usr/src/redhat/BUILD/indent-root/usr/local/bin 中。 如果您檢查最終的二進制 RPM 文件(用 rpm -qlp indent-2.2.6-2.i386.rpm ),您會看到構建根已被 RPM 除去。如果您安裝 RPM,這些文件最終將安裝在正確的目錄,如 /usr/local/bin/indent 中。
在其它 Linux 分發(fā)版上使用 RPM 如果您在使用不同的 Linux 分發(fā)版,RPM 可能會有不同的內置路徑。例如,它幾乎肯定不會在 /usr/src/redhat 查找源文件!要確定希望的 RPM 安裝路徑,請運行 rpm --showrc 并查看下列部分如何被定義: - _sourcedir
- RPM 在哪里查找源文件(tar 文件,等)
- _srcrpmdir
- RPM 在哪里放入新的源 RPM 文件
- _rpmdir
- RPM 將把新的二進制 RPM 文件放在哪里(在特定于體系結構的子目錄中)
其中一些根據(jù)其它變量定義;例如,當您看到 %{_topdir} ,查找 _topdir 的定義,等等。
下一步是什么 我希望這篇用 RPM 打包軟件的介紹會對您有所幫助。有關相關的閱讀材料,請參閱下面的 參考資料。在本系列的后續(xù)文章中,我們將討論這些主題: - 構建 RPM 軟件包而不必是 root 用戶
- 在創(chuàng)建軟件之前為軟件打補丁
- 在安裝和卸載時運行腳本
- 在安裝或卸載 其它軟件包時運行腳本
參考資料
關于作者 | | | Dan Poirier 是 IBM 的顧問軟件工程師。他目前在美國北卡羅萊納州的 Research Triangle Park 工作,從事運行 Linux 的網絡設備的研究。可以通過 poirier@us.ibm.com 與他聯(lián)系。 | |