CMake工程中引入其他C++三方库,包管理

在Android NDK CMake工程中引入其他C++三方库时,有以下几种常见的依赖方式:

1. 源码依赖

如果三方库的源代码包含在你的项目目录中,并且它有自己的CMake配置,可以使用add_subdirectory将三方库的构建过程集成到你的项目中。

示例:

假设三方库的源代码位于third_party/SomeLibrary目录下。

# CMakeLists.txt

# 添加三方库的构建
add_subdirectory(third_party/SomeLibrary)

# 添加可执行文件目标
add_library(native-lib SHARED native-lib.cpp)

# 将三方库链接到目标
target_link_libraries(native-lib PRIVATE SomeLibrary)

2. 预编译库依赖

如果三方库已经预先编译成.so.a文件,可以使用add_librarytarget_link_libraries来引入这些预编译库。
适用场景:第三方库已提供针对 Android 的预编译动态库(.so)和头文件。

示例:

假设三方库的预编译.so文件位于libs/${ANDROID_ABI}/libSomeLibrary.so

# CMakeLists.txt

# 添加预编译库
add_library(SomeLibrary SHARED IMPORTED)
set_target_properties(SomeLibrary PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libSomeLibrary.so)

# 添加可执行文件目标
add_library(native-lib SHARED native-lib.cpp)

# 将预编译库链接到目标
target_link_libraries(native-lib PRIVATE SomeLibrary)
方式 1:预编译动态库(.so

步骤

  1. 放置文件

    • .so 文件按 ABI 分类放入 src/main/jniLibs/abi/ 目录(如 armeabi-v7a/libmylib.so)。
    • 将头文件放入 src/main/cpp/include/ 目录。
  2. 配置 CMakeLists.txt

    # 设置头文件路径
    include_directories(src/main/cpp/include)
    
    # 导入动态库(以 openssl 为例)
    add_library(openssl SHARED IMPORTED)
    set_target_properties(openssl PROPERTIES
        IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libssl.so
    )
    
    # 添加自己的目标(如共享库)
    add_library(native-lib SHARED
        src/main/cpp/native-lib.cpp
    )
    
    # 链接第三方库和系统库(如 log)
    target_link_libraries(native-lib
        openssl
        log  # Android 系统日志库
    )
    

方式 2:预编译静态库(.a

步骤

  1. 放置文件

    • .a 文件放入 src/main/jniLibs/abi/ 目录。
    • 头文件放入 src/main/cpp/include/
  2. 配置 CMakeLists.txt

    # 导入静态库(如 libpng)
    add_library(png STATIC IMPORTED)
    set_target_properties(png PROPERTIES
        IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libpng.a
    )
    
    # 链接静态库
    target_link_libraries(native-lib
        png
        log
    )
    

3. 使用FetchContent模块(CMake 3.11+)

如果三方库可以从远程位置(如Git仓库)获取,可以使用FetchContent模块在构建过程中自动下载和构建三方库。

示例:

假设三方库的Git地址为https://github.com/someuser/SomeLibrary.git

# CMakeLists.txt

# 启用FetchContent模块
include(FetchContent)

# 下载并构建三方库
FetchContent_Declare(
    SomeLibrary
    GIT_REPOSITORY https://github.com/someuser/SomeLibrary.git
    GIT_TAG        main
)
FetchContent_MakeAvailable(SomeLibrary)

# 添加可执行文件目标
add_library(native-lib SHARED native-lib.cpp)

# 将三方库链接到目标
target_link_libraries(native-lib PRIVATE SomeLibrary)

4. 手动指定源文件

如果三方库的源代码是一个简单的库,没有复杂的构建系统,可以直接将源文件添加到你的CMake工程中。

示例:

假设三方库的源文件位于third_party/SomeLibrary/src目录下,头文件位于third_party/SomeLibrary/include目录下。

# CMakeLists.txt

# 添加三方库的源文件
add_library(SomeLibrary STATIC)
target_sources(SomeLibrary PRIVATE
    third_party/SomeLibrary/src/file1.cpp
    third_party/SomeLibrary/src/file2.cpp
)
target_include_directories(SomeLibrary PRIVATE
    third_party/SomeLibrary/include
)

# 添加可执行文件目标
add_library(native-lib SHARED native-lib.cpp)

# 将三方库链接到目标
target_link_libraries(native-lib PRIVATE SomeLibrary)

步骤

  1. 将源码放入项目目录

    • 将第三方库的源码放在 thirdparty/ 目录下(如 thirdparty/openssl)。
  2. 配置 CMakeLists.txt

    # 将第三方库的源码目录加入子目录编译
    add_subdirectory(${CMAKE_SOURCE_DIR}/thirdparty/openssl)
    
    # 添加自己的目标
    add_library(native-lib SHARED src/main/cpp/native-lib.cpp)
    
    # 链接编译后的库
    target_link_libraries(native-lib
        openssl  # 假设第三方库生成的库名为 openssl
        log
    )
    

5. 使用include_directories指定头文件路径

如果需要引入三方库的头文件,可以使用include_directories指定头文件路径。

示例:

假设三方库的头文件位于third_party/SomeLibrary/include目录下。

# CMakeLists.txt

# 指定头文件路径
include_directories(third_party/SomeLibrary/include)

# 添加可执行文件目标
add_library(native-lib SHARED native-lib.cpp)

# 如果是预编译库,还需要指定库文件路径并链接
# add_library(SomeLibrary SHARED IMPORTED)
# set_target_properties(SomeLibrary PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libSomeLibrary.so)
# target_link_libraries(native-lib PRIVATE SomeLibrary)

方式 4:系统库查找(find_package

步骤

  1. 确保库已安装

    • 第三方库需在系统中安装(如通过 NDK 或包管理器安装)。
  2. 配置 CMakeLists.txt

    # 查找已安装的 OpenCV 库
    find_package(OpenCV REQUIRED)
    
    # 添加目标并链接
    add_library(native-lib SHARED src/main/cpp/native-lib.cpp)
    target_include_directories(native-lib PRIVATE ${OpenCV_INCLUDE_DIRS})
    target_link_libraries(native-lib ${OpenCV_LIBS} log)
    

方式 5:自动下载编译(ExternalProject_Add

步骤

  1. 配置 CMakeLists.txt
    # 下载并编译第三方库(如 gtest)
    include(ExternalProject)
    ExternalProject_Add(
        gtest
        GIT_REPOSITORY https://github.com/google/googletest.git
        CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/gtest
    )
    
    # 添加目标并链接
    add_library(gtest-lib INTERFACE)
    target_include_directories(gtest-lib INTERFACE
        ${CMAKE_BINARY_DIR}/gtest/include
    )
    target_link_libraries(gtest-lib INTERFACE
        ${CMAKE_BINARY_DIR}/gtest/lib/libgtest.a
    )
    
    # 使用 gtest
    add_executable(my_test test.cpp)
    target_link_libraries(my_test gtest-lib)
    

关键配置说明

  1. 头文件路径

    include_directories(  # 传统方式
        src/main/cpp/include
        thirdparty/include
    )
    
    # 推荐使用 target_include_directories
    target_include_directories(native-lib PRIVATE
        ${CMAKE_SOURCE_DIR}/src/main/cpp/include
    )
    
  2. 动态库路径的动态化

    • 使用 ${ANDROID_ABI} 变量自动适配不同 ABI:
      set(LIB_DIR ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})
      set_target_properties(png PROPERTIES
          IMPORTED_LOCATION ${LIB_DIR}/libpng.so
      )
      
  3. 依赖传递性

    • 如果第三方库依赖其他库(如 libcrypto.so),需逐层导入:
      add_library(crypto SHARED IMPORTED)
      set_target_properties(crypto PROPERTIES
          IMPORTED_LOCATION ${LIB_DIR}/libcrypto.so
      )
      target_link_libraries(openssl PRIVATE crypto)  # openssl 依赖 crypto
      

总结:依赖方式对比

  • 源码依赖:适用于三方库的源代码已经包含在项目目录中,并且有CMake配置。
  • 预编译库依赖:适用于三方库已经预先编译成.so.a文件。
  • FetchContent:适用于从远程位置获取并自动构建三方库。
  • 手动指定源文件:适用于简单的三方库,直接将源文件添加到工程中。
  • include_directories:适用于需要引入三方库的头文件。
    | 方式 | 是否需要源码 | 适用场景 | 优点 | 缺点 |
    |------------------------|--------------|-----------------------------------|-------------------------------|-------------------------------|
    | 预编译动态库(.so) | 不需要 | 第三方提供预编译动态库 | 快速集成,无需编译 | 需确保 ABI 兼容性 |
    | 预编译静态库(.a) | 不需要 | 第三方提供静态库 | 静态链接,无依赖传递问题 | 文件体积较大 |
    | 源码编译(add_subdirectory) | 需要 | 需自定义编译或库未提供预编译文件 | 灵活配置,支持修改源码 | 需处理交叉编译及依赖 |
    | 系统库(find_package) | 不需要 | 系统已安装且提供 CMake 配置文件 | 自动化配置,简洁 | 依赖系统环境 |
    | 自动下载(ExternalProject) | 需要 | 需自动管理源码下载和编译 | 自动化流程,便于版本控制 | 构建时间较长 |

通过以上方法,可以根据第三方库的提供形式和项目需求,灵活选择最合适的依赖方式。

ref:

https://www.echo.cool/docs/language/cpp/cpp-projects-and-tools/cpp-dependency-management/
http://chu-studio.com/posts/2019/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%9A%84C++%E5%8C%85%E7%AE%A1%E7%90%86%E5%99%A8CONAN%E4%B8%8A%E6%89%8B%E6%8C%87%E5%8D%97

<think>好的,用户想在一个大型跨平台的CMake项目中使用C++和Qt,大部分功能以插件形式存在,并且用vcpkg管理第三方库。他们需要在安装时自动拷贝依赖库,CMake版本是3.25以上,尽量用CMake自带命令。我需要帮他们解决这个问题。 首先,用户的问题核心是安装时自动拷贝依赖库。我知道CMake中有几个相关命令,比如install(),但可能需要处理动态库的依赖。特别是跨平台的话,Windows、Linux、macOS处理方式不同。 在Windows下,DLL的拷贝比较麻烦。CMake自带的BundleUtilities和File(GET_RUNTIME_DEPENDENCIES)可能有用。不过BundleUtilities可能有点旧了,而File命令在3.15之后引入,用户用的是3.25,所以没问题。 对于Qt插件,可能需要处理额外的资源文件,比如平台插件、图像格式插件等。这些需要手动指定安装路径,并设置环境变量,比如QT_PLUGIN_PATH。 然后,vcpkg管理的三方库,安装后应该已经被正确找到,但动态库可能需要被自动收集。可能需要用vcpkg的特定脚本来帮助,比如vcpkg_copy_pdbs,不过不确定是否需要。 接下来,我需要组织步骤。首先,确保目标正确安装,包括可执行文件、插件和库文件。然后,收集运行时依赖,使用file(GET_RUNTIME_DEPENDENCIES)来获取动态库列表,并安装到相应目录。同时处理Qt插件,将它们拷贝到指定位置,并设置环境变量。 可能需要注意不同平台的处理,比如Windows用DLL,Linux用so,macOS用dylib。同时,检查CMake的生成表达式,确保在安装阶段执行依赖收集。 另外,Qt的部署工具比如windeployqt可能在Windows上有用,但用户希望用CMake自带命令,所以可能需要替代方案。或者结合使用,但可能需要额外步骤。 最后,测试安装后的目录结构,确保所有依赖都被正确拷贝,并且程序能运行。可能需要验证环境变量是否正确设置,比如插件路径。 总结步骤应该是:配置安装目标,收集运行时依赖,处理Qt插件,设置环境变量,最后测试验证。</think>以下是在CMake 3.25+环境下实现自动依赖库拷贝的解决方案,分步骤说明: $$----------------------------------$$ # 基础项目配置(CMakeLists.txt) $$----------------------------------$$ cmake_minimum_required(VERSION 3.25) project(MyProject) # 设置vcpkg工具链 set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake") # 启用自动包含依赖目录 set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "Runtime") set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) # Windows特定 $$----------------------------------$$ # 主要功能实现步骤 $$----------------------------------$$ 1. **目标安装配置** ```cmake # 安装主程序 install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin BUNDLE DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) # 安装插件(每个插件单独处理) foreach(plugin ${PLUGIN_LIST}) install(TARGETS ${plugin} LIBRARY DESTINATION plugins/${plugin} RUNTIME DESTINATION plugins/${plugin} ) endforeach() ``` 2. **自动依赖收集** ```cmake # 获取所有运行时依赖 include(GNUInstallDirs) install(CODE [[ file(GET_RUNTIME_DEPENDENCIES EXECUTABLES $<TARGET_FILE:${PROJECT_NAME}> RESOLVED_DEPENDENCIES_VAR _r_deps UNRESOLVED_DEPENDENCIES_VAR _unresolved ) file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" TYPE SHARED_LIBRARY FILES ${_r_deps} ) ]]) ``` 3. **Qt特殊处理** ```cmake # 自动查找Qt依赖路径 find_package(Qt6 COMPONENTS Core Gui Widgets) get_target_property(QT_LIB_DIR Qt6::Core LOCATION) # 安装Qt核心运行时库 install(DIRECTORY "${QT_LIB_DIR}/" DESTINATION bin FILES_MATCHING PATTERN "*.dll" # Windows PATTERN "*.so*" # Linux PATTERN "*.dylib" # macOS ) ``` 4. **平台差异处理** ```cmake if(WIN32) # 处理VC运行时 include(InstallRequiredSystemLibraries) # 处理manifest文件 set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) install(FILES $<TARGET_PDB_FILE:${PROJECT_NAME}> DESTINATION bin OPTIONAL ) endif() ``` 5. **环境变量配置** ```cmake # 生成设置环境变量的脚本 install(CODE [[ file(WRITE "${CMAKE_INSTALL_PREFIX}/setenv.bat" "@echo off\n" "set PATH=%PATH%;%~dp0/bin\n" "set QT_PLUGIN_PATH=%~dp0/plugins\n" ) file(WRITE "${CMAKE_INSTALL_PREFIX}/setenv.sh" "export PATH=\"$PATH:$PWD/bin\"\n" "export QT_PLUGIN_PATH=\"$PWD/plugins\"\n" ) ]]) ``` $$----------------------------------$$ # 关键点说明 $$----------------------------------$$ 1. **依赖收集机制** - 使用`file(GET_RUNTIME_DEPENDENCIES)`代替旧的`BundleUtilities` - 自动处理递归依赖关系 - 支持多平台(Windows/Mac/Linux) 2. **Qt特殊处理** - 需要单独处理Qt插件目录 - 确保`QT_PLUGIN_PATH`环境变量设置正确 - 注意平台差异: - Windows:`.dll` + `*.dll`资源文件 - Linux:`.so` + `qt.conf`配置 - macOS:`.dylib` + Framework处理 3. **vcpkg集成** - 确保`VCPKG_ROOT`环境变量已设置 - 使用`vcpkg integrate install`后的自动集成 - 通过`vcpkg_copy_pdbs()`可补充调试符号(需单独调用) 4. **验证方法** ```bash # 生成安装包 cmake --build build --target install # 检查安装目录结构 tree install/ # 应该包含: # ├── bin # ├── lib # └── plugins ``` 5. **高级优化建议** ```cmake # 使用CPack生成安装包 include(CPack) set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF) set(CPACK_PACKAGE_VERSION_MAJOR 1) cpack_add_component(Runtime REQUIRED) ``` 此方案特点: - 纯CMake实现,无外部工具依赖 - 支持动态库自动递归收集 - 正确处理Qt插件机制 - 生成环境配置脚本 - 兼容vcpkg管理机制 - 支持跨平台部署 实际部署时需注意: 1. 测试不同平台的路径分隔符差异 2. 处理ICU等Qt特殊依赖 3. 验证符号链接在Linux/macOS下的有效性 4. 考虑代码签名需求(macOS/Windows) 5. 处理调试/发布版本冲突
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值