1. 程式人生 > 實用技巧 >kernel rpm 製作與過程探索

kernel rpm 製作與過程探索

製作kernelrpm步驟:

1.安裝工具
yum install rpm-devel;rpmdevtools
yum groupinstall "Development Tools"

2.rpmdev-setuptree #在當前使用者根目錄下生成rpmbuild目錄
rpmbuild/
├── BUILD
├── BUILDROOT
├── RPMS
├── SOURCES
├── SPECS
└── SRPMS

3.下載kernel package,並解壓
wget http://mirrors.ustc.edu.cn/kernel.org/linux/kernel/v5.x/Linux-5.7.1.tar.gz
tar -zxvf linux-5.7.1.tar.gz

4.生成rpm
cd linux-5.7.1/
make rpm-pkg
可以看到如下資訊
Wrote: /root/rpmbuild/SRPMS/kernel-5.7.1-1.src.rpm
Wrote: /root/rpmbuild/RPMS/x86_64/kernel-5.7.1-1.x86_64.rpm
Wrote: /root/rpmbuild/RPMS/x86_64/kernel-headers-5.7.1-1.x86_64.rpm
Wrote: /root/rpmbuild/RPMS/x86_64/kernel-devel-5.7.1-1.x86_64.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.4aODom

+ umask 022
+ cd /root/rpmbuild/BUILD
+ cd kernel-5.7.1
+ rm -rf /root/rpmbuild/BUILDROOT/kernel-5.7.1-1.x86_64
+ exit 0
到這裡就ok了,可以看到/root/rpmbuild/RPMS/下生成了kernel,kernel-header,kernel-devel的rpm包,以及SRPMS/下會生成kernel-src的rpm包

命令超級簡單,瞧瞧生成rpm 過程:
1.當我們在linux-5.7.1/下執行make help可以看到:

Kernel packaging:
  rpm-pkg             - Build both source and binary RPM kernel packages
  binrpm
-pkg - Build only the binary kernel RPM package deb-pkg - Build both source and binary deb kernel packages bindeb-pkg - Build only the binary kernel deb package snap-pkg - Build only the binary kernel snap package (will connect to external hosts) dir-pkg - Build the kernel as a plain directory structure tar-pkg - Build the kernel as an uncompressed tarball targz-pkg - Build the kernel as a gzip compressed tarball tarbz2-pkg - Build the kernel as a bzip2 compressed tarball tarxz-pkg - Build the kernel as a xz compressed tarball perf-tar-src-pkg - Build perf-5.7.1.tar source tarball perf-targz-src-pkg - Build perf-5.7.1.tar.gz source tarball perf-tarbz2-src-pkg - Build perf-5.7.1.tar.bz2 source tarball perf-tarxz-src-pkg - Build perf-5.7.1.tar.xz source tarball

這些引數告訴我們可以去build什麼樣的packages

當我們進一步開啟linux-5.7.1/Makefile可以看到有下面資訊:

%src-pkg: FORCE
        $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.package $@
%pkg: include/config/kernel.release FORCE
        $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.package $@

%是萬用字元,不管我們build %src-pkg 或者%pkg,都要去呼叫Makefile.package檔案
因此,可以開啟Makefile.package看一下

PHONY += rpm-pkg
rpm-pkg:
        $(MAKE) clean
        $(CONFIG_SHELL) $(MKSPEC) >$(objtree)/kernel.spec
        $(call cmd,src_tar,$(KERNELPATH),kernel.spec)
        +rpmbuild $(RPMOPTS) --target $(UTS_MACHINE) -ta $(KERNELPATH).tar.gz \
        --define='_smp_mflags %{nil}'

# binrpm-pkg
# ---------------------------------------------------------------------------
PHONY += binrpm-pkg
binrpm-pkg:
        $(MAKE) -f $(srctree)/Makefile
        $(CONFIG_SHELL) $(MKSPEC) prebuilt > $(objtree)/binkernel.spec
        +rpmbuild $(RPMOPTS) --define "_builddir $(objtree)" --target \
                $(UTS_MACHINE) -bb $(objtree)/binkernel.spec
.............

這裡告訴我們要去build package需要執行的步驟,例如build rpm-pkg

變數定義自己去找,下面翻譯每個步驟:
步驟1:make clean
步驟2:./scripts/package/mkspec >./kernel.spec #生成spec文件
步驟3:rpmbuild --target x86_64 -ta kernel-5.7.1.tar.gz --define='_smp_mflags %{nil}'
步驟1,2好懂,來具體看看步驟3:
首先看下主目錄下Makefile:

        ifeq ("$(origin V)", "command line")
            KBUILD_VERBOSE = $(V)
        endif
        ifndef KBUILD_VERBOSE
            KBUILD_VERBOSE = 0
        endif

        ifeq ($(KBUILD_VERBOSE),1)
            quiet =
            Q =
        else
            quiet=quiet_
        Q = @
        endif
        

origin的語法:$(origin <variable>;) 函式origin並不操作變數的值,只是告訴你你的這個變數是哪裡來的。
origin的返回值如下:
(1)返回值為"undefine"時,這個變數沒有被定義過;
(2)返回值為“command line”時,這個變數是被命令列定義的;
(3)返回值為“environment”時,這個變數是定義為環境變數;
(4)返回值為“file”時,這個變數是定義在Makefile中;
(5)返回值為“default”時,變數是預設定義的;
(6)返回值為“override”時,被override指示符重新定義;
(7)返回值為“automatic”時,是一個命令執行中自動化變數

如果是指定V的值,make V=【0|1|2】,0表示quiet build,1表示verbose build,2表示give reason for build,則KBUILD_VERBOSE = V;
否則KBUILD_VERBOSE = 0;決定了quiet和Q的值
在這裡顯然是KBUILD_VERBOSe = 0,quiet=quiet_;Q=@

再看下cmd和src_tar的定義,scripts/Kbuild.include:

echo-cmd = $(if $($(quiet)cmd_$(1)),\
        echo '  $(call escsq,$($(quiet)cmd_$(1)))$(echo-why)';)
# printing commands
cmd = @set -e; $(echo-cmd) $(cmd_$(1))

quiet_cmd_src_tar = TAR     $(2).tar.gz
      cmd_src_tar = \
set -e; \
if test "$(objtree)" != "$(srctree)"; then \
        echo >&2; \
        echo >&2 "  ERROR:"; \
        echo >&2 "  Building source tarball is not possible outside the"; \
        echo >&2 "  kernel source tree. Don't set KBUILD_OUTPUT, or use the"; \
        echo >&2 "  binrpm-pkg or bindeb-pkg target instead."; \
        echo >&2; \
        false; \
fi ; \
$(srctree)/scripts/setlocalversion --save-scmversion; \
tar -cz $(RCS_TAR_IGNORE) -f $(2).tar.gz \
        --transform 's:^:$(2)/:S' $(TAR_CONTENT) $(3); \
rm -f $(objtree)/.scmversion

@set -e: @意思是不回顯command,只回顯結果,set -e意思是,如果執行結果不是true則退出
escsq的作用是將單引號'替換成\' ,避免在echo 宣告中使用單引號
echo-why:需要KBUILD_VERBOSE=2時才有效,告訴為什麼target被build
echo-cmd作用是列印該命令,以及為什麼built target,但echo-why 只在make V=2時才enable
echo的作用是列印該命令,並且執行該命令
就cmd定義的結構來看
cmd = @set -e;     $(echo-cmd)      $(cmd_$(1))
開啟set -e功能    列印quiet_cmd_${1},           執行命令
            前提是quiet_cmd_${1}有定義


$(call cmd,src_tar,$(KERNELPATH),kernel.spec),這裡${1}是src_tar,${2}是${KERNELPATH},${3}是kernel.spec,但是在quiet_src_tar是沒有用到kernel.spec這個引數,在src_tar時才用到。
因此翻譯過來就是TAR kernel-5.7.1.tar.gz

對於步驟4:+rpmbuild $(RPMOPTS) --target $(UTS_MACHINE) -ta $(KERNELPATH).tar.gz --define='_smp_mflags %{nil}'
先了解下幾個特殊字元:
+ : 執行命令時不受到make的-n -t -q 三個引數的影響
make:
-n : 僅列印命令而不執行,通常用在需要看自己編寫的makefile是否錯誤,命令是否正確的情況下
-t : 跟新目標檔案的時間,只把目標檔案變成已經編譯的狀態,而不是真正的編譯目標,偽編譯
-q : 檢查目標檔案是否需要跟新
- : 任何命令列的非零退出狀態都被忽略,不會因為執行錯誤,退出狀態為非零而退出
@ : 不顯示命令本身而只顯示結果
$ : 列印makefile中定義的變數
$$ : 列印makefile中定義的shell變數
$@ : 目標檔案
$^ : 所有的依賴檔案
$< : 第一個依賴檔案

-ta的意思:build binary and source package with tarball
--defile='_smp_mflags ${nil}'是優化選項,目前還沒搞懂什麼意思~

這句命令翻譯過來就是rpmbuild --target x86_64 -ta kernel-5.7.1.tar.gz --define='_smp_mflags %{nil}'

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

整個make rpm-pkg的過程就是建立kernel.spec,然後執行rpmbuild,我跳過make rpm-pkg過程,直接用rpmbuild也可以,步驟如下:
1.安裝工具
yum install rpm-devel;rpmdevtools
yum groupinstall "Development Tools"

2.rpmdev-setuptree #在當前使用者根目錄下生成rpmbuild目錄
rpmbuild/
├── BUILD
├── BUILDROOT
├── RPMS
├── SOURCES
├── SPECS
└── SRPMS

3.下載kernel package,並解壓
wget http://mirrors.ustc.edu.cn/kernel.org/linux/kernel/v5.x/Linux-5.7.1.tar.gz
tar -zxvf linux-5.7.1.tar.gz

4.cd linux-5.7.1
1).make menuconfig #生成.config檔案,只需要點選save即可
2).修改.config:
CONFIG_SYSTEM_TRUSTED_KEYS="certs/rhel.pem" => #CONFIG_SYSTEM_TRUSTED_KEYS="certs/rhel.pem"
CONFIG_DEBUG_INFO_BTF=y => CONFIG_DEBUG_INFO_BTF=n
不然編譯會報錯

3.修改 scripts/package/mkspec,因為跳過了make,所以不能繼承Makefile裡面的變數
KERNELRELEASE=5.7.1
MAKE=make
export RCS_TAR_IGNORE=“--exclude SCCS --exclude BitKeeper --exclude .svn --exclude CVS --exclude .pc --exclude .hg --exclude .git”
然後./scripts/package/mkspec > ./kernel.spec

4.生成.config和kernel.spec後
mv linux-5.7.1 kernel-5.7.1 #這個不是必要的,只是kernel.spec裡寫的name是kernel,當然也可以把name改成linux,但是一般是kernel開頭吧
tar -zcvf kernel-5.7.1.tar.gz kernel-5.7.1 #生成tarball

5.rpmbuild --target x86_64 -ta kernel-5.7.1.tar.gz --define='_smp_mflags %{nil}'

只是幫助理解過程,實際中還是make rpm-pkg一個指令就搞定啦

有兩點疑問
1.TAR kernel-5.7.1.tar.gz這個TAR實在找不到哪裡來的
2.--define='_smp_mflags %{nil}' 還沒理解