ruyi 0.44.0b20251219__py3-none-any.whl → 0.45.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/__main__.py +16 -4
- ruyi/cli/cmd.py +6 -5
- ruyi/cli/config_cli.py +14 -11
- ruyi/cli/main.py +14 -4
- ruyi/cli/oobe.py +7 -3
- ruyi/cli/self_cli.py +48 -34
- ruyi/cli/user_input.py +42 -12
- ruyi/cli/version_cli.py +11 -5
- ruyi/config/__init__.py +26 -2
- ruyi/config/errors.py +19 -7
- ruyi/device/provision.py +116 -55
- ruyi/device/provision_cli.py +6 -3
- ruyi/i18n/__init__.py +129 -0
- ruyi/log/__init__.py +6 -5
- ruyi/mux/runtime.py +19 -6
- ruyi/mux/venv/maker.py +93 -35
- ruyi/mux/venv/venv_cli.py +13 -10
- ruyi/pluginhost/plugin_cli.py +4 -3
- ruyi/resource_bundle/__init__.py +22 -8
- ruyi/resource_bundle/__main__.py +6 -5
- ruyi/resource_bundle/data.py +13 -9
- ruyi/ruyipkg/admin_checksum.py +4 -1
- ruyi/ruyipkg/admin_cli.py +9 -6
- ruyi/ruyipkg/augmented_pkg.py +15 -14
- ruyi/ruyipkg/checksum.py +8 -2
- ruyi/ruyipkg/distfile.py +33 -9
- ruyi/ruyipkg/entity.py +12 -2
- ruyi/ruyipkg/entity_cli.py +20 -12
- ruyi/ruyipkg/entity_provider.py +11 -2
- ruyi/ruyipkg/fetcher.py +38 -9
- ruyi/ruyipkg/install.py +143 -42
- ruyi/ruyipkg/install_cli.py +18 -15
- ruyi/ruyipkg/list.py +27 -20
- ruyi/ruyipkg/list_cli.py +12 -7
- ruyi/ruyipkg/news.py +23 -11
- ruyi/ruyipkg/news_cli.py +10 -7
- ruyi/ruyipkg/profile_cli.py +8 -2
- ruyi/ruyipkg/repo.py +22 -8
- ruyi/ruyipkg/unpack.py +42 -8
- ruyi/ruyipkg/unpack_method.py +5 -1
- ruyi/ruyipkg/update_cli.py +8 -3
- ruyi/telemetry/provider.py +74 -29
- ruyi/telemetry/telemetry_cli.py +9 -8
- ruyi/utils/git.py +18 -11
- ruyi/utils/prereqs.py +10 -5
- ruyi/utils/ssl_patch.py +2 -1
- ruyi/version.py +9 -3
- {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/METADATA +2 -1
- ruyi-0.45.0.dist-info/RECORD +103 -0
- {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/WHEEL +1 -1
- ruyi-0.44.0b20251219.dist-info/RECORD +0 -102
- {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/entry_points.txt +0 -0
- {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/licenses/LICENSE-Apache.txt +0 -0
ruyi/i18n/__init__.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from io import BytesIO
|
|
2
|
+
import gettext
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Final, Mapping, NewType
|
|
6
|
+
|
|
7
|
+
if sys.version_info >= (3, 11):
|
|
8
|
+
from typing import LiteralString
|
|
9
|
+
else:
|
|
10
|
+
# It may happen that Python and typing_extensions are both too old, which
|
|
11
|
+
# is unfortunately the case with Ubuntu 22.04 LTS system packages, meaning
|
|
12
|
+
# typing_extensions cannot guarantee us LiteralString either.
|
|
13
|
+
#
|
|
14
|
+
# We don't expect development work within such an environment, so just
|
|
15
|
+
# alias to str to avoid importing typing_extensions altogether. This also
|
|
16
|
+
# helps CLI startup performance.
|
|
17
|
+
#
|
|
18
|
+
# Unfortunately, simply assigning str to LiteralString would not work either,
|
|
19
|
+
# due to mypy/pyright not wanting us to re-assign types; we have to
|
|
20
|
+
# resort to providing different function signatures for Python 3.10, which
|
|
21
|
+
# is done below.
|
|
22
|
+
#
|
|
23
|
+
# LiteralString = str # type: ignore[misc]
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
from ..resource_bundle import get_resource_blob
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _probe_lang(environ: Mapping[str, str]) -> list[str]:
|
|
30
|
+
"""Probe the environment variables the gettext way, to determine the list
|
|
31
|
+
of preferred languages."""
|
|
32
|
+
languages: list[str] = []
|
|
33
|
+
# check the variables in this order
|
|
34
|
+
for envar in ("LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG"):
|
|
35
|
+
if val := environ.get(envar):
|
|
36
|
+
languages = val.split(":")
|
|
37
|
+
break
|
|
38
|
+
if "C" not in languages:
|
|
39
|
+
languages.append("C")
|
|
40
|
+
|
|
41
|
+
for i, lang in enumerate(languages):
|
|
42
|
+
# normalize things like en_US.UTF-8 to en_US
|
|
43
|
+
if "." in lang:
|
|
44
|
+
languages[i] = lang.split(".", 1)[0]
|
|
45
|
+
|
|
46
|
+
return languages
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
_DOMAINS = (
|
|
50
|
+
"argparse",
|
|
51
|
+
"ruyi",
|
|
52
|
+
)
|
|
53
|
+
"""gettext domains we supply and use ourselves"""
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class I18nAdapter:
|
|
57
|
+
"""Adapter for gettext translation functions."""
|
|
58
|
+
|
|
59
|
+
def __init__(self) -> None:
|
|
60
|
+
self._t = gettext.NullTranslations()
|
|
61
|
+
|
|
62
|
+
def hook(self) -> None:
|
|
63
|
+
# monkey-patch the global gettext functions
|
|
64
|
+
# the type ignore comments are necessary because mypy doesn't see
|
|
65
|
+
# the bounded methods as compatible with the unbound functions
|
|
66
|
+
# (it doesn't remove self from the unbound method signature)
|
|
67
|
+
gettext.gettext = self.gettext # type: ignore[assignment]
|
|
68
|
+
gettext.ngettext = self.ngettext # type: ignore[assignment]
|
|
69
|
+
|
|
70
|
+
def init_from_env(self, environ: Mapping[str, str] | None = None) -> None:
|
|
71
|
+
if environ is None:
|
|
72
|
+
environ = os.environ
|
|
73
|
+
|
|
74
|
+
langs = _probe_lang(environ)
|
|
75
|
+
for domain in _DOMAINS:
|
|
76
|
+
for lang in langs:
|
|
77
|
+
if self.set_locale(domain, lang):
|
|
78
|
+
break
|
|
79
|
+
|
|
80
|
+
def _get_mo(self, domain: str, locale: str) -> BytesIO | None:
|
|
81
|
+
# this is always forward-slash-separated, because this is not a concrete
|
|
82
|
+
# filesystem path, rather a resource bundle key
|
|
83
|
+
path = f"locale/{locale}/LC_MESSAGES/{domain}.mo"
|
|
84
|
+
blob = get_resource_blob(path)
|
|
85
|
+
if blob:
|
|
86
|
+
return BytesIO(blob)
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
def set_locale(self, domain: str, locale: str | None = None) -> bool:
|
|
90
|
+
if locale is not None:
|
|
91
|
+
if mo_file := self._get_mo(domain, locale):
|
|
92
|
+
self._t.add_fallback(gettext.GNUTranslations(mo_file))
|
|
93
|
+
return True
|
|
94
|
+
return False
|
|
95
|
+
|
|
96
|
+
def gettext(self, x: str) -> str:
|
|
97
|
+
return self._t.gettext(x)
|
|
98
|
+
|
|
99
|
+
def ngettext(self, singular: str, plural: str, n: int) -> str:
|
|
100
|
+
return self._t.ngettext(singular, plural, n)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
ADAPTER: Final = I18nAdapter()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
DeferredI18nString = NewType("DeferredI18nString", str)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
if sys.version_info >= (3, 11):
|
|
110
|
+
|
|
111
|
+
def _(x: LiteralString | DeferredI18nString) -> str:
|
|
112
|
+
"""``gettext`` alias that ensures its input is string literal via type
|
|
113
|
+
signature."""
|
|
114
|
+
return ADAPTER.gettext(x)
|
|
115
|
+
|
|
116
|
+
def d_(x: LiteralString) -> DeferredI18nString:
|
|
117
|
+
"""Mark a string literal for deferred translation: call ``_`` at use sites."""
|
|
118
|
+
return DeferredI18nString(x)
|
|
119
|
+
|
|
120
|
+
else:
|
|
121
|
+
|
|
122
|
+
def _(x: str | DeferredI18nString) -> str:
|
|
123
|
+
"""``gettext`` alias that ensures its input is string literal via type
|
|
124
|
+
signature."""
|
|
125
|
+
return ADAPTER.gettext(x)
|
|
126
|
+
|
|
127
|
+
def d_(x: str) -> DeferredI18nString:
|
|
128
|
+
"""Mark a string literal for deferred translation: call ``_`` at use sites."""
|
|
129
|
+
return DeferredI18nString(x)
|
ruyi/log/__init__.py
CHANGED
|
@@ -11,6 +11,7 @@ if TYPE_CHECKING:
|
|
|
11
11
|
from rich.console import Console, RenderableType
|
|
12
12
|
from rich.text import Text
|
|
13
13
|
|
|
14
|
+
from ..i18n import _
|
|
14
15
|
from ..utils.global_mode import ProvidesGlobalMode
|
|
15
16
|
from ..utils.porcelain import PorcelainEntity, PorcelainEntityType, PorcelainOutput
|
|
16
17
|
|
|
@@ -217,7 +218,7 @@ class RuyiConsoleLogger(RuyiLogger):
|
|
|
217
218
|
return self._emit_porcelain_log("F", message, sep, *objects)
|
|
218
219
|
|
|
219
220
|
return self.log_console.print(
|
|
220
|
-
|
|
221
|
+
_("[bold red]fatal error:[/] {message}").format(message=message),
|
|
221
222
|
*objects,
|
|
222
223
|
sep=sep,
|
|
223
224
|
end=end,
|
|
@@ -234,7 +235,7 @@ class RuyiConsoleLogger(RuyiLogger):
|
|
|
234
235
|
return self._emit_porcelain_log("I", message, sep, *objects)
|
|
235
236
|
|
|
236
237
|
return self.log_console.print(
|
|
237
|
-
|
|
238
|
+
_("[bold green]info:[/] {message}").format(message=message),
|
|
238
239
|
*objects,
|
|
239
240
|
sep=sep,
|
|
240
241
|
end=end,
|
|
@@ -251,7 +252,7 @@ class RuyiConsoleLogger(RuyiLogger):
|
|
|
251
252
|
return self._emit_porcelain_log("W", message, sep, *objects)
|
|
252
253
|
|
|
253
254
|
return self.log_console.print(
|
|
254
|
-
|
|
255
|
+
_("[bold yellow]warn:[/] {message}").format(message=message),
|
|
255
256
|
*objects,
|
|
256
257
|
sep=sep,
|
|
257
258
|
end=end,
|
|
@@ -263,10 +264,10 @@ def humanize_list(
|
|
|
263
264
|
*,
|
|
264
265
|
sep: str = ", ",
|
|
265
266
|
item_color: str | None = None,
|
|
266
|
-
empty_prompt: str =
|
|
267
|
+
empty_prompt: str | None = None,
|
|
267
268
|
) -> str:
|
|
268
269
|
if not obj:
|
|
269
|
-
return empty_prompt
|
|
270
|
+
return empty_prompt if empty_prompt is not None else _("(none)")
|
|
270
271
|
if item_color is None:
|
|
271
272
|
return sep.join(obj)
|
|
272
273
|
return sep.join(f"[{item_color}]{x}[/]" for x in obj)
|
ruyi/mux/runtime.py
CHANGED
|
@@ -5,6 +5,7 @@ import shlex
|
|
|
5
5
|
from typing import Final, List, NoReturn
|
|
6
6
|
|
|
7
7
|
from ..config import GlobalConfig
|
|
8
|
+
from ..i18n import _
|
|
8
9
|
from ..utils.global_mode import ProvidesGlobalMode
|
|
9
10
|
from .venv_cfg import RuyiVenvConfig
|
|
10
11
|
|
|
@@ -31,8 +32,8 @@ def mux_main(
|
|
|
31
32
|
|
|
32
33
|
vcfg = RuyiVenvConfig.load_from_venv(gm, logger)
|
|
33
34
|
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")
|
|
35
|
+
logger.F(_("the Ruyi toolchain mux is not configured"))
|
|
36
|
+
logger.I(_("check out `ruyi venv` for making a virtual environment"))
|
|
36
37
|
return 1
|
|
37
38
|
|
|
38
39
|
direct_symlink_target = resolve_direct_symlink_target(gm.argv0, vcfg)
|
|
@@ -60,7 +61,11 @@ def mux_main(
|
|
|
60
61
|
tgt_data = vcfg.targets.get(target_tuple)
|
|
61
62
|
if tgt_data is None:
|
|
62
63
|
logger.F(
|
|
63
|
-
|
|
64
|
+
_(
|
|
65
|
+
"internal error: no target data for tuple [yellow]{target_tuple}[/]"
|
|
66
|
+
).format(
|
|
67
|
+
target_tuple=target_tuple,
|
|
68
|
+
)
|
|
64
69
|
)
|
|
65
70
|
return 1
|
|
66
71
|
toolchain_sysroot = tgt_data.get("toolchain_sysroot")
|
|
@@ -83,14 +88,22 @@ def mux_main(
|
|
|
83
88
|
if toolchain_bindir is None:
|
|
84
89
|
# should not happen
|
|
85
90
|
logger.F(
|
|
86
|
-
|
|
91
|
+
_(
|
|
92
|
+
"internal error: no bindir configured for target [yellow]{target_tuple}[/]"
|
|
93
|
+
).format(
|
|
94
|
+
target_tuple=target_tuple,
|
|
95
|
+
)
|
|
87
96
|
)
|
|
88
97
|
return 1
|
|
89
98
|
|
|
90
99
|
binpath = os.path.join(toolchain_bindir, basename)
|
|
91
100
|
|
|
92
101
|
if target_tuple is None:
|
|
93
|
-
logger.F(
|
|
102
|
+
logger.F(
|
|
103
|
+
_("no configured target found for command [yellow]{basename}[/]").format(
|
|
104
|
+
basename=basename,
|
|
105
|
+
)
|
|
106
|
+
)
|
|
94
107
|
return 1
|
|
95
108
|
|
|
96
109
|
logger.D(f"binary to exec: {binpath}")
|
|
@@ -176,7 +189,7 @@ def mux_qemu_main(
|
|
|
176
189
|
logger = gc.logger
|
|
177
190
|
binpath = vcfg.qemu_bin
|
|
178
191
|
if binpath is None:
|
|
179
|
-
logger.F("this virtual environment has no QEMU-like emulator configured")
|
|
192
|
+
logger.F(_("this virtual environment has no QEMU-like emulator configured"))
|
|
180
193
|
return 1
|
|
181
194
|
|
|
182
195
|
if vcfg.profile_emu_env is not None:
|
ruyi/mux/venv/maker.py
CHANGED
|
@@ -7,11 +7,13 @@ import shutil
|
|
|
7
7
|
from typing import Any, Final, Iterator, TypedDict
|
|
8
8
|
|
|
9
9
|
from ...config import GlobalConfig
|
|
10
|
+
from ...i18n import _
|
|
10
11
|
from ...log import RuyiLogger, humanize_list
|
|
11
12
|
from ...ruyipkg.atom import Atom
|
|
12
13
|
from ...ruyipkg.pkg_manifest import BoundPackageManifest, EmulatorProgDecl
|
|
13
14
|
from ...ruyipkg.profile import ProfileProxy
|
|
14
15
|
from ...utils.global_mode import ProvidesGlobalMode
|
|
16
|
+
from ...utils.l10n import match_lang_code
|
|
15
17
|
from ...utils.templating import render_template_str
|
|
16
18
|
from .emulator_cfg import ResolvedEmulatorProg
|
|
17
19
|
|
|
@@ -67,7 +69,7 @@ def do_make_venv(
|
|
|
67
69
|
# this should come after implementation of local state cache
|
|
68
70
|
if tc_atoms_str is None:
|
|
69
71
|
logger.F(
|
|
70
|
-
"You have to specify at least one toolchain atom for now, e.g. [yellow]`-t gnu-plct`[/]"
|
|
72
|
+
_("You have to specify at least one toolchain atom for now, e.g. [yellow]`-t gnu-plct`[/]")
|
|
71
73
|
)
|
|
72
74
|
return 1
|
|
73
75
|
|
|
@@ -75,7 +77,7 @@ def do_make_venv(
|
|
|
75
77
|
|
|
76
78
|
profile = mr.get_profile(profile_name)
|
|
77
79
|
if profile is None:
|
|
78
|
-
logger.F(
|
|
80
|
+
logger.F(_("profile '{profile}' not found").format(profile=profile_name))
|
|
79
81
|
return 1
|
|
80
82
|
|
|
81
83
|
target_arch = ""
|
|
@@ -93,32 +95,46 @@ def do_make_venv(
|
|
|
93
95
|
tc_atom = Atom.parse(tc_atom_str)
|
|
94
96
|
tc_pm = tc_atom.match_in_repo(mr, config.include_prereleases)
|
|
95
97
|
if tc_pm is None:
|
|
96
|
-
logger.F(
|
|
98
|
+
logger.F(_("cannot match a toolchain package with [yellow]{atom}[/]").format(
|
|
99
|
+
atom=tc_atom_str,
|
|
100
|
+
))
|
|
97
101
|
return 1
|
|
98
102
|
|
|
99
103
|
if tc_pm.toolchain_metadata is None:
|
|
100
|
-
logger.F(
|
|
104
|
+
logger.F(_("the package [yellow]{atom}[/] is not a toolchain").format(
|
|
105
|
+
atom=tc_atom_str,
|
|
106
|
+
))
|
|
101
107
|
return 1
|
|
102
108
|
|
|
103
109
|
if not tc_pm.toolchain_metadata.satisfies_quirk_set(profile.need_quirks):
|
|
104
|
-
logger.F(
|
|
105
|
-
|
|
110
|
+
logger.F(_(
|
|
111
|
+
"the package [yellow]{atom}[/] does not support all necessary features for the profile [yellow]{profile}[/]"
|
|
112
|
+
).format(
|
|
113
|
+
atom=tc_atom_str,
|
|
114
|
+
profile=profile_name,
|
|
115
|
+
)
|
|
106
116
|
)
|
|
107
117
|
logger.I(
|
|
108
|
-
|
|
118
|
+
_("quirks needed by profile: {humanized_list}").format(
|
|
119
|
+
humanized_list=humanize_list(profile.need_quirks, item_color='cyan'),
|
|
120
|
+
)
|
|
109
121
|
)
|
|
110
122
|
logger.I(
|
|
111
|
-
|
|
123
|
+
_("quirks provided by package: {humanized_list}").format(
|
|
124
|
+
humanized_list=humanize_list(tc_pm.toolchain_metadata.quirks, item_color='yellow'),
|
|
125
|
+
)
|
|
112
126
|
)
|
|
113
127
|
return 1
|
|
114
128
|
|
|
115
129
|
target_tuple = tc_pm.toolchain_metadata.target
|
|
116
130
|
if target_tuple in seen_target_tuples:
|
|
117
131
|
logger.F(
|
|
118
|
-
|
|
132
|
+
_("the target tuple [yellow]{target_tuple}[/] is already covered by one of the requested toolchains").format(
|
|
133
|
+
target_tuple=target_tuple,
|
|
134
|
+
)
|
|
119
135
|
)
|
|
120
136
|
logger.I(
|
|
121
|
-
"for now, only toolchains with differing target tuples can co-exist in one virtual environment"
|
|
137
|
+
_("for now, only toolchains with differing target tuples can co-exist in one virtual environment")
|
|
122
138
|
)
|
|
123
139
|
return 1
|
|
124
140
|
|
|
@@ -127,7 +143,7 @@ def do_make_venv(
|
|
|
127
143
|
tc_pm.name_for_installation,
|
|
128
144
|
)
|
|
129
145
|
if toolchain_root is None:
|
|
130
|
-
logger.F("cannot find the installed directory for the toolchain")
|
|
146
|
+
logger.F(_("cannot find the installed directory for the toolchain"))
|
|
131
147
|
return 1
|
|
132
148
|
|
|
133
149
|
tc_sysroot_dir: PathLike[Any] | None = None
|
|
@@ -139,7 +155,7 @@ def do_make_venv(
|
|
|
139
155
|
else:
|
|
140
156
|
if sysroot_atom_str is None:
|
|
141
157
|
logger.F(
|
|
142
|
-
"sysroot is requested but the toolchain package does not include one, and [yellow]--sysroot-from[/] is not given"
|
|
158
|
+
_("sysroot is requested but the toolchain package does not include one, and [yellow]--sysroot-from[/] is not given")
|
|
143
159
|
)
|
|
144
160
|
return 1
|
|
145
161
|
|
|
@@ -150,13 +166,17 @@ def do_make_venv(
|
|
|
150
166
|
gcc_pkg_pm = gcc_pkg_atom.match_in_repo(mr, config.include_prereleases)
|
|
151
167
|
if gcc_pkg_pm is None:
|
|
152
168
|
logger.F(
|
|
153
|
-
|
|
169
|
+
_("cannot match a toolchain package with [yellow]{atom}[/]").format(
|
|
170
|
+
atom=sysroot_atom_str,
|
|
171
|
+
)
|
|
154
172
|
)
|
|
155
173
|
return 1
|
|
156
174
|
|
|
157
175
|
if gcc_pkg_pm.toolchain_metadata is None:
|
|
158
176
|
logger.F(
|
|
159
|
-
|
|
177
|
+
_("the package [yellow]{atom}[/] is not a toolchain").format(
|
|
178
|
+
atom=sysroot_atom_str,
|
|
179
|
+
)
|
|
160
180
|
)
|
|
161
181
|
return 1
|
|
162
182
|
|
|
@@ -166,14 +186,16 @@ def do_make_venv(
|
|
|
166
186
|
)
|
|
167
187
|
if gcc_pkg_root is None:
|
|
168
188
|
logger.F(
|
|
169
|
-
"cannot find the installed directory for the sysroot package"
|
|
189
|
+
_("cannot find the installed directory for the sysroot package")
|
|
170
190
|
)
|
|
171
191
|
return 1
|
|
172
192
|
|
|
173
193
|
tc_sysroot_relpath = gcc_pkg_pm.toolchain_metadata.included_sysroot
|
|
174
194
|
if tc_sysroot_relpath is None:
|
|
175
195
|
logger.F(
|
|
176
|
-
|
|
196
|
+
_("sysroot is requested but the package [yellow]{atom}[/] does not contain one").format(
|
|
197
|
+
atom=sysroot_atom_str,
|
|
198
|
+
)
|
|
177
199
|
)
|
|
178
200
|
return 1
|
|
179
201
|
|
|
@@ -191,7 +213,7 @@ def do_make_venv(
|
|
|
191
213
|
# for now, require this directory to be present (or clang would barely work)
|
|
192
214
|
if gcc_install_dir is None:
|
|
193
215
|
logger.F(
|
|
194
|
-
"cannot find a GCC include & lib directory in the sysroot package"
|
|
216
|
+
_("cannot find a GCC include & lib directory in the sysroot package")
|
|
195
217
|
)
|
|
196
218
|
return 1
|
|
197
219
|
|
|
@@ -228,9 +250,11 @@ def do_make_venv(
|
|
|
228
250
|
warn_differing_target_arch = True
|
|
229
251
|
|
|
230
252
|
if warn_differing_target_arch:
|
|
231
|
-
logger.W("multiple toolchains specified with differing target architecture")
|
|
253
|
+
logger.W(_("multiple toolchains specified with differing target architecture"))
|
|
232
254
|
logger.I(
|
|
233
|
-
|
|
255
|
+
_("using the target architecture of the first toolchain: [yellow]{arch}[/]").format(
|
|
256
|
+
arch=target_arch,
|
|
257
|
+
)
|
|
234
258
|
)
|
|
235
259
|
|
|
236
260
|
# Now handle the emulator.
|
|
@@ -240,17 +264,24 @@ def do_make_venv(
|
|
|
240
264
|
emu_atom = Atom.parse(emu_atom_str)
|
|
241
265
|
emu_pm = emu_atom.match_in_repo(mr, config.include_prereleases)
|
|
242
266
|
if emu_pm is None:
|
|
243
|
-
logger.F(
|
|
267
|
+
logger.F(_("cannot match an emulator package with [yellow]{atom}[/]").format(
|
|
268
|
+
atom=emu_atom_str,
|
|
269
|
+
))
|
|
244
270
|
return 1
|
|
245
271
|
|
|
246
272
|
if emu_pm.emulator_metadata is None:
|
|
247
|
-
logger.F(
|
|
273
|
+
logger.F(_("the package [yellow]{atom}[/] is not an emulator").format(
|
|
274
|
+
atom=emu_atom_str,
|
|
275
|
+
))
|
|
248
276
|
return 1
|
|
249
277
|
|
|
250
278
|
emu_progs = list(emu_pm.emulator_metadata.list_for_arch(target_arch))
|
|
251
279
|
if not emu_progs:
|
|
252
280
|
logger.F(
|
|
253
|
-
|
|
281
|
+
_("the emulator package [yellow]{atom}[/] does not support the target architecture [yellow]{arch}[/]").format(
|
|
282
|
+
atom=emu_atom_str,
|
|
283
|
+
arch=target_arch,
|
|
284
|
+
)
|
|
254
285
|
)
|
|
255
286
|
return 1
|
|
256
287
|
|
|
@@ -260,13 +291,20 @@ def do_make_venv(
|
|
|
260
291
|
emu_pm.emulator_metadata.quirks,
|
|
261
292
|
):
|
|
262
293
|
logger.F(
|
|
263
|
-
|
|
294
|
+
_("the package [yellow]{atom}[/] does not support all necessary features for the profile [yellow]{profile}[/]").format(
|
|
295
|
+
atom=emu_atom_str,
|
|
296
|
+
profile=profile_name,
|
|
297
|
+
)
|
|
264
298
|
)
|
|
265
299
|
logger.I(
|
|
266
|
-
|
|
300
|
+
_("quirks needed by profile: {humanized_list}").format(
|
|
301
|
+
humanized_list=humanize_list(profile.get_needed_emulator_pkg_flavors(prog.flavor), item_color='cyan'),
|
|
302
|
+
)
|
|
267
303
|
)
|
|
268
304
|
logger.I(
|
|
269
|
-
|
|
305
|
+
_("quirks provided by package: {humanized_list}").format(
|
|
306
|
+
humanized_list=humanize_list(emu_pm.emulator_metadata.quirks or [], item_color='yellow'),
|
|
307
|
+
)
|
|
270
308
|
)
|
|
271
309
|
return 1
|
|
272
310
|
|
|
@@ -275,7 +313,7 @@ def do_make_venv(
|
|
|
275
313
|
emu_pm.name_for_installation,
|
|
276
314
|
)
|
|
277
315
|
if emu_root is None:
|
|
278
|
-
logger.F("cannot find the installed directory for the emulator")
|
|
316
|
+
logger.F(_("cannot find the installed directory for the emulator"))
|
|
279
317
|
return 1
|
|
280
318
|
|
|
281
319
|
venv_metadata["emulator_pkgs"][target_arch] = _venv_pkg_info_from_pkg(emu_pm)
|
|
@@ -291,21 +329,28 @@ def do_make_venv(
|
|
|
291
329
|
)
|
|
292
330
|
if extra_cmd_pm is None:
|
|
293
331
|
logger.F(
|
|
294
|
-
|
|
332
|
+
_("cannot match an extra command package with [yellow]{atom}[/]").format(
|
|
333
|
+
atom=extra_cmd_atom_str,
|
|
334
|
+
)
|
|
295
335
|
)
|
|
296
336
|
return 1
|
|
297
337
|
|
|
298
338
|
extra_cmd_bm = extra_cmd_pm.binary_metadata
|
|
299
339
|
if not extra_cmd_bm:
|
|
300
340
|
logger.F(
|
|
301
|
-
|
|
341
|
+
_("the package [yellow]{atom}[/] is not a binary-providing package").format(
|
|
342
|
+
atom=extra_cmd_atom_str,
|
|
343
|
+
)
|
|
302
344
|
)
|
|
303
345
|
return 1
|
|
304
346
|
|
|
305
347
|
extra_cmds_decl = extra_cmd_bm.get_commands_for_host(host)
|
|
306
348
|
if not extra_cmds_decl:
|
|
307
349
|
logger.W(
|
|
308
|
-
|
|
350
|
+
_("the package [yellow]{atom}[/] does not provide any command for host [yellow]{host}[/], ignoring").format(
|
|
351
|
+
atom=extra_cmd_atom_str,
|
|
352
|
+
host=host,
|
|
353
|
+
)
|
|
309
354
|
)
|
|
310
355
|
continue
|
|
311
356
|
|
|
@@ -315,7 +360,9 @@ def do_make_venv(
|
|
|
315
360
|
)
|
|
316
361
|
if cmd_root is None:
|
|
317
362
|
logger.F(
|
|
318
|
-
|
|
363
|
+
_("cannot find the installed directory for the package [yellow]{pkg}[/]").format(
|
|
364
|
+
pkg=extra_cmd_pm.name_for_installation,
|
|
365
|
+
)
|
|
319
366
|
)
|
|
320
367
|
return 1
|
|
321
368
|
cmd_root = pathlib.Path(cmd_root)
|
|
@@ -329,7 +376,7 @@ def do_make_venv(
|
|
|
329
376
|
# we don't allow commands to resolve outside of the
|
|
330
377
|
# providing package's install root
|
|
331
378
|
logger.F(
|
|
332
|
-
"internal error: resolved command path is outside of the providing package"
|
|
379
|
+
_("internal error: resolved command path is outside of the providing package")
|
|
333
380
|
)
|
|
334
381
|
return 1
|
|
335
382
|
|
|
@@ -338,10 +385,17 @@ def do_make_venv(
|
|
|
338
385
|
|
|
339
386
|
if override_name is not None:
|
|
340
387
|
logger.I(
|
|
341
|
-
|
|
388
|
+
_("Creating a Ruyi virtual environment [cyan]'{name}'[/] at [green]{dest}[/]...").format(
|
|
389
|
+
name=override_name,
|
|
390
|
+
dest=dest,
|
|
391
|
+
)
|
|
342
392
|
)
|
|
343
393
|
else:
|
|
344
|
-
logger.I(
|
|
394
|
+
logger.I(
|
|
395
|
+
_("Creating a Ruyi virtual environment at [green]{dest}[/]...").format(
|
|
396
|
+
dest=dest,
|
|
397
|
+
)
|
|
398
|
+
)
|
|
345
399
|
|
|
346
400
|
maker = VenvMaker(
|
|
347
401
|
config,
|
|
@@ -356,9 +410,11 @@ def do_make_venv(
|
|
|
356
410
|
)
|
|
357
411
|
maker.provision()
|
|
358
412
|
|
|
413
|
+
# TODO: move the template to PO
|
|
414
|
+
locale = match_lang_code(config.lang_code, avail=("en", "zh_CN"))
|
|
359
415
|
logger.I(
|
|
360
416
|
render_template_str(
|
|
361
|
-
"prompt.venv-created.txt",
|
|
417
|
+
f"prompt.venv-created.{locale}.txt",
|
|
362
418
|
{
|
|
363
419
|
"sysroot": maker.sysroot_destdir(None),
|
|
364
420
|
},
|
|
@@ -559,7 +615,9 @@ class VenvMaker:
|
|
|
559
615
|
for cmd, dest in extra_cmds.items():
|
|
560
616
|
if cmd in cmd_metadata_map:
|
|
561
617
|
self.logger.W(
|
|
562
|
-
|
|
618
|
+
_("extra command {cmd} is already provided by another package, overriding it").format(
|
|
619
|
+
cmd=cmd,
|
|
620
|
+
)
|
|
563
621
|
)
|
|
564
622
|
cmd_metadata_map[cmd] = {
|
|
565
623
|
"dest": dest,
|
ruyi/mux/venv/venv_cli.py
CHANGED
|
@@ -3,6 +3,7 @@ import pathlib
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
5
|
from ...cli.cmd import RootCommand
|
|
6
|
+
from ...i18n import _
|
|
6
7
|
|
|
7
8
|
if TYPE_CHECKING:
|
|
8
9
|
from ...cli.completion import ArgumentParser
|
|
@@ -12,55 +13,57 @@ if TYPE_CHECKING:
|
|
|
12
13
|
class VenvCommand(
|
|
13
14
|
RootCommand,
|
|
14
15
|
cmd="venv",
|
|
15
|
-
help="Generate a virtual environment adapted to the chosen toolchain and profile",
|
|
16
|
+
help=_("Generate a virtual environment adapted to the chosen toolchain and profile"),
|
|
16
17
|
):
|
|
17
18
|
@classmethod
|
|
18
19
|
def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
|
|
19
|
-
p.add_argument("profile", type=str, help="Profile to use for the environment")
|
|
20
|
-
|
|
20
|
+
p.add_argument("profile", type=str, help=_("Profile to use for the environment"),
|
|
21
|
+
)
|
|
22
|
+
p.add_argument("dest", type=str, help=_("Path to the new virtual environment"),
|
|
23
|
+
)
|
|
21
24
|
p.add_argument(
|
|
22
25
|
"--name",
|
|
23
26
|
"-n",
|
|
24
27
|
type=str,
|
|
25
28
|
default=None,
|
|
26
|
-
help="Override the venv's name",
|
|
29
|
+
help=_("Override the venv's name"),
|
|
27
30
|
)
|
|
28
31
|
p.add_argument(
|
|
29
32
|
"--toolchain",
|
|
30
33
|
"-t",
|
|
31
34
|
type=str,
|
|
32
35
|
action="append",
|
|
33
|
-
help="Specifier(s) (atoms) of the toolchain package(s) to use",
|
|
36
|
+
help=_("Specifier(s) (atoms) of the toolchain package(s) to use"),
|
|
34
37
|
)
|
|
35
38
|
p.add_argument(
|
|
36
39
|
"--emulator",
|
|
37
40
|
"-e",
|
|
38
41
|
type=str,
|
|
39
|
-
help="Specifier (atom) of the emulator package to use",
|
|
42
|
+
help=_("Specifier (atom) of the emulator package to use"),
|
|
40
43
|
)
|
|
41
44
|
p.add_argument(
|
|
42
45
|
"--with-sysroot",
|
|
43
46
|
action="store_true",
|
|
44
47
|
dest="with_sysroot",
|
|
45
48
|
default=True,
|
|
46
|
-
help="Provision a fresh sysroot inside the new virtual environment (default)",
|
|
49
|
+
help=_("Provision a fresh sysroot inside the new virtual environment (default)"),
|
|
47
50
|
)
|
|
48
51
|
p.add_argument(
|
|
49
52
|
"--without-sysroot",
|
|
50
53
|
action="store_false",
|
|
51
54
|
dest="with_sysroot",
|
|
52
|
-
help="Do not include a sysroot inside the new virtual environment",
|
|
55
|
+
help=_("Do not include a sysroot inside the new virtual environment"),
|
|
53
56
|
)
|
|
54
57
|
p.add_argument(
|
|
55
58
|
"--sysroot-from",
|
|
56
59
|
type=str,
|
|
57
|
-
help="Specifier (atom) of the sysroot package to use, in favor of the toolchain-included one if applicable",
|
|
60
|
+
help=_("Specifier (atom) of the sysroot package to use, in favor of the toolchain-included one if applicable"),
|
|
58
61
|
)
|
|
59
62
|
p.add_argument(
|
|
60
63
|
"--extra-commands-from",
|
|
61
64
|
type=str,
|
|
62
65
|
action="append",
|
|
63
|
-
help="Specifier(s) (atoms) of extra package(s) to add commands to the new virtual environment",
|
|
66
|
+
help=_("Specifier(s) (atoms) of extra package(s) to add commands to the new virtual environment"),
|
|
64
67
|
)
|
|
65
68
|
|
|
66
69
|
@classmethod
|
ruyi/pluginhost/plugin_cli.py
CHANGED
|
@@ -2,6 +2,7 @@ import argparse
|
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
from ..cli.cmd import AdminCommand
|
|
5
|
+
from ..i18n import _
|
|
5
6
|
|
|
6
7
|
if TYPE_CHECKING:
|
|
7
8
|
from ..cli.completion import ArgumentParser
|
|
@@ -11,7 +12,7 @@ if TYPE_CHECKING:
|
|
|
11
12
|
class AdminRunPluginCommand(
|
|
12
13
|
AdminCommand,
|
|
13
14
|
cmd="run-plugin-cmd",
|
|
14
|
-
help="Run a plugin-defined command",
|
|
15
|
+
help=_("Run a plugin-defined command"),
|
|
15
16
|
):
|
|
16
17
|
@classmethod
|
|
17
18
|
def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
|
|
@@ -19,14 +20,14 @@ class AdminRunPluginCommand(
|
|
|
19
20
|
"cmd_name",
|
|
20
21
|
type=str,
|
|
21
22
|
metavar="COMMAND-NAME",
|
|
22
|
-
help="Command name",
|
|
23
|
+
help=_("Command name"),
|
|
23
24
|
)
|
|
24
25
|
p.add_argument(
|
|
25
26
|
"cmd_args",
|
|
26
27
|
type=str,
|
|
27
28
|
nargs="*",
|
|
28
29
|
metavar="COMMAND-ARG",
|
|
29
|
-
help="Arguments to pass to the plugin command",
|
|
30
|
+
help=_("Arguments to pass to the plugin command"),
|
|
30
31
|
)
|
|
31
32
|
|
|
32
33
|
@classmethod
|