pkg-config 學習筆記

術語對照表:
參數 - option
選項 - flag算法

基本概念

在編譯和連接時,提供必要的庫文件細節。元數據存儲在 pkg-config 文件裏面,文件後綴 .pc ,文件須要存放在 pkg-config 工具可以找到的特定位置。工具

文件內容包括預約義的元數據關鍵詞和形式自由的變量。
示例內容以下:ui

prefix=/usr/local
exec_prefix=${prefix}
includedir=${prefix}/include
libdir=${exec_prefix}/lib

Name: foo
Description: The foo library
Version: 1.0.0
Cflags: -I${includedir}/foo
Libs: -L${libdir} -lfoo

關鍵字定義好比 Name:XXX ,後面跟着一個冒號 : 和值。關鍵字由 pkg-config 工具定義。
變量好比 prifix=XXX ,後面跟着一個等號 = 和值。變量一方面能夠簡化 .pc 文件的書寫,一方面能夠存儲一些 pkg-config 工具沒法覆蓋的數據。code

下面是對關鍵字字段的一個簡要介紹,更深刻的部分見 編寫 部分(也就是下一節):遞歸

  • Name:庫或者包的一個別名,方面人類閱讀。pkg-config 工具使用的名字是 .pc 文件的文件名,不會用到這個名字。ip

  • Description:對包的一個簡要描述。get

  • URL:人們能夠從這個地址下載包,並得到更多有關包的信息。io

  • Version:包的版本號編譯

  • Requires:這個包依賴的包的列表。依賴的包的版本號能夠經過比較操做符指定(例如 = 、<=)變量

  • Requires.private:這個包依賴的包的列表。可是目標應用沒法使用這些包。

  • Conflicts:可選字段,描述哪些包與此包衝突。

  • Cflags:不支持 pkg-config 的一些編譯選項和庫依賴。若是依賴的庫支持 pkg-config ,那麼這個庫就應該放到前面的 Requires 或者 Requires.private 字段中去。

  • Libs:不支持 pkg-config 的一些連接選項和庫依賴。

  • Libs.private:不支持 pkg-config 的一些用於私有庫依賴的連接選項。

編寫

在給一個包建立 pkg-config 文件(後面簡稱爲 pc 文件)的時候首先要決定這些文件的發佈方式,最好是每一個庫都有一個對應的 pc 文件,這樣每一個包至少有跟庫的數量一致的 pc 文件。

包的名字由 pc 元數據文件的文件名決定。也就是文件名中除去 .pc 後綴的那部分。一個常見的選擇是讓庫名字和 .pc 文件的名字一致。例如,下載了 libfoo.so 的包應該有一個對應的 libfoo.pc 文件包含相關的 pkg-config 元數據。可是不必定要這要,把pc文件命名爲 foo.pc 或者 foolib.pc 也是可行的。

Name、Description、URL字段都是純信息,很容易填。 Version 字段有一點點麻煩,須要確保該數據能被用戶所使用。pkg-config 使用 RPM 的算法來進行版本比較。建議使用經過句點 . 分開的十進制數,若是使用字母的話會出現不可預知的錯誤。版本號必須單調遞增,而且足以制定這個庫文件。一般使用包的版本號就足夠了,並且這樣方面用戶進行追蹤。

在介紹更有用的字段以前,先介紹一下變量的定義。變量最多見的用途是指定安裝路徑,使用變量可使元數據字段保持簡潔。變量是遞歸地進行展開的,所以跟自動生成的路徑一塊兒使用十分有用(這句話沒看懂)。

prefix=/usr/local
includedir=${prefix}/include

Cflags: -I${includedir}/foo

pkg-config 裏面最重要的元數據字段是 Requires、 Requires.private 、 Cflags 、 Libs 和 Libs.private 。它們定義了外部項目編譯連接這個庫時所須要元數據。

Requires 和 Requires.private 定義了這個庫所需的其餘模塊。一般建議使用 Require 的 private 變體,以免把沒必要要的庫暴露給用戶程序。若是用戶程序不會用到依賴庫的符號,那麼這個庫經不該該被直接連接到用戶程序上。更詳盡的討論參見 overlinking

由於 pkg-config 老是暴露 Requires 庫的連接選項,因此這些模塊會成爲程序的直接依賴。另外一方面,Require.private 中的庫在靜態連接的時候只會被包含。所以,一般比較合適的作法是僅在 Requires 字段添加同一個包裏面的模塊。 (老外的寫做水平也是堪憂啊,各類 包、模塊、庫詞彙混雜在一塊兒使用。)

Libs 包含使用庫所必需的連接選項。此外,Libs 和 Libs.private 還包含 pkg-config 不支持的其餘庫的連接選項。一樣的,推薦把外部庫的連接選項放到 Libs.private 字段裏面去。

Cflags 包含這個庫所必需的編譯選項。與 Libs 不一樣的是這個字段沒有 private 變體。由於不論是怎麼樣的連接場景,數據類型和宏定義都是須要的。

使用

假設系統安裝了 .pc 文件,那麼 pkg-config 工具就是用來抽取元數據的。執行 pkg-config --help 能夠查看其各個參數的簡要描述。更詳細的描述能夠在 pkg-config(1) 手冊頁中找到。本節會簡要介紹一些經常使用的狀況。

假設一個系統有個兩個模塊,foo 和 bar 。他們的 pc 文件能夠是這樣:

foo.pc:

prefix=/usr
exec_prefix=${prefix}
includedir=${prefix}/include
libdir=${exec_prefix}/lib

Name: foo
Description: The foo library
Version: 1.0.0
Cflags: -I${includedir}/foo
Libs: -L${libdir} -lfoo

bar.pc:

prefix=/usr
exec_prefix=${prefix}
includedir=${prefix}/include
libdir=${exec_prefix}/lib

Name: bar
Description: The bar library
Version: 2.1.2
Requires.private: foo >= 0.7
Cflags: -I${includedir}
Libs: -L${libdir} -lbar

模塊的版本號能夠經過 --modversion 參數查看

$ pkg-config --modversion foo
1.0.0
$ pkg-config --modversion bar
2.1.2

每一個模塊所須要的連接選項能夠經過 --libs 參數查看

$ pkg-config --libs foo
-lfoo
$ pkg-config --libs bar
-lbar

注意 pkg-config 隱去了部分 Libs 中的字段。這是由於當它看到 -L 選項時,知道 ${libdir} 路徑 /usr/lib 是系統連接器的查找路徑。使用 -L 能夠避免 pkg-config 干涉連接操做。

並且,儘管 foo 被 bar 所依賴,foo 的連接選項卻沒有輸出。這是由於 foo 並非用戶應用直接須要的模塊。而若是要靜態連接一個 bar 應用,須要同時設置兩個連接選項。

$ pkg-config --libs --static bar
-lbar -lfoo

pkg-config 須要把兩個連接選項都輸出來,確保靜態連接的應用可以找到全部須要的符號。另外一方面,它會把全部 Cflags
輸出。

$ pkg-config --cflags bar
-I/usr/include/foo
$ pkg-config --cflags --static bar
-I/usr/include/foo

另外一個經常使用的參數 --exists ,能夠用來檢測一個模塊的可用性。

$ pkg-config --exists foo
$ echo $?
0

pkg-config 的一個最優秀的特性是提供了版本檢測。它能夠檢測是否存在知足條件的版本。

$ pkg-config --libs "bar >= 2.7"
Requested 'bar >= 2.7' but version of bar is 2.1.2

有些命令在使用 --print-errors 參數後會進行更詳盡的輸出。

$ pkg-config --exists --print-errors xoxo
Package xoxo was not found in the pkg-config search path.
Perhaps you should add the directory containing `xoxo.pc'
to the PKG_CONFIG_PATH environment variable
No package 'xoxo' found

上面的輸出信息提到了 PKG_CONFIG_PATH 環境變量。這個變量用來增長 pkg-config 的搜索路徑。在一個典型的 Unix 系統中,它會搜索 /usr/lib/pkgconfig/usr/share/pkgconfig 。這兩個路徑能夠覆蓋系統安裝的模塊。可是,有些本地模塊可能下載到了其餘路徑中,例如 /usr/local
這種狀況下,有必要把這個搜索路徑添加進去以便 pkg-config 能夠定位 pc 文件。

$ pkg-config --modversion hello
Package hello was not found in the pkg-config search path.
Perhaps you should add the directory containing `hello.pc'
to the PKG_CONFIG_PATH environment variable
No package 'hello' found
$ export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
$ pkg-config --modversion hello
1.0.0

把 pkg-config 模塊集成到用戶項目中的時候,使用自動配置(autoconf)的宏能夠簡化這個過程。

  • PKG_PREREQ(MIN-VERSION):確保所使用的自動配置宏高於或等於 MIN-VERSION 。

  • 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 和 <VARIABLE-PREFIX>_LIBS 。

常見問題

相關文章
相關標籤/搜索