|
8 | 8 | Primary build targets are "emscripten-node-dl" (NodeJS, dynamic linking), |
9 | 9 | "emscripten-browser", and "wasi". |
10 | 10 |
|
11 | | -Emscripten builds require Emscripten SDK. The tools looks for 'EMSCRIPTEN' |
12 | | -env var and falls back to EMSDK installed at /opt/emsdk. |
| 11 | +Emscripten builds require a recent Emscripten SDK. The tools looks for an |
| 12 | +activated EMSDK environment (". /path/to/emsdk_env.sh"). |
13 | 13 |
|
14 | 14 | WASI builds require WASI SDK and wasmtime. The tool looks for 'WASI_SDK_PATH' |
15 | 15 | and falls back to /opt/wasi-sdk. |
|
35 | 35 |
|
36 | 36 | # path to WASI-SDK root |
37 | 37 | WASI_SDK_PATH = pathlib.Path(os.environ.get("WASI_SDK_PATH", "/opt/wasi-sdk")) |
38 | | -# path to Emscripten directory (upstream/emscripten subdirectory in the EMSDK) |
39 | | -EMSCRIPTEN_PATH = pathlib.Path(os.environ.get("EMSCRIPTEN", "/opt/emsdk/upstream/emscripten")) |
| 38 | + |
| 39 | +# path to Emscripten SDK config file. |
| 40 | +# auto-detect's EMSDK in /opt/emsdk without ". emsdk_env.sh". |
| 41 | +EM_CONFIG = pathlib.Path(os.environ.setdefault("EM_CONFIG", "/opt/emsdk/.emscripten")) |
| 42 | +# 3.1.16 has broken utime() |
| 43 | +EMSDK_MIN_VERSION = (3, 1, 17) |
| 44 | +_MISSING = pathlib.PurePath("MISSING") |
40 | 45 |
|
41 | 46 | # WASM_WEBSERVER = WASMTOOLS / "wasmwebserver.py" |
42 | 47 |
|
43 | 48 | INSTALL_EMSDK = """ |
44 | 49 | wasm32-emscripten builds need Emscripten SDK. Please follow instructions at |
45 | 50 | https://emscripten.org/docs/getting_started/downloads.html how to install |
46 | | -Emscripten tp "/opt/emsdk". Alternatively you can install the SDK in a |
47 | | -different location and set the environment variable |
48 | | -EMSCRIPTEN=.../upstream/emscripten (the directory with emcc and |
49 | | -emconfigure scripts), e.g. with ". emsdk_env.sh". |
| 51 | +Emscripten and how to activate the SDK with ". /path/to/emsdk_env.sh". |
50 | 52 | """ |
51 | 53 |
|
52 | 54 | INSTALL_WASI_SDK = """ |
|
63 | 65 | """ |
64 | 66 |
|
65 | 67 |
|
| 68 | +def get_emscripten_root(emconfig: pathlib.Path = EM_CONFIG) -> pathlib.Path: |
| 69 | + """Parse EM_CONFIG file and lookup EMSCRIPTEN_ROOT |
| 70 | +
|
| 71 | + The ".emscripten" config file is a Python snippet that uses "EM_CONFIG" |
| 72 | + environment variable. EMSCRIPTEN_ROOT is the "upstream/emscripten" |
| 73 | + subdirectory with tools like "emconfigure". |
| 74 | + """ |
| 75 | + if not emconfig.exists(): |
| 76 | + return _MISSING |
| 77 | + with open(emconfig, encoding="utf-8") as f: |
| 78 | + code = f.read() |
| 79 | + # EM_CONFIG file is a Python snippet |
| 80 | + local = {} |
| 81 | + exec(code, globals(), local) |
| 82 | + return pathlib.Path(local["EMSCRIPTEN_ROOT"]) |
| 83 | + |
| 84 | + |
| 85 | +EMSCRIPTEN_ROOT = get_emscripten_root() |
| 86 | + |
| 87 | + |
66 | 88 | class MissingDependency(ValueError): |
67 | 89 | def __init__(self, command: str, text: str): |
68 | 90 | self.command = command |
@@ -107,17 +129,34 @@ def getenv(self) -> dict: |
107 | 129 |
|
108 | 130 |
|
109 | 131 | def _check_emscripten(): |
110 | | - emconfigure = EMSCRIPTEN_PATH / "emconfigure" |
| 132 | + if EMSCRIPTEN_ROOT is _MISSING: |
| 133 | + raise MissingDependency("Emscripten SDK EM_CONFIG", INSTALL_EMSDK) |
| 134 | + # sanity check |
| 135 | + emconfigure = EMSCRIPTEN.configure_wrapper |
111 | 136 | if not emconfigure.exists(): |
112 | 137 | raise MissingDependency(emconfigure, INSTALL_EMSDK) |
| 138 | + # version check |
| 139 | + version_txt = EMSCRIPTEN_ROOT / "emscripten-version.txt" |
| 140 | + if not version_txt.exists(): |
| 141 | + raise MissingDependency(version_txt, INSTALL_EMSDK) |
| 142 | + with open(version_txt) as f: |
| 143 | + version = f.read().strip().strip('"') |
| 144 | + version_tuple = tuple(int(v) for v in version.split(".")) |
| 145 | + if version_tuple < EMSDK_MIN_VERSION: |
| 146 | + raise MissingDependency( |
| 147 | + version_txt, |
| 148 | + f"Emscripten SDK {version} in '{EMSCRIPTEN_ROOT}' is older than " |
| 149 | + "minimum required version " |
| 150 | + f"{'.'.join(str(v) for v in EMSDK_MIN_VERSION)}.", |
| 151 | + ) |
113 | 152 |
|
114 | 153 |
|
115 | 154 | EMSCRIPTEN = Platform( |
116 | 155 | "emscripten", |
117 | 156 | pythonexe="python.js", |
118 | 157 | config_site=WASMTOOLS / "config.site-wasm32-emscripten", |
119 | | - configure_wrapper=EMSCRIPTEN_PATH / "emconfigure", |
120 | | - make_wrapper=EMSCRIPTEN_PATH / "emmake", |
| 158 | + configure_wrapper=EMSCRIPTEN_ROOT / "emconfigure", |
| 159 | + make_wrapper=EMSCRIPTEN_ROOT / "emmake", |
121 | 160 | environ={"EM_COMPILER_WRAPPER": "ccache"} if HAS_CCACHE else None, |
122 | 161 | check=_check_emscripten, |
123 | 162 | ) |
@@ -434,7 +473,7 @@ def main(): |
434 | 473 | try: |
435 | 474 | builder.host.platform.check() |
436 | 475 | except MissingDependency as e: |
437 | | - parser.exit(2, str(e)) |
| 476 | + parser.exit(2, str(e) + "\n") |
438 | 477 |
|
439 | 478 | # hack for WASI |
440 | 479 | if builder.host.is_wasi and not SETUP_LOCAL.exists(): |
|
0 commit comments