现代C++包管理器Conan实战指南:从依赖管理到私有包发布
1. 从零开始为什么你需要一个现代的C包管理器如果你是一个C开发者并且经历过手动下载第三方库、配置头文件路径、链接库文件、处理不同平台和编译器的依赖关系那么你肯定对“依赖地狱”这个词深有体会。一个项目依赖Boost、OpenSSL、Protobuf每个库又有自己的版本和编译选项光是让它们在Windows、Linux和macOS上都能顺利编译就足以消耗掉你一天中大部分的精力。这不仅仅是效率问题更是项目可维护性和团队协作的噩梦。Conan的出现就是为了终结这个混乱的局面。它不是一个简单的下载工具而是一个功能完备的、去中心化的C包管理器。你可以把它想象成Python的pip、Node.js的npm或者Java的Maven但它是专门为C这门复杂语言量身定制的。Conan的核心思想是“二进制兼容性管理”和“依赖关系解析”。它不仅能帮你下载源代码更重要的是它能根据你当前项目的配置比如编译器是gcc还是MSVC是Debug还是Release模式是x86还是x64架构自动获取或构建出完全匹配的、预编译好的二进制包。这意味着你的同事在Mac上用Clang编译项目而你在Windows上用Visual Studio只要你们的conanfile.txt或conanfile.py一致Conan就能为各自的环境提供正确的库文件实现“一次配置处处编译”。我最初接触Conan是在一个跨平台的中型C项目里。当时我们手动管理着十几个第三方库每次有新成员加入光搭建环境就要一整天还经常因为系统路径、环境变量不同而出错。引入Conan后新成员只需要执行conan install喝杯咖啡的功夫所有依赖就绪项目直接可以编译。这种体验的提升是颠覆性的。接下来我将以一个实战者的角度带你完整走一遍Conan的核心工作流安装、基础使用、创建自己的包以及将包分享给团队或社区。2. 环境部署与核心概念扫盲在动手之前我们需要先把Conan安装到你的系统上并理解几个关键概念这能让你后面的操作更加顺畅。2.1 安装Conan多种途径与推荐选择Conan是基于Python开发的因此最通用、最推荐的安装方式就是通过Python的包管理工具pip。这保证了你能始终获得最新版本。步骤1确保Python环境打开你的终端Linux/macOS或命令提示符/PowerShellWindows输入python --version或python3 --version。Conan需要Python 3.5及以上版本。如果你的系统没有Python请先去 Python官网 下载安装记得勾选“Add Python to PATH”选项。步骤2使用pip安装Conan在终端中执行以下命令pip install conan如果你只想为当前用户安装或者遇到权限问题可以加上--user标志pip install --user conan安装完成后通过conan --version命令验证安装是否成功。你应该能看到类似Conan version 2.0.5的输出。注意在某些Linux发行版上系统自带的Python可能版本较旧或者pip命令对应的是Python 2。请务必使用python3和pip3来执行上述命令。例如pip3 install conan。其他安装方式备选Homebrew (macOS)brew install conan。这是macOS用户非常便捷的方式。Chocolatey (Windows)choco install conan。适合习惯使用Chocolatey的Windows用户。Linux包管理器一些发行版如Arch Linux的仓库也提供了Conan但版本可能不是最新的。我个人强烈推荐使用pip安装因为它能最方便地进行版本升级pip install --upgrade conan并且与操作系统环境隔离得更好。2.2 核心概念解析仓库、配置、Profile和Graph安装好之后先别急着敲命令。理解下面几个术语能让你明白Conan在背后做了什么。包PackageConan管理的基本单元。一个包不仅包含库的源代码或二进制文件还包含一个最重要的元数据文件——conanfile.py或conanfile.txt。这个文件定义了包的名称、版本、依赖、如何构建、如何打包等信息。一个包可以有多个“二进制配置”比如zlib/1.2.11这个包会有compilergcc, compiler.version9, build_typeRelease和compilermsvc, compiler.version191, build_typeDebug等不同的二进制变体。远程仓库Remote存放Conan包的地方类似于Docker Hub或Maven Central。默认情况下Conan配置了conancenter远程这是Conan官方的中央仓库里面有成千上万个常用的C/C库。你也可以搭建自己的私有远程仓库如使用Artifactory用于存放公司内部的私有库。本地缓存Local Cache当你从远程下载或本地构建一个包后Conan会将其存储在用户主目录下的.conan2文件夹中Conan 2.x版本。下次再需要同样的包和配置时Conan会直接从这里读取无需重新下载或构建极大加快了依赖解析速度。Profile这是Conan的一个强大功能。Profile文件通常位于~/.conan2/profiles定义了一套默认的构建配置比如默认的编译器、编译架构、编译类型Debug/Release、标准库等。你可以为不同的项目或平台创建不同的profile例如linux_gcc_releasewindows_msvc2019_debug。在执行conan install时通过--profile参数指定Conan就会按照这个配置去查找或构建对应的二进制包。依赖图Dependency Graph当你为一个项目执行conan install时Conan会解析项目conanfile中声明的依赖以及这些依赖的依赖生成一个完整的依赖关系图。然后它会遍历这个图为图中每一个节点包找到或创建符合当前profile设置的二进制包。这个过程是自动的你无需关心底层库的依赖关系。理解了这些我们就可以进入实战环节了。让我们从一个最简单的消费者角度开始如何在你的项目中使用一个现有的Conan包。3. 作为消费者在项目中引入和管理第三方依赖这是Conan最常用、最能立即带来价值的场景。我们假设你有一个简单的CMake项目需要用到著名的JSON解析库nlohmann_json。3.1 创建项目与声明依赖首先为你的项目创建一个目录并在其中创建两个关键文件。项目结构my_conan_project/ ├── CMakeLists.txt ├── conanfile.txt # 依赖声明文件 └── src/ └── main.cpp1. 编写conanfile.txt 这个文件以声明式的方式告诉Conan你的项目需要哪些依赖。[requires] nlohmann_json/3.11.2 [generators] CMakeDeps CMakeToolchain[requires]部分列出所有需要的包及其版本。这里我们要求nlohmann_json库的 3.11.2 版本。[generators]部分指定Conan如何为你的构建系统生成文件。CMakeDeps会生成FindXXX.cmake之类的文件帮助CMake找到依赖包。CMakeToolchain会生成一个toolchain文件将Conan的配置如编译器路径、标准库传递给CMake。这是Conan 2.x推荐的方式与CMake集成得非常好。2. 编写CMakeLists.txt 这是一个极简的CMake文件演示如何与Conan生成的文件配合。cmake_minimum_required(VERSION 3.15) project(MyConanProject LANGUAGES CXX) # 包含Conan生成的toolchain文件必须在 project() 之后立即调用 include(${CMAKE_BINARY_DIR}/conan_toolchain.cmake) # 查找通过 Conan 安装的包 find_package(nlohmann_json 3.11.2 REQUIRED) add_executable(my_app src/main.cpp) # 链接库包名通常就是 target 名 target_link_libraries(my_app PRIVATE nlohmann_json::nlohmann_json)3. 编写src/main.cpp#include iostream #include nlohmann/json.hpp // 头文件路径会自动被Conan设置好 using json nlohmann::json; int main() { json j; j[name] Conan; j[awesome] true; j[version] 2.0; std::cout j.dump(4) std::endl; // 漂亮打印JSON return 0; }3.2 安装依赖与构建项目现在进入项目根目录my_conan_project打开终端。步骤1创建构建目录并进入mkdir build cd build步骤2让Conan安装依赖并生成文件这是最关键的一步。我们告诉Conan根据当前目录上一级的conanfile.txt来安装依赖。conan install ..执行这个命令后Conan会做以下几件事读取../conanfile.txt。检查本地缓存中是否有nlohmann_json/3.11.2包且二进制配置与当前默认profile匹配例如默认可能是compilergcc, build_typeRelease。如果没有它会从配置的远程仓库默认是conancenter查找。对于nlohmann_json这样的纯头文件库header-onlyConancenter上通常提供了预编译的“包”实际上只是包含了头文件布局信息的元数据包Conan会直接下载到本地缓存。根据[generators]的指示在build目录下生成conan_toolchain.cmake、CMakePresets.json以及一系列.cmake文件用于find_package。步骤3使用CMake配置和构建项目由于我们使用了CMakeToolchain推荐使用CMake Presets这是现代CMake的最佳实践。Conan已经为我们生成了CMakePresets.json。# 方式一使用Conan生成的Preset推荐 cmake --preset conan-default .. # 然后构建 cmake --build --preset conan-default # 方式二传统方式显式指定toolchain文件 cmake .. -DCMAKE_TOOLCHAIN_FILEconan_toolchain.cmake -DCMAKE_BUILD_TYPERelease cmake --build .步骤4运行程序构建成功后在build目录下或build/Release等子目录取决于你的生成器会生成可执行文件my_app。运行它你将看到漂亮的JSON输出。实操心得第一次运行conan install时如果本地和远程都没有对应配置的二进制包Conan可能会尝试从源代码构建这可能会花费一些时间并需要你的系统有相应的编译工具链如gcc、make。对于nlohmann_json这种头文件库则很快。你可以通过conan install .. --buildmissing命令显式要求Conan在缺少二进制包时进行构建。3.3 管理多配置与Profile实战现实项目往往需要Debug/Release、x86/x64等多种配置。手动为每种配置执行conan install并指定参数很麻烦。这时Profile就派上用场了。创建自定义Profile 在终端中运行conan profile detect --force。这个命令会探测你当前的系统环境编译器、架构等并创建一个名为default的profile。你可以查看它conan profile show default。但我们需要更精细的控制。让我们创建一个针对Linux系统GCC编译器Release版本的profile。conan profile new linux_gcc_release --detect # 基于检测结果创建新profile conan profile update settings.compilergcc linux_gcc_release conan profile update settings.compiler.version11 linux_gcc_release # 根据你的GCC版本修改 conan profile update settings.compiler.libcxxlibstdc11 linux_gcc_release conan profile update settings.build_typeRelease linux_gcc_release conan profile update settings.archx86_64 linux_gcc_release现在当你安装依赖时可以指定这个profileconan install .. --profilelinux_gcc_releaseConan就会严格按照这个profile里的设置去为nlohmann_json寻找compilergcc, compiler.version11, build_typeRelease, ...的二进制包。对于Windows Visual Studio你可以创建类似windows_msvc2022_release的profile将compiler设置为msvccompiler.version设置为193对应VS2022等。在CMake中切换配置 一个更工程化的做法是在项目根目录的CMakePresets.json中定义多个preset每个preset对应一个Conan profile。然后在构建时选择不同的preset即可。Conan生成的CMakePresets.json已经是一个很好的起点你可以在此基础上复制修改关联不同的profile。4. 进阶为生产者创建并测试你自己的Conan包当你有一个自己开发的、希望被其他项目复用的C库时将其打包成Conan包是最佳选择。这比单纯提供源代码要专业和方便得多。我们将创建一个名为mymath的简单数学库作为示例。4.1 规划包结构与编写conanfile.py与消费端的conanfile.txt不同创建包需要一个功能更强大的conanfile.py它是一个Python脚本定义了包的完整生命周期。创建项目结构mymath_conan_pkg/ ├── conanfile.py # 包的“配方”recipe核心文件 ├── CMakeLists.txt # 库的构建脚本 ├── include/ │ └── mymath/ │ └── mymath.h # 公共头文件 └── src/ └── mymath.cpp # 源文件1. 实现库代码include/mymath/mymath.h:#pragma once namespace mymath { int add(int a, int b); float multiply(float a, float b); }src/mymath.cpp:#include mymath/mymath.h namespace mymath { int add(int a, int b) { return a b; } float multiply(float a, float b) { return a * b; } }CMakeLists.txt(库的):cmake_minimum_required(VERSION 3.15) project(mymath LANGUAGES CXX) add_library(mymath src/mymath.cpp) target_include_directories(mymath PUBLIC $BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include $INSTALL_INTERFACE:include ) # 设置导出目标名便于find_package set_target_properties(mymath PROPERTIES EXPORT_NAME mymath::mymath) # 安装规则将库文件、头文件和CMake配置文件安装到指定目录 include(GNUInstallDirs) install(TARGETS mymath EXPORT mymath-targets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(EXPORT mymath-targets FILE mymath-config.cmake NAMESPACE mymath:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mymath )2. 编写核心的conanfile.py 这是包的“灵魂”它定义了包的元数据、依赖、如何构建、如何打包。from conan import ConanFile from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout from conan.tools.files import copy class MymathRecipe(ConanFile): # 包的基本元数据 name mymath version 1.0.0 license MIT author Your Name your.emailexample.com url https://github.com/yourusername/mymath description A simple math library example for Conan topics (math, example) # 包的类型和设置 package_type library settings os, compiler, build_type, arch options {shared: [True, False], fPIC: [True, False]} default_options {shared: False, fPIC: True} # 导出源码除了conanfile.py本身 exports_sources CMakeLists.txt, include/*, src/* def config_options(self): # 在Windows上fPIC选项没有意义将其移除 if self.settings.os Windows: del self.options.fPIC def configure(self): # 如果选择构建动态库在Windows上需要删除fPIC选项 if self.options.shared: self.options.rm_safe(fPIC) def layout(self): # 定义源码和构建目录的布局这是Conan 2.x的推荐做法 cmake_layout(self) def generate(self): # 生成CMake需要的toolchain文件 tc CMakeToolchain(self) tc.generate() def build(self): # 执行CMake构建 cmake CMake(self) cmake.configure() cmake.build() def package(self): # 将构建好的文件复制到包目录中 cmake CMake(self) cmake.install() # 可选手动复制一些CMake install可能漏掉的文件例如许可证 copy(self, LICENSE, srcself.source_folder, dstos.path.join(self.package_folder, licenses)) def package_info(self): # 定义消费者如何使用这个包。这是最关键的函数之一。 self.cpp_info.set_property(cmake_file_name, mymath) self.cpp_info.set_property(cmake_target_name, mymath::mymath) # 如果消费者使用CMakeDeps以上两行就足够了。 # 为了兼容旧方式也可以设置这些属性 self.cpp_info.libs [mymath] self.cpp_info.includedirs [include]这个conanfile.py做了以下几件重要的事定义身份name,version等。声明配置settings和options。settings是影响二进制兼容性的“不可变”配置如操作系统、编译器options是包作者定义的“可变”配置如是否构建为动态库shared。布局layout()函数告诉Conan源码和构建目录的结构。生成generate()创建构建系统文件如CMakeToolchain。构建build()执行实际的编译命令。打包package()将构建产物头文件、库文件等从构建目录复制到最终的“包”目录中。包信息package_info()是灵魂。它定义了其他项目如何链接和使用这个包。cpp_info.set_property是与现代CMake (find_package) 集成的最佳方式。4.2 在本地创建、测试和消费你的包包“配方”写好了我们需要在本地验证它是否能正确工作。步骤1进入包目录并创建本地包cd mymath_conan_pkg conan create . --buildmissingconan create命令是创建包的“一站式”命令。它会将当前目录.的源码导出到一个临时源码目录。在临时构建目录中根据当前profile或默认profile执行conanfile.py中定义的generate(),build(),package()等所有步骤。将最终打包好的内容安装到你的本地Conan缓存~/.conan2/p中。--buildmissing参数确保如果依赖缺失或需要重新构建Conan会执行构建。执行成功后终端会输出类似mymath/1.0.0: Package xxxxxxxx created的信息。你的第一个Conan包已经诞生并存储在本地缓存了步骤2在另一个测试项目中消费这个本地包现在回到我们之前创建的my_conan_project修改它的conanfile.txt添加对我们刚创建的mymath包的依赖。[requires] nlohmann_json/3.11.2 mymath/1.0.0 # 注意这里的 表示使用本地缓存的包后面没有用户/频道信息 [generators] CMakeDeps CMakeToolchain然后在main.cpp中使用这个库#include iostream #include nlohmann/json.hpp #include mymath/mymath.h // 引入我们自己的库 using json nlohmann::json; int main() { json j; j[name] Conan; j[awesome] true; j[add_result] mymath::add(10, 20); // 使用 mymath 库 j[multiply_result] mymath::multiply(1.5f, 2.0f); std::cout j.dump(4) std::endl; return 0; }修改CMakeLists.txt链接mymath库... find_package(mymath 1.0.0 REQUIRED) ... target_link_libraries(my_app PRIVATE nlohmann_json::nlohmann_json mymath::mymath # 链接我们的库 )最后在build目录下记得先清空或新建一个目录重新运行conan install ..和cmake --preset conan-default ..以及cmake --build --preset conan-default。Conan会自动从本地缓存找到mymath/1.0.0包并将其配置信息传递给CMake。运行程序你会看到计算结果被包含在JSON输出中。踩坑实录在package_info()中self.cpp_info.set_property(cmake_target_name, mymath::mymath)这里的cmake_target_name必须与你库的CMakeLists.txt中install(EXPORT ...)部分定义的命名空间和目标名完全一致。如果不一致CMake的find_package将无法找到正确的目标导致链接错误。务必仔细检查这两处的字符串。5. 分享与协作将你的包上传到远程仓库本地包只能自己用。为了团队协作或开源分享你需要将包上传到一个远程仓库。这里我们以上传到Conan官方中心仓库conancenter的流程为例这需要审核同时介绍上传到私有仓库如JFrog Artifactory的通用方法。5.1 为包添加用户与频道User/Channel在本地缓存中包的身份是name/version。但在上传和共享时Conan使用name/versionuser/channel的格式来唯一标识一个包。user通常是你的团队名或GitHub用户名channel常用于区分稳定版、测试版等如stable,testing,latest。我们在创建包时就可以指定# 在 mymath_conan_pkg 目录下 conan create . myteam/stable --buildmissing这将在本地创建一个mymath/1.0.0myteam/stable的包。5.2 配置远程仓库并上传上传到ConanCenter ConanCenter是只读的官方仓库。你不能直接conan upload。你需要将你的conanfile.py和源码提交到一个公共Git仓库如GitHub。前往 Conan Center Index 仓库提交一个Pull Request (PR)将你的包“配方”添加到对应的目录下。经过CI自动构建测试和社区维护者审核后你的包会被合并并自动发布到ConanCenter。 这是一个相对严格但标准化的开源流程确保了包的质量和可复现性。上传到私有Artifactory仓库更常见的团队场景 大多数公司会搭建内部的JFrog Artifactory作为私有Conan远程仓库。步骤1添加远程仓库首先你需要知道Artifactory服务器的URL和仓库名。conan remote add my-company http://artifactory.my-company.com/artifactory/api/conan/conan-repo # 如果需要认证通常需要 conan remote login my-company # 按提示输入用户名和密码/API Keymy-company是你给这个远程起的别名可以任意命名。步骤2上传包假设我们已经本地创建了mymath/1.0.0myteam/stable。conan upload mymath/1.0.0myteam/stable --remotemy-company --all--remotemy-company指定上传到哪个远程。--all上传该包的所有二进制配置例如你之前可能为gcc和clang都构建了包。如果只上传当前profile对应的包可以不加此参数。上传成功后团队其他成员只要添加了同一个远程仓库就可以在他们的conanfile.txt或conanfile.py中直接声明mymath/1.0.0myteam/stable然后通过conan install来获取这个包。5.3 版本管理与持续集成在实际项目中包的版本管理至关重要。版本号遵循语义化版本控制SemVer主版本号.次版本号.修订号。在conanfile.py中更新version字段。渠道Channel利用channel来管理发布流程。例如myteam/testing用于CI流水线自动构建和测试的包。myteam/stable经过测试验证可供其他项目使用的稳定包。CI/CD集成在你的Git仓库中设置CI如GitHub Actions, GitLab CI, Jenkins。当有代码推送时CI自动执行conan create来构建包并运行单元测试。如果测试通过可以自动将包上传到私有仓库的testing频道。当打上Git Tag如v1.0.1时CI可以自动构建并上传到stable频道。一个简单的GitHub Actions工作流片段可能如下所示jobs: build-and-test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: Install Conan run: pip install conan - name: Create and Test Package run: | conan create . --buildmissing -s build_typeRelease # 这里可以运行你的库的单元测试 - name: Upload to Artifactory (Testing) if: github.ref refs/heads/main # 仅对main分支上传测试包 run: | conan remote login my-company -p ${{ secrets.ARTIFACTORY_API_KEY }} conan upload mymath/* --remotemy-company --all --confirm6. 高级主题与生产环境最佳实践当你熟悉了基本流程后下面这些高级主题和技巧能帮助你更好地在生产环境中使用Conan。6.1 使用conanfile.py作为消费者对于复杂的项目仅用conanfile.txt可能不够。你可以使用conanfile.py来定义项目依赖这提供了更大的灵活性。from conan import ConanFile from conan.tools.cmake import CMakeDeps, CMakeToolchain class MyProjectConan(ConanFile): settings os, compiler, build_type, arch requires nlohmann_json/3.11.2, mymath/1.0.0myteam/stable generators CMakeDeps, CMakeToolchain def generate(self): tc CMakeToolchain(self) # 可以在这里定制CMake变量 tc.variables[MY_CUSTOM_VARIABLE] ON tc.generate() deps CMakeDeps(self) deps.generate()然后使用conan install .来安装依赖。conanfile.py允许你在generate()方法中执行更复杂的逻辑比如根据选项设置不同的CMake变量。6.2 管理可选依赖和工具依赖可选依赖options在创建包的conanfile.py中你可以定义选项。例如你的库可能支持用OpenMP加速。options {shared: [True, False], with_openmp: [True, False]} default_options {shared: False, with_openmp: True} def requirements(self): if self.options.with_openmp: self.requires(openmp/11.0.0) # 假设存在这个包消费者在安装你的包时可以通过-o mymath:with_openmpFalse来关闭这个特性。工具依赖tool_requires有些包只在构建时需要不作为运行时依赖。例如你的包可能需要cmake/3.25.0来构建或者需要doxygen来生成文档。使用tool_requires来声明它们它们不会被传递到消费者的依赖图中。tool_requires cmake/3.25.06.3 交叉编译与多配置构建Conan对交叉编译有很好的支持。关键在于使用正确的Profile。你可以创建一个代表目标平台的profile文件例如raspberry_pi_profile[settings] osLinux archarmv7hf compilergcc compiler.version10 compiler.libcxxlibstdc11 build_typeRelease [env] CCarm-linux-gnueabihf-gcc CXXarm-linux-gnueabihf-g然后在构建包时指定这个profileconan create . --profile:hostraspberry_pi_profile --profile:builddefault --buildmissing这里--profile:host指定目标设备Host的配置--profile:build指定构建机Build的配置。Conan会为交叉编译环境正确地构建包。6.4 常见问题排查与调试技巧“Package not found”错误检查远程conan remote list确认远程仓库已正确添加。conan search pkg -r all在所有远程搜索包。检查版本和用户/频道确认你要求的包名、版本、用户/频道完全正确。检查Profile你的当前profile设置如compiler.version可能太新或太旧远程仓库没有对应的二进制包。尝试conan install .. --buildmissing让Conan从源码构建。构建失败查看详细日志在命令后添加--verbose或设置环境变量CONAN_VERBOSE_TRACEBACK1来获取更详细的错误信息。检查本地缓存conan cache path pkg可以找到包的本地缓存路径进去查看构建日志通常在build/子文件夹的.log文件中。清理缓存有时旧的构建状态会导致问题。conan remove * -c可以清理所有缓存慎用或者只清理特定包conan remove pkg -c。CMake找不到包确认生成器确保conanfile.txt或conanfile.py中包含了CMakeDeps生成器。确认package_info()检查包生产者的conanfile.py中的package_info()函数是否正确设置了cmake_target_name等属性。手动检查生成的文件在build目录下查看conan_toolchain.cmake和那些FindXXX.cmake或XXX-config.cmake文件是否存在内容是否正确。经过这一整套从安装、使用、创建到上传的流程走下来Conan的核心价值已经非常清晰它将C/C项目从繁琐、易错的依赖管理手工劳动中解放出来通过声明式配置和自动化构建实现了依赖的精确控制、二进制兼容性管理和团队高效协作。虽然初期需要花一些时间学习其概念和工作流但这点投资对于任何严肃的、特别是跨平台和团队协作的C项目来说回报都是巨大的。从我个人的经验看一旦团队习惯了Conan的工作方式就再也回不去手动管理依赖的旧时代了。开始可能会在conanfile.py的编写和package_info()的设置上踩些坑但这些都是可预测、可解决的工程问题远比解决因环境不一致导致的“在我机器上是好的”这类玄学问题要简单和愉快得多。