如何構建 RPM 包
自20多年前我開始使用 Linux 以來,我已經使用過基於 rpm 的軟體包管理器在 Red Hat 和 Fedora Linux 系統上安裝軟體。我使用過 rpm
程序本身,還有 yum
和 dnf
,用於在我的 Linux 主機上安裝和更新軟體包,dnf
是 yum
的一個近親。 yum
和 dnf
工具是 rpm
實用程序的包裝器,它提供了其他功能,例如查找和安裝包依賴項的功能。
多年來,我創建了許多 Bash 腳本,其中一些腳本具有單獨的配置文件,我希望在大多數新計算機和虛擬機上安裝這些腳本。這也能解決安裝所有這些軟體包需要花費大量時間的難題,因此我決定通過創建一個 rpm 軟體包來自動執行該過程,我可以將其複製到目標主機並將所有這些文件安裝在適當的位置。雖然 rpm
工具以前用於構建 rpm 包,但該功能已被刪除,並且創建了一個新工具來構建新的 rpm。
當我開始這個項目時,我發現很少有關於創建 rpm 包的信息,但我找到了一本書,名為《Maximum RPM》,這本書才幫我弄明白了。這本書現在已經過時了,我發現的絕大多數信息都是如此。它也已經絕版,用過的副本也需要花費數百美元。Maximum RPM 的在線版本是免費提供的,並保持最新。該 RPM 網站還有其他網站的鏈接,這些網站上有很多關於 rpm 的文檔。其他的信息往往是簡短的,顯然都是假設你已經對該過程有了很多了解。
此外,我發現的每個文檔都假定代碼需要在開發環境中從源代碼編譯。我不是開發人員。我是一個系統管理員,我們系統管理員有不同的需求,因為我們不需要或者我們不應該為了管理任務而去編譯代碼;我們應該使用 shell 腳本。所以我們沒有源代碼,因為它需要被編譯成二進位可執行文件。我們擁有的源代碼也應該是可執行的。
在大多數情況下,此項目應作為非 root 用戶執行。 rpm 包永遠不應該由 root 用戶構建,而只能由非特權普通用戶構建。我將指出哪些部分應該以 root 身份執行,哪些部分應由非 root,非特權用戶執行。
準備
首先,打開一個終端會話,然後 su
到 root 用戶。 請務必使用 -
選項以確保啟用完整的 root 環境。 我不認為系統管理員應該使用 sudo
來執行任何管理任務。 在我的個人博客文章中可以找出為什麼:真正的系統管理員不要使用 sudo。
[student@testvm1 ~]$ su -
Password:
[root@testvm1 ~]#
創建可用於此項目的普通用戶 student,並為該用戶設置密碼。
[root@testvm1 ~]# useradd -c "Student User" student
[root@testvm1 ~]# passwd student
Changing password for user student.
New password: <Enter the password>
Retype new password: <Enter the password>
passwd: all authentication tokens updated successfully.
[root@testvm1 ~]#
構建 rpm 包需要 rpm-build
包,該包可能尚未安裝。 現在以 root 身份安裝它。 請注意,此命令還將安裝多個依賴項。 數量可能會有所不同,具體取決於主機上已安裝的軟體包; 它在我的測試虛擬機上總共安裝了 17 個軟體包,這是非常小的。
dnf install -y rpm-build
除非另有明確指示,否則本項目的剩餘部分應以普通用戶用戶 student 來執行。 打開另一個終端會話並使用 su
切換到該用戶以執行其餘步驟。 使用以下命令從 GitHub 下載我準備好的開發目錄結構 utils.tar 這個 tar 包 (LCTT 譯註:tarball 是以 tar 命令來打包和壓縮的文件的統稱):
wget https://github.com/opensourceway/how-to-rpm/raw/master/utils.tar
此 tar 包包含將由最終 rpm
程序安裝的所有文件和 Bash 腳本。 還有一個完整的 spec 文件,你可以使用它來構建 rpm。 我們將詳細介紹 spec 文件的每個部分。
作為普通學生 student,使用你的家目錄作為當前工作目錄(pwd
),解壓縮 tar 包。
[student@testvm1 ~]$ cd ; tar -xvf utils.tar
使用 tree
命令驗證 ~/development
的目錄結構和包含的文件,如下所示:
[student@testvm1 ~]$ tree development/
development/
├── license
│ ├── Copyright.and.GPL.Notice.txt
│ └── GPL_LICENSE.txt
├── scripts
│ ├── create_motd
│ ├── die
│ ├── mymotd
│ └── sysdata
└── spec
└── utils.spec
3 directories, 7 files
[student@testvm1 ~]$
mymotd
腳本創建一個發送到標準輸出的「當日消息」數據流。 create_motd
腳本運行 mymotd
腳本並將輸出重定向到 /etc/motd
文件。 此文件用於向使用 SSH 遠程登錄的用戶顯示每日消息。
die
腳本是我自己的腳本,它將 kill
命令包裝在一些代碼中,這些代碼可以找到與指定字元串匹配的運行程序並將其終止。 它使用 kill -9
來確保 kill
命令一定會執行。
sysdata
腳本可以顯示有關計算機硬體,還有已安裝的 Linux 版本,所有已安裝的軟體包以及硬碟驅動器元數據等數萬行數據。 我用它來記錄某個時間點的主機狀態。 我以後可以用它作為參考。 我曾經這樣做是為了維護我為客戶安裝的主機記錄。
你可能需要將這些文件和目錄的所有權更改為 student:student
。 如有必要,使用以下命令執行此操作:
chown -R student:student development
此文件樹中的大多數文件和目錄將通過你在此項目期間創建的 rpm 包安裝在 Fedora 系統上。
創建構建目錄結構
rpmbuild
命令需要非常特定的目錄結構。 你必須自己創建此目錄結構,因為沒有提供自動方式。 在家目錄中創建以下目錄結構:
~ ─ rpmbuild
├── RPMS
│ └── noarch
├── SOURCES
├── SPECS
└── SRPMS
我們不會創建 rpmbuild/RPMS/X86_64
目錄,因為它是特定於體系結構編譯的 64 位二進位文件。 我們有 shell 腳本,不是特定於體系結構的。 實際上,我們也不會使用 SRPMS
目錄,它將包含編譯器的源文件。
檢查 spec 文件
每個 spec 文件都有許多部分,其中一些部分可能會被忽視或省略,取決於 rpm 構建的具體情況。 這個特定的 spec 文件不是工作所需的最小文件的示例,但它是一個包含不需要編譯的文件的中等複雜 spec 文件的很好例子。 如果需要編譯,它將在 %build
部分中執行,該部分在此 spec 文件中省略掉了,因為它不是必需的。
前言
這是 spec 文件中唯一沒有標籤的部分。 它包含運行命令 rpm -qi [Package Name]
時看到的大部分信息。 每個數據都是一行,由標籤和標籤值的文本數據組成。
###############################################################################
# Spec file for utils
################################################################################
# Configured to be built by user student or other non-root user
################################################################################
#
Summary: Utility scripts for testing RPM creation
Name: utils
Version: 1.0.0
Release: 1
License: GPL
URL: http://www.both.org
Group: System
Packager: David Both
Requires: bash
Requires: screen
Requires: mc
Requires: dmidecode
BuildRoot: ~/rpmbuild/
# Build with the following syntax:
# rpmbuild --target noarch -bb utils.spec
rpmbuild
程序會忽略注釋行。我總是喜歡在本節中添加註釋,其中包含創建包所需的 rpmbuild
命令的確切語法。
Summary
標籤是包的簡短描述。
Name
、Version
和 Release
標籤用於創建 rpm 文件的名稱,如 utils-1.00-1.rpm
。通過增加發行版號碼和版本號,你可以創建 rpm 包去更新舊版本的。
License
標籤定義了發布包的許可證。我總是使用 GPL 的一個變體。指定許可證對於澄清包中包含的軟體是開源的這一事實非常重要。這也是我將 License
和 GPL
語句包含在將要安裝的文件中的原因。
URL
通常是項目或項目所有者的網頁。在這種情況下,它是我的個人網頁。
Group
標籤很有趣,通常用於 GUI 應用程序。 Group
標籤的值決定了應用程序菜單中的哪一組圖標將包含此包中可執行文件的圖標。與 Icon
標籤(我們此處未使用)一起使用時,Group
標籤允許在應用程序菜單結構中添加用於啟動程序的圖標和所需信息。
Packager
標籤用於指定負責維護和創建包的人員或組織。
Requires
語句定義此 rpm 包的依賴項。每個都是包名。如果其中一個指定的軟體包不存在,DNF 安裝實用程序將嘗試在 /etc/yum.repos.d
中定義的某個已定義的存儲庫中找到它,如果存在則安裝它。如果 DNF 找不到一個或多個所需的包,它將拋出一個錯誤,指出哪些包丟失並終止。
BuildRoot
行指定頂級目錄,rpmbuild
工具將在其中找到 spec 文件,並在構建包時在其中創建臨時目錄。完成的包將存儲在我們之前指定的 noarch
子目錄中。
注釋顯示了構建此程序包的命令語法,包括定義了目標體系結構的 –target noarch
選項。因為這些是 Bash 腳本,所以它們與特定的 CPU 架構無關。如果省略此選項,則構建將選用正在執行構建的 CPU 的體系結構。
rpmbuild
程序可以針對許多不同的體系結構,並且使用 --target
選項允許我們在不同的體系結構主機上構建特定體系結構的包,其具有與執行構建的體系結構不同的體系結構。所以我可以在 x86_64 主機上構建一個用於 i686 架構的軟體包,反之亦然。
如果你有自己的網站,請將打包者的名稱更改為你自己的網站。
描述部分(%description
)
spec 文件的 %description
部分包含 rpm 包的描述。 它可以很短,也可以包含許多信息。 我們的 %description
部分相當簡潔。
%description
A collection of utility scripts for testing RPM creation.
準備部分(%prep
)
%prep
部分是在構建過程中執行的第一個腳本。 在安裝程序包期間不會執行此腳本。
這個腳本只是一個 Bash shell 腳本。 它準備構建目錄,根據需要創建用於構建的目錄,並將相應的文件複製到各自的目錄中。 這將包括作為構建的一部分的完整編譯所需的源代碼。
$RPM_BUILD_ROOT
目錄表示已安裝系統的根目錄。 在 $RPM_BUILD_ROOT
目錄中創建的目錄是真實文件系統中的絕對路徑,例如 /user/local/share/utils
、/usr/local/bin
等。
對於我們的包,我們沒有預編譯源,因為我們的所有程序都是 Bash 腳本。 因此,我們只需將這些腳本和其他文件複製到已安裝系統的目錄中。
%prep
################################################################################
# Create the build tree and copy the files from the development directories #
# into the build tree. #
################################################################################
echo "BUILDROOT = $RPM_BUILD_ROOT"
mkdir -p $RPM_BUILD_ROOT/usr/local/bin/
mkdir -p $RPM_BUILD_ROOT/usr/local/share/utils
cp /home/student/development/utils/scripts/* $RPM_BUILD_ROOT/usr/local/bin
cp /home/student/development/utils/license/* $RPM_BUILD_ROOT/usr/local/share/utils
cp /home/student/development/utils/spec/* $RPM_BUILD_ROOT/usr/local/share/utils
exit
請注意,本節末尾的 exit
語句是必需的。
文件部分(%files
)
spec 文件的 %files
這一部分定義了要安裝的文件及其在目錄樹中的位置。 它還指定了要安裝的每個文件的文件屬性(%attr
)以及所有者和組所有者。 文件許可權和所有權是可選的,但我建議明確設置它們以消除這些屬性在安裝時不正確或不明確的任何可能性。 如果目錄尚不存在,則會在安裝期間根據需要創建目錄。
%files
%attr(0744, root, root) /usr/local/bin/*
%attr(0644, root, root) /usr/local/share/utils/*
安裝前(%pre
)
在我們的實驗室項目的 spec 文件中,此部分為空。 這應該放置那些需要 rpm 中的文件安裝前執行的腳本。
安裝後(%post
)
spec 文件的這一部分是另一個 Bash 腳本。 這個在文件安裝後運行。 此部分幾乎可以是你需要或想要的任何內容,包括創建文件、運行系統命令以及重新啟動服務以在進行配置更改後重新初始化它們。 我們的 rpm 包的 %post
腳本執行其中一些任務。
%post
################################################################################
# Set up MOTD scripts #
################################################################################
cd /etc
# Save the old MOTD if it exists
if [ -e motd ]
then
cp motd motd.orig
fi
# If not there already, Add link to create_motd to cron.daily
cd /etc/cron.daily
if [ ! -e create_motd ]
then
ln -s /usr/local/bin/create_motd
fi
# create the MOTD for the first time
/usr/local/bin/mymotd > /etc/motd
此腳本中包含的注釋應明確其用途。
卸載後(%postun
)
此部分包含將在卸載 rpm 軟體包後運行的腳本。 使用 rpm
或 dnf
刪除包會刪除文件部分中列出的所有文件,但它不會刪除安裝後部分創建的文件或鏈接,因此我們需要在本節中處理。
此腳本通常由清理任務組成,只是清除以前由 rpm
安裝的文件,但 rpm 本身無法完成清除。 對於我們的包,它包括刪除 %post
腳本創建的鏈接並恢復 motd 文件的已保存原件。
%postun
# remove installed files and links
rm /etc/cron.daily/create_motd
# Restore the original MOTD if it was backed up
if [ -e /etc/motd.orig ]
then
mv -f /etc/motd.orig /etc/motd
fi
清理(%clean
)
這個 Bash 腳本在 rpm 構建過程之後開始清理。 下面 %clean
部分中的兩行刪除了 rpm-build
命令創建的構建目錄。 在許多情況下,可能還需要額外的清理。
%clean
rm -rf $RPM_BUILD_ROOT/usr/local/bin
rm -rf $RPM_BUILD_ROOT/usr/local/share/utils
變更日誌(%changelog
)
此可選的文本部分包含 rpm 及其包含的文件的變更列表。最新的變更記錄在本部分頂部。
%changelog
* Wed Aug 29 2018 Your Name <Youremail@yourdomain.com>
- The original package includes several useful scripts. it is
primarily intended to be used to illustrate the process of
building an RPM.
使用你自己的姓名和電子郵件地址替換標題行中的數據。
構建 rpm
spec 文件必須位於 rpmbuild
目錄樹的 SPECS
目錄中。 我發現最簡單的方法是創建一個指向該目錄中實際 spec 文件的鏈接,以便可以在開發目錄中對其進行編輯,而無需將其複製到 SPECS
目錄。 將 SPECS
目錄設為當前工作目錄,然後創建鏈接。
cd ~/rpmbuild/SPECS/
ln -s ~/development/spec/utils.spec
運行以下命令以構建 rpm。 如果沒有錯誤發生,只需要花一點時間來創建 rpm。
rpmbuild --target noarch -bb utils.spec
檢查 ~/rpmbuild/RPMS/noarch
目錄以驗證新的 rpm 是否存在。
[student@testvm1 ~]$ cd rpmbuild/RPMS/noarch/
[student@testvm1 noarch]$ ll
total 24
-rw-rw-r--. 1 student student 24364 Aug 30 10:00 utils-1.0.0-1.noarch.rpm
[student@testvm1 noarch]$
測試 rpm
以 root 用戶身份安裝 rpm 以驗證它是否正確安裝並且文件是否安裝在正確的目錄中。 rpm 的確切名稱將取決於你在前言部分中標籤的值,但如果你使用了示例中的值,則 rpm 名稱將如下面的示例命令所示:
[root@testvm1 ~]# cd /home/student/rpmbuild/RPMS/noarch/
[root@testvm1 noarch]# ll
total 24
-rw-rw-r--. 1 student student 24364 Aug 30 10:00 utils-1.0.0-1.noarch.rpm
[root@testvm1 noarch]# rpm -ivh utils-1.0.0-1.noarch.rpm
Preparing... ################################# [100%]
Updating / installing...
1:utils-1.0.0-1 ################################# [100%]
檢查 /usr/local/bin
以確保新文件存在。 你還應驗證是否已創建 /etc/cron.daily
中的 create_motd
鏈接。
使用 rpm -q --changelog utils
命令查看更改日誌。 使用 rpm -ql utils
命令(在 ql
中為小寫 L
)查看程序包安裝的文件。
[root@testvm1 noarch]# rpm -q --changelog utils
* Wed Aug 29 2018 Your Name <Youremail@yourdomain.com>
- The original package includes several useful scripts. it is
primarily intended to be used to illustrate the process of
building an RPM.
[root@testvm1 noarch]# rpm -ql utils
/usr/local/bin/create_motd
/usr/local/bin/die
/usr/local/bin/mymotd
/usr/local/bin/sysdata
/usr/local/share/utils/Copyright.and.GPL.Notice.txt
/usr/local/share/utils/GPL_LICENSE.txt
/usr/local/share/utils/utils.spec
[root@testvm1 noarch]#
刪除包。
rpm -e utils
試驗
現在,你將更改 spec 文件以要求一個不存在的包。 這將模擬無法滿足的依賴關係。 在現有依賴行下立即添加以下行:
Requires: badrequire
構建包並嘗試安裝它。 顯示什麼消息?
我們使用 rpm
命令來安裝和刪除 utils
包。 嘗試使用 yum
或 dnf
安裝軟體包。 你必須與程序包位於同一目錄中,或指定程序包的完整路徑才能使其正常工作。
總結
在這篇對創建 rpm 包的基礎知識的概覽中,我們沒有涉及很多標籤和很多部分。 下面列出的資源可以提供更多信息。 構建 rpm 包並不困難;你只需要正確的信息。 我希望這對你有所幫助——我花了幾個月的時間來自己解決問題。
我們沒有涵蓋源代碼構建,但如果你是開發人員,那麼從這一點開始應該是一個簡單的步驟。
創建 rpm 包是另一種成為懶惰系統管理員的好方法,可以節省時間和精力。 它提供了一種簡單的方法來分發和安裝那些我們作為系統管理員需要在許多主機上安裝的腳本和其他文件。
資料
- Edward C. Baily,《Maximum RPM》,Sams 出版於 2000 年,ISBN 0-672-31105-4
- Edward C. Baily,《Maximum RPM》,更新在線版本
- RPM 文檔:此網頁列出了 rpm 的大多數可用在線文檔。 它包括許多其他網站的鏈接和有關 rpm 的信息。
via: https://opensource.com/article/18/9/how-build-rpm-packages
作者:David Both 選題:lujun9972 譯者:Flowsnow 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive