CMake Error at CMakeLists.txt (find_package)幕后真凶

news2024/11/14 9:14:04

在这里插入图片描述

竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生~
个人主页: rainInSunny  |  个人专栏: C++那些事儿、 Learn OpenGL In Qt

文章目录

  • 写在前面
  • find_package报错解决
    • Module模式
    • Config模式
  • find_package()用法
    • Module模式
    • Config模式

写在前面

  本文从CMake中find_package()报错入手,首先给出了如何解决这类报错,然后深入探讨了find_package()的用法,揭示了find_package()背后帮我们做的事情,阐明了find_package()Module模式和Config模式的区别,同时针对在这两种模式下如何为自己写的三方库适配find_package()给出了详细说明。

find_package报错解决

  find_package报错描述的是依赖的三库文件没有找到,要解决这个问题也很简单,告诉find_package()要找的三方库在哪就行。先分析出错的原因:

  1. 可能是没有安装find_package寻找的三方库。
  2. 安装了三方库,但是是没设置路径或者路径设置错误。

  基本就是上面两种原因,那破案就简单了。针对第一种情况没什么好说的,先安装三方库再说。针对第二种情况,由于find_package支持Module模式和Config模式,下面举例分析。

Module模式

  以FontConfig为例,编译安装好后目录结构如下。一般在share/cmake目录能否找到提供find_package()支持的构建配置文件,Find<package>.cmake,这里名称为FindFontConfig.cmake。如果报错找的三方库安装目录有这样名称的文件,那么只需要将这个文件的路径,例子中也就是FindFontConfig.cmake路径添加到CMAKE_MODULE_PATH中,就像这样set(CMAKE_MODULE_PATH path/to/fontconfig;${CMAKE_MODULE_PATH}")

在这里插入图片描述
在这里插入图片描述

Config模式

  以Qt5为例,官方的安装目录结构如下。一般在share/cmake目录能否找到提供find_package()支持的构建配置文件,<package>Config.cmake,这里名称为Qt5Config.cmake,注意也可能通过-连接,即<package>-config.cmake。如果找到三方库中有这里文件,那么只需要将该路径添设置到package_DIR变量中,就像这样set(Qt5_DIR path/to/Qt5)

在这里插入图片描述
在这里插入图片描述

  核心思路就是通过三方库提供的配置文件名称来确定是Module模式还是Config模式,然后添加到CMake对应的预设变量中,帮助find_package找到三方库的构建配置即可。如果Module模式和Config模式同时存在,CMake会优先使用Module模式。

find_package()用法

  上面提到了find_package()有Module和Config两种模式,虽然问题解决了,但是如果自己开发的三方库需要给使用者提供这样便捷的能力,就需要更加理解find_package(),下面就仔细聊聊find_package(),其实本质目的就是帮助工程找到依赖三方库的头文件、动态库/静态库文件,因为在编译链接过程中需要这些文件。

Module模式

  假设你写出了一个震惊世界的消息库ZeroMQ,显然你希望大家能够很容易的在CMake中使用,就像这样find_package(ZeroMQ REQUIRED),为此你必须提供一个名称为Find<package>.cmake的文件来支持find_package(),这里文件的名称为FindZeroMQ.cmake。在其它三方库安装目录看到类似Find<package>.cmake,可以知道这个三方库支持find_package()的Module模式。接下来让我们一步步准备好FindZeroMQ.cmake。

  1. 首先FindZeroMQ.cmake中需要检查ZeroMQ_ROOT变量是否设置。此变量可用于ZeroMQ库的检测,并引导到自定义安装目录。用户可能设置了ZeroMQ_ROOT作为环境变量,类似<package>_ROOT一般用于指向package的安装路径,该路径下包含了三方库的头文件、库文件等。
if(NOT ZeroMQ_ROOT)
    set(ZeroMQ_ROOT "$ENV{ZeroMQ_ROOT}") //如果用户没有定义ZeroMQ_ROOT,则设置为环境变量中的ZeroMQ_ROOT
endif()
  1. 一般来说,当依赖一个三方库时最好显示指定三方库安装路径。如果ZeroMQ_ROOT有定义,则赋值给_ZeroMQ_ROOT。如果上面既没有定义ZeroMQ_ROOT,又没有在环境变量中设置ZeroMQ_ROOT的值,则尝试通过find_path去找三方库的典型头文件,并指定这个路径为_ZeroMQ_ROOT的值。_ZeroMQ_ROOT是最后实际查找ZeroMQ库的路径。通常会将三方库的路径添加到在${CMAKE_MODULE_PATH}中
if(NOT ZeroMQ_ROOT)
    find_path(_ZeroMQ_ROOT NAMES include/zmq.h)
else()
    set(_ZeroMQ_ROOT "${ZeroMQ_ROOT}")
endif()

find_path(ZeroMQ_INCLUDE_DIRS NAMES zmq.h HINTS ${_ZeroMQ_ROOT}/include) //ZeroMQ_INCLUDE_DIRS设置为头文件路径
  1. 如果成功找到头文件,则将ZeroMQ_INCLUDE_DIRS设置为其位置。然后继续通过使用字符串操作和正则表达式,寻找相应版本的ZeroMQ库,如果没有多版本区分的需求,不需要这个步骤,后续说明将省略版本号区分。
set(_ZeroMQ_H ${ZeroMQ_INCLUDE_DIRS}/zmq.h)

function(_zmqver_EXTRACT _ZeroMQ_VER_COMPONENT _ZeroMQ_VER_OUTPUT)
set(CMAKE_MATCH_1 "0")
set(_ZeroMQ_expr "^[ \\t]*#define[ \\t]+${_ZeroMQ_VER_COMPONENT}[ \\t]+([0-9]+)$")
file(STRINGS "${_ZeroMQ_H}" _ZeroMQ_ver REGEX "${_ZeroMQ_expr}")
string(REGEX MATCH "${_ZeroMQ_expr}" ZeroMQ_ver "${_ZeroMQ_ver}")
set(${_ZeroMQ_VER_OUTPUT} "${CMAKE_MATCH_1}" PARENT_SCOPE)
endfunction()

_zmqver_EXTRACT("ZMQ_VERSION_MAJOR" ZeroMQ_VERSION_MAJOR)
_zmqver_EXTRACT("ZMQ_VERSION_MINOR" ZeroMQ_VERSION_MINOR)
_zmqver_EXTRACT("ZMQ_VERSION_PATCH" ZeroMQ_VERSION_PATCH)
  1. 接着使用find_library命令搜索ZeroMQ库。因为库的命名有所不同,这里我们需要区分Unix的平台和Windows平台。
if(NOT ${CMAKE_C_PLATFORM_ID} STREQUAL "Windows")
    find_library(ZeroMQ_LIBRARIES
        NAMES
            libzmq
        HINTS
            ${_ZeroMQ_ROOT}/lib
            ${_ZeroMQ_ROOT}/lib/x86_64-linux-gnu
        )
else()
    find_library(ZeroMQ_LIBRARIES
        NAMES
            zmq
        HINTS
            ${_ZeroMQ_ROOT}/lib
        )
endif()

//如果需要区分release和debug
if(NOT ${CMAKE_C_PLATFORM_ID} STREQUAL "Windows")
    find_library(ZeroMQ_LIBRARY_RELEASE
        NAMES
            libzmq
        HINTS
            ${_ZeroMQ_ROOT}/lib
            ${_ZeroMQ_ROOT}/lib/x86_64-linux-gnu
        )
    find_library(ZeroMQ_LIBRARY_DEBUG
        NAMES
            libzmqd
        HINTS
            ${_ZeroMQ_ROOT}/lib
            ${_ZeroMQ_ROOT}/lib/x86_64-linux-gnu
        )
else()
    find_library(ZeroMQ_LIBRARY_RELEASE
        NAMES
            zmq
        HINTS
            ${_ZeroMQ_ROOT}/lib
        )
    find_library(ZeroMQ_LIBRARY_DEBUG
        NAMES
            zmqd
        HINTS
            ${_ZeroMQ_ROOT}/lib
        )
endif()
  1. 最后包含了标准FindPackageHandleStandardArgs.cmake,并调用相应的CMake命令。如果找到所有需要的变量,这里是ZeroMQ_INCLUDE_DIRSZeroMQ_LIBRARIES,则将ZeroMQ_FOUND变量设置为TRUE。完成这些步骤后,用户就能通过find_package(ZeroMQ REQUIRED)来使用ZeroMQ库,通过ZeroMQ_FOUND判断find_package是否成功,如果成功${ZeroMQ_INCLUDE_DIRS}${ZeroMQ_LIBRARIES}变量中就保存了ZeroMQ库的头文件路径和库文件路径供用户在target_include_directories和target_link_libraries中使用。
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ZeroMQ
    FOUND_VAR
        ZeroMQ_FOUND
    REQUIRED_VARS
        ZeroMQ_INCLUDE_DIRS
        ZeroMQ_LIBRARIES
    )

//如果需要区分release和debug
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ZeroMQ
    FOUND_VAR
        ZeroMQ_FOUND
    REQUIRED_VARS
        ZeroMQ_INCLUDE_DIRS
        ZeroMQ_LIBRARY_RELEASE
        ZeroMQ_LIBRARY_DEBUG
    )

Config模式

  Config模式相对于Module模式更为复杂些,需要配合目标安装过程,如果使用的三方库是二进制文件并不是源码编译的,建议通过Module模式为三方库提供find_package支持。这里假设我们通过自己编写的源码编译出了message这样一个target,我们需要通过Config模式为message提供find_package支持。接下来一步步实现这个目标

  1. 在安装目标过程中添加EXPORT关键字,这样CMake将为目标生成一个导出的目标文件。这里的${INSTALL_LIBDIR}和${INSTALL_BINDIR}分别为库文件和可执行文件安装目录。
install(
    TARGETS
        message
    EXPORT
        messageTargets
    ARCHIVE
        DESTINATION ${INSTALL_LIBDIR}
        COMPONENT lib
    RUNTIME
        DESTINATION ${INSTALL_BINDIR}
        COMPONENT bin
    LIBRARY
        DESTINATION ${INSTALL_LIBDIR}
        COMPONENT lib
    PUBLIC_HEADER
        DESTINATION ${INSTALL_INCLUDEDIR}/message
        COMPONENT dev
    )
  1. 自动生成的导出目标文件称为messageTargets.cmake,需要显式地指定它的安装规则。这里的${INSTALL_CMAKEDIR}是CMake配置文件路径,一般设置为库安装路径下的share目录。
install(
    EXPORT
        messageTargets
    NAMESPACE
        "message::"
    DESTINATION
        ${INSTALL_CMAKEDIR}
    COMPONENT
        dev
    )
  1. 需要生成正确的CMake配置文件,这些将确保下游项目能够找到消息库导出的目标。为此需要包括CMakePackageConfigHelpers.cmake标准模块,然后用模块提供的函数生成版本配置文件messageConfigVersion.cmake和配置文件messageConfig.cmake。注意这里的messageConfig.cmake是CMake通过模版文件messageConfig.cmake.in生成的,这个模版文件需要在message库的源码中提供,这里目录为${PROJECT_SOURCE_DIR}/cmake/messageConfig.cmake.in,模版的内容很简单,如下。
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
    ${CMAKE_CURRENT_BINARY_DIR}/messageConfigVersion.cmake
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY SameMajorVersion
    )

configure_package_config_file(
    ${PROJECT_SOURCE_DIR}/cmake/messageConfig.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/messageConfig.cmake
    INSTALL_DESTINATION ${INSTALL_CMAKEDIR}
    )
// messageConfig.cmake.in
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/messageTargets.cmake")
  1. 最后安装配置文件。完成后用户在CMake中设定<package>_DIR变量指向这两个文件的安装路径,这里是message_DIR,就能通过find_package(message REQUIRED)引用message库了。
install(
    FILES
        ${CMAKE_CURRENT_BINARY_DIR}/messageConfig.cmake
        ${CMAKE_CURRENT_BINARY_DIR}/messageConfigVersion.cmake
    DESTINATION
        ${INSTALL_CMAKEDIR}
    )

欢迎留言讨论,创作不易,感谢点赞、关注和收藏~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2083908.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI无损放大工具 AI lossless zoomer 3.1.0.0

这款工具是作者基于腾讯开源免费的AI算法而开发的&#xff0c;腾讯开源算法地址&#xff1a;GitHub.com/xinntao/Real-ESRGAN 打开软件的主页面是黑色为主调的界面。把需要无损放大的图片直接拖入到软件中&#xff0c;再点【开始任务】&#xff0c;软件就会自动处理图片&#…

【论文阅读|cryoET】本周粗读汇总

论文1&#xff1a;CryoDRGN-ET&#xff1a;深度重建生成网络以可视化细胞内动态生物分子 Abstract 虽然冷冻电子断层扫描可以以分子分辨率揭示结构&#xff0c;但图像处理算法仍然是解决原位生物分子结构异质性的瓶颈。本文介绍CryoDRGN-ET用于cryoET断层图的异质重建。CryoD…

大厂产品经理面试:阿里、字节、百度、腾讯、拼多多等全国顶级大厂面试题一网打尽!

在互联网行业蓬勃发展的今天&#xff0c;产品经理作为连接技术、设计和市场的核心角色&#xff0c;其重要性日益凸显。想要进入国内顶尖的互联网大厂&#xff0c;如阿里巴巴、字节跳动、百度、腾讯、拼多多等&#xff0c;产品经理岗位的面试无疑是一场硬仗。本文将为你揭秘这些…

stm32定时器同步(二)

本文介绍使用一个定时器使能另一个定时器。 旧瓶装新酒&#xff0c;就是门控模式加一个TRGO&#xff0c;如果看到这里你还没有明白&#xff0c;那就去看看我前面的文章 代码部分也是旧瓶装新酒 void Timer3_Config(void) {TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM…

皮皮虾保存视频怎么去除水印?教你3种技巧轻松去除水印

在如今这个信息爆炸的时代&#xff0c;我们经常会在网络上看到一些有趣的皮皮虾视频&#xff0c;想要保存下来&#xff0c;却发现视频带有水印。水印虽然可以保护视频的版权&#xff0c;但有时也会影响我们的观看体验。如何去除视频中的水印呢&#xff1f;本文将为您介绍三种实…

日期转换异常

报错 org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: Error querying database. Cause: java.lang.NumberFormatException: For input string: "2024-07-01" Cause: java.lang.NumberFormatE…

Windows TCP/IP IPv6 DDos远程蓝屏复现及修复(CVE-2024-38063)

【前言】   最近&#xff0c;windows爆出重量级漏洞CVE-2024-38063)&#xff0c;攻击者通过不断发送ipv6数据包&#xff0c;可实现远程DDOS导致目标windows直接蓝屏&#xff0c;或RCE。   下面就直接开展下&#xff0c;如何复现的&#xff0c;以及怎么修复。 一、漏洞分析 …

如何从官方 Yum 仓库安装 MySQL 5.6

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 介绍 2013年10月&#xff0c;MySQL开发团队正式推出了对yum存储库的支持。这意味着您现在可以确保直接从源安装最新和最好的MySQL版本&a…

论文阅读:SimVP: Simpler yet Better Video Prediction

论文地址&#xff1a;arxiv 摘要 作者认为&#xff0c;现有的CNN&#xff0c;RNN&#xff0c;Transformer 之类的视频预测领域的模型都过于复杂了&#xff0c;作者想要找到一个简单的方式&#xff0c;同时可以达到与之相当的效果。 作者提出了 SimVP&#xff0c;这是一个简单…

Linux工具使用

Linux编辑器-vim使用 1.vim的基本概念 在vim中&#xff0c;主要的三种模式分别是命令模式&#xff0c;插入模式和底行模式。 正常/普通/命令模式(Normal mode) 控制屏幕光标的移动&#xff0c;字符、字或行的删除&#xff0c;移动复制某区段及进入Insert mode下&#xff0c;…

【软件工程】软件生命周期(生存周期)

考点3 软件生命周期&#xff08;生存周期&#xff09; 一、定义 软件产品或软件系统从设计、投入使用到被淘汰的全过程。 二、3周期 题目 一、判断题 二、选择题

冲击大厂算法面试=>链表专题【链表反转】

目录标题 最基础的反转链表上代码题解呀实在不会的时候记住 最基础的反转链表 上代码 class Solution {public ListNode reverseList(ListNode head) {ListNode pre null;ListNode cur head;while(cur ! null){ListNode nextNode cur.next;cur.next pre;pre cur;cur nex…

[HZNUCTF 2023 preliminary]flask

[HZNUCTF 2023 preliminary]flask 点开之后页面如图所示&#xff1a; 猜测是SSTI模板注入&#xff0c;先输入{7*7},发现模板是倒序输入的&#xff1a; 然后我们输入}}‘7’*7{{返回777777&#xff0c;这是jinja2模板&#xff1a; 我们需要让用户输入一个字符串&#xff0c;对其…

计算机视觉基础. 1 学习导论

1 .引言 学习的目的是从过去的经验中吸取教训&#xff0c;以解决未来的问题。通常&#xff0c;这涉及搜索解决问题过去实例的算法。然后&#xff0c;该算法可以应用于该问题的未来实例。 过去和未来不一定指日历日期&#xff1b;相反&#xff0c;它们指的是学习者之前看到的内…

Windows下devecostudio-windows-3.1.0.501的下载与安装教程

Windows下devecostudio-windows-3.1.0.501的下载与安装教程 1.华为开发者联盟 2.Deveco Studio下载 一、注册华为账号 进入华为开发者联盟&#xff0c;注册华为账号&#xff08;如果有手机华为账号&#xff0c;这里通用&#xff09; 二、下载Deveco Studio3.1.0.501 链接&am…

C++入门基础知识39——【关于C++ 运算符——位运算符】

成长路上不孤单&#x1f60a;【14后&#xff0c;C爱好者&#xff0c;持续分享所学&#xff0c;如有需要欢迎收藏转发&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#xff01;&#xff01;&#xff01;&#xff01;&#xff…

GitHub的AI未来:CEO Thomas Dohmke深入解析

在近期的一次深入访谈中,GitHub的首席执行官Thomas Dohmke分享了他对AI在编程领域未来的看法,GitHub Copilot的成就,以及在微软生态下保持独立性的重要性。以下是对这次讨论的精华总结。 GitHub在AI领域的定位 Thomas Dohmke认为,AI技术需要竞争来推动发展。他指出,GitH…

类与ES6类之间的继承

前言 ● 下面是之前学习ES6 classes的代码 class PersonCl {constructor(fullName, birthYear) {this.fullName fullName;this.birthYear birthYear;}calcAge() {console.log(2037 - this.birthYear);}greet() {console.log(你好${this.fullName});}get age() {return 2037…

JVM 锁的种类

优质博文&#xff1a;IT-BLOG-CN 一、JVM 锁【偏向锁|轻量级锁|重量级锁】 对象头[每个对象都具有对象头] Mark&#xff1a;对象头的标记&#xff08;32位&#xff09;&#xff0c;描述对象的hash、锁信息、垃圾回收标记、年龄&#xff1b;内容包括&#xff1a;①、指向锁记录…

OpenShift 与 Rancher

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…