如何获取处于可编辑模式的Conan包的CMake构建目录?
问题描述
我正尝试推导本地使用CMake构建的Conan包的构建目录,以便在该包处于可编辑模式时设置库目录(libdirs)和二进制目录(bindirs)等路径。理想逻辑如下:
def package_info(self): self.cpp_info.libs = [f"{self.name}"] # for Conan editable mode if not self.in_local_cache: self.cpp_info.includedirs = glob.glob("sources/*/include") self.cpp_info.libdirs = [f"{cmake_build_dir}/{self.settings.build_type}"] self.cpp_info.bindirs = [f"{cmake_build_dir}/{self.settings.build_type}"]
核心问题是找不到可靠的方式获取cmake_build_dir:
- 内部约定是把代码放在
sources目录(CMakeLists.txt所在处),在build/win执行CMake,但这只是约定,跨平台构建时需要适配实际结构 self.build_folder在package_info方法中为空- 尝试在
package_info里创建CMake对象(cmake = CMake(self))时报错,提示self没有replace属性 - 仅能通过
self.recipe_folder获取conanfile.py所在目录
请问有没有简便方法从Conan/CMake获取构建目录,还是只能强制开发者遵循目录约定?
可行解决方案
1. 利用Conan的cmake.build_directory配置(推荐)
Conan本身支持通过配置指定CMake构建目录,你可以在conanfile.py里读取这个配置值,同时允许用户自定义:
import os from conans import ConanFile, tools class YourPackage(ConanFile): settings = "os", "compiler", "build_type", "arch" def package_info(self): self.cpp_info.libs = [self.name] if not self.in_local_cache: # 读取CMake构建目录配置,默认用约定的build/{os}路径 cmake_build_dir = self.conf.get("tools.cmake.build:directory", default=f"build/{self.settings.os.lower()}") # 拼接build_type子目录 build_type_dir = os.path.join(cmake_build_dir, str(self.settings.build_type)) self.cpp_info.libdirs = [build_type_dir] self.cpp_info.bindirs = [build_type_dir] self.cpp_info.includedirs = tools.glob("sources/*/include")
用户如果有自定义构建目录,只需要在conan.conf或者命令行里设置:
conan config set tools.cmake.build:directory=my_custom_build_dir
或者在conan editable add时通过--conf传递:
conan editable add . my_package/1.0@user/channel --conf tools.cmake.build:directory=my_build
2. 从CMakeCache.txt反向推导
如果用户已经运行过CMake,构建目录里会有CMakeCache.txt,可以通过搜索这个文件来定位构建目录:
import os from conans import ConanFile, tools class YourPackage(ConanFile): settings = "os", "compiler", "build_type", "arch" def package_info(self): self.cpp_info.libs = [self.name] if not self.in_local_cache: # 从recipe_folder开始递归查找CMakeCache.txt cmake_cache = tools.find_recursive(self.recipe_folder, "CMakeCache.txt") if cmake_cache: cmake_build_dir = os.path.dirname(cmake_cache[0]) build_type_dir = os.path.join(cmake_build_dir, str(self.settings.build_type)) self.cpp_info.libdirs = [build_type_dir] self.cpp_info.bindirs = [build_type_dir] else: # 找不到时 fallback 到约定目录 default_build_dir = f"build/{self.settings.os.lower()}" build_type_dir = os.path.join(default_build_dir, str(self.settings.build_type)) self.cpp_info.libdirs = [build_type_dir] self.cpp_info.bindirs = [build_type_dir] self.cpp_info.includedirs = tools.glob("sources/*/include")
这种方式不需要用户额外配置,但前提是用户已经执行过CMake生成构建目录,第一次运行时可能还是需要依赖约定。
3. 在build方法中保存构建目录到文件
在build方法里把CMake的构建路径写入一个临时文件,然后在package_info里读取这个文件:
import os from conans import ConanFile, CMake, tools class YourPackage(ConanFile): settings = "os", "compiler", "build_type", "arch" generators = "cmake" def build(self): cmake = CMake(self) cmake.configure(source_folder="sources") cmake.build() # 把构建目录写入文件 build_dir_file = os.path.join(self.recipe_folder, ".conan_build_dir") with open(build_dir_file, "w") as f: f.write(cmake.build_directory) def package_info(self): self.cpp_info.libs = [self.name] if not self.in_local_cache: # 读取保存的构建目录 build_dir_file = os.path.join(self.recipe_folder, ".conan_build_dir") if os.path.exists(build_dir_file): with open(build_dir_file, "r") as f: cmake_build_dir = f.read().strip() else: # fallback 到约定目录 cmake_build_dir = f"build/{self.settings.os.lower()}" build_type_dir = os.path.join(cmake_build_dir, str(self.settings.build_type)) self.cpp_info.libdirs = [build_type_dir] self.cpp_info.bindirs = [build_type_dir] self.cpp_info.includedirs = tools.glob("sources/*/include")
这种方式能准确获取CMake实际使用的构建目录,但需要用户先执行过conan build或者在可编辑模式下先构建过包。
不推荐强制目录约定
强制约定虽然简单,但会限制用户的自定义构建流程,跨平台时也容易出问题,上面的方法都能在保留灵活性的同时解决问题。
内容的提问来源于stack exchange,提问作者MBraedley
相关产品推荐
相关产品推荐

