GNU 編碼標準,上次更新於 2024 年 5 月 26 日。
Copyright © 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled “GNU Free Documentation License”.
• 前言 | 關於 GNU 編碼標準。 | |
• 法律議題 | 維護自由軟體的自由。 | |
• 設計建議 | 通用程式設計。 | |
• 程式行為 | 所有程式的程式行為 | |
• 撰寫 C 程式碼 | 充分利用 C 語言。 | |
• 文件化 | 程式文件化。 | |
• 管理發行版本 | 發布流程。 | |
• 參考資料 | 提及非自由軟體或文件。 | |
• GNU 自由文件授權條款 | 複製和分享本手冊。 | |
• 索引 | ||
GNU 編碼標準由 Richard Stallman 和其他 GNU 專案志願者撰寫。其目的是使 GNU 系統清晰、一致且易於安裝。此文件也可以作為編寫可移植、穩健和可靠程式的指南。它著重於以 C 語言編寫的程式,但即使您使用其他程式語言,許多規則和原則也很有用。這些規則經常說明以特定方式編寫的原因。
如果您不是直接從 GNU 專案且最近取得此檔案,請檢查是否有較新版本。您可以從 GNU 網站伺服器以多種不同格式取得 GNU 編碼標準,包括 Texinfo 原始碼、PDF、HTML、DVI、純文字等等,網址為:https://gnu.dev.org.tw/prep/standards/。
如果您正在維護官方 GNU 套件,除了本文檔外,請閱讀並遵循 GNU 維護者資訊(請參閱目錄,在GNU 軟體維護者資訊中)。
如果您想接收這些 GNU 文件每次變更的差異,請透過網站介面加入郵件列表 gnustandards-commit@gnu.org
,網址為 https://lists.gnu.org/mailman/listinfo/gnustandards-commit。歸檔也於該處提供。
請將對本文檔的更正或建議發送至 bug-standards@gnu.org。如果您提出建議,請包含建議的新措辭,以幫助我們有效率地考慮該建議。我們偏好 Texinfo 原始碼的上下文差異,但如果您覺得困難,您可以針對本文檔的其他版本製作上下文差異,或以任何明確的方式提出建議。本文檔的原始碼儲存庫位於 https://savannah.gnu.org/projects/gnustandards。
這些標準涵蓋了編寫 GNU 套件時最重要的最低要求。很可能需要額外的標準。有時,您可能會建議將這些標準添加到本文檔中。如果您認為您的標準通常很有用,請提出建議。
您也應該為您的套件設定許多此處未提及或未明確指定的標準。最重要的一點是要自我一致——盡量堅持您選擇的慣例,並盡可能將它們記錄下來。這樣,您的程式將更容易被其他人維護。
GNU Hello 程式作為如何遵循 GNU 編碼標準的簡單程式範例。https://gnu.dev.org.tw/software/hello/hello.html。
此版本的 GNU 編碼標準上次更新於 2024 年 5 月 26 日。
本章討論如何確保 GNU 軟體避免法律上的困難,以及其他相關議題。
• 閱讀非自由程式碼 | 提及專有程式。 | |
• 貢獻 | 接受貢獻。 | |
• 商標 | 我們如何處理商標議題。 |
在您從事 GNU 工作時,無論在任何情況下都不要參考 Unix 原始碼!(或任何其他專有程式。)
如果您對 Unix 程式的內部結構有模糊的印象,這並不絕對表示您不能模仿它,但請嘗試在內部以不同的方式組織模仿,因為這很可能使 Unix 版本的細節變得無關緊要,並且與您的結果不同。
例如,Unix 工具程式通常經過最佳化以最大程度地減少記憶體使用量;如果您改為追求速度,您的程式將會非常不同。您可以將整個輸入檔案保存在記憶體中並在那裡掃描,而不是使用 stdio。使用比 Unix 程式更新發現的更聰明的演算法。消除臨時檔案的使用。一次完成而不是兩次(我們在組譯器中這樣做)。
或者,相反地,強調簡潔而不是速度。對於某些應用程式,當今電腦的速度使得更簡單的演算法足夠用。
或者追求通用性。例如,Unix 程式通常具有靜態表格或固定大小的字串,這會造成任意限制;改為使用動態分配。確保您的程式處理輸入檔案中的 NUL 和其他有趣的字元。新增一種程式語言以實現可擴展性,並以該語言編寫程式的一部分。
或者將程式的某些部分變成可獨立使用的函式庫。或者使用簡單的垃圾收集器,而不是精確追蹤何時釋放記憶體,或者使用新的 GNU 功能,例如 obstacks。
如果您正在開發的程式版權歸自由軟體基金會所有,那麼當其他人向您發送一段程式碼以添加到程式中時,我們需要法律文件才能使用它——就像我們最初要求您簽署文件一樣。每位對程式做出非微不足道貢獻的人都必須簽署某種法律文件,以便我們擁有該程式的明確所有權;僅靠主要作者是不夠的。
因此,在加入其他人的任何貢獻之前,請告訴我們,以便我們可以安排取得文件。然後等待我們告訴您我們已收到簽署的文件,然後您再實際使用該貢獻。
這適用於您發布程式之前和之後。如果您收到修復錯誤的差異,並且它們做出了重大變更,我們需要該變更的法律文件。
這也適用於註解和文件檔案。對於著作權法而言,註解和程式碼只是文字。著作權適用於所有種類的文字,因此我們需要所有種類的法律文件。
我們知道要求法律文件令人沮喪;對我們來說也很沮喪。但是,如果您不等待,您就是在冒險——例如,如果貢獻者的雇主不簽署免責聲明怎麼辦?您可能必須再次刪除該程式碼!
對於這裡或那裡幾行程式碼的變更,您不需要文件,因為它們對於著作權目的而言並不顯著。此外,如果您從建議中獲得的只是某些想法,而不是您使用的實際程式碼,則也不需要文件。例如,如果有人向您發送了一個實作,但您編寫了同一想法的不同實作,則您無需取得文件。
最糟糕的事情是您忘記告訴我們其他貢獻者。有一天我們可能會因此在法庭上感到非常尷尬。
我們為 GNU 套件的維護者提供了更詳細的建議。如果您已達到維護 GNU 程式(無論是否已發布)的階段,請查看:請參閱 法律事務,在GNU 維護者資訊中。
請不要在 GNU 軟體套件或文件中包含任何商標致謝。
商標致謝是指諸如此類的聲明,即某某是某某的商標。GNU 專案不反對商標的基本概念,但這些致謝感覺像是屈服,並且沒有法律要求,因此我們不使用它們。
就其他人的商標而言,法律要求是避免以讀者可能合理理解為命名或標記我們自己的程式或活動的方式使用它們。例如,由於 “Objective C” 是(或至少曾經是)商標,因此我們確保說我們提供 “Objective C 語言的編譯器”,而不是 “Objective C 編譯器”。後者本來是前者的簡短說法,但它沒有明確說明關係,因此可能會被誤解為使用 “Objective C” 作為編譯器的標籤,而不是語言的標籤。
請不要在 GNU 軟體或文件中使用 “win” 作為 Microsoft Windows 的縮寫。在駭客術語中,稱某物為 “win” 是一種讚美形式。如果您願意,您可以自由地讚美 Microsoft Windows,但請不要在 GNU 套件中這樣做。請完整寫出 “Windows”,或將其縮寫為 “w.”。請參閱系統移植性。
本章討論您在設計程式時應考慮到的一些議題。
• 原始碼語言 | 應使用的語言。 | |
• 相容性 | 與其他實作的相容性。 | |
• 使用擴充功能 | 使用非標準特性。 | |
• 標準 C | 使用標準 C 特性。 | |
• 條件編譯 | 僅在條件為真時編譯程式碼。 |
當您想要使用一種經過編譯並以高速運行的語言時,最好的語言是 C 語言。C++ 也可以,但請不要大量使用模板。Java 也可以,如果您編譯它的話。
當不需要最高效率時,自由軟體社群中常用的其他語言,如 Lisp、Scheme、Python、Ruby 和 Java 也可以。Scheme,由 GNU Guile 實作,在 GNU 系統中扮演著特殊的角色:它是擴充以 C/C++ 編寫的程式的首選語言,也是適用於廣泛應用程式的優秀語言。使用 Guile 和 Scheme 的 GNU 組件越多,使用者就能夠擴充和組合它們(請參閱 Emacs 理論,在GNU Guile 參考手冊中)。
許多程式被設計成可擴展的:它們包含一個比 C 更高階的語言的直譯器。通常程式的大部分也是用該語言編寫的。Emacs 編輯器開創了這種技術。
GNU 軟體的標準可擴展性直譯器是 Guile (https://gnu.dev.org.tw/software/guile/),它實作了 Scheme 語言(Lisp 的一種特別清晰和簡單的方言)。Guile 還包含 GTK+/GNOME 的綁定,使得在 Guile 中編寫現代 GUI 功能成為可能。我們不拒絕以其他 “腳本語言” 編寫的程式,如 Perl 和 Python,但使用 Guile 是實現 GNU 系統整體一致性的途徑。
除了偶爾的例外情況,GNU 的工具程式和函式庫應向上相容於 Berkeley Unix 中的工具程式和函式庫,如果標準 C 指定其行為,則應向上相容於標準 C,如果 POSIX 指定其行為,則應向上相容於 POSIX。
當這些標準衝突時,提供每個標準的相容模式會很有用。
標準 C 和 POSIX 禁止許多種類的擴充功能。請隨意進行擴充,並包含 ‘--ansi’、‘--posix’ 或 ‘--compatible’ 選項以關閉它們。但是,如果擴充功能很有可能破壞任何實際的程式或腳本,那麼它就不是真正的向上相容。因此,您應該嘗試重新設計其介面以使其向上相容。
如果定義了環境變數 POSIXLY_CORRECT
(即使它定義為空值),許多 GNU 程式會抑制與 POSIX 衝突的擴充功能。如果適用,請使您的程式識別此變數。
當某個功能僅由使用者使用(而非由程式或命令檔案使用),並且在 Unix 中做得不好時,請隨意用完全不同且更好的東西完全替換它。(例如,vi
被 Emacs 替換。)但提供相容的功能也很好。(有一個免費的 vi
複製品,所以我們提供了它。)
無論是否有任何先例,都歡迎額外的實用功能。
許多現有的 GNU 功能支援許多優於可比較的 Unix 功能的便利擴充功能。是否在實作您的程式中使用這些擴充功能是一個難題。
一方面,使用擴充功能可以使程式更簡潔。另一方面,除非其他 GNU 工具可用,否則人們將無法建置程式。這可能會導致程式在較少種類的機器上工作。
對於某些擴充功能,可能很容易提供兩種替代方案。例如,您可以使用 “keyword” INLINE
定義函式,並將其定義為巨集以擴充為 inline
或無,具體取決於編譯器。
總的來說,如果您可以簡單地在沒有擴充功能的情況下完成工作,最好不要使用它們,但如果擴充功能有很大的改進,則可以使用它們。
此規則的一個例外是大型、已建立的程式(如 Emacs),它們在各種系統上運行。在這些程式中使用 GNU 擴充功能會讓許多使用者不滿意,因此我們不這樣做。
另一個例外是用作編譯一部分的程式:為了引導 GNU 編譯工具,必須使用其他編譯器編譯的任何東西。如果這些需要 GNU 編譯器,那麼沒有人可以在沒有預先安裝它們的情況下編譯它們。在某些情況下,這將非常麻煩。
1989 標準 C 現在已足夠普及,可以使用其特性在程式中。有一個例外:永遠不要使用標準 C 的 “三字符序列” 功能。
1999 年和 2011 年版本的標準 C 並非在所有平台上都完全支援。如果您旨在支援由 GCC 以外的編譯器進行編譯,則不應在您的程式中要求這些 C 特性。當編譯器支援這些特性時,有條件地使用它們是可以的。
如果您的程式僅旨在與 GCC 一起編譯,那麼如果 GCC 支援這些特性,並且它們能帶來實質性的好處,您就可以使用這些特性。
但是,在大多數程式中支援前標準編譯器很容易,因此如果您知道如何做到這一點,請隨意。
為了支援前標準 C,而不是以標準原型形式編寫函式定義,
int foo (int x, int y) …
請以前標準風格編寫定義,如下所示,
int foo (x, y) int x, y; …
並使用單獨的宣告來指定引數原型
int foo (int, int);
無論如何,您都需要這樣一個宣告,在標頭檔中,以在呼叫函式的所有檔案中獲得原型的優點。而且一旦您有了宣告,通常以前標準風格編寫函式定義就不會有任何損失。
此技術不適用於窄於 int
的整數類型。如果您認為引數的類型窄於 int
,請將其宣告為 int
。
在少數特殊情況下,此技術很難使用。例如,如果函式引數需要保存系統類型 dev_t
,您就會遇到麻煩,因為在某些機器上 dev_t
比 int
短;但是您不能改用 int
,因為在某些機器上 dev_t
比 int
寬。在非標準定義中,沒有任何類型可以安全地在所有機器上使用。支援非標準 C 並傳遞此類引數的唯一方法是使用 Autoconf 檢查 dev_t
的寬度,並相應地選擇引數類型。這可能不值得麻煩。
為了支援不識別原型的前標準編譯器,您可能想要使用類似這樣的預處理器巨集
/* Declare the prototype for a general external function. */ #if defined (__STDC__) || defined (WINDOWSNT) #define P_(proto) proto #else #define P_(proto) () #endif
當支援在建置程式時已知的組態選項時,我們偏好使用 if (... )
而不是條件編譯,因為在前一種情況下,編譯器能夠對所有可能的程式碼路徑執行更廣泛的檢查。
例如,請編寫
if (HAS_FOO) ... else ...
而不是
#ifdef HAS_FOO ... #else ... #endif
像 GCC 這樣的現代編譯器在這兩種情況下都會產生完全相同的程式碼,並且我們已在多個專案中成功使用類似的技術。當然,前一種方法假設 HAS_FOO
被定義為 0 或 1。
雖然這不是解決所有移植性問題的靈丹妙藥,並且並非總是適用,但遵循此策略將為 GCC 開發人員每年節省許多小時甚至數天的時間。
對於像 GCC 中的 REVERSIBLE_CC_MODE
這樣不能簡單地用於 if (...)
語句的函式式巨集,有一個簡單的解決方法。只需引入另一個巨集 HAS_REVERSIBLE_CC_MODE
,如下例所示
#ifdef REVERSIBLE_CC_MODE #define HAS_REVERSIBLE_CC_MODE 1 #else #define HAS_REVERSIBLE_CC_MODE 0 #endif
本章描述了編寫穩健軟體的慣例。它還描述了錯誤訊息、命令列介面以及函式庫應如何運作的通用標準。
• 非 GNU 標準 | 我們將 POSIX 等標準視為建議,而不是命令。我們不 “遵守” 它們。 | |
• 語意 | 編寫穩健的程式。 | |
• 函式庫 | 函式庫行為。 | |
• 錯誤 | 錯誤訊息的格式。 | |
• 使用者介面 | 關於通用介面的標準。 | |
• 尋找程式檔案 | 如何尋找程式的可執行檔和與之相關的其他檔案。 | |
• 圖形介面 | 圖形介面的標準。 | |
• 命令列介面 | 命令列介面的標準。 | |
• 動態外掛程式介面 | 動態外掛程式介面的標準。 | |
• 選項表 | 長選項表。 | |
• OID 分配 | GNU 的 OID 插槽表。 | |
• 記憶體使用量 | 何時以及如何關心記憶體需求。 | |
• 檔案使用量 | 要使用的檔案以及位置。 |
GNU 專案將其他組織發布的標準視為建議,而不是命令。我們會考慮這些標準,但我們不 “遵守” 它們。在開發 GNU 程式時,當實作外部標準的規範在客觀意義上使 GNU 系統整體上變得更好時,您應該實作它們。當情況並非如此時,您不應該這樣做。
在大多數情況下,遵循已發布的標準對使用者來說很方便——這意味著他們的程式或腳本將更具可移植性。例如,GCC 幾乎實作了標準 C 規範中規定的所有特性。如果它沒有做到,C 程式開發人員會不高興。GNU 工具程式主要遵循 POSIX.2 的規範;如果我們的程式不相容,shell 腳本編寫者和使用者會不高興。
但我們並不嚴格遵循這些規範中的任何一個,並且在某些特定點上,我們決定不遵循它們,以便使 GNU 系統對使用者更好。
例如,標準 C 規定幾乎禁止對 C 進行所有擴充。多麼愚蠢!GCC 實作了許多擴充功能,其中一些後來被採納為標準的一部分。如果您希望這些結構給出錯誤訊息,如標準 “要求” 的那樣,您必須指定 ‘--pedantic’,它的實作只是為了讓我們可以說 “GCC 是標準的 100% 實作”,而不是因為有任何理由實際使用它。
POSIX.2 規定 ‘df’ 和 ‘du’ 預設必須以 512 位元組為單位輸出大小。使用者想要的是 1k 單位,所以這是我們預設的做法。如果您想要 POSIX “要求” 的荒謬行為,您必須設定環境變數 ‘POSIXLY_CORRECT’(最初打算命名為 ‘POSIX_ME_HARDER’)。
當 GNU 工具程式支援長名稱命令列選項以及將選項與普通引數混合使用時,它們也偏離了 POSIX.2 規範的字面意思。與 POSIX 的這種細微的不相容性在實踐中從來都不是問題,而且非常有用。
特別是,不要僅僅因為標準說它是 “禁止” 或 “已棄用” 而拒絕新功能或刪除舊功能。
避免對任何資料結構的長度或數量設定任意限制,包括檔案名稱、行數、檔案數和符號,方法是動態配置所有資料結構。在大多數 Unix 工具程式中,「長行會被靜默截斷」。這在 GNU 工具程式中是不可接受的。
讀取檔案的工具程式不應捨棄 NUL 字元或任何其他非列印字元。程式應能正確處理多位元組字元編碼,例如 UTF-8。您可以使用 libiconv 來處理各種編碼。
檢查每個系統呼叫是否有錯誤返回,除非您知道您想要忽略錯誤。在每個因系統呼叫失敗而產生的錯誤訊息中,都包含系統錯誤文字(來自 strerror
或等效函式),以及檔案名稱(如果有的話)和工具程式名稱。僅僅「無法開啟 foo.c」或「stat 失敗」是不夠的。
檢查每次對 malloc
或 realloc
的呼叫,看看它是否返回 NULL
。即使您正在縮小區塊,也要檢查 realloc
;在將區塊大小四捨五入為 2 的冪的系統中,如果您要求較小的空間,realloc
可能會取得不同的區塊。
您必須預期 free
會更改已釋放區塊的內容。任何您想要從區塊中獲取的內容,都必須在呼叫 free
之前獲取。
如果 malloc
在非互動式程式中失敗,請將其設為致命錯誤。在互動式程式(從使用者讀取命令的程式)中,最好中止命令並返回命令讀取迴圈。這允許使用者終止其他程序以釋放虛擬記憶體,然後再次嘗試該命令。
使用 getopt_long
來解碼參數,除非參數語法使其不合理。
當靜態儲存區要在程式執行期間寫入時,請使用明確的 C 程式碼來初始化它。這樣,重新啟動程式(無需重新載入它)或部分程式,將重新初始化這些變數。保留 C 初始化宣告用於不會變更的資料。
盡量避免使用不明確的 Unix 資料結構(例如檔案目錄、utmp 或核心記憶體佈局)的低階介面,因為這些介面不太可能相容地運作。如果您需要尋找目錄中的所有檔案,請使用 readdir
或其他高階介面。這些都受到 GNU 的相容性支援。
偏好的訊號處理機制是 BSD 變體的 signal
和 POSIX sigaction
函式;替代的 USG signal
介面是一種較差的設計。
現今,使用 POSIX 訊號函式可能是使程式可移植的最簡單方法。如果您使用 signal
,那麼在執行 GNU libc 版本 1 的 GNU/Linux 系統上,您應該包含 bsd/signal.h 而不是 signal.h,以便獲得 BSD 行為。是否支援 signal
僅具有 USG 行為的系統,或者放棄它們,由您決定。
在偵測到「不可能」條件的錯誤檢查中,直接中止即可。通常沒有必要列印任何訊息。這些檢查表明存在錯誤。任何想要修復錯誤的人都必須閱讀原始碼並運行除錯器。因此,請在原始碼中用註解解釋問題。相關資料將在變數中,這些變數很容易使用除錯器檢查,因此沒有必要將它們移到其他地方。
不要使用錯誤計數作為程式的結束狀態。那樣行不通,因為結束狀態值限制為 8 位元(0 到 255)。程式的單次運行可能會有 256 個錯誤;如果您嘗試返回 256 作為結束狀態,父程序將看到 0 作為狀態,並且它會顯示程式已成功。
如果您建立暫存檔,請檢查 TMPDIR
環境變數;如果該變數已定義,請使用指定的目錄而不是 /tmp。
此外,請注意在世界可寫入目錄中建立暫存檔時,可能存在安全性問題。在 C 語言中,您可以透過以下方式避免此問題來建立暫存檔
fd = open (filename, O_WRONLY | O_CREAT | O_EXCL, 0600);
或使用 Gnulib 中的 mkstemps
函式(請參閱 mkstemps in Gnulib)。
在 bash 中,使用 set -C
(長名稱 noclobber
)來避免此問題。此外,mktemp
工具程式是從 shell 腳本建立暫存檔的更通用解決方案(請參閱 mktemp invocation in GNU Coreutils)。
盡量使函式庫函式可重入。如果它們需要執行動態儲存空間配置,至少盡量避免除了 malloc
本身之外的任何不可重入性。
以下是一些函式庫的命名慣例,以避免名稱衝突。
為函式庫選擇一個名稱前綴,長度超過兩個字元。所有外部函式和變數名稱都應以此前綴開頭。此外,在任何給定的函式庫成員中,應該只有其中一個。這通常意味著將每個成員放在單獨的原始碼檔案中。
當兩個外部符號總是共同使用時,可以例外,這樣任何合理的程式都不可能在沒有另一個的情況下使用其中一個;那麼它們可以都放在同一個檔案中。
非使用者文件入口點的外部符號,其名稱應以「_」開頭。「_」後面應跟著為函式庫選擇的名稱前綴,以防止與其他函式庫發生衝突。如果您願意,這些可以與使用者入口點放在同一個檔案中。
靜態函式和變數可以根據您的喜好使用,並且不需要符合任何命名慣例。
來自編譯器的錯誤訊息應如下所示
sourcefile:lineno: message
如果您想提及行號,請使用以下其中一種格式
sourcefile:lineno:column: message sourcefile:lineno.column: message
行號應從檔案開頭的 1 開始,而列號應從行開頭的 1 開始。(這兩個慣例都是為了相容性而選擇的。)計算列號時,假設空格和所有 ASCII 列印字元具有相同的寬度,並假設制表符間隔為每 8 列。對於非 ASCII 字元,在 UTF-8 locale 環境中,應使用 Unicode 字元寬度;GNU libc 和 GNU gnulib 提供了合適的 wcwidth
函式。
錯誤訊息也可以給出錯誤文字的起始和結束位置。有多種格式,以便您可以避免冗餘資訊,例如重複的行號。以下是可能的格式
sourcefile:line1.column1-line2.column2: message sourcefile:line1.column1-column2: message sourcefile:line1-line2: message
當錯誤分散在多個檔案中時,您可以使用此格式
file1:line1.column1-file2:line2.column2: message
來自其他非互動式程式的錯誤訊息應如下所示
program:sourcefile:lineno: message
當有適當的原始碼檔案時,或如下所示
program: message
當沒有相關的原始碼檔案時。
如果您想提及行號,請使用此格式
program:sourcefile:lineno:column: message
在互動式程式(從終端機讀取命令的程式)中,最好不要在錯誤訊息中包含程式名稱。指示哪個程式正在運行的位置是在提示符號或螢幕佈局中。(當同一個程式從終端機以外的來源讀取輸入時,它不是互動式的,最好使用非互動式樣式列印錯誤訊息。)
當字串 message 跟在程式名稱和/或檔案名稱之後時,不應以大寫字母開頭,因為那不是句子的開頭。(句子在概念上從行的開頭開始。)此外,它不應以句點結尾。
來自互動式程式的錯誤訊息,以及其他訊息(例如用法訊息),應以大寫字母開頭。但它們不應以句點結尾。
請不要使工具程式的行為取決於用於調用它的名稱。有時建立一個具有不同名稱的工具程式的連結很有用,這不應改變它的功能。因此,如果您將 foo 連結到 ls,則無論使用哪個名稱調用程式,程式的行為都應相同。
相反,請使用執行時期選項或編譯開關或兩者來選擇替代行為。您也可以建置程式的兩個版本,具有不同的預設行為,並以兩個不同的名稱安裝它們。
同樣地,請不要使命令列程式的行為取決於它作為標準輸出或標準輸入獲得的輸出裝置類型。裝置獨立性是系統設計的重要原則;不要僅僅為了讓某人偶爾不必輸入選項而損害它。(使用終端機時,錯誤訊息語法的變化是可以接受的,因為這是一個不重要的问题,人們不依賴它。)
如果您認為當輸出到終端機時,一種行為最有用,而當輸出到檔案或管線時,另一種行為最有用,那麼通常最好將預設行為設為對終端機輸出有用的行為,並為另一種行為提供一個選項。您也可以建置兩個不同名稱的程式版本。
對於某些情況下輸出為二進制資料的程式,有一個例外。將此類輸出發送到終端機是無用的,並且可能會導致問題。如果這樣的程式通常將其輸出發送到 stdout,則在這些情況下,它應該偵測到輸出是終端機,並改為給出錯誤訊息。-f
選項應覆蓋此例外,從而允許輸出發送到終端機。
相容性要求某些程式依賴於輸出裝置的類型。如果 ls
或 sh
沒有以所有使用者期望的方式執行,那將是災難性的。在其中一些情況下,我們為程式補充了一個首選的替代版本,該版本不依賴於輸出裝置類型。例如,我們提供了一個 dir
程式,它很像 ls
,只是它的預設輸出格式始終是多欄格式。
程式可能需要找到它啟動的可執行檔,以便重新啟動同一個程式。它可能需要找到關聯檔案,無論是原始碼檔案還是建置產生的檔案,這些檔案在運行時使用。
找到它們的方法從查看 argv[0]
開始。
如果該字串包含斜線,則按照慣例,它是可執行檔的檔案名稱,其目錄部分是包含可執行檔的目錄。當程式不是透過 PATH
找到時,情況就是如此,這通常意味著它是已建置但未安裝的程式,並且從建置目錄運行。程式可以使用 argv[0]
檔案名稱來重新啟動自身,並可以在其目錄部分中尋找關聯檔案。如果該檔案名稱不是絕對路徑,則它是相對於程式啟動時的工作目錄的路徑。
如果 argv[0]
不包含斜線,則它是一個命令名稱,其可執行檔是透過 PATH
找到的。程式應在 PATH
的目錄中搜尋該名稱,將 . 解釋為程式啟動時的目前工作目錄。
如果此程序找到可執行檔,我們將找到它的目錄稱為調用目錄。程式應檢查該目錄中是否存在它需要的關聯檔案。
如果程式的可執行檔通常在主要建置目錄的子目錄中建置,並且主要建置目錄包含關聯檔案(可能包括子目錄),則程式應查看調用目錄的父目錄,檢查主要建置目錄應包含的關聯檔案和子目錄。
如果調用目錄不包含所需的內容,但可執行檔名稱是符號連結,則程式應嘗試使用連結目標的包含目錄作為調用目錄。
如果此程序沒有找到有效的調用目錄——通常是透過 PATH
找到的已安裝程式的情況——程式應在程式的 makefile 安裝它們的目錄中尋找關聯檔案。請參閱 目錄變數。
在 argv[0]
中提供有效資訊是一種慣例,而不是保證。行為良好的程式(例如 shell)啟動其他程式時,會遵循此慣例;您的程式碼在啟動其他程式時也應遵循此慣例。但是,始終可以啟動程式並在 argv[0]
中給出無意義的值。
因此,任何需要知道其可執行檔位置或其他關聯檔案位置的程式,都應提供使用者環境變數來明確指定這些位置。
不要將特殊權限(例如使用 setuid
位元)授予將以啟發式方式搜尋關聯檔案或在以這種方式調用時搜尋自身可執行檔的程式。 將該權限限制為在硬式編碼的安裝位置(例如在 /usr 和 /etc 下)尋找關聯檔案的程式。
有關 PATH
的更多資訊,請參閱 Bash 參考手冊中的 Bourne Shell 變數。
當您編寫提供圖形使用者介面的程式時,請使其與 X Window 系統配合使用,使用 GTK+ 工具組或 GNUstep 工具組,除非該功能明確需要某些替代方案(例如,「在控制台模式下顯示 jpeg 影像」)。
此外,請提供命令列介面來控制功能。(在許多情況下,圖形使用者介面可以是調用命令列程式的單獨程式。)這是為了可以從腳本完成相同的工作。
也請考慮提供 D-bus 介面,以便從其他正在運行的程式中使用,例如在 GNOME 中。(GNOME 過去使用 CORBA 來實現此目的,但正在逐步淘汰。)此外,請考慮提供函式庫介面(供 C 語言使用),以及可能提供鍵盤驅動的控制台介面(供使用者從控制台模式使用)。一旦您完成了提供功能和圖形介面的工作,這些就不會增加太多額外的工作。
請使您的程式與螢幕閱讀器等輔助科技互通操作(請參閱 https://gnu.dev.org.tw/accessibility/accessibility.html)。如果您使用 GTK+,這應該是自動的。
遵循 POSIX 指導原則來設定程式的命令列選項是一個好主意。最簡單的方法是使用 getopt
來解析它們。請注意,GNU 版本的 getopt
通常允許選項出現在參數中的任何位置,除非使用了特殊參數「--」。這不是 POSIX 規範所指定的;它是一個 GNU 擴充功能。
請定義與單字母 Unix 樣式選項等效的長名稱選項。我們希望以這種方式使 GNU 更加使用者友善。這很容易使用 GNU 函式 getopt_long
來完成。
長名稱選項的優點之一是它們可以在程式之間保持一致。例如,使用者應該能夠期望任何具有「verbose」選項的 GNU 程式,其拼寫都精確地為「--verbose」。為了實現這種一致性,請在為您的程式選擇選項名稱時,查看常用長選項名稱的表格(請參閱 選項表)。
通常,作為一般參數給出的檔案名稱最好僅為輸入檔案;任何輸出檔案都將使用選項指定(最好是「-o」或「--output」)。即使您允許將輸出檔案名稱作為一般參數以實現相容性,也請嘗試提供一個選項作為指定它的另一種方式。這將導致 GNU 工具程式之間更加一致,並減少使用者需要記住的特殊性。
所有程式都應支援兩個標準選項:「--version」和「--help」。CGI 程式應接受這些選項作為命令列選項,並且如果作為 PATH_INFO
給出也應接受;例如,在瀏覽器中訪問「http://example.org/p.cgi/--help
」應輸出與從命令列調用「p.cgi --help」相同的資訊。
• --version | –version 的標準輸出。 | |
• --help | –help 的標準輸出。 |
標準的 --version
選項應指示程式列印關於其名稱、版本、來源和法律狀態的資訊,全部在標準輸出上,然後成功結束。一旦看到此選項,其他選項和參數都應被忽略,並且程式不應執行其正常功能。
第一行旨在方便程式解析;版本號碼的正確格式從最後一個空格之後開始。此外,它包含此程式的標準或規範名稱,格式如下
GNU Emacs 19.30
程式的名稱應為常數字串;不要從 argv[0]
計算它。其目的是聲明程式的標準或規範名稱,而不是其檔案名稱。還有其他方法可以找到命令在 PATH
中找到的精確檔案名稱。
如果程式是較大套件的附屬部分,請在括號中提及套件名稱,如下所示
emacsserver (GNU Emacs) 19.30
如果套件的版本號碼與此程式的版本號碼不同,您可以在右括號之前提及套件版本號碼。
如果您需要提及與包含此程式的套件分開發行的函式庫的版本號碼,您可以透過為您想要提及的每個函式庫列印額外的版本資訊行來做到這一點。對於這些行,請使用與第一行相同的格式。
請不要「僅僅為了完整性」而提及程式使用的所有函式庫——那樣會產生大量無用的雜亂資訊。只有當您在實務中發現函式庫版本號碼對您的除錯非常重要時,才請提及它們。
版本號碼行之後的下一行應為著作權聲明。如果需要多個著作權聲明,請將每個聲明放在單獨的一行上。
接下來應跟著一行聲明授權條款,最好使用以下縮寫之一,以及簡短聲明程式是自由軟體,並且使用者可以自由複製和更改它。另請提及在法律允許的範圍內沒有保固。請參閱下面建議的措辭。
可以將程式的主要作者列表放在輸出的末尾,作為一種表示感謝的方式。
以下是一個遵循這些規則的輸出範例
GNU hello 2.3 Copyright (C) 2007 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
您應該根據您的程式調整此範例,當然,填寫正確的年份、著作權持有人、程式名稱以及對發行條款的引用,並根據需要更改其餘措辭。
此著作權聲明只需要提及進行變更的最新年份——無需列出先前版本變更的年份。如果程式名稱不方便,您不必在這些聲明中提及程式名稱,因為它已出現在第一行中。(原始碼檔案中的著作權聲明的規則不同;請參閱 GNU 維護者資訊中的 著作權聲明。)
上述行的翻譯必須保留著作權聲明的有效性(請參閱 國際化)。如果翻譯的字元集支援,則應將「(C)」替換為著作權符號,如下所示
©
寫下「Copyright」這個詞,完全像那樣,用英文。不要將其翻譯成另一種語言。國際條約承認英文單字「Copyright」;翻譯成其他語言不具有法律意義。
最後,這是我們建議的授權條款縮寫表。任何縮寫後面都可以跟著「vversion[+]」,表示特定版本,或帶有「+」的更高版本,如上所示。對於 GNU 授權條款,始終以這種方式指示允許的版本。
對於 GPL 的額外權限例外情況,我們使用「/」作為分隔符;版本號碼可以像往常一樣跟在授權條款縮寫之後,如下面的範例所示。
GNU 通用公共授權條款,https://gnu.dev.org.tw/licenses/gpl.html。
GNU 寬鬆通用公共授權條款,https://gnu.dev.org.tw/licenses/lgpl.html。
帶有 Ada 例外的 GNU GPL。
Apache 軟體基金會授權條款,https://directory.fsf.org/wiki/License:Apache2.0。
用於 Perl 的 Artistic 授權條款,https://directory.fsf.org/wiki/License:ArtisticLicense2.0。
Expat 授權條款,https://directory.fsf.org/wiki/License:Expat。
Mozilla 公共授權條款,https://directory.fsf.org/wiki/License:MPLv2.0。
原始(4 條款)BSD 授權條款,與 GNU GPL 不相容,
https://directory.fsf.org/wiki/License:BSD_4Clause.
用於 PHP 的授權條款,https://directory.fsf.org/wiki/License:PHPv3.01。
屬於公共領域的非授權條款,
https://gnu.dev.org.tw/licenses/license-list.html#PublicDomain.
Python 的授權條款,https://directory.fsf.org/wiki/License:Python2.0.1。
修訂後的(3 條款)BSD,與 GNU GPL 相容,
https://directory.fsf.org/wiki/License:BSD_3Clause.
用於大多數 X Window 系統版本的簡單非著作權所有授權條款,https://directory.fsf.org/wiki/License:X11。
Zlib 的授權條款,https://directory.fsf.org/wiki/License:Zlib。
有關這些授權條款和更多資訊,請參閱 GNU 授權條款網頁,https://gnu.dev.org.tw/licenses/license-list.html。
標準的 --help
選項應在標準輸出上輸出關於如何調用程式的簡要說明文件,然後成功結束。一旦看到此選項,其他選項和參數都應被忽略,並且程式不應執行其正常功能。
在「--help」選項輸出的末尾附近,請放置幾行,給出錯誤回報的電子郵件地址、套件的首頁(通常為「https://gnu.dev.org.tw/software/pkg
」)以及使用 GNU 程式的協助通用頁面。格式應如下所示
Report bugs to: mailing-address pkg home page: <https://gnu.dev.org.tw/software/pkg/> General help using GNU software: <https://gnu.dev.org.tw/gethelp/>
可以提及其他適當的郵寄清單和網頁。
保持自由程式自由的另一個方面是鼓勵開發自由外掛程式,並阻止開發專有外掛程式。許多 GNU 程式根本不會有任何類似外掛程式的東西,但那些有的程式應該遵循這些做法。
首先,通用外掛程式架構設計應將外掛程式與原始碼緊密結合,以便外掛程式和基礎程式是同一個擴充程式的一部分。例如,對於 GCC,外掛程式接收並修改 GCC 的內部資料結構,因此顯然與基礎 GCC 形成一個擴充程式。
其次,您應該要求外掛程式開發人員聲明他們的外掛程式是在適當的授權條款下發行的。這應該通過簡單的程式化檢查來強制執行。例如,再次以 GCC 為例,外掛程式必須定義全域符號 plugin_is_GPL_compatible
,從而聲明外掛程式是在 GPL 相容授權條款下發行的(請參閱 GCC 內部文件中的 外掛程式)。
通過將此檢查添加到您的程式中,您並不是在創建新的法律要求。GPL 本身要求外掛程式必須是自由軟體,並以相容的授權條款授權。只要您遵循上述第一條規則,將外掛程式與您的原始程式緊密結合,GPL 和 AGPL 就已經要求這些外掛程式以相容的授權條款發行。外掛程式中的符號定義——或在您的程式中效果最好的任何等效方法——使得任何可能發行專有外掛程式的人都難以在法律上為自己辯護。如果關於此事的案件訴諸法院,我們可以指出該符號作為證據,證明外掛程式開發人員理解該授權條款有此要求。
以下是 GNU 程式使用的長選項表。它肯定是不完整的,但我們的目標是列出新程式可能希望相容的所有選項。如果您使用表中尚未存在的名稱,請發送電子郵件至 bug-standards@gnu.org,並附上其含義列表,以便我們可以更新表格。
tar
中的「-N」。
du
、ls
、nm
、stty
、uname
和 unexpand
中的「-a」。
diff
中的「-a」。
ls
中的「-A」。
etags
、tee
、time
中的「-a」;tar
中的「-r」。
cp
中的「-a」。
shar
中的「-n」。
m4
中的「-l」。
diff
中的「-a」。
gawk
中的「-v」。
make
中的「-W」。
make
中的「-o」。
recode
中的「-a」。
wdiff
中的「-a」。
ptx
中的「-A」。
wdiff
中的「-n」。
對於伺服器程式,在背景執行。
ctags
中的「-B」。
shar
中的「-f」。
用於 GDB 除錯器。
用於 GDB 除錯器。
tac
中的「-b」。
cpio
和 diff
中的「-b」。
shar
中的「-b」。
用於 cpio
和 tar
。
head
和 tail
中的「-b」。
ptx
中的「-b」。
用於各種程式以縮短輸出。
head
、split
和 tail
中的「-c」。
etags
中的「-C」。
tar
中的「-A」。
用於各種程式以指定要使用的目錄。
‘-c’ 在 chgrp
和 chown
中。
‘-F’ 在 ls
中。
‘-c’ 在 recode
中。
‘-c’ 在 su
中;‘-x’ 在 GDB 中。
‘-d’ 在 tar
中。
用於 gawk
中。
‘-Z’ 在 tar
和 shar
中。
tar
中的「-A」。
‘-w’ 在 tar
中。
用於 diff
中。
‘-W copyleft’ 在 gawk
中。
‘-C’ 在 ptx
、recode
和 wdiff
中;‘-W copyright’ 在 gawk
中。
用於 GDB 除錯器。
‘-q’ 在 who
中。
‘-l’ 在 du
中。
用於 tar
和 cpio
中。
‘-c’ 在 shar
中。
‘-x’ 在 ctags
中。
‘-d’ 在 touch
中。
‘-d’ 在 make
和 m4
中;‘-t’ 在 Bison 中。
‘-D’ 在 m4
中。
‘-d’ 在 Bison 和 ctags
中。
‘-D’ 在 tar
中。
‘-L’ 在 chgrp
、chown
、cpio
、du
、ls
和 tar
中。
‘-D’ 在 du
中。
指定一個 I/O 裝置(特殊檔案名稱)。
‘-d’ 在 recode
中。
‘-d’ 在 look
中。
‘-d’ 在 tar
中。
‘-n’ 在 csplit
中。
在各種程式中指定要使用的目錄。在 ls
中,表示顯示目錄本身而不是其內容。在 rm
和 ln
中,表示不特別處理目錄的連結。
‘-x’ 在 strip
中。
‘-X’ 在 strip
中。
‘-n’ 在 make
中。
‘-e’ 在 diff
中。
‘-z’ 在 csplit
中。
‘-x’ 在 wdiff
中。
‘-z’ 在 wdiff
中。
‘-N’ 在 diff
中。
‘-e’ 在 make
中。
‘-e’ 在 xargs
中。
用於 GDB 除錯器。
用於 makeinfo
中。
‘-o’ 在 m4
中。
‘-b’ 在 ls
中。
‘-X’ 在 tar
中。
用於 GDB 除錯器。
‘-x’ 在 xargs
中。
‘-e’ 在 unshar
中。
‘-t’ 在 diff
中。
‘-e’ 在 sed
中。
‘-g’ 在 nm
中。
‘-i’ 在 cpio
中;‘-x’ 在 tar
中。
‘-f’ 在 finger
中。
‘-f’ 在 su
中。
‘-E’ 在 m4
中。
‘-f’ 在 gawk
、info
、make
、mt
、sed
和 tar
中。
‘-F’ 在 gawk
中。
‘-b’ 在 Bison 中。
‘-F’ 在 ls
中。
‘-T’ 在 tar
中。
用於 makeinfo
中。
‘-F’ 在 ptx
中。
‘-y’ 在 Bison 中。
‘-f’ 在 tail
中。
用於 makeinfo
中。
‘-f’ 在 cp
、ln
、mv
和 rm
中。
‘-F’ 在 shar
中。
對於伺服器程式,在前景執行;換句話說,不要做任何特殊處理使伺服器在背景執行。
用於 ls
、time
和 ptx
中。
‘-F’ 在 m4
中。
用於 GDB 除錯器。
‘-g’ 在 ptx
中。
‘-x’ 在 tar
中。
‘-i’ 在 ul
中。
‘-g’ 在 recode
中。
‘-g’ 在 install
中。
‘-z’ 在 tar
和 shar
中。
‘-H’ 在 m4
中。
‘-h’ 在 objdump
和 recode
中
‘-H’ 在 who
中。
用於詢問簡短的使用資訊。
‘-d’ 在 shar
中。
‘-q’ 在 ls
中。
在 makeinfo
中,輸出 HTML 格式。
‘-u’ 在 who
中。
‘-D’ 在 diff
中。
‘-I’ 在 ls
中;‘-x’ 在 recode
中。
‘-w’ 在 diff
中。
‘-B’ 在 ls
中。
‘-B’ 在 diff
中。
‘-f’ 在 look
和 ptx
中;‘-i’ 在 diff
和 wdiff
中。
‘-i’ 在 make
中。
‘-i’ 在 ptx
中。
‘-I’ 在 etags
中。
‘-f’ 在 Oleo 中。
‘-i’ 在 tee
中。
‘-I’ 在 diff
中。
‘-b’ 在 diff
中。
‘-i’ 在 tar
中。
‘-i’ 在 etags
中;‘-I’ 在 m4
中。
‘-I’ 在 make
中。
‘-G’ 在 tar
中。
‘-i’、‘-l’ 和 ‘-m’ 在 Finger 中。
在某些程式中,指定要讀取作為使用者初始化檔案的檔案名稱。
‘-i’ 在 expand
中。
‘-T’ 在 diff
中。
‘-i’ 在 ls
中。
‘-i’ 在 cp
、ln
、mv
、rm
中;‘-e’ 在 m4
中;‘-p’ 在 xargs
中;‘-w’ 在 tar
中。
‘-p’ 在 shar
中。
用於 date
‘-j’ 在 make
中。
‘-n’ 在 make
中。
‘-k’ 在 make
中。
‘-k’ 在 csplit
中。
‘-k’ 在 du
和 ls
中。
‘-l’ 在 etags
中。
‘-l’ 在 wdiff
中。
‘-g’ 在 shar
中。
‘-C’ 在 split
中。
用於 split
、head
和 tail
中。
‘-l’ 在 cpio
中。
用於 gawk
中。
‘-t’ 在 cpio
中;‘-l’ 在 recode
中。
‘-t’ 在 tar
中。
‘-N’ 在 ls
中。
‘-l’ 在 make
中。
用於 su
中。
用於 uname
中。
‘-M’ 在 ptx
中。
‘-m’ 在 hello
和 uname
中。
‘-d’ 在 cpio
中。
‘-f’ 在 make
中。
用於 GDB 除錯器。
‘-n’ 在 xargs
中。
‘-n’ 在 xargs
中。
‘-l’ 在 xargs
中。
‘-l’ 在 make
中。
‘-P’ 在 xargs
中。
‘-T’ 在 who
中。
‘-T’ 在 who
中。
‘-d’ 在 diff
中。
‘-M’ 在 shar
中。
‘-m’ 在 install
、mkdir
和 mkfifo
中。
‘-m’ 在 tar
中。
‘-M’ 在 tar
中。
‘-a’ 在 Bison 中。
‘-L’ 在 m4
中。
‘-a’ 在 shar
中。
make
中的「-W」。
‘-r’ 在 make
中。
‘-w’ 在 shar
中。
‘-x’ 在 shar
中。
‘-3’ 在 wdiff
中。
‘-c’ 在 touch
中。
‘-D’ 在 etags
中。
‘-1’ 在 wdiff
中。
‘-d’ 在 cp
中。
‘-2’ 在 wdiff
中。
‘-S’ 在 make
中。
‘-l’ 在 Bison 中。
‘-P’ 在 shar
中。
‘-e’ 在 gprof
中。
‘-R’ 在 etags
中。
‘-p’ 在 nm
中。
不顯示啟動畫面。
用於 makeinfo
中。
‘-a’ 在 gprof
中。
‘-E’ 在 gprof
中。
‘-m’ 在 shar
中。
用於 makeinfo
中。
用於 emacsclient
中。
用於各種程式中以抑制警告訊息。
‘-n’ 在 info
中。
‘-n’ 在 uname
中。
‘-f’ 在 cpio
中。
‘-n’ 在 objdump
中。
‘-0’ 在 xargs
中。
‘-n’ 在 cat
中。
‘-b’ 在 cat
中。
‘-n’ 在 nm
中。
‘-n’ 在 cpio
和 ls
中。
用於 GDB 除錯器。
‘-o’ 在 tar
中。
make
中的「-o」。
‘-l’ 在 tar
、cp
和 du
中。
‘-o’ 在 ptx
中。
‘-f’ 在 gprof
中。
‘-F’ 在 gprof
中。
‘-o’ 在 getopt
、fdlist
、fdmount
、fdmountd
和 fdumount
中。
在各種程式中,指定輸出檔案名稱。
‘-o’ 在 shar
中。
‘-o’ 在 rm
中。
‘-c’ 在 unshar
中。
‘-o’ 在 install
中。
‘-l’ 在 diff
中。
用於 makeinfo
中。
‘-p’ 在 mkdir
和 rmdir
中。
‘-p’ 在 ul
中。
‘-p’ 在 cpio
中。
‘-P’ 在 finger
中。
‘-c’ 在 cpio
和 tar
中。
用於 gawk
中。
‘-P’ 在 m4
中。
‘-f’ 在 csplit
中。
用於 tar
和 cp
中。
‘-p’ 在 su
中。
‘-m’ 在 cpio
中。
‘-s’ 在 tar
中。
‘-p’ 在 tar
中。
‘-l’ 在 diff
中。
‘-L’ 在 cmp
中。
‘-p’ 在 make
中。
‘-w’ 在 make
中。
‘-o’ 在 nm
中。
‘-s’ 在 nm
中。
‘-p’ 在 wdiff
中。
‘-p’ 在 ed
中。
指定一個 HTTP 代理伺服器。
‘-X’ 在 shar
中。
‘-q’ 在 make
中。
在許多程式中用於抑制一般輸出。每個接受 ‘--quiet’ 的程式也應接受 ‘--silent’ 作為同義詞。
‘-Q’ 在 shar
中
‘-Q’ 在 ls
中。
‘-n’ 在 diff
中。
用於 gawk
中。
‘-B’ 在 tar
中。
用於 GDB 除錯器。
‘-n’ 在 make
中。
‘-R’ 在 tar
中。
用於 chgrp
、chown
、cp
、ls
、diff
和 rm
中。
‘-r’ 在 touch
中。
‘-r’ 在 ptx
中。
‘-r’ 在 tac
和 etags
中。
‘-r’ 在 uname
中。
‘-R’ 在 m4
中。
‘-r’ 在 objdump
中。
‘-r’ 在 cpio
中。
‘-i’ 在 xargs
中。
‘-s’ 在 diff
中。
‘-a’ 在 cpio
中。
‘-r’ 在 ls
和 nm
中。
‘-f’ 在 diff
中。
‘-R’ 在 ptx
中。
‘-s’ 在 tar
中。
‘-p’ 在 tar
中。
‘-g’ 在 stty
中。
用於 GDB 除錯器。
‘-S’ 在 ptx
中。
‘-S’ 在 du
中。
‘-s’ 在 tac
中。
由 recode
用於選擇檔案或管線以進行序列傳遞。
‘-s’ 在 su
中。
‘-A’ 在 cat
中。
‘-p’ 在 diff
中。
‘-E’ 在 cat
中。
‘-F’ 在 diff
中。
‘-T’ 在 cat
中。
在許多程式中用於抑制一般輸出。每個接受 ‘--silent’ 的程式也應接受 ‘--quiet’ 作為同義詞。
‘-s’ 在 ls
中。
指定網路伺服器用於其 Socket 的檔案描述符,而不是開啟和綁定新的 Socket。這提供了一種在非特權程序中運行通常需要保留埠號的伺服器的方法。
用於 ls
中。
‘-W source’ 在 gawk
中。
‘-S’ 在 tar
中。
‘-H’ 在 diff
中。
‘-E’ 在 unshar
中。
‘-L’ 在 shar
中。
‘-s’ 在 cat
中。
‘-w’ 在 wdiff
中。
‘-y’ 在 wdiff
中。
用於 tar
和 diff
中,指定從目錄中的哪個檔案開始處理。
‘-s’ 在 wdiff
中。
‘-S’ 在 shar
中。
‘-S’ 在 make
中。
‘-s’ 在 recode
中。
‘-s’ 在 install
中。
‘-s’ 在 strip
中。
‘-S’ 在 strip
中。
‘-s’ 在 shar
中。
‘-S’ 在 cp
、ln
、mv
中。
‘-b’ 在 csplit
中。
‘-s’ 在 gprof
中。
‘-s’ 在 du
中。
‘-s’ 在 ln
中。
用於 GDB 和 objdump
中。
‘-s’ 在 m4
中。
‘-s’ 在 uname
中。
‘-t’ 在 expand
和 unexpand
中。
‘-T’ 在 ls
中。
‘-T’ 在 tput
和 ul
中。‘-t’ 在 wdiff
中。
diff
中的「-a」。
‘-T’ 在 shar
中。
用於 ls
和 touch
中。
指定在放棄某些操作之前要等待多久。
‘-O’ 在 tar
中。
‘-c’ 在 du
中。
‘-t’ 在 make
、ranlib
和 recode
中。
‘-t’ 在 m4
中。
‘-t’ 在 hello
中;‘-W traditional’ 在 gawk
中;‘-G’ 在 ed
、m4
和 ptx
中。
用於 GDB 除錯器。
‘-t’ 在 ctags
中。
‘-T’ 在 ctags
中。
‘-t’ 在 ptx
中。
‘-z’ 在 tar
中。
‘-u’ 在 cpio
中。
‘-U’ 在 m4
中。
‘-u’ 在 nm
中。
‘-u’ 在 cp
、ctags
、mv
、tar
中。
用於 gawk
中;與 ‘--help’ 相同。
‘-B’ 在 shar
中。
‘-V’ 在 shar
中。
印出更多關於進度的資訊。許多程式都支援此選項。
‘-W’ 在 tar
中。
印出版本號碼。
‘-V’ 在 cp
、ln
、mv
中。
‘-v’ 在 ctags
中。
‘-V’ 在 tar
中。
make
中的「-W」。
‘-l’ 在 shar
中。
‘-w’ 在 ls
和 ptx
中。
‘-W’ 在 ptx
中。
‘-T’ 在 who
中。
‘-z’ 在 gprof
中。
OID (物件識別碼) 1.3.6.1.4.1.11591 已分配給 GNU 專案(感謝 Sergey Poznyakoff)。這些用於 SNMP、LDAP、X.509 憑證等等。網站 https://www.alvestrand.no/objectid 有一個(自願性)列出許多 OID 分配的清單。
如果您需要為您的 GNU 套件新增一個槽位,請寫信至 maintainers@gnu.org。以下是目前已分配弧線的列表
1.3.6.1.4.1.11591 GNU 1.3.6.1.4.1.11591.1 GNU Radius 1.3.6.1.4.1.11591.2 GnuPG 1.3.6.1.4.1.11591.2.1 notation 1.3.6.1.4.1.11591.2.1.1 pkaAddress 1.3.6.1.4.1.11591.3 GNU Radar 1.3.6.1.4.1.11591.4 GNU GSS 1.3.6.1.4.1.11591.5 GNU Mailutils 1.3.6.1.4.1.11591.6 GNU Shishi 1.3.6.1.4.1.11591.7 GNU Radio 1.3.6.1.4.1.11591.8 GNU Dico 1.3.6.1.4.1.11591.9 GNU Rush 1.3.6.1.4.1.11591.12 digestAlgorithm 1.3.6.1.4.1.11591.12.2 TIGER/192 1.3.6.1.4.1.11591.13 encryptionAlgorithm 1.3.6.1.4.1.11591.13.2 Serpent 1.3.6.1.4.1.11591.13.2.1 Serpent-128-ECB 1.3.6.1.4.1.11591.13.2.2 Serpent-128-CBC 1.3.6.1.4.1.11591.13.2.3 Serpent-128-OFB 1.3.6.1.4.1.11591.13.2.4 Serpent-128-CFB 1.3.6.1.4.1.11591.13.2.21 Serpent-192-ECB 1.3.6.1.4.1.11591.13.2.22 Serpent-192-CBC 1.3.6.1.4.1.11591.13.2.23 Serpent-192-OFB 1.3.6.1.4.1.11591.13.2.24 Serpent-192-CFB 1.3.6.1.4.1.11591.13.2.41 Serpent-256-ECB 1.3.6.1.4.1.11591.13.2.42 Serpent-256-CBC 1.3.6.1.4.1.11591.13.2.43 Serpent-256-OFB 1.3.6.1.4.1.11591.13.2.44 Serpent-256-CFB 1.3.6.1.4.1.11591.14 CRC algorithms 1.3.6.1.4.1.11591.14.1 CRC 32 1.3.6.1.4.1.11591.15 ellipticCurve 1.3.6.1.4.1.11591.15.1 Ed25519
如果程式通常僅使用幾 MB 的記憶體,則不必費力減少記憶體使用量。例如,如果由於其他原因,處理超過幾 MB 長度的檔案是不切實際的,則將整個輸入檔案讀入記憶體以進行操作是合理的。
但是,對於諸如 cat
或 tail
之類的程式,它們可以有效地處理非常大的檔案,避免使用會人為限制其可以處理的檔案大小的技術非常重要。如果程式按行工作並且可以應用於任意使用者提供的輸入檔案,則它應僅將一行保留在記憶體中,因為這並不難,並且使用者將希望能夠操作大於一次可以容納在記憶體中的輸入檔案。
如果您的程式建立複雜的資料結構,只需在記憶體中建立它們,如果 malloc
返回 NULL
,則給出致命錯誤。
諸如 valgrind
之類的記憶體分析工具可能很有用,但不要僅僅為了避免其誤報而使程式複雜化。例如,如果在進程退出之前一直使用記憶體,則不要僅僅為了消除此類工具的警告而釋放它。
程式應準備好在 /usr 和 /etc 是唯讀檔案系統時運作。因此,如果程式管理日誌檔案、鎖定檔案、備份檔案、分數檔案或任何其他為內部目的而修改的檔案,則這些檔案不應儲存在 /usr 或 /etc 中。
有兩個例外。 /etc 用於儲存系統配置資訊;程式修改 /etc 中的檔案以更新系統配置是合理的。此外,如果使用者明確要求修改目錄中的一個檔案,則程式將其他檔案儲存在同一個目錄中也是合理的。
本章提供有關在編寫 GNU 軟體時如何最佳使用 C 語言的建議。
• 格式化 | 格式化您的原始碼。 | |
• 註解 | 為您的工作添加註解。 | |
• 語法慣例 | C 結構的清晰使用。 | |
• 名稱 | 命名變數、函數和檔案。 | |
• 系統可移植性 | 不同作業系統之間的可移植性。 | |
• CPU 可移植性 | 支援各種 CPU 類型。 | |
• 系統函數 | 可移植性和「標準」函式庫函數。 | |
• 國際化 | 國際化技術。 | |
• 字元集 | 預設使用 ASCII。 | |
• 引號字元 | 在 C locale 中使用 "..." 或 '...'。 | |
• Mmap | 如何安全地使用 mmap 。 |
請將原始碼行的長度保持在 79 個字元或更少,以便在最廣泛的環境中獲得最大的可讀性。
將開始 C 函數主體的左大括號放在第一欄非常重要,這樣它們將啟動一個 defun。多個工具會查找第一欄中的左大括號以查找 C 函數的開頭。這些工具將無法在未以此方式格式化的程式碼上運作。
避免在函數內部時將左大括號、左括號或左方括號放在第一欄中,這樣它們就不會啟動 defun。如果您發現將 struct
主體的定義視為 defun 很有用,則開始 struct
主體的左大括號可以放在第一欄中。
函數定義將函數名稱從第一欄開始也很重要。這有助於人們搜尋函數定義,也可能有助於某些工具識別它們。因此,使用標準 C 語法,格式如下
static char * concat (char *s1, char *s2) { … }
或者,如果您想使用傳統 C 語法,請像這樣格式化定義
static char * concat (s1, s2) /* Name starts in column one here */ char *s1, *s2; { /* Open brace in column one here */ … }
在標準 C 中,如果參數不能很好地放在一行中,請像這樣分割它
int lots_of_args (int an_integer, long a_long, short a_short, double a_double, float a_float) …
對於 struct
和 enum
類型,同樣將大括號放在第一欄中,除非整個內容可以放在一行中
struct foo { int a, b; }
or
struct foo { int a, b; }
本節的其餘部分給出了我們對 C 格式化樣式其他方面的建議,這也是版本 1.2 及更高版本中 indent
程式的預設樣式。它對應於選項
-nbad -bap -nbc -bbo -bl -bli2 -bls -ncdb -nce -cp1 -cs -di2 -ndj -nfc1 -nfca -hnl -i2 -ip5 -lp -pcs -psl -nsc -nsob
我們不認為這些建議是要求,因為如果兩個不同的程式具有不同的格式化樣式,則不會給使用者帶來任何問題。
但是,無論您使用哪種樣式,請始終如一地使用它,因為在一個程式中混合使用多種樣式往往看起來很難看。如果您要為現有程式貢獻變更,請遵循該程式的樣式。
對於函數主體,我們建議的樣式如下所示
if (x < foo (y, z)) haha = bar[4] + 5; else { while (z) { haha += foo (z, z); z--; } return ++x + bar (); }
當程式在左括號之前和逗號之後有空格時,我們發現它更容易閱讀。尤其是在逗號之後。
當您將表達式分割成多行時,請在運算符之前分割它,而不是在運算符之後分割。這是正確的方法
if (foo_this_is_long && bar > win (x, y, z) && remaining_condition)
盡量避免在相同的縮排層級上有兩個不同優先順序的運算符。例如,不要寫成這樣
mode = (inmode[j] == VOIDmode || GET_MODE_SIZE (outmode[j]) > GET_MODE_SIZE (inmode[j]) ? outmode[j] : inmode[j]);
相反,使用額外的括號,以便縮排顯示巢狀結構
mode = ((inmode[j] == VOIDmode || (GET_MODE_SIZE (outmode[j]) > GET_MODE_SIZE (inmode[j]))) ? outmode[j] : inmode[j]);
插入額外的括號,以便 Emacs 可以正確縮排程式碼。例如,如果您手動執行以下縮排,則看起來不錯,
v = rup->ru_utime.tv_sec*1000 + rup->ru_utime.tv_usec/1000 + rup->ru_stime.tv_sec*1000 + rup->ru_stime.tv_usec/1000;
但 Emacs 會更改它。添加一組括號會產生看起來同樣美觀,並且 Emacs 將保留的內容
v = (rup->ru_utime.tv_sec*1000 + rup->ru_utime.tv_usec/1000 + rup->ru_stime.tv_sec*1000 + rup->ru_stime.tv_usec/1000);
像這樣格式化 do-while 語句
do { a = foo (a); } while (a > 0);
請使用換頁符號(control-L)在邏輯位置將程式劃分為頁面(但不要在函數內)。頁面有多長並不重要,因為它們不必適合列印頁面。換頁符號應單獨出現在行中。
每個程式都應以註解開頭,簡要說明其用途。範例: ‘fmt - filter for simple filling of text’(fmt - 用於簡單文字填充的過濾器)。此註解應位於包含程式 ‘main’ 函數的原始碼檔案的頂部。
此外,請在每個原始碼檔案的開頭編寫簡短註解,其中包含檔案名稱以及一兩行關於檔案整體用途的說明。
請以英文在 GNU 程式中編寫註解,因為英文是幾乎所有國家/地區的所有程式設計師都能讀懂的一種語言。如果您英文寫得不好,請盡可能用英文寫註解,然後請其他人協助重寫它們。如果您無法用英文編寫註解,請找人與您合作並將您的註解翻譯成英文。
請在每個函數上添加註解,說明函數的作用、它取得的參數類型,以及參數的可能值及其含義和用途。如果 C 類型以其慣用方式使用,則無需用文字重複 C 參數宣告的含義。如果其使用方式有任何非標準之處(例如,類型為 char *
的參數實際上是字串的第二個字元的地址,而不是第一個字元的地址),或者任何可能的值都無法按預期方式運作(例如,不保證包含換行符號的字串可以運作),請務必說明。
如果有的話,也請說明傳回值的意義。
請在註解中每句句尾加上兩個空格,以便 Emacs 句子指令可以運作。此外,請編寫完整的句子並將第一個單字大寫。如果句子開頭是一個小寫識別字,請不要將其大寫!更改拼寫會使其成為不同的識別字。如果您不喜歡以小寫字母開頭句子,請以不同的方式編寫句子(例如,「識別字 lower-case 是…」)。
如果您使用參數名稱來說明參數值,則函數的註解會更清楚。變數名稱本身應為小寫,但在您談論值而不是變數本身時,請以大寫形式編寫。因此,「inode 節點編號 NODE_NUM」而不是「一個 inode」。
通常沒有必要在函數之前的註解中重述函數的名稱,因為讀者可以自己看到。當註解太長以至於函數本身會超出螢幕底部時,可能會出現例外情況。
每個靜態變數也應有一個註解,如下所示
/* Nonzero means truncate lines in the display; zero means continue them. */ int truncate_lines;
每個 ‘#endif’ 都應有一個註解,除非在不巢狀的簡短條件式(僅幾行)的情況下。註解應說明正在結束的條件式的條件,包括其含義。 ‘#else’ 應有一個註解,說明以下程式碼的條件和含義。例如
#ifdef foo … #else /* not foo */ … #endif /* not foo */
#ifdef foo … #endif /* foo */
但相反,對於 ‘#ifndef’,請以這種方式編寫註解
#ifndef foo … #else /* foo */ … #endif /* foo */
#ifndef foo … #endif /* not foo */
請明確宣告所有物件的類型。例如,您應該明確宣告函數的所有參數,並且您應該宣告函數傳回 int
而不是省略 int
。
有些程式設計師喜歡使用 GCC 的 ‘-Wall’ 選項,並且在每次它發出警告時就修改程式碼。如果你想這樣做,那就做吧。其他程式設計師則偏好不使用 ‘-Wall’,因為它會對有效且合理的程式碼發出警告,而他們不想修改這些程式碼。如果你想這樣做,那就做吧。編譯器應該是你的僕人,而不是你的主人。
不要為了安撫靜態分析工具(例如 lint
、clang
和 GCC,以及像 -Wconversion 和 -Wundef 這樣的額外警告選項)而把程式弄得難看。這些工具可以幫助找到錯誤和不清楚的程式碼,但它們也可能產生太多誤報,以至於為了消除它們而加入不必要的型別轉換、包裝函式和其他複雜的設計,反而會損害可讀性。例如,請不要僅僅為了安撫 lint 檢查器而插入轉換為 void
的型別轉換或呼叫無作用的函式。
外部函式和稍後在原始碼檔案中出現的函式的宣告,都應該放在檔案開頭附近的一個地方(在檔案中的第一個函式定義之前),或者放在標頭檔中。不要將 extern
宣告放在函式內部。
過去常見的做法是在一個函式內針對不同的值重複使用相同的區域變數(名稱如 tem
)。與其這樣做,不如為每個不同的目的宣告一個單獨的區域變數,並給它一個有意義的名稱。這不僅使程式更容易理解,也有助於優秀編譯器的最佳化。你也可以將每個區域變數的宣告移到包含其所有用途的最小範圍內。這會使程式碼更乾淨。
不要使用會遮蔽全域識別符的區域變數或參數。GCC 的 ‘-Wshadow’ 選項可以偵測到這個問題。
不要在跨越多行的單一宣告中宣告多個變數。而是在每一行開始一個新的宣告。例如,不要像這樣
int foo, bar;
而是寫成這樣
int foo, bar;
或是這樣
int foo; int bar;
(如果它們是全域變數,則每個變數都應該在前面加上註解。)
當你在另一個 if
敘述中巢狀了一個 if
-else
敘述時,務必在 if
-else
周圍加上大括號。因此,永遠不要像這樣寫
if (foo) if (bar) win (); else lose ();
永遠要像這樣寫
if (foo) { if (bar) win (); else lose (); }
如果你有一個巢狀在 else
敘述內的 if
敘述,可以將 else if
寫在同一行,像這樣,
if (foo) … else if (bar) …
它的 then
部分與前面的 then
部分一樣縮排,或者將巢狀的 if
放在大括號內,像這樣
if (foo) … else { if (bar) … }
不要在同一個宣告中同時宣告結構標籤以及變數或 typedef。而是應該分別宣告結構標籤,然後使用它來宣告變數或 typedef。
盡量避免在 if
條件式內進行賦值(while
條件式內的賦值是可以接受的)。例如,不要寫成這樣
if ((foo = (char *) malloc (sizeof *foo)) == NULL) fatal ("virtual memory exhausted");
而是寫成這樣
foo = (char *) malloc (sizeof *foo); if (foo == NULL) fatal ("virtual memory exhausted");
程式中全域變數和函式的名稱本身就是一種註解。所以不要選擇簡潔的名稱——相反地,尋找能夠提供關於變數或函式意義的有用資訊的名稱。在 GNU 程式中,名稱應該使用英文,就像其他的註解一樣。
區域變數的名稱可以較短,因為它們只在一個上下文中使用,而(推測上)註解會解釋它們的用途。
盡量限制你在符號名稱中使用縮寫。可以適度使用一些縮寫,解釋它們的含義,然後經常使用它們,但不要使用大量晦澀難懂的縮寫。
請使用底線來分隔名稱中的單字,以便 Emacs 的單字命令可以在它們內部使用。堅持使用小寫字母;保留大寫字母給巨集和 enum
常數,以及遵循統一慣例的名稱前綴。
例如,你應該使用像 ignore_space_change_flag
這樣的名稱;不要使用像 iCantReadThis
這樣的名稱。
指示是否已指定命令列選項的變數,應該根據選項的含義來命名,而不是根據選項字母。註解應該說明選項的確切含義及其字母。例如,
/* Ignore changes in horizontal whitespace (-b). */ int ignore_space_change_flag;
當你想定義具有常數整數值的名稱時,使用 enum
而不是 ‘#define’。GDB 知道列舉常數。
你可能需要確保如果這些檔案被載入到會縮短名稱的 MS-DOS 檔案系統上,檔案名稱都不會衝突。你可以使用 doschk
程式來測試這個問題。
有些 GNU 程式被設計為將檔案名稱限制在 14 個字元或更少,以避免在將它們讀入較舊的 System V 系統時發生檔案名稱衝突。請在現有的具有此功能的 GNU 程式中保留此功能,但在新的 GNU 程式中沒有必要這樣做。doschk
也會報告超過 14 個字元的檔案名稱。
在 Unix 世界中,「可移植性」指的是移植到不同的 Unix 版本。對於 GNU 程式來說,這種可移植性是理想的,但不是最重要的。
GNU 軟體的主要目的是作為 GNU 作業系統的一部分運行,使用 GNU 編譯器在各種硬體類型上編譯。因此,絕對必要的可移植性種類相當有限。支援基於 Linux 的 GNU 系統非常重要,因為它們是人們主要使用的 GNU 形式。
使 GNU 程式在 GNU 系統以外的作業系統上運作,並不是開發 GNU 套件的核心目標的一部分。你永遠不必這樣做。然而,使用者會要求你這樣做,並且配合這些要求是有用的——只要你不讓它主導專案或阻礙主要目標。
支援其他自由或近乎自由的作業系統(例如,*BSD)是很好的。支援各種 Unix 類型的系統是理想的,但不是最重要的。通常不會太難,所以你可能也會這樣做。但如果它真的變得困難,你不必認為這是一種義務。
在大多數情況下,將程式移植到更多平台是好的,但你不應該讓它佔用你太多的時間,以至於妨礙你以更核心的方式改進程式。如果它開始這樣做,請告訴使用者你不想再花費任何時間在這上面——必須由其他人編寫程式碼、偵錯、記錄等等,然後你才能安裝它。
你也可以因為技術原因拒絕移植補丁,就像拒絕使用者提交的任何其他補丁一樣。這取決於你。
實現大多數 Unix 類型系統可移植性的最簡單方法是使用 Autoconf。你的程式不太可能需要比 Autoconf 能提供的更多關於主機平台資訊的知識,僅僅因為大多數需要這種知識的程式已經被編寫出來了。
當有更高等級的替代方案(readdir
)時,避免使用半內部資料庫(例如,目錄)的格式。
至於不像 Unix 的系統,例如 MS-DOS、Windows、VMS、MVS 和較舊的 Macintosh 系統,支援它們通常需要大量的工作。在這種情況下,最好花時間添加在 GNU 和 GNU/Linux 上有用的功能,而不是支援其他不相容的系統。
如果你支援 Windows,請不要將其縮寫為 “win”。請參閱 商標。
通常我們完整地寫出 “Windows” 這個名稱,但是當簡潔性非常重要時(例如在檔案名稱和一些符號名稱中),我們會將其縮寫為 “w”。例如,在 GNU Emacs 中,我們在 Windows 特定檔案的檔案名稱中使用 ‘w32’,但 Windows 條件式的巨集稱為 WINDOWSNT
。原則上,也可能會有 ‘w64’。
在編譯 C 檔案時,定義「功能測試巨集」_GNU_SOURCE
是個好主意。當你在 GNU 或 GNU/Linux 上編譯時,這將啟用 GNU 函式庫擴充函式的宣告,並且如果你在程式中以其他方式定義相同的函式名稱,通常會給你一個編譯器錯誤訊息。(如果你希望程式更能移植到其他系統,你不必實際使用這些函式。)
但是,無論你是否使用這些 GNU 擴充功能,你都應該避免將它們的名稱用於任何其他含義。這樣做會使你的程式碼難以移轉到其他 GNU 程式中。
即使是 GNU 系統也會因為 CPU 類型的差異而有所不同——例如,位元組順序和對齊要求的差異。處理這些差異絕對是必要的。然而,不要努力迎合 int
小於 32 位元的可能性。我們在 GNU 中不支援 16 位元的機器。
你不需要迎合 long
小於指標和 size_t
的可能性。我們知道有一個這樣的平台:Microsoft Windows 上的 64 位元程式。如果你關心讓你的套件在使用 Mingw64 的 Windows 上運行,你需要處理 8 位元組的指標和 4 位元組的 long
,這會破壞這個程式碼
printf ("size = %lu\n", (unsigned long) sizeof array); printf ("diff = %ld\n", (long) (pointer2 - pointer1));
是否在你的套件中支援 Mingw64 和 Windows 總體而言,取決於你的選擇。GNU 專案沒有說你有任何責任這樣做。我們的目標是取代專有系統,包括 Windows,而不是增強它們。如果人們迫使你的程式在 Windows 上運行,而你沒有興趣,你可以回應說:「切換到 GNU/Linux — 你的自由取決於它。」
像 off_t
這樣的預定義檔案大小類型是一個例外:它們在許多平台上比 long
更長,因此上面的程式碼無法與它們一起使用。可移植地列印 off_t
值的一種方法是自己逐位列印它的數字。
不要假設 int
物件的位址也是其最低有效位元組的位址。這在 big-endian 機器上是錯誤的。因此,不要犯以下錯誤
int c; … while ((c = getchar ()) != EOF) write (file_descriptor, &c, 1);
相反,使用 unsigned char
如下。(unsigned
是為了可移植到不尋常的系統,在這些系統中 char
是有號的,並且有整數溢位檢查。)
int c; while ((c = getchar ()) != EOF) { unsigned char u = c; write (file_descriptor, &u, 1); }
如果可以的話,避免將指標轉換為整數。這種轉換會大大降低可移植性,並且在大多數程式中,它們很容易避免。在必須將指標轉換為整數的情況下——例如,Lisp 直譯器將型別資訊以及位址儲存在一個字組中——你必須明確規定處理不同的字組大小。你還需要為系統做出規定,在這些系統中,你可以從 malloc
獲得的正常位址範圍從遠離零的地方開始。
從歷史上看,C 實作差異很大,許多系統缺乏 ANSI/ISO C89 的完整實作。然而,現在,所有實用的系統都有 C89 編譯器,而 GNU C 幾乎支援所有 C99 和一些 C11。同樣地,大多數系統實作了 POSIX.1-2001 函式庫和工具,許多系統都具有 POSIX.1-2008。
因此,幾乎沒有理由支援舊的 C 或非 POSIX 系統,你可能希望利用標準 C 和 POSIX 來編寫更清晰、更可移植或更快速的程式碼。你應該在可能的情況下使用標準介面;但是,如果 GNU 擴充功能使你的程式更易於維護、更強大或在其他方面更好,請不要猶豫使用它們。在任何情況下,都不要自行宣告系統函式;那是衝突的根源。
儘管有這些標準,幾乎每個函式庫函式在某些系統上都存在某種可移植性問題。以下是一些範例
open
在許多平台上,帶有尾隨 /
的名稱處理不當。
printf
long double
可能未實作;浮點值 Infinity 和 NaN 通常處理不當;高精度的輸出可能不正確。
readlink
可能會傳回 int
而不是 ssize_t
。
scanf
在 Windows 上,失敗時不會設定 errno
。
Gnulib 在這方面有很大的幫助。Gnulib 在許多缺乏標準介面的系統上提供了標準介面的實作,包括增強型 GNU 介面的可移植實作,從而使其使用具有可移植性,以及 POSIX-1.2008 介面,其中一些甚至在最新的 GNU 系統中也缺失。
Gnulib 還提供了許多有用的非標準介面;例如,標準資料結構(雜湊表、二元樹)的 C 實作、用於記憶體配置函式(xmalloc
、xrealloc
)的錯誤檢查型別安全包裝函式,以及錯誤訊息的輸出。
Gnulib 與 GNU Autoconf 和 Automake 整合,以消除程式設計師編寫可移植程式碼的大部分負擔:Gnulib 使你的 configure 腳本自動確定缺少哪些功能,並使用 Gnulib 程式碼來提供缺少的組件。
Gnulib 和 Autoconf 手冊中有關可移植性的廣泛章節:簡介,在 Gnulib 中;以及參閱 可移植的 C 和 C++,在 Autoconf 中。請查閱它們以獲得更多詳細資訊。
GNU 有一個名為 GNU gettext 的函式庫,它可以輕鬆地將程式中的訊息翻譯成各種語言。你應該在每個程式中使用這個函式庫。對於程式中顯示的訊息使用英文,並讓 gettext 提供將它們翻譯成其他語言的方法。
使用 GNU gettext 涉及在每個可能需要翻譯的字串周圍放置對 gettext
巨集的呼叫——像這樣
printf (gettext ("Processing file '%s'..."), file);
這允許 GNU gettext 將字串 "Processing file '%s'..."
替換為翻譯後的版本。
一旦程式使用了 gettext,請務必在添加需要翻譯的新字串時,呼叫 gettext
。
在套件中使用 GNU gettext 涉及為套件指定一個文字網域名稱。文字網域名稱用於將此套件的翻譯與其他套件的翻譯分開。通常,文字網域名稱應該與套件的名稱相同——例如,GNU 核心工具程式的 ‘coreutils’。
為了使 gettext 能夠良好地工作,請避免編寫對單字或句子的結構做出假設的程式碼。當你希望句子的精確文字根據資料而變化時,請使用兩個或多個替代字串常數,每個常數都包含完整的句子,而不是將條件化的單字或短語插入到單一句子框架中。
以下是一個不應該做的事情的範例
printf ("%s is full", capacity > 5000000 ? "disk" : "floppy disk");
如果你將 gettext 應用於所有字串,像這樣,
printf (gettext ("%s is full"), capacity > 5000000 ? gettext ("disk") : gettext ("floppy disk"));
翻譯人員幾乎不會知道 “disk” 和 “floppy disk” 是要替換到另一個字串中的。更糟的是,在某些語言(如法語)中,這種結構將無法運作:「full」一詞的翻譯取決於句子第一部分的性別;對於 “disk” 和 “floppy disk” 來說,它恰好是不一樣的。
完整的句子可以沒有問題地翻譯
printf (capacity > 5000000 ? gettext ("disk is full") : gettext ("floppy disk is full"));
類似的問題出現在句子結構層面,如下面的程式碼
printf ("# Implicit rule search has%s been done.\n", f->tried_implicit ? "" : " not");
為此程式碼添加 gettext
呼叫無法為所有語言提供正確的結果,因為某些語言中的否定需要在句子中的多個位置添加單字。相比之下,如果程式碼像這樣開始,添加 gettext
呼叫就可以直接完成工作
printf (f->tried_implicit ? "# Implicit rule search has been done.\n", : "# Implicit rule search has not been done.\n");
另一個範例是這個
printf ("%d file%s processed", nfiles, nfiles != 1 ? "s" : "");
這個範例的問題在於它假設複數形式是透過添加 ‘s’ 來構成的。如果你將 gettext 應用於格式字串,像這樣,
printf (gettext ("%d file%s processed"), nfiles, nfiles != 1 ? "s" : "");
訊息可以使用不同的單字,但它仍然會被迫使用 ‘s’ 作為複數形式。以下是一種更好的方法,將 gettext 獨立地應用於兩個字串
printf ((nfiles != 1 ? gettext ("%d files processed") : gettext ("%d file processed")), nfiles);
但這仍然不適用於像波蘭語這樣的語言,波蘭語有三種複數形式:一種用於 nfiles == 1,一種用於 nfiles == 2、3、4、22、23、24、...,另一種用於其餘情況。GNU 的 ngettext
函式解決了這個問題
printf (ngettext ("%d files processed", "%d file processed", nfiles), nfiles);
在 GNU 原始碼註解、文字文件和其他情境中,最好堅持使用 ASCII 字元集(純文字,7 位元字元),除非有充分的理由因為應用程式領域而做其他事情。例如,如果原始碼處理法國大革命曆,則其文字字串中包含月份名稱(如 “Floréal”)中的重音符號字元是可以接受的。此外,在變更日誌中使用非 ASCII 字元來表示貢獻者的姓名是可以接受的(但不是必需的)(請參閱 變更日誌)。
如果你需要使用非 ASCII 字元,你通常應該堅持使用一種編碼,當然在單個檔案中是這樣。UTF-8 可能是最佳選擇。
在 C locale 中,GNU 程式的輸出應該堅持使用純 ASCII 作為給使用者的訊息中的引號字元:最好是 0x22(‘"’)或 0x27(‘'’),用於開頭和結尾引號。儘管 GNU 程式傳統上使用 0x60(‘`’)作為開頭引號和 0x27(‘'’)作為結尾引號,但如今引號 ‘`like this'’ 通常被非對稱地呈現,因此引號 ‘"like this"’ 或 ‘'like this'’ 通常看起來更好。
GNU 程式在非 C locale 中產生特定於 locale 的引號是可以接受的,但不是必需的。例如
printf (gettext ("Processing file '%s'..."), file);
在這裡,法語翻譯可能會導致 gettext
傳回字串 "Traitement de fichier ‹ %s ›..."
,產生更適合法語 locale 的引號。
有時程式可能需要直接使用開頭和結尾引號。依照慣例,gettext
將字串 ‘"`"’ 翻譯為開頭引號,將字串 ‘"'"’ 翻譯為結尾引號,程式可以使用這些翻譯。不過,一般來說,最好在較長字串的上下文中翻譯引號字元。
如果你的程式的輸出有可能被另一個程式解析,最好提供一個選項,使這種解析可靠。例如,你可以使用 C 語言或 Bourne shell 中的慣例來跳脫特殊字元。例如,請參閱 GNU ls
的 --quoting-style 選項。
如果你使用 mmap
來讀取或寫入檔案,不要假設它在所有檔案上都有效,或是在所有檔案上都失敗。它可能在某些檔案上有效,而在其他檔案上失敗。
使用 mmap
的正確方法是在你想使用的特定檔案上嘗試它——如果 mmap
無效,則退而求其次,使用 read
和 write
以另一種方式完成工作。
需要這種預防措施的原因是,GNU 核心(HURD)提供了一個使用者可擴充的檔案系統,其中可能存在許多不同種類的「普通檔案」。它們中的許多都支援 mmap
,但有些則不支援。重要的是要使程式能夠處理所有這些種類的檔案。
理想情況下,GNU 程式應該附帶完整的自由文件,足以用於參考和教學目的。如果套件可以被程式設計或擴充,則文件應涵蓋程式設計或擴充它,以及僅僅使用它。
• GNU 手冊 | 撰寫適當的手冊。 | |
• 文件字串與手冊 | 編譯文件字串不會產生手冊。 | |
• 手冊結構細節 | 特定的結構慣例。 | |
• 手冊的授權 | 撰寫手冊的發行條款。 | |
• 手冊致謝 | 感謝文件貢獻者。 | |
• 印刷手冊 | 提及印刷手冊。 | |
• NEWS 檔案 | NEWS 檔案補充手冊。 | |
• 變更日誌 | 記錄變更。 | |
• Man Pages | Man pages 是次要的。 | |
• 閱讀其他手冊 | 從其他手冊中學習的程度。 |
GNU 系統的首選文件格式是 Texinfo 格式化語言。每個 GNU 套件(理想情況下)都應該有 Texinfo 格式的文件,既用於參考,也用於學習者。Texinfo 使使用 TeX 生成高品質的格式化書籍,以及生成 Info 檔案成為可能。也可以從 Texinfo 原始碼生成 HTML 輸出。請參閱 Texinfo 手冊,可以是硬拷貝,也可以是透過 info
或 Emacs Info 子系統(C-h i)提供的線上版本。
如今,一些其他格式(如 Docbook 和 Sgmltexi)可以自動轉換為 Texinfo。透過這種方式轉換來產生 Texinfo 文件是可以接受的,只要它能產生良好的結果。
確保你的手冊對於對該主題一無所知並直接閱讀它的讀者來說是清晰易懂的。這意味著在開頭涵蓋基本主題,而只在稍後涵蓋進階主題。這也意味著在第一次使用每個專業術語時就對其進行定義。
請記住,GNU 手冊(和其他 GNU 文件)的受眾是全球性的,並且將被使用多年,甚至數十年。這意味著讀者可能具有非常不同的文化參考點。從現在起幾十年後,除了老年人之外的所有人都將具有非常不同的文化參考點;今天「每個人都知道」的許多事情可能大部分都被遺忘了。
因此,盡量避免以依賴文化參考點來正確理解的方式寫作,或者以會阻礙不認識它們的人閱讀的方式提及它們。
同樣地,在你的單字選擇(除了技術術語)、語言結構和拼字方面要保守:目標是使十年前的讀者也能理解它們。在任何關於潮流的競賽中,GNU 寫作甚至不應該有資格參加。
如果與空間或時間上本地化的參考點或事實直接相關,或作為題外話,則可以偶爾提及一次。當這些少數事物(在任何情況下都很突出)不再有意義時,更改它們不會花費太多精力。
相比之下,當 GNU 和自由軟體運動的概念相關時,始終可以提及它們。這些是我們訊息的核心部分,因此我們應該利用機會提及它們。它們是基本的道德立場,因此它們幾乎永遠不會改變。
程式設計師傾向於將程式的結構延續為其文件的結構。但是,這種結構不一定適合解釋如何使用程式;對於使用者來說,它可能是無關緊要且令人困惑的。
相反,組織文件的正確方法是根據使用者在閱讀時會想到的概念和問題。這個原則適用於每個層次,從最低層次(段落中句子的順序)到最高層次(手冊中章節主題的順序)。有時,這種思想結構與正在記錄的軟體的實作結構相符——但通常它們是不同的。學習編寫良好文件的重要部分是學會注意到你何時不假思索地按照實作結構組織了文件,停下來,並尋找更好的替代方案。
例如,GNU 系統中的每個程式都應該記錄在一個手冊中;但這並不意味著每個程式都應該有自己的手冊。那樣將會遵循實作的結構,而不是幫助使用者理解的結構。
相反,每個手冊都應該涵蓋一個連貫的主題。例如,我們沒有針對 diff
的手冊和針對 diff3
的手冊,而是有一個針對「檔案比較」的手冊,其中涵蓋了這兩個程式以及 cmp
。透過將這些程式放在一起記錄,我們可以使整個主題更清晰。
討論程式的手冊當然應該記錄程式的所有命令列選項及其所有命令。它應該提供它們的使用範例。但不要將手冊組織成功能列表。相反,按邏輯方式,按子主題組織它。解決使用者在思考程式所做的工作時會提出的問題。不要只是告訴讀者每個功能可以做什麼——說明它適用於哪些工作,並展示如何將其用於這些工作。解釋建議的用法,以及使用者應避免的用法類型。
總體而言,GNU 手冊應該同時作為教學和參考。它應該設定為可以透過 Info 方便地存取每個主題,並且可以從頭到尾直接閱讀(附錄除外)。GNU 手冊應該為從頭開始閱讀的初學者提供良好的入門,並且還應該提供駭客想要的所有細節。Bison 手冊是這方面的一個很好的範例——請看一下它,看看我們的意思。
這並不像最初聽起來那麼困難。將每個章節安排為其主題的邏輯分解,但對章節進行排序,並編寫其文字,以便直接閱讀章節是有意義的。在將書組織成章節以及將章節組織成段落時,也要這樣做。警句是,在每個點,解決前面文字提出的最基本和最重要的問題。
如有必要,在手冊的開頭添加額外的章節,這些章節純粹是教學性的,涵蓋該主題的基礎知識。這些為初學者提供了理解手冊其餘部分的框架。Bison 手冊提供了一個關於如何做到這一點的良好範例。
為了作為參考,手冊應該有一個索引,列出作為程式一部分的所有函式、變數、選項和重要概念。對於簡短的手冊,一個組合索引就足夠了,但有時對於複雜的套件,最好使用多個索引。Texinfo 手冊包含有關準備良好索引條目的建議,請參閱 建立索引條目,在 GNU Texinfo 中;以及參閱 定義索引的條目,在 GNU Texinfo 中。
不要使用 Unix man pages 作為如何編寫 GNU 文件的模型;它們中的大多數都很簡潔、結構不良,並且對基本概念的解釋不足。(當然,也有一些例外。)此外,Unix man pages 使用一種特定的格式,這種格式與我們在 GNU 手冊中使用的格式不同。
請在手冊中包含一個電子郵件地址,用於報告手冊文字中的錯誤。
請不要使用 Unix 文件中使用的術語 “pathname”;請改用 “file name”(兩個單字)。我們僅將術語 “path” 用於搜尋路徑,即目錄名稱列表。
請不要使用術語 “illegal” 來指稱電腦程式的錯誤輸入。請使用 “invalid” 來指稱這個,並保留術語 “illegal” 用於法律禁止的活動。
請不要在函式名稱後寫 ‘()’,只是為了表明它是一個函式。foo ()
不是一個函式,它是一個沒有引數的函式呼叫。
在可能的情況下,請堅持使用主動語態,避免被動語態,並使用現在時,而不是未來時。例如,寫「函式 foo
傳回一個包含 a 和 b 的列表」,而不是「將傳回一個包含 a 和 b 的列表。」主動語態的一個優點是它要求你陳述句子的主語;使用被動語態,你可能會省略主語,這會導致模糊不清。
當文法要求時,使用未來時是適當的,例如,「如果你輸入 x,電腦將在 10 秒內自毀。」
一些程式設計系統,例如 Emacs,為每個函式、命令或變數提供一個文件字串。你可能會想透過編譯文件字串並編寫一些額外的文字來圍繞它們來編寫參考手冊——但你絕不能這樣做。這種方法是一個根本性的錯誤。寫得好的文件字串的文字對於手冊來說將是完全錯誤的。
文件字串需要獨立存在——當它出現在螢幕上時,不會有其他文字來介紹或解釋它。同時,它的風格可以相當隨意。
手冊中描述函式或變數的文字絕不能獨立存在;它出現在章節或小節的上下文中。章節開頭的其他文字應該解釋一些概念,並且通常應該提出一些適用於多個函式或變數的通用要點。章節中先前對函式和變數的描述也將提供有關該主題的資訊。為獨立存在而編寫的描述會重複一些資訊;這種冗餘看起來很糟糕。同時,在文件字串中可以接受的隨意性在手冊中是完全不能接受的。
在編寫好的手冊時使用文件字串的唯一好方法是將它們用作編寫好的文字的資訊來源。
手冊的標題頁應說明手冊中記錄的程式或套件的版本。手冊的頂層節點也應包含此資訊。如果手冊的變更頻率高於程式或與程式無關,則還應在這兩個位置說明手冊的版本號。
手冊中記錄的每個程式都應該有一個名為「program 呼叫」或「呼叫 program」的節點。此節點(連同其任何子節點)應描述程式的命令列引數以及如何執行它(人們會在 man page 中尋找的那種資訊)。從包含程式使用的所有選項和引數範本的「@example」開始。
或者,在某個選單中放置一個選單項目,其項目名稱符合上述模式之一。這會將該項目指向的節點識別為此目的的節點,無論該節點的實際名稱為何。
Info 閱讀器的「--usage」功能會尋找這樣的節點或選單項目,以便找到相關的文字,因此對於每個 Texinfo 檔案來說,擁有一個是至關重要的。
如果一本手冊描述了多個程式,則對於手冊中描述的每個程式都應該有這樣一個節點。
對於所有超過幾頁的 GNU 手冊,請使用 GNU 自由文件許可證。對於簡短文件集合也是如此——整個集合只需要一份 GNU FDL 副本即可。對於單個簡短文件,您可以使用非常寬鬆的非著作權許可證,以避免佔用空間來放置冗長的許可證。
請參閱 https://gnu.dev.org.tw/copyleft/fdl-howto.html 以獲得關於如何使用 GFDL 的更多說明。
請注意,在許可證既非 GPL 也非 LGPL 的手冊中,沒有義務包含 GNU GPL 或 GNU LGPL 的副本。在大型手冊中包含程式的許可證可能是個好主意;在簡短的手冊中,包含程式的許可證會大大增加其大小,可能最好不要包含它。
請在手冊的標題頁上,將手冊的主要人類作者署名為作者。如果公司贊助了這項工作,請在手冊中合適的位置感謝該公司,但不要將公司列為作者。
FSF 以印刷形式出版一些 GNU 手冊。為了鼓勵這些手冊的銷售,手冊的線上版本應在最開始提及印刷手冊是可用的,並應指向獲取它的資訊——例如,連結到 https://gnu.dev.org.tw/order/order.html 頁面。但是,這不應包含在印刷手冊中,因為它是多餘的。
在手冊的線上形式中,解釋使用者如何從原始碼列印出手冊也是有用的。
除了手冊之外,套件還應該有一個名為 NEWS 的檔案,其中包含值得提及的使用者可見變更的列表。在每個新版本中,將項目新增到檔案的前面,並標識它們所屬的版本。不要丟棄舊項目;將它們留在檔案中,放在較新的項目之後。這樣,從任何先前版本升級的使用者都可以看到新增內容。
如果 NEWS 檔案變得非常長,請將一些較舊的項目移動到名為 ONEWS 的檔案中,並在末尾放置一個註記,將使用者指向該檔案。
保留變更日誌以描述對程式原始碼檔案所做的所有變更。這樣做的目的是為了讓未來調查錯誤的人員了解可能引入錯誤的變更。通常,可以通過查看最近變更的內容來找到新的錯誤。更重要的是,變更日誌可以通過提供衝突概念的產生歷史、它們的來源以及進行衝突變更的原因,來幫助您消除程式不同部分之間的概念不一致性。
因此,變更日誌應該足夠詳細和準確,以提供此類軟體鑑識通常需要的資訊。具體來說,變更日誌應該使找到以下問題的答案變得容易
從歷史上看,變更日誌維護在特殊格式化的檔案上。如今,專案通常將其原始碼檔案保存在版本控制系統 (VCS) 下,例如 Git、Subversion 或 Mercurial。如果 VCS 儲存庫是公開可訪問的,並且變更分別提交到其中(每個邏輯變更集一個提交)並記錄每個變更的作者,那麼 VCS 記錄的資訊可以用於從 VCS 日誌生成變更日誌,並通過使用合適的 VCS 命令來回答上述問題。(但是,VCS 日誌訊息仍然需要提供一些支援資訊,如下所述。)維護此類 VCS 儲存庫的專案可以決定不維護單獨的變更日誌檔案,而是依賴 VCS 來保留變更日誌。
如果您決定不維護單獨的變更日誌檔案,您仍然應該考慮在發布 tarball 中提供它們,以便希望在不訪問專案的 VCS 儲存庫的情況下查看變更日誌的使用者受益。存在可以從 VCS 日誌生成 ChangeLog 檔案的腳本;例如,gitlog-to-changelog 腳本(它是 Gnulib 的一部分)可以為 Git 儲存庫執行此操作。在 Emacs 中,命令 C-x v a (vc-update-change-log
) 執行從 VCS 日誌增量更新 ChangeLog 檔案的工作。
如果確實維護了單獨的變更日誌檔案,它們通常被稱為 ChangeLog,並且每個此類檔案都涵蓋整個目錄。每個目錄都可以有自己的變更日誌檔案,或者目錄可以使用其父目錄的變更日誌——這取決於您。
• 變更日誌概念 | ||
• 變更日誌的風格 | ||
• 簡單變更 | ||
• 條件變更 | ||
• 指示變更的部分 |
您可以將變更日誌視為概念上的「還原列表」,其中說明了早期版本與目前版本的不同之處。人們可以看到目前版本;他們不需要變更日誌來告訴他們其中的內容。他們從變更日誌中想要的是對早期版本有何不同的明確解釋。變更日誌中的每個條目描述的是單個變更或屬於一起的最小批量變更,也稱為變更集。
最好以標題行開始變更日誌條目:單行,是一個完整的句子,總結了變更集。如果您將變更日誌保存在 VCS 中,這應該是一個要求,因為以縮寫形式顯示變更日誌的 VCS 命令(例如 git log --oneline)會特別處理標題行。(在 ChangeLog 檔案中,標題行跟在說明變更作者以及安裝時間的行之後。)
在變更日誌條目的標題行之後,是整體變更的描述。這應該盡可能長,以給出清晰的描述。特別注意變更集中不易從 diff 或修改檔案和函式名稱中收集到的方面:變更的總體思路及其必要性,以及對不同檔案/函式所做變更之間的關係(如果有的話)。如果變更或其原因在某些公共論壇(例如專案的問題追蹤器或郵件列表)上進行了討論,最好在變更的描述中總結該討論的要點,並包含指向該討論或問題 ID 的指標,供那些想要完整閱讀的人使用。
解釋新程式碼的各部分如何與其他程式碼協同工作的最佳位置是在程式碼中的註解中,而不是在變更日誌中。
如果您認為變更需要解釋為什麼需要進行變更——也就是說,舊程式碼存在什麼問題以至於需要進行此變更——您可能是對的。請將解釋放在程式碼中的註解中,人們在看到程式碼時就會看到它。此類解釋的一個範例是:「此函式以前是迭代的,但當 MUMBLE 是一棵樹時,它就失敗了。」(儘管如此簡單的原因不需要這種解釋。)
對於變更的其他種類的解釋,最佳位置是在變更日誌條目中。特別是,註解通常不會說明為什麼某些程式碼被刪除或移動到另一個位置——這屬於進行該操作的變更的描述。
在變更的自由文字描述之後,最好根據實體所在的檔案,以及每個檔案中變更的內容,給出您變更的實體或定義的名稱列表。請參閱 變更日誌的風格。如果專案使用現代 VCS 來保留變更日誌資訊,如 變更日誌 中所述,則明確列出已變更的檔案和函式不是嚴格必要的,在某些情況下(例如在許多位置進行相同的機械變更),甚至很繁瑣。您可以決定是否允許專案的開發人員從日誌條目中省略已變更的檔案和函式列表,以及是否允許在某些特定條件下進行此類省略。但是,在做出此決策時,請考慮為每個變更提供已變更實體列表的以下好處
由於這些原因,為每個變更提供修改後的檔案和函式列表使變更日誌更有用,因此我們建議在可能和實際的情況下都包含它們。
也可以通過執行腳本來生成命名修改後實體的列表。其中一個腳本是 mklog.py(用 Python 3 撰寫);它被 GCC
專案使用。Gnulib 提供了此類腳本的另一個變體,稱為 vcs-to-changelog.py,它是 vcs-to-changelog
模組的一部分。請注意,這些腳本目前支援的程式語言比 Emacs 提供的手動命令少(請參閱 變更日誌的風格)。因此,上述從 VCS 提交歷史記錄生成 ChangeLog
檔案的方法(例如通過 gitlog-to-changelog
腳本)通常會提供更好的結果——前提是貢獻者堅持提供良好的提交訊息。
以下是一些變更日誌條目的簡單範例,從標題行開始,說明誰進行了變更以及何時安裝,然後是特定變更的描述。(這些範例取自 Emacs。)請記住,僅在單獨的 ChangeLog 檔案中才需要顯示變更日期以及作者姓名和電子郵件地址的行,而不是在 VCS 中保存變更日誌時。
2019-08-29 Noam Postavsky <npostavs@gmail.com> Handle completely undecoded input in term (Bug#29918) * lisp/term.el (term-emulate-terminal): Avoid errors if the whole decoded string is eight-bit characters. Don't attempt to save the string for next iteration in that case. * test/lisp/term-tests.el (term-decode-partial) (term-undecodable-input): New tests. 2019-06-15 Paul Eggert <eggert@cs.ucla.edu> Port to platforms where tputs is in libtinfow * configure.ac (tputs_library): Also try tinfow, ncursesw (Bug#33977). 2019-02-08 Eli Zaretskii <eliz@gnu.org> Improve documentation of 'date-to-time' and 'parse-time-string' * doc/lispref/os.texi (Time Parsing): Document 'parse-time-string', and refer to it for the description of the argument of 'date-to-time'. * lisp/calendar/time-date.el (date-to-time): Refer in the doc string to 'parse-time-string' for more information about the format of the DATE argument. (Bug#34303)
如果您提及修改後的函式或變數的名稱,請務必完整命名它們。不要縮寫函式或變數名稱,也不要組合它們。後續維護人員通常會搜尋函式名稱以找到與其相關的所有變更日誌條目;如果您縮寫名稱,他們在搜尋時將找不到它。
例如,有些人傾向於通過寫「* register.el ({insert,jump-to}-register)」來縮寫函式名稱組;這不是一個好主意,因為搜尋 jump-to-register
或 insert-register
將找不到該條目。
用空行分隔不相關的變更日誌條目。不要在條目的各個變更之間放置空行。當連續的各個變更在同一個檔案中時,您可以省略檔案名稱和星號。
通過用「)」而不是「,」結束續行,並用「(」打開續行,來中斷函式名稱的長列表。這使 Emacs 中的突出顯示效果更好。這是一個範例
* src/keyboard.c (menu_bar_items, tool_bar_items) (Fexecute_extended_command): Deal with 'keymap' property.
向 ChangeLog 新增條目的最簡單方法是使用 Emacs 命令 M-x add-change-log-entry,或其變體 C-x 4 a (add-change-log-entry-other-window
)。這會自動收集變更檔案的名稱和變更的函式或變數,並根據上述約定格式化變更日誌條目,讓您描述您對該函式或變數所做的變更。
當您安裝別人的變更時,請將貢獻者的姓名放在變更日誌條目中,而不是放在條目的文字中。換句話說,寫成這樣
2002-07-14 John Doe <jdoe@gnu.org> * sewing.c: Make it sew.
而不是這樣
2002-07-14 Usual Maintainer <usual@gnu.org> * sewing.c: Make it sew. Patch by jdoe@gnu.org.
將別人的變更提交到 VCS 時,請使用 VCS 功能來指定作者。例如,使用 Git 時,請使用 git commit --author=author。
至於日期,那應該是您應用變更的日期。(對於 VCS,請使用適當的命令列開關,例如 git commit --date=date。)
現代 VCS 具有應用通過電子郵件發送的變更的命令(例如,Git 具有 git am);在這種情況下,變更集的作者及其創建日期將自動從電子郵件訊息中收集並記錄在儲存庫中。如果補丁是使用合適的 VCS 命令(例如 git format-patch)準備的,則電子郵件訊息正文也將具有變更集的原始作者,因此重新發送或轉發訊息不會干擾將變更歸於其作者。因此,我們建議您要求您的貢獻者使用諸如 git format-patch 之類的命令來準備補丁。
某些簡單類型的變更不需要在變更日誌中提供太多詳細資訊。
如果變更的描述足夠簡短,它可以充當自己的標題行
2019-08-29 Eli Zaretskii <eliz@gnu.org> * lisp/simple.el (kill-do-not-save-duplicates): Doc fix. (Bug#36827)
當您以簡單的方式變更函式的呼叫序列,並且您變更函式的所有呼叫者以使用新的呼叫序列時,無需為您變更的所有呼叫者創建單獨的條目。只需在被呼叫函式的條目中寫「所有呼叫者已變更」——像這樣
* keyboard.c (Fcommand_execute): New arg SPECIAL. All callers changed.
當您僅變更註解或文件字串時,為檔案編寫條目就足夠了,無需提及函式。僅「文件修復」對於變更日誌來說就足夠了。
當您在許多檔案中進行變更,這些變更機械地遵循一個底層變更時,描述底層變更就足夠了。以下是一個影響儲存庫中所有檔案的變更範例
2019-01-07 Paul Eggert <eggert@cs.ucla.edu> Update copyright year to 2019 Run 'TZ=UTC0 admin/update-copyright $(git ls-files)'.
測試套件檔案是軟體的一部分,因此我們建議將它們視為用於變更日誌目的的程式碼。
對於非軟體檔案(手冊、幫助檔案、媒體檔案等),沒有技術需要來編寫變更日誌條目。這是因為它們不易受到難以理解的錯誤的影響。要更正錯誤,您無需知道錯誤段落的歷史記錄;將檔案所說的內容與實際事實進行比較就足夠了。
但是,當專案從其貢獻者那裡獲得著作權轉讓時,您應該保留非軟體檔案的變更日誌,以便使作者記錄更加準確。因此,我們建議為您專案手冊的 Texinfo 原始碼保留變更日誌。
原始碼檔案通常可以包含對建置時或靜態條件有條件的程式碼。例如,C 程式可以包含編譯時 #if
條件;以直譯語言實作的程式可以包含模組匯入,其中函式定義僅針對特定版本的直譯器執行;而 Automake Makefile.am 檔案可以包含變數定義或目標宣告,這些定義或宣告僅在配置時 Automake 條件為真時才被考慮。
許多變更也是有條件的:有時您新增一個新的變數、函式,甚至是新的程式或程式庫,這完全取決於建置時條件。在變更日誌中指示變更適用的條件是有用的。
我們指示條件變更的約定是在條件名稱周圍使用方括號。
條件變更可能在多種場景和多種變體中發生,因此以下是一些範例以幫助澄清。第一個範例描述了 C、Perl 和 Python 檔案中的變更,這些變更是有條件的,但沒有關聯的函式或實體名稱
* xterm.c [SOLARIS2]: Include <string.h>. * FilePath.pm [$^O eq 'VMS']: Import the VMS::Feature module. * framework.py [sys.version_info < (2, 6)]: Make "with" statement available by importing it from __future__, to support also python 2.5.
為了簡潔起見,我們的其他範例將僅限於 C,因為將它們適應其他語言所需的小變更應該是不言自明的。
接下來,這是一個描述完全有條件的新定義的條目:C 巨集 FRAME_WINDOW_P
僅在定義了巨集 HAVE_X_WINDOWS
時才定義(和使用)
* frame.h [HAVE_X_WINDOWS] (FRAME_WINDOW_P): Macro defined.
接下來,是函式 init_display
中變更的條目,其定義整體上是無條件的,但變更本身包含在「#ifdef HAVE_LIBNCURSES」條件中
* dispnew.c (init_display) [HAVE_LIBNCURSES]: If X, call tgetent.
最後,這是一個僅在未定義某個巨集時才生效的變更的條目
* host.c (gethostname) [!HAVE_SOCKETS]: Replace with winsock version.
通過使用角括號括起對變更部分功能的指示,來指示函式中變更的部分。以下是函式 sh-while-getopts
中處理 sh
命令的部分的變更條目
* progmodes/sh-script.el (sh-while-getopts) <sh>: Handle case that user-specified option string is empty.
在 GNU 專案中,man page 是次要的。並非每個 GNU 程式都必須或期望擁有 man page,但其中一些程式確實有。是否在您的程式中包含 man page 是您的選擇。
當您做出此決定時,請考慮支援 man page 需要每次程式變更時都持續付出努力。您花在 man page 上的時間是從更有用的工作中奪走的時間。
對於變更很少的簡單程式,更新 man page 可能是一項小工作。那麼,如果您有一個 man page,就沒有太多理由不包含它。
對於變更很大的大型程式,更新 man page 可能是一個相當大的負擔。如果使用者主動捐贈 man page,您可能會發現接受這份禮物代價高昂。最好拒絕 man page,除非同一個人同意完全負責維護它——這樣您就可以完全置身事外。如果這位志願者後來停止執行這項工作,那麼不要覺得有義務自己接手;最好從發行版中撤回 man page,直到其他人同意更新它。
當程式僅變更一點點時,您可能會覺得差異很小,以至於 man page 在不更新的情況下仍然有用。如果是這樣,請在 man page 的開頭附近放置一個醒目的註記,說明您不維護它,並且 Texinfo 手冊更具權威性。該註記應說明如何訪問 Texinfo 文件。
請確保 man page 包含著作權聲明和自由許可證。簡單的全許可許可證適用於簡單的 man page(請參閱 其他檔案的許可證聲明,在 GNU 維護者資訊 中)。
對於較長的 man page,其中有足夠的解釋和文件,可以將其視為真正的手冊,請使用 GFDL(請參閱 手冊許可證)。
最後,GNU help2man 程式 (https://gnu.dev.org.tw/software/help2man/) 是一種自動生成 man page 的方法,在這種情況下是從 --help 輸出生成。這在許多情況下都足夠了。
可能存在描述您正在記錄的程式的非自由書籍或文件檔案。
使用這些文件作為參考是可以的,就像新代數教科書的作者可以閱讀其他關於代數的書籍一樣。任何非小說類書籍的很大一部分都由事實組成,在這種情況下,事實是關於特定程式如何運作的,並且對於每個撰寫該主題的人來說,這些事實必然是相同的。但請注意不要從現有的非自由文件中複製您的綱要結構、措辭、表格或範例。從自由文件複製可能是可以的;請就個案諮詢 FSF。
發布不僅僅是將您的原始碼檔案捆綁到 tar 檔案中並將其上傳到 FTP。您應該設定您的軟體,使其可以配置為在各種系統上執行。您的 Makefile 應符合以下描述的 GNU 標準,並且您的目錄佈局也應符合以下討論的標準。這樣做可以輕鬆地將您的套件包含到所有 GNU 軟體的更大框架中。
• 配置 | GNU 套件的配置應如何運作。 | |
• Makefile 約定 | Makefile 約定。 | |
• 發布 | 進行發布 |
下一步:Makefile 約定, 上層:管理發布 [目錄][索引]
每個 GNU 發行版都應該附帶一個名為 configure
的 shell 腳本。此腳本被賦予描述您要為其編譯程式的機器和系統類型的引數。configure
腳本必須記錄配置選項,以便它們影響編譯。
此處的描述是 GNU 套件中 configure
腳本介面的規範。許多套件使用 GNU Autoconf(請參閱 Autoconf 中的 簡介)和/或 GNU Automake(請參閱 Automake 中的 簡介)來實作它,但您不必使用這些工具。您可以以任何您喜歡的方式實作它;例如,通過使 configure
成為完全不同的配置系統的包裝器。
configure
腳本運作的另一種方式是從標準名稱(例如 config.h)連結到所選系統的正確配置檔案。如果您使用此技術,則發行版不應包含名為 config.h 的檔案。這是為了防止人們在未配置程式的情況下構建程式。
configure
可以做的另一件事是編輯 Makefile。如果您這樣做,則發行版不應包含名為 Makefile 的檔案。相反,它應該包含一個檔案 Makefile.in,其中包含用於編輯的輸入。再次強調,這是為了防止人們在未配置程式的情況下構建程式。
如果 configure
確實寫入 Makefile,則 Makefile 應該有一個名為 Makefile 的目標,該目標會導致 configure
重新執行,設定上次設定的相同配置。configure
讀取的檔案應列為 Makefile 的依賴項。
所有從 configure
腳本輸出的檔案都應在開頭帶有註解,說明它們是使用 configure
自動生成的。這樣做的目的是為了防止使用者想到嘗試手動編輯它們。
configure
腳本應寫入一個名為 config.status 的檔案,其中描述了上次配置程式時指定的配置選項。此檔案應為 shell 腳本,如果執行,將重新創建相同的配置。
configure
腳本應接受「--srcdir=dirname」形式的選項,以指定在何處找到原始碼的目錄(如果它不是當前目錄)。這使得可以在單獨的目錄中構建程式,以便不修改實際的原始碼目錄。
如果使用者未指定 ‘--srcdir’,則 configure
應檢查目前目錄 . 和上一層目錄 ..,以查看是否能找到原始碼。如果在這兩個位置之一找到原始碼,則應從該處使用它們。否則,應報告找不到原始碼,並以非零狀態碼退出。
通常支援 ‘--srcdir’ 的簡單方法是在 Makefile 中編輯 VPATH
的定義。有些規則可能需要明確地參考指定的原始碼目錄。為了實現這一點,configure
可以向 Makefile 添加一個名為 srcdir
的變數,其值正是指定的目錄。
此外,‘configure’ 腳本應接受與大多數標準目錄變數相對應的選項(請參閱目錄變數)。以下是列表:
--prefix --exec-prefix --bindir --sbindir --libexecdir --sysconfdir --sharedstatedir --localstatedir --runstatedir --libdir --includedir --oldincludedir --datarootdir --datadir --infodir --localedir --mandir --docdir --htmldir --dvidir --pdfdir --psdir
configure
腳本也應接受一個參數,用於指定要為其建置程式的系統類型。此參數應如下所示:
cpu-company-system
例如,基於 Athlon 的 GNU/Linux 系統可能是 ‘i686-pc-linux-gnu’。
configure
腳本需要能夠解碼所有用於描述機器的合理替代方案。因此,‘athlon-pc-gnu/linux’ 將會是一個有效的別名。有一個名為 config.sub 的 shell 腳本,您可以將其用作子程式來驗證系統類型並規範化別名。
configure
腳本也應接受選項 --build=buildtype,這應等同於純粹的 buildtype 參數。例如,‘configure --build=i686-pc-linux-gnu’ 等同於 ‘configure i686-pc-linux-gnu’。當未透過選項或參數指定建置類型時,configure
腳本通常應使用 shell 腳本 config.guess 來猜測它。
允許使用其他選項來更詳細地指定機器上存在的軟體或硬體,以包含或排除套件的可選部分,或調整某些工具的名稱或其參數。
配置套件以建置和安裝名為 feature 的可選使用者層級功能。這允許使用者選擇要包含哪些可選功能。如果預設建置 feature,則給定可選的 parameter ‘no’ 應省略 feature。
任何 ‘--enable’ 選項都絕不應導致一個功能取代另一個功能。任何 ‘--enable’ 選項都絕不應以另一種有用的行為來取代一種有用的行為。‘--enable’ 的唯一正確用法是關於是否要建置程式的一部分或將其排除。
package 套件將被安裝,因此配置此套件以與 package 協同運作。
package 的可能值包括 ‘gnu-as’ (或 ‘gas’)、‘gnu-ld’、‘gnu-libc’、‘gdb’、‘x’ 和 ‘x-toolkit’。
請勿使用 ‘--with’ 選項來指定用於尋找特定檔案的檔案名稱。這超出了 ‘--with’ 選項的適用範圍。
將變數 variable 的值設定為 value。這用於覆寫建置過程中命令或參數的預設值。例如,使用者可以發出 ‘configure CFLAGS=-g CXXFLAGS=-g’,以使用偵錯資訊且不使用預設最佳化進行建置。
像這樣將變數指定為 configure
的參數:
./configure CC=gcc
比在環境變數中設定它們更佳:
CC=gcc ./configure
因為這有助於稍後使用 config.status 重新建立相同的配置。但是,應同時支援這兩種方法。
所有 configure
腳本都應接受所有「詳細」選項和變數設定,無論它們是否對手邊的特定套件產生任何影響。特別是,它們應接受任何以 ‘--with-’ 或 ‘--enable-’ 開頭的選項。這是為了讓使用者能夠使用一組選項一次配置整個 GNU 原始碼樹。
您會注意到 ‘--with-’ 和 ‘--enable-’ 類別範圍狹窄:它們並未為您可能想到的任何類型的選項提供位置。這是故意的。我們希望限制 GNU 軟體中可能的配置選項。我們不希望 GNU 程式具有特有的配置選項。
執行編譯過程一部分的套件可能支援跨編譯。在這種情況下,程式的主機和目標機器可能不同。
configure
腳本通常應將指定的系統類型視為主機和目標,從而產生一個可在其運行的相同類型機器上運作的程式。
若要編譯一個在與建置類型不同的主機類型上運行的程式,請使用 configure 選項 --host=hosttype,其中 hosttype 使用與 buildtype 相同的語法。主機類型通常預設為建置類型。
若要配置跨編譯器、跨組譯器或任何類似工具,您應使用 configure 選項 ‘--target=targettype’ 指定與主機不同的目標。targettype 的語法與主機類型相同。因此,命令看起來會像這樣:
./configure --host=hosttype --target=targettype
目標類型通常預設為主機類型。跨操作沒有意義的程式不需要接受 ‘--target’ 選項,因為為跨操作配置整個作業系統並不是有意義的操作。
有些程式具有自動配置自身的方法。如果您的程式已設定為執行此操作,則您的 configure
腳本可以簡單地忽略其大多數參數。
這描述了撰寫 GNU 程式 Makefile 的慣例。使用 Automake 將有助於您撰寫遵循這些慣例的 Makefile。有關可移植 Makefile 的更多資訊,請參閱 POSIX 和 Autoconf 中的 可移植 Make 程式設計。
• Makefile 基礎知識 | Makefile 的一般慣例。 | |
• Makefile 中使用的工具程式 | Makefile 中要使用的工具程式。 | |
• 命令變數 | 用於指定命令的變數。 | |
• DESTDIR | 支援分階段安裝。 | |
• 目錄變數 | 安裝目錄的變數。 | |
• 標準目標 | 使用者的標準目標。 | |
• 安裝命令類別 | ‘install’ 規則中的三類命令:一般、安裝前和安裝後。 |
下一個:Makefile 中使用的工具程式,上一層:Makefile 慣例 [目錄][索引]
每個 Makefile 都應包含以下行:
SHELL = /bin/sh
以避免在系統上發生問題,其中 SHELL
變數可能從環境繼承。(這在使用 GNU make
時永遠不會有問題。)
不同的 make
程式具有不相容的後綴列表和隱含規則,這有時會造成混淆或錯誤行為。因此,最好明確設定後綴列表,僅使用特定 Makefile 中需要的後綴,如下所示:
.SUFFIXES: .SUFFIXES: .c .o
第一行清除後綴列表,第二行引入可能在此 Makefile 中受隱含規則約束的所有後綴。
不要假設 . 在命令執行的路徑中。當您需要在 make 期間運行屬於您套件的程式時,請確保如果程式是作為 make 的一部分建置的,則使用 ./,或者如果檔案是原始碼的不可變部分,則使用 $(srcdir)/。如果沒有這些前綴之一,則會使用目前的搜尋路徑。
./(建置目錄)和 $(srcdir)/(原始碼目錄)之間的區別很重要,因為使用者可以使用 ‘--srcdir’ 選項到 configure 在單獨的目錄中進行建置。表格的規則:
foo.1 : foo.man sedscript sed -f sedscript foo.man > foo.1
當建置目錄不是原始碼目錄時,將會失敗,因為 foo.man 和 sedscript 位於原始碼目錄中。
當使用 GNU make
時,依靠 ‘VPATH’ 尋找原始檔在只有單個依賴檔案的情況下會有效,因為 make
自動變數 ‘$<’ 將表示原始檔,無論它在哪裡。(許多版本的 make
僅在隱含規則中設定 ‘$<’。)像這樣的 Makefile 目標:
foo.o : bar.c $(CC) -I. -I$(srcdir) $(CFLAGS) -c bar.c -o foo.o
應改寫為:
foo.o : bar.c $(CC) -I. -I$(srcdir) $(CFLAGS) -c $< -o $@
以便 ‘VPATH’ 正常運作。當目標有多個依賴項時,使用明確的 ‘$(srcdir)’ 是使規則良好運作的最簡單方法。例如,上面針對 foo.1 的目標最好寫成:
foo.1 : foo.man sedscript sed -f $(srcdir)/sedscript $(srcdir)/foo.man > $@
GNU 發行版通常包含一些不是原始檔的檔案—例如,Info 檔案,以及來自 Autoconf、Automake、Bison 或 Flex 的輸出。由於這些檔案通常出現在原始碼目錄中,因此它們應始終出現在原始碼目錄中,而不是在建置目錄中。因此,更新它們的 Makefile 規則應將更新後的檔案放在原始碼目錄中。
但是,如果檔案未出現在發行版中,則 Makefile 不應將其放在原始碼目錄中,因為在正常情況下建置程式不應以任何方式修改原始碼目錄。
盡可能使建置和安裝目標(至少,以及它們的所有子目標)在平行 make
下正確運作。
下一個:命令變數,上一個:Makefile 基礎知識,上一層:Makefile 慣例 [目錄][索引]
撰寫 Makefile 命令(以及任何 shell 腳本,例如 configure
)以在 sh
(傳統 Bourne shell 和 POSIX shell)下運行,而不是 csh
。請勿使用 ksh
或 bash
的任何特殊功能,或傳統 Bourne sh
中未廣泛支援的 POSIX 功能。
用於建置和安裝的 configure
腳本和 Makefile 規則不應直接使用任何工具程式,除了以下這些:
awk cat cmp cp diff echo expr false grep install-info ln ls mkdir mv printf pwd rm rmdir sed sleep sort tar test touch tr true
壓縮程式(例如 gzip
)可用於 dist
規則中。
通常,請堅持這些程式的廣泛支援(通常為 POSIX 指定)的選項和功能。例如,不要使用 ‘mkdir -p’,儘管它可能很方便,因為少數系統根本不支援它,而在其他系統上,它對於平行執行並不安全。有關已知不相容性的列表,請參閱 Autoconf 中的 可移植 Shell 程式設計。
最好避免在 makefile 中建立符號連結,因為少數檔案系統不支援它們。
用於建置和安裝的 Makefile 規則也可以使用編譯器和相關程式,但應透過 make
變數來執行,以便使用者可以替換替代方案。以下是我們指的一些程式:
ar bison cc flex install ld ldconfig lex make makeinfo ranlib texi2dvi yacc
使用以下 make
變數來運行這些程式:
$(AR) $(BISON) $(CC) $(FLEX) $(INSTALL) $(LD) $(LDCONFIG) $(LEX) $(MAKE) $(MAKEINFO) $(RANLIB) $(TEXI2DVI) $(YACC)
當您使用 ranlib
或 ldconfig
時,您應確保如果系統沒有相關程式,則不會發生任何不良情況。安排忽略來自該命令的錯誤,並在命令之前列印訊息,以告知使用者此命令的失敗並不表示存在問題。(Autoconf ‘AC_PROG_RANLIB’ 巨集可以對此有所幫助。)
如果您使用符號連結,則應為不支援符號連結的系統實作後備方案。
可透過 Make 變數使用的其他工具程式包括:
chgrp chmod chown mknod
在僅適用於您知道這些工具程式存在的特定系統的 Makefile 部分(或腳本)中使用其他工具程式是可以接受的。
下一個:DESTDIR,上一個:Makefile 中使用的工具程式,上一層:Makefile 慣例 [目錄][索引]
Makefile 應提供變數來覆寫某些命令、選項等等。
特別是,您應透過變數來運行大多數工具程式。因此,如果您使用 Bison,請建立一個名為 BISON
的變數,其預設值設定為 ‘BISON = bison’,並在您需要使用 Bison 時使用 $(BISON)
引用它。
檔案管理工具程式(例如 ln
、rm
、mv
等等)不需要以這種方式透過變數引用,因為使用者不需要用其他程式替換它們。
每個程式名稱變數都應帶有一個選項變數,用於為程式提供選項。將 ‘FLAGS’ 附加到程式名稱變數名稱以取得選項變數名稱—例如,BISONFLAGS
。(C 編譯器的名稱 CFLAGS
、yacc 的 YFLAGS
和 lex 的 LFLAGS
是此規則的例外,但我們保留它們,因為它們是標準的。)在任何運行前處理器的編譯命令中使用 CPPFLAGS
,並在任何執行連結的編譯命令以及直接使用 ld
的情況下使用 LDFLAGS
。
如果有些 C 編譯器選項必須用於正確編譯某些檔案,請勿將它們包含在 CFLAGS
中。使用者希望能夠自行自由指定 CFLAGS
。相反,安排將必要的選項獨立於 CFLAGS
傳遞給 C 編譯器,方法是將它們明確地寫在編譯命令中,或定義隱含規則,如下所示:
CFLAGS = -g ALL_CFLAGS = -I. $(CFLAGS) .c.o: $(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $<
在 CFLAGS
中包含 ‘-g’ 選項,因為這不是正確編譯的必要條件。您可以將其視為僅建議的預設值。如果套件設定為預設使用 GCC 編譯,那麼您不妨也在 CFLAGS
的預設值中包含 ‘-O’。
將 CFLAGS
放在編譯命令的最後,在包含編譯器選項的其他變數之後,以便使用者可以使用 CFLAGS
來覆寫其他選項。
CFLAGS
應在每次調用 C 編譯器時使用,包括執行編譯和執行連結的調用。
每個 Makefile 都應定義變數 INSTALL
,這是將檔案安裝到系統中的基本命令。
每個 Makefile 也應定義變數 INSTALL_PROGRAM
和 INSTALL_DATA
。(INSTALL_PROGRAM
的預設值應為 $(INSTALL)
;INSTALL_DATA
的預設值應為 ${INSTALL} -m 644
。)然後,它應使用這些變數作為實際安裝的命令,分別用於可執行檔和非可執行檔。這些變數的最小使用方式如下:
$(INSTALL_PROGRAM) foo $(bindir)/foo $(INSTALL_DATA) libfoo.a $(libdir)/libfoo.a
但是,最好支援目標檔案上的 DESTDIR
前綴,如下一節所述。
在一個命令中安裝多個檔案是可以接受的,但不是必需的,最後一個參數是目錄,如:
$(INSTALL_PROGRAM) foo bar baz $(bindir)
下一個:目錄變數,上一個:命令變數,上一層:Makefile 慣例 [目錄][索引]
DESTDIR
:對階段式安裝的支援DESTDIR
是一個前置於每個已安裝目標檔案的變數,如下所示:
$(INSTALL_PROGRAM) foo $(DESTDIR)$(bindir)/foo $(INSTALL_DATA) libfoo.a $(DESTDIR)$(libdir)/libfoo.a
DESTDIR
變數由使用者在 make
命令列上指定為絕對檔案名稱。例如:
make DESTDIR=/tmp/stage install
DESTDIR
應僅在 install*
和 uninstall*
目標中支援,因為這些是唯一有用的目標。
如果您的安裝步驟通常會安裝 /usr/local/bin/foo 和 /usr/local/lib/libfoo.a,那麼如上述範例中調用的安裝將會改為安裝 /tmp/stage/usr/local/bin/foo 和 /tmp/stage/usr/local/lib/libfoo.a。
以此方式將變數 DESTDIR
前置於每個目標,可提供分階段安裝,其中已安裝的檔案不會直接放置在其預期位置,而是複製到臨時位置 (DESTDIR
)。但是,已安裝的檔案會維護其相對目錄結構,並且任何嵌入的檔案名稱都不會被修改。
您不應在 Makefile 中設定 DESTDIR
的值;然後檔案預設會安裝到其預期位置。此外,指定 DESTDIR
不應以任何方式更改軟體的運作方式,因此其值不應包含在任何檔案內容中。
DESTDIR
支援通常用於套件建立。它也有助於想要了解給定套件將安裝在哪裡的使用者,並允許通常沒有權限安裝到受保護區域的使用者在獲得這些權限之前進行建置和安裝。最後,它對於諸如 stow
之類的工具也很有用,在這些工具中,程式碼安裝在一個位置,但使用符號連結或特殊掛載操作使其看起來安裝在其他位置。因此,我們強烈建議 GNU 套件支援 DESTDIR
,儘管這不是絕對要求。
下一個:標準目標,上一個:DESTDIR,上一層:Makefile 慣例 [目錄][索引]
安裝目錄應始終以變數命名,以便易於安裝在非標準位置。以下描述了這些變數的標準名稱以及它們在 GNU 套件中應具有的值。它們基於標準檔案系統佈局;GNU/Linux 和其他現代作業系統中使用了它的變體。
安裝程式預期在調用 make
(例如,make prefix=/usr install) 或 configure
(例如,configure --prefix=/usr) 時覆寫這些值。GNU 套件不應嘗試猜測哪個值對於它們正在安裝的系統是合適的:使用此處指定的預設設定,以便所有 GNU 套件的行為都相同,允許安裝程式實現任何所需的佈局。
所有安裝目錄及其父目錄都應在安裝到其中之前建立(如果需要)。
前兩個變數設定安裝的根目錄。所有其他安裝目錄都應是這兩個目錄之一的子目錄,並且不應將任何內容直接安裝到這兩個目錄中。
prefix
用於建構以下列出變數的預設值的前綴。prefix
的預設值應為 /usr/local。在建置完整的 GNU 系統時,前綴將為空,並且 /usr 將是 / 的符號連結。(如果您正在使用 Autoconf,請將其寫為 ‘@prefix@’。)
使用與用於建置程式不同的 prefix
值運行 ‘make install’ 不應重新編譯程式。
exec_prefix
用於建構以下列出的一些變數的預設值的前綴。exec_prefix
的預設值應為 $(prefix)
。(如果您正在使用 Autoconf,請將其寫為 ‘@exec_prefix@’。)
通常,$(exec_prefix)
用於包含機器特定檔案(例如可執行檔和子例程庫)的目錄,而 $(prefix)
直接用於其他目錄。
使用與用於建置程式不同的 exec_prefix
值運行 ‘make install’ 不應重新編譯程式。
可執行程式安裝在以下目錄之一中。
bindir
用於安裝使用者可以運行的可執行程式的目錄。這通常應為 /usr/local/bin,但請將其寫為 $(exec_prefix)/bin。(如果您正在使用 Autoconf,請將其寫為 ‘@bindir@’。)
sbindir
用於安裝可以從 shell 運行的可執行程式的目錄,但通常僅對系統管理員有用。這通常應為 /usr/local/sbin,但請將其寫為 $(exec_prefix)/sbin。(如果您正在使用 Autoconf,請將其寫為 ‘@sbindir@’。)
libexecdir
用於安裝要由其他程式而不是使用者運行的可執行程式的目錄。此目錄通常應為 /usr/local/libexec,但請將其寫為 $(exec_prefix)/libexec。(如果您正在使用 Autoconf,請將其寫為 ‘@libexecdir@’。)
‘libexecdir’ 的定義對於所有套件都相同,因此您應將資料安裝在其子目錄中。大多數套件將其資料安裝在 $(libexecdir)/package-name/ 下,可能在其中的其他子目錄中,例如 $(libexecdir)/package-name/machine/version。
程式在其執行期間使用的資料檔案分為兩類。
這產生了六種不同的可能性。但是,我們不鼓勵使用與架構相關的檔案,除了目標檔案和庫之外。使其他資料檔案與架構無關要乾淨得多,而且通常並不困難。
以下是 Makefile 應使用的變數,用於指定放置這些各種檔案的目錄:
唯讀、與架構無關的資料檔案的目錄樹根目錄。這通常應為 /usr/local/share,但請將其寫為 $(prefix)/share。(如果您正在使用 Autoconf,請將其寫為 ‘@datarootdir@’。)‘datadir’ 的預設值基於此變數;‘infodir’、‘mandir’ 和其他也是如此。
用於安裝此程式的特有唯讀、與架構無關的資料檔案的目錄。這通常與 ‘datarootdir’ 相同,但我們使用兩個單獨的變數,以便您可以移動這些程式特定的檔案,而不會更改 Info 檔案、man 頁面等的位置。
這通常應為 /usr/local/share,但請將其寫為 $(datarootdir)。(如果您正在使用 Autoconf,請將其寫為 ‘@datadir@’。)
‘datadir’ 的定義對於所有套件都相同,因此您應將資料安裝在其子目錄中。大多數套件將其資料安裝在 $(datadir)/package-name/ 下。
用於安裝與單一機器相關的唯讀資料檔案的目錄—也就是說,用於配置主機的檔案。郵件程式和網路配置檔案、/etc/passwd 等都屬於這裡。此目錄中的所有檔案都應為普通 ASCII 文字檔案。此目錄通常應為 /usr/local/etc,但請將其寫為 $(prefix)/etc。(如果您正在使用 Autoconf,請將其寫為 ‘@sysconfdir@’。)
此目錄不是安裝透過運行 ‘make’ 建置的可執行檔的正確位置—它們可能屬於 $(libexecdir) 或 $(sbindir)。也不要在此處安裝在正常使用過程中會被修改的檔案(程式的目的是排除更改系統配置)。這些檔案可能屬於 $(localstatedir)。
用於安裝程式在運行時修改的與架構無關的資料檔案的目錄。這通常應為 /usr/local/com,但請將其寫為 $(prefix)/com。(如果您正在使用 Autoconf,請將其寫為 ‘@sharedstatedir@’。)
用於安裝程式在運行時修改的資料檔案的目錄,這些檔案與一台特定機器相關。使用者永遠不需要修改此目錄中的檔案來配置套件的運作;將此類配置資訊放在單獨的檔案中,這些檔案放在 $(datadir) 或 $(sysconfdir) 中。$(localstatedir) 通常應為 /usr/local/var,但請將其寫為 $(prefix)/var。(如果您正在使用 Autoconf,請將其寫為 ‘@localstatedir@’。)
用於安裝程式在運行時修改的資料檔案的目錄,這些檔案與一台特定機器相關,並且不需要在程式執行期間(通常是長時間運行,例如,直到下次重新啟動)之後持續存在。系統守護程式的 PID 檔案是典型的用途。此外,除了可能在重新啟動時之外,不應清理此目錄,而一般的 /tmp (TMPDIR
) 可能會被任意清理。這通常應為 /var/run,但請將其寫為 $(localstatedir)/run。將其作為單獨的變數允許在需要時使用 /run,例如。(如果您正在使用 Autoconf 2.70 或更高版本,請將其寫為 ‘@runstatedir@’。)
這些變數指定了用於安裝某些特定類型檔案的目錄,如果您的程式有這些檔案。每個 GNU 套件都應具有 Info 檔案,因此每個程式都需要 ‘infodir’,但並非所有程式都需要 ‘libdir’ 或 ‘lispdir’。
用於安裝標頭檔的目錄,這些標頭檔將由使用者程式透過 C ‘#include’ 前處理器指示詞包含。這通常應為 /usr/local/include,但請將其寫為 $(prefix)/include。(如果您正在使用 Autoconf,請將其寫為 ‘@includedir@’。)
除了 GCC 之外,大多數編譯器都不會在 /usr/local/include 目錄中尋找標頭檔。因此,以這種方式安裝標頭檔僅對 GCC 有用。有時這不是問題,因為某些程式庫實際上僅旨在與 GCC 協同運作。但是,有些程式庫旨在與其他編譯器協同運作。它們應將其標頭檔安裝在兩個位置,一個由 includedir
指定,另一個由 oldincludedir
指定。
用於安裝 ‘#include’ 標頭檔以與 GCC 以外的編譯器一起使用的目錄。這通常應為 /usr/include。(如果您正在使用 Autoconf,您可以將其寫為 ‘@oldincludedir@’。)
Makefile 命令應檢查 oldincludedir
的值是否為空。如果為空,則它們不應嘗試使用它;它們應取消標頭檔的第二次安裝。
套件不應替換此目錄中現有的標頭,除非該標頭來自同一個套件。因此,如果您的 Foo 套件提供標頭檔 foo.h,則如果 (1) foo.h 不存在於 oldincludedir
目錄中,或者 (2) 存在的 foo.h 來自 Foo 套件,則它應將標頭檔安裝在 oldincludedir
目錄中。
若要判斷 foo.h 是否來自 Foo 套件,請在檔案中放置一個魔術字串—註解的一部分—並 grep
該字串。
用於安裝此套件的文件檔案(Info 檔案除外)的目錄。預設情況下,它應為 /usr/local/share/doc/yourpkg,但應將其寫為 $(datarootdir)/doc/yourpkg。(如果您正在使用 Autoconf,請將其寫為 ‘@docdir@’。)yourpkg 子目錄可能包含版本號,以防止具有通用名稱(例如 README)的檔案之間發生衝突。
用於安裝此套件的 Info 檔案的目錄。預設情況下,它應為 /usr/local/share/info,但應將其寫為 $(datarootdir)/info。(如果您正在使用 Autoconf,請將其寫為 ‘@infodir@’。)infodir
與 docdir
分開是為了與現有慣例相容。
用於安裝特定格式的文件檔案的目錄。它們都應預設設定為 $(docdir)
。(如果您正在使用 Autoconf,請將它們寫為 ‘@htmldir@’、‘@dvidir@’ 等。)提供多種文件翻譯的套件應將它們安裝在 ‘$(htmldir)/’ll、‘$(pdfdir)/’ll 等中,其中 ll 是語言環境縮寫,例如 ‘en’ 或 ‘pt_BR’。
用於目標檔案和目標程式碼庫的目錄。不要在此處安裝可執行檔,它們可能應該放在 $(libexecdir) 中。libdir
的值通常應為 /usr/local/lib,但請將其寫為 $(exec_prefix)/lib。(如果您正在使用 Autoconf,請將其寫為 ‘@libdir@’。)
用於安裝此套件中任何 Emacs Lisp 檔案的目錄。預設情況下,它應為 /usr/local/share/emacs/site-lisp,但應將其寫為 $(datarootdir)/emacs/site-lisp。
如果您正在使用 Autoconf,請將預設值寫為 ‘@lispdir@’。為了使 ‘@lispdir@’ 正常運作,您需要在您的 configure.ac 檔案中加入以下幾行:
lispdir='${datarootdir}/emacs/site-lisp' AC_SUBST(lispdir)
用於安裝此套件的特定於語言環境的訊息目錄的目錄。預設情況下,它應為 /usr/local/share/locale,但應將其寫為 $(datarootdir)/locale。(如果您正在使用 Autoconf,請將其寫為 ‘@localedir@’。)此目錄通常每個語言環境都有一個子目錄。
Unix 風格的 man 頁面安裝在以下其中之一:
用於安裝此套件的 man 頁面(如果有)的頂層目錄。它通常應為 /usr/local/share/man,但您應將其寫為 $(datarootdir)/man。(如果您正在使用 Autoconf,請將其寫為 ‘@mandir@’。)
用於安裝第 1 節 man 頁面的目錄。將其寫為 $(mandir)/man1。
用於安裝第 2 節 man 頁面的目錄。將其寫為 $(mandir)/man2
不要將任何 GNU 軟體的主要文件設定為 man 頁面。請改為撰寫 Texinfo 手冊。Man 頁面僅適用於在 Unix 上運行 GNU 軟體的人員,這僅是次要應用。
已安裝 man 頁面的檔案名稱副檔名。這應包含一個句點,後跟適當的數字;它通常應為 ‘.1’。
已安裝第 1 節 man 頁面的檔案名稱副檔名。
已安裝第 2 節 man 頁面的檔案名稱副檔名。
如果套件需要在手冊的多個章節中安裝 man 頁面,請使用這些名稱而不是 ‘manext’。
最後,您應設定以下變數:
要編譯的原始碼目錄。此變數的值通常由 configure
shell 腳本插入。(如果您正在使用 Autoconf,請使用 ‘srcdir = @srcdir@’。)
例如:
# Common prefix for installation directories. # NOTE: This directory must exist when you start the install. prefix = /usr/local datarootdir = $(prefix)/share datadir = $(datarootdir) exec_prefix = $(prefix) # Where to put the executable for the command 'gcc'. bindir = $(exec_prefix)/bin # Where to put the directories used by the compiler. libexecdir = $(exec_prefix)/libexec # Where to put the Info files. infodir = $(datarootdir)/info
如果您的程式將大量檔案安裝到標準使用者指定目錄之一,則將它們分組到特定於該程式的子目錄中可能很有用。如果您這樣做,則應撰寫 install
規則以建立這些子目錄。
請勿期望使用者在上述任何變數的值中包含子目錄名稱。為安裝目錄設置統一變數名稱的目的,是為了讓使用者能為數個不同的 GNU 套件指定完全相同的值。為了使其發揮作用,所有套件的設計都必須合理地運作,當使用者這樣做時。
有時,並非所有這些變數都可能在目前發行的 Autoconf 和/或 Automake 中實作;但截至 Autoconf 2.60,我們相信它們都已實作。當有任何變數遺失時,此處的描述可作為 Autoconf 將實作的規範。作為程式設計師,您可以選擇使用 Autoconf 的開發版本,或避免使用這些變數,直到發布支援它們的穩定版本為止。
下一步: 安裝命令類別, 上一步: 目錄變數, 上層: Makefile 慣例 [目錄][索引]
所有 GNU 程式的 Makefile 中都應包含以下目標
編譯整個程式。這應為預設目標。此目標不必重建任何文件檔案;Info 檔案通常應包含在發行版中,而 DVI(和其他文件格式)檔案應僅在明確要求時才製作。
預設情況下,Make 規則應使用 ‘-g’ 進行編譯和連結,以便可執行程式具有除錯符號。否則,當發生崩潰時,您基本上束手無策,而且通常很難用全新的建置重現。
編譯程式,並將可執行檔、程式庫等複製到它們實際使用時應存放的檔案名稱位置。如果有一個簡單的測試可以驗證程式是否已正確安裝,則此目標應執行該測試。
安裝時請勿去除可執行檔的符號。這有助於日後可能需要的除錯,而且現在磁碟空間便宜,動態載入器通常確保在正常執行期間不會載入除錯區段。需要去除符號二進位檔案的使用者可以調用 install-strip
目標來執行此操作。
如果可能,請編寫 install
目標規則,使其不會修改程式建置所在目錄中的任何內容,前提是 ‘make all’ 剛剛執行完畢。這對於以一個使用者名稱建置程式,然後以另一個使用者名稱安裝程式非常方便。
命令應建立所有要安裝檔案的目錄,如果它們尚不存在。這包括指定為變數 prefix
和 exec_prefix
值的目錄,以及所有需要的子目錄。一種方法是使用如下所述的 installdirs
目標。
在任何用於安裝 man page 的命令之前使用 ‘-’,以便 make
忽略任何錯誤。這是為了以防某些系統未安裝 Unix man page 文件系統。
安裝 Info 檔案的方法是使用 $(INSTALL_DATA)
(請參閱 命令變數) 將它們複製到 $(infodir) 中,然後在 install-info
程式存在時執行它。install-info
是一個程式,用於編輯 Info dir 檔案,以新增或更新給定 Info 檔案的選單項目;它是 Texinfo 套件的一部分。
以下是一個範例規則,用於安裝 Info 檔案,同時也嘗試處理一些額外情況,例如 install-info
不存在。
do-install-info: foo.info installdirs $(NORMAL_INSTALL) # Prefer an info file in . to one in srcdir. if test -f foo.info; then d=.; \ else d="$(srcdir)"; fi; \ $(INSTALL_DATA) $$d/foo.info \ "$(DESTDIR)$(infodir)/foo.info" # Run install-info only if it exists. # Use 'if' instead of just prepending '-' to the # line so we notice real errors from install-info. # Use '$(SHELL) -c' because some shells do not # fail gracefully when there is an unknown command. $(POST_INSTALL) if $(SHELL) -c 'install-info --version' \ >/dev/null 2>&1; then \ install-info --dir-file="$(DESTDIR)$(infodir)/dir" \ "$(DESTDIR)$(infodir)/foo.info"; \ else true; fi
在編寫 install
目標時,您必須將所有命令分為三類:一般命令、安裝前命令和安裝後命令。請參閱 安裝命令類別。
這些目標安裝 Info 以外格式的文件;它們旨在由安裝套件的人員明確調用,如果需要該格式。GNU 偏好 Info 檔案,因此這些檔案必須由 install
目標安裝。
當您有許多文件檔案要安裝時,我們建議您透過安排這些目標安裝到適當安裝目錄的子目錄中 (例如 htmldir
),來避免衝突和混亂。舉例來說,如果您的套件有多個手冊,並且您希望安裝具有許多檔案的 HTML 文件(例如 makeinfo --html
的「分割」模式輸出),您肯定會想要使用子目錄,否則不同手冊中具有相同名稱的兩個節點將會互相覆寫。
請使這些 install-format
目標調用 format 目標的命令,例如,透過使 format 成為相依性。
刪除所有已安裝的檔案— ‘install’ 和 ‘install-*’ 目標建立的副本。
此規則不應修改執行編譯的目錄,僅修改安裝檔案的目錄。
解除安裝命令分為三類,就像安裝命令一樣。請參閱 安裝命令類別。
與 install
類似,但在安裝可執行檔時去除其符號。在簡單的情況下,此目標可以以簡單的方式使用 install
目標
install-strip: $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' \ install
但是,如果套件同時安裝腳本和真正的可執行檔,則 install-strip
目標不能僅參考 install
目標;它必須去除可執行檔的符號,但不能去除腳本的符號。
install-strip
不應去除建置目錄中要複製以進行安裝的可執行檔的符號。它應該只去除已安裝的副本的符號。
通常,除非您確定程式沒有錯誤,否則我們不建議去除可執行檔的符號。但是,安裝已去除符號的可執行檔以供實際執行,同時將未去除符號的可執行檔保存在其他地方以防萬一出現錯誤,這是合理的。
刪除目前目錄中通常由建置程式建立的所有檔案。如果其他目錄中的檔案由此 makefile 建立,也請刪除這些檔案。但是,不要刪除記錄組態的檔案。同時保留可能由建置建立,但通常不會建立的檔案,因為發行版已包含它們。無需刪除使用 ‘mkdir -p’ 建立的父目錄,因為它們可能本來就存在。
如果 .dvi 檔案不是發行版的一部分,請在此處刪除它們。
刪除目前目錄中(或由此 makefile 建立的)由組態或建置程式建立的所有檔案。如果您已解壓縮原始碼並在未建立任何其他檔案的情況下建置程式,則 ‘make distclean’ 應僅留下發行版中的檔案。但是,無需刪除使用 ‘mkdir -p’ 建立的父目錄,因為它們可能本來就存在。
與 ‘clean’ 類似,但可能會避免刪除一些人們通常不想重新編譯的檔案。例如,GCC 的 ‘mostlyclean’ 目標不會刪除 libgcc.a,因為重新編譯它很少需要並且需要很長時間。
刪除幾乎所有可以使用此 Makefile 重建的東西。這通常包括 distclean
刪除的所有內容,以及更多內容:Bison 產生的 C 原始碼檔案、標籤表、Info 檔案等等。
我們之所以說「幾乎所有東西」,是因為即使可以使用 Makefile 中的規則重新製作 configure,執行命令 ‘make maintainer-clean’ 也不應刪除 configure。更廣泛地說,‘make maintainer-clean’ 不應刪除執行 configure 然後開始建置程式所需的任何內容。此外,無需刪除使用 ‘mkdir -p’ 建立的父目錄,因為它們可能本來就存在。這些是唯一的例外;maintainer-clean
應刪除所有其他可以重建的東西。
‘maintainer-clean’ 目標旨在供套件的維護者使用,而不是普通使用者使用。您可能需要特殊工具來重建 ‘make maintainer-clean’ 刪除的某些檔案。由於這些檔案通常包含在發行版中,因此我們不費心讓它們易於重建。如果您發現需要再次解壓縮完整發行版,請不要責怪我們。
為了幫助使用者意識到這一點,特殊 maintainer-clean
目標的命令應以以下兩項開頭
@echo 'This command is intended for maintainers to use; it' @echo 'deletes files that may need special tools to rebuild.'
更新此程式的標籤表。
產生任何需要的 Info 檔案。編寫規則的最佳方式如下
info: foo.info foo.info: foo.texi chap1.texi chap2.texi $(MAKEINFO) $(srcdir)/foo.texi
您必須在 Makefile 中定義變數 MAKEINFO
。它應該執行 makeinfo
程式,該程式是 Texinfo 發行版的一部分。
通常,GNU 發行版會附帶 Info 檔案,這表示 Info 檔案存在於原始碼目錄中。因此,info 檔案的 Make 規則應在原始碼目錄中更新它。當使用者建置套件時,通常 Make 不會更新 Info 檔案,因為它們已經是最新的。
以給定的格式產生文件檔案。這些目標應始終存在,但如果無法產生給定的輸出格式,則任何或所有目標都可以是空操作。這些目標不應是 all
目標的相依性;使用者必須手動調用它們。
以下是從 Texinfo 產生 DVI 檔案的範例規則
dvi: foo.dvi foo.dvi: foo.texi chap1.texi chap2.texi $(TEXI2DVI) $(srcdir)/foo.texi
您必須在 Makefile 中定義變數 TEXI2DVI
。它應該執行程式 texi2dvi
,該程式是 Texinfo 發行版的一部分。(texi2dvi
使用 TeX 來完成格式化的實際工作。TeX 未與 Texinfo 一起發行。) 或者,僅編寫相依性,並允許 GNU make
提供命令。
以下是另一個範例,這個範例是從 Texinfo 產生 HTML
html: foo.html foo.html: foo.texi chap1.texi chap2.texi $(TEXI2HTML) $(srcdir)/foo.texi
同樣,您需要在 Makefile 中定義變數 TEXI2HTML
;例如,它可能會執行 makeinfo --no-split --html
(makeinfo
是 Texinfo 發行版的一部分)。
為此程式建立發行版 tar 檔案。應設定 tar 檔案,使其中的檔案名稱以子目錄名稱開頭,該子目錄名稱是其發行版的套件名稱。此名稱可以包含版本號。
例如,GCC 1.40 版的發行版 tar 檔案會解壓縮到名為 gcc-1.40 的子目錄中。
最簡單的方法是建立一個適當命名的子目錄,使用 ln
或 cp
將正確的檔案安裝到其中,然後 tar
該子目錄。
使用 gzip
壓縮 tar 檔案。例如,GCC 1.40 版的實際發行檔案名為 gcc-1.40.tar.gz。也可以支援其他免費壓縮格式。
dist
目標應明確相依於發行版中的所有非原始碼檔案,以確保它們在發行版中是最新的。請參閱 製作發行版。
執行自我測試(如果有的話)。使用者必須先建置程式才能執行測試,但不必安裝程式;您應該編寫自我測試,使其在程式已建置但未安裝時也能運作。
以下目標建議作為慣例名稱,用於它們有用的程式中。
installcheck
執行安裝測試(如果有的話)。使用者必須先建置並安裝程式才能執行測試。您不應假設 $(bindir) 在搜尋路徑中。
installdirs
新增一個名為 ‘installdirs’ 的目標來建立檔案安裝目錄及其父目錄非常有用。有一個名為 mkinstalldirs 的腳本對此很方便;您可以在 Gnulib 套件中找到它。您可以使用類似以下的規則
# Make sure all installation directories (e.g. $(bindir)) # actually exist by making them if necessary. installdirs: mkinstalldirs $(srcdir)/mkinstalldirs $(bindir) $(datadir) \ $(libdir) $(infodir) \ $(mandir)
或者,如果您希望支援 DESTDIR
(強烈建議),
# Make sure all installation directories (e.g. $(bindir)) # actually exist by making them if necessary. installdirs: mkinstalldirs $(srcdir)/mkinstalldirs \ $(DESTDIR)$(bindir) $(DESTDIR)$(datadir) \ $(DESTDIR)$(libdir) $(DESTDIR)$(infodir) \ $(DESTDIR)$(mandir)
此規則不應修改執行編譯的目錄。它應該只執行建立安裝目錄的操作。
上一步: 標準目標, 上層: Makefile 慣例 [目錄][索引]
在編寫 install
目標時,您必須將所有命令分為三類:一般命令、安裝前命令和安裝後命令。
一般命令將檔案移動到其正確位置,並設定其模式。它們不得變更任何檔案,除非是完全來自它們所屬套件的檔案。
安裝前和安裝後命令可能會變更其他檔案;特別是,它們可以編輯全域組態檔案或資料庫。
安裝前命令通常在一般命令之前執行,而安裝後命令通常在一般命令之後執行。
安裝後命令最常見的用途是執行 install-info
。這不能使用一般命令來完成,因為它會變更一個檔案 (Info 目錄),該檔案並非完全且僅來自正在安裝的套件。它是一個安裝後命令,因為它需要在安裝套件 Info 檔案的一般命令之後執行。
大多數程式都不需要任何安裝前命令,但我們提供了此功能,以防萬一需要。
若要將 install
規則中的命令分為這三類,請在它們之間插入類別行。類別行指定後續命令的類別。
類別行由一個 Tab 字元和對特殊 Make 變數的參考組成,外加結尾的可選註解。您可以使用三個變數,每個類別一個;變數名稱指定類別。類別行在一般執行中是空操作,因為這三個 Make 變數通常未定義 (而且您不應在 makefile 中定義它們)。
以下是三個可能的類別行,每個類別行都帶有說明其含義的註解
$(PRE_INSTALL) # Pre-install commands follow. $(POST_INSTALL) # Post-install commands follow. $(NORMAL_INSTALL) # Normal commands follow.
如果您在 install
規則的開頭未使用類別行,則所有命令都會被分類為一般命令,直到第一個類別行。如果您不使用任何類別行,則所有命令都會被分類為一般命令。
以下是 uninstall
的類別行
$(PRE_UNINSTALL) # Pre-uninstall commands follow. $(POST_UNINSTALL) # Post-uninstall commands follow. $(NORMAL_UNINSTALL) # Normal commands follow.
通常,解除安裝前命令將用於從 Info 目錄中刪除條目。
如果 install
或 uninstall
目標有任何充當安裝子例程的相依性,那麼您應該以類別行開始每個相依性的命令,並且也以類別行開始主要目標的命令。這樣,您可以確保每個命令都放置在正確的類別中,無論實際執行哪個相依性。
安裝前和安裝後命令不應執行任何程式,除非是以下程式
[ basename bash cat chgrp chmod chown cmp cp dd diff echo expand expr false find getopt grep gunzip gzip hostname install install-info kill ldconfig ln ls md5sum mkdir mkfifo mknod mv printenv pwd rm rmdir sed sort tee test touch true uname xargs yes
以這種方式區分命令的原因是為了製作二進位套件。通常,二進位套件包含所有需要安裝的可執行檔和其他檔案,並且有其自己的安裝方法—因此它不需要執行一般安裝命令。但是,安裝二進位套件確實需要執行安裝前和安裝後命令。
用於建置二進位套件的程式透過提取安裝前和安裝後命令來運作。以下是提取安裝前命令的一種方法(需要 make
的 -s 選項來靜音有關進入子目錄的訊息)
make -s -n install -o all \ PRE_INSTALL=pre-install \ POST_INSTALL=post-install \ NORMAL_INSTALL=normal-install \ | gawk -f pre-install.awk
其中檔案 pre-install.awk 可能包含以下內容
$0 ~ /^(normal-install|post-install)[ \t]*$/ {on = 0} on {print $0} $0 ~ /^pre-install[ \t]*$/ {on = 1}
上一步: Makefile 慣例, 上層: 管理發行版 [目錄][索引]
您應該使用一對版本號(主要版本和次要版本)來識別每個發行版。我們不反對使用兩個以上的數字,但您不太可能真正需要它們。
將 Foo 69.96 版
的發行版打包到名為 foo-69.96.tar.gz 的 gzipped tar 檔案中。它應該解壓縮到名為 foo-69.96 的子目錄中。
建置和安裝程式絕不應修改發行版中包含的任何檔案。這表示以任何方式構成程式一部分的所有檔案都必須分為原始碼檔案和非原始碼檔案。原始碼檔案由人類編寫,永遠不會自動變更;非原始碼檔案由程式根據 Makefile 的控制從原始碼檔案產生。
發行版應包含名為 README 的檔案,其中包含套件的總體概述
當然,所有原始碼檔案都必須在發行版中。可以將非原始碼檔案與它們產生的原始碼檔案一起包含在發行版中,前提是它們與它們產生的原始碼保持最新,並且與機器無關,以便發行版的正常建置永遠不會修改它們。我們通常包含由 Autoconf、Automake、Bison、flex
、TeX 和 makeinfo
產生的非原始碼檔案;這有助於避免我們的發行版之間不必要的相依性,以便使用者可以安裝他們喜歡的任何版本的任何套件。不要輕易引入對其他軟體的新相依性。
在建置和安裝程式時可能會實際修改的非原始碼檔案絕不應包含在發行版中。因此,如果您確實發行了非原始碼檔案,請務必確保它們在您製作新發行版時是最新的。
確保發行版中的所有檔案都是世界可讀的,並且目錄是世界可讀和世界可搜尋的(八進制模式 755)。我們過去建議發行版中的所有目錄也應是世界可寫的(八進制模式 777),因為舊版本的 tar
在以非特權使用者身份解壓縮封存檔時無法應付。但是,在建立封存檔時,這很容易導致安全問題,因此我們現在建議不要這樣做。
不要在發行版本身中包含任何符號連結。如果 tar 檔案包含符號連結,那麼人們甚至無法在不支援符號連結的系統上解壓縮它。此外,不要在不同目錄中使用一個檔案的多個名稱,因為某些檔案系統無法處理此問題,這會阻止解壓縮發行版。
嘗試確保所有檔案名稱在 MS-DOS 上都是唯一的。MS-DOS 上的名稱最多由 8 個字元組成,可選擇後跟一個句點和最多三個字元。MS-DOS 將截斷句點之前和之後的額外字元。因此,foobarhacker.c 和 foobarhacker.o 並不含糊;它們被截斷為 foobarha.c 和 foobarha.o,它們是不同的。
在您的發行版中包含您用於測試列印任何 *.texinfo 或 *.texi 檔案的 texinfo.tex 副本。
同樣地,如果您的程式使用小型 GNU 軟體套件,例如 regex、getopt、obstack 或 termcap,請將它們包含在發行檔案中。將它們排除在外會使發行檔案小一點,但代價是可能會給不知道要取得哪些其他檔案的使用者帶來不便。
下一步: GNU 自由文件授權條款, 上一步: 管理發行版, 上層: 頂層 [目錄][索引]
GNU 程式不應推薦、宣傳或授予任何非自由程式使用的合法性。專有軟體是一個社會和道德問題,我們的目標是結束這個問題。我們無法阻止某些人編寫專有程式,也無法阻止其他人使用它們,但我們可以並且應該拒絕向新的潛在客戶宣傳它們,或讓公眾產生它們的存在是合法的印象。
GNU 對自由軟體的定義可在 GNU 網站 https://gnu.dev.org.tw/philosophy/free-sw.html 上找到,自由文件的定義可在 https://gnu.dev.org.tw/philosophy/free-doc.html 上找到。本文檔中使用的術語「自由」和「非自由」指的是這些定義。
重要授權條款及其是否符合自由授權的清單位於 https://gnu.dev.org.tw/licenses/license-list.html。如果不清楚某個授權條款是否符合自由授權,請寫信至 licensing@gnu.org 詢問 GNU 專案。我們會回覆,如果該授權條款很重要,我們會將其新增到清單中。
當一個非自由程式或系統廣為人知時,您可以順便提及它—這是無害的,因為可能想使用它的使用者可能已經知道它了。例如,在首先解釋如何在 GNU 系統上使用您的套件之後,解釋如何在一些廣泛使用的非自由作業系統之上建置您的套件,或者如何將其與一些廣泛使用的非自由程式一起使用,是可以的。
但是,您應該只提供必要的信息,以幫助已經使用非自由程式的人員將您的程式與之一起使用—不要提供或參考有關專有程式的任何進一步信息,也不要暗示專有程式增強了您的程式,或者它的存在在任何方面都是一件好事。目標應該是已經使用專有程式的人員將獲得他們需要的關於如何將您的自由程式與之一起使用的建議,而尚未使用專有程式的人員將不會看到任何可能導致他們對其產生興趣的東西。
您不應為非自由程式推薦任何非自由附加元件,但可以提及有助於其與您的程式一起運作的自由附加元件,以及如何安裝自由附加元件,即使這需要執行某些非自由程式。
如果非自由程式或系統在您的程式領域中是默默無聞的,您的程式根本不應提及或支援它,因為這樣做會比宣傳您的程式更傾向於推廣非自由程式。(如果 Foobar 的存在在可能想使用您的程式的人員中並不廣為人知,您就不能指望在 Foobar 的使用者中為您的程式找到許多額外使用者。)
有時,一個程式本身是自由軟體,但依賴於非自由平台才能執行。例如,過去許多 Java 程式都依賴於一些非自由 Java 程式庫。(請參閱 https://gnu.dev.org.tw/philosophy/java-trap.html。)推薦或宣傳這樣的程式就是宣傳它需要的其他程式;因此,將對前者的提及視為對後者的提及來判斷。基於這個原因,我們在自由軟體目錄中列出 Java 程式時很謹慎:我們想避免宣傳非自由 Java 程式庫。
Java 不再有這個問題,但一般原則將保持不變:不要推薦、宣傳或使依賴於非自由軟體才能運行的程式合法化。
一些自由程式強烈鼓勵使用非自由軟體。一個典型的例子是 mplayer
。它本身是自由軟體,自由程式碼可以處理某些種類的檔案。但是,mplayer
建議對其他種類的檔案使用非自由編解碼器,並且安裝 mplayer
的使用者很可能會同時安裝這些編解碼器。推薦 mplayer
實際上是在推廣使用非自由編解碼器。
因此,您不應推薦強烈鼓勵使用非自由軟體的程式。這就是我們不在自由軟體目錄中列出 mplayer
的原因。
GNU 套件不應將使用者導向任何用於自由軟體的非自由文件。可以包含在自由作業系統中的自由文件對於完成 GNU 系統或任何自由作業系統至關重要,因此鼓勵它是當務之急;推薦使用我們不允許包含的文件會削弱社群製作我們可以包含的文件的動力。因此,GNU 套件絕不應推薦非自由文件。
相比之下,即使期刊文章和教科書是非自由的,在程式的註解中引用它們來解釋其功能是可以的。這是因為即使它們是自由的,我們也不會將這些東西包含在 GNU 系統中—它們超出了軟體發行版需要包含的範圍。
引用描述或推薦非自由程式的網站是在宣傳該程式,因此請不要連結到(或指名提及)包含此類材料的網站。此政策尤其適用於 GNU 套件的網頁。
連結鏈呢?從幾乎任何網站追蹤連結最終都可能導致宣傳非自由軟體;這是網路本質固有的。以下是我們如何處理它的。
如果 AT&T 的網站推薦 AT&T 的非自由軟體套件,您不應參考 AT&T 的網站;您不應參考頁面 p,該頁面連結到 AT&T 的網站,將其呈現為取得某些非自由程式的地方,因為頁面 p 的該部分本身推薦並使非自由程式合法化。
但是,如果 p 包含用於其他目的(例如長途電話服務)的 AT&T 網站連結,則您沒有理由不連結到 p。
如果網頁需要使用者執行該程式才能使用該頁面,則該網頁會以隱含但特別強烈的方式推薦程式。許多頁面包含 JavaScript 程式碼,它們以這種方式推薦程式碼。此 JavaScript 程式碼可能是自由的或非自由的,但非自由是通常情況。
如果您要參考頁面的目的在不執行非自由 JavaScript 程式碼的情況下無法實現,那麼您不應參考它。因此,如果參考該頁面的目的是讓人們觀看影片或訂閱郵寄清單,並且如果使用者的瀏覽器封鎖非自由 JavaScript 程式碼,則觀看或訂閱無法運作,那麼請不要參考該頁面。
極端情況是,網站甚至依賴非自由 JavaScript 程式碼來查看頁面的內容。任何託管在 ‘wix.com
’ 上的網站都有這個問題,其他一些網站也是如此。引導人們到這些頁面閱讀其內容實際上是在敦促他們執行這些非自由程式—所以請不要參考這些頁面。(此類頁面也會破壞網路,因此它們因兩個原因而應受到譴責。)
相反,請引用頁面中的摘錄來表達您的觀點,或尋找另一個地方來參考該資訊。
Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. https://fsf.org/ Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
本授權條款的目的是使手冊、教科書或其他功能性和有用的文件在自由的意義上是自由的:確保每個人都擁有複製和重新發行它的有效自由,無論是否修改它,無論是商業性還是非商業性。其次,本授權條款為作者和出版商保留了一種方式來獲得其作品的讚譽,同時不對其他人所做的修改負責。
本授權條款是一種「著作權保護」,這表示文件的衍生作品本身也必須在相同的意義上是自由的。它補充了 GNU 通用公共授權條款,該條款是為自由軟體設計的著作權保護授權條款。
我們設計本授權條款是為了將其用於自由軟體的手冊,因為自由軟體需要自由文件:自由程式應附帶提供與軟體相同的自由的手冊。但是本授權條款不僅限於軟體手冊;它可以適用於任何文字作品,無論主題為何,也無論是否以印刷書籍形式出版。我們主要針對以教學或參考為目的的作品推薦本授權條款。
本授權條款適用於任何手冊或其他作品,以任何媒介形式,其中包含版權持有者放置的聲明,表明它可以根據本授權條款的條款發行。此類聲明授予全球範圍內、免版稅的授權,期限不受限制,以根據本文所述的條件使用該作品。「文件」(Document),在下文中,指的是任何此類手冊或作品。任何公眾成員都是被授權人,並被稱為「您」。如果您以版權法下需要許可的方式複製、修改或發行作品,則表示您接受本授權條款。
文件的「修改版本」(Modified Version)是指任何包含文件或其一部分的作品,無論是逐字複製,還是經過修改和/或翻譯成另一種語言。
「次要章節」(Secondary Section)是文件的具名附錄或前言章節,專門處理文件的出版商或作者與文件整體主題(或相關事項)的關係,並且不包含任何可以直接歸入該整體主題的內容。(因此,如果文件部分是數學教科書,則次要章節不得解釋任何數學。)這種關係可能是與主題或相關事項的歷史聯繫,或是關於它們的法律、商業、哲學、倫理或政治立場。
「不變章節」(Invariant Sections)是某些次要章節,其標題在聲明文件根據本授權條款發行的聲明中被指定為不變章節的標題。如果章節不符合上述次要章節的定義,則不允許將其指定為不變章節。文件可能包含零個不變章節。如果文件未識別任何不變章節,則沒有不變章節。
「封面文字」(Cover Texts)是在聲明文件根據本授權條款發行的聲明中列出的某些簡短文字段落,作為封面文字或底面文字。封面文字最多可以有 5 個字,而底面文字最多可以有 25 個字。
「透明」的文件副本意指機器可讀副本,以一種規格向大眾公開的格式呈現,該格式適用於使用通用文字編輯器(對於由像素組成的影像)或通用繪圖程式(對於繪圖)一些廣泛可用的繪圖編輯器直接修訂文件,且適用於輸入到文字格式器或自動翻譯成各種適用於輸入到文字格式器的格式。以其他透明檔案格式製作的副本,但其標記或缺少標記的安排旨在阻撓或阻止讀者後續修改,則不屬於透明。若影像格式用於大量文字,則不屬於透明。不「透明」的副本稱為「不透明」。
適用於透明副本的格式範例包括不含標記的純 ASCII、Texinfo 輸入格式、LaTeX 輸入格式、使用公開可用的 DTD 的 SGML 或 XML,以及符合標準的簡單 HTML、PostScript 或 PDF,其設計旨在供人修改。透明影像格式的範例包括 PNG、XCF 和 JPG。不透明格式包括僅能由專有文書處理器讀取和編輯的專有格式、DTD 和/或處理工具非普遍可用的 SGML 或 XML,以及某些文書處理器僅為輸出目的而產生的機器產生 HTML、PostScript 或 PDF。
「標題頁」對於印刷書籍而言,意指標題頁本身,加上後續頁面,以容納本授權條款要求出現在標題頁的資料,且必須清晰易讀。對於沒有標題頁格式的作品,「標題頁」意指最顯著顯示作品標題附近的文字,位於正文文字的開頭之前。
「發行者」意指向公眾發行文件副本的任何個人或實體。
「標題為 XYZ」的章節意指文件的具名子單元,其標題精確為 XYZ 或包含 XYZ,並在括號中附帶以另一種語言翻譯 XYZ 的文字。(此處 XYZ 代表下述特定的章節名稱,例如「致謝」、「獻詞」、「背書」或「歷史」)。當您修改文件時,「保留」此類章節的「標題」意指根據本定義,該章節仍為「標題為 XYZ」的章節。
文件可能在聲明本授權條款適用於文件的通知旁邊包含免責聲明。這些免責聲明被視為透過引用方式納入本授權條款,但僅限於聲明免責保證:這些免責聲明可能具有的任何其他暗示均為無效,且對本授權條款的含義不產生任何影響。
您可以使用任何媒介複製和發行文件,無論是商業或非商業用途,前提是在所有副本中複製本授權條款、著作權聲明以及聲明本授權條款適用於文件的授權條款通知,且您不得在本授權條款的條件中新增任何其他條件。您不得使用技術措施來阻礙或控制您製作或發行的副本的閱讀或進一步複製。但是,您可以接受報酬以換取副本。如果您發行數量足夠龐大的副本,您也必須遵守第 3 節中的條件。
您也可以在上述相同的條件下借出副本,並且您可以公開展示副本。
如果您發行數量超過 100 份的文件印刷副本(或常用印刷封面的媒體副本),且文件的授權條款通知要求封面文字,您必須將副本封入封面中,封面必須清晰易讀地載明所有這些封面文字:正面封面的正面封面文字和背面封面的背面封面文字。兩個封面也必須清晰易讀地將您標識為這些副本的發行者。正面封面必須呈現完整標題,且標題的所有詞語均同樣醒目且可見。您可以在封面上額外新增其他材料。只要對封面的變更不影響文件標題的保留並滿足這些條件,則在其他方面可視為逐字複製。
如果任一封面的所需文字過於冗長而無法清晰易讀地容納,您應將最先列出的文字(盡可能容納)放在實際封面上,並將其餘文字繼續放在相鄰頁面上。
如果您發行或散佈數量超過 100 份的文件不透明副本,您必須在每個不透明副本中包含一份機器可讀的透明副本,或在每個不透明副本中或隨附聲明一個電腦網路位置,一般網路使用者公眾可以透過該位置使用公用標準網路協定下載文件的完整透明副本,且不含新增材料。如果您使用後一種選項,當您開始大量發行不透明副本時,您必須採取合理謹慎的步驟,以確保透明副本在所述位置保持可存取狀態,直到至少在您最後一次向公眾發行該版本的任何不透明副本(直接或透過您的代理商或零售商)後一年為止。
我們要求(但非必要)您在重新散佈大量副本之前充分聯繫文件作者,讓他們有機會向您提供文件的更新版本。
您可以根據上述第 2 節和第 3 節的條件複製和發行文件的修改版本,前提是您根據本授權條款精確發行修改版本,修改版本因此扮演文件的角色,從而向擁有修改版本副本的任何人授權修改版本的發行和修改。此外,您必須在修改版本中執行以下操作:
如果修改版本包含符合次要章節資格且未包含從文件中複製的材料的新前言章節或附錄,您可以選擇將部分或全部這些章節指定為不變章節。若要執行此操作,請將其標題新增至修改版本授權條款通知中的不變章節清單。這些標題必須與任何其他章節標題不同。
您可以新增一個標題為「背書」的章節,前提是其中僅包含各方對您的修改版本的背書,例如,同儕審查聲明或組織已批准該文字作為標準的權威定義。
您可以新增最多五個詞語的段落作為正面封面文字,以及最多 25 個詞語的段落作為背面封面文字,新增至修改版本中封面文字清單的末尾。任何單一實體只能新增(或透過安排新增)一段正面封面文字和一段背面封面文字。如果文件已包含您先前新增或由您代表的同一實體安排新增的相同封面的封面文字,則您不得新增另一個;但您可以替換舊的封面文字,但須經新增舊封面文字的先前發行者的明確許可。
文件的作者和發行者並未透過本授權條款授予使用其姓名進行宣傳或聲明或暗示對任何修改版本的背書的許可。
您可以根據上述第 4 節中針對修改版本的定義條款,將文件與根據本授權條款發行的其他文件合併,前提是您在合併版本中包含所有原始文件的所有不變章節(未經修改),並在合併作品的授權條款通知中將其全部列為不變章節,且您保留其所有免責聲明。
合併作品僅需包含一份本授權條款的副本,且多個相同的不變章節可以用單一副本替換。如果有多個標題相同但內容不同的不變章節,請透過在每個此類章節的標題末尾加上括號,括號中註明該章節的原始作者或發行者姓名(如果已知),或使用唯一編號,使每個此類章節的標題成為唯一。對合併作品的授權條款通知中的不變章節清單中的章節標題進行相同的調整。
在合併版本中,您必須合併各原始文件中標題為「歷史」的任何章節,形成一個標題為「歷史」的章節;同樣合併標題為「致謝」的任何章節以及標題為「獻詞」的任何章節。您必須刪除所有標題為「背書」的章節。
您可以製作一個文件集,其中包含文件和根據本授權條款發行的其他文件,並將各文件中本授權條款的個別副本替換為包含在文件集中的單一副本,前提是您在所有其他方面均遵循本授權條款針對每個文件的逐字複製規則。
您可以從此類文件集中提取單一文件,並根據本授權條款個別發行,前提是您在提取的文件中插入本授權條款的副本,並在關於該文件的逐字複製的所有其他方面均遵循本授權條款。
在儲存或發行媒體的卷冊中或之上,將文件或其衍生作品與其他個別且獨立的文件或作品彙編在一起,如果從彙編產生的著作權不用於限制彙編使用者的合法權利,使其超出個別作品許可的範圍,則稱為「彙編」。當文件包含在彙編中時,本授權條款不適用於彙編中並非文件衍生作品的其他作品。
如果第 3 節的封面文字要求適用於這些文件副本,則如果文件少於整個彙編的一半,文件的封面文字可以放在括住彙編內文件的封面上,或者如果文件為電子形式,則放在封面的電子等效物上。否則,它們必須出現在括住整個彙編的印刷封面上。
翻譯被視為一種修改,因此您可以根據第 4 節的條款發行文件的翻譯版本。將不變章節替換為翻譯版本需要其著作權持有人的特別許可,但您可以除了這些不變章節的原始版本之外,還包含部分或全部不變章節的翻譯版本。您可以包含本授權條款的翻譯版本,以及文件中所有授權條款通知和任何免責聲明,前提是您也包含本授權條款的原始英文版本以及這些通知和免責聲明的原始版本。如果本授權條款的翻譯版本與原始版本或通知或免責聲明之間存在歧義,則以原始版本為準。
如果文件中的章節標題為「致謝」、「獻詞」或「歷史」,則保留其標題(第 1 節)的要求(第 4 節)通常需要變更實際標題。
除非本授權條款明確規定,否則您不得複製、修改、再授權或發行文件。任何其他嘗試複製、修改、再授權或發行文件的行為均為無效,並將自動終止您在本授權條款下的權利。
但是,如果您停止所有違反本授權條款的行為,則您從特定著作權持有人處獲得的授權將恢復 (a) 暫時恢復,除非且直到著作權持有人明確且最終終止您的授權,以及 (b) 永久恢復,如果著作權持有人未在停止違規行為後 60 天內以某種合理方式通知您違規行為。
此外,如果您從特定著作權持有人處獲得的授權,在著作權持有人以某種合理方式通知您違規行為後,且這是您第一次收到該著作權持有人發出的違反本授權條款(針對任何作品)的通知,並且您在收到通知後 30 天內糾正違規行為,則您的授權將永久恢復。
根據本節終止您的權利不會終止已從您處根據本授權條款收到副本或權利的各方的授權。如果您的權利已被終止且未永久恢復,則收到部分或全部相同材料的副本不會授予您任何使用權。
自由軟體基金會可能會不時發布 GNU 自由文檔許可證的新修訂版本。此類新版本在精神上將與目前版本相似,但在細節上可能有所不同,以解決新問題或疑慮。請參閱 https://gnu.dev.org.tw/licenses/。
每個授權條款版本都有一個可區分的版本號。如果文件指定本授權條款的特定編號版本「或任何後續版本」適用於該文件,您可以選擇遵循該指定版本或自由軟體基金會發布的任何後續版本(非草稿)的條款和條件。如果文件未指定本授權條款的版本號,您可以選擇自由軟體基金會曾發布的任何版本(非草稿)。如果文件指定代理人可以決定可以使用本授權條款的哪些未來版本,則該代理人對版本的公開接受聲明將永久授權您為文件選擇該版本。
「大型多人協作網站」(或「MMC 網站」)意指任何發布受著作權保護作品,並為任何人編輯這些作品提供顯著便利設施的全球資訊網伺服器。任何人都可以編輯的公共 wiki 就是此類伺服器的範例。網站中包含的「大型多人協作」(或「MMC」)意指 MMC 網站上發布的任何受著作權保護作品集。
「CC-BY-SA」意指創用 CC 姓名標示-相同方式分享 3.0 授權條款,由創用 CC 公司發布,該公司是一家位於加利福尼亞州舊金山的非營利公司,以及該同一組織發布的該授權條款的未來著作權分享版本。
「併入」意指將文件全部或部分作為另一份文件的一部分發布或重新發布。
如果 MMC 根據本授權條款授權,且所有首次在本授權條款下發布在非此 MMC 的其他位置,且隨後全部或部分併入 MMC 的作品,(1) 沒有封面文字或不變章節,且 (2) 在 2008 年 11 月 1 日之前併入,則 MMC「符合重新授權的資格」。
MMC 網站的營運者可以在 2009 年 8 月 1 日之前的任何時間,在同一網站上根據 CC-BY-SA 重新發布網站中包含的 MMC,前提是 MMC 符合重新授權的資格。
若要在您撰寫的文件中使用本授權條款,請在文件中包含本授權條款的副本,並在標題頁之後放置以下著作權和授權條款通知
Copyright (C) year your name. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''.
如果您有不變章節、正面封面文字和背面封面文字,請將「with…Texts.」行替換為以下內容
with the Invariant Sections being list their titles, with the Front-Cover Texts being list, and with the Back-Cover Texts being list.
如果您有不含封面文字的不變章節,或三者的其他組合,請合併這兩個替代方案以適應情況。
如果您的文件包含程式碼的非平凡範例,我們建議根據您選擇的自由軟體授權條款(例如 GNU 通用公共許可證)平行發行這些範例,以允許在自由軟體中使用它們。
上一頁:GNU 自由文檔許可證,上一層:頂層 [目錄][索引]
跳至: | # - A B C D E F G H I K L M N O P Q R S T U V W X |
---|
跳至: | # - A B C D E F G H I K L M N O P Q R S T U V W X |
---|