從原始碼包構建.deb檔案的備忘
原始碼包是什麼
我們知道ubuntu有別於gentoo之一的特點就是,gentoo是基於原始碼包安裝的系統,而ubuntu是基於二進位制的。我們執行一個apt-get install foo安裝包命令時,apt從對應的apt source源地址下載一個二進位制包-以.deb為字尾名的檔案到/var/cache/apt/archives下,再用dpkg工具安裝它們。這些.deb檔案都是包的維護者在某臺build machine上build之後放上去的,而與foo.deb對應的原始碼包,一般都是指三個檔案的一個集合:foo.orig.gz, foo.dsc, foo.diff.gz. foo.orig.gz是該軟體的原始原始碼,通常取自於git,svn或sourceforge,可以把它看作是中立的,和Linux發行版無關的原始碼。foo.diff.gz包含了將一份原始原始碼加工、改造為debian系安裝包的非功能性的補丁檔案,或許同時也包括一些功能性的修補原始碼bug的補丁。foo.dsc是一個包描述檔案,它是一些ubuntu上的原始碼包處理工具如dpkg-source的輸入。foo.diff.gz和foo.dsc扮演meta package的角色。一般來說,一個或者多個二進位制包都對應一份原始碼包(因為多個二進位制包可能由同一個原始碼產生),source.list的deb-src標鑑說明了可以通過apt-get從哪些網路地址獲取原始碼包.
為什麼從原始碼包生成軟體
既然已經有了二進位制包,為什麼有時候還要從原始碼包來生成軟體?因為二進位制包畢竟是在別人的build machine上生成的,自然產生的二進位制依賴也是基於維護人的環境。假如你想使用一個軟體的更高版本,可能是因為高版本修訂了當前使用版本的某個bug,或者想看看新的更省電的feature,當你把該軟體高版本的apt source源新增到了source.list,然後使用apt-get update; apt-get install foo=high_version時,你發現apt-get抱怨很多執行時依賴得不到滿足;而不得不把Ubuntu的整個release version升級,其實,你想做的只是升級這個軟體包而已,發行版其他的部分仍然想保留已經使用了很久的版本,那這時從原始碼包構建軟體就是必須的了。這裡我個人就經歷過這樣的例子,在12.04LTS系統裡,cairo-dock這個軟體是有bug的,如果要升級,就得升級發行版,因為新的cairo-dock依賴新的xserver stack,而新的xserver stack牽涉的東西太多,那麼就得升級發行版,當採用官方的源把xserver stack升級到最新版時,發現它已經幫你預設採用xmir了…天,我只是想用一個新版本的cairo-dock而已,並不想升級我的核心,libc,更不想用什麼mir。這時我只有從網上下載更新版本的cairo-dock的原始碼包來構建,當然在構建過程中,必然會發先cairo-dock確實有些依賴不被當前的xserver stack所滿足(比如用了更新的api或者資料結構都變了),那這時你就只有再把滿足依賴的xserver stack包下下來編譯(是一個遞迴收斂的過程),有可能要調整預設的編譯引數(比如build最新的xserver core時把xmir disable掉)這個過程當然不容易,但做多了有經驗後再碰到類似問題就相對容易多了。你也許會說從原始碼構建很簡單啊,不就是git clone原始碼倉庫然後./configure; make; make install嗎?但這種構建方式只會產生二進位制檔案,不會產生二進位制.deb包,作業系統的包管理器只有通過.deb包安裝,它才知道使用者安裝過這個程式,有哪些二進位制檔案屬於這個”包”等,這樣不管以後的升級,解除安裝,替換等都有蹤可循,如果直接從原始碼構建,那你以後要解除安裝它怎麼辦?你還記得它的原始碼放在哪裡嗎?它是用make uninstall解除安裝的還是其他什麼命令?一些目錄甚至可能是寫死的,並且,如果你的軟體包含一些庫會被其他軟體依賴,那麼包管理器也可以通過包資料庫為這些依賴提供線索,如果直接使用原始碼構建軟體,那相當於你機器上的軟體依賴鏈條出現了斷裂,這會影響後續軟體的安裝解除安裝管理。
從原始碼包構建的備忘
- 用dpkg-source -x
foo.dsc從foo.orig.gz和foo.diff.gz建立工作目錄foo:一份發行版中立的原始碼目錄,加上一個debian目錄以及目錄下的meta檔案,就構成了一份可以生成二進位制deb包的原始碼工作目錄。其實從apt-get
source抓下來的目錄,已經是通過dpkg-source -x解壓過的了。dpkg-source
-x所做的主要事情就是1.解壓;2.把foo.diff.gz裡的patch打到原始檔案上。生成的foo目錄下的原始檔,都已經是打過deb原始碼包裡的patch了的。 - 在foo目錄下,執行dpkg-buildpackage -us
-uc構建包。-us和-uc引數是不做簽名,適合於本地構建本地使用的情況。這個命令的輸出有兩個,一個是二進位制deb包,另一個是原始碼包,為什麼這裡還要生成原始碼包?因為你可能改動某些檔案,那麼會生成新的diff.gz來記錄所有你針對原始原始碼的改動,不管釋出還是儲存更改都更方便,下一次你只需要在生成的新的.dsc檔案上執行dpkg-source -x就可以產生一個一模一樣的原始碼了。如果你什麼都沒改動,那麼新產生的原始碼包同你構建所來源的原始碼包是一樣的。你也可以用引數-b和-S來控制這次構建只產生二進位制包或者只產生原始碼包。 - 兩個最重要的meta檔案,debian/control和debian/rules。control檔案決定了哪些二進位制包將從這份原始碼目錄中構建,一個原始碼目錄往往是好幾個二進位制包的輸入源。你不想生成哪個遮蔽它就行。二進位制包的執行時依賴關係也在包的宣告中可見,並且control檔案也聲明瞭構建過程中的依賴,不過可以給dpkg-buildpackage傳-d引數來忽略構建依賴。
- debian/rules檔案其實就是個Makefile,你可以執行make -f debian/rules target來單獨執行某個目標。rules檔案裡基本上都是對debhelper指令碼函式的呼叫,像是dh_*這樣的函式,它們負責大部分的構建過程。常用的clean, install目標在rules檔案中也有,有些基於原始碼包的Makefile上所做的事情如make clean需要通過make -f debian/rules clean來代替。
- 和傳統意義的Make過程有點不一樣的就是,預設狀態下,每次dpkg-buildpackage,其實都是把從configure.ac生成configure指令碼,到生成Makefile,到構建source,到安裝binary都做一遍,哪怕你並沒有改過configure.ac,或者改過原始碼.c檔案,假如構建失敗了,就需要嘗試改動原始碼重新構建,有時候需要反覆嘗試這個過程直到構建成功,如果包很大的話那需要花費的時間就很長,這時傳入-nc引數可以讓dpkg-buildpackage保留當前的構建結果,就像傳統的make一樣只會從出錯的地方重新開始。當然,當對原始碼的改動終止後,最後還是需要再執行一遍不帶-nc引數的命令”dpkg-buildpackage -us -uc”來重新完全構建一遍,否則在生成原始碼包時可能會出錯。
- dpkg-buildpackage不用擔心它會自動改變你的原始檔(即通過dpkg-source
-x產生的檔案),當然前提是你確實改動的是”源”檔案,比如是configura.ac而不是configure,是dkms.conf.in而不是dkms.conf。 - 構建軟體時做得最多的事就是根據自己系統的需求調整./configure引數了吧,比如–enable–xxx或者–disable-xxx,在rules檔案中,通過帶override字首的target可以起到為預設的target定製引數的目的,如override_dh_auto_xconfigure:
- 對原始碼包有修改最好通過dch -i來生成一個新的changelog檔案,每個change item的title部分都是表示這次change的最新版本號,dpkg-buildpackage的輸出二進位制包的版本號其實就是從changelog裡提取的(不是寫在control檔案裡的)。