cmake中的find_package()

1. 问题来源

问题的来源是当在编译一个库或者可执行文件的时候,可能需要使用其他库或者头文件。如果是自己写的小demo,那手动把这些路径写到gcc的参数项里就可以了:

g++ test.cpp -o test -I./include -L./lib -lmylib

但正式的项目这样肯定是不行的,一是库太多,要挨个指定太麻烦。二是一些三方库,你根本不知道它的安装路径,也不知道库的名字,所以就有了库管理的机制。

2. pkg-config 

在最开始(现在也在用),unix系统采用pkg-config的方式来管理库。其实现方式是在安装一个三方库的时候,会在对应lib目录下的pkgconfig里产生一个xxx.pc文件。这个文件里保存了这个库相关的编译选项,开发者调用pkg-config,然后pkg-config通过读这个文件,就可以知道怎么使用这个库了。

[xxx@VM_0_14_centos ~]$ which openssl #定位库的大致路径,.pc文件一般在跟bin同级目录下的lib或lib64里
/usr/bin/openssl
[xxx@VM_0_14_centos ~]$ ll /usr/lib64/pkgconfig/ #下面这些都是已经安装好的库
com_err.pc            krb5.pc               libpcreposix.pc       ncurses++.pc          tic.pc                xcb.pc                xcb-xfixes.pc
expat.pc              libcrypto.pc          libpng15.pc           ncurses++w.pc         tinfo.pc              xcb-present.pc        xcb-xinerama.pc
fontconfig.pc         libcurl.pc            libpng.pc             ncursesw.pc           uuid.pc               xcb-randr.pc          xcb-xinput.pc
fontutil.pc           libevent_openssl.pc   libselinux.pc         openssl.pc            x11.pc                xcb-record.pc         xcb-xkb.pc
form.pc               libevent.pc           libsepol.pc           panel.pc              x11-xcb.pc            xcb-render.pc         xcb-xselinux.pc
formw.pc              libevent_pthreads.pc  libssl.pc             panelw.pc             xau.pc                xcb-res.pc            xcb-xtest.pc
freetype2.pc          libffi.pc             libverto.pc           pthread-stubs.pc      xcb-composite.pc      xcb-screensaver.pc    xcb-xvmc.pc
gssrpc.pc             liblzma.pc            menu.pc               python-2.7.pc         xcb-damage.pc         xcb-shape.pc          xcb-xv.pc
kadm-client.pc        libpcre16.pc          menuw.pc              python2.pc            xcb-dpms.pc           xcb-shm.pc            xft.pc
kadm-server.pc        libpcre32.pc          mit-krb5-gssapi.pc    python.pc             xcb-dri2.pc           xcb-sync.pc           xrender.pc
kdb.pc                libpcrecpp.pc         mit-krb5.pc           sqlite3.pc            xcb-dri3.pc           xcb-xevie.pc          zlib.pc
krb5-gssapi.pc        libpcre.pc            ncurses.pc            systemd.pc            xcb-glx.pc            xcb-xf86dri.pc
[xxx@VM_0_14_centos ~]$ cat /usr/lib64/pkgconfig/openssl.pc #查看内容
prefix=/usr
exec_prefix=${prefix}
libdir=${exec_prefix}/lib64
includedir=${prefix}/include

Name: OpenSSL
Description: Secure Sockets Layer and cryptography libraries and tools
Version: 1.0.2k
Requires: libssl libcrypto
[xxx@VM_0_14_centos ~]$
[xxx@VM_0_14_centos ~]$ pkg-config --list-all | grep openssl #用 pkg-config查看支持的库,其实就是读上面那些pc文件出来的
openssl           OpenSSL - Secure Sockets Layer and cryptography libraries and tools
libevent_openssl  libevent_openssl - libevent_openssl adds openssl-based TLS support to libevent
[xxx@VM_0_14_centos ~]$
[xxx@VM_0_14_centos ~]$ pkg-config --libs openssl #查看特定库的链接选项
-lssl -lcrypto
[xxx@VM_0_14_centos ~]$ gcc myprogram.c -o myprogram `pkg-config --cflags --libs openssl` #最后这样用就可以了,前提是库路径已经导入PATH了

3. MakeFile

链接选项获得后,当然可以通过命令行设置给gcc,不过这个时候已经有MakeFile了,编译选项一般在MakeFile里指定,然后通过MakeFile将其设置给编译选项。

GTK_CFLAGS := $(shell pkg-config --cflags gtk+-3.0)

这种方式来自动调用pkg-config,但现在看来这显然还是太原始了。 

4. cmake

2000年左右,cmake出现了,相当于在makefile的基础上又封装了一层,更像一门语言了。而find_package,就是对上面那句话的封装。
它的运行机制大概是这样的:
1. cmake在安装的时候,会提供常见库的.cmake文件(注意即使这些库你还没安装),这些文件的路径可以在cmake里输出CMAKE_ROOT查看(在里面的Modules下面)。

[xxx@VM_0_14_centos cmake]$ cmake .
-- The C compiler identification is GNU 9.3.1
......
-- Detecting CXX compile features - done
-- cmake root: /opt/cmake/cmake-3.14.5-Linux-x86_64/share/cmake-3.14
-- Configuring done
-- Generating done
-- Build files have been written to: /home/xxx/code/cmake
[xxx@VM_0_14_centos ~]$ ls /opt/cmake/cmake-3.14.5-Linux-x86_64/share/cmake-3.14/
completions/ editors/     Help/        include/     Licenses/    Modules/     Templates/
[xxx@VM_0_14_centos ~]$ ls /opt/cmake/cmake-3.14.5-Linux-x86_64/share/cmake-3.14/Modules/
AddFileDependencies.cmake                                       CMakeSystemSpecificInformation.cmake  FindOpenGL.cmake
AndroidTestUtilities                                            CMakeSystemSpecificInitialize.cmake   FindOpenMP.cmake
AndroidTestUtilities.cmake                                      CMakeTestASM-ATTCompiler.cmake        FindOpenSceneGraph.cmake
BasicConfigVersion-AnyNewerVersion.cmake.in                     CMakeTestASMCompiler.cmake            FindOpenSSL.cmake
BasicConfigVersion-ExactVersion.cmake.in                        CMakeTestASM_MASMCompiler.cmake       FindOpenThreads.cmake
BasicConfigVersion-SameMajorVersion.cmake.in                    CMakeTestASM_NASMCompiler.cmake       FindosgAnimation.cmake
BasicConfigVersion-SameMinorVersion.cmake.in                    CMakeTestCCompiler.cmake              Findosg.cmake
BundleUtilities.cmake                                           CMakeTestCompilerCommon.cmake         FindosgDB.cmake
CheckCCompilerFlag.cmake                                        CMakeTestCSharpCompiler.cmake         Findosg_functions.cmake
CheckCSourceCompiles.cmake                                      CMakeTestCUDACompiler.cmake           FindosgFX.cmake
CheckCSourceRuns.cmake                                          CMakeTestCXXCompiler.cmake            FindosgGA.cmake
......

2. 用户使用的时候,直接find_package就可以了,cmake会去找对应的.cmake文件,然后执行它。
3. cmake文件的作用是执行pkg-config,然后将对应的值赋值给固定名字的变量,再find_package执行完后就可以用这些变量。

对于一些不常见的库,或者干脆用户自定义的模块。也可以自己写一个.cmake文件,然后使用find_package机制,对于这些文件,可以放到CMAKE_ROOT下,但显然不是最佳实践,因为这是个全局的路径。所以cmake又提供了一个变量CMAKE_MODULE_PATH,可以在cmake中将我们“定制版”的.cmake文件路径设置给它,然后调用find_package就可以了。

最佳实践:

要通过find_package()引用三方库,要确认下面两点:
1. cmake是支持的
就是看有没有对应的.cmake文件,如果没有,要么不用find_package了,要么自己写一个。
对于正常人,这两种方式都比较难受,还有一种偷懒方式是安装库的时候,库的作者会自己给你带一个.cmake让你使用(可以在安装目录下找找)。
2. 确定pkg-config支持,
一般比较正式的库,只要你正确安装了(注意不是命令行里用那个,是带后缀devl的),都会带.pc文件,pkg-config都支持。
TIPS: 如果确认安装好了呢:
which curl
可以看到curl可执行文件的路径,然后
ldd /path/to/curl
可以看到libcurl.so的路径,然后去这个库路径下找对应的pkgconfig文件夹,看里面有没有对应的(类似curl.pc)的文件,如果有说明安装好了。

5. 意外情况(持续更新)

然而,现实是残酷,本人在使用过程中就遇到了很多奇怪问题(主要是cmake在这块儿明的暗的规则很多,学起来很复杂),现在总结一下:

  • cmake文件命找不到

        首先检查路径有没有指定给CMAKE_MODULE_PATH(打印出来看看)

        检查文件命名是否正确,find_package()支持两种模式,两种模式会去找不同的名字(具体规则不展开了)

        还要确认你自己用的是哪种模式,默认是Module模式,可能需要切到Config模式(方法自行百度)

  • 找到了但找错了

        这可能是有多个符合要求的.cmake,而搜索路径的优先级不同,可以通过直接指定路径的方式解决(方法自行百度)

  • 找到了,最后编译报错

        这是最复杂的情况,由于版本不兼容等问题,你的.cmake文件可能是错的,这个时候可以重点观察下安装路径是否符合预期

`find_package` 是 CMake 用于查找和加载外部依赖库的命令。它会在系统搜索指定名称的库,并自动配置编译选项。一般情况下,我们需要在 CMakeLists.txt 文件使用 `find_package` 命令来查找我们需要的库,然后将它们链接到我们的项目。 下面是使用 `find_package` 命令的一般步骤: 1. 在 CMakeLists.txt 文件使用 `find_package` 命令,指定要查找的库的名称和版本。 例如,要查找 Boost 库,可以使用以下命令: ``` find_package(Boost 1.46 REQUIRED COMPONENTS system thread) ``` 2. 如果库没有被找到,可以通过设置 `CMAKE_PREFIX_PATH` 环境变量或者 `CMAKE_MODULE_PATH` 变量来指定库的安装路径或者 CMake 模块所在路径。 3. 如果找到了库,`find_package` 命令会自动定义一些变量,用于指定库的路径、头文件路径和库文件路径等信息。 例如,对于 Boost 库,`find_package` 命令会定义以下变量: ``` Boost_FOUND Boost_INCLUDE_DIRS Boost_LIBRARY_DIRS Boost_LIBRARIES ``` 4. 在项目使用这些变量,例如: ``` include_directories(${Boost_INCLUDE_DIRS}) link_directories(${Boost_LIBRARY_DIRS}) target_link_libraries(my_target ${Boost_LIBRARIES}) ``` 这些变量包含了库的路径信息,可以用来编译和链接项目。 需要注意的是,`find_package` 命令只能用于已经被 CMake 支持的库。对于一些非常规的库,可能需要手动配置编译选项来使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值