-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathupdate-blend2d-code.py
More file actions
259 lines (179 loc) · 7.2 KB
/
Copy pathupdate-blend2d-code.py
File metadata and controls
259 lines (179 loc) · 7.2 KB
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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# Requires Python 3.10
import argparse
import glob
import os
import shutil
import subprocess
import sys
import stat
from typing import Callable
from pathlib import Path
from os import PathLike
from utils.cli.cli_printing import print_stage_name
from utils.cli.console_color import ConsoleColor
# List of glob patterns to remove from the Blend2D/ASMJIT folders after code is
# initially cloned.
rmFilePatterns: list[str] = [
"**/*_test.cpp",
"**/*.natvis",
]
# -----
# Utility functions
# -----
def echo_call(bin: PathLike | str, *args: str):
print(">", str(bin), *args)
def run(
bin_name: str,
*args: str,
cwd: str | Path | None = None,
echo: bool = True,
silent: bool = False
):
if echo:
echo_call(bin_name, *args)
if silent:
subprocess.check_output([bin_name] + list(args), cwd=cwd)
else:
subprocess.check_call([bin_name] + list(args), cwd=cwd)
def git(command: str, *args: str, cwd: str | Path | None = None, echo: bool = True):
run("git", command, *args, cwd=cwd, echo=echo)
def git_output(*args: str, cwd: str | None = None, echo: bool = True) -> str:
if echo:
echo_call("git", *args)
return subprocess.check_output(["git"] + list(args), cwd=cwd).decode("UTF8")
def path(root: PathLike | str, *args: PathLike | str) -> Path:
return Path(root).joinpath(*args)
def cwd_path(*args: PathLike | str) -> Path:
return path(Path.cwd(), *args)
# From: https://github.com/gitpython-developers/GitPython/blob/ea43defd777a9c0751fc44a9c6a622fc2dbd18a0/git/util.py#L101
# Windows has issues deleting readonly files that git creates
def git_rmtree(path: os.PathLike) -> None:
"""Remove the given recursively.
:note: we use shutil rmtree but adjust its behaviour to see whether files that
couldn't be deleted are read-only. Windows will not remove them in that case"""
def onerror(func: Callable, path: os.PathLike, _) -> None:
# Is the error an access error ?
os.chmod(path, stat.S_IWUSR)
try:
func(path) # Will scream if still not possible to delete.
except Exception:
raise
return shutil.rmtree(path, False, onerror)
# ----
# Main logic
# ----
BLEND2D_REPO = "https://github.com/blend2d/blend2d.git"
ASMJIT_REPO = "https://github.com/asmjit/asmjit.git"
BLEND2D_TARGET_PATH = cwd_path("Sources", "blend2d")
ASMJIT_TARGET_PATH = cwd_path("Sources", "asmjit")
TEMP_FOLDER_NAME = "temp"
def create_temporary_folder() -> Path:
temp_path = cwd_path(TEMP_FOLDER_NAME)
if temp_path.exists():
git_rmtree(temp_path)
os.mkdir(temp_path)
return temp_path
def remove_files_with_pattern(base_folder: Path, patterns: list[str]):
for pattern in patterns:
for result in glob.iglob(pattern, root_dir=base_folder, recursive=True):
rel_path = os.path.relpath(Path(base_folder, result), base_folder)
final_path = Path(base_folder, rel_path).resolve()
if not str(final_path).startswith(str(base_folder.resolve())):
continue
os.remove(final_path)
def clone_repo(tag_or_branch: str | None, repo: str, clone_path: str):
if tag_or_branch is None:
git("clone", repo, "--depth=1", clone_path)
else:
git("clone", repo, clone_path)
git("checkout", tag_or_branch, cwd=clone_path)
def clone_blend2d(tag_or_branch: str | None, base_folder: Path) -> Path:
print_stage_name("Cloning Blend2D...")
blend2d_clone_path = str(path(base_folder, "blend2d").absolute())
clone_repo(tag_or_branch, BLEND2D_REPO, blend2d_clone_path)
remove_files_with_pattern(Path(blend2d_clone_path), rmFilePatterns)
return Path(blend2d_clone_path)
def clone_asmjit(tag_or_branch: str | None, base_folder: Path) -> Path:
print_stage_name("Cloning asmjit...")
asmjit_clone_path = str(path(base_folder, "asmjit").absolute())
clone_repo(tag_or_branch, ASMJIT_REPO, asmjit_clone_path)
remove_files_with_pattern(Path(asmjit_clone_path), rmFilePatterns)
return Path(asmjit_clone_path)
def backup_includes(from_path: Path, to_path: Path):
if to_path.exists():
shutil.rmtree(to_path)
shutil.copytree(from_path, to_path)
def copy_repo_files(source_files: Path, target_path: Path):
# Backup includes
include_target_path = target_path.joinpath("include")
include_backup_path = source_files.joinpath("include")
backup_includes(include_target_path, include_backup_path)
# Erase files and copy over
shutil.rmtree(target_path)
shutil.copytree(source_files, target_path)
def copy_blend2d_files(clone_path: Path, target_path: Path):
print_stage_name("Copying over Blend2D files...")
copy_repo_files(clone_path.joinpath("src"), target_path)
def copy_asmjit_files(clone_path: Path, target_path: Path):
print_stage_name("Copying over asmjit files...")
copy_repo_files(clone_path.joinpath("src").joinpath("asmjit"), target_path)
def update_code(
blend2d_tag_or_branch: str | None, asmjit_tag_or_branch: str | None, force: bool
) -> int:
if (not force) and len(git_output("status", "--porcelain", echo=False).strip()) > 0:
print(
ConsoleColor.RED(
"Current git repo's state is not committed! Please commit and try again. (override with --force)"
)
)
return 1
# Create temp path
temp_path = create_temporary_folder()
# Clone
blend2d_clone_path = clone_blend2d(blend2d_tag_or_branch, temp_path)
asmjit_clone_path = clone_asmjit(asmjit_tag_or_branch, temp_path)
# Copy files
copy_blend2d_files(blend2d_clone_path, BLEND2D_TARGET_PATH)
copy_asmjit_files(asmjit_clone_path, ASMJIT_TARGET_PATH)
print(ConsoleColor.GREEN("Success!"))
git_status = git_output("status", "--porcelain").strip()
if len(git_status) > 0:
print(ConsoleColor.YELLOW("New unstaged changes:"))
print(git_status)
git_rmtree(temp_path)
return 0
# -----
# Entry point
# -----
def main() -> int:
def make_argparser() -> argparse.ArgumentParser:
argparser = argparse.ArgumentParser()
argparser.add_argument(
"-b",
"--blend2d_tag",
type=str,
help="A tag or branch to clone from the Blend2D repository. If not provided, defaults to latest commit of default branch.",
)
argparser.add_argument(
"-a",
"--asmjit_tag",
type=str,
help="A tag or branch to clone from the AsmJit repository. If not provided, defaults to latest commit of default branch.",
)
argparser.add_argument(
"-f",
"--force",
action="store_true",
help="Whether to ignore non-committed state of the repository. By default, the script fails if the repository has changes that are not committed to avoid conflicts and unintended changes.",
)
return argparser
argparser = make_argparser()
args = argparser.parse_args()
return update_code(args.blend2d_tag, args.asmjit_tag, args.force)
if __name__ == "__main__":
try:
sys.exit(main())
except subprocess.CalledProcessError as err:
sys.exit(err.returncode)
except KeyboardInterrupt:
sys.exit(1)