工作区¶
警告
此功能是新的孵化功能的一部分。这意味着它正在开发中,并正在寻求用户测试和反馈。有关更多信息,请参阅孵化部分。
在上一节中,我们研究了可编辑包以及如何定义自定义布局。让我们介绍工作区的概念以及如何使用它。
介绍¶
重要
可以通过定义环境变量CONAN_WORKSPACE_ENABLE=will_break_next
来启用工作区功能。值will_break_next
用于强调它将在后续版本中更改,并且此功能仅用于测试,不能在生产环境中使用。
Conan 工作区允许您以编排或整体(也称为超级构建)方式管理多个处于可编辑
模式的包。
编排:我们指的是 Conan 逐个构建可编辑包,如果存在应用程序/消费者,则从它们开始。
整体:我们指的是可编辑包作为一个整体构建,为整个工作区生成一个单一的结果(生成器等)。
请注意,添加到工作区的包会自动解析为可编辑
包。这些可编辑包被称为工作区的包
。
如何定义工作区¶
工作区由文件conanws.yml
和/或conanws.py
定义。任何 Conan 工作区命令都会从当前工作目录向上遍历文件系统直到文件系统根目录,直到找到其中一个文件。这将定义“根”工作区文件夹。conanws
文件中的路径旨在是相对路径,以便在必要时可以重新定位,或者可以在类似 monorepo 的项目中提交到 Git。
通过conan workspace
命令,我们可以打开、添加和/或从当前工作区中删除包
。
另请参阅
阅读工作区文件部分。阅读conan workspace 命令部分。
整体构建¶
Conan 工作区可以作为一个单一的整体项目(超级项目)构建,这可能非常方便。我们通过一个例子来看看
$ conan new workspace
$ conan workspace super-install
$ cmake --preset conan-release # use conan-default in Win
$ cmake --build --preset conan-release
让我们稍微解释一下发生了什么。conan new workspace
首先创建了一个包含一些相关文件和以下结构的模板项目
.
├── CMakeLists.txt
├── app1
│ ├── CMakeLists.txt
│ ├── conanfile.py
│ ├── src
│ │ ├── app1.cpp
│ │ ├── app1.h
│ │ └── main.cpp
│ └── test_package
│ └── conanfile.py
├── conanws.py
├── conanws.yml
├── liba
│ ├── CMakeLists.txt
│ ├── conanfile.py
│ ├── include
│ │ └── liba.h
│ ├── src
│ │ └── liba.cpp
│ └── test_package
│ ├── CMakeLists.txt
│ ├── conanfile.py
│ └── src
│ └── example.cpp
└── libb
├── CMakeLists.txt
├── conanfile.py
├── include
│ └── libb.h
├── src
│ └── libb.cpp
└── test_package
├── CMakeLists.txt
├── conanfile.py
└── src
└── example.cpp
根CMakeLists.txt
使用以下内容定义超级项目
cmake_minimum_required(VERSION 3.25)
project(monorepo CXX)
include(FetchContent)
function(add_project PACKAGE_NAME SUBFOLDER)
message(STATUS "Adding project: ${PACKAGE_NAME}. Folder: ${SUBFOLDER}")
FetchContent_Declare(
${PACKAGE_NAME}
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/${SUBFOLDER}
SYSTEM
OVERRIDE_FIND_PACKAGE
)
FetchContent_MakeAvailable(${PACKAGE_NAME})
endfunction()
include(build/conanws_build_order.cmake)
foreach(pair ${CONAN_WS_BUILD_ORDER})
string(FIND "${pair}" ":" pos)
string(SUBSTRING "${pair}" 0 "${pos}" pkg)
math(EXPR pos "${pos} + 1") # Skip the separator
string(SUBSTRING "${pair}" "${pos}" -1 folder)
add_project(${pkg} ${folder})
# This target should be defined in the liba/CMakeLists.txt, but we can fix it here
get_target_property(target_type ${pkg} TYPE)
if (NOT target_type STREQUAL "EXECUTABLE")
add_library(${pkg}::${pkg} ALIAS ${pkg})
endif()
endforeach()
所以基本上,超级项目使用FetchContent
来添加子文件夹的子项目。为了使其正常工作,子项目必须是基于 CMake 的子项目,并带有CMakeLists.txt
。此外,子项目必须定义正确的find_package()
脚本所定义的正确目标,例如liba::liba
。如果不是这种情况,总是可以定义一些本地的ALIAS
目标。
这个超级构建的CMakeLists.txt
动态定义了正确的子项目顺序。请注意,FetchContent
策略要求以正确的构建顺序定义不同的子项目。虽然这对于只有少量包的工作区来说很容易,但对于较大的工作区来说可能会成为负担。构建顺序的定义是在生成的conanws_build_order.cmake
文件中完成的,该文件由调用conanws.py
工作区build_order()
方法的conan workspace super-install
命令创建。工作区有责任将build_order()
的信息转换为构建系统特有的内容。确切的实现,例如conanws_build_order.cmake
文件,并非“内置”工作区功能,请注意这仅仅是conan new workspace
默认模板提供的示例方法。用户可以在其conanws.py
文件中实现自己的逻辑。
另一个重要的部分是conanws.py
文件
from conan import Workspace
from conan import ConanFile
from conan.tools.files import save
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
def build_order(self, order):
super().build_order(order) # default behavior prints the build order
pkglist = " ".join([f'{it["ref"].name}:{it["folder"]}' for level in order for it in level])
save(self, "build/conanws_build_order.cmake", f"set(CONAN_WS_BUILD_ORDER {pkglist})")
嵌入式 conanfile 中的class MyWs(ConanFile)
的作用很重要,它定义了超级项目所需的生成器和布局。
conan workspace super-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 super-install
$ cmake ...
注意
当前的conan new workspace
会生成一个基于 CMake 的超级项目。但是也可以使用其他构建系统定义超级项目,例如添加不同.vcxproj
子项目的 MSBuild 解决方案文件。只要超级项目知道如何聚合和管理子项目,这是可能的。
如果存在某些结构,conanws.py
中的add()
方法也可以管理子项目到超级项目的添加。
编排构建¶
Conan 工作区还可以单独构建不同的包
,并考虑到是否存在定义为其他包的消费者的包。
让我们使用另一个结构来更好地理解它的工作原理。现在,让我们使用conan workspace init .
从头开始创建它,这将创建一个几乎为空的 conanws.py/conanws.yml,并使用conan new cmake_lib/cmake_exe
基本模板,这些模板创建基于 CMake 的常规 conan 包
$ mkdir myproject && cd myproject
$ conan workspace init .
$ conan new cmake_lib -d name=hello -d version=1.0 -o hello
$ conan new cmake_exe -d name=app -d version=1.0 -d requires=hello/1.0 -o app
这些命令创建了一个如下所示的文件结构
.
├── conanws.py
├── conanws.yml
├── app
│ ├── CMakeLists.txt
│ ├── conanfile.py
│ ├── src
│ │ ├── app.cpp
│ │ ├── app.h
│ │ └── main.cpp
│ └── test_package
│ └── conanfile.py
└── hello
├── CMakeLists.txt
├── conanfile.py
├── include
│ └── hello.h
├── src
│ └── hello.cpp
└── test_package
├── CMakeLists.txt
├── conanfile.py
└── src
└── example.cpp
现在,conanws.yml
是空的,conanws.py
的定义也很少。让我们将app
应用程序(消费hello
)和hello
库作为新的包
添加到工作区
$ conan workspace add hello
Reference 'hello/1.0' added to workspace
$ conan workspace add app
Reference 'app/1.0' added to workspace
定义了工作区的包
后,我们可以构建它们并执行应用程序
$ conan workspace build
$ app/build/Release/app
hello/1.0: Hello World Release!
...
app/1.0: Hello World Release!
...
如有任何反馈,请在https://github.com/conan-io/conan/issues上提交新工单。