2024-05-31 09:27:57 +00:00
|
|
|
import argparse
|
|
|
|
import logging
|
2022-05-18 05:04:18 +00:00
|
|
|
import shutil
|
|
|
|
import subprocess
|
2024-05-31 09:27:57 +00:00
|
|
|
import textwrap
|
2022-05-18 05:04:18 +00:00
|
|
|
from pathlib import Path
|
|
|
|
|
2024-05-31 09:27:57 +00:00
|
|
|
try:
|
|
|
|
from pyodide_build.build_env import (
|
|
|
|
get_build_flag,
|
|
|
|
get_unisolated_packages,
|
|
|
|
)
|
|
|
|
from pyodide_build.recipe import load_all_recipes
|
|
|
|
except ImportError:
|
|
|
|
print("Requires pyodide-build package to be installed")
|
|
|
|
exit(1)
|
2022-05-18 05:04:18 +00:00
|
|
|
|
|
|
|
|
2023-04-07 11:54:11 +00:00
|
|
|
def _copy_xbuild_files(
|
|
|
|
pyodide_root: Path, xbuildenv_path: Path, skip_missing_files: bool = False
|
|
|
|
) -> None:
|
2023-05-06 08:17:22 +00:00
|
|
|
site_packages = Path(get_build_flag("HOSTSITEPACKAGES"))
|
2022-07-01 18:20:45 +00:00
|
|
|
# Store package cross-build-files into site_packages_extras in the same tree
|
|
|
|
# structure as they would appear in the real package.
|
|
|
|
# In install_xbuildenv, we will use:
|
|
|
|
# pip install -t $HOSTSITEPACKAGES -r requirements.txt
|
|
|
|
# cp site-packages-extras $HOSTSITEPACKAGES
|
|
|
|
site_packages_extras = xbuildenv_path / "site-packages-extras"
|
2023-04-07 11:54:11 +00:00
|
|
|
recipes = load_all_recipes(pyodide_root / "packages")
|
2023-01-27 04:31:26 +00:00
|
|
|
for recipe in recipes.values():
|
|
|
|
xbuild_files = recipe.build.cross_build_files
|
2022-05-18 05:04:18 +00:00
|
|
|
for path in xbuild_files:
|
2023-04-07 11:54:11 +00:00
|
|
|
source = site_packages / path
|
2022-07-01 18:20:45 +00:00
|
|
|
target = site_packages_extras / path
|
2022-05-18 05:04:18 +00:00
|
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
2023-04-07 11:54:11 +00:00
|
|
|
if not source.exists():
|
|
|
|
if skip_missing_files:
|
2024-05-31 09:27:57 +00:00
|
|
|
logging.warning(f"Cross-build file '{path}' not found")
|
2023-04-07 11:54:11 +00:00
|
|
|
continue
|
|
|
|
|
|
|
|
raise FileNotFoundError(f"Cross-build file '{path}' not found")
|
|
|
|
|
|
|
|
shutil.copy(source, target)
|
2022-05-18 05:04:18 +00:00
|
|
|
|
2022-07-01 18:20:45 +00:00
|
|
|
|
2023-04-07 11:54:11 +00:00
|
|
|
def _copy_wasm_libs(
|
|
|
|
pyodide_root: Path, xbuildenv_root: Path, skip_missing_files: bool = False
|
|
|
|
) -> None:
|
|
|
|
def get_relative_path(pyodide_root: Path, flag: str) -> Path:
|
2023-05-06 08:17:22 +00:00
|
|
|
return Path(get_build_flag(flag)).relative_to(pyodide_root)
|
2022-07-01 18:20:45 +00:00
|
|
|
|
|
|
|
pythoninclude = get_relative_path(pyodide_root, "PYTHONINCLUDE")
|
|
|
|
sysconfig_dir = get_relative_path(pyodide_root, "SYSCONFIGDATA_DIR")
|
2024-08-10 13:58:07 +00:00
|
|
|
|
2022-07-01 18:20:45 +00:00
|
|
|
to_copy: list[Path] = [
|
|
|
|
pythoninclude,
|
|
|
|
sysconfig_dir,
|
|
|
|
Path("Makefile.envs"),
|
2023-06-19 05:57:43 +00:00
|
|
|
Path("dist/pyodide-lock.json"),
|
2022-12-27 04:00:24 +00:00
|
|
|
Path("dist/python"),
|
2023-04-12 04:14:18 +00:00
|
|
|
Path("dist/python_stdlib.zip"),
|
2023-07-19 06:47:05 +00:00
|
|
|
Path("tools/constraints.txt"),
|
2022-07-01 18:20:45 +00:00
|
|
|
]
|
2022-09-14 03:19:01 +00:00
|
|
|
to_copy.extend(
|
|
|
|
x.relative_to(pyodide_root) for x in (pyodide_root / "dist").glob("pyodide.*")
|
|
|
|
)
|
2022-07-01 18:20:45 +00:00
|
|
|
|
|
|
|
for path in to_copy:
|
2023-04-07 11:54:11 +00:00
|
|
|
if not (pyodide_root / path).exists():
|
|
|
|
if skip_missing_files:
|
2024-05-31 09:27:57 +00:00
|
|
|
logging.warning(f"Cross-build file '{path}' not found")
|
2023-04-07 11:54:11 +00:00
|
|
|
continue
|
|
|
|
|
|
|
|
raise FileNotFoundError(f"Cross-build file '{path}' not found")
|
|
|
|
|
2022-07-01 18:20:45 +00:00
|
|
|
if (pyodide_root / path).is_dir():
|
|
|
|
shutil.copytree(
|
|
|
|
pyodide_root / path, xbuildenv_root / path, dirs_exist_ok=True
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
(xbuildenv_root / path).parent.mkdir(exist_ok=True, parents=True)
|
|
|
|
shutil.copy(pyodide_root / path, xbuildenv_root / path)
|
|
|
|
|
|
|
|
|
2023-04-07 11:54:11 +00:00
|
|
|
def create(
|
|
|
|
path: str | Path,
|
2024-05-31 09:27:57 +00:00
|
|
|
pyodide_root: Path,
|
2023-04-07 11:54:11 +00:00
|
|
|
*,
|
|
|
|
skip_missing_files: bool = False,
|
|
|
|
) -> None:
|
|
|
|
xbuildenv_path = Path(path) / "xbuildenv"
|
2022-09-19 00:36:12 +00:00
|
|
|
xbuildenv_root = xbuildenv_path / "pyodide-root"
|
2023-04-07 11:54:11 +00:00
|
|
|
|
2022-05-18 05:04:18 +00:00
|
|
|
shutil.rmtree(xbuildenv_path, ignore_errors=True)
|
2023-04-07 11:54:11 +00:00
|
|
|
xbuildenv_path.mkdir(parents=True, exist_ok=True)
|
|
|
|
xbuildenv_root.mkdir()
|
2022-07-01 18:20:45 +00:00
|
|
|
|
2023-04-07 11:54:11 +00:00
|
|
|
_copy_xbuild_files(pyodide_root, xbuildenv_path, skip_missing_files)
|
|
|
|
_copy_wasm_libs(pyodide_root, xbuildenv_root, skip_missing_files)
|
2022-09-14 03:19:01 +00:00
|
|
|
|
2022-09-19 00:36:12 +00:00
|
|
|
(xbuildenv_root / "package.json").write_text("{}")
|
|
|
|
res = subprocess.run(
|
2023-05-06 08:17:22 +00:00
|
|
|
["pip", "freeze", "--path", get_build_flag("HOSTSITEPACKAGES")],
|
2022-09-19 00:36:12 +00:00
|
|
|
capture_output=True,
|
|
|
|
encoding="utf8",
|
2022-07-06 21:37:19 +00:00
|
|
|
)
|
2022-09-19 00:36:12 +00:00
|
|
|
if res.returncode != 0:
|
2024-05-31 09:27:57 +00:00
|
|
|
logging.error("Failed to run pip freeze:")
|
|
|
|
if res.stdout:
|
|
|
|
logging.error(" stdout:")
|
|
|
|
logging.error(textwrap.indent(res.stdout, " "))
|
|
|
|
if res.stderr:
|
|
|
|
logging.error(" stderr:")
|
|
|
|
logging.error(textwrap.indent(res.stderr, " "))
|
|
|
|
exit(1)
|
2022-09-19 00:36:12 +00:00
|
|
|
|
|
|
|
(xbuildenv_path / "requirements.txt").write_text(res.stdout)
|
|
|
|
(xbuildenv_root / "unisolated.txt").write_text("\n".join(get_unisolated_packages()))
|
2024-05-31 09:27:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
def parse_args():
|
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
description="Create cross-build environment for building packages for Pyodide."
|
|
|
|
)
|
|
|
|
parser.add_argument("path", help="path to cross-build environment directory")
|
|
|
|
parser.add_argument(
|
|
|
|
"--skip-missing-files",
|
|
|
|
action="store_true",
|
|
|
|
help="skip if cross build files are missing instead of raising an error. This is useful for testing.",
|
|
|
|
)
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
return args
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
args = parse_args()
|
|
|
|
root = Path(__file__).parent.parent
|
|
|
|
|
|
|
|
create(args.path, pyodide_root=root, skip_missing_files=args.skip_missing_files)
|
|
|
|
|
|
|
|
print(f"Pyodide cross-build environment created at {args.path}")
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|