4.4. 進階專題

4.4.1. 自己做個 port

想必現在各位一定對做一個自己的 port 和升級現有的 port 有興趣吧?太棒了!

接下來的是如何做一個 FreeBSD 的 port 的指引。 如果要升級現有的 port 就應該看完這個再看 Section 4.4.15

覺得這份文件不夠詳細時,可以參考所有 port 的 Makefile 都有包含的 /usr/ports/Mk/bsd.port.mk。即使沒有每天都在 hack Makefile,仍然建議這樣做,而且可以從裡面得到很多知識。 此外也可以把特別的問題送到 FreeBSD ports mailing list

Note: 只有少數可以更動的變數 (VAR) 才會在這份文件中提到。大部份(如果不是全部)都在 bsd.port.mk 的開始有註解。 這個檔案用的是不標準的 tab 設定。EmacsVim 在載入檔案時應該都能辯認出來。 一旦檔案載入時,viex 都能藉由鍵入 :set tabstop=4 得到正確的設定值。

4.4.2. 快速 Porting

這一章說明了如何快速地移植(port)。 不過我們會看到在很多地方這樣是不夠的。

首先拿到原始的 tarball 並把它放進 DISTDIR, 預設是 /usr/ports/distfiles

Note: The following assumes that the software compiled out-of-the-box, 就是說這個 port 完全不需要修改就可以在 FreeBSD 上執行。 假如要改變某個地方,那也必須參考下一節。

4.4.2.1. 撰寫 Makefile

最小的 Makefile 看起來會是這副德性:

    # New ports collection makefile for:   oneko
    # Version required:    1.1b
    # Date created:        5 December 1994
    # Whom:                asami
    #
    # $FreeBSD$
    #
           
    DISTNAME=      oneko-1.1b
    CATEGORIES=    games
    MASTER_SITES=  ftp://ftp.cs.columbia.edu/archives/X11R5/contrib/
           
    MAINTAINER=    asami@FreeBSD.org
           
    MAN1=          oneko.1
    MANCOMPRESSED= yes
    USE_IMAKE=     yes
           
    .include <bsd.port.mk>

看能不能了解吧。不用擔心有 $FreeBSD$ 那行,當 port 進入主要的 port tree 時會由 CVS 自動填上。 可以在 Makefile 的樣本那章裡有更詳細的例子。

4.4.2.2. 撰寫敘述(description)檔

有三個敘述檔是每個 port 都要有的,不管它們實際上是不是 package。 這三個檔是 COMMENTDESCRPLIST,並放在 pkg 這個子目錄裡。

4.4.2.2.1. COMMENT

這個檔僅用一行來描述 port。拜托不要把 package 的名稱也寫進去(或是軟體的版本)。 這份註解應該以大寫字母開頭,而結尾不能有英文的句點。 這裡有一個範例:

    A cat chasing a mouse all over the screen

4.4.2.2.2. DESCR

這是一個較長的敘述。 用一到幾段的敘述簡潔扼要的說明 port 的事是足夠的。

Note: 這並不是一份關於如何使用或編譯 port 的指南或深入的描述!如果直接從 README 或 manpage 抄過來的話請慎重;因為太多時後它們並不是個扼要的說明, 或者它們的格式很難應付(如 manpage 有調整過的 spacing)。 如果被移植的軟體有正式的 WWW 首頁,那也應該列在這個檔案裡。 在一個網站的字首加上 WWW: 這樣自動化的工具才能正確地執行。

建議在檔案的最後寫上自己的名字,如下:

    This is a port of oneko, in which a cat chases a poor mouse all over
    the screen.
     :
    (etc.)
    
    WWW: http://www.oneko.org/            
    
    - Satoshi
    asami@cs.berkeley.edu

4.4.2.2.3. PLIST

這個檔案列出了所有 port 安裝的檔案。因為 package 就是藉由把所列出的檔案包裝起來產生的,所以這個檔又叫做 ``packing list''。其路徑則是相對於安裝的前置目錄(通常是 /usr/local/usr/X11R6)。如果有用 MANn 這個變數(本來就該這麼做),那千萬不要在這裡列出任何 manpage。

這裡有個小例子:

    bin/oneko
    lib/X11/app-defaults/Oneko
    lib/X11/oneko/cat1.xpm
    lib/X11/oneko/cat2.xpm
    lib/X11/oneko/mouse.xpm
    @dirrm lib/X11/oneko

詳細資料參考 pkg_create(1) man page 裡的包裝列表(packing list)。

Note: 列表裡應該列出所有的檔案,而不是 the name directories。 如果 port 在安裝過程自己新增了目錄,確定要有加上 @dirrm 這幾行,因為要移除 port 時不可或缺。

建議把檔案名稱照字母順序排列。這樣在升級 port 時可以更容易看出哪裡改變了。

自己手動做一個包裝列表是很冗長乏味的。如果 port 要裝很多檔案,那自動產生包裝列表 可能會較省時。

4.4.2.3. 產生 checksum file

只要鍵入 make makesum。Port 編譯的規則會自動產生 files/md5 這個檔。

4.4.2.4. 測試 port

先確定 port 的規則都遵君旨意,包括包裝 port。 這些都是得先確認的要點。

  • PLIST 沒有包含任何 port 沒裝的東西

  • PLIST 包含 port 安裝的所有東西

  • Port 可以用 reinstall 的標的重覆安裝

  • Port 在解除安裝後可以清理乾淨

建議的測試順序

  1. make install

  2. make package

  3. make deinstall

  4. pkg_add package-name

  5. make deinstall

  6. make reinstall

  7. make package

確定 port 在 packagedeinstall 的階段沒有任何警告訊息, 在步驟三後檢查是否所有新的目錄都被正確的刪除。還有, 試著在步驟四後使用這個軟體以確定當用 package 安裝後可以正確的執行。

4.4.2.5. 用 portlint 檢查 port

請用 portlint 來看看 port 有沒有符合我們的指示。portlint 這個程式也是 ports collection 的一部份。特別是當想用來檢查 Makefile 長得正不正確和 package 的命名是否恰當時。

4.4.2.6. 提出 port

首先要確定已經看過做與不做的章節。

現在你一定對自己的 port 感到興奮吧, 再來唯一要做的就是把它放到主要的 FreeBSD ports tree 裡去, 讓其他人也同樣感到高興。我們不需要 work 這個目錄或是 pkgname.tgz 的 package, 所以砍了它吧。再來,只要把執行 shar `find port_dir` 產生的訊息也包括進來,用 send-pr(1) 以 bug 報告的形式寄出(參考 bug 報告和一般說明以得到更多有關 send-pr(1) 的資訊)。 如果 port 沒壓縮前大於 20KB, 那就要在把它包括到 bug 報告前先壓成 tar 的檔案形式並且執行 uuencode(1)(即使 bug 報告比 20KB 小也是可以把它壓起來再 uuencoded,不過並不鼓勵)。要確定把 bug 報告分類為 ports 而且等級為 change-request。 (千萬不要標記為機密報告!)

再說一次,不要包括原始檔,目錄 work,和用 make package 做出來的 package

Note: 以前建議說把新 port 上傳到 ftp 站 (ftp.FreeBSD.org)。 不過因為 incoming/ 這個目錄的讀取權限被關掉,所以這個方法已經不管用了。 這是因為有太多的非法軟體出現在裡面的關係。

我們會檢視這個 port 並把它放進 ports tree,若必要的話會把它歸還。 你的名字也會出現在 FreeBSD 使用者手冊``額外的 FreeBSD 貢獻者''的列表和其它檔案裡。帥吧?!:-)

4.4.3. 慢慢 Porting

好吧。這沒有那麼簡單,而且這個 port 需要一些修改才能工作。 在這章裡我們用將 port 的範例一步步的解釋如何修改它才能用。

4.4.3.1. 這些東西如何運作的

首先,這是當在 port 的目錄下鍵入 make 後的結果的順序,而且你可以把 bsd.port.mk 開在另一個視窗,和這份文件配合著看,這樣比較好懂。

不用擔心看不懂 bsd.port.mk 在做什麼, 也沒很多人懂 ...:->

  1. fetch 這個標的正在執行。 fetch 這個標的負責確定 tarball 有在 DISTDIR 裡。如果 fetchDISTDIR 找不到它要的檔案,它會去 Makefile 裡定義的 MASTER_SITES 裡的 URL 找, 正如同我們主要的 ftp 站 ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/distfiles/ 裡放著認可的檔案做為備份。假如所請求的站可以在網路上直接存取, 它會試著用 FETCH 去抓指名的發行版本。 如果成功就會把檔案存在 DISTDIR 以便繼續進行和再利用。

  2. extract 這個標的正在執行。 它會找放在 DISTDIR 裡的發行版本的檔案 (一般是 gzip 的 tarball) 並把它解到 WRKDIR 所寫的暫時目錄下(預設是 work)。

  3. patch 這個標的正在執行。首先, 施行任何定義在 PATCHFILES 裡的 patch。 再來,照字母順序施行任何在 PATCHDIR (預設是子目錄 patches)。

  4. configure 這個標的正在執行。 它可以從很多不一樣的事裡挑任何一個來做。

    1. 如果存在 scripts/configure, 就執行它。

    2. 如果設定了 HAS_CONFIGUREGNU_CONFIGURE, 就執行 WRKSRC/configure

    3. 如果設定了 USE_IMAKE, 執行 XMKMF(預設值: xmkmf -a)。

  5. build 正在執行。 這個負責往下延伸進到 port 私人的工作目錄 (WRKSRC)並開始編譯。 如果設定了 USE_GMAKE, 就會使用 GNU make, 否則就會用系統的 make

上面這些是預設的動作。也可以定義額外的標的 pre-somethingpost-something, 或是把 scripts 換這這些名字放到子目錄 scripts 裡,這樣它們就會在預設的動做完成前或完成後執行。

例如有一個叫做 post-extract的標的定義在 Makefile 裡,和一個叫做 pre-build 的檔案放在子目錄 scripts 裡,那麼 post-extract 這個標的會在規定的解開 (extraction)動作完成後被呼叫,而 pre-build 這個 script 會在預設的編譯規則前執行。假如動作夠簡單的話, 建議用 Makefile 裡的標的, 因為這樣別人比較容易發現這個 port 需要哪些非預設的動作。

這些預設的動作是由在 bsd.port.mk 的標的 do-something 來執行。例如,用來解開一個 port 的指令寫在 do-extract 這個標的裡。 如果看這個預設的標的不高興的話,可以在 Makefile 裡重新定義 do-something 來修改它。

Note: ``主要的''標的(如 extractconfigure 等等)只是用來確定到這之前的階段已經完成,並呼叫真正的標的或 script, 而且我們並沒有打算改變這些標的。假如要修改解開這個動作,請修改 do-extract,絕不要碰 extract

現在應該知道當鍵入 make 會發生什麼了吧, 讓我們繼續看看創造一個完美的 port 所建議的步驟吧。

4.4.3.2. 取得原始程式碼

(通常)取得壓縮形式的原始碼的 tarball (foo.tar.gzfoo.tar.Z) 並把它複製到 DISTDIR。 有辦法的話就都用主流的程式碼。

如果找不到連線狀況良好的 ftp/http 網站, 或是只找得到格式不標準的站, 可以把它複製一份放在可信任的 ftp 站或是自己控制的 http 伺服器 (例如自己網頁)。確定所設定的 MASTER_SITES 能反應到所選擇的網站。

如果找不到方便或可信賴的地方放 distfile (如果你是 FreeBSD committer,只要放在自己在 freefallpublic_html/ 目錄裡即可), 最後手段就是我們會把它``收藏''在 ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/distfiles/LOCAL_PORTS/。 請參考 MASTER_SITE_LOCAL 所在的位置。 MASTER_SITE_LOCAL。如果不確定要怎麼做到話, 寄封信到 FreeBSD ports mailing list 吧。

如果 port 的 distfile 經常沒理由的改來改去的話, 可以考慮把 distfile 放在你的網頁上並把位址列為第一個 MASTER_SITES。這樣可以避免讓使用者得到 ``checksum mismatch'' 的錯誤訊息, 也讓我們 ftp 站的 maintainers 輕鬆點。如果這個 port 只有一個主要的站,建議你收藏一份備份在自己的站並列為第二個 MASTER_SITES

如果這個 port 需要一些額外的 `patches' 且可以在網路上取得, 也把它們抓下來放到 DISTDIR 吧。 別擔心它門跟主要的程式碼的 tarball 來源不同, 我們有辦法處理(看下面 PATCHFILES 的描述吧)。

4.4.3.3. 修改 port

把 tarball 解到一個私人的目錄下再做任何必要的改變 使得 port 可以在 current 版本的 FreeBSD 正確的編譯。 仔細地追蹤所有過程,這樣才能簡短地把過程自動化。 當 port 完成時,所有東西包括對檔案的刪除、增加, 或是修改都要能藉由一個自動化的 script 或是 patch 檔來執行。

如果該 port 需要使用者做重要的互動/定製才能編譯或安裝, 那就該看看 Larry Wall's 的經典作之一 Configure scripts, 可能的話並自己做些相似的事。新 port 收集的目的在讓最終的使用者 用最少的空間盡可能地做到 ``plug-and-play''。

Note: 除非有明白的宣告,否則所有創造和貢獻給 FreeBSD ports 收集的 patch 檔、scripts和其它的檔案都受到標準 BSD 版權條約的保護。

4.4.3.4. Patching

在準備 port 時,已經被增加或修改的檔案可以用一個遞迴的 diff 整理起來以為了之後用來 patch。所要提供的每一組 patch 都應該收集成一個叫做 patch-xx 的檔案,xx 表示所要提供的 patch 的順序 -- 以字母順序,因此 aa 排第一個,再來是 ab 等等。 這些檔案應該存放在 PATCHDIR 這樣才能被自動提供。所有的 patch 應該相對於 WRKSRC (通常是把 tarball 解開的目錄,編譯也在那裡完成)。 要讓修正和升級容易的話,應該避免讓超過一個 patch 修正同一個檔案 (例如 patch-aapatch-ab 都修正 WRKSRC/foobar.c)。

4.4.3.5. Configuring

把任何附加的 customization 命令包含到 configure script 裡並存在子目錄 scripts 裡。 正如同前面提過的,也可以藉由 Makefile 裡的標的和/或以 pre-configurepost-configure 命名的 script 來做到這件事。

4.4.3.6. 處理使用者的輸入

如果 port 需要使用者輸入以編譯,configuration,或安裝,那就在 Makefile 裡設定 IS_INTERACTIVE。 這樣會允許設定環境變數 BATCH 的使用者在``徹夜編譯''時跳過這個 port (如果使用者設了變數 INTERACTIVE, 那就只有需要互動的程式會被編譯)。

同樣地,假如有合理的預設答案,則建議當變數 PACKAGE_BUILDING 設定時關掉那個互動的 script。 這樣便允許我們為光碟和 ftp 編譯 packages。

4.4.4. Configuring the Makefile

Configuring the Makefile 相當簡單, 再次建議你在開始前看看現有的例子。 使用手冊裡也有一個 Makefile 的樣品,所以看看並跟隨著樣品裡變數和章節的順序, 讓你的 port 對別人來說更具可讀性。

現在,在設計新的 Makefile 時依序考慮下面的問題吧:

4.4.4.1. 原始程式碼

它有以標準 gzip 的 tarball 形式放在 DISTDIR 嗎?如果有,跳到下一步吧。如果沒有, 檢視 port 發行的檔案是哪種星球的格式來決定要用 EXTRACT_CMDEXTRACT_BEFORE_ARGSEXTRACT_AFTER_ARGSEXTRACT_SUFX,或 DISTFILES 中的任一變數。(當 tarball 被標準的壓縮程式而不是 gzip 壓縮時, 最常見的格式是 EXTRACT_SUFX=.tar.Z。)

在最糟的情況下,可以簡單的創造一個自己的 do-extract 標的來蓋過預設的, 雖然這很少見,但如果出現就只得這麼做了。

4.4.4.2. DISTNAME

DISTNAME 設為 port 的名稱。 預設的規定是希望發行版本的檔案列表以 DISTNAMEEXTRACT_SUFX 的形式命名,對一個正常的 tarball 而言, 一個名為 DISTNAME=foozolix-1.0 的 port,其檔案列表應該長得像 foozolix-1.0.tar.gz

預設的規則也期望把 tarball(s) 解到子目錄 work/DISTNAME 下, 如 work/foozolix-1.0/

當然這些都可以被推翻;它只是代表了大部份省時的共同預設值。 對一個需要多個發行版本的檔案的 port,只要明確的設定 DISTFILES即可。如果只有一小組 DISTFILES 是實際可解開的檔案, 那就把它們寫在 EXTRACT_ONLY, 這樣當要解開時便會取代 DISTFILES 的列表, 而其它的檔案則會留在 DISTDIR 以後再用。

4.4.4.3. PKGNAME

如果 DISTNAME 不符合 好的 package 名稱的指南, 那就設定 PKGNAME 這個變數來取個好名字吧。 要知道更多細節就看上面那個指南吧。

4.4.4.4. CATEGORIES

當一個 package 做好時,它會放在 /usr/ports/packages/All 下並連結到 /usr/ports/packages 下的一個或多個子目錄。這些子目錄的名稱在變數 CATEGORIES 裡有詳細說明。 這是打算讓使用者在 ftp 站或光碟上費力渡過一堆 package 來找東西時容易一點。請看一下現有的分類(category)並選一個最適合你的 port 的。

這也決定了 port 會被收入到 port tree 的位置。 如果在這變數裡放進了太多的分類,將假設 port 的檔案會被放在第一個分類的子目錄裡。分類這個章節裡有更多關於如何 挑選正確的分類的討論。

如果 port 真的不屬於現有的任何分類,就自己創造一個吧。 在這情況下請寄封信到 FreeBSD ports mailing list 提議一個新的分類。

Note: 檢查種類名稱時不會有任何錯誤。如果打錯種類名稱,make package 會很樂意幫你生出一個新目錄, 所以要小心點!

4.4.4.5. MASTER_SITES

把 ftp/http-URL 裡指出來源 tarball 所在目錄的部份記錄在 MASTER_SITES裡。 別忘了跟在後面的斜線 (/)!

如果在系統裡找不到發行版本的檔案的話,make 這個巨集指令在用 FETCH 抓檔時會試著用到這份清單。

建議在列表裡放多一點站,最好來自各洲。 這樣會避免大範圍的網路問題,我們甚至計畫增加可自動決定最近的 主站並從那裡抓取的支援!

如果來源的 tarball 是下面幾個普遍的檔案庫: X-contrib,GNU,Perl CPAN,TeX CTAN,或 Linux Sunsite, 可以利用 MASTER_SITE_XCONTRIBMASTER_SITE_GNUMASTER_SITE_PERL_CPANMASTER_SITE_TEX_CTAN,和 MASTER_SITE_SUNSITE 來做參考。只要把 MASTER_SITE_SUBDIR 設到檔案庫裡的路徑。 這有個例子:

    MASTER_SITES=         ${MASTER_SITE_XCONTRIB}
    MASTER_SITE_SUBDIR=   applications

使用者也可在 /etc/make.conf 設定變數 MASTER_SITE_* 來蓋過我們的選擇, 然後用他們喜歡的 mirror 站來代替。

4.4.4.6. PATCHFILES

如果你的 port 需要一些額外的 patch,而又可以由 ftp 或 http 取得,那就把 PATCHFILES 設為檔案名稱, 而 PATCH_SITES 為包含該檔案的目錄的 URL (格式跟 MASTER_SITES 一樣)。

假如因為 patch 包含一些額外的路徑,使得 patch 並不相對於程式碼的樹狀結構的最上層(如 WRKSRC), 那就適當地設定 PATCH_DIST_STRIP。舉個例, 假如 patch 裡所有的路徑前面都多加了 foozolix-1.0/,那就設成 PATCH_DIST_STRIP=-p1

別擔心 patch 被壓縮起來,如果檔案名稱是以 .gz.Z 結尾, 那它們會自動被解壓。

如果 patch 是跟其它檔案,如文件,一起以 gzip 形式的 tarball 發行,那就不能用 PATCHFILES。 在這情形下,就把 patch tarball 的名稱和位置加進 DISTFILESMASTER_SITES。 然後,從 pre-patch 的標的提供 patch, 不論是從那個標的執行,或是把 path 複製到目錄 PATCHDIR 裡並稱之為 patch-xx

Note: 要注意 tarball 已經跟著正常的程式碼被解開了, 所以如果是以 gzip 或 compress 壓起來的 tarball 也不必要再解開。 如果你把它解開了,特別小心不要把已經存在目錄裡的東西改寫掉。 同樣也別忘了在 pre-clean 的標的裡加個命令來移除複製的 patch。

4.4.4.7. MAINTAINER

在這留下你的郵件地址吧。拜托 :-)

要知道有關 maintainer 負責事項詳細的描述的話, 參考 MAINTAINER on Makefiles 的章節。

4.4.4.8. Dependencies

很多 port 都依賴其它的 port。有五個變數可用來確定所有需要的 部份都已經在使用者的機器裡了。在一般情形下還有一些預先支援的 dependency 變數,加上了多一些東西來控制 dependency 的表現。

4.4.4.8.1. LIB_DEPENDS

這個變數詳述了該 port 所依賴的共享函式。 它是個多組 lib:dir[:target] 的列表,lib 是共享函式的名稱, dir 是萬一系統裡沒有時可用來找到它的目錄,而 target 是在那目錄下所呼叫的標的。 例如

     LIB_DEPENDS=
                  jpeg.9:${PORTSDIR}/graphics/jpeg:install
會檢查主版本為 9 的共享函式 jpeg,且假如找不到的話, 便會往下到 ports tree 的子目錄 graphics/jpeg 建立並安裝它。假如 targetDEPENDS_TARGET 一樣的話則可省略(預設是 install)。

Note: lib 的部份是一個要給 ldconfig -r | grep -wF 的參數。 在這個變數裡不可有正規表示式(regular expression)。

Denpendency 會被檢查兩次,一次是在 extract 的標的裡,一次是在 install 的標的。 同樣地,dependency 的名稱也會放到 package 裡這樣當使用者系統裡沒有時 pkg_add 便會自動把它裝上。

4.4.4.8.2. RUN_DEPENDS

這個變數詳述了這個 port 執行時所依賴的可執行檔或檔案。 它是個多組 path:dir[:target] 的列表, path 是執行檔或檔案的名稱, dir 是萬一系統裡沒有時可用來找到它的目錄, 而 target 是在該目錄所呼叫的標的。 如果 path 以短斜線開頭 (/),那它會被看做一個檔案並用 test -e 這個命令來測試其是否存在; 否則就認為是可執行檔並用 which -s 來決定程式是否在使用者的搜尋路徑裡。

例如,

    RUN_DEPENDS=   ${PREFIX}/etc/innd:${PORTSDIR}/news/inn \
                   wish8.0:${PORTSDIR}/x11-toolkits/tk80

會檢查 /usr/local/etc/innd 這個檔案或目錄是否存在,如果找不到就會去 port tree 的子目錄 news/inn 把它建立並裝好。 它也會看在搜尋路徑裡是否有個叫 wish8.0 的可執行檔,沒找到的話就到 port tree 的子目錄 x11-toolkits/tk80 裡建立並安裝好。

Note: 在這裡 innd 實際上是個可執行檔; 如果一個可執行檔所在目錄並不是一般使用者的搜尋路徑, 那就應該用完整的路徑表示。

Dependency 會在 install 的標的裡檢查。同樣地,dependency 的名稱也要放到 package 裡, 這樣當使用者的系統沒有時 pkg_add 這個命令便會自動安裝它。如果 target 的部份和 DEPENDS_TARGET 相同則可省略。

4.4.4.8.3. BUILD_DEPENDS

這個變數詳述了這個 port 編譯時需要的執行檔或檔案。 就跟 RUN_DEPENDS 一樣,它是由多組 path:dir[:target] 構成的列表。 例如

     BUILD_DEPENDS=
                  unzip:${PORTSDIR}/archivers/unzip
會檢查是否有個執行檔 unzip, 沒有的話就到 port tree 的子目錄 archivers/unzip 下建立並裝它。

Note: 在這裡``建立''指得是從解壓到編譯整個過程。 Dependency 會在 extract 的標的裡檢查。 假如 target 的部份跟 DEPENDS_TARGET 一樣則可以省略。

4.4.4.8.4. FETCH_DEPENDS

這個變數詳述在取得該 port 時所需的執行檔或檔案。 跟前兩個一樣,它也是由多組 path:dir[:target] 組成的列表。 例如

     FETCH_DEPENDS=
                  ncftp2:${PORTSDIR}/net/ncftp2
會檢查 ncftp2 這個執行檔,沒有的話就到 port tree 下的子目錄 net/ncftp2 建立並安裝它。

Dependency 會在 fetch 的標的裡檢查。假如 target 的部份跟 DEPENDS_TARGET 一樣則可省略。

4.4.4.8.5. DEPENDS

如果有個 dependency 不屬於上面四種之一,或是你的 port 需要其它 port 解開的程式碼才能安裝,那就用這個變數吧。 這是一份 dir[:target] 的列表, 不像前面四個,這裡沒什麼好檢查的。 如果 target 的部份跟 DEPENDS_TARGET 一樣則可省略。

4.4.4.8.6. 一般的 dependency 變數

如果 port 需要有安裝 X Window System 才能安裝, 請定義 USE_XLIB=yes(它被包含在 USE_IMAKE裡)。假如 port 需要 GNU make 以取代 BSD make ,請定義 USE_GMAKE=yes。假如 port 需要執行到 GNU autoconf,請定義 USE_AUTOCONF=yes。假如 port 要用最新的 qt toolkit,請定義 USE_QT=yes。 如果 port 需要第五版的 perl 程式語言,那就用 USE_PERL5=yes。(最後一個最重要, 因為一些版本的 FreeBSD 把 perl5 弄為陽春版的一部份,而其它的沒有。)

4.4.4.8.7. Dependency 的注意事項

在前面提過,當需要 dependency 時會呼叫的預設標的是 DEPENDS_TARGET。預設值是 install。這是個使用者的變數;它從未在 port 的 Makefile 裡定義。如果 port 需要特別的方法來處裡 dependency,用 *_DEPENDS:target 的部份而不要重新定義 DEPENDS_TARGET

當鍵入 make clean後, 它的 dependency 也會被自動清除。如果不想清除的話, 可以設定環境變數 NOCLEANDEPENDS

要無條件地依賴其它 port,照慣例是以字串 nonexistentBUILD_DEPENDSRUN_DEPENDS 的第一個欄位。只有當需要其它 port 的程式碼時才這麼做。也可以經常用這個標的來省下編譯的時間。 例如

    BUILD_DEPENDS=   /nonexistent:${PORTSDIR}/graphics/jpeg:extract
每次都會到 JPEG 這個 port 裡並解開它。

除非沒有別的辦法可以完成所想要表現的,否則別用 DEPENDS。它會使得其它 port 每次都被編譯( 還有安裝,照預設值的話),而且 dependency 也會進到 package 裡。 如果真的需要,建議你寫成 BUILD_DEPENDSRUN_DEPENDS 代替 -- 至少這樣目的較清楚。

4.4.4.9. 建立的機制

如果 package 要用到 GNU make,設定 USE_GMAKE=yes。如果 package 要用到 configure,設定 HAS_CONFIGURE=yes。如果 package 要用到 GNU configure,設定 GNU_CONFIGURE=yes (這包含 HAS_CONFIGURE)。如果要對 configure 下一些額外的參數(對 GNU configure 預設參數為 --prefix=${PREFIX},非 GNU configure 則無),把這些額外的參數設在 CONFIGURE_ARGS 裡。如果 package 要用到 GNU autoconf,設定 USE_AUTOCONF=yes。這包含了 GNU_CONFIGURE,而且會使 autoconfconfigure 之前執行。

如果 package 是用 imakeImakefiles 產生 Makefiles 的 X 應用程式,就設定 USE_IMAKE=yes。 這樣會使 configure 的階段自動做 xmkmf -a。 如果 -a 這個旗標會對 port 造成問題,就設成 XMKMF=xmkmf。如果 port 用imake 這個命令卻又認不得 install.man 這個指標, 那就應該設 NO_INSTALL_MANPAGES=yes。還有, 最初的 port 的作者名字應該被記上一筆。 :->

如果 port 的程式碼裡的 Makefileall 以外的標的作為主要的建立標的, 那就設成 ALL_TARGET。同樣的道理也適用在 installINSTALL_TARGET

4.4.5. 特殊考量

還有一些事項是創造一個新 port 時所要考慮的。 這個章節解釋了大部份的一般情形。

4.4.5.1. ldconfig

如果 port 安裝了一個共享函式,在 Makefile 裡加上 post-install 這個標的, 內容為在新函式安裝的目錄上(通常是 PREFIX/lib)執行 ${LDCONFIG} -m 以將其註冊進共享函式暫存區裡。

此外,增加一對 @exec /sbin/ldconfig -m@unexec /sbin/ldconfig -R 的組合到檔案 pkg/PLIST 裡以便安裝此 package 的使用者可以立刻開始用這個函式, 而解除安裝也不會讓系統以為函式還賴著不走。 這幾行應該緊跟在寫著函式名稱該行後面,如下:

    lib/libtvl80.so.1
    @exec /sbin/ldconfig -m %D/lib
    @unexec /sbin/ldconfig -R

絕對絕對,千萬不要Makefilepkg/PLIST 裡加上一行沒有任何參數的 ldconfig。 這樣會使得共享函式暫存區裡重設成只含 /usr/lib 裡的內容,而且會像皇帝般地弄亂使用者的系統("救命, 我裝完這個 port 後 xinit 就再也不動了")。 任何這麼做的人都會被槍殺,用生鏽的刀大切 65,536 塊, 被一群烏鴉把肝臟扯出剁碎,然後爛死在地獄深處,萬劫不復 (不一定會照這順序來...)

4.4.6. ELF support

自從 FreeBSD 在 3.0-RELEASE 後立刻改為 ELF binary format, 我們要更換很多建立共享函式的 port 來支援 ELF。 複雜的是 3.0 可以同時執行 ELF 和 a.out, 而且我們希望非正式地支援 2.2,越久越好。 下面這些指引如何更換只支援 a.out 的 port 以同時支援 a.out 和 ELF。

這份列表的某些部份只有在轉換過程中才適用, 不過仍然留在這當作參考以免你遇到想要升級的老 port。

4.4.6.1. 把 a.out 的函式移開

任何 a.out 的函式都應該移開 /usr/local/lib 到一個 aout 的子目錄。(如果不把它們移走, ELF ports 會很樂意把 a.out 的函式覆寫掉。) 在 3.0-CURRENT 的 src/Makefile 裡的 move-aout-libs (從 aout-to-elf 呼叫)這個標的會幫你做這件事。 它只會移動 a.ou 的函式,所以在同時有 ELF 和 a.out 函式的標準目錄呼叫它很安全的。

4.4.6.2. 格式

Ports tree 會依照系統的格式來建立 package。 表示由 `objformat` 傳回的值決定在 2.2 是 a.out 而在 3.0是 a.out 或 ELF。同樣地,一旦使用者將 a.out 的函式移到一個子目錄下,將不支援建立 a.out 的函式。(換句話說,如果你真的知道在做什麼的話仍然可以運作, 不過得靠自己來。)

Note: 如果 port 只能在 a.out 環境執行,把 BROKEN_ELF 設為一說明原因的字串。 在 ELF 的系統上建立 port 時會把這類的跳過去。

4.4.6.3. PORTOBJFORMAT

bsd.port.mk 會把 PORTOBJFORMAT 設為 aoutelf 並將它輸出到環境變數 CONFIGURE_ENVSCRIPTS_ENVMAKE_ENV。(在 2.2-STABLE 裡永遠是 aout)。它也會被以 PORTOBJFORMAT=${PORTOBJFORMAT} 傳入 PLIST_SUB。 (看下面幾行 ldconfig 的說明。)

這個變數設定使用 bsd.port.mk 裡這行:

    PORTOBJFORMAT!= test -x /usr/bin/objformat && /usr/bin/objformat || echo aout

Ports 的 make 程序應該用這個變數來決定該做什麼。 然而,假如 port 的 configure script 已經自動查出為 ELF 的系統,那就沒有必要參照 PORTOBJFORMAT

4.4.6.4. 建立共享函式

接下來的在為 a.out 和 ELF 的系統處理共享函式的部份有很多不同。

  • 共享函式的版本

    一個 ELF 的共享函式應該名為 libfoo.so.MM 是個單一的版本編號,而 a.out 的共享函式應該名為 libfoo.so.M.NM 是主版本編號而 N 是次版本編號。別把它們搞混了; 千萬不要安裝一個叫做 libfoo.so.N.M 的 ELF 共享函式,或是叫做 libfoo.so.N 的 a.out 共享函式(或是符號連結)。

  • 連結器(linker)的命令列

    假設 cc -sharedld 用起來更直接,那唯一不同的地方就是在 ELF 系統的命令列要加上 -Wl,-soname,libfoo.so.M

還需要安裝一個符號連結(symlink)從 libfoo.solibfoo.so.N 讓 ELF 的連結器快樂點。因為它也應該列在 PLIST 裡,而且在 a.out 的情況下也不會造成傷害(有些 port 甚至動態載入時仍需要個連結),你只須要做個連結而不必管 PORTOBJFORMAT 的設定。

4.4.6.5. LIB_DEPENDS

LIB_DEPENDS 裡的次編號應該從所有 port 的 Makefiles 移除,也要移除所有 regexp 的支援。 (例如,foo\\.1\\.\\(33|40\\) 變成 foo.2。)可以用 grep -wF 把它們找出來。

4.4.6.6. PLIST

假如 a.out 的主編號是 0 則 PLIST 應該包含短的(ELF)共享函式名稱,反之則為長的(a.out)共享函式名稱。 bsd.port.mk 會在當 PORTOBJFORMAT 等於 aout 時自動於短的共享函式該行後加上 .0,而當 PORTOBJFORMAT 等於 elf 時刪去長的共享函式之次編號。

萬一你真的必須在 ELF 的系統上安裝兩個版本的共享函式, 或是要在 a.out 的系統安裝一個版本(例如安裝其它的作業系統的相容函式的 port),就設定變數 NO_FILTER_SHLIBS。 這樣會關掉上一段裡所提到對 PLIST 編輯。

4.4.6.7. ldconfig

ldconfig 那行在 Makefiles 裡應該讀取:

    ${SETENV} OBJFORMAT=${PORTOBJFORMAT} ${LDCONFIG} -m ....

PLIST 裡應該讀取:

    @exec /usr/bin/env OBJFORMAT=%%PORTOBJFORMAT%% /sbin/ldconfig -m ...
    @unexec /usr/bin/env OBJFORMAT=%%PORTOBJFORMAT%% /sbin/ldconfig -R

這是用來確定正確的 ldconfig 的呼叫是決定在 package 的格式而不是系統的預設格式:

4.4.7. MASTERDIR

如果 port 需要藉變數值的不同來建立稍微不同版本的 package (例如,resolution 或 paper size),就替每個 package 開個子目錄讓使用者更容易知道該做什麼,不過儘量讓各個 port 之間共用 的檔案越多越好。如果你很巧妙地運用變數, 那幾乎只須要在其中一個目錄放個簡短的 Makefile 便可。在這些單一的 Makefiles,可以用 MASTERDIR 來說明其它的檔案在哪個目錄。 同樣地,使用一個變數做為 PKGNAME 的一部份,讓每個 package 擁有不同的名稱。

一個例子是最佳的說明。這是 japanese/xdvi300/Makefile 的一部份;

    PKGNAME=       ja-xdvi${RESOLUTION}-17
     :
    # default
    RESOLUTION?=   300
    .if ${RESOLUTION} != 118 && ${RESOLUTION} != 240 && \
           ${RESOLUTION} != 300 && ${RESOLUTION} != 400
           @${ECHO} "Error: invalid value for RESOLUTION: \"${RESOLUTION}\""
           @${ECHO} "Possible values are: 118, 240, 300 (default) and 400."
           @${FALSE}
    .endif

japanese/xdvi300 也有所有正式的 patch, package 檔案....等等。如果在此鍵入 make, 它會取用預設值做為 resolution 的值(300)並照一般情形建立 port。

對其它 resolution 來說,這是完整的 xdvi118/Makefile

    RESOLUTION=     118
    MASTERDIR=      ${.CURDIR}/../xdvi300
    
    .include ${MASTERDIR}/Makefile

(xdvi240/Makefilexdvi400/Makefile 差不多)。 MASTERDIR 的定義告訴 bsd.port.mkPATCHDIRPKGDIR 這類正式的子目錄的設定在 xdvi300 下面。RESOLUTION=118 這行會蓋掉 xdvi300/Makefile 裡的 RESOLUTION=300,而 port 會以 resolution 為 118 來建立。

4.4.8. 共享函式的版本

首先,請先讀讀共享函式版本的政策 以了解一般如何決定共享函式的版本。不要盲目地以為軟體作者知道他們在做什麼; 很多都不知道。謹慎地考慮這些細節是很重要的, 因為我們試著讓一堆可能不相容的軟體一起存在,這是相當獨特的情況。 在以前粗心的 port 曾經在共享函式的認定上造成很大的困擾。 (有沒有想過為什麼 jpeg-6b 的共享函式版本是 9.0?)。不信的話就到 FreeBSD ports mailing list 問問。大部份的時候, 你的工作最後就是決定共享函式的版本和做出適當的 patch 來做這件事。

然而,如果有個 port 跟 port tree 裡同一軟體的版本不同的話, 那就更複雜了。簡單的說,FreeBSD 的實行並不允許使用者指定連結器連結哪一版本的共享函式 (連結器永遠都挑選編號最高的版本)。這表示假如系統裡有 libfoo.so.3.2libfoo.so.4.0, 就沒有辦法叫連結器連結個別的應用程式到 libfoo.so.3.2。It is essentially completely overshadowed in terms of compilation-time linkage.在這情形下, 唯一的解決辦法就是更改共享函式基礎部份的名稱。 例如,把 libfoo.so.4.0 改成 libfoo4.so.1.0 這樣 3.2 和 4.0 的版本便都可以從其它 port 連結。

4.4.9. Manpages

MAN[1-9LN] 這些變數會自動把任何 manpage 加進 pkg/PLIST (這表示絕不要 把 manpage 列在 PLIST 裡 -- generating PLIST 有更多料)。 在安裝的階段是否會壓縮 manpage 決定於 /etc/make.conf 裡是否有設定 NOMANCOMPRESS

如果要用符號連結(symlink)或實體連結(hardlink)為 manpage 安裝多個名稱,則應該用 MLINKS 這個變數來確認。 Port 所安裝的連結會被 bsd.port.mk 毀掉或重新創造以確定它指向正確的檔案。任何列在 MLINKS 的 manpage 絕不可列在 PLIST 裡。

要指定 manpage 是否在安裝時壓縮可用變數 MANCOMPRESSED。這個變數有三個值可用, yesnomaybeyes 表示 manpages 已經以壓縮過的形式安裝,no 表示沒有壓縮, 而 maybe 表示早已遵守 NOMANCOMPRESS 的值,所以 bsd.port.mk 並不需要做什麼特別的事了。

假如設定了 USE_IMAKE 而且沒有設定 NO_INSTALL_MANPAGES,則 MANCOMPRESSED 會自動設為 yes, 反之則設為 no。沒有必要明確地定義它, 除非預設值不適合你的 port。

如果你的 port 的主要樹狀結構位於 PREFIX 以外,可以用 MANPREFIX 來設定。 同樣地,如果只有特定章節的 manpages 在不標準的地方, 例如一些 Perl 模組(module)的 port,可以利用 MANsectPREFIX (sect1-9LN 其中之一) 來設定個別的主要路徑。

如果 manpages 裝在指定語言的子目錄, 把 MANLANG 設為該語言的名稱。 這個變數的預設值是 "" (也就是只有英文)。

這個例子把上面說的都放一起了。

    MAN1=          foo.1
    MAN3=          bar.3
    MAN4=          baz.4
    MLINKS=        foo.1 alt-name.8
    MANLANG=       "" ja
    MAN3PREFIX=    ${PREFIX}/share/foobar
    MANCOMPRESSED= yes

這表示這個 port 裝了六個檔案;

    ${PREFIX}/man/man1/foo.1.gz
    ${PREFIX}/man/ja/man1/foo.1.gz
    ${PREFIX}/share/foobar/man/man3/bar.3.gz
    ${PREFIX}/share/foobar/man/ja/man3/bar.3.gz
    ${PREFIX}/man/man4/baz.4.gz
    ${PREFIX}/man/ja/man4/baz.4.gz

附加的 ${PREFIX}/man/man8/alt-name.8.gz 可能會也可能不會被 port 安裝。無論如何, foo(1) manpage 和 alt-name(8) manpage 會用一個符號連結(symlink)接起來。

4.4.10. 需要 Motif 的 ports

有很多程式需要 Motif 的函式(可以從一些商業性的廠商取得, 雖然有報導指出很多應用程式也可以用免錢的彷製軟體 x11-toolkits/lesstif 來執行)來編譯。 因為這是個很普及的 toolkit 而且它的授權通常允許使用者重覆散佈靜態連結的 binary,我們為處理需要 Motif 的 port 做了些準備,就某觀點而言, 這樣不論是動態(為了那些從 port 編譯的人)或是靜態(為了散佈 package 的人) 連結的 binary 都可以容易地編譯。

4.4.10.1. REQUIRES_MOTIF

如果 port 需要 Motif,在 Makefile 裡定義這個變數。這樣可以防止沒有 Motif 的人試圖去編譯它。

4.4.10.2. MOTIFLIB

這個變數在 bsd.port.mk 設定, 用來適當的參考 Motif 的函式。不論 Motif 的函式是在 MakefileImakefile 裡被參考,請 patch 程式碼以使用這個變數。

有兩種一般的情況:

  • 如果 port 在它的 MakefileImakefile-lXm 來參考 Motif 的函式,只要把它換成 ${MOTIFLIB}

  • 如果 port 在它的 ImakefileXmClientLibs,把它改成 ${MOTIFLIB} ${XTOOLLIB} ${XLIB}

注意 MOTIFLIB (通常)展開成 -L/usr/X11R6/lib -lXm/usr/X11R6/lib/libXm.a,所以沒有必要在前面加上 -L-l

4.4.11. X11 的字型

如果 port 會為 X Window system 安裝字型,把字型放到 X11BASE/lib/X11/fonts/local 裡。 這個目錄是 XFree86 release 3.3.3 新有的。如果不存在的話請開一個, 並印出一個訊息催促使用者升級他們的 XFree86 到 3.3.3 或更新的, 或者至少把這個目錄加進 /etc/XF86Config 裡的字型路徑。

4.4.12. Info 檔

新版的 texinfo (包括在 2.2.2-RELEASE 和之後的) 包含一個實用的程式叫做 install-info, 用來增加或刪除記錄到 dir 裡。如果 port 安裝任何的 info 文件,請跟著這些指引,這樣 port/package 才會正確地更新使用者的 PREFIX/info/dir 檔案。 (抱歉這一章這麼長,不過把所有的 info 檔案編在一起是必要的。 如果做的正確將會產生一個漂亮的列表, 所以請跟我一起忍吧!

首先,這是你(身為一個 porter)得知道的

    % install-info --help
    install-info [OPTION]... [INFO-FILE [DIR-FILE]]
      Install INFO-FILE in the Info directory file DIR-FILE.
    
    Options:
    --delete          Delete existing entries in INFO-FILE;
                        don't insert any new entries.
     :
    --entry=TEXT      Insert TEXT as an Info directory entry.
     :
    --section=SEC     Put this file's entries in section SEC of the directory. :

Note: 這個程是並不會真的安裝 info 檔案; 它只是在 dir 檔裡插入或刪除記錄。

這裡有七個步驟的程序用來更換 port 來使用 install-info。我會用 editors/emacs 當例子。

  1. 檢視 texinfo 程式碼並做個 patch 來把 @dircategory@direntry 加進沒有這兩個敘述的檔案。這是我的 patch 的一部份:

        --- ./man/vip.texi.org  Fri Jun 16 15:31:11 1995
        +++ ./man/vip.texi      Tue May 20 01:28:33 1997
        @@ -2,6 +2,10 @@
                     
         @setfilename ../info/vip
         @settitle VIP
        +@dircategory The Emacs editor and associated tools
        +@direntry
        +* VIP: (vip).          A VI-emulation for Emacs.
        +@end direntry
                     
         @iftex
         @finalout
         :

    這格式應該是一看就懂的。很多作者把 dir 檔留在 source tree 裡,該檔包含所有所需的記錄, 所以在要寫一個自己的檔前先留意一下。同樣地,確定有查看相關的 ports 並讓章節的名稱和記錄一致(我們建議所有記錄的正文從第四個 tab 停止的地方開始)。

    Note: 要注意每個檔案只能有一筆 info 記錄因為 install-info --delete 的一個錯誤導致如果 的部份有多個記錄也只會砍掉第一個。

    可以把 dir 的記錄給 install-info 當參數用 (--section--entry) 以取代 patch texinfo 原始碼。我不認為這對 port 來說是好方法, 因為這樣必須把同樣的東西複製到三個地方 (MakefilePLIST@exec/@unexec; 看下面)。可是如果有日文的(或是其它雙位元編碼的) info 檔, 這樣 install-info 必須用額外的參數, 因為 makeinfo 不能處理那些 texinfo 程式碼。 (拿 japanese/skkMakefilePLIST 當例子看看怎麼做)。

  2. 回到 port 的目錄執行 make clean; make 並確認 info 檔已經從 texinfo 程式碼重新產生。 既然 texinfo 程式碼比 info 檔還新,當鍵入 make 時就應該被重新建立;不過很多 Makefiles 並沒有為 info 檔包含正確的 dependency。在 emacs 的情況,便必須 patch 主要的Makefile.in 這樣它才會往下到 man 的子目錄重新建立 info。

        --- ./Makefile.in.org   Mon Aug 19 21:12:19 1996
        +++ ./Makefile.in       Tue Apr 15 00:15:28 1997
        @@ -184,7 +184,7 @@
         # Subdirectories to make recursively.  `lisp' is not included
         # because the compiled lisp files are part of the distribution
         # and you cannot remake them without installing Emacs first.
        -SUBDIR = lib-src src
        +SUBDIR = lib-src src man
         
         # The makefiles of the directories in $SUBDIR.
         SUBDIR_MAKEFILES = lib-src/Makefile man/Makefile src/Makefile oldXMenu/Makefile
         lwlib/Makefile
        --- ./man/Makefile.in.org       Thu Jun 27 15:27:19 1996
        +++ ./man/Makefile.in   Tue Apr 15 00:29:52 1997
        @@ -66,6 +66,7 @@
         ${srcdir}/gnu1.texi \
         ${srcdir}/glossary.texi
                     
        +all: info
         info: $(INFO_TARGETS)
                     
         dvi: $(DVI_TARGETS)

    第二塊(hunk)是必要的因為子目錄 man 預設的標的叫做 info,而主要的 Makefile 會呼叫 all。 我也刪除了 info 檔的安裝因為 /usr/share/info 裡已經有一個同名的檔案 (那個 patch 沒有在這秀出來)。

  3. 如果 Makefile 裡有個地方會安裝 dir 檔,砍了它。你的 port 可能不會麼這做。 同樣地,刪去任何以別的方式弄髒 dir 檔的命令。

        --- ./Makefile.in.org   Mon Aug 19 21:12:19 1996
        +++ ./Makefile.in       Mon Apr 14 23:38:07 1997
        @@ -368,14 +368,8 @@
                if [ `(cd ${srcdir}/info && /bin/pwd)` != `(cd ${infodir} && /bin/pwd)` ]; \
                then \
                  (cd ${infodir};  \
        -          if [ -f dir ]; then \
        -            if [ ! -f dir.old ]; then mv -f dir dir.old; \
        -            else mv -f dir dir.bak; fi; \
        -          fi; \
                   cd ${srcdir}/info ; \
        -          (cd $${thisdir}; ${INSTALL_DATA} ${srcdir}/info/dir ${infodir}/dir); 
        \
        -          (cd $${thisdir}; chmod a+r ${infodir}/dir); \
                   for f in ccmode* cl* dired-x* ediff* emacs* forms* gnus* info* message* mh-e* sc* vip*; do \
                     (cd $${thisdir}; \
                      ${INSTALL_DATA} ${srcdir}/info/$$f ${infodir}/$$f; \
                      chmod a+r ${infodir}/$$f); \
  4. (這個步驟只有在修改一個現存的 port 時才需要。) 看一下 pkg/PLIST 並刪去任何試著修改 info/dir 的部份。 可能在 pkg/INSTALL 或其它檔案裡, 所以大規模地搜索吧。

        Index: pkg/PLIST
        ===================================================================
        RCS file: /usr/cvs/ports/editors/emacs/pkg/PLIST,v
        retrieving revision 1.15
        diff -u -r1.15 PLIST
        --- PLIST       1997/03/04 08:04:00     1.15
        +++ PLIST       1997/04/15 06:32:12
        @@ -15,9 +15,6 @@
         man/man1/emacs.1.gz
         man/man1/etags.1.gz
         man/man1/ctags.1.gz
        -@unexec cp %D/info/dir %D/info/dir.bak
        -info/dir
        -@unexec cp %D/info/dir.bak %D/info/dir
         info/cl
         info/cl-1
         info/cl-2
  5. Makefile 加一個 post-install 的標的來呼叫 伴隨著要安裝的 info 檔的 install-info。 (不需要再自己建一個 dir 檔了;當它不存在時 install-info 會自動創造一個。)

        Index: Makefile
        ===================================================================
        RCS file: /usr/cvs/ports/editors/emacs/Makefile,v
        retrieving revision 1.26
        diff -u -r1.26 Makefile
        --- Makefile    1996/11/19 13:14:40     1.26
        +++ Makefile    1997/05/20 10:25:09     1.28
        @@ -20,5 +20,8 @@
         post-install:
         .for file in emacs-19.34 emacsclient etags ctags b2m
                strip ${PREFIX}/bin/${file}
         .endfor
        +.for info in emacs vip viper forms gnus mh-e cl sc dired-x ediff ccmode
        +       install-info ${PREFIX}/info/${info} ${PREFIX}/info/dir
        +.endfor
                     
         .include <bsd.port.mk>
  6. 編輯 PLIST 並加上等效的 @exec 敘述還有因應 pkg_delete@unexec

        Index: pkg/PLIST
        ===================================================================
        RCS file: /usr/cvs/ports/editors/emacs/pkg/PLIST,v
        retrieving revision 1.15
        diff -u -r1.15 PLIST
        --- PLIST       1997/03/04 08:04:00     1.15
        +++ PLIST       1997/05/20 10:25:12     1.17
        @@ -16,7 +14,14 @@
         man/man1/etags.1.gz
         man/man1/ctags.1.gz
        +@unexec install-info --delete %D/info/emacs %D/info/dir
         :
        +@unexec install-info --delete %D/info/ccmode %D/info/dir
         info/cl
         info/cl-1
        @@ -87,6 +94,18 @@
         info/viper-3
         info/viper-4
        +@exec install-info %D/info/emacs %D/info/dir
         :
        +@exec install-info %D/info/ccmode %D/info/dir
         libexec/emacs/19.34/i386--freebsd/cvtmail
         libexec/emacs/19.34/i386--freebsd/digest-doc

    Note: @unexec install-info --delete 這個命令 必須列在 info 檔之前,這樣才可以讀取這個檔。同樣地, @exec install-info 這個指令要在 info 檔 和創造 dir 檔的命令 @exec 之後。

  7. 測試並誇讚你的成果吧。 :-)。 在每個步驟開始之前和之後檢查 dir 檔。

4.4.13. 子目錄 pkg/

這有一些有關 pkg/ 而我們未曾提及的小技巧, 有時後它們也會派上用場。

4.4.13.1. MESSAGE

如果要對安裝的人顯示訊息的話,可以把訊息放在 pkg/MESSAGE 裡。這個功能在當 pkg_add 之後要顯示額外的安裝步驟, 或是要顯示授權資訊時往往相當有用。

Note: pkg/MESSAGE 這個檔不需要加進 pkg/PLIST 裡。同樣地,如果使用者是用 port 而非 package,那它就不會自動顯示出來,所以你應該自己在 post-install 的標的裡把它顯示出來。

4.4.13.2. INSTALL

如果 port 在用 pkg_add 安裝 binary package 時需要執行命令,可以藉由 pkg/INSTALL 這個 script 來達成。這個 script 會自動加進 package 裡,且會藉由 pkg_add 執行兩次。第一次是 INSTALL ${PKGNAME} PRE-INSTALL 而第二次是 INSTALL ${PKGNAME} POST-INSTALL$2 可用以確定 script 正進入何種模式。 PKG_PREFIX 這個環境變數會設定為 package 的安裝目錄。 查看 pkg_add(1) 以獲得更多訊息。

Note: 如果用 make install 安裝 port,則此 script 不會自動執行。如果你必須依賴它的執行,則必須在 port 的 Makefile 裡明確地呼叫它。

4.4.13.3. REQ

如果 port 需要決定它是否應該安裝,則可建一個 pkg/REQ ``必備條件(requirements)'' 的 script。在安裝/反安裝時它會被自動採用以決定 安裝/反安裝程序是否繼續進行。

4.4.13.4. 根據 make 的變數改變 PLIST

有些 port,尤其是 p5- 的 port,必須取決於 configure 時用的選項(或是在 p5- ports 時為 perl 的版本)來改變 PLIST。要使這更容易一點,在 PLIST 裡的 %%OSREL%%%%PERL_VER%%%%PERL_VERSION%% 的任何 instance 都將被適當地替代。%%OSREL%% 的值是作業系統的修訂版號碼(如 2.2.7)。 %%PERL_VERSION%% 是 perl 完整的版本編號(如 5.00502) 而 %%PERL_VER%% 則是 perl 版本編號減去 patchlevel (如 5.005)。

如果必須做其它的替代,可以用成對的 VAR=VALUE 的列表來設定變數 PLIST_SUB,而 %%VAR%% 的 instance 將會被 PLIST 裡的 VALUE 所取代。

舉個例,假如有個 port 安裝了很多檔案在一個指定版本的子目錄, 可以把一個類似

    OCTAVE_VERSION= 2.0.13
    PLIST_SUB=      OCTAVE_VERSION=${OCTAVE_VERSION}
的東西放進 Makefile 並在所有 PLIST 裡出現版本的位置使用 %%OCTAVE_VERSION%%。照這方法,當你升級 port 時就不必更改 PLIST 裡那數(或者在一些情形下, 數百)行的東西。

這種替代(以及任何 man pages) 的增加)會藉由讀取 PLIST 和寫入 TMPPLIST(預設: WRKDIR/.PLIST.mktmp)在 do-installpost-install 的標的之間完成 因此假如你匆忙地建立 PLIST,在 do-install 裡或之前這樣做。同樣地, 如果 port 必須編輯結果的檔案,在 post-install 裡對名為 TMPPLIST 的檔案這麼做。

4.4.13.5. 改變子目錄 pkg 裡的檔案名稱

所有在子目錄 pkg 裡的檔名都是用變數定義的, 所以假如需要可以在 Makefile 裡改變它們。 當要在數個 port 間分享同一個子目錄 pkg, 或者必須寫入上面所述檔案中的某個檔案(看看 WRKDIR 以外的地方寫入可知為何直接寫入子目錄 pkg 是個爛方法)時,這方法特別地有用。

這裡有份變數名稱及其預設值的列表。

變數預設值
COMMENT${PKGDIR}/DESCR
DESCR${PKGDIR}/DESCR
PLIST${PKGDIR}/PLIST
PKGINSTALL${PKGDIR}/PKGINSTALL
PKGDEINSTALL${PKGDIR}/PKGDEINSTALL
PKGREQ${PKGDIR}/REQ
PKGMESSAGE${PKGDIR}/MESSAGE

請改變這些變數而不要亂弄 PKG_ARGS。 如果改變了 PKG_ARGS,則從 port 安裝時那些檔案便不會被正確地安裝到 /var/db/pkg

4.4.14. Licensing Problems

有些軟體 package 有許可的限制或可造成違法(PKP 在 public key crypto 的專利權,ITAR (crypto 軟體的輸出)這兩個恰屬於這種)。 對這種軟體我們的作法差異很大,端賴於個別的許可的確實說法。

Note: 身為一個 porter,有責任要了解軟體的許可條件,並確保 FreeBSD project 不會因為重新散佈程式碼或編譯過的 binary 而為違反許可負責任,不論是經由 ftp 或光碟。如果不肯定的話請與 FreeBSD ports mailing list 聯繫。

有兩個變數可以設在 Makefile 裡以處理經常發生的情況:

  1. 如果 port 有``不可售以獲利''這種形式的許可,設定變數 NO_CDROM 為一敘述原因的字串。我們會確定這類的 port 不會在 release 時進入光碟裡。而 distfile 和 package 仍可經由取得。

  2. 如果 resulting package 必須在各個站獨立建立,或者 resulting binary package 因為許可不得散佈;設定變數 NO_PACKAGE 為一敘述原因的字串。我們會確定這類的 package 不會出現在 ftp 站上,也不會在 release 時進入光碟裡。 而 distfile 仍會包括在那兩者裡。

  3. 假如 port 有法定的限制可使用的人(如 crypto 此類)或者有 ``非營利用途''的許可,設定變數 RESTRICTED 為敘述原因的字串。對這類的 port, distfiles/packages 即使從我們的 ftp 站也無法取得。

Note: 第一版跟第二版的 GNU 大眾公用版權(GPL)對 port 應該都不是個問題。

Note: 如果你是個 committer,確定你也更新了 ports/LEGAL

4.4.15. 升級

當你的 port 比來自起源作者的最新版還老舊時,先確保你有最新的 port。 可以從 ftp mirror 站的目錄 ports/ports-current 取得。也可以用 CVSup 使整個 ports collection 保持為新的,如同 Section 19.3.3.3 裡所描述的。

下一步是寄信給 maintainer,如果 port 的 Makefile 有列出。那個人可能已經在著手升級, 或者有現在不升級的原因(例如因為新版本的穩定性問題)。

如果 maintainer 要你進行升級或是沒有任何人開始著手, 請做好升級並把新的和舊的 port 目錄的 recursive diff (unified 或 context diff 也好,不過 port committer 似乎比較喜歡 unified diff) 給我們(比如,如果修改的 port 目錄是 superedit 而原本在 port tree 裡的是 superedit.bak, 接著就把 diff -ruN superedit.bak superedit 的結果寄給我們)。請檢查輸出訊息確定所有的改變都有道理。把 diff 寄給我們最好的方法就是把它包括進 send-pr(1) (分類為 ports)。請在訊息裡提及增加或刪除的檔案, 因為當 commit 時它們必須被明確地記入 CVS。如果 diff 大於 20KB, 請把它壓縮並 uuencode:不然就只要把它包含進來如同包含進 PR。

Note: 再說一次,請用 diff(1) 來寄出現存的 port 的更新而不要用 shar(1)

4.4.16. Do's and Dont's

這裡有份當在 porting 過程中應該與不應該下的命令的列表。 你應該比對這份列表來檢查自己的 port,不過也可以檢查 PR 資料庫裡其他人提出的 port。以 Bug Reports and General Commentary 所描述的來提出所檢查的 port 的註解。檢查 PR 資料庫裡的 port 不僅讓我們能更快地 commit 它們, 而且也證明了你知道你在幹什麼。

4.4.16.1. Strip Binaries

Do strip binaries. 如果原本的程式碼已經 strips the binaries, 那很好;否則就應該自己加一個 post-install 的規則 來做這件事。這裡有個例子:

    post-install:
            strip ${PREFIX}/bin/xdl

file(1) 這個命令在一個可安裝的執行檔上來檢查這個 binary 是否有被 strip。如果它沒有說 not stripped, 就表示這個檔已經被 strip。

4.4.16.2. INSTALL_* macros

*-install 的標的裡使用 bsd.port.mk 裡提供的巨集指令來確保檔案有正確的模式和擁有權。 這些指令是:

  • INSTALL_PROGRAM 這個指令是安裝 binary 的可執行檔。

  • INSTALL_SCRIPT 這個指令是安裝可執行的 script。

  • INSTALL_DATA 這個指令是安裝可分享的資料。

  • INSTALL_MAN 這個指令是安裝 manpage 和其它 文件(它不會壓縮任何東西)。

這些是有所有適當的旗標的基本 install 指令。 看下面的例子以知如何使用它們。

4.4.16.3. WRKDIR

不要在 WRKDIR 以外寫入任何東西。 WRKDIR 是建立 port 時唯一保證可寫入的地方 (從光碟編譯 port 有從唯讀的 tree 編譯 port 的例子)。如果必須修改任何 PKGDIR 裡的檔案,藉由重新定義一個變數來做, 而不是直接寫入。

4.4.16.4. WRKDIRPREFIX

確保你的 port 會尊敬 WRKDIRPREFIX。 大部份的 port 不用擔心這個。特別是假如你參考另一個 port 的 WRKDIR,正確的位置應為 WRKDIRPREFIXPORTSDIR/subdir/name/work 而不是 PORTSDIR/subdir/name/work.CURDIR/../../subdir/name/work 或者一些這類的。

同樣地,如果自己定義 WRKDIR, 確定你有在前面加上 ${WRKDIRPREFIX}${.CURDIR}

4.4.16.5. 區別作業系統和 OS 版本

你可能會遇到必需修改或是有條件的編譯的程式碼,這要看它是以何種版本 UNIX 為基礎來執行。如果為了有條件的編譯必須對程式碼做這類的更改, 確定你有盡量把它改的一般化點以便我們可以把 port 往回移植到 FreeBSD 1.x 的系統而且可以移植到其它的 BSD 系統,如源自 CSRG 的 4.4BSD、BSD/386、 386BSD、NetBSD 和 OpenBSD。

分辨 4.3BSD/Reno (1990) 和較新版的 BSD 的程式碼較好的方法是用 <sys/param.h> 裡定義的 BSD 巨集指令。希望那個檔已經被包括進去了; 假如沒有,把這段程式碼:

    #if (defined(__unix__) || defined(unix)) && !defined(USG)
    #include <sys/param.h>
    #endif

加進 .c 檔案裡適當的位置。 我們相信任何有定義這兩個符號的系統都有 sys/param.h。如果發現有系統沒有, 我們會很樂意知道的。請寄信到 FreeBSD ports mailing list

另一種方法是用 GNU Autoconf 的形式來做:

    #ifdef HAVE_SYS_PARAM_H
    #include <sys/param.h>
    #endif

別忘了這種方式要在 Makefile 裡的 CFLAGS 加上 -DHAVE_SYS_PARAM_H

一旦包括了 sys/param.h,可以用:

    #if (defined(BSD) && (BSD >= 199103))

來檢查程式是否在 4.3 Net2 code base 或更新版的上編譯(如 FreeBSD 1.x、4.3/Reno、NetBSD 0.9、386BSD、BSD/386 1.1 和之下的版本)。

用:

    #if (defined(BSD) && (BSD >= 199306))

來檢查程式是否在 4.4 code base 或更新的基礎上編義(如 FreeBSD 2.x、4.4、NetBSD 1.0、BSD/386 2.0 或這之上的版本)。

對 4.4BSD-Lite2 code base 而言,BSD 巨集指令的值是 199506。 這個陳述只是為了提供訊息。它不應該被用來分辯單純以 4.4 Lite 為基礎的 FreeBSD 版本以及融合了來自 4.4-Lite2 的改變的版本。 這時後應該用 __FreeBSD__ 這個巨集指令來替代。

Use sparingly:

  • __FreeBSD__ 定義在所有版本的 FreeBSD 裡。 假如你做的改變只有影響到 FreeBSD 就用它。 Porting gotchas like the use of sys_errlist[] vs strerror() are Berkeleyisms, not FreeBSD changes.

  • 在 FreeBSD 2.x 裡,__FreeBSD__ 定為 2。在更早的版本則為 1。 較新的版本會提升這個值來配合它們的主版本編號。

  • 如果必須分辯 FreeBSD 1.x 系統和 FreeBSD 2.x 或 3.x 系統間的不同,通常正確的解答是用上述的 BSD 巨集指令。如果真的有專為 FreeBSD 的改變(如用到 ld時的特殊共享函式選項)那就可以用 __FreeBSD__#if __FreeBSD__ > 1 來認出 FreeBSD 2.x 和其之後的系統。 如果需要更多細節來認出 2.0-RELEASE 之後的 FreeBSD 系統, 可以用下面的方法:

        #if __FreeBSD__ >= 2
        #include <osreldate.h>
        #    if __FreeBSD_version >= 199504
                 /* 2.0.5+ release specific code here */
        #    endif
        #endif

    Release__FreeBSD_version
    2.0-RELEASE119411
    2.1-CURRENT199501, 199503
    2.0.5-RELEASE199504
    2.1 之前的 2.2-CURRENT199508
    2.1.0-RELEASE199511
    2.1.5 之前的 2.2-CURRENT199512
    2.1.5-RELEASE199607
    2.1.6 之前的 2.2-CURRENT199608
    2.1.6-RELEASE199612
    2.1.7-RELEASE199612
    2.2-RELEASE220000
    2.2.1-RELEASE220000 (不變)
    2.2.1-RELEASE 之後的 2.2-STABLE220000 (不變)
    2.2-STABLE after texinfo-3.9221001
    2.2-STABLE after top221002
    2.2.2-RELEASE222000
    2.2.2-RELEASE 之後的 2.2-STABLE222001
    2.2.5-RELEASE225000
    2.2.5-RELEASE 之後的 2.2-STABLE225001
    併入 ldconfig -R 之後的 2.2-STABLE225002
    2.2.6-RELEASE226000
    2.2.7-RELEASE227000
    2.2.7-RELEASE 之後的 2.2-STABLE227001
    semctl(2) 改變後的 2.2-STABLE227002
    2.2.8-RELEASE228000
    2.2.8-RELEASE 之後的 2.2-STABLE228001
    mount(2) 改變前的 3.0-CURRENT300000
    mount(2) 改變後的 3.0-CURRENT300001
    semctl(2) 改變後的 3.0-CURRENT300002
    ioctl arg 改變後的 3.0-CURRENT300003
    轉換為 ELF 後的 3.0-CURRENT300004
    3.0-RELEASE300005
    3.0-RELEASE 之後的 3.0-CURRENT300006
    3/4 分支後的 3.0-STABLE300007
    3.1-RELEASE310000
    3.1-RELEASE 後的 3.1-STABLE310001
    C++ constructor/destructor order 改變後的 3.1-STABLE 310002
    3.2-RELEASE320000
    3.2-STABLE320001
    binary-incompatible IPFW 和 socket 改變後的 3.2-STABLE 320002
    3.3-RELEASE330000
    3.3-STABLE330001
    mkstemps() 加進 libc 後的 3.3-STABLE330002
    3/4 分支後的 4.0-CURRENT400000
    動態連結器的管理改變後的 4.0-CURRENT 400001
    C++ constructor/destructor order 改變後的 4.0-CURRENT 400002
    dladdr(3) 起作用後的 4.0-CURRENT400003
    __deregister_frame_info 動態連結器的錯誤修正後的 4.0-CURRENT (整合 EGCS 1.1.2 後的 4.0-CURRENT 也是) 400004
    suser(9) API 改變後的 4.0-CURRENT (newbus 之後的 4.0-CURRENT 也是)400005
    cdevsw registration 改變後的 4.0-CURRENT400006
    4.0-CURRENT after the addition of so_cred for socket level credentials400007
    4.0-CURRENT after the addition of a poll syscall wrapper to libc_r400008
    更換 kernel 的 dev_t 格式為 struct specinfo 指標後的 4.0-CURRENT 400009
    修正 jail(2) 的漏洞後的 4.0-CURRENT400010
    sigset_t 的資料格式改變後的 4.0-CURRENT400011
    更新至 GCC 2.95.2 編譯器後的 4.0-CURRENT 400012
    4.0-CURRENT after adding pluggable linux-mode ioctl handlers400013
    輸入 OpenSSL 後的 4.0-CURRENT400014
    GCC 2.95.2 裡的 C++ ABI 由預設的 -fno-vtable-thunks 改為 -fvtable-thunks 後的 4.0-CURRENT400015

Note: 注意一下有時後 2.2-STABLE 會以為自己是在 2.2.5-RELEASE 後的 ``2.2.5-STABLE''。原本該樣式是年份後緊接著月份, 不過從 2.2 開始我們決定改為更直接的 主要的/次要的 系統。 這是因為一些分支的平行發展讓簡單地用釋出日期來分類變得不可行。 如果你正在做一個 port,不用擔心舊的 -CURRENT; 它們會列在這就是為了讓你參考。

在已經做好的數百個 port 裡,只有少數一兩個情形應該已經用到了 __FreeBSD__。只因為一個較早的 port 弄亂了它並把它用錯地方不代表你也應該這麼做。

4.4.16.6. 在 bsd.port.mk 後寫東西

不要在 .include <bsd.port.mk> 這行後寫上任何東西。這通常可以藉由在你的 Makefile 中間某處含有 bsd.port.pre.mk 和結尾有 bsd.port.post.mk 來避免。

Note: 你必須含有 pre.mk/post.mk 這一對或是只有 bsd.port.mk; 不要把兩者混在一起。

bsd.port.pre.mk 只有定義少數可以放在 Makefile 用來測試的變數, bsd.port.post.mk 定義其餘的變數。

這裡有幾個定義在 bsd.port.pre.mk 裡的重要變數(這不是完整的列表,要看完整的請看 bsd.port.mk)。

變數描述
ARCH系統架構會藉由 uname -m 回傳(如 i386)
OPSYS作業細統形式,由 uname -s 回傳(如 FreeBSD)
OSREL作業系統的 release 版本(如 2.1.52.2.7)
OSVERSION用數字表示的作業系統版本,跟 __FreeBSD_version 一樣。
PORTOBJFORMAT系統的物件格式(aoutelf
LOCALBASE``local'' tree 的基底(如 /usr/local/)
X11BASE``X11'' tree 的基底(如 /usr/X11R6)
PREFIXport 該把自己裝到哪裡(看 more on PREFIX).

Note: 如要必須定義變數 USE_IMAKEUSE_X_PREFIXMASTERDIR, 在包括 bsd.port.pre.mk 前做這件事。

這裡有幾個可以寫在 bsd.port.pre.mk 後的例子;

    # no need to compile lang/perl5 if perl5 is already in system
    .if ${OSVERSION} > 300003
    BROKEN= perl is in system
    .endif
    
    # only one shlib version number for ELF
    .if ${PORTOBJFORMAT} == "elf"
    TCL_LIB_FILE=  ${TCL_LIB}.${SHLIB_MAJOR}
    .else
    TCL_LIB_FILE=  ${TCL_LIB}.${SHLIB_MAJOR}.${SHLIB_MINOR}
    .endif
    
    # software already makes link for ELF, but not for a.out
    post-install:
    .if ${PORTOBJFORMAT} == "aout"
           ${LN} -sf liblinpack.so.1.0 ${PREFIX}/lib/liblinpack.so
    .endif

4.4.16.7. 安裝額外的文件資料

如果軟體有標準的 man 和 info 以外你認為對使用者有用的文件, 把它安裝到 PREFIX/share/doc 下。如同前面的項目,這可以在 post-install 的標的裡做到。

為你的 port 建一個新的目錄。該目錄應該要能反應該 port 為何物。 這通常意謂著 PKGNAME 減去版本的部份。 然而,如果認為使用者可能同時安裝不同版本的 port,那就可以用整個 PKGNAME

讓安裝的動作依賴變數 NOPORTDOCS 以便使用者可以在 /etc/make.conf 讓它失用作用, 如下:

    post-install:
    .if !defined(NOPORTDOCS)
            ${MKDIR}${PREFIX}/share/doc/xv
            ${INSTALL_MAN} ${WRKSRC}/docs/xvdocs.ps ${PREFIX}/share/doc/xv
    .endif

別忘了也把它們加進 pkg/PLIST! (在這邊不用擔心 NOPORTDOCS;目前 package 沒有辦法從 /etc/make.conf 讀取變數。)

也可以用 pkg/MESSAGE 這個檔讓安裝時顯示訊息。 看 使用 pkg/MESSAGE 的章節來獲得更多細節。

Note: MESSAGE 不需要加進 pkg/PLIST 裡。

4.4.16.8. DIST_SUBDIR

不要讓 port 弄亂了 /usr/ports/distfiles。 如果 port 需要抓很多檔案,或是含有檔名可能會和其它 port 相衝的檔案(如,Makefile),把 DIST_SUBDIR 設為 port 的名稱(沒有版本的部份的 PKGNAME 應該就行了)。這樣會把 DISTDIR 從預設的 /usr/ports/distfiles 改為 /usr/ports/distfiles/DIST_SUBDIR, 實際上並把 port 需要的所有東西都放進那個子目錄。

它在主要的備份站 ftp.FreeBSD.org 上也是看同名的子目錄。(在 Makefile 明確地設定 DISTDIR 不會做到這一點,所以請用 DIST_SUBDIR。)

Note: 這不會影響 Makefile 裡設定的 MASTER_SITES

4.4.16.9. Package 的資訊

把 package 的資訊包括到 pkg 裡,如. COMMENTDESCR,和 PLIST

Note: 這些檔案已不再只用於包裝,現在即使設定了 NO_PACKAGE 這些檔案仍是必要的

4.4.16.10. RCS 字串

不要把 RCS 字串放進 patch 裡。當我們把檔案放進 ports tree 時 CVS 會把它們切去,而當我們檢查時又一次, 這樣它們會變得不一樣而且 patch 會失敗。RCS 字串由 dollar 符號($)圍起來,一般來說由 $Id$RCS 起頭。

4.4.16.11. 遞迴的 diff

diff 使用遞迴選項(-r) diff 來產生 patch 會好多了,不過請看一下最後的 patch 來確定沒有任何不需要的垃圾在裡面。尤其是兩個備份檔間的 diff, 和當 port 用 Imake 或 GNU configure 等等時的 Makefiles 也不需要而該砍掉。如果必須編輯 configure.in 並執行 autoconf 來重新產生 configure,不要做 configure 的 diff (這通常會數千行!);定義 USE_AUTOCONF=yes 並做 configure.in 的 diff。

同樣地,如果必須砍掉檔案,那就寫在 post-extract 的標的裡來做而不要弄為 patch 的一部份。如果對最終的 diff 滿意, 請把它分割成一個 patch 就對應一個原始檔。

4.4.16.12. PREFIX

試著讓 port 安裝至相對於 PREFIX (這個值應該設為 LOCALBASE (預設是 /usr/local),除非已經設了 USE_X_PREFIXUSE_IMAKE, 這樣的話就應該是 X11BASE (預設是 /usr/X11R6)。)的位置。

不要把 /usr/local/usr/X11R6 寫死在程式碼的任何地方, 這會讓 port 更有彈性而且可以迎合其它站的需求。對用 imake 的 X port 來說,這是自動產生的; 不然往往可以僅藉由把 port 裡各個 scripts/Makefiles 出現 /usr/local (沒有用 imake 的 X port 就是 /usr/X11R6) 的地方取代為去讀取 PREFIX 來達成, 因為這個變數會自動地往下傳至建立與安裝過程中的每個階段。

不要設 USE_X_PREFIX,除非你的 port 真的需要它 (例如,它連結 X 的函式或它必須參考到 X11BASE 裡的檔案)。

PREFIX 這個變數可以在你的 Makefile 或使用者的環境裡重新指定。然而, 在單獨 port 的 Makefiles 明確地設定這個變數是相當令人感到氣餒的。

同樣地,用上面提到的變數來參考其它 port 的程式/檔案, 而非用明確的路徑名稱。舉個例,如果 port 需要一個完整的 less 路徑名稱做為巨集 PAGER, 用編譯器的旗標:

    -DPAGER=\"${PREFIX}/bin/less\"
    -DPAGER=\"${LOCALBASE}/bin/less\"
如果這是一個 X port,取代掉 -DPAGER=\"/usr/local/bin/less\"。 這樣假如系統管理者把整個 `/usr/local' tree 移到其它地方, 它會有較佳的機會可以運作。

4.4.16.13. 子目錄

試著讓 port 把東西放到 PREFIX 下正確的子目錄。 有些 port 把所有東西聚在並放進與 port 同名的子目錄,這是不對的。 同樣地,有很多 port 把 binaries,header files 和 manual pages 以外的所有東西放到 lib 的一個子目錄下,這對 BSD 的模範並不是個好兆頭。這裡面有很多檔案都應該移到下面所列位置的其中一個: etc (setup/configuration 檔), libexec (內部啟動的可執行檔), sbin (superusers/managers 用的可執行檔) info (為 info 瀏覽器做的文件) 或 share (與架構無關的檔案)。詳細資料參考 man hier(7),支配 /usr 的規則有相當多的部份也適用在 /usr/local。 除了處理 USENET ``news'' 的 port 以外。它們以 PREFIX/news 為檔案目的地。

4.4.16.14. 清理空目錄

當 port 被反安裝時讓它能清理乾淨。這通常藉由為每個明確地由 port 建立的目錄加上 @dirrm 這幾行來達成。 在刪除 parent 目錄前必須先刪除子目錄。

     :
    lib/X11/oneko/pixmaps/cat.xpm
    lib/X11/oneko/sounds/cat.au
     :
    @dirrm lib/X11/oneko/pixmaps
    @dirrm lib/X11/oneko/sounds
    @dirrm lib/X11/oneko

然而有時後 @dirrm 會出現錯誤因為其它的 port 也在共用同一個子目錄。可以從 @unexec 呼叫 rmdir 來移除空目錄而不出現警告。

    @unexec rmdir %D/share/doc/gimp 2>/dev/null || true

這將不會印出任何錯誤訊息,也不會造成 pkg_delete 不正常跳出即使 PREFIX/share/doc/gimp 不是空的,由於其它 port 把一些檔案裝在裡面。

4.4.16.15. UIDs

如果需要安裝的系統上有特定的某使用者,讓 pkg/INSTALL script 呼叫 pw 來自動建立它。例子可在 net/cvsup-mirror 看到。

如果 port 安裝一個 binary package 時必須使用與編譯時相同的使用者/群組(user/group) ID 編號, 那就必須從 50 到 99 選擇一個未被佔用的 UID 並登記它。 例子可到 japanese/Wnn 看。

確定你沒有用一個已經被系統或其它 port 佔用的 UID。 這裡是現在 50 到 99 間的 UID 的列表。

    majordom:*:54:54:Majordomo Pseudo User:/usr/local/majordomo:/nonexistent
    cyrus:*:60:60:the cyrus mail server:/nonexistent:/nonexistent
    gnats:*:61:1:GNATS database owner:/usr/local/share/gnats/gnats-db:/bin/sh
    uucp:*:66:66:UUCP pseudo-user:/var/spool/uucppublic:/usr/libexec/uucp/uucico
    xten:*:67:67:X-10 daemon:/usr/local/xten:/nonexistent
    pop:*:68:6:Post Office Owner (popper):/nonexistent:/nonexistent
    wnn:*:69:7:Wnn:/nonexistent:/nonexistent
    ifmail:*:70:66:Ifmail user:/nonexistent:/nonexistent
    pgsql:*:70:70:PostgreSQL pseudo-user:/usr/local/pgsql:/bin/sh
    ircd:*:72:72:IRCd hybrid:/nonexistent:/nonexistent
    alias:*:81:81:QMail user:/var/qmail/alias:/nonexistent
    qmaill:*:83:81:QMail user:/var/qmail:/nonexistent
    qmaild:*:82:81:QMail user:/var/qmail:/nonexistent
    qmailq:*:85:82:QMail user:/var/qmail:/nonexistent
    qmails:*:87:82:QMail user:/var/qmail:/nonexistent
    qmailp:*:84:81:QMail user:/var/qmail:/nonexistent
    qmailr:*:86:82:QMail user:/var/qmail:/nonexistent
    msql:*:87:87:mSQL-2 pseudo-user:/var/db/msqldb:/bin/sh
    mysql:*:88:88:MySQL Daemon:/var/db/mysql:/sbin/nologin

當提出一個要預留這範圍內新的 UID 或 GID 的 port (或升級)時, 請包括進一個公告。這會容許我們把預留的 ID 列表保持在最新狀態。

4.4.16.16. 合理地做事

Makefile 應該簡單並合理地做事。 如果能讓兩三行變得更短或更合裡,那就這麼做吧。這些例子包括用 .if construct 來替代 shell,如果可以重新定義 EXTRACT* 就不要再定義 do-extract,還有用 GNU_CONFIGURE 來替代 CONFIGURE_ARGS += --prefix=${PREFIX}

4.4.16.17. 尊重 CFLAGS

port 應該尊重 CFLAGS 這個變數。 如果沒有請加上 NO_PACKAGE=ignores cflagsMakefile 裡。

4.4.16.18. Configuration 檔

如果 port 需要放一些 configuration 檔在 PREFIX/etc 裡, 不要只是安裝它們並列在 pkg/PLIST 裡。這會造成 pkg_delete 刪除使用者費心編輯的檔案和被新的安裝完全消除。

反之,安裝有字尾語的樣品檔 (filename.sample 就不錯)並印出一段message 指明使用者在該軟體能運作錢必須先拷貝並編輯該檔案。

4.4.16.19. Portlint

在提出或 commit 之前用 portlint 檢查成果。

4.4.16.20. 回饋

把適合的改變/patch 寄給原先的作者/maintainer 以在下一個釋出的程式碼包含進去。這只會讓你在下一版的工作更輕鬆。

4.4.16.21. Miscellanea

pkg/DESCRpkg/COMMENT, 和 pkg/PLIST 每個檔都應該加倍檢查。 如果重新檢視一個 port 並覺得可以表達的更好,就做吧。

不要複製額外的 GNU 大眾公用版權(GPL) 的複本到我們的系統,拜托。

請小心注意任何的法律議題!不要讓我們違法散佈軟體!

4.4.16.22. 如果感到困惑...

在問我們任何問題前先看現存的例子和 bsd.port.mk 檔! ;)

如果有任何麻煩就問我們吧!不要用頭去撞牆!:-)

4.4.17. 一個 Makefile 的樣品

這裡有個 Makefile 的樣品可以用來創造一個新的 port。確定已移除所有額外的說明(方括弧內的東西)!

建議你跟著這個格式(變數的次序,章節間的空行等等)。 這個格式被設計於容易找到大部份的重要訊息。我們建議用 portlint 來檢查 Makefile

    [檔頭...只是讓我們容易確認 port。]
    # New ports collection makefile for:   xdvi
    [當升級一個 port 時 version required 的檔頭應該更新。]
    # Version required:    pl18 [things like "1.5alpha" are fine here too]
    [這是這個 Makefile 的第一版創造時的日期。更新 port 時絕不要改變這個。]
    # Date created:                26 May 1995
    [這是做出最初的 port 給 FreeBSD 的人,尤其是寫出這個 Makefile 第一版的人。
    記住,以後升級 port 時這不應該改變。]
    # Whom:                        Satoshi Asami <asami@FreeBSD.org>
    #
    # $FreeBSD$
    [ ^^^^^^^^^ 當它被 commit 到我們的貯藏庫時會被 CVS 自動地以 RCS ID 字串取代。 
    如果升級 port,不要把這行改回 "$FreeBSD$"。CVS 會自動處理。]
    #
           
    [用來描述 port 本身和主站的章節 - DISTNAME 永遠最先,跟著是 PKGNAME(如果需要),
     CATEGORIES,然後是 MASTER_SITES,MASTER_SITE_SUBDIR 可以跟在其後。
     在這些之後,EXTRACT_SUFX 或 DISTFILES 中之一也可以詳載。]
    DISTNAME=      xdvi
    PKGNAME=       xdvi-pl18
    CATEGORIES=    print
    [如果不是用 MASTER_SITE_* 巨集別忘了最後的斜線 ("/")!] 
    MASTER_SITES=  ${MASTER_SITE_XCONTRIB}
    MASTER_SITE_SUBDIR= applications
    [如果程式碼不是用標準的 ".tar.gz" 結構就設定這個]
    EXTRACT_SUFX=  .tar.Z
           
    [散佈的 patch 的章節 -- 可以空著]
    PATCH_SITES=   ftp://ftp.sra.co.jp/pub/X11/japanese/
    PATCHFILES=    xdvi-18.patch1.gz xdvi-18.patch2.gz
           
    [maintainer; *委託管理的*!這是一個使用者有問題和 bug 回報時聯絡的人
     (最好有 commit 的特權) - 這個人應該是 porter 或是可以即刻合理地傳達問題給起初的
     porter 的人。如果真的不想在這寫上位址,就設為 "ports@FreeBSD.org"。]
    MAINTAINER=    asami@FreeBSD.org
           
    [dependencies -- 可以空著]
    RUN_DEPENDS=   gs:${PORTSDIR}/print/ghostscript
    LIB_DEPENDS=   Xpm.5:${PORTSDIR}/graphics/xpm
           
    [這個章節是為了不屬於上面任一章的其它標準的 bsd.port.mk 變數]
    [如果在 configure,建立,安裝...的過程中會問問題]
    IS_INTERACTIVE=        yes
    [如果它解到 ${DISTNAME} ... 以外的目錄]
    WRKSRC=                ${WRKDIR}/xdvi-new
    [如果散佈的 patch 沒有相對於 ${WRKSRC},可能得費心處理這個]
    PATCH_DIST_STRIP=      -p1
    [如果它需要執行一個由 GNU autoconf 產生的 "configure" script]
    GNU_CONFIGURE= yes
    [如果它須要用 GNU make 來建立...,而不是 /usr/bin/make]
    USE_GMAKE=     yes
    [如果它是一個 X 應用程式且需要執行 "xmkmf -a"]
    USE_IMAKE=     yes
    [et cetera.]
           
    [用在下面規則的非標準變數]
    MY_FAVORITE_RESPONSE=  "yeah, right"
           
    [再來是特殊的規則,以它們被呼叫的順序排列]
    pre-fetch:
            i go fetch something, yeah
           
    post-patch:
            i need to do something after patch, great
           
    pre-install:
            and then some more stuff before installing, wow
           
    [然後就是結尾了]
    .include <bsd.port.mk>

4.4.18. Automated package list creation

首先,確定 port 已幾乎完成,只缺 PLIST。 創造一個空的 PLIST

    # touch PLIST

再來,創造一組 port 可以安裝的新目錄,並安裝任何的 dependency。

    # mtree -U -f /etc/mtree/BSD.local.dist -d -e -p /var/tmp/port-name
    # make depends PREFIX=/var/tmp/port-name

把目錄的結構存在一個新檔案裡。

    # (cd /var/tmp/port-name && find * \! -type d) > OLD-DIRS

如果 port 尊敬 PREFIX (它應該如此) 接著就可以安裝 port 並創造 package 列表。

    # make install PREFIX=/var/tmpport-name
    # (cd /var/tmp/port-name && find * \! -type d) > pkg/PLIST

你也必須把任何最新創造的目錄加進包裝列表。

    # (cd /var/tmp/port-name && find * -type d) | comm -13 OLD-DIRS - | sed -e 's#^#@dirrm#' >> pkg/PLIST

最後,必須自己動手整理包裝列表。當我說這是全自動時我撒了個謊。 Manual pages 應該列在 port 的 Makefile 裡的 MANn 下, 而不是在包裝列表裡。使用者的 configuration 檔應該移除,或是安裝為 filename.sample。 任何 port 安裝的函式應該如同 ldconfig 該節所詳述的方式列出。

4.4.19. Package 的名稱

接下來是在為 package 命名時應遵守的慣例。這是為了讓我們的 package 的目錄容易搜尋,因為已經有太多太多的 package, 假如這傷到他們的眼睛那他們就會避開了!

Package 的名稱應該看起來像 language-name-compiled.specifics-version.numbers.

如果 DISTNAME 看起來不像這個,就把 PKGNAME 設為這種格式。

  1. FreeBSD 盡力去支援使用者的本地語言。如果 port 指定給某特定語言, language- 的部份就應該是該語言由 ISO-639 所定義的兩個字母的縮寫。例如 Japanese 是 ja, Russian 是 ru,Vietnamese 是 vi,hinese 是 zh, Korean 是 ko,而 German 是 de

  2. name 的部份應該全是小寫,除了真的很大的 package (裡面有很多程式)。如 XFree86 (沒錯,真的有這個 port, 查一下吧) 和 ImageMagick 就是這一類。否則的話,把名字 (或至少第一個字母)轉成小寫。如果大寫字母對名稱很重要(例如像 RV 有一個字母的名稱) 你可以自己斟酌使用大寫字母。為 Perl 5 的模組命名傳統上會預先加上 p5- 並把雙冒號的分隔轉為連字號;例如 Data::Dumper 模組變為 p5-Data-Dumper。如果有爭議的軟體名稱中有 數字,連字號,或底線,也可以把它們都包括進去 (就像 kinput2)。

  3. 如果 port 可以用不同的 hardcoded defaults 建立 (通常是一個 port 的家族的目錄名稱的一部份), -compiled.specifics 的部份應該聲明了 compiled-in 的預設值 (連字號是選擇性的)。例如紙張大小和字型單元。

  4. version 字串應該是句點分隔的整數和一個照順序的小寫字母。 唯一的例外是 pl (表示 `patchlevel') 字串, 這唯有當軟體沒有主要和次要的版本編號才可以用。

這裡有些如何把 DISTNAME 轉換成合適的 PKGNAME 的(真實的)例子:

發行版本的名稱Package 名稱原因
mule-2.2.2.mule-2.2.2不用改
XFree86-3.1.2XFree86-3.1.2不用改
EmiClock-1.0.2emiclock-1.0.2單一程式不用大寫名稱
gmod1.4gmod-1.4版本編號前需要一個連字號
xmris.4.0.2xmris-4.0.2版本編號前需要一個連字號
rdist-1.3alphardist-1.3a不能有像 alpha 的字串
es-0.9-beta1es-0.9b1不能有像 beta 的字串
v3.3beta021.srctiff-3.3這倒底是什麼東西?
tvtwmtvtwm-pl11一定要有版本字串
piewmpiewm-1.0一定要有版本字串
xvgr-2.10pl1xvgr-2.10.1唯有沒主要/次要的版本編號才可用 pl
gawk-2.15.6ja-gawk-2.15.6日語的版本
psutils-1.13psutils-letter-1.13Package 編議時已寫死的紙張大小
pkfontspkfonts300-1.0300dpi 字型的 package

如果在原始的程式碼裡完全沒有版本資訊的痕跡, 而且起初的作者也不大可能曾經釋出其它版本,就把版本的字串設為 1.0 (就像上面的例子 piewm)。 否則就問起初的作者或用日期字串 (yy.mm.dd) 當版本。

4.4.20. 種類

如同已知,port 以數個種類來分類。不過要讓這發揮功效,porter 和使用者了解每個種類而且我們決定每個種類要放什麼是很重要的。

4.4.20.1. 現在的分類列表

首先,這是現在 port 的種類的列表。以星號(*) 標示的是虛擬的種類--那些在 ports tree 裡並沒有相對應的子目錄。

Note: 而非虛擬的種類,可以在該子目錄裡的 pkg/COMMENT 找到一行的描述(如 archivers/pkg/COMMENT)。

種類描述
afterstep*支援 AfterStep 視窗管理員的 port
archivers檔案處理的工具。
astro天文的 port。
audio聲音的支援。
benchmarks效能評比的工具。
biology有關生物的軟體。
cad電腦輔助設計的工具。
chinese中文的支援。
comms通訊軟體。大部份的軟體是用串列埠交談。
converters字碼的轉換器。
databases資料庫。
deskutils電腦發明前在桌上的東西。
devel發展工具。不要只因為它們是函式就把函式放在這-- 除非它們真的不屬於其它地方,否則它們不該在這種類裡。
editors一般的編輯器。特殊用途的編輯器在那些工具的區域裡 (例如,數學公式的編輯器會在 math)。
elispEmacs-lisp 的 port。
emulators其它作業系統的模擬器。終端機(terminal)的模擬器 屬於這裡--以 X 為基礎的應該在 x11 而文字基礎的不是在 comms 就是 misc, 視其確實的功能而定。
ftpFTP 客戶端和伺服器端的工具。如果 port 可與 FTP 和 HTTP 交談,把它以 www 為次要的分類放在 ftp
games遊戲。
german德語的支援。
gnome*來自 GNU Object Model Environment (GNOME) Project 的 port。
graphics圖形工具。
ircInternet Chat Relay 的工具。
japanese日語的支援。
javaJava 語言的支援。
kde*來自 K Desktop Environment (KDE) Project 的 port。
korean韓語的支援。
lang程式語言。
mail郵件軟體。
math數值運算的軟體和其它的數學工具。
mboneMBone 的應用程式。
misc各式各樣的工具--基本上是不屬於任何地方的東西。 這是唯一不應該出現其它非虛擬種類的分類。如果你在 CATEGORIES 那行有 misc 以外的東西,那你可以放心地刪除 misc 並把 port 放進另一個子目錄!
net各式各樣的網路軟體。
newsUSENET news 的軟體。
offix*來自 OffiX 套件的 port。
palm3Com Palm(tm) 系列的軟體支援。
perl5*需要 perl 第五版才能執行的 port。
plan9*Plan9 的各種程式。
print列印軟體。桌面出版的工具也屬於這裡 (previewers 等等)。
python*用 python 寫的軟體。
russian俄語的支援。
security電腦安全的工具。
shells命令列下的 shell。
sysutils系統工具。
tcl75*用 Tcl 7.5 版執行的 port。
tcl76*用 Tcl 7.6 版執行的 port。
tcl80*用 Tcl 8.0 版執行的 port。
tcl81*用 Tcl 8.1 版執行的 port。
textproc文字處理的工具。它不包括桌面出版的工具, 那屬於 print/。
tk41*用 Tk 4.1 版執行的 port。
tk42*用 Tk 4.2 版執行的 port。
tk80*用 Tk 8.0 版執行的 port。
tk81*用 Tk 8.1 版執行的 port。
tkstep80*用 TkSTEP 8.0 版執行的 port。
vietnamese越南語的支援。
windowmaker*支援 WindowMaker 視窗管理員的 port
www跟 World Wide Web 有關的軟體。HTML 語言的支援也屬於這裡。
x11X window system 和它的朋友們。 這個種類只給直接支援視窗系統的軟體。不要把平常的 X 應用程式放在這。如果 port 是個 X 應用程式, 定義USE_XLIB (包含有 USE_IMAKE) 並把它放在適當的種類。 同樣地,它們裡面有很多屬於其它的 x11-* 種類(看下面)。
x11-clocksX11 的時鐘。
x11-fmX11 的檔案管理員。
x11-fontsX11 的字型和字型工具。
x11-serversX11 servers.
x11-toolkitsX11 toolkits.
x11-wmX11 視窗管理員。

4.4.20.2. 選擇正確的種類

因為有很多的種類重疊,你必須選擇何者為 port 首要的種類。 有幾個規則支配著這個議題。這裡有份優先權的列表,其優先權依序遞減。

  • 特定語言的種類永遠是第一個。例如,如果 port 安裝 日語的 X11 字型,那 CATEGORIES 那行應是 japanese x11-fonts

  • 指定種類的勝過缺乏指定的。舉個例,一個 HTML 編輯器應該列成 www editors,而不是相反過來的方式。 同樣地,當 port 屬於 ircmailmbonenewssecurity, 或 www 其中之一時,便不需要列出 net

  • 只有當首要的種類是母語時 x11 才可當作第二個種類。X 的應用程式特別不該把 x11 放進種類那行。

  • Emacs 的模組應該和其支援的應用程式放在同一個種類裡, 而不是editors。例如, 用來編輯某些程式語言的原始檔的 Emacs 模組就應該在 lang 裡。

  • 如果 port 真的不屬於任何地方,就把它放在 misc

如果不確定是何種類,請在送出的 send-pr 裡附上一段註釋說明,以便我們可以在把它輸入前討論討論。 (如果你是 committer,寄上一份備忘錄到 FreeBSD ports mailing list , 這樣我們可以先討論它--新的 port 往往被輸入到錯誤的種類結果被立刻移走。)

4.4.21. 對這份文件和 ports 系統的改變

如果你維護大量的 port,應該考慮跟著FreeBSD ports mailing list 。對 port 的運作的重要改變都會在這裡公告。你永遠可以在 the bsd.port.mk CVS log 找到最新的變動的詳細資訊。

4.4.22. 各位同胞們,就這樣了!

哇,這段說明真是長,不是嗎?真的感謝各位跟我們走到這邊。 現在你知道如何做一個 port 並把世界上每個東西轉換到 port 裡了! 這是開始對 FreeBSD Project 做出貢獻最簡單的方式! :-)