Workspace 文件¶
警告
此功能是新的孵化功能的一部分。这意味着它仍在开发中,并正在寻找用户测试和反馈。有关更多信息,请参阅 孵化部分。
Workspace 由 conanws.yml 和/或 conanws.py 文件定义,这些文件将定义“根”workspace 文件夹。
conanws.yml¶
Workspace 最基本的实现是 conanws.yml 文件。它定义了 workspace 的 packages(可编辑的包)。例如,定义 2 个 packages 的 workspace conanws.yml 可能是
packages:
- path: dep1
ref: dep1/0.1
- path: dep2
ref: dep2/0.1
此外,它可能没有 ref 字段,让 Conan 从各自的 path/conanfile.py 中读取 *name/version*。
packages:
- path: dep1
- path: dep2
conanws.py¶
一个 conanws.yml 可以通过一个功能更强大的 conanws.py 扩展,它遵循与 ConanFile 及其 conandata.yml 之间的相同关系。如果我们要动态定义 packages,例如基于某些 name.txt 和 version.txt 文件是否存在,则可以在 conanws.py 中定义这些包,如下所示
import os
from conan import Workspace
class MyWorkspace(Workspace):
def packages(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.append({"path": f, "ref": f"{name}/{version}"})
return result
也可以在 set_name() 和 set_version() 方法中使用 conanfile.py 逻辑,使用 Workspace.load_conanfile() 助手
import os
from conan import Workspace
class MyWorkspace(Workspace):
def packages(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.append({"path": f, "ref": f"{conanfile.name}/{conanfile.version}"})
return result
conanws.py super-build ConanFile¶
当计算 workspace 依赖关系图时,workspace 中的所有包都将折叠到依赖关系图中的单个节点,并且该节点将具有依赖关系到 Conan 缓存中安装的 workspace 外部的其他包。 conanws.py 文件可以包含代表 super-build 的 ConanFile 的定义。
定义 workspace super-build 项目的 ConanFile 如下所示
from conan import ConanFile, Workspace
from conan.tools.cmake import cmake_layout
class MyWs(ConanFile):
settings = "os", "compiler", "arch", "build_type"
generators = "CMakeToolchain", "CMakeDeps"
def layout(self):
cmake_layout(self)
class Ws(Workspace):
def root_conanfile(self):
return MyWs
它定义了我们的 super-build 项目将是一个 CMake 项目,该项目使用 CMakeToolchain 和 CMakeDeps 生成器来集成和查找外部包依赖项。 ConanFile 根本不需要定义 requires,它们将通过聚合 workspace 中所有包的 requires 来计算。
重要
在 conanws.py 内部的 ConanFile 是一个特殊的 conanfile,仅用于 workspace super-build 定义布局和生成器。它不应该有任何类型的 requirements,既没有常规的 requires,tool_requires 或 test_requires。它通过收集和聚合 workspace 包的 requirements 来获得其依赖项。它也不应该有 build() 或 package() 方法。
conanws.py super-build workspace packages¶
使用 workspace_packages 属性,conanws.py super-build ConanFile 可以访问 workspace 包,以便能够重用它们的功能。例如,如果我们想要收集 workspace 包 toolchain 定义的行为,我们可以这样做。假设我们有一个包含 2 个包 pkga/1.2.3 和 pkgb/2.3.4 的 workspace
from conan import ConanFile
class PkgA(ConanFile):
name = "pkga"
version = "1.2.3"
def configure_toolchain(self, tc):
tc.preprocessor_definitions["PKGA_SOME_DEFINITION"] = self.version
def generate(self):
tc = CMakeToolchain(self)
self.configure_toolchain(tc)
tc.generate()
from conan import ConanFile
class PkgB(ConanFile):
name = "pkgb"
version = "2.3.4"
def configure_toolchain(self, tc):
tc.preprocessor_definitions["SOME_PKGB_DEFINE"] = self.version
def generate(self):
tc = CMakeToolchain(self)
self.configure_toolchain(tc)
tc.generate()
from conan import ConanFile
from conan import Workspace
from conan.tools.cmake import CMakeToolchain
class MyWs(ConanFile):
settings = "arch", "build_type"
def generate(self):
tc = CMakeToolchain(self)
for ref, dep in self.workspace_packages.items():
dep.configure_toolchain(tc)
tc.generate()
class Ws(Workspace):
def root_conanfile(self):
return MyWs
然后,workspace_packages.items() 迭代将能够调用 workspace 中的每个包 configure_toolchain() 并将它们的所有行为收集到当前的 super-build CMakeToolchain 中。生成的 toolchain 将包含 SOME_PKGB_DEFINE=2.3.4 和 PKGA_SOME_DEFINITION=1.2.3 的定义。
警告
重要
访问 workspace_packages 到 workspace 包 ConanFiles 必须是**只读**和**纯粹**的。它不能修改 workspace pkga 和 pkgb 包的数据,并且不能有任何副作用。例如,禁止调用任何方法,如 .build() 甚至 .generate() 方法。如果需要重用的逻辑,开发人员有责任定义一些约定,例如 configure_toolchain() 方法,如果从 conanws.py 调用,将完全不会修改(纯粹)pkga 或 pkgb 数据。
conanws.py super-build options¶
上述 workspace_packages 访问的一个特殊情况是读取各个 workspace 包的选项。用于 super-build workspace 文件的 conanws.py 可以以两种不同的方式管理选项
它可以定义自己的
options,使用正常的conanfile.py语法,以便生成的conan_toolchain.cmake使用这些输入。它可以收集 workspace 的选项,使用
workspace_packages并以任何用户自定义的方式处理它们。
super-project options
一个 conanws.py 必须在 ConanFile 类中定义 super-build 的选项,并在 generate() 方法中使用这些选项,就像通常在 conanfile.py 文件中发生的那样,如下所示
from conan import ConanFile, Workspace
class MyWs(ConanFile):
settings = "arch", "build_type"
options = {"myoption": [1, 2, 3]}
def generate(self):
self.output.info(f"Generating with my option {self.options.myoption}!!!!")
class Ws(Workspace):
def root_conanfile(self):
return MyWs
然后,可以通过配置文件或命令行使用常规语法提供选项
$ conan workspace super-install -of=build -o "*:myoption=1"
> conanws.py base project Conanfile: Generating with my option 1!!!!
请注意,workspace 包中定义的 options 可能会重叠,因为对于 super-project 而言,这些选项会被简单地忽略,并且只有 super-project 的选项才会被考虑在内,以生成 conan_toolchain.cmake。例如,conanws.py 可以定义一个 shared 选项,如果希望 conan_toolchain.cmake 在定义类似 -o "*:shared=True" 时正确定义 BUILD_SHARED_LIBS,因为 workspace 包具有 shared 选项信息在 workspace 包折叠到依赖关系图中以模拟 super-project 时会被丢弃。
packages options
第二种替代方案是收集已折叠的 workspace 包的 options。回想一下,在最终的依赖关系图中,workspace 包不再被表示,因为它们不再是单独的包,而是作为当前 super-build 的一部分。访问其选项信息的方式是通过 workspace_packages,并且可以使用该信息在 generate() 方法中执行 super-build 项目级别的任何所需操作。
因此,假设一个包含 dep/0.1 包的 workspace,该包包含标准的 shared 选项,定义了以下 super-build ConanFile
from conan import ConanFile, Workspace
class MyWs(ConanFile):
def generate(self):
for pkg, dep in self.workspace_packages.items():
for k, v in dep.options.items():
self.output.info(f"Generating with opt {pkg}:{k}={v}!!!!")
class Ws(Workspace):
def root_conanfile(self):
return MyWs
然后,当定义 workspace 包选项时,workspace ConanFile 可以收集它们。
$ conan workspace super-install -of=build -o "*:shared=True"
> conanws.py base project Conanfile: Generating with opt dep/0.1:shared=True!!!!!!!!
注意
实际上,workspace 创建者有责任定义如何处理这些选项,无论是通过定义自己的选项还是收集 workspace 包的选项。请注意,不可能自动将 workspace 包选项映射到 super-project,因为选项是按包定义的。两个不同的包可能具有不同的 shared=True 和 shared=False 值。此外,通常,对生成的 toolchain 文件的影响是自定义的,并在每个包的 generate() 方法中实现。这种影响是程序化的(不是声明性的),将所有这些影响聚合到一个 toolchain 中将非常具有挑战性。
另请参阅
阅读 Workspace 教程 部分。