【cmake】利用ExternalProject_Add解决第三方库target命名冲突问题

news2024/11/15 9:16:32

参考
cmake菜谱第八章第一节

我们经常会遇到这种情况:

project A 是最外层项目
project B 是A使用的外部库
project C 是A和B使用的外部库

.
├── extern
│   ├── B
│   │   ├── extern
│   │   │   └── C
│   │   └── src
│   └── C
└── src

这就导致project C被引用了两遍,从而出现target命名冲突的问题。由于target均是全局的,因此一旦冲突就很难解决。

解决方案有以下几种:

  1. 使用if(NOT target C)来保证target C只编译了一遍。缺点在于A中和B中C的版本和配置也许不同,但不得不使用同一个。
  2. 重命名A和B中的targetC。比如改名为A_C和B_C。缺点是你需要魔改太多东西。
  3. 使用超级构建,例如ExternalProject_Add。

我们这里重点讨论第三种

ExternalProject_add解决target冲突

问题描述

首先我们要来看一下没有解决前的报错是什么样的:

报错如下:

[cmake] CMake Error at E:/codes/vcpkg/scripts/buildsystems/vcpkg.cmake:582 (_add_executable):
[cmake]   _add_executable cannot create target "Cexe" because another target with the
[cmake]   same name already exists.  The existing target is an executable created in
[cmake]   source directory "E:/codes/try/cmake-target-clash/extern/B/extern/C".  See
[cmake]   documentation for policy CMP0002 for more details.
[cmake] Call Stack (most recent call first):
[cmake]   extern/C/CMakeLists.txt:9 (add_executable)
[cmake] 
[cmake] 
[cmake] -- Configuring incomplete, errors occurred!
[cmake] See also "E:/codes/try/cmake-target-clash/build/CMakeFiles/CMakeOutput.log".
[cmake] CMake Error at E:/codes/vcpkg/scripts/buildsystems/vcpkg.cmake:623 (_add_library):
[cmake]   _add_library cannot create target "Clib" because another target with the
[cmake]   same name already exists.  The existing target is a static library created
[cmake]   in source directory "E:/codes/try/cmake-target-clash/extern/B/extern/C".
[cmake]   See documentation for policy CMP0002 for more details.
[cmake] Call Stack (most recent call first):
[cmake]   extern/C/CMakeLists.txt:10 (add_library)
[cmake] 
[cmake] 
[proc] The command: "C:\Program Files\CMake\bin\cmake.EXE" --no-warn-unused-cli -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -Se:/codes/try/cmake-target-clash -Be:/codes/try/cmake-target-clash/build -G "Visual Studio 16 2019" -T host=x64 -A x64 exited with code: 1 and signal: null

重点是这句

_add_executable cannot create target “Cexe” because another target with the same name already exists. The existing target is an executable created in source directory “E:/codes/try/cmake-target-clash/extern/B/extern/C”.

显然,这告诉我们已经存在了一个Cexe目标。也就是因为嵌套引入C而导致的target重名冲突。

解决方案: 使用ExternalProject_Add添加外部库

# 使用ExternalProject可以解决target冲突问题
include(ExternalProject)
set_property(DIRECTORY PROPERTY EP_BASE ${CMAKE_BINARY_DIR}/ep_base_I_set)

ExternalProject_Add(External_C_name_I_set
  SOURCE_DIR
    ${CMAKE_CURRENT_LIST_DIR}/extern/C
  CMAKE_ARGS
    -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
    -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
  INSTALL_COMMAND
    ""
  )

ExternalProject_Add(External_B_name_I_set
  SOURCE_DIR
    ${CMAKE_CURRENT_LIST_DIR}/extern/B
  CMAKE_ARGS
    -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
    -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
  INSTALL_COMMAND
    ""
  )

首先要引入ExternalProject_Add这个cmake函数。这是一个cmake官方内置的函数。

接着为当前目录设置一下EP_BASE这个属性,这就代表ExternalProject Base。也就是外部项目的根目录。配置后会自动在该目录之下创建Build, Download, Install, Stamp和tmp文件夹。
在这里插入图片描述
(注:我们也可以通过在ExternalProject_add指定PREFIX参数来指定外部库的根目录。如果什么都不指定,那么默认为<name>-prefix 其中name就是该外部库的名字。)

然后我们利用ExternalProject_add这个函数

解释下这函数的参数:

  • 第一个参数代表你给定该外部项目的target名称(如External_B_name_I_set)
  • SOURCE_DIR代表你要指定的源码位置
  • CMAKE_ARGS代表你从外界传入的命令行参数
  • INSTALL_COMMAND 为空保证了不会报出如下错误。由于默认会执行install,所以会报下面的错误。
[build] MSBUILD : error MSB1009: 项目文件不存在。 [E:\codes\try\cmake-target-clash\build\B.vcxproj]
[build]   开关:install.vcxproj
[build] E:\App\Microsoft Visual 

当你配置cmake后,会出现如下几个目标
在这里插入图片描述

其中B和C都是UTILITY目标,这些目标是不能被直接使用的。(因此我们后面要手动将其设置为可以使用的目标)

外部项目就像存在于一个独立封闭的空间,除了你自己在ExternalProject_Add中定义的 target名之外,什么都不会暴露出来。

我们这时候编译External_B_name_I_set,不会出现任何target冲突的错误

UTILITY目标不可链接

上面说了。编译出来的是UTILITY目标,这些目标无法被直接使用。需要手动去设置将其变为正常的可链接的目标。

如果这时候直接去link会报错。

target_link_libraries(A PRIVATE
External_C_name_I_set
External_B_name_I_set
)

报错如下所示

[cmake] CMake Error at CMakeLists.txt:48 (target_link_libraries):
[cmake]   Target "External_B_name_I_set" of type UTILITY may not be linked into
[cmake]   another target.  One may link only to INTERFACE, OBJECT, STATIC or SHARED
[cmake]   libraries, or to executables with the ENABLE_EXPORTS property set.

它告诉我们UTILITY目标是个假目标,是不能被link的。

题外话:

假目标就是用来执行某些命令的假目标,比如add_custom_target当中的目标都是假目标。

不信我们可以定义一个假目标看看。

add_custom_target(hello_target
  COMMAND ${CMAKE_COMMAND} -E echo "hello I am a custom target"
  )

在这里插入图片描述

ZERO_CHECK也是个假目标。ZERO_CHECK就是保证更改了cmake文件后编译时自动config。这个伪目标是cmake内置的。

解决UTILITY不可链接错误

如何解决呢?

  1. 比较原始的方法,根据库的地址链接库。(由于我们这个例子比较简单,我们就不演示头文件了)。

  2. 就是调用install命令。这种比较好,但是要求你所用的第三方库作者写了install命令(一般都会写)。

首先演示方案1:

ExternalProject_Get_Property(External_B_name_I_set BINARY_DIR )
message(STATUS "BINARY_DIR: ${BINARY_DIR}")
target_link_libraries(A PRIVATE
${BINARY_DIR}/$<CONFIG>/Blib.lib
)
unset(BINARY_DIR)

没什么可说的,就是找到Blib.lib的绝对地址然后链接而已。

其次演示方案2:

首先去掉INSTALL_COMMAND “”

为B和C编写install命令

cmake_minimum_required(VERSION 3.23)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

project(A)

add_executable(A src/main.cpp)

# 直接add_subdirectory会导致target冲突
# add_subdirectory(extern/B)
# add_subdirectory(extern/C)

# 使用ExternalProject可以解决target冲突问题
include(ExternalProject)
set_property(DIRECTORY PROPERTY EP_BASE ${CMAKE_BINARY_DIR}/ExternalProject)

set(EXT_INALL_DIR ${CMAKE_BINARY_DIR}/ExternalProject/Install)
message(STATUS "EXT_INALL_DIR: ${EXT_INALL_DIR}")

ExternalProject_Add(External_C
  SOURCE_DIR
    ${CMAKE_CURRENT_LIST_DIR}/extern/C
  CMAKE_ARGS
    -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
    -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
    -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
    -DCMAKE_INSTALL_PREFIX:PATH=${EXT_INALL_DIR}/External_C 
  )

ExternalProject_Add(External_B
  SOURCE_DIR
    ${CMAKE_CURRENT_LIST_DIR}/extern/B
  CMAKE_ARGS
    -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
    -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
    -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
    -DCMAKE_INSTALL_PREFIX:PATH=${EXT_INALL_DIR}/External_B 
  )

# 方案1(INSTALL_COMMAND "")
# ExternalProject_Get_Property(External_B BINARY_DIR )
# message(STATUS "BINARY_DIR: ${BINARY_DIR}")
# target_link_libraries(A PRIVATE
# ${BINARY_DIR}/$<CONFIG>/Blib.lib
# )
# unset(BINARY_DIR)


# # 方案2
# 可以直接链接并include
ExternalProject_Get_Property(External_B INSTALL_DIR)
set(BLIB_INCLUDE_DIR ${INSTALL_DIR}/include)
set(BLIB_LIBRARIES ${INSTALL_DIR}/lib/Blib.lib)
unset(INSTALL_DIR)
# target_link_libraries(A PRIVATE
#   ${BLIB_LIBRARIES}
# )
# target_include_directories(A PUBLIC
#   ${BLIB_INCLUDE_DIR}
# )

# 也可以先封装成一个INTERFACE库,然后链接
add_library(External_B_to_link INTERFACE)
target_link_libraries(External_B_to_link INTERFACE
  ${BLIB_LIBRARIES}
)
target_include_directories(External_B_to_link INTERFACE
  ${BLIB_INCLUDE_DIR}
)
target_link_libraries(A PRIVATE
  External_B_to_link
)
add_dependencies(External_B_to_link External_B)

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

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

相关文章

JVM本地锁(二)ReentrantLock可重入锁源码解析

什么是可重入锁呢 顾名思义&#xff0c;就是可以重复进入的锁&#xff0c;学过操作系统或者计组的可参照理解pv&#xff0c;或者多重中断。 demo1(){lock(); //第一次锁demo2(){lock(); // 第二次锁unlock(); }unlock();}文章目录ReentrantLocklock 加锁1. ReentrantLock.lock…

1990-2022年6月上市公司高管信息数据

1990-2022年6月上市公司高管信息数据、董监高信息数据 1、时间&#xff1a;1990-2022年6月 2、指标&#xff1a;证券代码、统计截止日期、人员ID、姓名、国籍、籍贯、籍贯所在地区代码、出生地、出生地所在地区代码、性别、年龄、毕业院校、学历、专业、职称、个人简历、是否…

给数组创建视图(浅拷贝)修改视图值影响原数组值修改视图形状不影响原数组形状numpy.view()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 给数组创建视图&#xff08;浅拷贝&#xff09; 修改视图值影响原数组值 修改视图形状不影响原数组形状 numpy.view() 对于以下python代码表述错误的一项是? import numpy as np import nu…

代码随想录二刷day5 两数之和 四数相加 (三数之和 四数之和) ->多写几遍(解法双指针放缩)

二刷复习 文章目录二刷复习哈希表和哈希法unordered 和 ordered 的不同242.有效字母的异位词349.两个数组的交集202.快乐数两数之和四数相加2383.赎金信三数之和&#xff08;这道题需要重复做&#xff0c; 双指针&#xff09;四数之和哈希表和哈希法 哈希表&#xff1a;这是两…

BGP建邻实验

目录 1.拓扑图 2.要求 3.实验思路 4.主要配置 5.测试 6.实验总结 1.拓扑图 2.要求 每台路由器都有两个环回&#xff0c;一个24的环回&#xff0c;一个32的环回&#xff1b;32的环回用于建邻&#xff0c;24的环回用于用户网段&#xff0c;最终实现所有24的环回可以ping通即…

BeyondCorp 打造得物零信任安全架构

1. 背景 当前&#xff0c;大部分企业都使用防火墙 (firewall) 来加强网络边界安全。然而&#xff0c;这种安全模型是有缺陷&#xff0c;因为当该边界被破坏&#xff0c;攻击者可以相对容易地访问公司的特权内部网。 边界安全模型通常被比作中世纪城堡&#xff1a;城墙厚厚的堡…

Mysql 报“Finished with error”,该怎么及解决?

用了多年的Mysql,当用navicat导库时&#xff0c;偶尔会遇到“Finished with error”错误&#xff0c; 如下图&#xff1a; 下面是我结合工作经验&#xff0c;总结一下&#xff0c;将相应情况及解决方法提供给网友们&#xff1a; 情况1&#xff1a;导入的sql数据库脚本文件中日…

vue3 antd项目实战——Form表单的重置【使用resetFields()重置form表单数据】

vue3 ant design vue项目实战——Form表单【resetFields重置form表单数据】关于form表单的文章场景复现resetFields()重置表单数据项目实战关于form表单的文章 文章内容文章链接Form表单提交和校验https://blog.csdn.net/XSL_HR/article/details/128495087?spm1001.2014.3001…

剑指offer----C语言版----第五天

目录 1. 重建二叉树 1.1 题目描述 1.2 复习基础知识 1.3 思路分析 1.4 总结 1. 重建二叉树 原题链接&#xff1a; 剑指 Offer 07. 重建二叉树 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/zhong-jian-er-cha-shu-lcof/submissions/ 1.1 题目描述…

极简四则运算解释器

前言: 这是最近完成的一个小的 demo&#xff0c;一个极简四则运算解释器。前面&#xff0c;已经基于这个想法发了两篇博客了&#xff1a; 四则运算和二叉树 简单四则运算语法树可视化 然后&#xff0c;前两天也就完成了这个总体的 demo 程序。本来整个程序的思路大致上有了&…

前端框架 Nuxt3 集成axios 配置跨域

目录 一、安装axios 二、Nuxt3项目集成axios 1、项目根目录下创建server/api目录 2、调用封装的单例axios对象 3、页面中调用请求函数 刚开始通过Nuxt3使用axios时&#xff0c;以为axios还需要配置跨域&#xff0c;但经过多次测试发现&#xff0c;在Nuxt3框架里并不需要配…

磨金石教育摄影技能干货分享|胡杨为什么被新疆人奉为精神图腾

痴迷于胡杨的摄影家 新疆摄影师王汉冰&#xff0c;昨天我们介绍了他一张《沙狐之眼.》&#xff0c;天人合一的画面让我们感到震撼。 除此之外王汉冰还有一个称号那就是“胡杨王”。 意思很明确&#xff0c;那就是擅长拍摄胡杨&#xff0c;作品也多是以胡杨见长。 新疆地大物博&…

【OpenCV 例程 300篇】253. 多帧图像(动图)的读取与保存

『youcans 的 OpenCV 例程300篇 - 总目录』 【youcans 的 OpenCV 例程 300篇】253. 多帧图像&#xff08;动图&#xff09;的读取与保存 1. 多帧图像&#xff08;动图&#xff09; 多帧图像是将多幅图像或帧数据保存在单个文件中&#xff0c;也称为多页图像或图像序列&#xf…

eNSP 实现静态路由中的路由备份

eNSP 实现静态路由中的路由备份 eNSP 实现静态路由中的路由备份&#xff1b;路由备份功能&#xff0c;可以提高网络的可靠性。用户可以根据实际情况&#xff0c;配置到同一目的地的多条路由&#xff0c;其中一条路由的优先级最高&#xff0c;作为主路由&#xff0c;其余的路由…

06 retrieveFileStream 之后需要调用 completePendingCommand 否则业务代码会存在问题

前言 问题是这样的 之前 同事碰到了这样的一个问题, 说是基于 ftp 客户端更新文件名字 更新失败 然后 看了一下, 原来是 调用了 retrieveFileStream, 然后 没有同步等待 数据传输完成, 然后 之后直接调用了 rename 的方法 然后 发现 rename 返回的是 false, 并且 文件名…

看我今年奋斗,观我未来之路

看我今年奋斗&#xff0c;观我未来之路 。 博客之星评选已经进行了几天。我也没有很去关注这东西&#xff0c;毕竟一个刚注册一年、写了无数水文的误导他人的博主&#xff0c;怎么可能拿到博客之星&#xff1f; 我&#xff0c;只是无聊时转发一下&#xff0c;那句毫无新意、从…

大学生如何在网上赚零花钱,适合学生党可做的零花钱项目

大学生的课余时间是非常多的&#xff0c;利用这些时间&#xff0c;我们可以去做点小兼职赚点零花钱&#xff0c;既可以补贴生活费&#xff0c;又可以获得不一样的生活体验。 现在大学生完全可以不用去大街上发传单&#xff0c;或者去咖啡店当服务员了&#xff0c;自己在网上就…

【数据结构】单链表(线性表)的实现

目录 一、什么是链表 二、单链表的实现 1、动态申请一个结点 2、单链表打印 3、单链表尾插 4、单链表的尾删 5、单链表的头插 6、单链表头删 7、单链表查找 8、单链表在pos位置之后插入x 9、单链表删除pos位置之后的值 10、单链表在pos位置之前插入x 11、单链表删除pos位置的值…

前端期末考试试题及参考答案(04)

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 一、 填空题 在页面中&#xff0c; ______标签用于创建一个表单。< form>中的______属性用于指定接收并处理表单数据的服务器url地址。< form>中的______表示以…

基于springboot+Vue的疫情防控系统(程序+数据库)

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…