https://opensource.com/article/18/9/how-build-rpm-packages
作者 | David Both
譯者 | Liang Chen (Flowsnow) ????共計翻譯:28.0 篇 貢獻時間:1062 天
節省跨多個主機安裝檔案和指令碼的時間和精力。
自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[1] 的線上版本是免費提供的,並保持最新。該 RPM 網站[2]還有其他網站的連結,這些網站上有很多關於 rpm 的檔案。其他的資訊往往是簡短的,顯然都是假設你已經對該過程有了很多瞭解。
此外,我發現的每個檔案都假定程式碼需要在開發環境中從原始碼編譯。我不是開發人員。我是一個系統管理員,我們系統管理員有不同的需求,因為我們不需要或者我們不應該為了管理任務而去編譯程式碼;我們應該使用 shell 指令碼。所以我們沒有原始碼,因為它需要被編譯成二進位制可執行檔案。我們擁有的原始碼也應該是可執行的。
在大多數情況下,此專案應作為非 root 使用者執行。 rpm 包永遠不應該由 root 使用者構建,而只能由非特權普通使用者構建。我將指出哪些部分應該以 root 身份執行,哪些部分應由非 root,非特權使用者執行。
準備
首先,開啟一個終端會話,然後 su
到 root 使用者。 請務必使用 -
選項以確保啟用完整的 root 環境。 我不認為系統管理員應該使用 sudo
來執行任何管理任務。 在我的個人部落格文章中可以找出為什麼:真正的系統管理員不要使用 sudo[3]。
[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 包是另一種成為懶惰系統管理員的好方法,可以節省時間和精力。 它提供了一種簡單的方法來分發和安裝那些我們作為系統管理員需要在許多主機上安裝的指令碼和其他檔案。
資料
via: https://opensource.com/article/18/9/how-build-rpm-packages
作者:David Both[6] 選題:lujun9972 譯者:Flowsnow 校對:wxy
本文由 LCTT 原創編譯,Linux中國 榮譽推出