Skip to content

Commit 30cbe32

Browse files
committed
Build with wasm exception handling
For now, we need to use a custom rust toolchain for this. Apparently all alternatives to this don't work. -Zbuild-std doesn't work with panic=abort (rust-lang/cargo#7359) and my attempts to use a custom sysroot with either https://github.com/RalfJung/rustc-build-sysroot/ or https://github.com/DianaNites/cargo-sysroot/ seem to hit the same problem as with `-Zbuild-std`. Thus, I think the only reasonable way to go is to build the sysroot from the rust source directory. Perhaps we can eventually approach this by copying the `lib/rustlib/wasm32-unknown-emscripten/lib/` folder out of the build of the rust compiler on top of a nightly install of the compiler. For now, there is the additional problem that we need this patch to fix unwind=abort: rust-lang/rust#135450 I got my copy of the rust compiler by checking out this commit: hoodmane/rust@052ba16 two commits ahead of the rust main branch and running: ``` ./x build --stage 2 --target x86_64-unknown-linux-gnu,wasm32-unknown-emscripten ```
1 parent 3492aa6 commit 30cbe32

File tree

4 files changed

+63
-47
lines changed

4 files changed

+63
-47
lines changed

pyodide_build/build_env.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919
from pyodide_build.recipe import load_all_recipes
2020

2121
RUST_BUILD_PRELUDE = """
22-
rustup toolchain install ${RUST_TOOLCHAIN} && rustup default ${RUST_TOOLCHAIN}
23-
rustup target add wasm32-unknown-emscripten --toolchain ${RUST_TOOLCHAIN}
22+
rustup default ${RUST_TOOLCHAIN}
2423
"""
2524

2625

pyodide_build/buildpkg.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,13 +460,29 @@ def _get_helper_vars(self) -> dict[str, str]:
460460
}
461461

462462

463+
def _ensure_rust_toolchain():
464+
return
465+
pyodide_root = get_pyodide_root()
466+
toolchain_version = "emscripten_2025-01-13_052ba16"
467+
toolchain_path = (pyodide_root / ".rust-toolchain" / toolchain_version)
468+
if toolchain_path.exists():
469+
return
470+
from .xbuildenv import download_and_unpack_archive
471+
download_and_unpack_archive("http://pyodide-cache.s3-website-us-east-1.amazonaws.com/rustc/{toolchain_version}", toolchain_path)
472+
result = subprocess.run(["rustup", "toolchain", "link", toolchain_version, toolchain_path], check = False)
473+
if result.returncode != 0:
474+
logger.error("ERROR: rustup toolchain install failed")
475+
exit_with_stdio(result)
476+
477+
463478
class RecipeBuilderPackage(RecipeBuilder):
464479
"""
465480
Recipe builder for python packages.
466481
"""
467482

468483
def _build_package(self, bash_runner: BashRunnerWithSharedEnvironment) -> None:
469484
if self.recipe.is_rust_package():
485+
_ensure_rust_toolchain()
470486
bash_runner.run(
471487
RUST_BUILD_PRELUDE,
472488
script_name="rust build prelude",

pyodide_build/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ def to_env(self) -> dict[str, str]:
207207
"rustflags": "-C link-arg=-sSIDE_MODULE=2 -C link-arg=-sWASM_BIGINT -Z link-native-libraries=no",
208208
"cargo_build_target": "wasm32-unknown-emscripten",
209209
"cargo_target_wasm32_unknown_emscripten_linker": "emcc",
210-
"rust_toolchain": "nightly-2024-01-29",
210+
"rust_toolchain": "emscripten_2025-01-13_052ba16",
211211
# Other configuration
212212
"pyodide_jobs": "1",
213213
"skip_emscripten_version_check": "0",

pyodide_build/xbuildenv.py

Lines changed: 45 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,50 @@
2121
PYTHON_VERSION_MARKER_FILE = ".build-python-version"
2222

2323

24+
def download_and_unpack_archive(url: str, path: Path) -> None:
25+
"""
26+
Download the cross-build environment from the given URL and extract it to the given path.
27+
28+
Parameters
29+
----------
30+
url
31+
URL to download the cross-build environment from.
32+
path
33+
Path to extract the cross-build environment to.
34+
If the path already exists, raise an error.
35+
"""
36+
logger.info("Downloading Pyodide cross-build environment from %s", url)
37+
38+
if path.exists():
39+
raise FileExistsError(f"Path {path} already exists")
40+
41+
try:
42+
resp = urlopen(url)
43+
data = resp.read()
44+
except Exception as e:
45+
raise ValueError(
46+
f"Failed to download cross-build environment from {url}"
47+
) from e
48+
49+
# FIXME: requests makes a verbose output (see: https://github.com/pyodide/pyodide/issues/4810)
50+
# r = requests.get(url)
51+
52+
# if r.status_code != 200:
53+
# raise ValueError(
54+
# f"Failed to download cross-build environment from {url} (status code: {r.status_code})"
55+
# )
56+
57+
with NamedTemporaryFile(suffix=".tar") as f:
58+
f_path = Path(f.name)
59+
f_path.write_bytes(data)
60+
with warnings.catch_warnings():
61+
# Python 3.12-3.13 emits a DeprecationWarning when using shutil.unpack_archive without a filter,
62+
# but filter doesn't work well for zip files, so we suppress the warning until we find a better solution.
63+
# https://github.com/python/cpython/issues/112760
64+
warnings.simplefilter("ignore")
65+
shutil.unpack_archive(str(f_path), path)
66+
67+
2468
class CrossBuildEnvManager:
2569
"""
2670
Manager for the cross-build environment.
@@ -194,7 +238,7 @@ def install(
194238
download_path,
195239
)
196240
else:
197-
self._download(download_url, download_path)
241+
download_and_unpack_archive(download_url, download_path)
198242

199243
try:
200244
# there is an redundant directory "xbuildenv" inside the xbuildenv archive
@@ -240,49 +284,6 @@ def _find_latest_version(self) -> str:
240284

241285
return latest.version
242286

243-
def _download(self, url: str, path: Path) -> None:
244-
"""
245-
Download the cross-build environment from the given URL and extract it to the given path.
246-
247-
Parameters
248-
----------
249-
url
250-
URL to download the cross-build environment from.
251-
path
252-
Path to extract the cross-build environment to.
253-
If the path already exists, raise an error.
254-
"""
255-
logger.info("Downloading Pyodide cross-build environment from %s", url)
256-
257-
if path.exists():
258-
raise FileExistsError(f"Path {path} already exists")
259-
260-
try:
261-
resp = urlopen(url)
262-
data = resp.read()
263-
except Exception as e:
264-
raise ValueError(
265-
f"Failed to download cross-build environment from {url}"
266-
) from e
267-
268-
# FIXME: requests makes a verbose output (see: https://github.com/pyodide/pyodide/issues/4810)
269-
# r = requests.get(url)
270-
271-
# if r.status_code != 200:
272-
# raise ValueError(
273-
# f"Failed to download cross-build environment from {url} (status code: {r.status_code})"
274-
# )
275-
276-
with NamedTemporaryFile(suffix=".tar") as f:
277-
f_path = Path(f.name)
278-
f_path.write_bytes(data)
279-
with warnings.catch_warnings():
280-
# Python 3.12-3.13 emits a DeprecationWarning when using shutil.unpack_archive without a filter,
281-
# but filter doesn't work well for zip files, so we suppress the warning until we find a better solution.
282-
# https://github.com/python/cpython/issues/112760
283-
warnings.simplefilter("ignore")
284-
shutil.unpack_archive(str(f_path), path)
285-
286287
def _install_cross_build_packages(
287288
self, xbuildenv_root: Path, xbuildenv_pyodide_root: Path
288289
) -> None:

0 commit comments

Comments
 (0)