Skip to content

Commit

Permalink
Build with wasm exception handling
Browse files Browse the repository at this point in the history
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
```
  • Loading branch information
hoodmane committed Jan 13, 2025
1 parent 3492aa6 commit 30cbe32
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 47 deletions.
3 changes: 1 addition & 2 deletions pyodide_build/build_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
from pyodide_build.recipe import load_all_recipes

RUST_BUILD_PRELUDE = """
rustup toolchain install ${RUST_TOOLCHAIN} && rustup default ${RUST_TOOLCHAIN}
rustup target add wasm32-unknown-emscripten --toolchain ${RUST_TOOLCHAIN}
rustup default ${RUST_TOOLCHAIN}
"""


Expand Down
16 changes: 16 additions & 0 deletions pyodide_build/buildpkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,13 +460,29 @@ def _get_helper_vars(self) -> dict[str, str]:
}


def _ensure_rust_toolchain():
return
pyodide_root = get_pyodide_root()
toolchain_version = "emscripten_2025-01-13_052ba16"
toolchain_path = (pyodide_root / ".rust-toolchain" / toolchain_version)
if toolchain_path.exists():
return
from .xbuildenv import download_and_unpack_archive
download_and_unpack_archive("http://pyodide-cache.s3-website-us-east-1.amazonaws.com/rustc/{toolchain_version}", toolchain_path)
result = subprocess.run(["rustup", "toolchain", "link", toolchain_version, toolchain_path], check = False)
if result.returncode != 0:
logger.error("ERROR: rustup toolchain install failed")
exit_with_stdio(result)


class RecipeBuilderPackage(RecipeBuilder):
"""
Recipe builder for python packages.
"""

def _build_package(self, bash_runner: BashRunnerWithSharedEnvironment) -> None:
if self.recipe.is_rust_package():
_ensure_rust_toolchain()
bash_runner.run(
RUST_BUILD_PRELUDE,
script_name="rust build prelude",
Expand Down
2 changes: 1 addition & 1 deletion pyodide_build/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ def to_env(self) -> dict[str, str]:
"rustflags": "-C link-arg=-sSIDE_MODULE=2 -C link-arg=-sWASM_BIGINT -Z link-native-libraries=no",
"cargo_build_target": "wasm32-unknown-emscripten",
"cargo_target_wasm32_unknown_emscripten_linker": "emcc",
"rust_toolchain": "nightly-2024-01-29",
"rust_toolchain": "emscripten_2025-01-13_052ba16",
# Other configuration
"pyodide_jobs": "1",
"skip_emscripten_version_check": "0",
Expand Down
89 changes: 45 additions & 44 deletions pyodide_build/xbuildenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,50 @@
PYTHON_VERSION_MARKER_FILE = ".build-python-version"


def download_and_unpack_archive(url: str, path: Path) -> None:
"""
Download the cross-build environment from the given URL and extract it to the given path.
Parameters
----------
url
URL to download the cross-build environment from.
path
Path to extract the cross-build environment to.
If the path already exists, raise an error.
"""
logger.info("Downloading Pyodide cross-build environment from %s", url)

if path.exists():
raise FileExistsError(f"Path {path} already exists")

try:
resp = urlopen(url)
data = resp.read()
except Exception as e:
raise ValueError(
f"Failed to download cross-build environment from {url}"
) from e

# FIXME: requests makes a verbose output (see: https://github.com/pyodide/pyodide/issues/4810)
# r = requests.get(url)

# if r.status_code != 200:
# raise ValueError(
# f"Failed to download cross-build environment from {url} (status code: {r.status_code})"
# )

with NamedTemporaryFile(suffix=".tar") as f:
f_path = Path(f.name)
f_path.write_bytes(data)
with warnings.catch_warnings():
# Python 3.12-3.13 emits a DeprecationWarning when using shutil.unpack_archive without a filter,
# but filter doesn't work well for zip files, so we suppress the warning until we find a better solution.
# https://github.com/python/cpython/issues/112760
warnings.simplefilter("ignore")
shutil.unpack_archive(str(f_path), path)


class CrossBuildEnvManager:
"""
Manager for the cross-build environment.
Expand Down Expand Up @@ -194,7 +238,7 @@ def install(
download_path,
)
else:
self._download(download_url, download_path)
download_and_unpack_archive(download_url, download_path)

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

return latest.version

def _download(self, url: str, path: Path) -> None:
"""
Download the cross-build environment from the given URL and extract it to the given path.
Parameters
----------
url
URL to download the cross-build environment from.
path
Path to extract the cross-build environment to.
If the path already exists, raise an error.
"""
logger.info("Downloading Pyodide cross-build environment from %s", url)

if path.exists():
raise FileExistsError(f"Path {path} already exists")

try:
resp = urlopen(url)
data = resp.read()
except Exception as e:
raise ValueError(
f"Failed to download cross-build environment from {url}"
) from e

# FIXME: requests makes a verbose output (see: https://github.com/pyodide/pyodide/issues/4810)
# r = requests.get(url)

# if r.status_code != 200:
# raise ValueError(
# f"Failed to download cross-build environment from {url} (status code: {r.status_code})"
# )

with NamedTemporaryFile(suffix=".tar") as f:
f_path = Path(f.name)
f_path.write_bytes(data)
with warnings.catch_warnings():
# Python 3.12-3.13 emits a DeprecationWarning when using shutil.unpack_archive without a filter,
# but filter doesn't work well for zip files, so we suppress the warning until we find a better solution.
# https://github.com/python/cpython/issues/112760
warnings.simplefilter("ignore")
shutil.unpack_archive(str(f_path), path)

def _install_cross_build_packages(
self, xbuildenv_root: Path, xbuildenv_pyodide_root: Path
) -> None:
Expand Down

0 comments on commit 30cbe32

Please sign in to comment.