FAQ

另请参阅

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

错误:缺少预构建包

在安装软件包(使用 conan installconan create)时,您可能会遇到类似以下的错误:

ERROR: Missing binary: zlib/1.3.1:b1d267f77ddd5d10d06d2ecf5a6bc433fbb7eeed

zlib/1.3.1: WARN: Can't find a 'zlib/1.3.1' 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.3.1'. 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.3.1' argument

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

这意味着软件包配方 zlib/1.3.1 存在,但由于某种原因,您的当前设置或选项没有对应的预编译软件包。也许软件包创建者根本没有构建和共享预构建软件包,只上传了软件包配方,或者他们只为某些平台或编译器提供软件包。例如,软件包创建者为 apple-clang 11 从配方构建了软件包,而您使用的是 apple-clang 14。您还应该检查您的 软件包 ID 模式,因为它可能会影响可用的软件包。

默认情况下,Conan 不会从源代码构建软件包。有几种可能性可以解决此错误:

  • 您可以尝试从源代码为您的设置构建软件包,方法是作为参数指示某种构建策略,例如 --build zlib*--build missing。如果软件包配方和源代码适用于您的设置,您将拥有本地构建好的二进制文件并可以使用。

  • 如果从源代码构建失败,并且您使用的是 conancenter 远程仓库,您可以在 Conan Center Index 仓库 中打开一个 issue。

错误:无效设置

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

$ 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 用户很有用,请将其作为 issue 或 pull request 提交,以便在未来的版本中包含。

某些内置的辅助工具或集成(如 CMakeCMakeToolchain)可能无法理解新添加的设置,或者即使您在现有设置中添加了一些新的意外值,它们也可能不使用这些设置甚至失败。像 CMake 这样的辅助工具只是用于将 Conan 设置转换为相应的构建系统语法和命令行参数的简单实用程序,因此它们可以被扩展或替换为您自己的处理私有设置的工具。

错误:AuthenticationException:

当 Conan 的 HTTP 请求中没有或身份验证凭据不正确时,可能会发生此错误。要获取更多信息,请尝试启用 HTTP 连接的调试级别。

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

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

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

Git(默认情况下)在 Windows 系统上会使用 CRLF 行尾符检出文件,这会在 Linux 上产生不同的文件,因为 Linux 文件会使用 LF 行尾符。由于文件不同,Conan 配方修订版本将与在 Linux 等其他平台上计算出的修订版本不同,从而导致在其他修订版本中找不到相应的二进制文件。

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

* text eol=lf

另一种方法是更改 .gitconfig 以全局更改。现代编辑器(甚至记事本)在 Windows 上也能完美处理带有 LF 的文件,不再需要更改行尾符。

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

Conan 以深度优先的方式展开依赖图,这对于实现许多非常特殊的 C/C++ 传播逻辑(头文件、静态和共享库、应用程序、tool-requires、test-requires、冲突、覆盖等)非常重要。

这意味着当一个 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 可能会在 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 files)中定义选项值比在配方中定义更受推荐。配方中定义的选项始终具有低于配置文件中定义的选项的优先级。

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

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

...
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-hard 问题,称为 SAT-solver。在 Conan 中评估此 NP-hard 问题中的每个假设都非常昂贵,因为它通常需要查找所有已定义的远程仓库中的版本/修订版本,然后下载该版本/修订版本的压缩文件,解压缩,加载并进行 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-hard 的“回溯”部分,因此算法在此停止并引发冲突。

这可以通过简单地交换 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 的图形化交互式输出,以理解和导航冲突。

Conan 将输出重定向到 stderr

正如 命令参考 中所述,Conan 设计上将日志信息重定向到标准错误输出 (stderr),而命令的实际结果则发送到标准输出 (stdout)。

这样做是为了让用户能够轻松地将 Conan 命令的输出重定向到文件或其他进程,而不会将日志信息与实际结果混合。

例如,运行类似这样的命令:

$ conan graph info --requires=zlib/1.3.1 --format=json > graph.json

...

======== Computing dependency graph ========
Graph root
    cli
Requirements
    zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76 - Cache

======== Computing necessary packages ========
Connecting to remote 'conancenter' anonymously
Requirements
    zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76:dbb40f41e6e9a5c4a9a1fd8d9e6ccf6d92676c92#8976086f07d37e3f6288e2fccf9650ae - Cache

将创建一个名为 graph.json 的文件,其中包含命令的 JSON 输出,但不包含任何日志信息,日志信息将打印到控制台。

请注意,这种方法在许多命令行工具中很常见,例如 gitcurl,并且并非 Conan 特有。

使用 conan create 创建的(工具)软件包缺少二进制文件

有时会出现这种情况:一个打算被其他软件包用作 tool_requires 的软件包,其配方包含一个 test_package 文件夹,该文件夹要求使用该工具,例如:

test_package/conanfile.py
from conan import ConanFile


class secure_scannerTestConan(ConanFile):
    settings = "os", "compiler", "build_type", "arch"

    def build_requirements(self):
        self.tool_requires(self.tested_reference_str)

然后,用户在创建软件包时使用:

$ conan create
...
mytool/0.1: Package created successfully

======== Launching test_package ========
...
======== Computing necessary packages ========
...
ERROR: Missing binary: mytool/0.1

conan create 命令,会在 test_package 步骤中遇到“缺少二进制文件”错误。这是怎么可能的,即使软件包的二进制文件在几行之前就被创建了?

默认情况下,conan create 命令使用“host”上下文创建软件包,并使用“host”配置文件。但是,如果我们要创建的软件包打算用作工具,即 tool_requires,那么它需要为“build”上下文构建。

如果出于任何原因,“host”和“build”上下文不相同,那么在初始软件包创建中构建的二进制文件将是“host”上下文的二进制文件,然后 test_package 将要求它作为 tool_requires(),要求的是“build”上下文中的二进制文件,而该二进制文件将缺失,因为它尚未被构建。

这在一个交叉编译的例子中很明显,例如,在 Windows 笔记本上为 RaspberryPI 构建一个带有交叉编译器的二进制文件。“build”上下文将是“Windows”上下文,而“host”上下文将是“RaspberryPI”上下文。为“mytool/0.1”运行普通的 conan create .,默认情况下将为“RaspberryPI”创建一个二进制文件!因此,当稍后 test_package 想将其用作 tool_requires() 时,它将查找“Windows”二进制文件,因为它需要在当前的 Windows 机器(“build”上下文)上运行。

--build-require 参数指定了这一点。当提供此参数时,当前的配方二进制文件将被构建以在“build”上下文中运行。

注意

当为打算用作 tool_requires 的软件包执行 conan create 时,请始终指定 conan create ... --build-require 参数。