歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> pkg-config指南

pkg-config指南

日期:2017/2/28 15:59:08   编辑:Linux教程

原文:

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]
  1. prefix=/usr/local
  2. exec_prefix=${prefix}
  3. includedir=${prefix}/include
  4. libdir=${exec_prefix}/lib
  5. Name: foo
  6. Description: The foo library
  7. Version: 1.0.0
  8. Cflags: -I${includedir}/foo
  9. Libs: -L${libdir} -lfoo

以預定義關鍵字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]
  1. prefix=/usr/local
  2. includedir=${prefix}/include
  3. Cflags: -I${includedir}/foo

最重要的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]
  1. foo.pc:
  2. prefix=/usr
  3. exec_prefix=${prefix}
  4. includedir=${prefix}/include
  5. libdir=${exec_prefix}/lib
  6. Name: foo
  7. Description: The foo library
  8. Version: 1.0.0
  9. Cflags: -I${includedir}/foo
  10. Libs: -L${libdir} -lfoo
  11. bar.pc:
  12. prefix=/usr
  13. exec_prefix=${prefix}
  14. includedir=${prefix}/include
  15. libdir=${exec_prefix}/lib
  16. Name: bar
  17. Description: The bar library
  18. Version: 2.1.2
  19. Requires.private: foo >= 0.7
  20. Cflags: -I${includedir}
  21. Libs: -L${libdir} -lbar

模塊的版本可以用--modversion選項獲得。

[python]
  1. $ pkg-config --modversion foo
  2. 1.0.0
  3. $ pkg-config --modversion bar
  4. 2.1.2

要打印模塊的鏈接標識,就用--libs選項。

[python]
  1. $ pkg-config --libs foo
  2. -lfoo
  3. $ pkg-config --libs bar
  4. -lbar
請注意,pkg-config壓縮了兩個模塊Libs字段。這是因為pkg-config對-L標識有特殊處理,它知道${libdir}目錄/usr/lib是系統鏈接器搜素路徑的一部分。也就是pkg-config受到了鏈接器選項的影響。

還有就是,雖然foo是bar所需要的,但是沒有輸出foo的鏈接標識。這是因為,只使用bar庫的應用並不直接需要foo。對應靜態鏈接bar的應用,我們需要兩個鏈接標識:

[python]
  1. $ pkg-config --libs --static bar
  2. -lbar -lfoo

這種情況下,pkg-config就要輸出兩個鏈接標識,這樣才能保證靜態鏈接的應用可以找到所有必須的符號。另一方面,它會輸出所有的Cflags字段。

[python]
  1. $ pkg-config --cflags bar
  2. -I/usr/include/foo
  3. $ pkg-config --cflags --static bar
  4. -I/usr/include/foo

還有一個有用的選項,--exists,可以用來測試模塊的可用性。

[python]
  1. $ pkg-config --exists foo
  2. $ echo $?
  3. 0

最值得注意的pkg-config特性是它所提供的版本檢測,可以用來確定某個版本是否可用。

[python]
  1. $ pkg-config --exists foo
  2. $ echo $?
  3. 0

有些命令在結合--print-errors選項使用時可以輸出更詳細的信息。

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

上面的信息出現了PKG_CONFIG_PATH環境變量。這個變量用來配置pkg-config的搜索路徑。在類Unix操作系統中,會搜索/usr/lib/pkconfig和/usr/share/pkgconfig目錄。這通常已經覆蓋了系統已經安裝的模塊。但是,有些本地模塊可能安裝在了其他路徑,例如/usr/local。這種情況下,需要指定搜索路徑,以便pkg-config可以定位.pc文件。

[python]
  1. $ pkg-config --modversion hello
  2. Package hello was not found in the pkg-config search path.
  3. Perhaps you should add the directory containing `hello.pc'
  4. to the PKG_CONFIG_PATH environment variable
  5. No package 'hello' found
  6. $ export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
  7. $ pkg-config --modversion hello
  8. 1.0.0

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]
  1. cc `pkg-config --cflags --libs x` -o myapp myapp.c

將pkg-config集成到autoconfautomake中使用會更強大。但是,用PKG_CONFIG_PATH宏可以很容易的在建立過程中訪問元數據。

[python]
  1. configure.ac:
  2. PKG_CHECK_MODULES([X], [x])
  3. Makefile.am:
  4. myapp_CFLAGS = $(X_CFLAGS)
  5. myapp_LDADD = $(X_LIBS)

如果找到了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.

Copyright © Linux教程網 All Rights Reserved