Skip to content

Commit

Permalink
extra patches path
Browse files Browse the repository at this point in the history
  • Loading branch information
memsharded committed Dec 23, 2024
1 parent ef5d9d5 commit 1cefc59
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 26 deletions.
81 changes: 58 additions & 23 deletions conan/tools/files/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import shutil

import patch_ng
import yaml

from conan.errors import ConanException
from conans.util.files import mkdir
from conan.internal.paths import DATA_YML
from conans.util.files import mkdir, load, save


class PatchLogHandler(logging.Handler):
Expand Down Expand Up @@ -122,27 +124,60 @@ def export_conandata_patches(conanfile):
if conanfile.conan_data is None:
raise ConanException("conandata.yml not defined")

patches = conanfile.conan_data.get('patches')
if patches is None:
conanfile.output.info("export_conandata_patches(): No patches defined in conandata")
return
conanfile_patches = conanfile.conan_data.get('patches')

if isinstance(patches, dict):
assert conanfile.version, "Can only be exported if conanfile.version is already defined"
entries = patches.get(conanfile.version, [])
if entries is None:
conanfile.output.warning(f"export_conandata_patches(): No patches defined for version {conanfile.version} in conandata.yml")
def _handle_patches(patches, patches_folder):
if patches is None:
conanfile.output.info("export_conandata_patches(): No patches defined in conandata")
return
elif isinstance(patches, list):
entries = patches
else:
raise ConanException("conandata.yml 'patches' should be a list or a dict {version: list}")
for it in entries:
patch_file = it.get("patch_file")
if patch_file:
src = os.path.join(conanfile.recipe_folder, patch_file)
dst = os.path.join(conanfile.export_sources_folder, patch_file)
if not os.path.exists(src):
raise ConanException(f"Patch file does not exist: '{src}'")
mkdir(os.path.dirname(dst))
shutil.copy2(src, dst)

if isinstance(patches, dict):
assert conanfile.version, "Can only be exported if conanfile.version is already defined"
entries = patches.get(conanfile.version, [])
if entries is None:
conanfile.output.warning("export_conandata_patches(): No patches defined for "
f"version {conanfile.version} in conandata.yml")
return
elif isinstance(patches, list):
entries = patches
else:
raise ConanException("conandata.yml 'patches' should be a list or a dict "
"{version: list}")
for it in entries:
patch_file = it.get("patch_file")
if patch_file:
src = os.path.join(patches_folder, patch_file)
dst = os.path.join(conanfile.export_sources_folder, patch_file)
if not os.path.exists(src):
raise ConanException(f"Patch file does not exist: '{src}'")
mkdir(os.path.dirname(dst))
shutil.copy2(src, dst)
return entries

_handle_patches(conanfile_patches, conanfile.recipe_folder)

# NOTE: This only works after drim_conandata, it assumes versions are gone now in the cache
extra_path = conanfile.conf.get("core.sources.patch:extra_path")
if extra_path:
if not os.path.isdir(extra_path):
raise ConanException(f"Patches extra path '{extra_path}' does not exist")
pkg_path = os.path.join(extra_path, conanfile.name)
if not os.path.isdir(pkg_path):
return
data_path = os.path.join(pkg_path, DATA_YML)
try:
data = yaml.safe_load(load(data_path))
except Exception as e:
raise ConanException("Invalid yml format at {}: {}".format(data_path, e))
data = data or {}
conanfile.output.info(f"Applying extra patches 'core.sources.patch:extra_path': {data_path}")
new_patches = _handle_patches(data.get('patches'), pkg_path)

# Update the CONANDATA.YML
conanfile_patches = conanfile_patches or []
conanfile_patches.extend(new_patches)
conanfile.conan_data['patches'] = conanfile_patches
# Saving in the EXPORT folder
conanfile_data_path = os.path.join(conanfile.export_folder, DATA_YML)
new_conandata_yml = yaml.safe_dump(conanfile.conan_data, default_flow_style=False)
save(conanfile_data_path, new_conandata_yml)
1 change: 1 addition & 0 deletions conans/model/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"core.sources:download_urls": "List of URLs to download backup sources from",
"core.sources:upload_url": "Remote URL to upload backup sources to",
"core.sources:exclude_urls": "URLs which will not be backed up",
"core.sources.patch:extra_path": "Extra path to search for patch files for conan create",
# Package ID
"core.package_id:default_unknown_mode": "By default, 'semver_mode'",
"core.package_id:default_non_embed_mode": "By default, 'minor_mode'",
Expand Down
50 changes: 47 additions & 3 deletions test/functional/tools/test_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@

from conan.test.assets.genconanfile import GenConanfile
from conan.test.utils.file_server import TestFileServer
from conan.test.utils.test_files import temp_folder
from conan.test.utils.tools import TestClient
from conans.util.files import save
from conans.util.files import save, load


class MockPatchset:
Expand Down Expand Up @@ -365,7 +366,7 @@ def build(self):
assert mock_patch_ng.apply_args[1:] == (0, False)


def test_export_conandata_patches(mock_patch_ng):
def test_export_conandata_patches():
conanfile = textwrap.dedent("""
import os
from conan import ConanFile
Expand Down Expand Up @@ -402,7 +403,7 @@ def source(self):
# wrong patches
client.save({"conandata.yml": "patches: 123"})
client.run("create .", assert_error=True)
assert "conandata.yml 'patches' should be a list or a dict" in client.out
assert "conandata.yml 'patches' should be a list or a dict" in client.out

# No patch found
client.save({"conandata.yml": conandata_yml})
Expand Down Expand Up @@ -447,3 +448,46 @@ def build(self):
client.save({"conandata.yml": conandata_yml, "conanfile.py": conanfile})
client.run("create .")
assert "No patches defined for version 1.0 in conandata.yml" in client.out


def test_export_conandata_patches_extra_origin():
conanfile = textwrap.dedent("""
import os
from conan import ConanFile
from conan.tools.files import export_conandata_patches, load, trim_conandata
class Pkg(ConanFile):
name = "mypkg"
version = "1.0"
def export(self):
trim_conandata(self)
def layout(self):
self.folders.source = "source_subfolder"
def export_sources(self):
export_conandata_patches(self)
def source(self):
self.output.info(load(self, os.path.join(self.export_sources_folder,
"patches/mypatch.patch")))
""")
client = TestClient(light=True)
patches_folder = temp_folder()
conandata_yml = textwrap.dedent("""
patches:
"1.0":
- patch_file: "patches/mypatch.patch"
""")
save(os.path.join(patches_folder, "mypkg", "conandata.yml"), conandata_yml)
save(os.path.join(patches_folder, "mypkg", "patches", "mypatch.patch"), "mypatch!!!")

client.save({"conanfile.py": conanfile,
"conandata.yml": "patches:"})
client.run(f'create . -cc core.sources.patch:extra_path="{patches_folder}"')
assert "mypkg/1.0: Applying extra patches" in client.out
assert "mypkg/1.0: mypatch!!!" in client.out

conandata = load(client.exported_layout().conandata())
assert "patch_file: patches/mypatch.patch" in conandata

0 comments on commit 1cefc59

Please sign in to comment.