什么是pkg-config
简单理解,pkg-config根据.pc
结尾的文件做依赖配置。
找到.pc文件周,解析其内容,然后对底层构建工具(C/C++编译器、链接器)或高层构建工具(automake?, cmake)提供具体配置项目。
通常是在POSIX系统(Linux,MacOS等)使用pkg-config,解决第三方依赖项配置问题。
近些年来随着CMake的越发流行,原本用pkg-config的很多软件包提供了cmake的配置作为替代;少部分仍然提供.pc文件作为兼容考虑;还有另外一小部分的软件包,即使基于cmake构建了,对外提供依赖配置时仍然只有.pc文件。
这就导致一个问题:虽然我学会了cmake在大部分时候都能解决依赖问题,但个别格楞子软件包还是要用pkg-config来搞。
pkg-config的基本使用
官方说明
最直接的说明,来自于Ubuntu下pkg-config的man页面:
DESCRIPTION
The pkg-config program is used to retrieve information about installed libraries in the system. It
is typically used to compile and link against one or more libraries. Here is a typical usage sce‐
nario in a Makefile:
program: program.c
cc program.c $(pkg-config --cflags --libs gnomeui)
pkg-config retrieves information about packages from special metadata files. These files are named
after the package, and has a .pc extension. On most systems, pkg-config looks in /usr/lib/pkgcon‐
fig, /usr/share/pkgconfig, /usr/local/lib/pkgconfig and /usr/local/share/pkgconfig for these files.
It will additionally look in the colon-separated (on Windows, semicolon-separated) list of directo‐
ries specified by the PKG_CONFIG_PATH environment variable.
The package name specified on the pkg-config command line is defined to be the name of the metadata
file, minus the .pc extension. If a library can install multiple versions simultaneously, it must
give each version its own name (for example, GTK 1.2 might have the package name "gtk+" while GTK
2.0 has "gtk+-2.0").
In addition to specifying a package name on the command line, the full path to a given .pc file may
be given instead. This allows a user to directly query a particular .pc file.
也就是说,pkg-config默认会在以下路径中查找指定的包(库)对应的.pc文件:
-
/usr/lib/pkgconfig
目录 -
/usr/share/pkgconfig
目录 -
/usr/local/lib/pkgconfig
目录 -
/usr/local/share/pkgconfig
目录 -
PKG_CONFIG_PATH
环境变量里的目录(可通过export PKG_CONFIG_PATH=XXX
来修改) - 给pkg-config传入的.pc文件绝对路径
而比较常用的选项是:
--cflags 表示C/C++编译选项,例如指定头文件搜索目录
--libs 表示链接选项,例如库的绝对目录,链接库按顺序列出等
例如Linux下apt安装的opencv的结果分别为:
(base) 1080Ti% pkg-config opencv --cflags
-I/usr/include/opencv
(base) 1080Ti% pkg-config opencv --libs
/usr/lib/x86_64-linux-gnu/libopencv_calib3d.so -lopencv_calib3d /usr/lib/x86_64-linux-gnu/libopencv_contrib.so -lopencv_contrib /usr/lib/x86_64-linux-gnu/libopencv_core.so -lopencv_core /usr/lib/x86_64-linux-gnu/libopencv_features2d.so -lopencv_features2d /usr/lib/x86_64-linux-gnu/libopencv_flann.so -lopencv_flann /usr/lib/x86_64-linux-gnu/libopencv_gpu.so -lopencv_gpu /usr/lib/x86_64-linux-gnu/libopencv_highgui.so -lopencv_highgui /usr/lib/x86_64-linux-gnu/libopencv_imgproc.so -lopencv_imgproc /usr/lib/x86_64-linux-gnu/libopencv_legacy.so -lopencv_legacy /usr/lib/x86_64-linux-gnu/libopencv_ml.so -lopencv_ml /usr/lib/x86_64-linux-gnu/libopencv_objdetect.so -lopencv_objdetect /usr/lib/x86_64-linux-gnu/libopencv_ocl.so -lopencv_ocl /usr/lib/x86_64-linux-gnu/libopencv_photo.so -lopencv_photo /usr/lib/x86_64-linux-gnu/libopencv_stitching.so -lopencv_stitching /usr/lib/x86_64-linux-gnu/libopencv_superres.so -lopencv_superres /usr/lib/x86_64-linux-gnu/libopencv_ts.so -lopencv_ts /usr/lib/x86_64-linux-gnu/libopencv_video.so -lopencv_video /usr/lib/x86_64-linux-gnu/libopencv_videostab.so -lopencv_videostab
pkg-config到底用的是哪个.pc文件?
比如我系统装了好多个版本的opencv,那么pkg-config到底找到并使用的是哪个opencv.pc呢?
pkg-config opencv --debug > log.txt 2>&1
ag 'opencv.pc' log.txt
(不知为何,我用不了管道操作符,用了xargs也不行)
输出:
⚡ ag 'opencv.pc' log.txt
167:File 'opencv.pc' appears to be a .pc file
168:Will find package 'opencv' in file '/usr/lib/x86_64-linux-gnu/pkgconfig/opencv.pc'
521:Reading 'opencv' from file '/usr/lib/x86_64-linux-gnu/pkgconfig/opencv.pc'
522:Parsing package file '/usr/lib/x86_64-linux-gnu/pkgconfig/opencv.pc'
于是打开/usr/lib/x86_64-linux-gnu/pkgconfig/opencv.pc
文件,可以发现是2.4.9.1版本。
给编译器和链接器传入pkg-config的结果
例如单个文件使用opencv:
g++ -o a.out hello_opencv.cpp `pkg-config --libs opencv`
也可以在Makefile中进行设定。例如著名的Darknet,它早期某个版本的makefile内容如下:
CC=gcc
CFLAGS=-Wall `pkg-config --cflags opencv` -O3 -flto -ffast-math
CFLAGS=-Wall `pkg-config --cflags opencv` -O0 -g
LDFLAGS=`pkg-config --libs opencv` -lm
VPATH=./src/
OBJ=network.o image.o tests.o convolutional_layer.o connected_layer.o maxpool_layer.o activations.o
all: cnn
cnn: $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
.PHONY: clean
clean:
rm -rf $(OBJ) cnn
CMake中使用pkg-config
1. 安装pkg-config
安装pkg-config
,并确保在CMake中能找到它的可执行文件。
Ubuntu: sudo apt install pkg-config
Windows: 下载 pkg-config-lite 。注意Anaconda/Miniconda中也带了pkg-config,但实测cmake中无法使用。为避免冲突,这里不把pkg-config-lite版的可执行文件路径放PATH
系统环境变量,而是在cmake中单独配置:
set(PKG_CONFIG_EXECUTABLE "D:/soft/pkg-config/bin/pkg-config.exe")
2. CMakeLists.txt中使用pkg-config
包括3个具体的步骤:
-
确定.pc文件绝对路径(例如只提供.pc方式配置),记作$prefix/xxx.pc
-
在CMakeLists.txt中,分别把$prefix和xxx填写入座:
set(ENV{PKG_CONFIG_PATH} $prefix)
find_package(PkgConfig)
pkg_search_module(MyDepName REQUIRED xxx)
其中MyDepName
是自行起的名字,可以和原始包的名字不一样
- 后续使用
${MyDepName_LIBRARIES}
和${MyDepName_INCLUDE_DIRS}
即可
3. 举例
Ubuntu 16.04下用apt安装openblas并在CMake中用pkg-config方式配置:
#安装
sudo apt install libopenblas-dev
#查找.pc文件。
#若查不到,执行sudo apt install --reinstall libopenblas-dev再执行
dpkg -L libopenblas-dev | grep '.pc'
#找到,在/usr/lib/pkgconfig/blas-openblas.pc
编写CMakeLists.txt,把/usr/lib/pkgconfig/blas-openblas.pc
拆分为/usr/lib/pkgconfig
和blas-openblas
:
cmake_minimum_required(VERSION 3.15)
project(cmake_pkg_config_example)
set(ENV{PKG_CONFIG_PATH} /usr/lib/pkgconfig)
find_package(PkgConfig)
pkg_search_module(OBS REQUIRED blas-openblas)
message(STATUS "=== OBS_LIBRARIES: ${OBS_LIBRARIES}")
message(STATUS "=== OBS_INCLUDE_DIRS: ${OBS_INCLUDE_DIRS}")
其中OBS
是我随便起的前缀,你也可以换成别的。后续使用这个库的时候,使用OBS_LIBRARIES
和OBS_INCLUDE_DIRS
即可。
Windows 10下用CMake配置Pangolin安装中配置的zlib
Pangolin怎么配置这里就不贴了,正常使用cmake调用Visual Studio编译的流程即可。它会自动源码编译安装zlib。这里假设另一个项目要用到这个版本的zlib,则CMakeLists.txt写法如下:
cmake_minimum_required(VERSION 3.15)
project(cmake_pkg_config_example)
#指定pkg-config.exe绝对路径
set(PKG_CONFIG_EXECUTABLE "D:/soft/pkg-config/bin/pkg-config.exe")
#指定zlib.pc所在目录
set(ENV{PKG_CONFIG_PATH} "D:/lib/pangolin/share/pkgconfig")
find_package(PkgConfig)
message(STATUS "--- PKG_CONFIG_FOUND: ${PKG_CONFIG_FOUND}")
message(STATUS "--- PKG_CONFIG_VERSION_STRING: ${PKG_CONFIG_VERSION_STRING}")
pkg_search_module(ZLIB REQUIRED zlib)
message(STATUS "=== ZLIB_LIBRARIES: ${ZLIB_LIBRARIES}")
message(STATUS "=== ZLIB_INCLUDE_DIRS: ${ZLIB_INCLUDE_DIRS}")
参考
FindPkgConfig----CMake的pkg-config模块
网友评论