ruyi 0.39.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ruyi/__init__.py +21 -0
- ruyi/__main__.py +98 -0
- ruyi/cli/__init__.py +5 -0
- ruyi/cli/builtin_commands.py +14 -0
- ruyi/cli/cmd.py +224 -0
- ruyi/cli/completer.py +50 -0
- ruyi/cli/completion.py +26 -0
- ruyi/cli/config_cli.py +153 -0
- ruyi/cli/main.py +111 -0
- ruyi/cli/self_cli.py +295 -0
- ruyi/cli/user_input.py +127 -0
- ruyi/cli/version_cli.py +45 -0
- ruyi/config/__init__.py +401 -0
- ruyi/config/editor.py +92 -0
- ruyi/config/errors.py +76 -0
- ruyi/config/news.py +39 -0
- ruyi/config/schema.py +197 -0
- ruyi/device/__init__.py +0 -0
- ruyi/device/provision.py +591 -0
- ruyi/device/provision_cli.py +40 -0
- ruyi/log/__init__.py +272 -0
- ruyi/mux/.gitignore +1 -0
- ruyi/mux/__init__.py +0 -0
- ruyi/mux/runtime.py +213 -0
- ruyi/mux/venv/__init__.py +12 -0
- ruyi/mux/venv/emulator_cfg.py +41 -0
- ruyi/mux/venv/maker.py +782 -0
- ruyi/mux/venv/venv_cli.py +92 -0
- ruyi/mux/venv_cfg.py +214 -0
- ruyi/pluginhost/__init__.py +0 -0
- ruyi/pluginhost/api.py +206 -0
- ruyi/pluginhost/ctx.py +222 -0
- ruyi/pluginhost/paths.py +135 -0
- ruyi/pluginhost/plugin_cli.py +37 -0
- ruyi/pluginhost/unsandboxed.py +246 -0
- ruyi/py.typed +0 -0
- ruyi/resource_bundle/__init__.py +20 -0
- ruyi/resource_bundle/__main__.py +55 -0
- ruyi/resource_bundle/data.py +26 -0
- ruyi/ruyipkg/__init__.py +0 -0
- ruyi/ruyipkg/admin_checksum.py +88 -0
- ruyi/ruyipkg/admin_cli.py +83 -0
- ruyi/ruyipkg/atom.py +184 -0
- ruyi/ruyipkg/augmented_pkg.py +212 -0
- ruyi/ruyipkg/canonical_dump.py +320 -0
- ruyi/ruyipkg/checksum.py +39 -0
- ruyi/ruyipkg/cli_completion.py +42 -0
- ruyi/ruyipkg/distfile.py +208 -0
- ruyi/ruyipkg/entity.py +387 -0
- ruyi/ruyipkg/entity_cli.py +123 -0
- ruyi/ruyipkg/entity_provider.py +273 -0
- ruyi/ruyipkg/fetch.py +271 -0
- ruyi/ruyipkg/host.py +55 -0
- ruyi/ruyipkg/install.py +554 -0
- ruyi/ruyipkg/install_cli.py +150 -0
- ruyi/ruyipkg/list.py +126 -0
- ruyi/ruyipkg/list_cli.py +79 -0
- ruyi/ruyipkg/list_filter.py +173 -0
- ruyi/ruyipkg/msg.py +99 -0
- ruyi/ruyipkg/news.py +123 -0
- ruyi/ruyipkg/news_cli.py +78 -0
- ruyi/ruyipkg/news_store.py +183 -0
- ruyi/ruyipkg/pkg_manifest.py +657 -0
- ruyi/ruyipkg/profile.py +208 -0
- ruyi/ruyipkg/profile_cli.py +33 -0
- ruyi/ruyipkg/protocols.py +55 -0
- ruyi/ruyipkg/repo.py +763 -0
- ruyi/ruyipkg/state.py +345 -0
- ruyi/ruyipkg/unpack.py +369 -0
- ruyi/ruyipkg/unpack_method.py +91 -0
- ruyi/ruyipkg/update_cli.py +54 -0
- ruyi/telemetry/__init__.py +0 -0
- ruyi/telemetry/aggregate.py +72 -0
- ruyi/telemetry/event.py +41 -0
- ruyi/telemetry/node_info.py +192 -0
- ruyi/telemetry/provider.py +411 -0
- ruyi/telemetry/scope.py +43 -0
- ruyi/telemetry/store.py +238 -0
- ruyi/telemetry/telemetry_cli.py +127 -0
- ruyi/utils/__init__.py +0 -0
- ruyi/utils/ar.py +74 -0
- ruyi/utils/ci.py +63 -0
- ruyi/utils/frontmatter.py +38 -0
- ruyi/utils/git.py +169 -0
- ruyi/utils/global_mode.py +204 -0
- ruyi/utils/l10n.py +83 -0
- ruyi/utils/markdown.py +73 -0
- ruyi/utils/nuitka.py +33 -0
- ruyi/utils/porcelain.py +51 -0
- ruyi/utils/prereqs.py +77 -0
- ruyi/utils/ssl_patch.py +170 -0
- ruyi/utils/templating.py +34 -0
- ruyi/utils/toml.py +115 -0
- ruyi/utils/url.py +7 -0
- ruyi/utils/xdg_basedir.py +80 -0
- ruyi/version.py +67 -0
- ruyi-0.39.0.dist-info/LICENSE-Apache.txt +201 -0
- ruyi-0.39.0.dist-info/METADATA +403 -0
- ruyi-0.39.0.dist-info/RECORD +101 -0
- ruyi-0.39.0.dist-info/WHEEL +4 -0
- ruyi-0.39.0.dist-info/entry_points.txt +3 -0
ruyi/log/__init__.py
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import datetime
|
|
3
|
+
from functools import cached_property
|
|
4
|
+
import io
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
from typing import Any, TextIO, TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
# too heavy at package import time
|
|
11
|
+
from rich.console import Console, RenderableType
|
|
12
|
+
from rich.text import Text
|
|
13
|
+
|
|
14
|
+
from ..utils.global_mode import ProvidesGlobalMode
|
|
15
|
+
from ..utils.porcelain import PorcelainEntity, PorcelainEntityType, PorcelainOutput
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PorcelainLog(PorcelainEntity):
|
|
19
|
+
t: int
|
|
20
|
+
"""Timestamp of the message line in microseconds"""
|
|
21
|
+
|
|
22
|
+
lvl: str
|
|
23
|
+
"""Log level of the message line (one of D, F, I, W)"""
|
|
24
|
+
|
|
25
|
+
msg: str
|
|
26
|
+
"""Message content"""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def log_time_formatter(x: datetime.datetime) -> "Text":
|
|
30
|
+
from rich.text import Text
|
|
31
|
+
|
|
32
|
+
return Text(f"debug: [{x.isoformat()}]")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _make_porcelain_log(
|
|
36
|
+
t: int,
|
|
37
|
+
lvl: str,
|
|
38
|
+
message: "RenderableType",
|
|
39
|
+
sep: str,
|
|
40
|
+
*objects: Any,
|
|
41
|
+
) -> PorcelainLog:
|
|
42
|
+
from rich.console import Console
|
|
43
|
+
|
|
44
|
+
with io.StringIO() as buf:
|
|
45
|
+
tmp_console = Console(file=buf)
|
|
46
|
+
tmp_console.print(message, *objects, sep=sep, end="")
|
|
47
|
+
return {
|
|
48
|
+
"ty": PorcelainEntityType.LogV1,
|
|
49
|
+
"t": t,
|
|
50
|
+
"lvl": lvl,
|
|
51
|
+
"msg": buf.getvalue(),
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class RuyiLogger(metaclass=abc.ABCMeta):
|
|
56
|
+
def __init__(self) -> None:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
@abc.abstractmethod
|
|
61
|
+
def log_console(self) -> "Console":
|
|
62
|
+
raise NotImplementedError
|
|
63
|
+
|
|
64
|
+
@abc.abstractmethod
|
|
65
|
+
def stdout(
|
|
66
|
+
self,
|
|
67
|
+
message: "RenderableType",
|
|
68
|
+
*objects: Any,
|
|
69
|
+
sep: str = " ",
|
|
70
|
+
end: str = "\n",
|
|
71
|
+
) -> None:
|
|
72
|
+
raise NotImplementedError
|
|
73
|
+
|
|
74
|
+
@abc.abstractmethod
|
|
75
|
+
def D(
|
|
76
|
+
self,
|
|
77
|
+
message: "RenderableType",
|
|
78
|
+
*objects: Any,
|
|
79
|
+
sep: str = " ",
|
|
80
|
+
end: str = "\n",
|
|
81
|
+
_stack_offset_delta: int = 0,
|
|
82
|
+
) -> None:
|
|
83
|
+
raise NotImplementedError
|
|
84
|
+
|
|
85
|
+
@abc.abstractmethod
|
|
86
|
+
def F(
|
|
87
|
+
self,
|
|
88
|
+
message: "RenderableType",
|
|
89
|
+
*objects: Any,
|
|
90
|
+
sep: str = " ",
|
|
91
|
+
end: str = "\n",
|
|
92
|
+
) -> None:
|
|
93
|
+
raise NotImplementedError
|
|
94
|
+
|
|
95
|
+
@abc.abstractmethod
|
|
96
|
+
def I( # noqa: E743 # the name intentionally mimics Android logging for brevity
|
|
97
|
+
self,
|
|
98
|
+
message: "RenderableType",
|
|
99
|
+
*objects: Any,
|
|
100
|
+
sep: str = " ",
|
|
101
|
+
end: str = "\n",
|
|
102
|
+
) -> None:
|
|
103
|
+
raise NotImplementedError
|
|
104
|
+
|
|
105
|
+
@abc.abstractmethod
|
|
106
|
+
def W(
|
|
107
|
+
self,
|
|
108
|
+
message: "RenderableType",
|
|
109
|
+
*objects: Any,
|
|
110
|
+
sep: str = " ",
|
|
111
|
+
end: str = "\n",
|
|
112
|
+
) -> None:
|
|
113
|
+
raise NotImplementedError
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class RuyiConsoleLogger(RuyiLogger):
|
|
117
|
+
def __init__(
|
|
118
|
+
self,
|
|
119
|
+
gm: ProvidesGlobalMode,
|
|
120
|
+
stdout: TextIO = sys.stdout,
|
|
121
|
+
stderr: TextIO = sys.stderr,
|
|
122
|
+
) -> None:
|
|
123
|
+
super().__init__()
|
|
124
|
+
|
|
125
|
+
self._gm = gm
|
|
126
|
+
self._stdout = stdout
|
|
127
|
+
self._stderr = stderr
|
|
128
|
+
|
|
129
|
+
@cached_property
|
|
130
|
+
def _stdout_console(self) -> "Console":
|
|
131
|
+
from rich.console import Console
|
|
132
|
+
|
|
133
|
+
return Console(
|
|
134
|
+
file=self._stdout,
|
|
135
|
+
highlight=False,
|
|
136
|
+
soft_wrap=True,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
@cached_property
|
|
140
|
+
def _debug_console(self) -> "Console":
|
|
141
|
+
from rich.console import Console
|
|
142
|
+
|
|
143
|
+
return Console(
|
|
144
|
+
file=self._stderr,
|
|
145
|
+
log_time_format=log_time_formatter,
|
|
146
|
+
soft_wrap=True,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
@cached_property
|
|
150
|
+
def _log_console(self) -> "Console":
|
|
151
|
+
from rich.console import Console
|
|
152
|
+
|
|
153
|
+
return Console(
|
|
154
|
+
file=self._stderr,
|
|
155
|
+
highlight=False,
|
|
156
|
+
soft_wrap=True,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
@cached_property
|
|
160
|
+
def _porcelain_sink(self) -> PorcelainOutput:
|
|
161
|
+
return PorcelainOutput(self._stderr.buffer)
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def log_console(self) -> "Console":
|
|
165
|
+
return self._log_console
|
|
166
|
+
|
|
167
|
+
def _emit_porcelain_log(
|
|
168
|
+
self,
|
|
169
|
+
lvl: str,
|
|
170
|
+
message: "RenderableType",
|
|
171
|
+
sep: str = " ",
|
|
172
|
+
*objects: Any,
|
|
173
|
+
) -> None:
|
|
174
|
+
t = int(time.time() * 1000000)
|
|
175
|
+
obj = _make_porcelain_log(t, lvl, message, sep, *objects)
|
|
176
|
+
self._porcelain_sink.emit(obj)
|
|
177
|
+
|
|
178
|
+
def stdout(
|
|
179
|
+
self,
|
|
180
|
+
message: "RenderableType",
|
|
181
|
+
*objects: Any,
|
|
182
|
+
sep: str = " ",
|
|
183
|
+
end: str = "\n",
|
|
184
|
+
) -> None:
|
|
185
|
+
return self._stdout_console.print(message, *objects, sep=sep, end=end)
|
|
186
|
+
|
|
187
|
+
def D(
|
|
188
|
+
self,
|
|
189
|
+
message: "RenderableType",
|
|
190
|
+
*objects: Any,
|
|
191
|
+
sep: str = " ",
|
|
192
|
+
end: str = "\n",
|
|
193
|
+
_stack_offset_delta: int = 0,
|
|
194
|
+
) -> None:
|
|
195
|
+
if not self._gm.is_debug:
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
if self._gm.is_porcelain:
|
|
199
|
+
return self._emit_porcelain_log("D", message, sep, *objects)
|
|
200
|
+
|
|
201
|
+
return self._debug_console.log(
|
|
202
|
+
message,
|
|
203
|
+
*objects,
|
|
204
|
+
sep=sep,
|
|
205
|
+
end=end,
|
|
206
|
+
_stack_offset=2 + _stack_offset_delta,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
def F(
|
|
210
|
+
self,
|
|
211
|
+
message: "RenderableType",
|
|
212
|
+
*objects: Any,
|
|
213
|
+
sep: str = " ",
|
|
214
|
+
end: str = "\n",
|
|
215
|
+
) -> None:
|
|
216
|
+
if self._gm.is_porcelain:
|
|
217
|
+
return self._emit_porcelain_log("F", message, sep, *objects)
|
|
218
|
+
|
|
219
|
+
return self.log_console.print(
|
|
220
|
+
f"[bold red]fatal error:[/] {message}",
|
|
221
|
+
*objects,
|
|
222
|
+
sep=sep,
|
|
223
|
+
end=end,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def I( # noqa: E743 # the name intentionally mimics Android logging for brevity
|
|
227
|
+
self,
|
|
228
|
+
message: "RenderableType",
|
|
229
|
+
*objects: Any,
|
|
230
|
+
sep: str = " ",
|
|
231
|
+
end: str = "\n",
|
|
232
|
+
) -> None:
|
|
233
|
+
if self._gm.is_porcelain:
|
|
234
|
+
return self._emit_porcelain_log("I", message, sep, *objects)
|
|
235
|
+
|
|
236
|
+
return self.log_console.print(
|
|
237
|
+
f"[bold green]info:[/] {message}",
|
|
238
|
+
*objects,
|
|
239
|
+
sep=sep,
|
|
240
|
+
end=end,
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
def W(
|
|
244
|
+
self,
|
|
245
|
+
message: "RenderableType",
|
|
246
|
+
*objects: Any,
|
|
247
|
+
sep: str = " ",
|
|
248
|
+
end: str = "\n",
|
|
249
|
+
) -> None:
|
|
250
|
+
if self._gm.is_porcelain:
|
|
251
|
+
return self._emit_porcelain_log("W", message, sep, *objects)
|
|
252
|
+
|
|
253
|
+
return self.log_console.print(
|
|
254
|
+
f"[bold yellow]warn:[/] {message}",
|
|
255
|
+
*objects,
|
|
256
|
+
sep=sep,
|
|
257
|
+
end=end,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def humanize_list(
|
|
262
|
+
obj: list[str] | set[str],
|
|
263
|
+
*,
|
|
264
|
+
sep: str = ", ",
|
|
265
|
+
item_color: str | None = None,
|
|
266
|
+
empty_prompt: str = "(none)",
|
|
267
|
+
) -> str:
|
|
268
|
+
if not obj:
|
|
269
|
+
return empty_prompt
|
|
270
|
+
if item_color is None:
|
|
271
|
+
return sep.join(obj)
|
|
272
|
+
return sep.join(f"[{item_color}]{x}[/]" for x in obj)
|
ruyi/mux/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!venv
|
ruyi/mux/__init__.py
ADDED
|
File without changes
|
ruyi/mux/runtime.py
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import atexit
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import shlex
|
|
5
|
+
from typing import Final, List, NoReturn
|
|
6
|
+
|
|
7
|
+
from ..config import GlobalConfig
|
|
8
|
+
from ..utils.global_mode import ProvidesGlobalMode
|
|
9
|
+
from .venv_cfg import RuyiVenvConfig
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _run_exit_handlers_and_execv(
|
|
13
|
+
path: str,
|
|
14
|
+
argv: list[str],
|
|
15
|
+
) -> NoReturn:
|
|
16
|
+
# run all exit handlers before execv
|
|
17
|
+
# crucially this includes our telemetry handler so we don't lose telemetry
|
|
18
|
+
# events in mux mode
|
|
19
|
+
atexit._run_exitfuncs()
|
|
20
|
+
os.execv(path, argv)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def mux_main(
|
|
24
|
+
gm: ProvidesGlobalMode,
|
|
25
|
+
gc: GlobalConfig,
|
|
26
|
+
argv: List[str],
|
|
27
|
+
) -> int | NoReturn:
|
|
28
|
+
basename = os.path.basename(gm.argv0)
|
|
29
|
+
logger = gc.logger
|
|
30
|
+
logger.D(f"mux mode: argv = {argv}, basename = {basename}")
|
|
31
|
+
|
|
32
|
+
vcfg = RuyiVenvConfig.load_from_venv(gm, logger)
|
|
33
|
+
if vcfg is None:
|
|
34
|
+
logger.F("the Ruyi toolchain mux is not configured")
|
|
35
|
+
logger.I("check out `ruyi venv` for making a virtual environment")
|
|
36
|
+
return 1
|
|
37
|
+
|
|
38
|
+
direct_symlink_target = resolve_direct_symlink_target(gm.argv0, vcfg)
|
|
39
|
+
if direct_symlink_target is not None:
|
|
40
|
+
logger.D(
|
|
41
|
+
f"detected direct symlink target: {direct_symlink_target}, overriding basename"
|
|
42
|
+
)
|
|
43
|
+
basename = direct_symlink_target
|
|
44
|
+
|
|
45
|
+
if basename == "ruyi-qemu":
|
|
46
|
+
return mux_qemu_main(gc, vcfg, argv)
|
|
47
|
+
|
|
48
|
+
# match the basename with one of the configured target tuples
|
|
49
|
+
target_tuple: str | None = None
|
|
50
|
+
binpath: str | None = None
|
|
51
|
+
toolchain_sysroot: str | None = None
|
|
52
|
+
toolchain_flags: str | None = None
|
|
53
|
+
gcc_install_dir: str | None = None
|
|
54
|
+
|
|
55
|
+
# prefer v1+ cached info which is lossless
|
|
56
|
+
if md := vcfg.resolve_cmd_metadata_with_cache(basename):
|
|
57
|
+
target_tuple = md["target_tuple"]
|
|
58
|
+
binpath = md["dest"]
|
|
59
|
+
if target_tuple:
|
|
60
|
+
tgt_data = vcfg.targets.get(target_tuple)
|
|
61
|
+
if tgt_data is None:
|
|
62
|
+
logger.F(
|
|
63
|
+
f"internal error: no target data for tuple [yellow]{target_tuple}[/]"
|
|
64
|
+
)
|
|
65
|
+
return 1
|
|
66
|
+
toolchain_sysroot = tgt_data.get("toolchain_sysroot")
|
|
67
|
+
toolchain_flags = tgt_data.get("toolchain_flags")
|
|
68
|
+
gcc_install_dir = tgt_data.get("gcc_install_dir")
|
|
69
|
+
else:
|
|
70
|
+
toolchain_bindir: str | None = None
|
|
71
|
+
for tgt_tuple, tgt_data in vcfg.targets.items():
|
|
72
|
+
if not basename.startswith(f"{tgt_tuple}-"):
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
logger.D(f"matched target '{tgt_tuple}', data {tgt_data}")
|
|
76
|
+
target_tuple = tgt_tuple
|
|
77
|
+
toolchain_bindir = tgt_data["toolchain_bindir"]
|
|
78
|
+
toolchain_sysroot = tgt_data.get("toolchain_sysroot")
|
|
79
|
+
toolchain_flags = tgt_data.get("toolchain_flags")
|
|
80
|
+
gcc_install_dir = tgt_data.get("gcc_install_dir")
|
|
81
|
+
break
|
|
82
|
+
|
|
83
|
+
if toolchain_bindir is None:
|
|
84
|
+
# should not happen
|
|
85
|
+
logger.F(
|
|
86
|
+
f"internal error: no bindir configured for target [yellow]{target_tuple}[/]"
|
|
87
|
+
)
|
|
88
|
+
return 1
|
|
89
|
+
|
|
90
|
+
binpath = os.path.join(toolchain_bindir, basename)
|
|
91
|
+
|
|
92
|
+
if target_tuple is None:
|
|
93
|
+
logger.F(f"no configured target found for command [yellow]{basename}[/]")
|
|
94
|
+
return 1
|
|
95
|
+
|
|
96
|
+
logger.D(f"binary to exec: {binpath}")
|
|
97
|
+
|
|
98
|
+
argv_to_insert: list[str] | None = None
|
|
99
|
+
if is_proxying_to_cc(basename):
|
|
100
|
+
logger.D(f"{basename} is considered a CC")
|
|
101
|
+
|
|
102
|
+
argv_to_insert = []
|
|
103
|
+
|
|
104
|
+
if is_proxying_to_clang(basename):
|
|
105
|
+
logger.D(f"adding target for clang: {target_tuple}")
|
|
106
|
+
argv_to_insert.append(f"--target={target_tuple}")
|
|
107
|
+
if gcc_install_dir is not None:
|
|
108
|
+
logger.D(f"informing clang of GCC install dir: {gcc_install_dir}")
|
|
109
|
+
argv_to_insert.append(f"--gcc-install-dir={gcc_install_dir}")
|
|
110
|
+
|
|
111
|
+
if toolchain_flags is not None:
|
|
112
|
+
argv_to_insert.extend(shlex.split(toolchain_flags))
|
|
113
|
+
logger.D(f"parsed toolchain flags: {argv_to_insert}")
|
|
114
|
+
|
|
115
|
+
if toolchain_sysroot is not None:
|
|
116
|
+
logger.D(f"adding sysroot: {toolchain_sysroot}")
|
|
117
|
+
argv_to_insert.extend(("--sysroot", toolchain_sysroot))
|
|
118
|
+
|
|
119
|
+
new_argv = [binpath]
|
|
120
|
+
if argv_to_insert:
|
|
121
|
+
new_argv.extend(argv_to_insert)
|
|
122
|
+
if len(argv) > 1:
|
|
123
|
+
new_argv.extend(argv[1:])
|
|
124
|
+
|
|
125
|
+
ensure_venv_in_path(vcfg)
|
|
126
|
+
|
|
127
|
+
logger.D(f"exec-ing with argv {new_argv}")
|
|
128
|
+
return _run_exit_handlers_and_execv(binpath, new_argv)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# TODO: dedup with venv provision logic (into a command name parser)
|
|
132
|
+
CC_ARGV0_RE: Final = re.compile(
|
|
133
|
+
r"(?:^|-)(?:g?cc|c\+\+|g\+\+|cpp|clang|clang\+\+|clang-cl|clang-cpp)(?:-[0-9.]+)?$"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def resolve_direct_symlink_target(argv0: str, vcfg: RuyiVenvConfig) -> str | None:
|
|
138
|
+
direct_symlink_target = resolve_argv0_symlink(argv0, vcfg)
|
|
139
|
+
if direct_symlink_target is not None and os.path.sep in direct_symlink_target:
|
|
140
|
+
# we're not designed to handle such indirections
|
|
141
|
+
return None
|
|
142
|
+
return direct_symlink_target
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def resolve_argv0_symlink(argv0: str, vcfg: RuyiVenvConfig) -> str | None:
|
|
146
|
+
if os.path.sep in argv0:
|
|
147
|
+
# argv[0] contains path information that we can just use
|
|
148
|
+
try:
|
|
149
|
+
return os.readlink(argv0)
|
|
150
|
+
except OSError:
|
|
151
|
+
# argv[0] is not a symlink
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
# argv[0] is bare command name, in which case we expect venv root to
|
|
155
|
+
# be available, so we can just check f'{venv_root}/bin/{argv[0]}'.
|
|
156
|
+
# we're guaranteed a venv_root because of the vcfg init logic.
|
|
157
|
+
try:
|
|
158
|
+
return os.readlink(vcfg.venv_root / "bin" / argv0)
|
|
159
|
+
except OSError:
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def is_proxying_to_cc(argv0: str) -> bool:
|
|
164
|
+
return CC_ARGV0_RE.search(argv0) is not None
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def is_proxying_to_clang(basename: str) -> bool:
|
|
168
|
+
return "clang" in basename
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def mux_qemu_main(
|
|
172
|
+
gc: GlobalConfig,
|
|
173
|
+
vcfg: RuyiVenvConfig,
|
|
174
|
+
argv: List[str],
|
|
175
|
+
) -> int | NoReturn:
|
|
176
|
+
logger = gc.logger
|
|
177
|
+
binpath = vcfg.qemu_bin
|
|
178
|
+
if binpath is None:
|
|
179
|
+
logger.F("this virtual environment has no QEMU-like emulator configured")
|
|
180
|
+
return 1
|
|
181
|
+
|
|
182
|
+
if vcfg.profile_emu_env is not None:
|
|
183
|
+
logger.D(f"seeding QEMU environment with {vcfg.profile_emu_env}")
|
|
184
|
+
for k, v in vcfg.profile_emu_env.items():
|
|
185
|
+
os.environ[k] = v
|
|
186
|
+
|
|
187
|
+
logger.D(f"QEMU binary to exec: {binpath}")
|
|
188
|
+
|
|
189
|
+
new_argv = [binpath]
|
|
190
|
+
if len(argv) > 1:
|
|
191
|
+
new_argv.extend(argv[1:])
|
|
192
|
+
|
|
193
|
+
logger.D(f"exec-ing with argv {new_argv}")
|
|
194
|
+
return _run_exit_handlers_and_execv(binpath, new_argv)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def ensure_venv_in_path(vcfg: RuyiVenvConfig) -> None:
|
|
198
|
+
venv_root = vcfg.venv_root
|
|
199
|
+
venv_bindir = venv_root / "bin"
|
|
200
|
+
venv_bindir = venv_bindir.resolve()
|
|
201
|
+
|
|
202
|
+
orig_path = os.environ.get("PATH", "")
|
|
203
|
+
for p in orig_path.split(os.pathsep):
|
|
204
|
+
try:
|
|
205
|
+
if os.path.samefile(p, venv_bindir):
|
|
206
|
+
# TODO: what if our bindir actually comes after the system ones?
|
|
207
|
+
return
|
|
208
|
+
except FileNotFoundError:
|
|
209
|
+
# maybe the PATH entry is stale
|
|
210
|
+
continue
|
|
211
|
+
|
|
212
|
+
# we're not in PATH, so prepend the bindir to PATH
|
|
213
|
+
os.environ["PATH"] = f"{venv_bindir}:{orig_path}" if orig_path else str(venv_bindir)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from os import PathLike
|
|
2
|
+
from typing import Any, TypedDict
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ConfiguredTargetTuple(TypedDict):
|
|
6
|
+
target: str
|
|
7
|
+
toolchain_root: PathLike[Any]
|
|
8
|
+
toolchain_sysroot: PathLike[Any] | None
|
|
9
|
+
toolchain_flags: str
|
|
10
|
+
binutils_flavor: str
|
|
11
|
+
cc_flavor: str
|
|
12
|
+
gcc_install_dir: PathLike[Any] | None
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any, TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from typing_extensions import Self
|
|
6
|
+
|
|
7
|
+
from ...ruyipkg.pkg_manifest import EmulatorProgDecl
|
|
8
|
+
from ...ruyipkg.profile import ProfileProxy
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ResolvedEmulatorProg:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
display_name: str,
|
|
15
|
+
binfmt_misc_str: str | None,
|
|
16
|
+
env: dict[str, str] | None,
|
|
17
|
+
) -> None:
|
|
18
|
+
self.display_name = display_name
|
|
19
|
+
self.binfmt_misc_str = binfmt_misc_str
|
|
20
|
+
self.env = env
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def new(
|
|
24
|
+
cls,
|
|
25
|
+
prog: EmulatorProgDecl,
|
|
26
|
+
prog_install_root: os.PathLike[Any],
|
|
27
|
+
profile: ProfileProxy,
|
|
28
|
+
sysroot: os.PathLike[Any] | None,
|
|
29
|
+
) -> "Self":
|
|
30
|
+
return cls(
|
|
31
|
+
get_display_name_for_emulator(prog, prog_install_root),
|
|
32
|
+
prog.get_binfmt_misc_str(prog_install_root),
|
|
33
|
+
profile.get_env_config_for_emu_flavor(prog.flavor, sysroot),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_display_name_for_emulator(
|
|
38
|
+
prog: EmulatorProgDecl,
|
|
39
|
+
prog_install_root: os.PathLike[Any],
|
|
40
|
+
) -> str:
|
|
41
|
+
return f"{os.path.basename(prog.relative_path)} from {prog_install_root}"
|