From 9061c3a52d355e478cbf83a6110b4cc3d2330ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marczewski?= Date: Fri, 10 Dec 2021 14:26:37 +0100 Subject: [PATCH] wrap: Add support for applying a list of patch files Co-authored-by: Xavier Claessens --- .../markdown/Wrap-dependency-system-manual.md | 33 +++++++++++++++++ docs/markdown/snippets/diff_files.md | 6 +++ mesonbuild/msubprojects.py | 1 + mesonbuild/wrap/wrap.py | 37 ++++++++++++++++++- .../meson.build | 7 ++++ .../0001-Change-foo-to-executable.patch | 33 +++++++++++++++++ .../0001-Change-return-value-to-43.patch | 21 +++++++++++ .../0002-Change-return-value-to-44.patch | 21 +++++++++++ .../subprojects/patchfile.wrap | 14 +++++++ 9 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 docs/markdown/snippets/diff_files.md create mode 100644 test cases/common/153 wrap file should not failed/subprojects/packagefiles/patchfile/0001-Change-foo-to-executable.patch create mode 100644 test cases/common/153 wrap file should not failed/subprojects/packagefiles/patchfile/0001-Change-return-value-to-43.patch create mode 100644 test cases/common/153 wrap file should not failed/subprojects/packagefiles/patchfile/0002-Change-return-value-to-44.patch create mode 100644 test cases/common/153 wrap file should not failed/subprojects/patchfile.wrap diff --git a/docs/markdown/Wrap-dependency-system-manual.md b/docs/markdown/Wrap-dependency-system-manual.md index eb5de1b13..314cd1f10 100644 --- a/docs/markdown/Wrap-dependency-system-manual.md +++ b/docs/markdown/Wrap-dependency-system-manual.md @@ -84,6 +84,8 @@ previously reserved to `wrap-file`: - `patch_directory` - *Since 0.55.0* Overlay directory, alternative to `patch_filename` in the case files are local instead of a downloaded archive. The directory must be placed in `subprojects/packagefiles`. +- `diff_files` - *Since 0.63.0* Comma-separated list of local diff files (see + [Diff files](#diff-files) below). ### Specific to wrap-file - `source_url` - download url to retrieve the wrap-file source archive @@ -147,6 +149,37 @@ wrap-file mode. When using wrap-git, the repository must contain all Meson build definitions. Since *0.55.0* Meson build patches are supported for any wrap modes, including wrap-git. +## Diff files + +*Since: 0.63.0* + +You can also provide local patch files in `diff` format. For historic reasons, +they are referred to as "diff files", since the "patch" name is already used for +overlay archives. + +The diff files are described by the `diff_files` property (a comma-separated +list), and must be available locally in the `subprojects/packagefiles` +directory. + +Meson will apply the diff files after extracting or cloning the project, and +after applying the overlay archive (`patch_*`). For this feature, the `patch` or +`git` command-line tool must be available. + +The diff files will be applied with `-p1`, i.e. treating the first path +component as a prefix to be stripped. This is the default for diffs produced by +Git. + +```ini +[wrap-file] +directory = libfoobar-1.0 + +source_url = https://example.com/foobar-1.0.tar.gz +source_filename = foobar-1.0.tar.gz +source_hash = 5ebeea0dfb75d090ea0e7ff84799b2a7a1550db3fe61eb5f6f61c2e971e57663 + +diff_files = libfoobar-1.0/0001.patch, libfoobar-1.0/0002.patch +``` + ## `provide` section *Since *0.55.0* diff --git a/docs/markdown/snippets/diff_files.md b/docs/markdown/snippets/diff_files.md new file mode 100644 index 000000000..3f425a2d6 --- /dev/null +++ b/docs/markdown/snippets/diff_files.md @@ -0,0 +1,6 @@ +## Diff files for wraps + +Wrap files can now define `diff_files`, a list of local patch files in `diff` +format. Meson will apply the diff files after extracting or cloning the project, +and after applying the overlay archive (`patch_*`). For this feature, the +`patch` or `git` command-line tool must be available. diff --git a/mesonbuild/msubprojects.py b/mesonbuild/msubprojects.py index 455b6e72d..8a58d7df5 100755 --- a/mesonbuild/msubprojects.py +++ b/mesonbuild/msubprojects.py @@ -205,6 +205,7 @@ class Runner: self.git_stash() self.git_output(['reset', '--hard', 'FETCH_HEAD']) self.wrap_resolver.apply_patch() + self.wrap_resolver.apply_diff_files() except GitException as e: self.log(' -> Could not reset', mlog.bold(self.repo_dir), 'to', mlog.bold(revision)) self.log(mlog.red(e.output)) diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index f54412276..00ef9a093 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -33,7 +33,7 @@ import textwrap from pathlib import Path from . import WrapMode from .. import coredata -from ..mesonlib import quiet_git, GIT, ProgressBar, MesonException, windows_proof_rmtree +from ..mesonlib import quiet_git, GIT, ProgressBar, MesonException, windows_proof_rmtree, Popen_safe from ..interpreterbase import FeatureNew from ..interpreterbase import SubProject from .. import mesonlib @@ -55,6 +55,8 @@ WHITELIST_SUBDOMAIN = 'wrapdb.mesonbuild.com' ALL_TYPES = ['file', 'git', 'hg', 'svn'] +PATCH = shutil.which('patch') + def whitelist_wrapdb(urlstr: str) -> urllib.parse.ParseResult: """ raises WrapException if not whitelisted subdomain """ url = urllib.parse.urlparse(urlstr) @@ -116,6 +118,7 @@ class PackageDefinition: self.values = {} # type: T.Dict[str, str] self.provided_deps = {} # type: T.Dict[str, T.Optional[str]] self.provided_programs = [] # type: T.List[str] + self.diff_files = [] # type: T.List[Path] self.basename = os.path.basename(fname) self.has_wrap = self.basename.endswith('.wrap') self.name = self.basename[:-5] if self.has_wrap else self.basename @@ -177,6 +180,15 @@ class PackageDefinition: raise WrapException(f'{self.wrap_section!r} is not a valid first section in {self.basename}') self.type = self.wrap_section[5:] self.values = dict(config[self.wrap_section]) + if 'diff_files' in self.values: + FeatureNew('Wrap files with diff_files', '0.63.0').use(self.subproject) + for s in self.values['diff_files'].split(','): + path = Path(s.strip()) + if path.is_absolute(): + raise WrapException('diff_files paths cannot be absolute') + if '..' in path.parts: + raise WrapException('diff_files paths cannot contain ".."') + self.diff_files.append(path) def parse_provide_section(self, config: configparser.ConfigParser) -> None: if config.has_section('provide'): @@ -366,6 +378,7 @@ class Resolver: raise WrapException(f'Unknown wrap type {self.wrap.type!r}') try: self.apply_patch() + self.apply_diff_files() except Exception: windows_proof_rmtree(self.dirname) raise @@ -628,6 +641,28 @@ class Resolver: raise WrapException(f'patch directory does not exist: {patch_dir}') self.copy_tree(src_dir, self.dirname) + def apply_diff_files(self) -> None: + for filename in self.wrap.diff_files: + mlog.log(f'Applying diff file "{filename}"') + path = Path(self.wrap.filesdir) / filename + if not path.exists(): + raise WrapException(f'Diff file "{path}" does not exist') + if PATCH: + cmd = [PATCH, '-f', '-p1', '-i', str(path)] + elif GIT: + # If the `patch` command is not available, fall back to `git + # apply`. The `--work-tree` is necessary in case we're inside a + # Git repository: by default, Git will try to apply the patch to + # the repository root. + cmd = [GIT, '--work-tree', '.', 'apply', '-p1', str(path)] + else: + raise WrapException('Missing "patch" or "git" commands to apply diff files') + + p, out, _ = Popen_safe(cmd, cwd=self.dirname, stderr=subprocess.STDOUT) + if p.returncode != 0: + mlog.log(out.strip()) + raise WrapException(f'Failed to apply diff file "{filename}"') + def copy_tree(self, root_src_dir: str, root_dst_dir: str) -> None: """ Copy directory tree. Overwrites also read only files. diff --git a/test cases/common/153 wrap file should not failed/meson.build b/test cases/common/153 wrap file should not failed/meson.build index 48d10688a..544850244 100644 --- a/test cases/common/153 wrap file should not failed/meson.build +++ b/test cases/common/153 wrap file should not failed/meson.build @@ -2,6 +2,10 @@ project('mainproj', 'c', default_options : ['wrap_mode=nodownload'], ) +if not find_program('patch', required : false).found() and not find_program('git', required : false).found() + error('MESON_SKIP_TEST: patch/git not found.') +endif + subproject('zlib') foo = subproject('foo') bar = subproject('bar') @@ -14,3 +18,6 @@ executable('grabprog2', files('src/subprojects/foo/prog2.c')) subdir('src') subproject('patchdir') + +exe = subproject('patchfile').get_variable('foo_exe') +test('test_foo', exe) diff --git a/test cases/common/153 wrap file should not failed/subprojects/packagefiles/patchfile/0001-Change-foo-to-executable.patch b/test cases/common/153 wrap file should not failed/subprojects/packagefiles/patchfile/0001-Change-foo-to-executable.patch new file mode 100644 index 000000000..a29fb6c2b --- /dev/null +++ b/test cases/common/153 wrap file should not failed/subprojects/packagefiles/patchfile/0001-Change-foo-to-executable.patch @@ -0,0 +1,33 @@ +From b79f6cc4a096f6c2888f73b947b652491885896a Mon Sep 17 00:00:00 2001 +From: Xavier Claessens +Date: Fri, 30 Nov 2018 14:13:47 -0500 +Subject: [PATCH] Change foo to executable + +--- + foo.c | 4 ++++ + meson.build | 2 +- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/foo.c b/foo.c +index 54f9119..468f033 100644 +--- a/foo.c ++++ b/foo.c +@@ -1,3 +1,7 @@ + int dummy_func(void) { + return 44; + } ++ ++int main(void) { ++ return dummy_func() == 44 ? 0 : 1; ++} +diff --git a/meson.build b/meson.build +index 318e81d..4a281d9 100644 +--- a/meson.build ++++ b/meson.build +@@ -1,2 +1,2 @@ + project('static lib patchdir', 'c') +-libfoo = static_library('foo', 'foo.c') ++foo_exe = executable('foo', 'foo.c') +-- +2.17.1 + diff --git a/test cases/common/153 wrap file should not failed/subprojects/packagefiles/patchfile/0001-Change-return-value-to-43.patch b/test cases/common/153 wrap file should not failed/subprojects/packagefiles/patchfile/0001-Change-return-value-to-43.patch new file mode 100644 index 000000000..49cf1e9dc --- /dev/null +++ b/test cases/common/153 wrap file should not failed/subprojects/packagefiles/patchfile/0001-Change-return-value-to-43.patch @@ -0,0 +1,21 @@ +From 7001dcc738e5ae7dfa8af20ed582b9a985804f72 Mon Sep 17 00:00:00 2001 +From: Xavier Claessens +Date: Fri, 30 Nov 2018 10:15:33 -0500 +Subject: [PATCH 1/2] Change return value to 43 + +--- + foo.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/foo.c b/foo.c +index 019f2ba..e4577b8 100644 +--- a/foo.c ++++ b/foo.c +@@ -1,3 +1,3 @@ + int dummy_func(void) { +- return 42; ++ return 43; + } +-- +2.17.1 + diff --git a/test cases/common/153 wrap file should not failed/subprojects/packagefiles/patchfile/0002-Change-return-value-to-44.patch b/test cases/common/153 wrap file should not failed/subprojects/packagefiles/patchfile/0002-Change-return-value-to-44.patch new file mode 100644 index 000000000..4794ceac7 --- /dev/null +++ b/test cases/common/153 wrap file should not failed/subprojects/packagefiles/patchfile/0002-Change-return-value-to-44.patch @@ -0,0 +1,21 @@ +From c2da2e490b09f2e251c7f4ef7c1240acee215fec Mon Sep 17 00:00:00 2001 +From: Xavier Claessens +Date: Fri, 30 Nov 2018 10:15:47 -0500 +Subject: [PATCH 2/2] Change return value to 44 + +--- + foo.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/foo.c b/foo.c +index e4577b8..54f9119 100644 +--- a/foo.c ++++ b/foo.c +@@ -1,3 +1,3 @@ + int dummy_func(void) { +- return 43; ++ return 44; + } +-- +2.17.1 + diff --git a/test cases/common/153 wrap file should not failed/subprojects/patchfile.wrap b/test cases/common/153 wrap file should not failed/subprojects/patchfile.wrap new file mode 100644 index 000000000..3d446fe74 --- /dev/null +++ b/test cases/common/153 wrap file should not failed/subprojects/patchfile.wrap @@ -0,0 +1,14 @@ +[wrap-file] +directory = foo-1.0-patchfile + +source_url = http://something.invalid +source_filename = foo-1.0.tar.xz +source_hash = 9ed8f67d75e43d3be161efb6eddf30dd01995a958ca83951ea64234bac8908c1 +lead_directory_missing = true + +patch_directory = foo-1.0 + +diff_files = + patchfile/0001-Change-return-value-to-43.patch, + patchfile/0002-Change-return-value-to-44.patch, + patchfile/0001-Change-foo-to-executable.patch