CMakeToolchain: 使用包内的 xxx-config.cmake 文件¶
Conan 通常依赖于 package_info() 抽象,以便使用任何构建系统构建的包可以被使用于使用任何其他构建系统构建的任何其他包。对于 CMake,Conan 依赖于 CMakeDeps 生成器为每个依赖项生成 xxxx-config.cmake 文件,即使这些依赖项没有生成一个,或者根本不是用 CMake 构建的。
ConanCenter 使用这种抽象,不打包 xxx-config.cmake 文件,而是使用 package_info() 中的信息。提供尽可能与构建系统无关的包,并对不同的构建系统、供应商和用户公平非常重要。例如,有许多 Conan 用户高兴地使用原生 MSBuild (VS) 项目,而根本没有使用 CMake。如果 ConanCenter 包仅使用包内的 config.cmake 文件构建,这将是不可能的。
但是 ConanCenter 这样做,并不意味着这不可能或强制性的。完全有可能使用包内的 xxx-config.cmake 文件,放弃使用 CMakeDeps 生成器。
您可以在 GitHub 的 examples2 仓库 中找到重现此示例的源代码
$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/examples/tools/cmake/pkg_config_files
如果我们看一下 conanfile.py
class pkgRecipe(ConanFile):
name = "pkg"
version = "0.1"
...
def package_info(self):
# No information provided, only the in-package .cmake is used here
# Other build systems or CMake via CMakeDeps will fail
self.cpp_info.builddirs = ["pkg/cmake"]
self.cpp_info.set_property("cmake_find_mode", "none")
这是一个非常典型的配方,主要的区别是 package_info() 方法。需要注意三点
它没有定义诸如
self.cpp_info.libs = ["mypkg"]之类的字段。Conan 不会将此信息传递给使用者,此信息仅位于包内的xxx-config.cmake文件中以防万一仍有用户实例化
CMakeDeps,它使用set_property("cmake_find_mode", "none")禁用客户端生成xxx-config.cmake文件。它定义了它将在该文件夹内包含构建脚本(例如
xxx-config.cmake包),以便使用者可以找到。
因此,定义包详细信息的责任已转移到包含以下内容的 CMakeLists.txt
add_library(mylib src/pkg.cpp) # Use a different name than the package, to make sure
set_target_properties(mylib PROPERTIES PUBLIC_HEADER "include/pkg.h")
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
# Use non default mypkgConfig name
install(TARGETS mylib EXPORT mypkgConfig)
export(TARGETS mylib
NAMESPACE mypkg:: # to simulate a different name and see it works
FILE "${CMAKE_CURRENT_BINARY_DIR}/mypkgConfig.cmake"
)
install(EXPORT mypkgConfig
DESTINATION "${CMAKE_INSTALL_PREFIX}/pkg/cmake"
NAMESPACE mypkg::
)
有了这些信息,当执行 conan create 时
build()方法将构建包package()方法将调用cmake install,这将创建mypkgConfig.cmake文件它将在包文件夹
pkg/cmake/mypkgConfig.cmake文件中创建它将包含有关标头和创建
mypkg::mylib目标的足够信息。
请注意,配置文件的文件名、命名空间和目标也为 Conan 所不知,因此这也是使用者构建脚本应该知道的内容。
这足以拥有一个带有内部 mypkgConfig.cmake 文件的包,该文件可以被使用者使用。在此示例代码中,使用者只是 test_package/conanfile.py,但完全相同的规则也适用于任何任意使用者。
使用者 conanfile.py 不需要使用 CMakeDeps,只需要 generators = "CMakeToolchain"。请注意,仍然需要 CMakeToolchain 生成器,因为需要在 Conan 缓存中找到 mypkgConfig.cmake。 CMakeToolchain 生成的 conan_toolchain.cmake 文件包含这些定义的路径。
使用者的 CMakeLists.txt 将是标准的
find_package(mypkg CONFIG REQUIRED)
add_executable(example src/example.cpp)
target_link_libraries(example mypkg::mylib)
您可以使用以下命令验证它是否有效:
$ conan create .
======== Testing the package: Executing test ========
pkg/0.1 (test package): Running test()
pkg/0.1 (test package): RUN: Release\example
pkg/0.1: Hello World Release!
pkg/0.1: _M_X64 defined
pkg/0.1: MSVC runtime: MultiThreadedDLL
pkg/0.1: _MSC_VER1939
pkg/0.1: _MSVC_LANG201402
pkg/0.1: __cplusplus199711
pkg/0.1 test_package
重要注意事项¶
所呈现的方法有一个限制,它不适用于多配置 IDE。实施这种方法将不允许开发人员直接在诸如 Visual Studio 之类的 IDE 之间从 Release 切换到 Debug,反之亦然,并且需要 conan install 才能更改。对于单配置设置,这根本不是问题,但对于 VS 开发人员来说可能有点不方便。团队正在致力于 VS 插件,这可能有助于缓解此问题。原因是 CMake 的一个限制,find_package() 只能找到一个配置,并且由于此处放弃了 CMakeDeps,因此 Conan 无法避免此限制。
重要的是要知道,包作者和包的 CMakeLists.txt 负责正确管理到其他依赖项的传递性,并且在某些情况下这并非易事。如果未正确完成,包内的 xxx-config.cmake 文件可能会在其它的地方找到其传递依赖项,例如在系统中,而不是在传递的 Conan 包依赖项中。
最后,请记住,这些包将无法被其他构建系统(CMake 除外)使用。