源代码打补丁

在本例中,我们将展示如何打补丁源代码。这有时是必要的,尤其是在您为第三方库创建包时。如果需要,例如应用安全补丁,可能需要在构建系统脚本或库的源代码本身中应用补丁。

请先克隆源代码以重新创建此项目。您可以在 GitHub 上的examples2 仓库中找到它们。

$ git clone https://github.com/conan-io/examples2.git
$ cd examples/tools/files/patches

使用 ‘replace_in_file’ 进行打补丁

打补丁文件的最简单方法是使用您配方中的 replace_in_file 工具。它会在文件中搜索指定的字符串并将其替换为另一个字符串。

在 source() 方法中

source() 方法仅为所有配置调用一次(不同的 settings/options 调用 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.settingsself.options…)应用不同的补丁,因此必须在 build() 方法中完成。让我们修改配方以引入一个取决于 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” 工具进行打补丁

如果您有一个补丁文件(两个文件版本之间的 diff),您可以使用 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

  1. 导入 patch 工具。

  2. 将补丁文件添加到 exports_sources,以便在缓存中可用。

  3. 调用 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!
...