处理包中的源代码¶
在上一个教程部分中,我们为“Hello World”C++ 库创建了一个 Conan 包。我们使用 Conanfile 的 exports_sources
属性来声明库源代码的位置。当源代码与 Conanfile 位于同一文件夹时,此方法是定义源代码位置的最简单方法。但是,有时源代码存储在远程服务器上的存储库或文件中,而不是与 Conanfile 位于同一位置。在本节中,我们将修改我们之前创建的配方,添加一个 source()
方法,并解释如何
从远程存储库中存储的 *zip* 文件中检索源代码。
从 *git* 存储库的分支中检索源代码。
请先克隆源代码以重新创建此项目。您可以在 GitHub 上的examples2 仓库中找到它们。
$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/tutorial/creating_packages/handle_sources
项目的结构与上一个示例中的相同,但没有库源代码
.
├── CMakeLists.txt
├── conanfile.py
└── test_package
├── CMakeLists.txt
├── conanfile.py
└── src
└── example.cpp
来自远程存储库中存储的 *zip* 文件的源代码¶
让我们看看 *conanfile.py* 中的更改
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout
from conan.tools.files import get
class helloRecipe(ConanFile):
name = "hello"
version = "1.0"
...
# Binary configuration
settings = "os", "compiler", "build_type", "arch"
options = {"shared": [True, False], "fPIC": [True, False]}
default_options = {"shared": False, "fPIC": True}
def source(self):
# Please, be aware that using the head of the branch instead of an immutable tag
# or commit is a bad practice and not allowed by Conan
get(self, "https://github.com/conan-io/libhello/archive/refs/heads/main.zip",
strip_root=True)
def config_options(self):
if self.settings.os == "Windows":
del self.options.fPIC
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
cmake = CMake(self)
cmake.install()
def package_info(self):
self.cpp_info.libs = ["hello"]
如您所见,配方是相同的,但我们没有像以前那样声明 exports_sources
属性,即
exports_sources = "CMakeLists.txt", "src/*", "include/*"
我们用这些信息声明了一个 source()
方法
def source(self):
# Please, be aware that using the head of the branch instead of an immutable tag
# or commit is strongly discouraged, unsupported by Conan and likely to cause issues
get(self, "https://github.com/conan-io/libhello/archive/refs/heads/main.zip",
strip_root=True)
我们使用了 conan.tools.files.get() 工具,它将首先从我们作为参数传递的 URL 下载 *zip* 文件,然后解压缩它。请注意,我们传递了 strip_root=True
参数,以便如果所有解压缩的内容都在一个文件夹中,则所有内容都将移动到父文件夹(有关更多详细信息,请查看 conan.tools.files.unzip() 参考)。
警告
期望将来检索源代码会产生相同的结果。强烈不鼓励并支持使用可变的源来源,例如 git 中移动的引用(例如 HEAD 分支),或内容可能随时间变化的文件 URL。不遵循此做法将导致未定义的行为,很可能导致中断
zip 文件的内容与我们之前在 Conan 配方旁边的源代码相同,因此如果您执行 conan create,结果将与之前相同。
$ conan create .
...
-------- Installing packages ----------
Installing (downloading, building) binaries...
hello/1.0: Calling source() in /Users/user/.conan2/p/0fcb5ffd11025446/s/.
Downloading update_source.zip
hello/1.0: Unzipping 3.7KB
Unzipping 100 %
hello/1.0: Copying sources to build folder
hello/1.0: Building your package in /Users/user/.conan2/p/tmp/369786d0fb355069/b
...
-------- Testing the package: Running test() ----------
hello/1.0 (test package): Running test()
hello/1.0 (test package): RUN: ./example
hello/1.0: Hello World Release!
hello/1.0: __x86_64__ defined
hello/1.0: __cplusplus199711
hello/1.0: __GNUC__4
hello/1.0: __GNUC_MINOR__2
hello/1.0: __clang_major__13
hello/1.0: __clang_minor__1
hello/1.0: __apple_build_version__13160021
请检查突出显示的行,其中包含有关下载和解压缩操作的消息。
来自 *git* 存储库分支的源代码¶
现在,让我们修改 source()
方法,从 *git* 存储库而不是 *zip* 文件获取源代码。我们只显示相关部分
...
from conan.tools.scm import Git
class helloRecipe(ConanFile):
name = "hello"
version = "1.0"
...
def source(self):
git = Git(self)
git.clone(url="https://github.com/conan-io/libhello.git", target=".")
...
在这里,我们使用 conan.tools.scm.Git() 工具。Git
类实现了多种处理 *git* 存储库的方法。在这种情况下,我们调用 clone()
方法,将 https://github.com/conan-io/libhello.git 存储库克隆到默认分支,并使用与克隆源代码相同的文件夹而不是子文件夹(传递 target="."
参数)。
警告
如上所述,这只是一个简单的例子。Git()
的源来源也必须是不可变的。必须签出不可变标签或特定提交才能保证正确的行为。不允许使用存储库的 HEAD,这可能导致未定义的行为和中断。
要在存储库中签出提交或标签,我们使用 Git 工具的 checkout()
方法
def source(self):
git = Git(self)
git.clone(url="https://github.com/conan-io/libhello.git", target=".")
git.checkout("<tag> or <commit hash>")
有关 Git
类方法的更多信息,请查看 conan.tools.scm.Git() 参考。
请注意,也可以通过调用 self.run()
方法来运行其他命令。
使用 conandata.yml 文件¶
我们可以在与 conanfile.py
相同的文件夹中创建一个名为 conandata.yml
的文件。此文件将由 Conan 自动导出和解析,我们可以从配方中读取该信息。这很方便,例如,用于提取外部源存储库、zip 文件等的 URL。这是一个 conandata.yml
的示例
sources:
"1.0":
url: "https://github.com/conan-io/libhello/archive/refs/heads/main.zip"
sha256: "7bc71c682895758a996ccf33b70b91611f51252832b01ef3b4675371510ee466"
strip_root: true
"1.1":
url: ...
sha256: ...
配方不需要针对每个版本的代码进行修改。我们可以将指定版本的所有 keys
(url
、sha256
和 strip_root
)作为参数传递给 get
函数,在这种情况下,这使我们能够验证下载的 zip 文件是否具有正确的 sha256
。因此我们可以将 source 方法修改为如下:
def source(self):
get(self, **self.conan_data["sources"][self.version])
# Equivalent to:
# data = self.conan_data["sources"][self.version]
# get(self, data["url"], sha256=data["sha256"], strip_root=data["strip_root"])
conandata.yml
中适用于当前版本的信息也包含在序列化的配方信息中,请参阅 图信息 JSON 格式
另请参阅
捕获 Git SCM 源代码信息 而不是使用
exports_sources
复制源代码。