Cmake使用

[待更新中]

find_package用法

必读:

注意,要想在自己的项目中使用find_package命令查找包的前提是:

包的开发者用CMake配置好了这个包,并提供了<PackageName>Config.cmakeFind<PackageName>.cmake的配置文件。

有2种搜包模式 ModuleConfig模式

默认采用 Module模式,找不到的时候转为 Config模式

Module模式

语法

find_package(<packageName> [version] [EXACT] [QUIET] [MODULE]
             [REQUIRED] [[COMPONENTS] [components...]]
             [OPTIONAL_COMPONENTS components...]
             [NO_POLICY_SCOPE])

Module模式下会去查找名为Find<PackageName>.cmake的配置文件,查找路径为:

# Module模式只有两个查找路径:CMAKE_MODULE_PATH和CMake安装路径下的Modules目录,即
CMAKE_MODULE_PATH  # 默认为空,可以利用set命令赋值。
CMAKE_ROOT # 即cmake安装路径的根目录

Config模式

语法(Config模式的参数更多,也更复杂,但实际在使用过程中我们并不会用到所有参数,大部分参数都是可选的)

find_package(<package> [version] [EXACT] [QUIET]
             [REQUIRED] [[COMPONENTS] [components...]]
             [CONFIG|NO_MODULE]
             [NO_POLICY_SCOPE]
             [NAMES name1 [name2 ...]]
             [CONFIGS config1 [config2 ...]]
             [HINTS path1 [path2 ... ]]
             [PATHS path1 [path2 ... ]]
             [PATH_SUFFIXES suffix1 [suffix2 ...]]
             [NO_DEFAULT_PATH]
             [NO_CMAKE_ENVIRONMENT_PATH]
             [NO_CMAKE_PATH]
             [NO_SYSTEM_ENVIRONMENT_PATH]
             [NO_CMAKE_PACKAGE_REGISTRY]
             [NO_CMAKE_BUILDS_PATH] # Deprecated; does nothing.
             [NO_CMAKE_SYSTEM_PATH]
             [NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
             [CMAKE_FIND_ROOT_PATH_BOTH |
              ONLY_CMAKE_FIND_ROOT_PATH |
              NO_CMAKE_FIND_ROOT_PATH])

CMake默认采取Module模式,如果Module模式未找到库,才会采取Config模式(也可显示使用CONFIG参数指定采用Config模式)。

Config模式会去查找名为<PackageName>Config.cmake<package-name>-config.cmake的模块文件,【查找路径】按优先级从高到低如下:

  1. 首先查找名为<PackageName>_DIR的CMake变量或环境变量路径,默认为空。
    这个路径是【非根目录】路径,要求该路径下存在<PackageName>Config.cmake<package-name>-config.cmake文件才可以找到。

  2. 其次查找名为CMAKE_PREFIX_PATHCMAKE_FRAMEWORK_PATHCMAKE_APPBUNDLE_PATH的CMake变量或环境变量路径

    这些路径为【根目录】,默认都为空。
    注意如果你电脑中安装了ROS并配置好之后,你在终端执行echo $CMAKE_PREFIX_PATH会发现ROS会将CMAKE_PREFIX_PATH这个变量设置为ROS中的库的路径,意思是会首先查找ROS安装的库,如果恰好你在ROS中安装了OpenCV库,就会发现首先找到的是ROS中的OpenCV,而不是你自己安装到系统中的OpenCV。

  3. 最后查找系统PATH环境变量路径

    可以用 echo %path% 查看系统环境变量设置的路径

查找到库后,通常XXXConfig.cmake文件内部会给 XXX_INCLUDE_DIRSXXX_LIBRARIES两个变量赋值,但这也不是一定的

上面提到的根目录通常是

<prefix>/(lib/<arch>|lib|share)/cmake/<name>*/
<prefix>/(lib/<arch>|lib|share)/<name>*/ 
<prefix>/(lib/<arch>|lib|share)/<name>*/(cmake|CMake)/
# 注:*为通配符

建议

  1. 建议在Config模式下的时候,使用set设置 <PackageName>_DIR 变量来指定查找的路径,这样就不怕找不到了

  2. 如果你有多个包的配置文件需要查找,可以将这些配置文件都统一放在一个命名为cmake的文件夹下,然后设置变量CMAKE_PREFIX_PATH变量指向这个cmake文件夹路径,需要注意根据上述的匹配规则,此时每个包的配置文件需要单独放置在命名为包名的文件夹下(文件夹名不区分大小写),否则会提示找不到。

相对路径问题

问题描述

在代码中如果用【相对路径】打开一个文件a.txt,编译时可能没问题,但执行的时候可能就会出问题了,因为源代码与a.txt的相对位置关系,和可执行文件与a.txt之间的位置关系不一样。

比如main.cpp和a.txt都在src/目录下,而 main.exe 在build/目录下,如下所示:

build/
  - main.exe
src/
  - main.cpp
  - a.txt

因此要想让程序正常运行,需要在代码中把打开a.txt文件的路径由原来的"a.txt"修改为"../src/a.txt",即可。

然而,这样看上去很不舒服,因此不建议这样做。

绝对路径虽然没有这种问题,但绝对路径又臭又长,在代码中也不美观,而且如果把代码分享给别人,别人也要修改。

解决办法

本质上还是使用绝对路径,但这个绝对路径在cmake编译时生成。我们知道,cmake可以获取到项目的根目录所在路径PROJECT_SOURCE_DIR,因此如果可以在CPP代码中获取到PROJECT_SOURCE_DIR的值就好了。

cmake提供了一个configure_file函数:

configure_file(<input> <output>
               [COPYONLY] [ESCAPE_QUOTES] [@ONLY]
               [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])

<input>表示输入文件,<output>表示输出文件。

输入文件内可以使用@VAR@${VAR}的方式获取到cmake变量值,在执行configure_file函数后,cmake会把@VAR@${VAR}替换为具体值后再把输入文件所有东西拷贝到输出文件(不存在的话会自动创建)

比如,设置输入文件为 ${PROJECT_SOURCE_DIR}/src/configPath.h.in,输出文件为 ${PROJECT_SOURCE_DIR}/src/configPath.h

并在 configPath.h.in写入下面的代码

#define PROJECT_ROOT_PATH ${PROJECT_SOURCE_DIR}

输出文件会自己创建,不用管。

然后在 main.cpp 中包含输出的头文件 #include "configPath.h"

接下来就能在 main.cpp 中使用变量 PROJECT_ROOT_PATH

cmake后的src文件结构如下

src/
  - main.cpp
  - a.txt
  - configPath.h.in
  - configPath.h

小技巧:cpp中,相邻两个字符串常量(可以是宏定义的字符串)会自动合并,如:

"abc" "de"; //会合并为 "abcde"
posted @ 2023-04-23 22:40  aJream  阅读(85)  评论(0编辑  收藏  举报