17db96d56Sopenharmony_ci#!/usr/bin/env python3 27db96d56Sopenharmony_ci"""Build script for Python on WebAssembly platforms. 37db96d56Sopenharmony_ci 47db96d56Sopenharmony_ci $ ./Tools/wasm/wasm_builder.py emscripten-browser build repl 57db96d56Sopenharmony_ci $ ./Tools/wasm/wasm_builder.py emscripten-node-dl build test 67db96d56Sopenharmony_ci $ ./Tools/wasm/wasm_builder.py wasi build test 77db96d56Sopenharmony_ci 87db96d56Sopenharmony_ciPrimary build targets are "emscripten-node-dl" (NodeJS, dynamic linking), 97db96d56Sopenharmony_ci"emscripten-browser", and "wasi". 107db96d56Sopenharmony_ci 117db96d56Sopenharmony_ciEmscripten builds require a recent Emscripten SDK. The tools looks for an 127db96d56Sopenharmony_ciactivated EMSDK environment (". /path/to/emsdk_env.sh"). System packages 137db96d56Sopenharmony_ci(Debian, Homebrew) are not supported. 147db96d56Sopenharmony_ci 157db96d56Sopenharmony_ciWASI builds require WASI SDK and wasmtime. The tool looks for 'WASI_SDK_PATH' 167db96d56Sopenharmony_ciand falls back to /opt/wasi-sdk. 177db96d56Sopenharmony_ci 187db96d56Sopenharmony_ciThe 'build' Python interpreter must be rebuilt every time Python's byte code 197db96d56Sopenharmony_cichanges. 207db96d56Sopenharmony_ci 217db96d56Sopenharmony_ci ./Tools/wasm/wasm_builder.py --clean build build 227db96d56Sopenharmony_ci 237db96d56Sopenharmony_ci""" 247db96d56Sopenharmony_ciimport argparse 257db96d56Sopenharmony_ciimport enum 267db96d56Sopenharmony_ciimport dataclasses 277db96d56Sopenharmony_ciimport logging 287db96d56Sopenharmony_ciimport os 297db96d56Sopenharmony_ciimport pathlib 307db96d56Sopenharmony_ciimport re 317db96d56Sopenharmony_ciimport shlex 327db96d56Sopenharmony_ciimport shutil 337db96d56Sopenharmony_ciimport socket 347db96d56Sopenharmony_ciimport subprocess 357db96d56Sopenharmony_ciimport sys 367db96d56Sopenharmony_ciimport sysconfig 377db96d56Sopenharmony_ciimport tempfile 387db96d56Sopenharmony_ciimport time 397db96d56Sopenharmony_ciimport warnings 407db96d56Sopenharmony_ciimport webbrowser 417db96d56Sopenharmony_ci 427db96d56Sopenharmony_ci# for Python 3.8 437db96d56Sopenharmony_cifrom typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union 447db96d56Sopenharmony_ci 457db96d56Sopenharmony_cilogger = logging.getLogger("wasm_build") 467db96d56Sopenharmony_ci 477db96d56Sopenharmony_ciSRCDIR = pathlib.Path(__file__).parent.parent.parent.absolute() 487db96d56Sopenharmony_ciWASMTOOLS = SRCDIR / "Tools" / "wasm" 497db96d56Sopenharmony_ciBUILDDIR = SRCDIR / "builddir" 507db96d56Sopenharmony_ciCONFIGURE = SRCDIR / "configure" 517db96d56Sopenharmony_ciSETUP_LOCAL = SRCDIR / "Modules" / "Setup.local" 527db96d56Sopenharmony_ci 537db96d56Sopenharmony_ciHAS_CCACHE = shutil.which("ccache") is not None 547db96d56Sopenharmony_ci 557db96d56Sopenharmony_ci# path to WASI-SDK root 567db96d56Sopenharmony_ciWASI_SDK_PATH = pathlib.Path(os.environ.get("WASI_SDK_PATH", "/opt/wasi-sdk")) 577db96d56Sopenharmony_ci 587db96d56Sopenharmony_ci# path to Emscripten SDK config file. 597db96d56Sopenharmony_ci# auto-detect's EMSDK in /opt/emsdk without ". emsdk_env.sh". 607db96d56Sopenharmony_ciEM_CONFIG = pathlib.Path(os.environ.setdefault("EM_CONFIG", "/opt/emsdk/.emscripten")) 617db96d56Sopenharmony_ciEMSDK_MIN_VERSION = (3, 1, 19) 627db96d56Sopenharmony_ciEMSDK_BROKEN_VERSION = { 637db96d56Sopenharmony_ci (3, 1, 14): "https://github.com/emscripten-core/emscripten/issues/17338", 647db96d56Sopenharmony_ci (3, 1, 16): "https://github.com/emscripten-core/emscripten/issues/17393", 657db96d56Sopenharmony_ci (3, 1, 20): "https://github.com/emscripten-core/emscripten/issues/17720", 667db96d56Sopenharmony_ci} 677db96d56Sopenharmony_ci_MISSING = pathlib.PurePath("MISSING") 687db96d56Sopenharmony_ci 697db96d56Sopenharmony_ciWASM_WEBSERVER = WASMTOOLS / "wasm_webserver.py" 707db96d56Sopenharmony_ci 717db96d56Sopenharmony_ciCLEAN_SRCDIR = f""" 727db96d56Sopenharmony_ciBuilds require a clean source directory. Please use a clean checkout or 737db96d56Sopenharmony_cirun "make clean -C '{SRCDIR}'". 747db96d56Sopenharmony_ci""" 757db96d56Sopenharmony_ci 767db96d56Sopenharmony_ciINSTALL_NATIVE = f""" 777db96d56Sopenharmony_ciBuilds require a C compiler (gcc, clang), make, pkg-config, and development 787db96d56Sopenharmony_ciheaders for dependencies like zlib. 797db96d56Sopenharmony_ci 807db96d56Sopenharmony_ciDebian/Ubuntu: sudo apt install build-essential git curl pkg-config zlib1g-dev 817db96d56Sopenharmony_ciFedora/CentOS: sudo dnf install gcc make git-core curl pkgconfig zlib-devel 827db96d56Sopenharmony_ci""" 837db96d56Sopenharmony_ci 847db96d56Sopenharmony_ciINSTALL_EMSDK = """ 857db96d56Sopenharmony_ciwasm32-emscripten builds need Emscripten SDK. Please follow instructions at 867db96d56Sopenharmony_cihttps://emscripten.org/docs/getting_started/downloads.html how to install 877db96d56Sopenharmony_ciEmscripten and how to activate the SDK with "emsdk_env.sh". 887db96d56Sopenharmony_ci 897db96d56Sopenharmony_ci git clone https://github.com/emscripten-core/emsdk.git /path/to/emsdk 907db96d56Sopenharmony_ci cd /path/to/emsdk 917db96d56Sopenharmony_ci ./emsdk install latest 927db96d56Sopenharmony_ci ./emsdk activate latest 937db96d56Sopenharmony_ci source /path/to/emsdk_env.sh 947db96d56Sopenharmony_ci""" 957db96d56Sopenharmony_ci 967db96d56Sopenharmony_ciINSTALL_WASI_SDK = """ 977db96d56Sopenharmony_ciwasm32-wasi builds need WASI SDK. Please fetch the latest SDK from 987db96d56Sopenharmony_cihttps://github.com/WebAssembly/wasi-sdk/releases and install it to 997db96d56Sopenharmony_ci"/opt/wasi-sdk". Alternatively you can install the SDK in a different location 1007db96d56Sopenharmony_ciand point the environment variable WASI_SDK_PATH to the root directory 1017db96d56Sopenharmony_ciof the SDK. The SDK is available for Linux x86_64, macOS x86_64, and MinGW. 1027db96d56Sopenharmony_ci""" 1037db96d56Sopenharmony_ci 1047db96d56Sopenharmony_ciINSTALL_WASMTIME = """ 1057db96d56Sopenharmony_ciwasm32-wasi tests require wasmtime on PATH. Please follow instructions at 1067db96d56Sopenharmony_cihttps://wasmtime.dev/ to install wasmtime. 1077db96d56Sopenharmony_ci""" 1087db96d56Sopenharmony_ci 1097db96d56Sopenharmony_ci 1107db96d56Sopenharmony_cidef parse_emconfig( 1117db96d56Sopenharmony_ci emconfig: pathlib.Path = EM_CONFIG, 1127db96d56Sopenharmony_ci) -> Tuple[pathlib.PurePath, pathlib.PurePath]: 1137db96d56Sopenharmony_ci """Parse EM_CONFIG file and lookup EMSCRIPTEN_ROOT and NODE_JS. 1147db96d56Sopenharmony_ci 1157db96d56Sopenharmony_ci The ".emscripten" config file is a Python snippet that uses "EM_CONFIG" 1167db96d56Sopenharmony_ci environment variable. EMSCRIPTEN_ROOT is the "upstream/emscripten" 1177db96d56Sopenharmony_ci subdirectory with tools like "emconfigure". 1187db96d56Sopenharmony_ci """ 1197db96d56Sopenharmony_ci if not emconfig.exists(): 1207db96d56Sopenharmony_ci return _MISSING, _MISSING 1217db96d56Sopenharmony_ci with open(emconfig, encoding="utf-8") as f: 1227db96d56Sopenharmony_ci code = f.read() 1237db96d56Sopenharmony_ci # EM_CONFIG file is a Python snippet 1247db96d56Sopenharmony_ci local: Dict[str, Any] = {} 1257db96d56Sopenharmony_ci exec(code, globals(), local) 1267db96d56Sopenharmony_ci emscripten_root = pathlib.Path(local["EMSCRIPTEN_ROOT"]) 1277db96d56Sopenharmony_ci node_js = pathlib.Path(local["NODE_JS"]) 1287db96d56Sopenharmony_ci return emscripten_root, node_js 1297db96d56Sopenharmony_ci 1307db96d56Sopenharmony_ci 1317db96d56Sopenharmony_ciEMSCRIPTEN_ROOT, NODE_JS = parse_emconfig() 1327db96d56Sopenharmony_ci 1337db96d56Sopenharmony_ci 1347db96d56Sopenharmony_cidef read_python_version(configure: pathlib.Path = CONFIGURE) -> str: 1357db96d56Sopenharmony_ci """Read PACKAGE_VERSION from configure script 1367db96d56Sopenharmony_ci 1377db96d56Sopenharmony_ci configure and configure.ac are the canonical source for major and 1387db96d56Sopenharmony_ci minor version number. 1397db96d56Sopenharmony_ci """ 1407db96d56Sopenharmony_ci version_re = re.compile("^PACKAGE_VERSION='(\d\.\d+)'") 1417db96d56Sopenharmony_ci with configure.open(encoding="utf-8") as f: 1427db96d56Sopenharmony_ci for line in f: 1437db96d56Sopenharmony_ci mo = version_re.match(line) 1447db96d56Sopenharmony_ci if mo: 1457db96d56Sopenharmony_ci return mo.group(1) 1467db96d56Sopenharmony_ci raise ValueError(f"PACKAGE_VERSION not found in {configure}") 1477db96d56Sopenharmony_ci 1487db96d56Sopenharmony_ci 1497db96d56Sopenharmony_ciPYTHON_VERSION = read_python_version() 1507db96d56Sopenharmony_ci 1517db96d56Sopenharmony_ci 1527db96d56Sopenharmony_ciclass ConditionError(ValueError): 1537db96d56Sopenharmony_ci def __init__(self, info: str, text: str): 1547db96d56Sopenharmony_ci self.info = info 1557db96d56Sopenharmony_ci self.text = text 1567db96d56Sopenharmony_ci 1577db96d56Sopenharmony_ci def __str__(self): 1587db96d56Sopenharmony_ci return f"{type(self).__name__}: '{self.info}'\n{self.text}" 1597db96d56Sopenharmony_ci 1607db96d56Sopenharmony_ci 1617db96d56Sopenharmony_ciclass MissingDependency(ConditionError): 1627db96d56Sopenharmony_ci pass 1637db96d56Sopenharmony_ci 1647db96d56Sopenharmony_ci 1657db96d56Sopenharmony_ciclass DirtySourceDirectory(ConditionError): 1667db96d56Sopenharmony_ci pass 1677db96d56Sopenharmony_ci 1687db96d56Sopenharmony_ci 1697db96d56Sopenharmony_ci@dataclasses.dataclass 1707db96d56Sopenharmony_ciclass Platform: 1717db96d56Sopenharmony_ci """Platform-specific settings 1727db96d56Sopenharmony_ci 1737db96d56Sopenharmony_ci - CONFIG_SITE override 1747db96d56Sopenharmony_ci - configure wrapper (e.g. emconfigure) 1757db96d56Sopenharmony_ci - make wrapper (e.g. emmake) 1767db96d56Sopenharmony_ci - additional environment variables 1777db96d56Sopenharmony_ci - check function to verify SDK 1787db96d56Sopenharmony_ci """ 1797db96d56Sopenharmony_ci 1807db96d56Sopenharmony_ci name: str 1817db96d56Sopenharmony_ci pythonexe: str 1827db96d56Sopenharmony_ci config_site: Optional[pathlib.PurePath] 1837db96d56Sopenharmony_ci configure_wrapper: Optional[pathlib.PurePath] 1847db96d56Sopenharmony_ci make_wrapper: Optional[pathlib.PurePath] 1857db96d56Sopenharmony_ci environ: dict 1867db96d56Sopenharmony_ci check: Callable[[], None] 1877db96d56Sopenharmony_ci # Used for build_emports(). 1887db96d56Sopenharmony_ci ports: Optional[pathlib.PurePath] 1897db96d56Sopenharmony_ci cc: Optional[pathlib.PurePath] 1907db96d56Sopenharmony_ci 1917db96d56Sopenharmony_ci def getenv(self, profile: "BuildProfile") -> dict: 1927db96d56Sopenharmony_ci return self.environ.copy() 1937db96d56Sopenharmony_ci 1947db96d56Sopenharmony_ci 1957db96d56Sopenharmony_cidef _check_clean_src(): 1967db96d56Sopenharmony_ci candidates = [ 1977db96d56Sopenharmony_ci SRCDIR / "Programs" / "python.o", 1987db96d56Sopenharmony_ci SRCDIR / "Python" / "frozen_modules" / "importlib._bootstrap.h", 1997db96d56Sopenharmony_ci ] 2007db96d56Sopenharmony_ci for candidate in candidates: 2017db96d56Sopenharmony_ci if candidate.exists(): 2027db96d56Sopenharmony_ci raise DirtySourceDirectory(os.fspath(candidate), CLEAN_SRCDIR) 2037db96d56Sopenharmony_ci 2047db96d56Sopenharmony_ci 2057db96d56Sopenharmony_cidef _check_native(): 2067db96d56Sopenharmony_ci if not any(shutil.which(cc) for cc in ["cc", "gcc", "clang"]): 2077db96d56Sopenharmony_ci raise MissingDependency("cc", INSTALL_NATIVE) 2087db96d56Sopenharmony_ci if not shutil.which("make"): 2097db96d56Sopenharmony_ci raise MissingDependency("make", INSTALL_NATIVE) 2107db96d56Sopenharmony_ci if sys.platform == "linux": 2117db96d56Sopenharmony_ci # skip pkg-config check on macOS 2127db96d56Sopenharmony_ci if not shutil.which("pkg-config"): 2137db96d56Sopenharmony_ci raise MissingDependency("pkg-config", INSTALL_NATIVE) 2147db96d56Sopenharmony_ci # zlib is needed to create zip files 2157db96d56Sopenharmony_ci for devel in ["zlib"]: 2167db96d56Sopenharmony_ci try: 2177db96d56Sopenharmony_ci subprocess.check_call(["pkg-config", "--exists", devel]) 2187db96d56Sopenharmony_ci except subprocess.CalledProcessError: 2197db96d56Sopenharmony_ci raise MissingDependency(devel, INSTALL_NATIVE) from None 2207db96d56Sopenharmony_ci _check_clean_src() 2217db96d56Sopenharmony_ci 2227db96d56Sopenharmony_ci 2237db96d56Sopenharmony_ciNATIVE = Platform( 2247db96d56Sopenharmony_ci "native", 2257db96d56Sopenharmony_ci # macOS has python.exe 2267db96d56Sopenharmony_ci pythonexe=sysconfig.get_config_var("BUILDPYTHON") or "python", 2277db96d56Sopenharmony_ci config_site=None, 2287db96d56Sopenharmony_ci configure_wrapper=None, 2297db96d56Sopenharmony_ci ports=None, 2307db96d56Sopenharmony_ci cc=None, 2317db96d56Sopenharmony_ci make_wrapper=None, 2327db96d56Sopenharmony_ci environ={}, 2337db96d56Sopenharmony_ci check=_check_native, 2347db96d56Sopenharmony_ci) 2357db96d56Sopenharmony_ci 2367db96d56Sopenharmony_ci 2377db96d56Sopenharmony_cidef _check_emscripten(): 2387db96d56Sopenharmony_ci if EMSCRIPTEN_ROOT is _MISSING: 2397db96d56Sopenharmony_ci raise MissingDependency("Emscripten SDK EM_CONFIG", INSTALL_EMSDK) 2407db96d56Sopenharmony_ci # sanity check 2417db96d56Sopenharmony_ci emconfigure = EMSCRIPTEN.configure_wrapper 2427db96d56Sopenharmony_ci if not emconfigure.exists(): 2437db96d56Sopenharmony_ci raise MissingDependency(os.fspath(emconfigure), INSTALL_EMSDK) 2447db96d56Sopenharmony_ci # version check 2457db96d56Sopenharmony_ci version_txt = EMSCRIPTEN_ROOT / "emscripten-version.txt" 2467db96d56Sopenharmony_ci if not version_txt.exists(): 2477db96d56Sopenharmony_ci raise MissingDependency(os.fspath(version_txt), INSTALL_EMSDK) 2487db96d56Sopenharmony_ci with open(version_txt) as f: 2497db96d56Sopenharmony_ci version = f.read().strip().strip('"') 2507db96d56Sopenharmony_ci if version.endswith("-git"): 2517db96d56Sopenharmony_ci # git / upstream / tot-upstream installation 2527db96d56Sopenharmony_ci version = version[:-4] 2537db96d56Sopenharmony_ci version_tuple = tuple(int(v) for v in version.split(".")) 2547db96d56Sopenharmony_ci if version_tuple < EMSDK_MIN_VERSION: 2557db96d56Sopenharmony_ci raise ConditionError( 2567db96d56Sopenharmony_ci os.fspath(version_txt), 2577db96d56Sopenharmony_ci f"Emscripten SDK {version} in '{EMSCRIPTEN_ROOT}' is older than " 2587db96d56Sopenharmony_ci "minimum required version " 2597db96d56Sopenharmony_ci f"{'.'.join(str(v) for v in EMSDK_MIN_VERSION)}.", 2607db96d56Sopenharmony_ci ) 2617db96d56Sopenharmony_ci broken = EMSDK_BROKEN_VERSION.get(version_tuple) 2627db96d56Sopenharmony_ci if broken is not None: 2637db96d56Sopenharmony_ci raise ConditionError( 2647db96d56Sopenharmony_ci os.fspath(version_txt), 2657db96d56Sopenharmony_ci ( 2667db96d56Sopenharmony_ci f"Emscripten SDK {version} in '{EMSCRIPTEN_ROOT}' has known " 2677db96d56Sopenharmony_ci f"bugs, see {broken}." 2687db96d56Sopenharmony_ci ), 2697db96d56Sopenharmony_ci ) 2707db96d56Sopenharmony_ci if os.environ.get("PKG_CONFIG_PATH"): 2717db96d56Sopenharmony_ci warnings.warn( 2727db96d56Sopenharmony_ci "PKG_CONFIG_PATH is set and not empty. emconfigure overrides " 2737db96d56Sopenharmony_ci "this environment variable. Use EM_PKG_CONFIG_PATH instead." 2747db96d56Sopenharmony_ci ) 2757db96d56Sopenharmony_ci _check_clean_src() 2767db96d56Sopenharmony_ci 2777db96d56Sopenharmony_ci 2787db96d56Sopenharmony_ciEMSCRIPTEN = Platform( 2797db96d56Sopenharmony_ci "emscripten", 2807db96d56Sopenharmony_ci pythonexe="python.js", 2817db96d56Sopenharmony_ci config_site=WASMTOOLS / "config.site-wasm32-emscripten", 2827db96d56Sopenharmony_ci configure_wrapper=EMSCRIPTEN_ROOT / "emconfigure", 2837db96d56Sopenharmony_ci ports=EMSCRIPTEN_ROOT / "embuilder", 2847db96d56Sopenharmony_ci cc=EMSCRIPTEN_ROOT / "emcc", 2857db96d56Sopenharmony_ci make_wrapper=EMSCRIPTEN_ROOT / "emmake", 2867db96d56Sopenharmony_ci environ={ 2877db96d56Sopenharmony_ci # workaround for https://github.com/emscripten-core/emscripten/issues/17635 2887db96d56Sopenharmony_ci "TZ": "UTC", 2897db96d56Sopenharmony_ci "EM_COMPILER_WRAPPER": "ccache" if HAS_CCACHE else None, 2907db96d56Sopenharmony_ci "PATH": [EMSCRIPTEN_ROOT, os.environ["PATH"]], 2917db96d56Sopenharmony_ci }, 2927db96d56Sopenharmony_ci check=_check_emscripten, 2937db96d56Sopenharmony_ci) 2947db96d56Sopenharmony_ci 2957db96d56Sopenharmony_ci 2967db96d56Sopenharmony_cidef _check_wasi(): 2977db96d56Sopenharmony_ci wasm_ld = WASI_SDK_PATH / "bin" / "wasm-ld" 2987db96d56Sopenharmony_ci if not wasm_ld.exists(): 2997db96d56Sopenharmony_ci raise MissingDependency(os.fspath(wasm_ld), INSTALL_WASI_SDK) 3007db96d56Sopenharmony_ci wasmtime = shutil.which("wasmtime") 3017db96d56Sopenharmony_ci if wasmtime is None: 3027db96d56Sopenharmony_ci raise MissingDependency("wasmtime", INSTALL_WASMTIME) 3037db96d56Sopenharmony_ci _check_clean_src() 3047db96d56Sopenharmony_ci 3057db96d56Sopenharmony_ci 3067db96d56Sopenharmony_ciWASI = Platform( 3077db96d56Sopenharmony_ci "wasi", 3087db96d56Sopenharmony_ci pythonexe="python.wasm", 3097db96d56Sopenharmony_ci config_site=WASMTOOLS / "config.site-wasm32-wasi", 3107db96d56Sopenharmony_ci configure_wrapper=WASMTOOLS / "wasi-env", 3117db96d56Sopenharmony_ci ports=None, 3127db96d56Sopenharmony_ci cc=WASI_SDK_PATH / "bin" / "clang", 3137db96d56Sopenharmony_ci make_wrapper=None, 3147db96d56Sopenharmony_ci environ={ 3157db96d56Sopenharmony_ci "WASI_SDK_PATH": WASI_SDK_PATH, 3167db96d56Sopenharmony_ci # workaround for https://github.com/python/cpython/issues/95952 3177db96d56Sopenharmony_ci "HOSTRUNNER": ( 3187db96d56Sopenharmony_ci "wasmtime run " 3197db96d56Sopenharmony_ci "--env PYTHONPATH=/{relbuilddir}/build/lib.wasi-wasm32-{version}:/Lib " 3207db96d56Sopenharmony_ci "--mapdir /::{srcdir} --" 3217db96d56Sopenharmony_ci ), 3227db96d56Sopenharmony_ci "PATH": [WASI_SDK_PATH / "bin", os.environ["PATH"]], 3237db96d56Sopenharmony_ci }, 3247db96d56Sopenharmony_ci check=_check_wasi, 3257db96d56Sopenharmony_ci) 3267db96d56Sopenharmony_ci 3277db96d56Sopenharmony_ci 3287db96d56Sopenharmony_ciclass Host(enum.Enum): 3297db96d56Sopenharmony_ci """Target host triplet""" 3307db96d56Sopenharmony_ci 3317db96d56Sopenharmony_ci wasm32_emscripten = "wasm32-unknown-emscripten" 3327db96d56Sopenharmony_ci wasm64_emscripten = "wasm64-unknown-emscripten" 3337db96d56Sopenharmony_ci wasm32_wasi = "wasm32-unknown-wasi" 3347db96d56Sopenharmony_ci wasm64_wasi = "wasm64-unknown-wasi" 3357db96d56Sopenharmony_ci # current platform 3367db96d56Sopenharmony_ci build = sysconfig.get_config_var("BUILD_GNU_TYPE") 3377db96d56Sopenharmony_ci 3387db96d56Sopenharmony_ci @property 3397db96d56Sopenharmony_ci def platform(self) -> Platform: 3407db96d56Sopenharmony_ci if self.is_emscripten: 3417db96d56Sopenharmony_ci return EMSCRIPTEN 3427db96d56Sopenharmony_ci elif self.is_wasi: 3437db96d56Sopenharmony_ci return WASI 3447db96d56Sopenharmony_ci else: 3457db96d56Sopenharmony_ci return NATIVE 3467db96d56Sopenharmony_ci 3477db96d56Sopenharmony_ci @property 3487db96d56Sopenharmony_ci def is_emscripten(self) -> bool: 3497db96d56Sopenharmony_ci cls = type(self) 3507db96d56Sopenharmony_ci return self in {cls.wasm32_emscripten, cls.wasm64_emscripten} 3517db96d56Sopenharmony_ci 3527db96d56Sopenharmony_ci @property 3537db96d56Sopenharmony_ci def is_wasi(self) -> bool: 3547db96d56Sopenharmony_ci cls = type(self) 3557db96d56Sopenharmony_ci return self in {cls.wasm32_wasi, cls.wasm64_wasi} 3567db96d56Sopenharmony_ci 3577db96d56Sopenharmony_ci def get_extra_paths(self) -> Iterable[pathlib.PurePath]: 3587db96d56Sopenharmony_ci """Host-specific os.environ["PATH"] entries. 3597db96d56Sopenharmony_ci 3607db96d56Sopenharmony_ci Emscripten's Node version 14.x works well for wasm32-emscripten. 3617db96d56Sopenharmony_ci wasm64-emscripten requires more recent v8 version, e.g. node 16.x. 3627db96d56Sopenharmony_ci Attempt to use system's node command. 3637db96d56Sopenharmony_ci """ 3647db96d56Sopenharmony_ci cls = type(self) 3657db96d56Sopenharmony_ci if self == cls.wasm32_emscripten: 3667db96d56Sopenharmony_ci return [NODE_JS.parent] 3677db96d56Sopenharmony_ci elif self == cls.wasm64_emscripten: 3687db96d56Sopenharmony_ci # TODO: look for recent node 3697db96d56Sopenharmony_ci return [] 3707db96d56Sopenharmony_ci else: 3717db96d56Sopenharmony_ci return [] 3727db96d56Sopenharmony_ci 3737db96d56Sopenharmony_ci @property 3747db96d56Sopenharmony_ci def emport_args(self) -> List[str]: 3757db96d56Sopenharmony_ci """Host-specific port args (Emscripten).""" 3767db96d56Sopenharmony_ci cls = type(self) 3777db96d56Sopenharmony_ci if self is cls.wasm64_emscripten: 3787db96d56Sopenharmony_ci return ["-sMEMORY64=1"] 3797db96d56Sopenharmony_ci elif self is cls.wasm32_emscripten: 3807db96d56Sopenharmony_ci return ["-sMEMORY64=0"] 3817db96d56Sopenharmony_ci else: 3827db96d56Sopenharmony_ci return [] 3837db96d56Sopenharmony_ci 3847db96d56Sopenharmony_ci @property 3857db96d56Sopenharmony_ci def embuilder_args(self) -> List[str]: 3867db96d56Sopenharmony_ci """Host-specific embuilder args (Emscripten).""" 3877db96d56Sopenharmony_ci cls = type(self) 3887db96d56Sopenharmony_ci if self is cls.wasm64_emscripten: 3897db96d56Sopenharmony_ci return ["--wasm64"] 3907db96d56Sopenharmony_ci else: 3917db96d56Sopenharmony_ci return [] 3927db96d56Sopenharmony_ci 3937db96d56Sopenharmony_ci 3947db96d56Sopenharmony_ciclass EmscriptenTarget(enum.Enum): 3957db96d56Sopenharmony_ci """Emscripten-specific targets (--with-emscripten-target)""" 3967db96d56Sopenharmony_ci 3977db96d56Sopenharmony_ci browser = "browser" 3987db96d56Sopenharmony_ci browser_debug = "browser-debug" 3997db96d56Sopenharmony_ci node = "node" 4007db96d56Sopenharmony_ci node_debug = "node-debug" 4017db96d56Sopenharmony_ci 4027db96d56Sopenharmony_ci @property 4037db96d56Sopenharmony_ci def is_browser(self): 4047db96d56Sopenharmony_ci cls = type(self) 4057db96d56Sopenharmony_ci return self in {cls.browser, cls.browser_debug} 4067db96d56Sopenharmony_ci 4077db96d56Sopenharmony_ci @property 4087db96d56Sopenharmony_ci def emport_args(self) -> List[str]: 4097db96d56Sopenharmony_ci """Target-specific port args.""" 4107db96d56Sopenharmony_ci cls = type(self) 4117db96d56Sopenharmony_ci if self in {cls.browser_debug, cls.node_debug}: 4127db96d56Sopenharmony_ci # some libs come in debug and non-debug builds 4137db96d56Sopenharmony_ci return ["-O0"] 4147db96d56Sopenharmony_ci else: 4157db96d56Sopenharmony_ci return ["-O2"] 4167db96d56Sopenharmony_ci 4177db96d56Sopenharmony_ci 4187db96d56Sopenharmony_ciclass SupportLevel(enum.Enum): 4197db96d56Sopenharmony_ci supported = "tier 3, supported" 4207db96d56Sopenharmony_ci working = "working, unsupported" 4217db96d56Sopenharmony_ci experimental = "experimental, may be broken" 4227db96d56Sopenharmony_ci broken = "broken / unavailable" 4237db96d56Sopenharmony_ci 4247db96d56Sopenharmony_ci def __bool__(self): 4257db96d56Sopenharmony_ci cls = type(self) 4267db96d56Sopenharmony_ci return self in {cls.supported, cls.working} 4277db96d56Sopenharmony_ci 4287db96d56Sopenharmony_ci 4297db96d56Sopenharmony_ci@dataclasses.dataclass 4307db96d56Sopenharmony_ciclass BuildProfile: 4317db96d56Sopenharmony_ci name: str 4327db96d56Sopenharmony_ci support_level: SupportLevel 4337db96d56Sopenharmony_ci host: Host 4347db96d56Sopenharmony_ci target: Union[EmscriptenTarget, None] = None 4357db96d56Sopenharmony_ci dynamic_linking: Union[bool, None] = None 4367db96d56Sopenharmony_ci pthreads: Union[bool, None] = None 4377db96d56Sopenharmony_ci default_testopts: str = "-j2" 4387db96d56Sopenharmony_ci 4397db96d56Sopenharmony_ci @property 4407db96d56Sopenharmony_ci def is_browser(self) -> bool: 4417db96d56Sopenharmony_ci """Is this a browser build?""" 4427db96d56Sopenharmony_ci return self.target is not None and self.target.is_browser 4437db96d56Sopenharmony_ci 4447db96d56Sopenharmony_ci @property 4457db96d56Sopenharmony_ci def builddir(self) -> pathlib.Path: 4467db96d56Sopenharmony_ci """Path to build directory""" 4477db96d56Sopenharmony_ci return BUILDDIR / self.name 4487db96d56Sopenharmony_ci 4497db96d56Sopenharmony_ci @property 4507db96d56Sopenharmony_ci def python_cmd(self) -> pathlib.Path: 4517db96d56Sopenharmony_ci """Path to python executable""" 4527db96d56Sopenharmony_ci return self.builddir / self.host.platform.pythonexe 4537db96d56Sopenharmony_ci 4547db96d56Sopenharmony_ci @property 4557db96d56Sopenharmony_ci def makefile(self) -> pathlib.Path: 4567db96d56Sopenharmony_ci """Path to Makefile""" 4577db96d56Sopenharmony_ci return self.builddir / "Makefile" 4587db96d56Sopenharmony_ci 4597db96d56Sopenharmony_ci @property 4607db96d56Sopenharmony_ci def configure_cmd(self) -> List[str]: 4617db96d56Sopenharmony_ci """Generate configure command""" 4627db96d56Sopenharmony_ci # use relative path, so WASI tests can find lib prefix. 4637db96d56Sopenharmony_ci # pathlib.Path.relative_to() does not work here. 4647db96d56Sopenharmony_ci configure = os.path.relpath(CONFIGURE, self.builddir) 4657db96d56Sopenharmony_ci cmd = [configure, "-C"] 4667db96d56Sopenharmony_ci platform = self.host.platform 4677db96d56Sopenharmony_ci if platform.configure_wrapper: 4687db96d56Sopenharmony_ci cmd.insert(0, os.fspath(platform.configure_wrapper)) 4697db96d56Sopenharmony_ci 4707db96d56Sopenharmony_ci cmd.append(f"--host={self.host.value}") 4717db96d56Sopenharmony_ci cmd.append(f"--build={Host.build.value}") 4727db96d56Sopenharmony_ci 4737db96d56Sopenharmony_ci if self.target is not None: 4747db96d56Sopenharmony_ci assert self.host.is_emscripten 4757db96d56Sopenharmony_ci cmd.append(f"--with-emscripten-target={self.target.value}") 4767db96d56Sopenharmony_ci 4777db96d56Sopenharmony_ci if self.dynamic_linking is not None: 4787db96d56Sopenharmony_ci assert self.host.is_emscripten 4797db96d56Sopenharmony_ci opt = "enable" if self.dynamic_linking else "disable" 4807db96d56Sopenharmony_ci cmd.append(f"--{opt}-wasm-dynamic-linking") 4817db96d56Sopenharmony_ci 4827db96d56Sopenharmony_ci if self.pthreads is not None: 4837db96d56Sopenharmony_ci assert self.host.is_emscripten 4847db96d56Sopenharmony_ci opt = "enable" if self.pthreads else "disable" 4857db96d56Sopenharmony_ci cmd.append(f"--{opt}-wasm-pthreads") 4867db96d56Sopenharmony_ci 4877db96d56Sopenharmony_ci if self.host != Host.build: 4887db96d56Sopenharmony_ci cmd.append(f"--with-build-python={BUILD.python_cmd}") 4897db96d56Sopenharmony_ci 4907db96d56Sopenharmony_ci if platform.config_site is not None: 4917db96d56Sopenharmony_ci cmd.append(f"CONFIG_SITE={platform.config_site}") 4927db96d56Sopenharmony_ci 4937db96d56Sopenharmony_ci return cmd 4947db96d56Sopenharmony_ci 4957db96d56Sopenharmony_ci @property 4967db96d56Sopenharmony_ci def make_cmd(self) -> List[str]: 4977db96d56Sopenharmony_ci """Generate make command""" 4987db96d56Sopenharmony_ci cmd = ["make"] 4997db96d56Sopenharmony_ci platform = self.host.platform 5007db96d56Sopenharmony_ci if platform.make_wrapper: 5017db96d56Sopenharmony_ci cmd.insert(0, os.fspath(platform.make_wrapper)) 5027db96d56Sopenharmony_ci return cmd 5037db96d56Sopenharmony_ci 5047db96d56Sopenharmony_ci def getenv(self) -> dict: 5057db96d56Sopenharmony_ci """Generate environ dict for platform""" 5067db96d56Sopenharmony_ci env = os.environ.copy() 5077db96d56Sopenharmony_ci env.setdefault("MAKEFLAGS", f"-j{os.cpu_count()}") 5087db96d56Sopenharmony_ci platenv = self.host.platform.getenv(self) 5097db96d56Sopenharmony_ci for key, value in platenv.items(): 5107db96d56Sopenharmony_ci if value is None: 5117db96d56Sopenharmony_ci env.pop(key, None) 5127db96d56Sopenharmony_ci elif key == "PATH": 5137db96d56Sopenharmony_ci # list of path items, prefix with extra paths 5147db96d56Sopenharmony_ci new_path: List[pathlib.PurePath] = [] 5157db96d56Sopenharmony_ci new_path.extend(self.host.get_extra_paths()) 5167db96d56Sopenharmony_ci new_path.extend(value) 5177db96d56Sopenharmony_ci env[key] = os.pathsep.join(os.fspath(p) for p in new_path) 5187db96d56Sopenharmony_ci elif isinstance(value, str): 5197db96d56Sopenharmony_ci env[key] = value.format( 5207db96d56Sopenharmony_ci relbuilddir=self.builddir.relative_to(SRCDIR), 5217db96d56Sopenharmony_ci srcdir=SRCDIR, 5227db96d56Sopenharmony_ci version=PYTHON_VERSION, 5237db96d56Sopenharmony_ci ) 5247db96d56Sopenharmony_ci else: 5257db96d56Sopenharmony_ci env[key] = value 5267db96d56Sopenharmony_ci return env 5277db96d56Sopenharmony_ci 5287db96d56Sopenharmony_ci def _run_cmd( 5297db96d56Sopenharmony_ci self, 5307db96d56Sopenharmony_ci cmd: Iterable[str], 5317db96d56Sopenharmony_ci args: Iterable[str] = (), 5327db96d56Sopenharmony_ci cwd: Optional[pathlib.Path] = None, 5337db96d56Sopenharmony_ci ): 5347db96d56Sopenharmony_ci cmd = list(cmd) 5357db96d56Sopenharmony_ci cmd.extend(args) 5367db96d56Sopenharmony_ci if cwd is None: 5377db96d56Sopenharmony_ci cwd = self.builddir 5387db96d56Sopenharmony_ci logger.info('Running "%s" in "%s"', shlex.join(cmd), cwd) 5397db96d56Sopenharmony_ci return subprocess.check_call( 5407db96d56Sopenharmony_ci cmd, 5417db96d56Sopenharmony_ci cwd=os.fspath(cwd), 5427db96d56Sopenharmony_ci env=self.getenv(), 5437db96d56Sopenharmony_ci ) 5447db96d56Sopenharmony_ci 5457db96d56Sopenharmony_ci def _check_execute(self): 5467db96d56Sopenharmony_ci if self.is_browser: 5477db96d56Sopenharmony_ci raise ValueError(f"Cannot execute on {self.target}") 5487db96d56Sopenharmony_ci 5497db96d56Sopenharmony_ci def run_build(self, *args): 5507db96d56Sopenharmony_ci """Run configure (if necessary) and make""" 5517db96d56Sopenharmony_ci if not self.makefile.exists(): 5527db96d56Sopenharmony_ci logger.info("Makefile not found, running configure") 5537db96d56Sopenharmony_ci self.run_configure(*args) 5547db96d56Sopenharmony_ci self.run_make("all", *args) 5557db96d56Sopenharmony_ci 5567db96d56Sopenharmony_ci def run_configure(self, *args): 5577db96d56Sopenharmony_ci """Run configure script to generate Makefile""" 5587db96d56Sopenharmony_ci os.makedirs(self.builddir, exist_ok=True) 5597db96d56Sopenharmony_ci return self._run_cmd(self.configure_cmd, args) 5607db96d56Sopenharmony_ci 5617db96d56Sopenharmony_ci def run_make(self, *args): 5627db96d56Sopenharmony_ci """Run make (defaults to build all)""" 5637db96d56Sopenharmony_ci return self._run_cmd(self.make_cmd, args) 5647db96d56Sopenharmony_ci 5657db96d56Sopenharmony_ci def run_pythoninfo(self, *args): 5667db96d56Sopenharmony_ci """Run 'make pythoninfo'""" 5677db96d56Sopenharmony_ci self._check_execute() 5687db96d56Sopenharmony_ci return self.run_make("pythoninfo", *args) 5697db96d56Sopenharmony_ci 5707db96d56Sopenharmony_ci def run_test(self, target: str, testopts: Optional[str] = None): 5717db96d56Sopenharmony_ci """Run buildbottests""" 5727db96d56Sopenharmony_ci self._check_execute() 5737db96d56Sopenharmony_ci if testopts is None: 5747db96d56Sopenharmony_ci testopts = self.default_testopts 5757db96d56Sopenharmony_ci return self.run_make(target, f"TESTOPTS={testopts}") 5767db96d56Sopenharmony_ci 5777db96d56Sopenharmony_ci def run_py(self, *args): 5787db96d56Sopenharmony_ci """Run Python with hostrunner""" 5797db96d56Sopenharmony_ci self._check_execute() 5807db96d56Sopenharmony_ci self.run_make( 5817db96d56Sopenharmony_ci "--eval", f"run: all; $(HOSTRUNNER) ./$(PYTHON) {shlex.join(args)}", "run" 5827db96d56Sopenharmony_ci ) 5837db96d56Sopenharmony_ci 5847db96d56Sopenharmony_ci def run_browser(self, bind="127.0.0.1", port=8000): 5857db96d56Sopenharmony_ci """Run WASM webserver and open build in browser""" 5867db96d56Sopenharmony_ci relbuilddir = self.builddir.relative_to(SRCDIR) 5877db96d56Sopenharmony_ci url = f"http://{bind}:{port}/{relbuilddir}/python.html" 5887db96d56Sopenharmony_ci args = [ 5897db96d56Sopenharmony_ci sys.executable, 5907db96d56Sopenharmony_ci os.fspath(WASM_WEBSERVER), 5917db96d56Sopenharmony_ci "--bind", 5927db96d56Sopenharmony_ci bind, 5937db96d56Sopenharmony_ci "--port", 5947db96d56Sopenharmony_ci str(port), 5957db96d56Sopenharmony_ci ] 5967db96d56Sopenharmony_ci srv = subprocess.Popen(args, cwd=SRCDIR) 5977db96d56Sopenharmony_ci # wait for server 5987db96d56Sopenharmony_ci end = time.monotonic() + 3.0 5997db96d56Sopenharmony_ci while time.monotonic() < end and srv.returncode is None: 6007db96d56Sopenharmony_ci try: 6017db96d56Sopenharmony_ci with socket.create_connection((bind, port), timeout=0.1) as s: 6027db96d56Sopenharmony_ci pass 6037db96d56Sopenharmony_ci except OSError: 6047db96d56Sopenharmony_ci time.sleep(0.01) 6057db96d56Sopenharmony_ci else: 6067db96d56Sopenharmony_ci break 6077db96d56Sopenharmony_ci 6087db96d56Sopenharmony_ci webbrowser.open(url) 6097db96d56Sopenharmony_ci 6107db96d56Sopenharmony_ci try: 6117db96d56Sopenharmony_ci srv.wait() 6127db96d56Sopenharmony_ci except KeyboardInterrupt: 6137db96d56Sopenharmony_ci pass 6147db96d56Sopenharmony_ci 6157db96d56Sopenharmony_ci def clean(self, all: bool = False): 6167db96d56Sopenharmony_ci """Clean build directory""" 6177db96d56Sopenharmony_ci if all: 6187db96d56Sopenharmony_ci if self.builddir.exists(): 6197db96d56Sopenharmony_ci shutil.rmtree(self.builddir) 6207db96d56Sopenharmony_ci elif self.makefile.exists(): 6217db96d56Sopenharmony_ci self.run_make("clean") 6227db96d56Sopenharmony_ci 6237db96d56Sopenharmony_ci def build_emports(self, force: bool = False): 6247db96d56Sopenharmony_ci """Pre-build emscripten ports.""" 6257db96d56Sopenharmony_ci platform = self.host.platform 6267db96d56Sopenharmony_ci if platform.ports is None or platform.cc is None: 6277db96d56Sopenharmony_ci raise ValueError("Need ports and CC command") 6287db96d56Sopenharmony_ci 6297db96d56Sopenharmony_ci embuilder_cmd = [os.fspath(platform.ports)] 6307db96d56Sopenharmony_ci embuilder_cmd.extend(self.host.embuilder_args) 6317db96d56Sopenharmony_ci if force: 6327db96d56Sopenharmony_ci embuilder_cmd.append("--force") 6337db96d56Sopenharmony_ci 6347db96d56Sopenharmony_ci ports_cmd = [os.fspath(platform.cc)] 6357db96d56Sopenharmony_ci ports_cmd.extend(self.host.emport_args) 6367db96d56Sopenharmony_ci if self.target: 6377db96d56Sopenharmony_ci ports_cmd.extend(self.target.emport_args) 6387db96d56Sopenharmony_ci 6397db96d56Sopenharmony_ci if self.dynamic_linking: 6407db96d56Sopenharmony_ci # Trigger PIC build. 6417db96d56Sopenharmony_ci ports_cmd.append("-sMAIN_MODULE") 6427db96d56Sopenharmony_ci embuilder_cmd.append("--pic") 6437db96d56Sopenharmony_ci 6447db96d56Sopenharmony_ci if self.pthreads: 6457db96d56Sopenharmony_ci # Trigger multi-threaded build. 6467db96d56Sopenharmony_ci ports_cmd.append("-sUSE_PTHREADS") 6477db96d56Sopenharmony_ci 6487db96d56Sopenharmony_ci # Pre-build libbz2, libsqlite3, libz, and some system libs. 6497db96d56Sopenharmony_ci ports_cmd.extend(["-sUSE_ZLIB", "-sUSE_BZIP2", "-sUSE_SQLITE3"]) 6507db96d56Sopenharmony_ci # Multi-threaded sqlite3 has different suffix 6517db96d56Sopenharmony_ci embuilder_cmd.extend( 6527db96d56Sopenharmony_ci ["build", "bzip2", "sqlite3-mt" if self.pthreads else "sqlite3", "zlib"] 6537db96d56Sopenharmony_ci ) 6547db96d56Sopenharmony_ci 6557db96d56Sopenharmony_ci self._run_cmd(embuilder_cmd, cwd=SRCDIR) 6567db96d56Sopenharmony_ci 6577db96d56Sopenharmony_ci with tempfile.TemporaryDirectory(suffix="-py-emport") as tmpdir: 6587db96d56Sopenharmony_ci tmppath = pathlib.Path(tmpdir) 6597db96d56Sopenharmony_ci main_c = tmppath / "main.c" 6607db96d56Sopenharmony_ci main_js = tmppath / "main.js" 6617db96d56Sopenharmony_ci with main_c.open("w") as f: 6627db96d56Sopenharmony_ci f.write("int main(void) { return 0; }\n") 6637db96d56Sopenharmony_ci args = [ 6647db96d56Sopenharmony_ci os.fspath(main_c), 6657db96d56Sopenharmony_ci "-o", 6667db96d56Sopenharmony_ci os.fspath(main_js), 6677db96d56Sopenharmony_ci ] 6687db96d56Sopenharmony_ci self._run_cmd(ports_cmd, args, cwd=tmppath) 6697db96d56Sopenharmony_ci 6707db96d56Sopenharmony_ci 6717db96d56Sopenharmony_ci# native build (build Python) 6727db96d56Sopenharmony_ciBUILD = BuildProfile( 6737db96d56Sopenharmony_ci "build", 6747db96d56Sopenharmony_ci support_level=SupportLevel.working, 6757db96d56Sopenharmony_ci host=Host.build, 6767db96d56Sopenharmony_ci) 6777db96d56Sopenharmony_ci 6787db96d56Sopenharmony_ci_profiles = [ 6797db96d56Sopenharmony_ci BUILD, 6807db96d56Sopenharmony_ci # wasm32-emscripten 6817db96d56Sopenharmony_ci BuildProfile( 6827db96d56Sopenharmony_ci "emscripten-browser", 6837db96d56Sopenharmony_ci support_level=SupportLevel.supported, 6847db96d56Sopenharmony_ci host=Host.wasm32_emscripten, 6857db96d56Sopenharmony_ci target=EmscriptenTarget.browser, 6867db96d56Sopenharmony_ci dynamic_linking=True, 6877db96d56Sopenharmony_ci ), 6887db96d56Sopenharmony_ci BuildProfile( 6897db96d56Sopenharmony_ci "emscripten-browser-debug", 6907db96d56Sopenharmony_ci support_level=SupportLevel.working, 6917db96d56Sopenharmony_ci host=Host.wasm32_emscripten, 6927db96d56Sopenharmony_ci target=EmscriptenTarget.browser_debug, 6937db96d56Sopenharmony_ci dynamic_linking=True, 6947db96d56Sopenharmony_ci ), 6957db96d56Sopenharmony_ci BuildProfile( 6967db96d56Sopenharmony_ci "emscripten-node-dl", 6977db96d56Sopenharmony_ci support_level=SupportLevel.supported, 6987db96d56Sopenharmony_ci host=Host.wasm32_emscripten, 6997db96d56Sopenharmony_ci target=EmscriptenTarget.node, 7007db96d56Sopenharmony_ci dynamic_linking=True, 7017db96d56Sopenharmony_ci ), 7027db96d56Sopenharmony_ci BuildProfile( 7037db96d56Sopenharmony_ci "emscripten-node-dl-debug", 7047db96d56Sopenharmony_ci support_level=SupportLevel.working, 7057db96d56Sopenharmony_ci host=Host.wasm32_emscripten, 7067db96d56Sopenharmony_ci target=EmscriptenTarget.node_debug, 7077db96d56Sopenharmony_ci dynamic_linking=True, 7087db96d56Sopenharmony_ci ), 7097db96d56Sopenharmony_ci BuildProfile( 7107db96d56Sopenharmony_ci "emscripten-node-pthreads", 7117db96d56Sopenharmony_ci support_level=SupportLevel.supported, 7127db96d56Sopenharmony_ci host=Host.wasm32_emscripten, 7137db96d56Sopenharmony_ci target=EmscriptenTarget.node, 7147db96d56Sopenharmony_ci pthreads=True, 7157db96d56Sopenharmony_ci ), 7167db96d56Sopenharmony_ci BuildProfile( 7177db96d56Sopenharmony_ci "emscripten-node-pthreads-debug", 7187db96d56Sopenharmony_ci support_level=SupportLevel.working, 7197db96d56Sopenharmony_ci host=Host.wasm32_emscripten, 7207db96d56Sopenharmony_ci target=EmscriptenTarget.node_debug, 7217db96d56Sopenharmony_ci pthreads=True, 7227db96d56Sopenharmony_ci ), 7237db96d56Sopenharmony_ci # Emscripten build with both pthreads and dynamic linking is crashing. 7247db96d56Sopenharmony_ci BuildProfile( 7257db96d56Sopenharmony_ci "emscripten-node-dl-pthreads-debug", 7267db96d56Sopenharmony_ci support_level=SupportLevel.broken, 7277db96d56Sopenharmony_ci host=Host.wasm32_emscripten, 7287db96d56Sopenharmony_ci target=EmscriptenTarget.node_debug, 7297db96d56Sopenharmony_ci dynamic_linking=True, 7307db96d56Sopenharmony_ci pthreads=True, 7317db96d56Sopenharmony_ci ), 7327db96d56Sopenharmony_ci # wasm64-emscripten (requires Emscripten >= 3.1.21) 7337db96d56Sopenharmony_ci BuildProfile( 7347db96d56Sopenharmony_ci "wasm64-emscripten-node-debug", 7357db96d56Sopenharmony_ci support_level=SupportLevel.experimental, 7367db96d56Sopenharmony_ci host=Host.wasm64_emscripten, 7377db96d56Sopenharmony_ci target=EmscriptenTarget.node_debug, 7387db96d56Sopenharmony_ci # MEMORY64 is not compatible with dynamic linking 7397db96d56Sopenharmony_ci dynamic_linking=False, 7407db96d56Sopenharmony_ci pthreads=False, 7417db96d56Sopenharmony_ci ), 7427db96d56Sopenharmony_ci # wasm32-wasi 7437db96d56Sopenharmony_ci BuildProfile( 7447db96d56Sopenharmony_ci "wasi", 7457db96d56Sopenharmony_ci support_level=SupportLevel.supported, 7467db96d56Sopenharmony_ci host=Host.wasm32_wasi, 7477db96d56Sopenharmony_ci ), 7487db96d56Sopenharmony_ci # no SDK available yet 7497db96d56Sopenharmony_ci # BuildProfile( 7507db96d56Sopenharmony_ci # "wasm64-wasi", 7517db96d56Sopenharmony_ci # support_level=SupportLevel.broken, 7527db96d56Sopenharmony_ci # host=Host.wasm64_wasi, 7537db96d56Sopenharmony_ci # ), 7547db96d56Sopenharmony_ci] 7557db96d56Sopenharmony_ci 7567db96d56Sopenharmony_ciPROFILES = {p.name: p for p in _profiles} 7577db96d56Sopenharmony_ci 7587db96d56Sopenharmony_ciparser = argparse.ArgumentParser( 7597db96d56Sopenharmony_ci "wasm_build.py", 7607db96d56Sopenharmony_ci description=__doc__, 7617db96d56Sopenharmony_ci formatter_class=argparse.RawTextHelpFormatter, 7627db96d56Sopenharmony_ci) 7637db96d56Sopenharmony_ci 7647db96d56Sopenharmony_ciparser.add_argument( 7657db96d56Sopenharmony_ci "--clean", 7667db96d56Sopenharmony_ci "-c", 7677db96d56Sopenharmony_ci help="Clean build directories first", 7687db96d56Sopenharmony_ci action="store_true", 7697db96d56Sopenharmony_ci) 7707db96d56Sopenharmony_ci 7717db96d56Sopenharmony_ciparser.add_argument( 7727db96d56Sopenharmony_ci "--verbose", 7737db96d56Sopenharmony_ci "-v", 7747db96d56Sopenharmony_ci help="Verbose logging", 7757db96d56Sopenharmony_ci action="store_true", 7767db96d56Sopenharmony_ci) 7777db96d56Sopenharmony_ci 7787db96d56Sopenharmony_ciparser.add_argument( 7797db96d56Sopenharmony_ci "--silent", 7807db96d56Sopenharmony_ci help="Run configure and make in silent mode", 7817db96d56Sopenharmony_ci action="store_true", 7827db96d56Sopenharmony_ci) 7837db96d56Sopenharmony_ci 7847db96d56Sopenharmony_ciparser.add_argument( 7857db96d56Sopenharmony_ci "--testopts", 7867db96d56Sopenharmony_ci help=( 7877db96d56Sopenharmony_ci "Additional test options for 'test' and 'hostrunnertest', e.g. " 7887db96d56Sopenharmony_ci "--testopts='-v test_os'." 7897db96d56Sopenharmony_ci ), 7907db96d56Sopenharmony_ci default=None, 7917db96d56Sopenharmony_ci) 7927db96d56Sopenharmony_ci 7937db96d56Sopenharmony_ci# Don't list broken and experimental variants in help 7947db96d56Sopenharmony_ciplatforms_choices = list(p.name for p in _profiles) + ["cleanall"] 7957db96d56Sopenharmony_ciplatforms_help = list(p.name for p in _profiles if p.support_level) + ["cleanall"] 7967db96d56Sopenharmony_ciparser.add_argument( 7977db96d56Sopenharmony_ci "platform", 7987db96d56Sopenharmony_ci metavar="PLATFORM", 7997db96d56Sopenharmony_ci help=f"Build platform: {', '.join(platforms_help)}", 8007db96d56Sopenharmony_ci choices=platforms_choices, 8017db96d56Sopenharmony_ci) 8027db96d56Sopenharmony_ci 8037db96d56Sopenharmony_ciops = dict( 8047db96d56Sopenharmony_ci build="auto build (build 'build' Python, emports, configure, compile)", 8057db96d56Sopenharmony_ci configure="run ./configure", 8067db96d56Sopenharmony_ci compile="run 'make all'", 8077db96d56Sopenharmony_ci pythoninfo="run 'make pythoninfo'", 8087db96d56Sopenharmony_ci test="run 'make buildbottest TESTOPTS=...' (supports parallel tests)", 8097db96d56Sopenharmony_ci hostrunnertest="run 'make hostrunnertest TESTOPTS=...'", 8107db96d56Sopenharmony_ci repl="start interactive REPL / webserver + browser session", 8117db96d56Sopenharmony_ci clean="run 'make clean'", 8127db96d56Sopenharmony_ci cleanall="remove all build directories", 8137db96d56Sopenharmony_ci emports="build Emscripten port with embuilder (only Emscripten)", 8147db96d56Sopenharmony_ci) 8157db96d56Sopenharmony_ciops_help = "\n".join(f"{op:16s} {help}" for op, help in ops.items()) 8167db96d56Sopenharmony_ciparser.add_argument( 8177db96d56Sopenharmony_ci "ops", 8187db96d56Sopenharmony_ci metavar="OP", 8197db96d56Sopenharmony_ci help=f"operation (default: build)\n\n{ops_help}", 8207db96d56Sopenharmony_ci choices=tuple(ops), 8217db96d56Sopenharmony_ci default="build", 8227db96d56Sopenharmony_ci nargs="*", 8237db96d56Sopenharmony_ci) 8247db96d56Sopenharmony_ci 8257db96d56Sopenharmony_ci 8267db96d56Sopenharmony_cidef main(): 8277db96d56Sopenharmony_ci args = parser.parse_args() 8287db96d56Sopenharmony_ci logging.basicConfig( 8297db96d56Sopenharmony_ci level=logging.INFO if args.verbose else logging.ERROR, 8307db96d56Sopenharmony_ci format="%(message)s", 8317db96d56Sopenharmony_ci ) 8327db96d56Sopenharmony_ci 8337db96d56Sopenharmony_ci if args.platform == "cleanall": 8347db96d56Sopenharmony_ci for builder in PROFILES.values(): 8357db96d56Sopenharmony_ci builder.clean(all=True) 8367db96d56Sopenharmony_ci parser.exit(0) 8377db96d56Sopenharmony_ci 8387db96d56Sopenharmony_ci # additional configure and make args 8397db96d56Sopenharmony_ci cm_args = ("--silent",) if args.silent else () 8407db96d56Sopenharmony_ci 8417db96d56Sopenharmony_ci # nargs=* with default quirk 8427db96d56Sopenharmony_ci if args.ops == "build": 8437db96d56Sopenharmony_ci args.ops = ["build"] 8447db96d56Sopenharmony_ci 8457db96d56Sopenharmony_ci builder = PROFILES[args.platform] 8467db96d56Sopenharmony_ci try: 8477db96d56Sopenharmony_ci builder.host.platform.check() 8487db96d56Sopenharmony_ci except ConditionError as e: 8497db96d56Sopenharmony_ci parser.error(str(e)) 8507db96d56Sopenharmony_ci 8517db96d56Sopenharmony_ci if args.clean: 8527db96d56Sopenharmony_ci builder.clean(all=False) 8537db96d56Sopenharmony_ci 8547db96d56Sopenharmony_ci # hack for WASI 8557db96d56Sopenharmony_ci if builder.host.is_wasi and not SETUP_LOCAL.exists(): 8567db96d56Sopenharmony_ci SETUP_LOCAL.touch() 8577db96d56Sopenharmony_ci 8587db96d56Sopenharmony_ci # auto-build 8597db96d56Sopenharmony_ci if "build" in args.ops: 8607db96d56Sopenharmony_ci # check and create build Python 8617db96d56Sopenharmony_ci if builder is not BUILD: 8627db96d56Sopenharmony_ci logger.info("Auto-building 'build' Python.") 8637db96d56Sopenharmony_ci try: 8647db96d56Sopenharmony_ci BUILD.host.platform.check() 8657db96d56Sopenharmony_ci except ConditionError as e: 8667db96d56Sopenharmony_ci parser.error(str(e)) 8677db96d56Sopenharmony_ci if args.clean: 8687db96d56Sopenharmony_ci BUILD.clean(all=False) 8697db96d56Sopenharmony_ci BUILD.run_build(*cm_args) 8707db96d56Sopenharmony_ci # build Emscripten ports with embuilder 8717db96d56Sopenharmony_ci if builder.host.is_emscripten and "emports" not in args.ops: 8727db96d56Sopenharmony_ci builder.build_emports() 8737db96d56Sopenharmony_ci 8747db96d56Sopenharmony_ci for op in args.ops: 8757db96d56Sopenharmony_ci logger.info("\n*** %s %s", args.platform, op) 8767db96d56Sopenharmony_ci if op == "build": 8777db96d56Sopenharmony_ci builder.run_build(*cm_args) 8787db96d56Sopenharmony_ci elif op == "configure": 8797db96d56Sopenharmony_ci builder.run_configure(*cm_args) 8807db96d56Sopenharmony_ci elif op == "compile": 8817db96d56Sopenharmony_ci builder.run_make("all", *cm_args) 8827db96d56Sopenharmony_ci elif op == "pythoninfo": 8837db96d56Sopenharmony_ci builder.run_pythoninfo(*cm_args) 8847db96d56Sopenharmony_ci elif op == "repl": 8857db96d56Sopenharmony_ci if builder.is_browser: 8867db96d56Sopenharmony_ci builder.run_browser() 8877db96d56Sopenharmony_ci else: 8887db96d56Sopenharmony_ci builder.run_py() 8897db96d56Sopenharmony_ci elif op == "test": 8907db96d56Sopenharmony_ci builder.run_test("buildbottest", testopts=args.testopts) 8917db96d56Sopenharmony_ci elif op == "hostrunnertest": 8927db96d56Sopenharmony_ci builder.run_test("hostrunnertest", testopts=args.testopts) 8937db96d56Sopenharmony_ci elif op == "clean": 8947db96d56Sopenharmony_ci builder.clean(all=False) 8957db96d56Sopenharmony_ci elif op == "cleanall": 8967db96d56Sopenharmony_ci builder.clean(all=True) 8977db96d56Sopenharmony_ci elif op == "emports": 8987db96d56Sopenharmony_ci builder.build_emports(force=args.clean) 8997db96d56Sopenharmony_ci else: 9007db96d56Sopenharmony_ci raise ValueError(op) 9017db96d56Sopenharmony_ci 9027db96d56Sopenharmony_ci print(builder.builddir) 9037db96d56Sopenharmony_ci parser.exit(0) 9047db96d56Sopenharmony_ci 9057db96d56Sopenharmony_ci 9067db96d56Sopenharmony_ciif __name__ == "__main__": 9077db96d56Sopenharmony_ci main() 908