source()

可以使用 source() 方法来检索必要的源代码,以便从源代码构建软件包,并在必要时将补丁应用于此类源代码。当从源代码构建软件包时,例如使用 conan createconan install  --build=pkg* 时,将调用此方法,但如果使用预编译的软件包二进制文件,则不会调用此方法。这意味着如果存在预编译的二进制文件,则不会下载源代码。

source() 方法可以实现不同的策略来检索源代码

  • 获取第三方库的源代码

    • 使用 Git(self).clone() 克隆 Git 仓库

    • 执行 download() + unzip() 或组合的 get() (内部执行下载 + 解压缩) 来下载 tarball、tgz 或 zip 压缩包。

  • 从其仓库中获取自身的源代码,其坐标已在 export() 方法的 conandata.yml 文件中捕获。这种策略将用于管理 conanfile.py 存在于软件包自身中的软件包的源代码,但由于某些原因,我们不想将源代码放入 recipe 中(例如,不分发我们的源代码,但能够分发我们的软件包二进制文件)。

source() 方法在 self.source_folder 中执行,当前工作目录将等于该文件夹(该值从 layout() 方法派生而来)。

source() 的实现可以使用方便的 get() 助手,或者使用其自身的机制或其他 Conan 助手来完成此任务,例如

import os
import shutil

from conan import ConanFile
from conan.tools.files import download, unzip, check_sha1


class PocoConan(ConanFile):
    name = "poco"
    version = "1.6.0"

    def source(self):
        zip_name = f"poco-{self.version}-release.zip"
        # Immutable source .zip
        download(self, f"https://github.com/pocoproject/poco/archive/poco-{self.version}-release.zip", zip_name)
        # Recommended practice, always check hashes of downloaded files
        check_sha1(self, zip_name, "8d87812ce591ced8ce3a022beec1df1c8b2fac87")
        unzip(self, zip_name)
        shutil.move(f"poco-poco-{self.version}-release", "poco")
        os.unlink(zip_name)

重要

请阅读关于 Python 3.14 破坏性更改和新的 tar 压缩包提取过滤器的 conan.tools.files.unzip() 中的注释。

如果补丁适用于所有可能的配置,则可以在 source() 方法中完成(并且应该完成)将补丁应用于下载的源代码。如下所述,不可能在 source() 方法中引入条件语句。如果补丁以文件形式存在,则必须将这些补丁与 recipe 一起导出,以便在从源代码构建时可以使用它们。

可以使用以下方法应用补丁:

  • 您自己的或 git 补丁实用程序

  • Conan 内置的 patch() 实用程序,用于逐个显式应用补丁

  • 应用 apply_conandata_patches() Conan 实用程序,以自动应用在 conandata.yml 文件中定义的遵循某些约定的所有补丁。

源代码缓存

一旦调用了 source() 方法,其结果将被缓存并在任何从源代码构建以及任何配置中重用。这意味着从 source() 方法检索源代码应该完全独立于配置。不可能在 settings 上实现条件语句,并且一般来说,任何尝试将任何条件逻辑应用于 source() 方法都是错误的。

def source(self):
    if self.settings.compiler == "gcc":  # ERROR, will raise
        # download some source

尝试通过使用其他机制(例如)来绕过 Conan 异常

def source(self):
    # Might work, but NOT recommended, try to avoid as much as possible
    if platform.system() == "Windows":
        # download something
    else:
        # download something different

如果在不进行任何交叉构建,并且不在不同的操作系统中重新收集源代码的情况下,可能表面上可以工作,但在其他情况下可能会出现问题。

为了完全安全,如果不同的配置需要不同的源代码,建议的方法是在 build() 方法中有条件地检索该代码。

强制检索源代码

当在用户文件夹中使用 recipe 时,可以轻松调用 source() 方法并强制检索源代码,这将根据 layout() 定义在同一用户文件夹中完成

$ conan source .

即使依赖项不是从源代码构建的,也可以使用 tools.build:download_source=True 配置来调用 source() 方法并强制在缓存中检索所有或某些依赖项的源代码。例如

$ conan graph info . -c tools.build:download_source=True

将计算依赖关系图,然后为图中的所有“host”软件包调用 source() 方法(由于默认配置是“host”配置,如果您还想要“build”上下文 tool_requires 的源代码,则可以使用 -c:b tools.build:download_source=True)。可以从 json 格式的输出中收集所有源文件夹,或自动化重新收集所有源代码,可以使用 deployer

同样,只需传递配置,就可以在其他 createinstall 命令中检索软件包的源代码。最后,由于配置也可以按软件包定义,因此使用 -c mypkg*:tools.build:download_source=True 将仅检索与 mypkg* 模式匹配的软件包的源代码。

请注意,tools.build:download_source=True 对处于 editable 模式的软件包没有任何影响。在这种情况下下载源代码可能会轻易覆盖和破坏本地开发人员对该代码的更改。必须在处于 editable 模式的软件包上使用 conan source 命令来下载源代码。

注意

最佳实践

  • source() 方法对于所有配置都应该是相同的,它不能依赖于任何配置。

  • source() 方法应检索不可变的源代码。使用某些分支名称、HEAD 或 URL 不可变且正在被覆盖的 tarball 是一种不良做法,将导致软件包损坏。使用 Git commit、冻结的 Git 发布标签或固定的和版本化的发布 tarball 是预期的输入。

  • 默认情况下应在 source() 方法中应用补丁,除非补丁是某个配置专有的,在这种情况下,它们可以在 build() 方法中应用。

  • source() 方法不应访问或操作与 self.source_folder 不同的其他文件夹中的文件。所有“exported”文件在调用它之前都复制到 self.source_folder

另请参阅

有关更多信息,请参阅 关于管理 recipe 源代码的教程