常见问题解答

另请参阅

Conan 背后有一个很棒的社区,用户们在 Cpplang Slack 中互相帮助。请加入我们的 #conan 频道!

故障排除

错误:缺少预构建的包

当安装包时(使用 conan installconan create),你可能会遇到如下错误

ERROR: Missing binary: zlib/1.2.11:b1d267f77ddd5d10d06d2ecf5a6bc433fbb7eeed

zlib/1.2.11: WARN: Can't find a 'zlib/1.2.11' package binary 'b1d267f77ddd5d10d06d2ecf5a6bc433fbb7eeed' for the configuration:
[settings]
arch=x86_64
build_type=Release
compiler=apple-clang
compiler.cppstd=gnu11
compiler.libcxx=libc++
compiler.version=14
os=Macos
[options]
fPIC=True
shared=False

ERROR: Missing prebuilt package for 'zlib/1.2.11'. You can try:
    - List all available packages using 'conan list "{ref}:*" -r=remote'
    - Explain missing binaries: replace 'conan install ...' with 'conan graph explain ...'
    - Try to build locally from sources using the '--build=zlib/1.2.11' argument

More Info at 'https://docs.conan.org.cn/en/2/knowledge/faq.html#error-missing-prebuilt-package'

这意味着包配方 zlib/1.2.11 存在,但由于某种原因,没有适用于你当前设置或选项的预编译包。可能包的创建者根本没有构建和分享预构建的包,只上传了包配方,或者他们只为某些平台或编译器提供了包。例如,包创建者使用配方为 apple-clang 11 构建了包,而你正在使用 apple-clang 14。此外,你可能想检查你的 package ID mode,因为它可能会影响可用的包。

默认情况下,Conan 不会从源代码构建包。有几种方法可以解决这个错误

  • 你可以尝试从源代码为你当前设置构建包,通过参数指定一些构建策略,例如 --build zlib*--build missing。如果包配方和源代码适用于你的设置,你将在本地构建好二进制文件并可以使用。

  • 如果从源代码构建失败,并且你正在使用 conancenter 远程仓库,你可以在Conan 中心索引仓库中提交一个问题。

错误:无效设置

有时可能会发生,当你指定一个默认设置中不存在的设置时,你会收到类似这样的消息

$ conan install . -s compiler.version=4.19 ...

ERROR: Invalid setting '4.19' is not a valid 'settings.compiler.version' value.
Possible values are ['4.4', '4.5', '4.6', '4.7', '4.8', '4.9', '5.1', '5.2', '5.3', '5.4', '6.1', '6.2']

这不意味着 Conan 不支持该编译器版本,只是它不在当前的默认设置中。你可以在用户主文件夹 ~/.conan2/settings.yml 中找到一个设置文件,你可以修改、编辑、添加任何设置或任何值,如果需要还可以嵌套。请参阅settings.yml,了解如何自定义设置以按照你的意愿建模二进制文件。

只要你的团队或用户拥有相同的设置(settings.ymlsettings_user.yml 可以通过 conan config install 命令轻松共享),一切都会正常工作。settings.yml 文件只是一种机制,让用户对典型设置的拼写达成一致。此外,如果你认为某些设置对许多其他 Conan 用户有用,请以问题或拉取请求的形式提交,以便将其包含在未来的版本中。

一些内置的助手或集成,如 CMakeCMakeToolchain,可能不理解新添加的设置,不会使用它们,或者如果你给现有设置添加了一些新的意外值,它们甚至会失败。像 CMake 这样的助手是简单的工具,用于将 Conan 设置转换为相应的构建系统语法和命令行参数,因此它们可以扩展或替换为你自己的助手,以便处理你自己的私有设置。

错误:AuthenticationException:

如果 Conan 的 HTTP 请求中没有或使用了错误的认证凭据,可能会发生此错误。要获取更多信息,请尝试启用 HTTP 连接的调试级别

import http.client
http.client.HTTPConnection.debuglevel = 1

一个可能的错误源是 .netrc 文件,它受到 requests 库的支持

错误:在 Linux 和 Windows 中获取不同的修订版本

Git 默认在 Windows 系统中使用 CRLF 换行符检出文件,这会生成与使用 LF 换行符的 Linux 中的文件不同的文件。由于文件不同,Conan 配方的修订版本将不同于在其他平台(如 Linux)中计算出的修订版本,导致在其他修订版本中缺少相应的二进制文件。

Conan 不会以任何方式规范化或更改源文件,这不是它的责任,并且存在破坏的风险。源代码控制是更改文件的应用程序,因此在那里处理这个问题更为正确。需要指示 Git 使用相同的换行符进行检出。这可以通过几种方法完成,例如,通过在项目仓库中添加一个 .gitattributes 文件,内容如下

* text eol=lf

另一种方法是修改 .gitconfig 进行全局更改。现代 Windows 编辑器(甚至包括记事本)都可以完美地处理使用 LF 换行符的文件,不再需要更改换行符。

在 conanfile.py 配方中为依赖项定义选项不起作用

Conan 采用深度优先的方式扩展依赖关系图,这对于实现许多非常特殊的 C/C++ 传播逻辑(头文件、静态库和共享库、应用程序、工具依赖、测试依赖、冲突、覆盖等)非常重要。

这意味着当一个 conanfile.py 声明如下内容时

class MyPkg(ConanFile):
    name = "mypkg"
    version = "0.1"
    default_options = {"zlib/*:shared": True}
    # Or
    def requirements(self):
        self.requires("zlib/1.3", options={"shared": True})

它可能无法总是被遵循,并且 zlib 依赖项最终可能具有不同的 shared=False 选项值。这种在配方中定义依赖项选项值的做法仅在以下情况下有效:

  • 图中没有其他包依赖于 zlib

  • 图中有其他包依赖于 zlib,但 mypkg/0.1 是第一个被要求的依赖(依赖图中的第一个分支)。这意味着 requires = "mypkg/0.1", "zlib/1.3" 将会生效,并将 zlib 作为共享库,但 requires = "zlib/1.3", "mypkg/0.1" 会首先展开 zlib 的默认设置(即 shared=False),当计算 mypkg/0.1 时,再将 zlib 改为 shared=True 就为时已晚了。

如果某些配方在依赖项的某个选项下完全无法工作,建议在配方中定义一个 validate() 方法,以确保如果上游依赖项由于某种原因没有正确的选项值时会引发错误。

Conan 可能会在命令输出中显示一些(不保证穷尽)这些问题,请仔细阅读。

Options conflicts
    liba/0.1:myoption=1 (current value)
        libc/0.1->myoption=2
    It is recommended to define options values in profiles, not in recipes

总的来说,更建议在 profile 文件中定义选项值,而不是在配方中。配方中定义的选项总是优先于 profile 中定义的选项。

即使使用版本范围也会出现版本冲突

在安装依赖项时,可能会出现版本冲突的错误消息,例如

...
Version conflict: Conflict between math/1.0.1 and math/1.0 in the graph

这篇关于版本冲突的教程总结了同一包依赖项的不同版本如何在依赖图中发生冲突以及如何解决这些冲突。

然而,有些情况下冲突并不那么明显,例如当存在混合的版本范围和固定依赖项时,就像这样

def requirements(self):
  self.requires("libb/1.0")  # requires liba/[>=1.0 <2]
  self.requires("libc/1.0")  # requires liba/1.0

并且恰好 libb/1.0liba/[>=1.0 <2] 有一个传递性依赖,而 libc/1.0 要求 liba/1.0,并且存在 liba/1.1 或更高版本的包。在这种情况下,Conan 也可能抛出“版本冲突”错误。

根本原因是,在图中解析版本范围定义的所有可能约束的联合兼容性是一个已知的 NP 难问题,称为 SAT 求解器。在 Conan 中评估这个 NP 难问题中的每个假设都非常耗时,因为它通常需要遍历所有定义的远程仓库查找版本/修订版本,然后下载该版本/修订版本的压缩文件,解压,加载并进行 Python 解析和评估,最后进行所有的图计算处理,这包括在已经展开的图上进行完整的传播,以传播可能产生冲突的 C/C++ 需求特性。这在实践中会使问题难以处理,需要等待很多小时才能完成。

因此,Conan 不这样做,而是使用了一种“贪婪”算法,该算法不需要回溯,但仍会尽可能尝试协调版本范围和固定版本。关于这一点,最重要的一点是 Conan 实现了“深度优先”的图扩展,按照声明的顺序评估 requires。了解这一点有助于解决此冲突。在上面的例子中,错误发生是因为 libb/1.0 首先被展开,它找到了 liba/[>=1.0 <2] 的需求,并且之前没有找到对 liba 的其他约束,因此它自由地解析到最新的 liba/1.1。当之后展开 libc/1.0 时,它发现了对 liba/1.0 的需求,但这已经太晚了,因为它会与之前的 liba/1.1 冲突。回溯之前的假设是将问题转换为 NP 难的部分,所以算法在那里停止并引发冲突。

这可以通过交换 requires 的顺序来解决

def requirements(self):
  self.requires("libc/1.0")  # requires liba/1.0
  self.requires("libb/1.0")  # requires liba/[>=1.0 <2]

如果 libc/1.0 首先被展开,它会解析到 liba/1.0。当之后展开 libb/1.0 时,其传递性需求 liba/[>=1.0 <2] 可以通过之前的 libb/1.0 成功满足,因此可以成功解析该图。

一般最佳实践是

  • 对于同一个依赖项,尽量在所有地方使用相同的方法:要么在所有地方使用版本范围,要么在所有地方使用固定版本。

  • 保持版本一致。如果使用版本范围,尽量在所有地方使用相同的版本范围。

  • 优先声明使用固定版本的依赖项,而不是版本范围

  • 使用 conan graph info ... --format=html > graph.html 图形化交互输出以理解和导航冲突。