孵化特性

本节专门介绍正在开发中的新功能,这些功能正在寻找用户测试和反馈。它们通常位于一个标志后面,以便明确选择加入此测试阶段。它们需要最新的 Conan 版本(有时建议从 develop2 源代码分支运行),并显式设置这些标志。

新的 CMakeConfigDeps 生成器

此生成器旨在替代当前的 CMakeDeps 生成器,其中包含多个待处理的修复和改进,如果不破坏当前生成器,则无法轻松完成。

  • 创建真实的 SHARED/STATIC/INTERFACE IMPORTED 目标,不再有人工接口目标。CONAN_LIB:: 和其他类似目标不再存在。

  • 为目标定义 IMPORTED_CONFIGURATIONS。

  • 依赖项的 CONFIG 定义与依赖项的 Release/Debug/etc build_type 匹配,不再使用使用者的定义。

  • 定义库目标的 IMPORTED_LOCATION 和 IMPORTED_IMPLIB。

  • 基于 recipe languagescpp_info/component languages 属性定义 LINK_LANGUAGES。

  • 所有这些都允许更好地传播链接要求和可见性,从而避免 Linux 中传递共享库的一些链接错误。

  • 更好地定义同一包内组件之间以及与其他包之间的 requires 关系。

  • 它不需要任何 build_context_activatedbuild_context_suffix 来使用 tool_requires 依赖项。

  • 定义 cpp_info/component.exe 信息(应包括 .location 定义),以定义可以运行的 EXECUTABLE 目标。

  • 来自 requires 的可执行文件也可以在非交叉构建方案中使用。当存在到相同依赖项的 tool_requires 时,这些可执行文件将具有优先级。

  • 创建一个新的 conan_cmakedeps_paths.cmake,其中包含用于直接查找依赖项的 <pkg>_DIR 路径的定义。此文件也计划在 cmake-conan 中使用,以扩展其用法并避免由于 CMake 驱动的安装以后无法注入工具链而导致的一些当前限制。

  • (Conan 2.14 之后的新功能)通过 cpp_info.frameworks 更好地管理系统 OSX 框架。

  • (Conan 2.14 之后的新功能)定义 cpp_info/component.package_framework 信息(应包括 .location 定义,例如,os.path.join(self.package_folder, "MyFramework.framework", "MyFramework")) 来定义要链接的自定义 OSX 框架库。

注意

此生成器仅用于生成 config.cmake 配置文件,它不会生成 Find*.cmake 查找模块,也不计划支持它。为此,请使用 CMakeDeps 生成器。

除了 CppInfo 中已定义的字段外,还可以在 cpp_infocpp_info.components 中定义以下新字段

# EXPERIMENTAL FIELDS, used exclusively by new CMakeConfigDeps (-c tools.cmake.cmakedeps:new)
self.cpp_info.type  # The type of this artifact "shared-library", "static-library", etc (same as package_type)
self.cpp_info.location # full location (path and filename with extension) of the artifact or the Apple Framework library one
self.cpp_info.link_location  # Location of the import library for Windows .lib associated to a dll
self.cpp_info.languages # same as "languages" attribute, it can be "C", "C++"
self.cpp_info.exe  # Definition of an executable artifact
self.cpp_info.package_framework  # Definition of an Apple Framework (new since Conan 2.14)

这些字段将从其他 cpp_infocomponents 定义中自动推导出来,例如 libslibdirs 字段,但自动推导可能有限制。显式定义它们将抑制自动推导并使用 recipe 提供的 value。

此功能通过 -c tools.cmake.cmakedeps:new=will_break_next 配置启用。值 will_break_next 将在下一个版本中更改,以强调此功能不适合用于测试以外的用途。只需启用此 conf 并强制构建使用 CMakeDeps 的软件包,将触发新生成器的使用。

此新生成器也可以在 conanfile 文件中使用,方法是

conanfile.txt
[generators]
CMakeConfigDeps
conanfile.py
class Pkg(ConanFile):
   generators = "CMakeConfigDeps"

或者

conanfile.py
from conan import ConanFile
from conan.tools.cmake import CMakeConfigDeps

class TestConan(ConanFile):

   def generate(self):
      deps = CMakeConfigDeps(self)
      deps.generate()

如果未启用配置,则此 recipe CMakeConfigDeps 用法仍然需要 -c tools.cmake.cmakedeps:new=will_break_next。如果未启用配置,这些 recipe 将失败。也可以定义 -c tools.cmake.cmakedeps:new=recipe_will_break 以专门启用 CMakeConfigDeps 生成器的用法,但不启用使用 CMakeConfigDeps 自动替换现有的 CMakeDeps

请注意,即使对于显式的 CMakeConfigDeps 生成器语法,该功能仍处于“孵化”阶段,此 recipe 随时可能中断或删除。

如有任何反馈,请在 https://github.com/conan-io/conan/issues 中打开新 issue。此反馈对于稳定该功能并使其脱离孵化非常重要,因此即使它运行良好并且您没有发现任何问题,报告积极的反馈也非常有用。

工作区

可以通过定义环境变量 CONAN_WORKSPACE_ENABLE=will_break_next 来启用工作区功能。值 will_break_next 用于强调它将在下一个版本中更改,并且此功能仅用于测试,不能在生产中使用。

启用该功能后,工作区由 conanws.yml 和/或 conanws.py 文件定义。默认情况下,任何 Conan 命令都会从当前工作目录向上遍历文件系统到文件系统根目录,直到找到其中一个文件。这将定义“根”工作区文件夹。

conan workspace 命令允许打开、添加、删除当前工作区中的软件包。查看 conan workspace -h 帮助和子命令的帮助以检查其用法。

添加到工作区的依赖项作为本地 editable 依赖项工作。只有在当前工作区下才能将它们解析为 editable。如果将当前目录移动到工作区之外,则不会再使用这些 editable 依赖项。

conanws 文件中的路径旨在相对于必要时可重定位,或者可以提交到类似 monorepo 的项目中的 Git。

工作区文件语法

工作区的最基本实现是仅包含属性定义的 conanws.yml 文件。例如,一个非常基本的工作区文件,仅定义当前的 CONAN_HOME 为本地文件夹,如下所示:

conanws.yml
home_folder: myhome

但是,conanws.yml 可以使用功能更强大的 conanws.py 进行扩展,该 conanws.py 遵循与 ConanFile 与其 conandata.yml 相同的关系,例如,它可以动态定义工作区主页,如下所示:

conanws.py
from conan import Workspace

class MyWs(Workspace):

   def home_folder(self):
      # This reads the "conanws.yml" file, and returns "new_myhome"
      # as the current CONAN_HOME for this workspace
      return "new_" + self.conan_data["home_folder"]

因此,命令 conan config home

$ conan config home
/path/to/ws/new_myhome

将显示当前 CONAN_HOME 为 new_myhome 文件夹(默认情况下,它相对于包含 conanws 文件的文件夹)

同样,定义 2 个 editables 的工作区 conanws.yml 可以是

conanws.yml
editables:
   dep1/0.1:
      path: dep1
   dep2/0.1:
      path: dep2

但是,如果我们想动态定义 editables,例如基于文件夹中某些 name.txtversion.txt 文件的存在,则可以在 conanws.py 中将 editables 定义为

conanws.py
import os
from conan import Workspace

class MyWorkspace(Workspace):

   def editables(self):
      result = {}
      for f in os.listdir(self.folder):
         if os.path.isdir(os.path.join(self.folder, f)):
            with open(os.path.join(self.folder, f, "name.txt")) as fname:
               name = fname.read().strip()
            with open(os.path.join(self.folder, f, "version.txt")) as fversion:
               version = fversion.read().strip()
            result[f"{name}/{version}"] = {"path": f}
      return result

也可以使用 Workspace.load_conanfile() 辅助程序在 set_name()set_version() 方法中重用 conanfile.py 逻辑

conanws.py
import os
from conan import Workspace

class MyWorkspace(Workspace):
   def editables(self):
      result = {}
      for f in os.listdir(self.folder):
         if os.path.isdir(os.path.join(self.folder, f)):
            conanfile = self.load_conanfile(f)
            result[f"{conanfile.name}/{conanfile.version}"] = {"path": f}
      return result

工作区命令

conan workspace add/remove

使用这些命令将 editable 软件包添加到当前工作区或从中删除 editable 软件包。conan workspace add <path> 文件夹必须包含 conanfile.py

conanws.py 具有默认实现,但可以覆盖默认行为

conanws.py
import os
from conan import Workspace

class MyWorkspace(Workspace):
   def name(self):
      return "myws"

   def add(self, ref, path, *args, **kwargs):
      self.output.info(f"Adding {ref} at {path}")
      super().add(ref, path, *args, **kwargs)

   def remove(self, path, *args, **kwargs):
      self.output.info(f"Removing {path}")
      return super().remove(path, *args, **kwargs)

conan workspace info

使用此命令显示有关当前工作区的信息

$ cd myfolder
$ conan new workspace
$ conan workspace info
WARN: Workspace found
WARN: Workspace is a dev-only feature, exclusively for testing
name: myfolder
folder: /path/to/myfolder
products
   app1
editables
   liba/0.1
      path: liba
   libb/0.1
      path: libb
   app1/0.1
      path: app1

conan workspace open

新的 conan workspace open 命令实现了一个新概念。那些在 conandata.yml 中包含 scm 信息的包 (使用 git.coordinates_to_conandata()) 可以根据其 Conan recipe 引用(包括 recipe revision)从当前工作空间中自动克隆和检出。

conan new workspace

conan new 命令学习了一个新的内置(实验性)模板 workspace,该模板创建一个包含一些可编辑包和一个代表它的 conanws.yml 的本地项目。 它对于快速演示、概念验证和实验很有用。

conan workspace build

conan workspace build 命令的功能相当于对工作空间中定义的每个 product 执行 conan build <product-path> --build=editable

Products 是“下游”使用者,是依赖关系图的“根”和起始节点。 可以使用 conan workspace add <folder> --product 新的 --product 参数来定义它们。

conan workspace build 命令只是迭代所有 products,因此它可能会重复构建 products 的可编辑依赖项。 在大多数情况下,这将是一个空操作,因为项目已经构建,但仍然可能需要一些时间。 正在等待优化,但这将在稍后完成,现在重要的是专注于工具、用户体验、流程和定义(例如 products)。

conan workspace install

conan workspace install 命令可用于将当前工作空间安装和构建为可编辑对象的整体超级项目。 请参阅下一节。

默认情况下,它使用工作空间中的所有 editable 包。 可以使用 conan workspace install <folder1> .. <folderN> 可选参数仅选择其中的一部分。 仅安装这些包的子图,包括它们的依赖项和传递依赖项。

工作空间整体构建

Conan 工作空间可以构建为单个整体项目(有时称为超级项目),这可能非常方便。 让我们用一个例子来说明

$ conan new workspace
$ conan workspace install
$ cmake --preset conan-release # use conan-default in Win
$ cmake --build --preset conan-release

让我们解释一下发生了什么。 首先,conan new workspace 创建了一个包含一些相关文件的模板项目

CMakeLists.txt 使用以下内容定义超级项目

CMakeLists.txt
cmake_minimum_required(VERSION 3.25)
project(monorepo CXX)

include(FetchContent)

function(add_project SUBFOLDER)
   FetchContent_Declare(
      ${SUBFOLDER}
      SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/${SUBFOLDER}
      SYSTEM
      OVERRIDE_FIND_PACKAGE
   )
   FetchContent_MakeAvailable(${SUBFOLDER})
endfunction()

add_project(liba)
# They should be defined in the liba/CMakeLists.txt, but we can fix it here
add_library(liba::liba ALIAS liba)
add_project(libb)
add_library(libb::libb ALIAS libb)
add_project(app1)

所以基本上,超级项目使用 FetchContent 来添加子文件夹子项目。 为使此功能正常工作,子项目必须是基于 CMake 的子项目,并带有 CMakeLists.txt。 此外,子项目必须定义正确的目标,就像 find_package() 脚本定义的 liba::liba 一样。 如果不是这种情况,始终可以定义一些本地 ALIAS 目标。

另一个重要的部分是 conanws.py 文件

conanws.py
from conan import Workspace
from conan import ConanFile
from conan.tools.cmake import CMakeDeps, CMakeToolchain, cmake_layout

class MyWs(ConanFile):
   """ This is a special conanfile, used only for workspace definition of layout
   and generators. It shouldn't have requirements, tool_requirements. It shouldn't have
   build() or package() methods
   """
   settings = "os", "compiler", "build_type", "arch"

   def generate(self):
      deps = CMakeDeps(self)
      deps.generate()
      tc = CMakeToolchain(self)
      tc.generate()

   def layout(self):
      cmake_layout(self)

class Ws(Workspace):
   def root_conanfile(self):
      return MyWs  # Note this is the class name

嵌入的 conanfile class MyWs(ConanFile) 的作用非常重要,它定义了超级项目所需的生成器和布局。

conan workspace install 不会分别安装不同的可编辑对象,对于此命令,可编辑对象不存在,它们只是被视为依赖关系图中的单个“节点”,因为它们将成为超级项目构建的一部分。 因此,对于所有超级项目外部依赖项,只有一个生成的 conan_toolchain.cmake 和一组通用的依赖项 xxx-config.cmake 文件。

上面的模板在没有外部依赖项的情况下工作,但是当存在外部依赖项时,一切都将以相同的方式工作。 可以通过以下方式进行测试

$ conan new cmake_lib -d name=mymath
$ conan create .
$ conan new workspace -d requires=mymath/0.1
$ conan workspace install
$ cmake ...

注意

当前的 conan new workspace 生成一个基于 CMake 的超级项目。 但是可以使用其他构建系统(例如,添加不同 .vcxproj 子项目的 MSBuild 解决方案文件)来定义超级项目。 只要超级项目知道如何聚合和管理子项目,这都是可能的。

如果存在某种结构,则 conanws.py 中的 add() 方法也可能管理将子项目添加到超级项目中。

如有任何反馈,请在 https://github.com/conan-io/conan/issues 中打开新的 issue。