-
Notifications
You must be signed in to change notification settings - Fork 878
/
Copy pathupdate_platform_patches.py
executable file
·184 lines (153 loc) · 6.59 KB
/
update_platform_patches.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!/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 _remove_files_with_dirs(root_dir, sorted_file_iter):
'''
Deletes a list of sorted files relative to root_dir, removing empty directories along the way
'''
past_parent = None
for partial_path in sorted_file_iter:
complete_path = Path(root_dir, partial_path)
try:
complete_path.unlink()
except FileNotFoundError:
get_logger().warning('Could not remove 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):
'''
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()))
# Remove prepended files with directories
_remove_files_with_dirs(platform_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 = dict()
# patch path -> inline comment for patch
path_inline_comments = dict()
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] = list()
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
if args.command == 'merge':
success = merge_platform_patches(args.platform_patches, repo_dir / 'patches')
elif args.command == 'unmerge':
success = unmerge_platform_patches(args.platform_patches)
else:
raise NotImplementedError(args.command)
if success:
return 0
return 1
if __name__ == '__main__':
sys.exit(main())