在cmake中使用pkg-config

package-153360_960_720.png

什么是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/pkgconfigblas-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_LIBRARIESOBS_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模块

CMake的pkg-config模块使用

How to install pkg config in windows?

pkg-config-lite SourceForge

你可能感兴趣的:(在cmake中使用pkg-config)