原文:
Guide for pkg-config
http://people.freedesktop.org/~dbn/pkg-config-guide.html
Dan Nicholson
-----------------------------------------------------
概述
為什麼?
一些概念
寫pkg-config文件
使用pkg-config文件
常見問題
-----------------------------------------------
概述
這個文檔的目的是從用戶和開發者的角度給一個pkg-config工具的使用概述。本文復習一些pkg-config背後的概念,怎樣寫pkg-config文件來支持你的項目,以及怎樣用pkg-config集成第三方項目。
關於pkg-config的更多信息可以在web站點和pkg-config的man手冊中找到。
本文檔假的pkg-config在類UNIX操作系統中使用,例如Linux。其他平台可能在一些細節上的存在差別。
為什麼?
現代計算機系統使用了很多分層組件為用戶提供應用。其中一個困難就是如何正確的整合這些組件。pkg-config會收集系統中安裝的庫的數據,然後提供給用戶。
如果沒有pkg-config這樣的數據系統,定位計算機提供的服務和獲取它們的細節會很困難。對於開發者,安裝軟件包的pkg-config文件極大的簡化了對API的獲取。
一些概念
使用pkg-config的初級階段是為編譯和鏈接程序時提供必要的細節。數據存儲在pkg-config文件中。這些文件有一個.pc的後綴,放在一個特定的、pkg-config工具所知道的位置。我們會在後面描述更多的細節。
這個文件的格式包括預定義的關鍵字和自由形式的變量。例如:
[plain]以預定義關鍵字Name:為例,以關鍵字開頭,後面跟一個冒號和一個值。變量是一個字符串和一個值,例如prefix=,用等號分開。關鍵字是由pkg-config定義和輸出的。變量不是必須的,但可以被關鍵字用來定位和存儲pkg-config沒有覆蓋的數據。
這裡只是簡單的描述一下關鍵字。更深入的描述和怎樣有效的使用它們將在“寫pkg-config文件”段中給出。
Name:一個人們可讀的鏈接庫或軟件包的名稱,這不影響pkg-config的使用,它用的是.pc文件的名稱。
Description:關於軟件包的簡單描述。
URL:一個URL,可以在那裡獲得更多的信息,並且下載這個軟件包。
Version:軟件包的版本。
Requires:這個軟件包所需的包的列表。這些包的版本可能用一寫運算符來指定:=、>、<、>=、<=。
Requires.private:這個軟件包所需的私有包的列表,不會暴露給應用。版本的指定規則與Requires相同。
Conflicts:可選,描述了會與這個軟件包產生沖突的包。版本的指定規則與Requires相同。這個域會提供同一個包的多個實例,例如:Conflicts: bar < 1.2.3, bar >= 1.3.0。
Cflags:為這個軟件包指定編譯器選項,以及pkg-config不支持的必要的庫。如果所需的庫支持pkg-config,應該將它們添加到Requires和Requires.private。
Libs:為這個軟件包指定的鏈接選項,以及pkg-config不支持的必要的庫。與Cflags的規則相同。
Libs.private:這個軟件包所需的私有庫的鏈接選項,不會暴露給應用。規則與Cflags相同。
寫pkg-config文件
為一個軟件包創建pkg-config時,首先要確定怎樣描述它。一個文件最好只用於描述一個庫,所以,每個軟件包至少需要像它所需的鏈接庫那麼多的pkg-config文件。
軟件包的名字是由pkg-config數據文件的名字確定的。就是文件名去掉.pc後綴的那一部分。通常都用庫的名字命名.pc文件。例如,一個安裝libfoo.so的包會有一個相應的libfoo.c文件來包含pkg-config數據。這不是必須的,.pc文件僅僅是一個對你的庫的唯一標識符。所以,foo.pc或foolib.pc也能正常工作。
Name、Description和URL的值是純粹的信息,容易填寫。Version比較棘手,它要確保這個包可以被用戶使用。pkg-config使用RPM算法來進行版本比較。Version最好是用點分開的十進制數字,例如1.2.3,因為字母可能引起意外的結果。數字應該是單調遞增的,並且要竟可能具體的描述這個庫。通常使用包的版本號即可,這樣可以方便使用者跟蹤。
在描述更多的有用的關鍵字之前,有必要展示一下變量的定義。最常見的用法是定義安裝路徑,這樣就不會使其他字段顯得雜亂。因為變量是擴大遞歸的,在結合autoconf派生路徑時,這會很有用。
[plain]最重要的pkg-config數據字段是Requires,Requires.private,Cflags,Libs 和 Libs.private。它們定義的數據被外部項目用來編譯和鏈接庫。
Requires和Requires.private定義了庫所需的其他模塊。通常首選Requires.private,以便避免程序鏈接到一些不必要的庫。如果一個程序不使用所需庫的符號,它就不應該直接鏈接到這個庫。可以在overlinking的討論中看到更多詳細的解釋。
由於pkg-config通常會公開Requires庫的鏈接標識,這些模塊會變成程序的直接依賴。另外���Requires.private中的庫只有在靜態鏈接是才會被包含。正因如此,pkg-config通常只會適當的從Requires中的同一個包中添加模塊。
Libs包含了使用庫是所必須的鏈接標識。此外,Libs和Libs.private還包含了pkg-config不支持的庫的鏈接標識。與Requires類似,首選將外部庫的鏈接標識添加到Libs.private,這樣,程序就不會獲得額外的直接依賴。
最後,Cflags包含了所用的庫的編譯標識。與Libs不同,Cflags沒有私有變種。這是因為,數據類型和宏定義在任何鏈接情況下都是需要的。
使用pkg-config文件
假設系統中已經安裝了.pc文件,pkg-config工具就被用來提取其中的數據。執行pkg-config --help命令可以看到一些關於命令選項的簡單描述。深入的描述可以在pkg-config(1)的man手冊頁中找到。本地將對一些常見的用法進行簡單的描述。
假設系統中已經有了兩個模塊:foo和bar。它們的.pc文件可能像下面這樣:
[plain]模塊的版本可以用--modversion選項獲得。
[python]要打印模塊的鏈接標識,就用--libs選項。
[python]還有就是,雖然foo是bar所需要的,但是沒有輸出foo的鏈接標識。這是因為,只使用bar庫的應用並不直接需要foo。對應靜態鏈接bar的應用,我們需要兩個鏈接標識:
[python]這種情況下,pkg-config就要輸出兩個鏈接標識,這樣才能保證靜態鏈接的應用可以找到所有必須的符號。另一方面,它會輸出所有的Cflags字段。
[python]還有一個有用的選項,--exists,可以用來測試模塊的可用性。
[python]最值得注意的pkg-config特性是它所提供的版本檢測,可以用來確定某個版本是否可用。
[python]有些命令在結合--print-errors選項使用時可以輸出更詳細的信息。
[python]上面的信息出現了PKG_CONFIG_PATH環境變量。這個變量用來配置pkg-config的搜索路徑。在類Unix操作系統中,會搜索/usr/lib/pkconfig和/usr/share/pkgconfig目錄。這通常已經覆蓋了系統已經安裝的模塊。但是,有些本地模塊可能安裝在了其他路徑,例如/usr/local。這種情況下,需要指定搜索路徑,以便pkg-config可以定位.pc文件。
[python]autoconf也提供了一些宏,可以將pkg-config集成到項目中。
* PKG_PROG_PKG_CONFIG([MIN-VERSION]):定位系統中的pkg-config工具,並檢測版本兼容性。
* PKG_CHECK_EXISTS(MODULES,[ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]):檢測指定的模塊是否存在。
* PKG_CHECK_MODULES(VARIABLE-PREFIX,MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])::檢測指定的模塊是否存在,如果存在,就根據 pkg-config --cflags和pkg-config --libs的輸出設置<VARIABLE-PREFIX>_CFLAGS and <VARIABLE-PREFIX>_LIBS。
常見問題
1. 我的程序使用了x庫,我該怎麼做?
pkg-config的輸出可以在編譯命令中使用,假設x庫已經有了一個叫做x.pc的pkg-config文件:
[python]將pkg-config集成到autoconf和automake中使用會更強大。但是,用PKG_CONFIG_PATH宏可以很容易的在建立過程中訪問元數據。
[python]如果找到了x模塊,宏會填充和替代X_CFLAGS和X_LIBS變量。如果沒有找到,會產生錯誤。配置PKG_CHECK_MODULES的第3、4個參數,可以控制沒有找到模塊時的動作。
2. 我的z庫安裝了保護libx頭的頭文件。我應該在z.pc中添加什麼?
如果x庫支持pkg-config,將它添加到Requires.private字段。如果不支持,就配置Cflags字段,添加一些使用libx頭時所需的編譯器標識。在這兩種情況下,無論是否使用了--static,pkg-config都會輸出編譯器標識。
3. 我的z庫內部使用了libx,但是不能再公開API中暴露libx的數據類��。我應該在z.pc中添加什麼?
同樣的,如果x支持pkg-config,就把它添加到Requires.private。這種情況下,就沒必要發出編譯器標識,但是在今天鏈接時要確保有鏈接器標識。如果libx不支持pkg-config,就將必要的鏈接器標識添加到Libs.private。
——————————————————————
Dan Nicholson <dbn.lists (at) gmail (dot) com>
Copyright (C) 2010 Dan Nicholson.
This document is licensed under the GNU General Public License, Version 2 or any later version.