source()

source() 方法可用于获取构建包所需的源代码,并在必要时对此源代码应用补丁。当包从源代码构建时,例如使用 conan createconan install  --build=pkg* 命令时,会调用此方法;但如果使用的是预编译的二进制包,则不会调用此方法。这意味着如果存在预编译的二进制文件,源代码将不会被下载。

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

  • 获取第三方库的源代码

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

    • 执行 download() + unzip() 或组合使用 get()(内部执行 download + unzip)来下载 tarball、tgz 或 zip 存档。

  • 从包自身的仓库中获取其源代码,该源代码的坐标已在 export() 方法中记录在 conandata.yml 文件里。这种策略适用于 conanfile.py 文件存在于包本身中,但出于某种原因我们不想将源代码放入配方的情况(例如,不分发我们的源代码,但能够分发我们的包二进制文件)。

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)

重要提示

请阅读 conan.tools.files.unzip() 中的注意事项,该注意事项涉及 Python 3.14 的重大更改以及新的 tar 存档提取过滤器。

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

可以使用以下方式应用补丁

  • 你自己的或 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() 方法中按条件获取该代码。

强制获取源代码

在使用用户文件夹中的配方时,可以轻松调用 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”的文件在调用 source() 方法之前都会复制到 self.source_folder 中。

另请参阅

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