修补源代码¶
在本示例中,我们将了解如何修补源代码。有时这是必需的,特别是在为第三方库创建包时。如果需要(例如,应用安全补丁),构建系统脚本甚至库的源代码可能都需要修补。
请首先克隆源代码以重新创建此项目。您可以在 GitHub 上的 examples2 仓库 中找到它们
$ git clone https://github.com/conan-io/examples2.git
$ cd examples/tools/files/patches
使用 ‘replace_in_file’ 进行修补¶
修补文件最简单的方法是在您的 Recipe 中使用 replace_in_file
工具。它会在文件中搜索指定的字符串并用另一个字符串替换它。
在 source() 方法中¶
source()
方法只对所有配置调用一次(conan create 对于不同的设置/选项会有不同的调用),因此如果更改对于所有配置都是通用的,则应仅在 source()
方法中进行修补。
查看 conanfile.py
中的 source()
方法
import os
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout
from conan.tools.files import get, replace_in_file
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):
get(self, "https://github.com/conan-io/libhello/archive/refs/heads/main.zip", strip_root=True)
replace_in_file(self, os.path.join(self.source_folder, "src", "hello.cpp"), "Hello World", "Hello Friends!")
...
我们将字符串 "Hello World"
替换为 “Hello Friends!”。我们可以运行 conan create .
并验证替换是否已完成
$ conan create .
...
-------- Testing the package: Running test() --------
hello/1.0: Hello Friends! Release!
...
在 build() 方法中¶
在这种情况下,我们需要根据配置(self.settings, self.options...)应用不同的补丁,因此必须在 build()
方法中完成。让我们修改 Recipe,引入一个取决于 self.options.shared
的更改
class helloRecipe(ConanFile):
...
def source(self):
get(self, "https://github.com/conan-io/libhello/archive/refs/heads/main.zip", strip_root=True)
def build(self):
replace_in_file(self, os.path.join(self.source_folder, "src", "hello.cpp"),
"Hello World",
"Hello {} Friends!".format("Shared" if self.options.shared else "Static"))
cmake = CMake(self)
cmake.configure()
cmake.build()
...
如果我们用不同的 option.shared
调用 conan create
,我们可以查看输出
$ conan create .
...
hello/1.0: Hello Static Friends! Release!
...
$ conan create . -o shared=True
...
hello/1.0: Hello Shared Friends! Debug!
...
使用 “patch” 工具进行修补¶
如果您有一个补丁文件(两个文件版本之间的差异),您可以使用 conan.tools.files.patch
工具来应用它。关于在哪里应用补丁(source()
或 build()
方法)的规则是相同的。
我们有这个补丁文件,其中我们再次更改消息,使其显示 “Hello Patched World Release!”
--- a/src/hello.cpp
+++ b/src/hello.cpp
@@ -3,9 +3,9 @@
void hello(){
#ifdef NDEBUG
- std::cout << "hello/1.0: Hello World Release!\n";
+ std::cout << "hello/1.0: Hello Patched World Release!\n";
#else
- std::cout << "hello/1.0: Hello World Debug!\n";
+ std::cout << "hello/1.0: Hello Patched World Debug!\n";
#endif
// ARCHITECTURES
编辑 conanfile.py
,进行如下修改
导入
patch
工具。将
exports_sources
添加到补丁文件,以便在缓存中可用。调用
patch
工具。
import os
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout
from conan.tools.files import get, replace_in_file, patch
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}
exports_sources = "*.patch"
def source(self):
get(self, "https://github.com/conan-io/libhello/archive/refs/heads/main.zip", strip_root=True)
patch_file = os.path.join(self.export_sources_folder, "hello_patched.patch")
patch(self, patch_file=patch_file)
...
我们可以运行 “conan create” 并看到补丁已生效
$ conan create .
...
-------- Testing the package: Running test() --------
hello/1.0: Hello Patched World Release!
...
我们还可以使用 conandata.yml
(教程中介绍的),这样我们可以声明要应用于每个版本的补丁
patches:
"1.0":
- patch_file: "hello_patched.patch"
这是我们在 source()
方法中引入的更改
.. code-block:: python
def source(self):
get(self, "https://github.com/conan-io/libhello/archive/refs/heads/main.zip", strip_root=True)
patches = self.conan_data["patches"][self.version]
for p in patches:
patch_file = os.path.join(self.export_sources_folder, p["patch_file"])
patch(self, patch_file=patch_file)
有关更多详细信息,请查看 patch。
如果我们运行 conan create,补丁也会被应用
$ conan create .
...
-------- Testing the package: Running test() --------
hello/1.0: Hello Patched World Release!
...
使用 “apply_conandata_patches” 工具进行修补¶
上面的示例是可行的,但有点复杂。如果您遵循相同的 yml 结构(查看 apply_conandata_patches 以了解完整支持的 yml),您只需调用 apply_conandata_patches
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout
from conan.tools.files import get, apply_conandata_patches
class helloRecipe(ConanFile):
name = "hello"
version = "1.0"
...
def source(self):
get(self, "https://github.com/conan-io/libhello/archive/refs/heads/main.zip", strip_root=True)
apply_conandata_patches(self)
让我们检查一下补丁是否也被应用了
$ conan create .
...
-------- Testing the package: Running test() --------
hello/1.0: Hello Patched World Release!
...