forked from yamsergey/yamsergey.termux.flutter
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathsysroot.py
More file actions
148 lines (112 loc) · 3.9 KB
/
sysroot.py
File metadata and controls
148 lines (112 loc) · 3.9 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
#!/usr/bin/env python3
import os
import utils
import pathlib
import asyncio
import aiohttp
import tempfile
import itertools
import subprocess
import urllib.parse
from loguru import logger
async def _download(sess, url, dst):
path = urllib.parse.urlparse(url).path
name = pathlib.Path(path).name
path = pathlib.Path(dst, name)
try:
async with sess.get(url) as resp:
resp.raise_for_status()
with open(path, 'wb') as f:
async for chunk in resp.content.iter_chunked(8192):
f.write(chunk)
return path
except Exception:
raise RuntimeError(f'✗ 下载失败 {name}')
async def _spawn(tasks):
if not tasks:
return []
tasks = [asyncio.create_task(t) for t in tasks]
done, pending = await asyncio.wait(
tasks,
return_when=asyncio.FIRST_EXCEPTION)
for task in pending:
task.cancel()
await asyncio.gather(*pending, return_exceptions=True)
return [r.result() for r in done]
async def _download_packages(out, arch, *src):
timeout = aiohttp.ClientTimeout(total=500)
async with aiohttp.ClientSession(timeout=timeout) as sess:
urls = await _spawn([
_resolve_packages(sess, arch, **it) for it in src
])
urls = itertools.chain(*urls)
return await _spawn([
_download(sess, url, out) for url in urls
])
async def _resolve_packages(sess, arch, repo, dist, pkgs):
if not repo or not pkgs:
return {}
bin = f'dists/{dist}/main/binary-{arch}/Packages'
url = urllib.parse.urljoin(repo, bin)
current = None
package = {}
async with sess.get(url) as resp:
resp.raise_for_status()
async for line in resp.content:
line = line.decode().strip()
if line.startswith('Package:'):
current = line.split(':')[1].strip()
elif current in pkgs and line.startswith('Filename:'):
urlpath = line.split(':')[1].strip()
package[current] = urllib.parse.urljoin(repo, urlpath)
if len(package) == len(pkgs):
break
remains = [it for it in pkgs if it not in package]
if remains:
raise FileNotFoundError(f'packages{remains} not found.')
return package.values()
def _extract(out, deb):
subprocess.run(['dpkg', '-x', str(deb), str(out)], check=True)
logger.info(f'✓ 成功安装 {deb.name}')
async def _work(out, arch, *src):
with tempfile.TemporaryDirectory() as tmp:
for deb in await _download_packages(tmp, arch, *src):
_extract(out, deb)
usr = out/'usr'
dst = 'data/data/com.termux/files/usr'
assert os.path.isdir(out/dst)
try:
usr.symlink_to(dst, True)
except FileExistsError:
if not usr.samefile(out/dst):
raise
pthread = out/'usr/lib/libpthread.a'
pthread.write_bytes(b'INPUT(-lc)')
@utils.record
class Sysroot:
def __init__(self, path: str, **kwargs):
self.path = pathlib.Path(path).expanduser().resolve()
self.data = {}
if not self.path.exists():
self.path.mkdir()
assert self.path.is_dir(), f'bad sysroot path: "{path}"'
for k, v in kwargs.items():
if isinstance(v, dict):
self.__include__(k, **v)
def __include__(self, name, repo, dist, pkgs):
assert name and repo and dist and pkgs
self.data[name] = {'repo': repo, 'dist': dist, 'pkgs': pkgs}
def __call__(self, arch: str):
arch = utils.termux_arch(arch)
if self.data:
asyncio.run(_work(self.path, arch, *self.data.values()))
else:
logger.info('no work to do.')
def __str__(self):
return str(self.path)
if __name__ == '__main__':
import fire
import tomllib
with open('build.toml', 'rb') as f:
src = tomllib.load(f)
fire.Fire(Sysroot(**src['sysroot']))