mirror of
https://github.com/morgan9e/helium
synced 2026-04-14 00:14:20 +09:00
Instead of deleting copies of generic patches when unmerging them from platform patches, move them back into the shared repository. This makes it much more pleasant and ergonomic to work on patches as a merged series, and allows to e.g. refresh existing patches without having to move them back to the original folder manually.
189 lines
6.8 KiB
Python
Executable File
189 lines
6.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
"""
|
|
Utility to ease the updating of platform patches against ungoogled-chromium's patches
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import shutil
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'utils'))
|
|
from _common import ENCODING, get_logger
|
|
from patches import merge_patches
|
|
|
|
sys.path.pop(0)
|
|
|
|
_SERIES = 'series'
|
|
_SERIES_ORIG = 'series.orig'
|
|
_SERIES_PREPEND = 'series.prepend'
|
|
_SERIES_MERGED = 'series.merged'
|
|
|
|
|
|
def merge_platform_patches(platform_patches_dir, prepend_patches_dir):
|
|
'''
|
|
Prepends prepend_patches_dir into platform_patches_dir
|
|
|
|
Returns True if successful, False otherwise
|
|
'''
|
|
if not (platform_patches_dir / _SERIES).exists():
|
|
get_logger().error('Unable to find platform series file: %s',
|
|
platform_patches_dir / _SERIES)
|
|
return False
|
|
|
|
# Make series.orig file
|
|
shutil.copyfile(str(platform_patches_dir / _SERIES), str(platform_patches_dir / _SERIES_ORIG))
|
|
|
|
# Make series.prepend
|
|
shutil.copyfile(str(prepend_patches_dir / _SERIES), str(platform_patches_dir / _SERIES_PREPEND))
|
|
|
|
# Merge patches
|
|
merge_patches([prepend_patches_dir], platform_patches_dir, prepend=True)
|
|
(platform_patches_dir / _SERIES).replace(platform_patches_dir / _SERIES_MERGED)
|
|
|
|
return True
|
|
|
|
|
|
def _dir_empty(path):
|
|
'''
|
|
Returns True if the directory exists and is empty; False otherwise
|
|
'''
|
|
try:
|
|
next(os.scandir(str(path)))
|
|
except StopIteration:
|
|
return True
|
|
except FileNotFoundError:
|
|
pass
|
|
return False
|
|
|
|
|
|
def _rename_files_with_dirs(root_dir, source_dir, sorted_file_iter):
|
|
'''
|
|
Moves a list of sorted files back to their original location,
|
|
removing empty directories along the way
|
|
'''
|
|
past_parent = None
|
|
for partial_path in sorted_file_iter:
|
|
complete_path = Path(root_dir, partial_path)
|
|
complete_source_path = Path(source_dir, partial_path)
|
|
try:
|
|
complete_path.rename(complete_source_path)
|
|
except FileNotFoundError:
|
|
get_logger().warning('Could not move prepended patch: %s', complete_path)
|
|
if past_parent != complete_path.parent:
|
|
while past_parent and _dir_empty(past_parent):
|
|
past_parent.rmdir()
|
|
past_parent = past_parent.parent
|
|
past_parent = complete_path.parent
|
|
# Handle last path's directory
|
|
while _dir_empty(complete_path.parent):
|
|
complete_path.parent.rmdir()
|
|
complete_path = complete_path.parent
|
|
|
|
|
|
def unmerge_platform_patches(platform_patches_dir, prepend_patches_dir):
|
|
'''
|
|
Undo merge_platform_patches(), adding any new patches from series.merged as necessary
|
|
|
|
Returns True if successful, False otherwise
|
|
'''
|
|
if not (platform_patches_dir / _SERIES_PREPEND).exists():
|
|
get_logger().error('Unable to find series.prepend at: %s',
|
|
platform_patches_dir / _SERIES_PREPEND)
|
|
return False
|
|
prepend_series = set(
|
|
filter(len,
|
|
(platform_patches_dir / _SERIES_PREPEND).read_text(encoding=ENCODING).splitlines()))
|
|
|
|
# Move prepended files back to original location, preserving changes
|
|
_rename_files_with_dirs(platform_patches_dir, prepend_patches_dir, sorted(prepend_series))
|
|
|
|
# Determine positions of blank spaces in series.orig
|
|
if not (platform_patches_dir / _SERIES_ORIG).exists():
|
|
get_logger().error('Unable to find series.orig at: %s', platform_patches_dir / _SERIES_ORIG)
|
|
return False
|
|
orig_series = (platform_patches_dir / _SERIES_ORIG).read_text(encoding=ENCODING).splitlines()
|
|
# patch path -> list of lines after patch path and before next patch path
|
|
path_comments = {}
|
|
# patch path -> inline comment for patch
|
|
path_inline_comments = {}
|
|
previous_path = None
|
|
for partial_path in orig_series:
|
|
if not partial_path or partial_path.startswith('#'):
|
|
if partial_path not in path_comments:
|
|
path_comments[previous_path] = []
|
|
path_comments[previous_path].append(partial_path)
|
|
else:
|
|
path_parts = partial_path.split(' #', maxsplit=1)
|
|
previous_path = path_parts[0]
|
|
if len(path_parts) == 2:
|
|
path_inline_comments[path_parts[0]] = path_parts[1]
|
|
|
|
# Apply changes on series.merged into a modified version of series.orig
|
|
if not (platform_patches_dir / _SERIES_MERGED).exists():
|
|
get_logger().error('Unable to find series.merged at: %s',
|
|
platform_patches_dir / _SERIES_MERGED)
|
|
return False
|
|
new_series = filter(len, (platform_patches_dir /
|
|
_SERIES_MERGED).read_text(encoding=ENCODING).splitlines())
|
|
new_series = filter((lambda x: x not in prepend_series), new_series)
|
|
new_series = list(new_series)
|
|
series_index = 0
|
|
while series_index < len(new_series):
|
|
current_path = new_series[series_index]
|
|
if current_path in path_inline_comments:
|
|
new_series[series_index] = current_path + ' #' + path_inline_comments[current_path]
|
|
if current_path in path_comments:
|
|
new_series.insert(series_index + 1, '\n'.join(path_comments[current_path]))
|
|
series_index += 1
|
|
series_index += 1
|
|
|
|
# Write series file
|
|
with (platform_patches_dir / _SERIES).open('w', encoding=ENCODING) as series_file:
|
|
series_file.write('\n'.join(new_series))
|
|
series_file.write('\n')
|
|
|
|
# All other operations are successful; remove merging intermediates
|
|
(platform_patches_dir / _SERIES_MERGED).unlink()
|
|
(platform_patches_dir / _SERIES_ORIG).unlink()
|
|
(platform_patches_dir / _SERIES_PREPEND).unlink()
|
|
|
|
return True
|
|
|
|
|
|
def main():
|
|
"""CLI Entrypoint"""
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('command',
|
|
choices=('merge', 'unmerge'),
|
|
help='Merge or unmerge ungoogled-chromium patches with platform patches')
|
|
parser.add_argument('platform_patches',
|
|
type=Path,
|
|
help='The path to the platform patches in GNU Quilt format to merge into')
|
|
args = parser.parse_args()
|
|
|
|
repo_dir = Path(__file__).resolve().parent.parent
|
|
|
|
success = False
|
|
prepend_patches_dir = repo_dir / 'patches'
|
|
if args.command == 'merge':
|
|
success = merge_platform_patches(args.platform_patches, prepend_patches_dir)
|
|
elif args.command == 'unmerge':
|
|
success = unmerge_platform_patches(args.platform_patches, prepend_patches_dir)
|
|
else:
|
|
raise NotImplementedError(args.command)
|
|
|
|
if success:
|
|
return 0
|
|
return 1
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|