xbuild 0.0.1__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.
- xbuild/__init__.py +20 -0
- xbuild/__main__.py +224 -0
- xbuild-0.0.1.dist-info/METADATA +99 -0
- xbuild-0.0.1.dist-info/RECORD +16 -0
- xbuild-0.0.1.dist-info/WHEEL +5 -0
- xbuild-0.0.1.dist-info/entry_points.txt +3 -0
- xbuild-0.0.1.dist-info/licenses/LICENSE +27 -0
- xbuild-0.0.1.dist-info/top_level.txt +2 -0
- xvenv/__init__.py +6 -0
- xvenv/__main__.py +90 -0
- xvenv/_cross_target.py.tmpl +95 -0
- xvenv/convert.py +174 -0
- xvenv/platforms/__init__.py +0 -0
- xvenv/platforms/android.py +79 -0
- xvenv/platforms/emscripten.py +16 -0
- xvenv/platforms/ios.py +33 -0
xbuild/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
"__version__",
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
# Read version from SCM metadata
|
|
7
|
+
# This will only exist in a development environment
|
|
8
|
+
from setuptools_scm import get_version
|
|
9
|
+
|
|
10
|
+
# Excluded from coverage because a pure test environment (such as the one
|
|
11
|
+
# used by tox in CI) won't have setuptools_scm
|
|
12
|
+
__version__ = get_version("../..", relative_to=__file__) # pragma: no cover
|
|
13
|
+
except (ModuleNotFoundError, LookupError): # pragma: no-cover-if-missing-setuptools_scm
|
|
14
|
+
# If setuptools_scm isn't in the environment, the call to import will fail.
|
|
15
|
+
# If it *is* in the environment, but the code isn't a git checkout (e.g.,
|
|
16
|
+
# it's been pip installed non-editable) the call to get_version() will fail.
|
|
17
|
+
# If either of these occurs, read version from the installer metadata.
|
|
18
|
+
from importlib.metadata import version
|
|
19
|
+
|
|
20
|
+
__version__ = version("xbuild")
|
xbuild/__main__.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from collections.abc import Sequence
|
|
7
|
+
from functools import partial
|
|
8
|
+
|
|
9
|
+
from build import ProjectBuilder
|
|
10
|
+
from build import env as _env
|
|
11
|
+
from build.__main__ import (
|
|
12
|
+
_cprint,
|
|
13
|
+
_handle_build_error,
|
|
14
|
+
_max_terminal_width,
|
|
15
|
+
_natural_language_list,
|
|
16
|
+
_setup_cli,
|
|
17
|
+
_styles,
|
|
18
|
+
)
|
|
19
|
+
from build._types import ConfigSettings, Distribution, StrPath
|
|
20
|
+
from build.env import DefaultIsolatedEnv
|
|
21
|
+
|
|
22
|
+
import xbuild
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _build_in_isolated_env(
|
|
26
|
+
srcdir: StrPath,
|
|
27
|
+
outdir: StrPath,
|
|
28
|
+
distribution: Distribution,
|
|
29
|
+
config_settings: ConfigSettings | None,
|
|
30
|
+
installer: _env.Installer,
|
|
31
|
+
) -> str:
|
|
32
|
+
with DefaultIsolatedEnv(installer=installer) as env:
|
|
33
|
+
builder = ProjectBuilder.from_isolated_env(env, srcdir)
|
|
34
|
+
# first install the build dependencies
|
|
35
|
+
env.install(builder.build_system_requires)
|
|
36
|
+
# then get the extra required dependencies from the backend
|
|
37
|
+
# (which was installed in the call above :P)
|
|
38
|
+
env.install(builder.get_requires_for_build(distribution, config_settings or {}))
|
|
39
|
+
return builder.build(distribution, outdir, config_settings or {})
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main_parser() -> argparse.ArgumentParser:
|
|
43
|
+
"""Construct the main parser."""
|
|
44
|
+
formatter_class = partial(
|
|
45
|
+
argparse.RawDescriptionHelpFormatter, width=min(_max_terminal_width, 127)
|
|
46
|
+
)
|
|
47
|
+
# Workaround for 3.14.0 beta 1, can remove once beta 2 is out
|
|
48
|
+
if sys.version_info >= (3, 14):
|
|
49
|
+
formatter_class = partial(formatter_class, color=True)
|
|
50
|
+
|
|
51
|
+
make_parser = partial(
|
|
52
|
+
argparse.ArgumentParser,
|
|
53
|
+
description="A cross-platform build backend for Python",
|
|
54
|
+
# Prevent argparse from taking up the entire width of the terminal window
|
|
55
|
+
# which impedes readability. Also keep the description formatted.
|
|
56
|
+
formatter_class=formatter_class,
|
|
57
|
+
)
|
|
58
|
+
if sys.version_info >= (3, 14):
|
|
59
|
+
make_parser = partial(make_parser, suggest_on_error=True, color=True)
|
|
60
|
+
|
|
61
|
+
parser = make_parser()
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"srcdir",
|
|
64
|
+
type=str,
|
|
65
|
+
nargs="?",
|
|
66
|
+
default=os.getcwd(),
|
|
67
|
+
help="source directory (defaults to current directory)",
|
|
68
|
+
)
|
|
69
|
+
parser.add_argument(
|
|
70
|
+
"--version",
|
|
71
|
+
"-V",
|
|
72
|
+
action="version",
|
|
73
|
+
version=f"xbuild {xbuild.__version__} ({','.join(xbuild.__path__)})",
|
|
74
|
+
)
|
|
75
|
+
parser.add_argument(
|
|
76
|
+
"--verbose",
|
|
77
|
+
"-v",
|
|
78
|
+
dest="verbosity",
|
|
79
|
+
action="count",
|
|
80
|
+
default=0,
|
|
81
|
+
help="increase verbosity",
|
|
82
|
+
)
|
|
83
|
+
# parser.add_argument(
|
|
84
|
+
# "--sdist",
|
|
85
|
+
# "-s",
|
|
86
|
+
# dest="distributions",
|
|
87
|
+
# action="append_const",
|
|
88
|
+
# const="sdist",
|
|
89
|
+
# help="build a source distribution (disables the default behavior)",
|
|
90
|
+
# )
|
|
91
|
+
# parser.add_argument(
|
|
92
|
+
# "--wheel",
|
|
93
|
+
# "-w",
|
|
94
|
+
# dest="distributions",
|
|
95
|
+
# action="append_const",
|
|
96
|
+
# const="wheel",
|
|
97
|
+
# help="build a wheel (disables the default behavior)",
|
|
98
|
+
# )
|
|
99
|
+
parser.add_argument(
|
|
100
|
+
"--outdir",
|
|
101
|
+
"-o",
|
|
102
|
+
type=str,
|
|
103
|
+
help=f"output directory (defaults to {{srcdir}}{os.sep}dist)",
|
|
104
|
+
metavar="PATH",
|
|
105
|
+
)
|
|
106
|
+
# parser.add_argument(
|
|
107
|
+
# "--skip-dependency-check",
|
|
108
|
+
# "-x",
|
|
109
|
+
# action="store_true",
|
|
110
|
+
# help="do not check that build dependencies are installed",
|
|
111
|
+
# )
|
|
112
|
+
# env_group = parser.add_mutually_exclusive_group()
|
|
113
|
+
# env_group.add_argument(
|
|
114
|
+
# "--no-isolation",
|
|
115
|
+
# "-n",
|
|
116
|
+
# action="store_true",
|
|
117
|
+
# help="disable building the project in an isolated virtual environment. "
|
|
118
|
+
# "Build dependencies must be installed separately when this option is used",
|
|
119
|
+
# )
|
|
120
|
+
# env_group.add_argument(
|
|
121
|
+
# "--installer",
|
|
122
|
+
# choices=_env.INSTALLERS,
|
|
123
|
+
# help="Python package installer to use (defaults to pip)",
|
|
124
|
+
# )
|
|
125
|
+
# config_group = parser.add_mutually_exclusive_group()
|
|
126
|
+
# config_group.add_argument(
|
|
127
|
+
# "--config-setting",
|
|
128
|
+
# "-C",
|
|
129
|
+
# dest="config_settings",
|
|
130
|
+
# action="append",
|
|
131
|
+
# help=(
|
|
132
|
+
# "settings to pass to the backend. Multiple settings can be "
|
|
133
|
+
# "provided. Settings beginning with a hyphen will erroneously "
|
|
134
|
+
# "be interpreted as options to build if separated by a space "
|
|
135
|
+
# "character; use ``--config-setting=--my-setting -C--my-other-setting``"
|
|
136
|
+
# ),
|
|
137
|
+
# metavar="KEY[=VALUE]",
|
|
138
|
+
# )
|
|
139
|
+
# config_group.add_argument(
|
|
140
|
+
# "--config-json",
|
|
141
|
+
# dest="config_json",
|
|
142
|
+
# help=(
|
|
143
|
+
# "settings to pass to the backend as a JSON object. This is an "
|
|
144
|
+
# "alternative to --config-setting that allows complex nested "
|
|
145
|
+
# "structures. Cannot be used together with --config-setting"
|
|
146
|
+
# ),
|
|
147
|
+
# metavar="JSON_STRING",
|
|
148
|
+
# )
|
|
149
|
+
|
|
150
|
+
return parser
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def main(cli_args: Sequence[str], prog: str | None = None) -> None:
|
|
154
|
+
"""Parse the CLI arguments and invoke the build process.
|
|
155
|
+
|
|
156
|
+
:param cli_args: CLI arguments
|
|
157
|
+
:param prog: Program name to show in help text
|
|
158
|
+
"""
|
|
159
|
+
parser = main_parser()
|
|
160
|
+
if prog:
|
|
161
|
+
parser.prog = prog
|
|
162
|
+
args = parser.parse_args(cli_args)
|
|
163
|
+
|
|
164
|
+
_setup_cli(verbosity=args.verbosity)
|
|
165
|
+
|
|
166
|
+
config_settings = {}
|
|
167
|
+
|
|
168
|
+
# # Handle --config-json
|
|
169
|
+
# if args.config_json:
|
|
170
|
+
# try:
|
|
171
|
+
# config_settings = json.loads(args.config_json)
|
|
172
|
+
# if not isinstance(config_settings, dict):
|
|
173
|
+
# _error(
|
|
174
|
+
# "--config-json must contain a JSON object (dict), "
|
|
175
|
+
# "not a list or primitive value"
|
|
176
|
+
# )
|
|
177
|
+
# except json.JSONDecodeError as e:
|
|
178
|
+
# _error(f"Invalid JSON in --config-json: {e}")
|
|
179
|
+
|
|
180
|
+
# # Handle --config-setting (original logic)
|
|
181
|
+
# elif args.config_settings:
|
|
182
|
+
# for arg in args.config_settings:
|
|
183
|
+
# setting, _, value = arg.partition("=")
|
|
184
|
+
# if setting not in config_settings:
|
|
185
|
+
# config_settings[setting] = value
|
|
186
|
+
# else:
|
|
187
|
+
# if not isinstance(config_settings[setting], list):
|
|
188
|
+
# config_settings[setting] = [config_settings[setting]]
|
|
189
|
+
|
|
190
|
+
# config_settings[setting].append(value)
|
|
191
|
+
|
|
192
|
+
# outdir is relative to srcdir only if omitted.
|
|
193
|
+
outdir = os.path.join(args.srcdir, "dist") if args.outdir is None else args.outdir
|
|
194
|
+
|
|
195
|
+
with _handle_build_error():
|
|
196
|
+
built = [
|
|
197
|
+
_build_in_isolated_env(
|
|
198
|
+
args.srcdir,
|
|
199
|
+
outdir,
|
|
200
|
+
"wheel",
|
|
201
|
+
config_settings,
|
|
202
|
+
"pip",
|
|
203
|
+
)
|
|
204
|
+
]
|
|
205
|
+
artifact_list = _natural_language_list(
|
|
206
|
+
[
|
|
207
|
+
"{underline}{}{reset}{bold}{green}".format(artifact, **_styles.get())
|
|
208
|
+
for artifact in built
|
|
209
|
+
]
|
|
210
|
+
)
|
|
211
|
+
_cprint("{bold}{green}Successfully built {}{reset}", artifact_list)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def entrypoint() -> None:
|
|
215
|
+
main(sys.argv[1:])
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
if __name__ == "__main__": # pragma: no cover
|
|
219
|
+
main(sys.argv[1:], "python -m build")
|
|
220
|
+
|
|
221
|
+
__all__ = [
|
|
222
|
+
"main",
|
|
223
|
+
"main_parser",
|
|
224
|
+
]
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: xbuild
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A PEP517 build frontend for cross-compiling wheels.
|
|
5
|
+
Author-email: Russell Keith-Magee <russell@keith-magee.com>
|
|
6
|
+
Maintainer-email: Russell Keith-Magee <russell@keith-magee.com>
|
|
7
|
+
License-Expression: BSD-3-Clause
|
|
8
|
+
Project-URL: Project, https://github.com/beeware/xbuild
|
|
9
|
+
Project-URL: Funding, https://beeware.org/contributing/membership/
|
|
10
|
+
Project-URL: Tracker, https://github.com/beeware/xbuild/issues
|
|
11
|
+
Project-URL: Source, https://github.com/beeware/xbuild
|
|
12
|
+
Keywords: pep517,build
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Classifier: Topic :: System :: Software Distribution
|
|
21
|
+
Classifier: Topic :: Software Development
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Requires-Python: >=3.13
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: build==1.3.0
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# xbuild
|
|
30
|
+
|
|
31
|
+
[](https://pypi.python.org/pypi/xbuild)
|
|
32
|
+
[](https://pypi.python.org/pypi/xbuild)
|
|
33
|
+
[](https://pypi.python.org/pypi/xbuild)
|
|
34
|
+
[](https://github.com/beeware/xbuild/blob/master/LICENSE)
|
|
35
|
+
[](https://beeware.org/bee/chat/)
|
|
36
|
+
|
|
37
|
+
`xbuild` is PEP517 build frontend that has additions and extensions to support cross-compiling wheels for platforms where compilation cannot be performed natively - most notably:
|
|
38
|
+
|
|
39
|
+
* Android
|
|
40
|
+
* Emscripten (WASM)
|
|
41
|
+
* iOS
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
### Creating a cross virtual environment
|
|
46
|
+
|
|
47
|
+
To create a cross virtual environment, you will need a distribution of Python that has been compiled for Android, Emscripten or iOS. Create a virtual environment for your build platform (i.e., the platform where you will be compiling), then use the `xvenv` script to convert that virtual environment in to a cross environment.
|
|
48
|
+
|
|
49
|
+
$ python3 -m venv venv
|
|
50
|
+
$ source venv/bin/activate
|
|
51
|
+
(venv) $ pip install xbuild
|
|
52
|
+
(venv) $ python -m venv x-venv
|
|
53
|
+
(venv) $ xvenv --sysconfig path/to/_sysconfig_vars__...json x-venv
|
|
54
|
+
|
|
55
|
+
You can then activate the cross virtual environment. For example, if `x-venv` was constructed using an iOS simulator sysconfig vars file (`_sysconfig_vars__ios_arm64-iphonesimulator.json`), you would see output like:
|
|
56
|
+
|
|
57
|
+
$ source x-venv/bin/activate
|
|
58
|
+
(x-venv) $ python -c "import sys; print(sys.platform)"
|
|
59
|
+
ios
|
|
60
|
+
(x-venv) $ python -c "import sys; print(sys.implementation._multiarch)"
|
|
61
|
+
arm64-iphonesimulator
|
|
62
|
+
|
|
63
|
+
This should now print the platform identifier for the target platform, not your build platform.
|
|
64
|
+
|
|
65
|
+
You can also configure xvenv with a `_sysconfigdata` Python file (e.g., `_sysconfigdata__ios_arm64-iphonesimulator.py`), instead of the `_sysconfig_var` JSON file. You'll have to use `_sysconfigdata` if you're on Python 3.13 (as the JSON format was only introduced in Python 3.14)
|
|
66
|
+
|
|
67
|
+
If you are in the cross environment, and you need to temporarily convert it back to the build platform, you can do so with the `XBUILD_ENV` environment variable. For example, if `x-venv` is an iOS cross environment:
|
|
68
|
+
|
|
69
|
+
$ source x-venv/bin/activate
|
|
70
|
+
(x-venv) $ python -c "import sys; print(sys.platform)"
|
|
71
|
+
ios
|
|
72
|
+
(x-venv) $ XBUILD_ENV=off python -c "import sys; print(sys.platform)"
|
|
73
|
+
darwin
|
|
74
|
+
|
|
75
|
+
## How this works
|
|
76
|
+
|
|
77
|
+
The cross build environment does not run the target platform binaries on the build platform - it uses binaries for the build platform, but monkey-patches the Python interpreter at startup so that any question asking details about the platform returns details about the target platform. For example, if you create an iOS cross-platform environment on a macOS machine, you'll be using the macOS `python.exe`; but if you ask for `sys.platform`, the answer will be `ios`, not `darwin`.
|
|
78
|
+
|
|
79
|
+
## Contributing
|
|
80
|
+
|
|
81
|
+
To set up a development environment:
|
|
82
|
+
|
|
83
|
+
$ python3 -m venv venv
|
|
84
|
+
$ source venv/bin/activate
|
|
85
|
+
(venv) $ python -m pip install -U pip
|
|
86
|
+
(venv) $ python -m pip install -e . --group dev
|
|
87
|
+
|
|
88
|
+
## Community
|
|
89
|
+
|
|
90
|
+
`xbuild` is part of the [BeeWare suite](http://beeware.org). You can talk to the community through:
|
|
91
|
+
|
|
92
|
+
- [@pybeeware on Twitter](https://twitter.com/pybeeware)
|
|
93
|
+
- [Discord](https://beeware.org/bee/chat/)
|
|
94
|
+
|
|
95
|
+
We foster a welcoming and respectful community as described in our [BeeWare Community Code of Conduct](http://beeware.org/community/behavior/).
|
|
96
|
+
|
|
97
|
+
## Contributing
|
|
98
|
+
|
|
99
|
+
If you experience problems with `xbuild`, [log them on GitHub](https://github.com/beeware/xbuild/issues). If you want to contribute code, please [fork the code](https://github.com/beeware/xbuild) and [submit a pull request](https://github.com/beeware/xbuild/pulls).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
xbuild/__init__.py,sha256=u3wDO6aPZzsZgXih4JqrDNHlgZ0SHvHDaYCy24P8wTg,869
|
|
2
|
+
xbuild/__main__.py,sha256=PeaMnXoI4aOHXdkPcTh1WIvssuRSXt_62eXR25L6lM8,7083
|
|
3
|
+
xbuild-0.0.1.dist-info/licenses/LICENSE,sha256=XYQSYHFcE30mh1LcQccvGSWMBfYnRdvEZoleqfnUios,1522
|
|
4
|
+
xvenv/__init__.py,sha256=6jjRnZghwSf0mG6KbapDKm1WJUDWBwv7Ro7GukzSCnY,103
|
|
5
|
+
xvenv/__main__.py,sha256=eppmUqYjKlVl80023Y849Wceq11WTec0Kr5XPhmDqvM,2094
|
|
6
|
+
xvenv/_cross_target.py.tmpl,sha256=CHcZ3-P7j-VGZBaG2gFU8QJmFwY4ssKVk0UYOBZrOnE,3247
|
|
7
|
+
xvenv/convert.py,sha256=UJDhOgfiqQ0z-Ddy_0GzgcYFSs4F_WXAg7shSwQyoXI,6698
|
|
8
|
+
xvenv/platforms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
xvenv/platforms/android.py,sha256=cmjRo3oekxnImFiRiPvElT6AjKlWDzL7R-W7KJQHnmc,2282
|
|
10
|
+
xvenv/platforms/emscripten.py,sha256=TJjUl5yiPhuqloiWuEM9b2py7N6JlQZ80CXbBJ8JB1E,526
|
|
11
|
+
xvenv/platforms/ios.py,sha256=qcK9DD7QcJsI6xrLB59EqD9T6yUA_kGov6v33Hk025k,1205
|
|
12
|
+
xbuild-0.0.1.dist-info/METADATA,sha256=FL0cDFumxqXsfqzxhxq0U3N-T1jwQ-CTDcjQUKwNVb8,5086
|
|
13
|
+
xbuild-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
14
|
+
xbuild-0.0.1.dist-info/entry_points.txt,sha256=GIHORBLMolwW-AkaAFdDP6-3rks6u6V8d_ZpTWy_K9Q,88
|
|
15
|
+
xbuild-0.0.1.dist-info/top_level.txt,sha256=vI5VC2agCGAoVSufMhr23Z0E9DwwK--TDsxcyRh5IVc,13
|
|
16
|
+
xbuild-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Copyright (c) 2025 Russell Keith-Magee.
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
|
5
|
+
are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
this list of conditions and the following disclaimer.
|
|
9
|
+
|
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
notice, this list of conditions and the following disclaimer in the
|
|
12
|
+
documentation and/or other materials provided with the distribution.
|
|
13
|
+
|
|
14
|
+
3. Neither the name of xbuild nor the names of its
|
|
15
|
+
contributors may be used to endorse or promote products derived from this
|
|
16
|
+
software without specific prior written permission.
|
|
17
|
+
|
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
22
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
23
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
24
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
25
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
27
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
xvenv/__init__.py
ADDED
xvenv/__main__.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from argparse import ArgumentParser
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from build.__main__ import (
|
|
9
|
+
_cprint,
|
|
10
|
+
_error,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
import xvenv
|
|
14
|
+
from xvenv.convert import convert_venv
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main_parser():
|
|
18
|
+
parser = ArgumentParser(
|
|
19
|
+
"xvenv",
|
|
20
|
+
description=(
|
|
21
|
+
"Convert a native virtual environment into a cross-platform "
|
|
22
|
+
"virtual environment."
|
|
23
|
+
),
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"--version",
|
|
27
|
+
"-V",
|
|
28
|
+
action="version",
|
|
29
|
+
version=f"xvenv {xvenv.__version__}",
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
"--verbose",
|
|
33
|
+
"-v",
|
|
34
|
+
dest="verbosity",
|
|
35
|
+
action="count",
|
|
36
|
+
default=0,
|
|
37
|
+
help="increase verbosity",
|
|
38
|
+
)
|
|
39
|
+
parser.add_argument(
|
|
40
|
+
"-s",
|
|
41
|
+
"--sysconfig",
|
|
42
|
+
help="The path to a sysconfig_vars JSON file or sysconfigdata Python file",
|
|
43
|
+
)
|
|
44
|
+
parser.add_argument(
|
|
45
|
+
"venv",
|
|
46
|
+
help="The location of a native virtual environment",
|
|
47
|
+
)
|
|
48
|
+
return parser
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def main(cli_args: Sequence[str], prog: str | None = None) -> None:
|
|
52
|
+
"""Parse the CLI arguments and convert the venv.
|
|
53
|
+
|
|
54
|
+
:param cli_args: CLI arguments
|
|
55
|
+
:param prog: Program name to show in help text
|
|
56
|
+
"""
|
|
57
|
+
parser = main_parser()
|
|
58
|
+
if prog:
|
|
59
|
+
parser.prog = prog
|
|
60
|
+
args = parser.parse_args(cli_args)
|
|
61
|
+
|
|
62
|
+
venv_path = Path(args.venv).resolve()
|
|
63
|
+
sysconfig_path = Path(args.sysconfig).resolve()
|
|
64
|
+
|
|
65
|
+
if not venv_path.exists():
|
|
66
|
+
_error(f"Native virtual environment {venv_path} does not exist.")
|
|
67
|
+
else:
|
|
68
|
+
try:
|
|
69
|
+
description = convert_venv(venv_path, sysconfig_path)
|
|
70
|
+
except Exception as e:
|
|
71
|
+
_error(e)
|
|
72
|
+
sys.exit(1)
|
|
73
|
+
else:
|
|
74
|
+
_cprint(
|
|
75
|
+
"{bold}{green}{}{reset}",
|
|
76
|
+
f"{args.venv} is now an {description} cross venv.",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def entrypoint() -> None:
|
|
81
|
+
main(sys.argv[1:])
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__": # pragma: no cover
|
|
85
|
+
main(sys.argv[1:], "python -m build")
|
|
86
|
+
|
|
87
|
+
__all__ = [
|
|
88
|
+
"main",
|
|
89
|
+
"main_parser",
|
|
90
|
+
]
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# A site package that turns a virtual environment
|
|
2
|
+
# into a {os} {multiarch} cross-platform virtual environment
|
|
3
|
+
import functools
|
|
4
|
+
import os
|
|
5
|
+
import platform
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
import sysconfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def monkeypatch(module):
|
|
12
|
+
"""A decorator for patching a function in a module."""
|
|
13
|
+
def _dec(fn):
|
|
14
|
+
orig_fn = getattr(module, fn.__name__, None)
|
|
15
|
+
setattr(module, fn.__name__, fn)
|
|
16
|
+
functools.wraps(orig_fn)(fn)
|
|
17
|
+
|
|
18
|
+
return _dec
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def patch_cross_env():
|
|
22
|
+
###########################################################################
|
|
23
|
+
# sys module patches
|
|
24
|
+
###########################################################################
|
|
25
|
+
sys.cross_compiling = True
|
|
26
|
+
sys.platform = "{platform}"
|
|
27
|
+
sys.implementation._multiarch = "{multiarch}"
|
|
28
|
+
sys.abiflags = "{abiflags}"
|
|
29
|
+
|
|
30
|
+
if sys.version_info < (3, 14):
|
|
31
|
+
sys.base_prefix = sysconfig.get_config_var("prefix")
|
|
32
|
+
sys.base_exec_prefix = sysconfig.get_config_var("prefix")
|
|
33
|
+
else:
|
|
34
|
+
sys.base_prefix = sysconfig._get_sysconfigdata()["prefix"]
|
|
35
|
+
sys.base_exec_prefix = sysconfig._get_sysconfigdata()["prefix"]
|
|
36
|
+
|
|
37
|
+
###########################################################################
|
|
38
|
+
# os module patches
|
|
39
|
+
###########################################################################
|
|
40
|
+
@monkeypatch(os)
|
|
41
|
+
def uname():
|
|
42
|
+
return os.uname_result(
|
|
43
|
+
(
|
|
44
|
+
"{os_sysname}",
|
|
45
|
+
"{os_nodename}",
|
|
46
|
+
"{os_release}",
|
|
47
|
+
"{os_version}",
|
|
48
|
+
"{arch}",
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
###########################################################################
|
|
53
|
+
# platform module patches
|
|
54
|
+
###########################################################################
|
|
55
|
+
@monkeypatch(platform)
|
|
56
|
+
def uname():
|
|
57
|
+
return platform.uname_result(
|
|
58
|
+
system="{os}",
|
|
59
|
+
node="{os_nodename}",
|
|
60
|
+
release="{release}",
|
|
61
|
+
version="{os_version}",
|
|
62
|
+
machine="{arch}",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# -------------------------------------------------------------------------
|
|
66
|
+
{platform_extra}
|
|
67
|
+
###########################################################################
|
|
68
|
+
# subprocess module patches
|
|
69
|
+
###########################################################################
|
|
70
|
+
subprocess._can_fork_exec = True
|
|
71
|
+
|
|
72
|
+
###########################################################################
|
|
73
|
+
# sysconfig module patches
|
|
74
|
+
###########################################################################
|
|
75
|
+
|
|
76
|
+
@monkeypatch(sysconfig)
|
|
77
|
+
def get_platform():
|
|
78
|
+
return "{platform}-{platform_version}-{machine}"
|
|
79
|
+
|
|
80
|
+
@monkeypatch(sysconfig)
|
|
81
|
+
def get_sysconfigdata_name():
|
|
82
|
+
return "_sysconfigdata__{platform}_{multiarch}"
|
|
83
|
+
|
|
84
|
+
# Ensure module-level values cached at time of import are updated.
|
|
85
|
+
sysconfig._BASE_PREFIX = sys.base_prefix
|
|
86
|
+
sysconfig._BASE_EXEC_PREFIX = sys.base_exec_prefix
|
|
87
|
+
|
|
88
|
+
# Force sysconfig data to be loaded (and cached).
|
|
89
|
+
sysconfig._init_config_vars()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# if XBUILD_ENV=off is in the environment, don't apply any environment shims.
|
|
93
|
+
# This reverts the environment to a native build environment.
|
|
94
|
+
if os.environ.get("XBUILD_ENV", "on").lower() in {{"1", "on"}}:
|
|
95
|
+
patch_cross_env()
|
xvenv/convert.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import pprint
|
|
3
|
+
import sys
|
|
4
|
+
from importlib import import_module
|
|
5
|
+
from importlib import util as importlib_util
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def localized_vars(orig_vars, slice_path):
|
|
10
|
+
"""Update (where possible) any references to build-time variables with the best
|
|
11
|
+
guess of the installed location."""
|
|
12
|
+
# The host's sysconfigdata will include references to build-time variables.
|
|
13
|
+
# Update these to refer to the current known install location.
|
|
14
|
+
orig_prefix = orig_vars["prefix"]
|
|
15
|
+
localized_vars = {}
|
|
16
|
+
for key, value in orig_vars.items():
|
|
17
|
+
final = value
|
|
18
|
+
if isinstance(value, str):
|
|
19
|
+
# Replace any reference to the build installation prefix
|
|
20
|
+
final = final.replace(orig_prefix, str(slice_path))
|
|
21
|
+
# Replace any reference to the build-time Framework location
|
|
22
|
+
final = final.replace("-F .", f"-F {slice_path}")
|
|
23
|
+
localized_vars[key] = final
|
|
24
|
+
|
|
25
|
+
return localized_vars
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def localize_sysconfigdata(sysconfigdata_path, venv_site_packages):
|
|
29
|
+
"""Localize a sysconfigdata python module.
|
|
30
|
+
|
|
31
|
+
:param support_path: The platform config that contains the sysconfigdata module to
|
|
32
|
+
localize.
|
|
33
|
+
:param venv_site_packages: The site packages folder where the localized
|
|
34
|
+
sysconfigdata module should be output.
|
|
35
|
+
"""
|
|
36
|
+
# Import the sysconfigdata module
|
|
37
|
+
spec = importlib_util.spec_from_file_location(
|
|
38
|
+
sysconfigdata_path.stem, sysconfigdata_path
|
|
39
|
+
)
|
|
40
|
+
if spec is None:
|
|
41
|
+
msg = f"Unable to load spec for {sysconfigdata_path}"
|
|
42
|
+
raise ValueError(msg)
|
|
43
|
+
if spec.loader is None:
|
|
44
|
+
msg = f"Spec for {sysconfigdata_path} does not define a loader"
|
|
45
|
+
raise ValueError(msg)
|
|
46
|
+
sysconfigdata = importlib_util.module_from_spec(spec)
|
|
47
|
+
spec.loader.exec_module(sysconfigdata)
|
|
48
|
+
|
|
49
|
+
# Write the updated sysconfigdata module into the cross-platform site.
|
|
50
|
+
slice_path = sysconfigdata_path.parent.parent.parent
|
|
51
|
+
with (venv_site_packages / sysconfigdata_path.name).open("w") as f:
|
|
52
|
+
f.write(f"# Generated from {sysconfigdata_path}\n")
|
|
53
|
+
f.write("build_time_vars = ")
|
|
54
|
+
pprint.pprint(
|
|
55
|
+
localized_vars(sysconfigdata.build_time_vars, slice_path),
|
|
56
|
+
stream=f,
|
|
57
|
+
compact=True,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return sysconfigdata.build_time_vars
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def localize_sysconfig_vars(sysconfig_vars_path, venv_site_packages):
|
|
64
|
+
"""Localize a sysconfig_vars.json file.
|
|
65
|
+
|
|
66
|
+
:param support_path: The platform config that contains the sysconfigdata module to
|
|
67
|
+
localize.
|
|
68
|
+
:param venv_site_packages: The site-packages folder where the localized
|
|
69
|
+
sysconfig_vars.json file should be output.
|
|
70
|
+
:return: The localized sysconfig
|
|
71
|
+
"""
|
|
72
|
+
with sysconfig_vars_path.open("rb") as f:
|
|
73
|
+
build_time_vars = json.load(f)
|
|
74
|
+
|
|
75
|
+
prefix = sysconfig_vars_path.parent.parent.parent
|
|
76
|
+
sysconfig_vars = localized_vars(build_time_vars, prefix)
|
|
77
|
+
|
|
78
|
+
with (venv_site_packages / sysconfig_vars_path.name).open("w") as f:
|
|
79
|
+
json.dump(sysconfig_vars, f, indent=2)
|
|
80
|
+
|
|
81
|
+
return sysconfig_vars
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def convert_venv(venv_path: Path, sysconfig_path: Path):
|
|
85
|
+
"""Convert a virtual environment into a cross-platform environment.
|
|
86
|
+
|
|
87
|
+
:param venv_path: The path to the root of the venv.
|
|
88
|
+
:param sysconfig_path: The path to the sysconfig_vars JSON or sysconfigdata Python
|
|
89
|
+
file for the target platform.
|
|
90
|
+
"""
|
|
91
|
+
if not venv_path.exists():
|
|
92
|
+
raise ValueError(f"Virtual environment {venv_path} does not exist.")
|
|
93
|
+
if not (venv_path / "bin/python3").exists():
|
|
94
|
+
raise ValueError(f"{venv_path} does not appear to be a virtual environment.")
|
|
95
|
+
|
|
96
|
+
# Update path references in the sysconfigdata to reflect local conditions.
|
|
97
|
+
platlibs = list(venv_path.glob("lib/*/site-packages"))
|
|
98
|
+
if len(platlibs) == 0:
|
|
99
|
+
raise ValueError(f"Couldn't find site packages in {venv_path}")
|
|
100
|
+
elif len(platlibs) > 1:
|
|
101
|
+
raise ValueError(f"Found more than one site packages in {venv_path}")
|
|
102
|
+
|
|
103
|
+
venv_site_packages_path = platlibs[0]
|
|
104
|
+
|
|
105
|
+
if not sysconfig_path.is_file():
|
|
106
|
+
raise ValueError(f"Could not find sysconfig file {sysconfig_path}")
|
|
107
|
+
|
|
108
|
+
match sysconfig_path.suffix:
|
|
109
|
+
case ".json":
|
|
110
|
+
sysconfig_vars_path = sysconfig_path
|
|
111
|
+
_, _, _, abiflags, platform, multiarch = sysconfig_vars_path.stem.split("_")
|
|
112
|
+
|
|
113
|
+
sysconfigdata_path = (
|
|
114
|
+
sysconfig_vars_path.parent
|
|
115
|
+
/ f"_sysconfigdata_{abiflags}_{platform}_{multiarch}.py"
|
|
116
|
+
)
|
|
117
|
+
case ".py":
|
|
118
|
+
sysconfigdata_path = sysconfig_path
|
|
119
|
+
_, _, abiflags, platform, multiarch = sysconfigdata_path.stem.split("_")
|
|
120
|
+
|
|
121
|
+
sysconfig_vars_path = (
|
|
122
|
+
sysconfigdata_path.parent
|
|
123
|
+
/ f"_sysconfig_vars_{abiflags}_{platform}_{multiarch}.py"
|
|
124
|
+
)
|
|
125
|
+
case _:
|
|
126
|
+
raise ValueError(
|
|
127
|
+
"Don't know how to process sysconfig data "
|
|
128
|
+
f"of type {sysconfig_path.suffix}"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Localize the sysconfig data. sysconfigdata *must* exist; sysconfig_vars
|
|
132
|
+
# will only exist on Python 3.14 or newer.
|
|
133
|
+
sysconfig = localize_sysconfigdata(sysconfigdata_path, venv_site_packages_path)
|
|
134
|
+
if sys.version_info[:2] >= (3, 14):
|
|
135
|
+
localize_sysconfig_vars(sysconfig_vars_path, venv_site_packages_path)
|
|
136
|
+
|
|
137
|
+
if sysconfig["VERSION"] != venv_site_packages_path.parts[-2][6:]:
|
|
138
|
+
raise ValueError(
|
|
139
|
+
f"target venv is Python {venv_site_packages_path.parts[-2][6:]}; "
|
|
140
|
+
f"sysconfig file is for Python {sysconfig['VERSION']}"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Generate the context for the templated cross-target file
|
|
144
|
+
arch, sdk = multiarch.split("-", 1)
|
|
145
|
+
context = {
|
|
146
|
+
"platform": platform,
|
|
147
|
+
"os": platform, # some platforms use different capitalization here
|
|
148
|
+
"multiarch": multiarch,
|
|
149
|
+
"abiflags": abiflags,
|
|
150
|
+
"arch": arch,
|
|
151
|
+
"sdk": sdk,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
platform_module = import_module(f"xvenv.platforms.{platform}")
|
|
156
|
+
platform_module.extend_context(context, sysconfig)
|
|
157
|
+
except ImportError:
|
|
158
|
+
raise ValueError(
|
|
159
|
+
f"Don't know how to build a cross-venv file for {platform}"
|
|
160
|
+
) from None
|
|
161
|
+
|
|
162
|
+
cross_multiarch = f"_cross_{platform}_{multiarch.replace('-', '_')}"
|
|
163
|
+
|
|
164
|
+
# Render the template for the cross-target file.
|
|
165
|
+
template = (Path(__file__).parent / "_cross_target.py.tmpl").read_text()
|
|
166
|
+
rendered = template.format(**context)
|
|
167
|
+
(venv_site_packages_path / f"{cross_multiarch}.py").write_text(rendered)
|
|
168
|
+
|
|
169
|
+
# Write the .pth file that will enable the cross-target modifications
|
|
170
|
+
(venv_site_packages_path / "_cross_venv.pth").write_text(
|
|
171
|
+
f"import {cross_multiarch}\n"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
return f"{context['os']} {multiarch}"
|
|
File without changes
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
def extend_context(context, sysconfig):
|
|
2
|
+
# Convert the API level into a release number
|
|
3
|
+
api_level = int(sysconfig["ANDROID_API_LEVEL"])
|
|
4
|
+
if api_level >= 33:
|
|
5
|
+
release = f"{api_level - 20}"
|
|
6
|
+
elif api_level == 32:
|
|
7
|
+
release = "12L"
|
|
8
|
+
elif api_level == 31:
|
|
9
|
+
release = "12"
|
|
10
|
+
elif api_level > 28:
|
|
11
|
+
release = f"{api_level - 19}"
|
|
12
|
+
elif api_level == 27:
|
|
13
|
+
release = "8.1"
|
|
14
|
+
elif api_level == 26:
|
|
15
|
+
release = "8.0"
|
|
16
|
+
elif api_level == 25:
|
|
17
|
+
release = "7.1"
|
|
18
|
+
elif api_level == 24:
|
|
19
|
+
release = "7.0"
|
|
20
|
+
elif api_level == 23:
|
|
21
|
+
release = "6.0"
|
|
22
|
+
elif api_level == 22:
|
|
23
|
+
release = "5.1"
|
|
24
|
+
elif api_level == 21:
|
|
25
|
+
release = "5.0"
|
|
26
|
+
elif api_level == 20:
|
|
27
|
+
release = "4.4W"
|
|
28
|
+
else:
|
|
29
|
+
raise ValueError("xbuild doesn't support API levels lower than 20")
|
|
30
|
+
|
|
31
|
+
######################################################################
|
|
32
|
+
context["os"] = "Android"
|
|
33
|
+
context["release"] = release
|
|
34
|
+
context["platform_version"] = api_level
|
|
35
|
+
context["machine"] = {
|
|
36
|
+
"x86_64": "x86_64",
|
|
37
|
+
"i686": "x86",
|
|
38
|
+
"aarch64": "arm64_v8a",
|
|
39
|
+
"armv7l": "armeabi_v7a",
|
|
40
|
+
}[context["arch"]]
|
|
41
|
+
|
|
42
|
+
# The Linux kernel version and release are unlikely to be
|
|
43
|
+
# significant, but return realistic values anyway (from an
|
|
44
|
+
# API level 24 emulator).
|
|
45
|
+
context["os_sysname"] = "Linux"
|
|
46
|
+
context["os_nodename"] = "localhost"
|
|
47
|
+
context["os_release"] = "3.18.91+"
|
|
48
|
+
context["os_version"] = "#1 SMP PREEMPT Tue Jan 9 20:35:43 UTC 2018"
|
|
49
|
+
|
|
50
|
+
context["platform_extra"] = f"""
|
|
51
|
+
@monkeypatch(platform)
|
|
52
|
+
def getandroidapilevel() -> int:
|
|
53
|
+
return {api_level}
|
|
54
|
+
|
|
55
|
+
@monkeypatch(platform)
|
|
56
|
+
def android_ver(
|
|
57
|
+
release="",
|
|
58
|
+
api_level=0,
|
|
59
|
+
manufacturer="",
|
|
60
|
+
model="",
|
|
61
|
+
device="",
|
|
62
|
+
is_emulator=False
|
|
63
|
+
):
|
|
64
|
+
if release == "":
|
|
65
|
+
release = "{release}"
|
|
66
|
+
if api_level == 0:
|
|
67
|
+
api_level = {api_level}
|
|
68
|
+
if manufacturer == "":
|
|
69
|
+
manufacturer = "Google"
|
|
70
|
+
if model == "":
|
|
71
|
+
model = "sdk_gphone64"
|
|
72
|
+
if device == "":
|
|
73
|
+
device = "emu64"
|
|
74
|
+
|
|
75
|
+
return platform.AndroidVer(
|
|
76
|
+
release, api_level, manufacturer, model, device, True
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
"""
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
def extend_context(context, sysconfig):
|
|
2
|
+
emscripten_version = "4.0.12"
|
|
3
|
+
context["release"] = emscripten_version
|
|
4
|
+
context["platform_version"] = emscripten_version
|
|
5
|
+
context["machine"] = context["arch"]
|
|
6
|
+
|
|
7
|
+
context["os_sysname"] = "Emscripten"
|
|
8
|
+
context["os_nodename"] = "emscripten"
|
|
9
|
+
context["os_release"] = emscripten_version
|
|
10
|
+
context["os_version"] = "#1"
|
|
11
|
+
|
|
12
|
+
context["platform_extra"] = f"""
|
|
13
|
+
@monkeypatch(platform)
|
|
14
|
+
def libc_ver() -> int:
|
|
15
|
+
return ("emscripten", "{emscripten_version}")
|
|
16
|
+
"""
|
xvenv/platforms/ios.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
def extend_context(context, sysconfig):
|
|
2
|
+
release = sysconfig["IPHONEOS_DEPLOYMENT_TARGET"]
|
|
3
|
+
is_simulator = context["sdk"] == "iphonesimulator"
|
|
4
|
+
|
|
5
|
+
######################################################################
|
|
6
|
+
context["os"] = "iOS"
|
|
7
|
+
context["release"] = release
|
|
8
|
+
context["platform_version"] = release
|
|
9
|
+
context["machine"] = context["multiarch"]
|
|
10
|
+
|
|
11
|
+
# The Darwin kernel version and release are unlikely to be
|
|
12
|
+
# significant, but return realistic values anyway (from an
|
|
13
|
+
# iPhone simulator).
|
|
14
|
+
context["os_sysname"] = "Darwin"
|
|
15
|
+
context["os_nodename"] = "buildmachine.local"
|
|
16
|
+
context["os_release"] = "24.4.0"
|
|
17
|
+
context["os_version"] = (
|
|
18
|
+
"Darwin Kernel Version 24.4.0: Fri Apr 11 18:33:47 PDT 2025; "
|
|
19
|
+
"root:xnu-11417.101.15~117/RELEASE_ARM64_T6000"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
context["platform_extra"] = f"""
|
|
23
|
+
@monkeypatch(platform)
|
|
24
|
+
def ios_ver(system="", release="", model="", is_simulator=False):
|
|
25
|
+
if system == "":
|
|
26
|
+
system = "iOS"
|
|
27
|
+
if release == "":
|
|
28
|
+
release = "{release}"
|
|
29
|
+
if model == "":
|
|
30
|
+
model = "{context["sdk"]}"
|
|
31
|
+
|
|
32
|
+
return platform.IOSVersionInfo(system, release, model, {is_simulator})
|
|
33
|
+
"""
|