美文网首页linux技巧
pkg-config用法详解

pkg-config用法详解

作者: Domibaba | 来源:发表于2022-03-29 03:28 被阅读0次

一、背景

  当开发过程中使用第三方库时,我们需要知道被使用的第三方库,它包含的头文件在哪儿、它依赖哪些库、它的编译选项有哪些、它的链接选项有哪些等等,也就是说需要知道这些信息我们才能正确的编译和链接它。当然可以通过直接在编译选项中指定的方式来使用,但是,对于一些大型或者比较复杂的第三方库,包含的头文件或者依赖库非常多、非常复杂的时候,这样做的成本就非常高,而且非常不便于在不同平台之间的移植。
  pkg-config就是解决这个问题的一种方案。

二、pkg-config是什么

  pkg-config是一个用于获取系统中安装的包/库的相关信息(例如依赖头文件目录、依赖库文件目录)的程序,通常用于编译和链接一个或多个库,典型的用法为:

# Makefile
program: program.c
            cc program.c `pkg-config --cflags --libs XXX` # 此处的XXX表示库名称

  pkg-config通过读取一个名为<PackageName>.pc的文件来获取<PackageName>包的信息,一般会从/usr/lib/pkgconfig, /usr/share/pkgconfig, /usr/local/lib/pkgconfig, /usr/local/share/pkgconfig来查找.pc文件,除此之外,也会查找PKG_CONFIG_PATH环境变量指定的路径。

三、如何使用pkg-config

  LinuxmacOS系统可以通过man pkg-config来查看pkg-config的用法,选项有很多,此处不一一介绍,最常用到的两个选项是pkg-config --cflags <PackageName>pkg-config --libs <PackageName>,其含义分别为:

  • --cflags:获取编译<PackageName>包的所需要的预处理或者编译选项,例如-I/usr/include,指定编译<PackageName>包的头文件搜索路径。

  • --libs:获取编译<PackageName>包所需要的链接选项,例如-L/usr/lib指定库文件搜索路径,或-lXXX指定具体的库文件。

  下面以一个例子来说明pkg-config的用法,例子将会用到liblzma库(可以使用pkg-config --list-all来查看当前pkg-config已经识别的库),程序很简单,就是通过调用一个接口打印出liblzma的版本号,当然,接口是库liblzma提供的:

// test.cpp 
// 打印lzma库的版本号
#include "lzma.h"
#include <iostream>

int main(int argc, char** argv)
{
    const char* version = lzma_version_string();
    std::cout << "lzma version is: " << version << std::endl;
    return 0;
}

  lzma.hlzma库提供的一个头文件,lzma_version_stringlzma库提供的一个接口,功能为获取lzma库的版本号,不需要入参,返回一个常量字符串。程序调用此接口并将版本号打印出来。
  直接使用g++ -o test test.cpp是无法编译通过的,因为我们不知道关于lzma库的任何信息(例如头文件lzma.h头文件在哪个目录下,lzma库在哪个目录下等等),此时就是pkg-config发挥作用的时候了,编译命令如下:

// 在命令行中执行
g++ -o test test.cpp `pkg-config --cflags --libs liblzma`

  编译通过后得到可执行文件test,在我的环境中运行test得到如下输出:

./test
lzma version is: 5.2.5

四、自己编写的库如何让pkg-config使用

  从前面的介绍可以知道,pkg-config会在常用的系统路径下查找.pc文件,除此之外,还会查找PKG_CONFIG_PATH环境变量指定的路径。
  那么自己编写的库,想让pkg-config找到并使用,需要两个步骤:

  • 编写库相关的.pc文件;
  • .pc文件放在pkg-config的查找路径之下;

  第二点好办,将.pc文件放在几个指定的路径或者环境变量PKG_CONFIG_PATH指定的路径即可,那么.pc文件怎么编写呢?

.pc文件的格式

.pc是用来描述一个库的名称、版本、编译选项、链接选项等内容的文件,因此它需要包含:名称、描述、版本信息、编译选项和链接选项几个部分,具体来说,是对Name、Description、Version、Cflags、Libs五个变量的一个定义,定义方式为变量: 内容,其中CflagsLibs可能会使用编译或者链接的一些选项,例如-I-L等。

# mylib.pc
Name: mylib
Description: mylib for test
Version: 1.0
Cflags:-I/XXX/mylib/include
Libs:-L/XXX/mylib/lib –lmylib

当然,这个文件也可以定义其他变量,以方便使用。例如目录可以使用一个变量来定义,这样在CflagsLibs中就直接使用该变量即可,例如:

# mylib.pc
librootdir=/XXX/mylib
Name: mylib
Description: mylib for test
Version: 1.0
Cflags:-I${librootdir}include
Libs:-L${librootdir}/lib –lmylib

  下面来写一个简单的示例,

4.1 库文件的编写

  首先编写一个库文件,目录结构如下:

mymath # 库的顶层目录
--mymath.pc # .pc文件,以便pkg-config使用
--include # 库提供的头文件目录
----mymath.h # 库提供的头文件
--lib # 库文件目录
----libmymath.a # 库文件

  库文件提供的内容很简单:一个add接口用于计算两个整数的和并打印出来。由于是使用库的方式,mymath.cpp文件只是在编译libmymath.a时需要使用,并不需要对外提供,下面是mymath.cppmymath.h的内容:

// mymath.h
namespace mymath {
    int add(int a, int b);
};

// mymath.cpp
#include "mymath.h"
#include <iostream>
namespace mymath {
    int add(int a, int b)
    {
        std::cout << "Add " << a << " and " << b << " is " << a + b << std::endl;
        return a + b;
    }
};
4.2 .pc文件的编写

  头文件和库文件所在的目录根据实际指定即可,此处以/XXX代替.

# mymath.pc
librootdir=/XXX/mymath

Name: mymath
Description: a simple add function
Version: 1.0
Cflags: -I${librootdir}/include
Libs: -L${librootdir}/lib -lmymath
4.3 让.pc文件被pkg-config可见

  可以把mymath.pc拷贝到系统查找的几个目录或者将mymath.pc文件所在的目录添加到环境变量PKG_CONFIG_PATH中,此处使用第二种办法:

# 命令行中执行,/XXX需要替换成实际的目录
PKG_CONFIG_PATH=/XXX/mymath
export PKG_CONFIG_PATH

  这样就可以使用pkg-config命令来获取mymath库的信息了:

# 命令行中执行
pkg-config --list-all | grep "mymath"
mymath           mymath - a simple add function

pkg-config --cflags --libs mymath
-I/XXX/mymath/include -L/XXX/mymath/lib -lmymath
4.4 测试pkg-config对库mymath的使用
// test.cpp
#include "mymath.h"
int main(int argc, char** argv)
{
    mymath::add(1, 2);
    return 0;
}

  编译命令如下:

g++ -o test test.cpp `pkg-config --cflags --libs mymath`

  运行结果:

./test
Add 1 and 2 is 3

相关文章

网友评论

    本文标题:pkg-config用法详解

    本文链接:https://www.haomeiwen.com/subject/baxvjrtx.html