mirror of
https://github.com/imputnet/helium.git
synced 2026-08-20 00:57:32 +02:00
a50bc6fddd
now if we add some patch to the middle of generic series, it should be moved to generic repo instead of staying at the top of the series file
226 lines
8.3 KiB
Python
Executable File
226 lines
8.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright (c) 2026 The Helium Authors
|
|
# You can use, redistribute, and/or modify this source code under
|
|
# the terms of the GPL-3.0 license that can be found in the LICENSE file.
|
|
|
|
# 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.ungoogled_chromium 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_source_path.parent.mkdir(parents=True, exist_ok=True)
|
|
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 _series_path(series_line):
|
|
return series_line.split(' #', maxsplit=1)[0]
|
|
|
|
|
|
def _parse_series_metadata(series_lines):
|
|
paths = set()
|
|
# 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 series_lines:
|
|
if not partial_path or partial_path.startswith('#'):
|
|
if previous_path not in path_comments:
|
|
path_comments[previous_path] = []
|
|
path_comments[previous_path].append(partial_path)
|
|
else:
|
|
previous_path = _series_path(partial_path)
|
|
paths.add(previous_path)
|
|
path_parts = partial_path.split(' #', maxsplit=1)
|
|
if len(path_parts) == 2:
|
|
path_inline_comments[previous_path] = path_parts[1]
|
|
return paths, path_comments, path_inline_comments
|
|
|
|
|
|
def _restore_series_metadata(series, path_comments, path_inline_comments):
|
|
series_index = 0
|
|
while series_index < len(series):
|
|
current_path = series[series_index]
|
|
clean_path = _series_path(current_path)
|
|
if clean_path in path_inline_comments:
|
|
series[series_index] = clean_path + ' #' + path_inline_comments[clean_path]
|
|
if clean_path in path_comments:
|
|
series.insert(series_index + 1, '\n'.join(path_comments[clean_path]))
|
|
series_index += 1
|
|
series_index += 1
|
|
|
|
|
|
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, prepend_path_comments, prepend_path_inline_comments = _parse_series_metadata(
|
|
(platform_patches_dir / _SERIES_PREPEND).read_text(encoding=ENCODING).splitlines())
|
|
|
|
# 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_paths, path_comments, path_inline_comments = _parse_series_metadata(
|
|
(platform_patches_dir / _SERIES_ORIG).read_text(encoding=ENCODING).splitlines())
|
|
|
|
# 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
|
|
merged_series = filter(len, (platform_patches_dir /
|
|
_SERIES_MERGED).read_text(encoding=ENCODING).splitlines())
|
|
generic_series = []
|
|
new_series = []
|
|
in_platform_series = False
|
|
for current_path in merged_series:
|
|
clean_path = _series_path(current_path)
|
|
if clean_path in orig_series_paths:
|
|
in_platform_series = True
|
|
if clean_path in prepend_series or not in_platform_series:
|
|
generic_series.append(current_path)
|
|
else:
|
|
new_series.append(current_path)
|
|
|
|
# Move prepended files back to original location, preserving changes
|
|
# including any new patches added before the platform patch block.
|
|
_rename_files_with_dirs(
|
|
platform_patches_dir, prepend_patches_dir,
|
|
sorted(prepend_series.union(_series_path(patch_path) for patch_path in generic_series)))
|
|
|
|
_restore_series_metadata(generic_series, prepend_path_comments, prepend_path_inline_comments)
|
|
_restore_series_metadata(new_series, path_comments, path_inline_comments)
|
|
|
|
# Write series file
|
|
with (prepend_patches_dir / _SERIES).open('w', encoding=ENCODING) as series_file:
|
|
series_file.write('\n'.join(generic_series))
|
|
series_file.write('\n')
|
|
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())
|