Skip to content

Latest commit

 

History

History
227 lines (172 loc) · 10 KB

可执行文件项目依赖管理.md

File metadata and controls

227 lines (172 loc) · 10 KB

可执行文件项目依赖管理

C/C++的依赖管理算是非常凌乱的,在前面的文章中我们也算是大致介绍了下使用cmake管理依赖的方式.很明显这个方式痛点不少,还是比较繁琐复杂的.一旦一个项目依赖上十几二十个库那维护起来还是很酸爽的.同时如果第三方库大量使用源码安装会大大拖慢编译时间,造成大量的资源浪费.依赖大量托管在git仓库,http服务器上也会对编译的可维护性造成一定影响,同时对代码安全有要求的场景这种方式也很难管理维护.

我们期望的是可以有一个集中式的依赖仓库,类似pypi,它里面有大量常用库在各个常用平台上已经编译好的二进制包,同时也提供源码包以供在不常用的平台上也可以尝试安装.同时这个平台也提供一个客户端工具,类似pip,可以用于管理项目的依赖项,同时也支持将自己的库上传到仓库中统一管理,同时最好提供用户自行部署仓库的能力,以方便企业或者团体私人使用. C/C++有这样的工具么?答案是有,而且不止一个,但又不完全有.目前没有一个工具可以满足上面所有的期望,但又有两个候选的工具他们可以一定程度上满足上面的需求.他们就是vcpkgconan,下面是它们以及各种linux发行版包管理工具对对比矩阵

特性\工具 linux发行版包管理仓库 vcpkg conan
集中式管理
跨平台
常用平台提供二进制包
保管库数量
支持自行部署仓库
客户端工具用于查找安装卸载依赖
客户端工具可以安装指定版本的依赖
客户端工具用于发布依赖
客户端工具提供系统级依赖管理
客户端工具提供项目级的依赖管理

看起来conan更有优势,但架不住conan上的资源太少.好在这两个工具都可以无缝的和cmake结合使用,这样相当于我们可以使用cmake来管理项目,而不同的依赖管理工具则专门用于管理项目依赖.

针对可执行文件的C/C++项目依赖管理策略

任何情况下我们都希望我们编译项目不会影响系统,因此项目级的依赖管理才是我们应该采取的策略.

C/C++项目的依赖管理可以遵从如下规则进行:

  1. 尽可能使用conan管理项目依赖
  2. conan中找不到的包尽量使用cmake的FetchContent模块从源码开始编译
  3. conan中找不到的依赖,而且FetchContent获取困难时可以尝试使用vcpkg安装.
  4. 尽量不用linux发行版包管理工具管理依赖

我们继续改造helloworld_with_log项目.这个项目依赖spdlog这个包而conan中刚好有.这里就借着这个项目介绍下如何使用conan构造可执行文件.

使用conan管理可执行文件项目的依赖

使用conan管理可执行文件项目的依赖只有5步

  1. 设置一些必要的环境变量和设置(根据平台情况设置)

    主要要设置这些环境变量

    • CC=/usr/bin/gcc
    • CXX=/usr/bin/g++
    • CMAKE_C_COMPILER=gcc
    • CMAKE_Fortran_COMPILER=gfortran

    conan则主要要设置它名为defaultprofile.所谓profile可以理解为编译环境设置,通过conan profile detect创建,通过conan profile update <key>=<value> <profile_name>设置.默认位置在~/.conan2/profiles/default文件中保存

    可以设置的项可以在这里查到

    通常我们比较关系如下几个设置项:

    • settings.compiler=gcc
    • settings.compiler.version=10
    • settings.compiler.exception=seh
    • settings.compiler.libcxx=libstdc++11
    • settings.compiler.threads=posix
    • settings.build_type=Release
  2. 项目根目录编写conanfile.txt用于描述依赖.

    conanfile.txt中通常只要设置好requiresgenerators即可.

    其中requires中以<package_name>/<version>的形式描述依赖,我们可以通过conan search <package_name>[ -r <REMOTE>]来查找conan仓库中托管的依赖包.默认会从conancenter中查找包.我们也可以用-r来指定已经设置过的conan仓库,私有仓库一块我们后面介绍.

    其中generators目前基本都是填的

    [generators]
    CMakeDeps
    CMakeToolchain
  3. 创建build文件夹,执行conan install . --output-folder=build [ --build=missing[ --profile=<profile_name>[ -r <REMOTE>]]]安装

    conan install命令用于安装并配置项目的依赖.默认只会下载当前平台已经编译好的二进制库,如果碰到二进制库有缺失就会失败,我们可以加上--build=missing让它碰到有缺失的库时尝试现编译.而--profile则是指定特定的编译配置项

  4. 在项目根目录创建你的cmake配置文件CmakeLists.txt,并在其中加入对conan管理包的引用:

    ...
    find_package(<依赖包名> REQUIRED)
    ...
    target_link_libraries(${PROJECT_NAME}  
        ...
        <依赖包名>::<依赖包名>
    )

    具体怎么导入依赖,我们通常可以在conancenter上对应仓库的说明中找到

  5. build文件夹下,根据执行操作系统的不同,执行如下命令:

    • windows下:

      cmake .. -G "Visual Studio 15 2017" -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake
      cmake --build . --config Release
    • linux/macos下

      cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
      cmake --build .

这样就编译完成了

我们修改前面的helloworld_with_log继续做例子,这个例子在https://github.com/hsz1273327/TutorialForCLang/tree/master/examples/%E5%B7%A5%E5%85%B7%E9%93%BE/%E7%BC%96%E8%AF%91%E5%B7%A5%E5%85%B7%E9%93%BE/%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86/helloworld_with_log看到

源码不用修改,只是增加conanfile.txt并修改CmakeLists.txt即可.

  • conanfile.txt

    [requires]
    spdlog/1.14.1
    
    [generators]
    CMakeDeps
    CMakeToolchain
    
  • CmakeLists.txt

    #项目编译环境
    cmake_minimum_required (VERSION 3.17)
    # # 确保可以描述项目
    # if (POLICY CMP0048)
    #   cmake_policy(SET CMP0048 NEW)
    # endif (POLICY CMP0048)
    
    project (helloworld_with_log
        VERSION 0.0.0
        DESCRIPTION "简单测试"
        LANGUAGES CXX
    )
    # 引入conan维护的依赖
    message(NOTICE "引入conan维护的依赖")
    # include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
    # conan_basic_setup()
    find_package(spdlog REQUIRED)
    
    option(AS_STATIC "是否作为纯静态可执行文件编译" off)
    # 构造可执行文件的配置
    add_executable(${PROJECT_NAME})
    ## 设置源码位置
    target_sources(${PROJECT_NAME} 
        PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/helloworld.cc
    )
    
    target_link_libraries(${PROJECT_NAME} 
        spdlog::spdlog
    )
    if(AS_STATIC)
        message("编译纯静态可执行文件")
        target_compile_options(${PROJECT_NAME}
            PRIVATE "-static"
        )
    endif()

conan外挂vcpkg管理的依赖

前面也说过很多包conan中是找不到的,这种情况下我们希望尽量在不动系统库的情况下控制依赖,怎么办呢?

一个最通用的方法是使用cmake自带的FetchContent模块直接在源头上管理依赖,这也是我更加推荐的方式.但有时候真的很麻烦.另一种选择是使用vcpkg先安装好依赖,然后把这个依赖融合到CmakeLists.txt中.

vcpkg安装依赖很简单,就是cvpkg install <package_name>[:TRIPLET],TRIPLET可以大致的和前面conan的profile划等号,它也是一组编译设置,放在安装目录的triplets文件夹下.

我们结合vcpkg的默认例子,改造上面的helloworld_with_log,让它打印用vckpg安装的sqlite3的版本号.这个例子在https://github.com/hsz1273327/TutorialForCLang/tree/master/examples/%E5%B7%A5%E5%85%B7%E9%93%BE/%E7%BC%96%E8%AF%91%E5%B7%A5%E5%85%B7%E9%93%BE/%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86/hello_sqlite

这个例子中我们的源码文件hello_sqlite.cpp

#include <sqlite3.h>
#include <spdlog/spdlog.h>

int main() {
    spdlog::info(sqlite3_libversion());
    return 0;
}

这个例子我们需要先用vcpkg install sqlite3安装号sqlite3.然后修改CmakeLists.txt

#项目编译环境
cmake_minimum_required (VERSION 3.17)
SET(CMAKE_TOOLCHAIN_FILE "/vcpkg/vcpkg/scripts/buildsystems/vcpkg.cmake")
# 确保可以描述项目
if (POLICY CMP0048)
  cmake_policy(SET CMP0048 NEW)
endif (POLICY CMP0048)

project (hello_sqlite
    VERSION 0.0.0
    DESCRIPTION "简单测试"
    LANGUAGES CXX
)
# 引入conan维护的依赖
message(NOTICE "引入conan维护的依赖")

# 找到vcpkg安装的sqlite3
find_package(unofficial-sqlite3 CONFIG REQUIRED)
# 找到conan安装的spdlog
find_package(spdlog REQUIRED)

option(AS_STATIC "是否作为纯静态可执行文件编译" off)
# 构造可执行文件的配置
add_executable(${PROJECT_NAME})
## 设置源码位置
target_sources(${PROJECT_NAME} 
    PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/hello_sqlite.cpp
)
target_link_libraries(${PROJECT_NAME}  
    PRIVATE unofficial::sqlite3::sqlite3
    spdlog::spdlog
)
if(AS_STATIC)
    message("编译纯静态可执行文件")
    target_compile_options(${PROJECT_NAME}
        PRIVATE "-static"
    )
endif()