forked from electron/electron
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpatches-mtime-cache.py
184 lines (147 loc) · 5.42 KB
/
patches-mtime-cache.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
import argparse
import hashlib
import json
import os
import posixpath
import sys
import traceback
from lib.patches import patch_from_dir
def patched_file_paths(patches_config):
for patch_dir, repo in patches_config.items():
for line in patch_from_dir(patch_dir).split("\n"):
if line.startswith("+++"):
yield posixpath.join(repo, line[6:])
def generate_cache(patches_config):
mtime_cache = {}
for file_path in patched_file_paths(patches_config):
if file_path in mtime_cache:
# File may be patched multiple times, we don't need to
# rehash it since we are looking at the final result
continue
if not os.path.exists(file_path):
print("Skipping non-existent file:", file_path)
continue
with open(file_path, "rb") as f:
mtime_cache[file_path] = {
"sha256": hashlib.sha256(f.read()).hexdigest(),
"atime": os.path.getatime(file_path),
"mtime": os.path.getmtime(file_path),
}
return mtime_cache
def apply_mtimes(mtime_cache):
updates = []
for file_path, metadata in mtime_cache.items():
if not os.path.exists(file_path):
print("Skipping non-existent file:", file_path)
continue
with open(file_path, "rb") as f:
if hashlib.sha256(f.read()).hexdigest() == metadata["sha256"]:
updates.append(
[file_path, metadata["atime"], metadata["mtime"]]
)
# We can't atomically set the times for all files at once, but by waiting
# to update until we've checked all the files we at least have less chance
# of only updating some files due to an error on one of the files
for [file_path, atime, mtime] in updates:
os.utime(file_path, (atime, mtime))
def set_mtimes(patches_config, mtime):
mtime_cache = {}
for file_path in patched_file_paths(patches_config):
if file_path in mtime_cache:
continue
if not os.path.exists(file_path):
print("Skipping non-existent file:", file_path)
continue
mtime_cache[file_path] = mtime
for file_path in mtime_cache:
os.utime(file_path, (mtime_cache[file_path], mtime_cache[file_path]))
def main():
parser = argparse.ArgumentParser(
description="Make mtime cache for patched files"
)
subparsers = parser.add_subparsers(
dest="operation", help="sub-command help"
)
apply_subparser = subparsers.add_parser(
"apply", help="apply the mtimes from the cache"
)
apply_subparser.add_argument(
"--cache-file", required=True, help="mtime cache file"
)
apply_subparser.add_argument(
"--preserve-cache",
action="store_true",
help="don't delete cache after applying",
)
generate_subparser = subparsers.add_parser(
"generate", help="generate the mtime cache"
)
generate_subparser.add_argument(
"--cache-file", required=True, help="mtime cache file"
)
set_subparser = subparsers.add_parser(
"set", help="set all mtimes to a specific date"
)
set_subparser.add_argument(
"--mtime",
type=int,
required=True,
help="mtime to use for all patched files",
)
for subparser in [generate_subparser, set_subparser]:
subparser.add_argument(
"--patches-config",
type=argparse.FileType("r"),
required=True,
help="patches' config in the JSON format",
)
args = parser.parse_args()
if args.operation == "generate":
try:
# Cache file may exist from a previously aborted sync. Reuse it.
with open(args.cache_file, mode="r") as f:
json.load(f) # Make sure it's not an empty file
print("Using existing mtime cache for patches")
return 0
except Exception:
pass
try:
with open(args.cache_file, mode="w") as f:
mtime_cache = generate_cache(json.load(args.patches_config))
json.dump(mtime_cache, f, indent=2)
except Exception:
print(
"ERROR: failed to generate mtime cache for patches",
file=sys.stderr,
)
traceback.print_exc(file=sys.stderr)
return 0
elif args.operation == "apply":
if not os.path.exists(args.cache_file):
print("ERROR: --cache-file does not exist", file=sys.stderr)
return 0 # Cache file may not exist, fail more gracefully
try:
with open(args.cache_file, mode="r") as f:
apply_mtimes(json.load(f))
if not args.preserve_cache:
os.remove(args.cache_file)
except Exception:
print(
"ERROR: failed to apply mtime cache for patches",
file=sys.stderr,
)
traceback.print_exc(file=sys.stderr)
return 0
elif args.operation == "set":
answer = input(
"WARNING: Manually setting mtimes could mess up your build. "
"If you're sure, type yes: "
)
if answer.lower() != "yes":
print("Aborting")
return 0
set_mtimes(json.load(args.patches_config), args.mtime)
return 0
if __name__ == "__main__":
sys.exit(main())