2323EMSCRIPTEN_DIR = Path (__file__ ).parent
2424CHECKOUT = EMSCRIPTEN_DIR .parent .parent .parent
2525
26- CROSS_BUILD_DIR = CHECKOUT / "cross-build"
27- NATIVE_BUILD_DIR = CROSS_BUILD_DIR / "build"
26+ DEFAULT_CROSS_BUILD_DIR = CHECKOUT / "cross-build"
2827HOST_TRIPLE = "wasm32-emscripten"
2928
30- DOWNLOAD_DIR = CROSS_BUILD_DIR / HOST_TRIPLE / "build"
31- HOST_BUILD_DIR = CROSS_BUILD_DIR / HOST_TRIPLE / "build"
32- HOST_DIR = HOST_BUILD_DIR / "python"
33- PREFIX_DIR = CROSS_BUILD_DIR / HOST_TRIPLE / "prefix"
29+
30+ def get_build_paths (cross_build_dir = None ):
31+ """Compute all build paths from the given cross-build directory."""
32+ if cross_build_dir is None :
33+ cross_build_dir = DEFAULT_CROSS_BUILD_DIR
34+ cross_build_dir = Path (cross_build_dir )
35+ host_triple_dir = cross_build_dir / HOST_TRIPLE
36+ return {
37+ "cross_build_dir" : cross_build_dir ,
38+ "native_build_dir" : cross_build_dir / "build" ,
39+ "host_triple_dir" : host_triple_dir ,
40+ "host_build_dir" : host_triple_dir / "build" ,
41+ "host_dir" : host_triple_dir / "build" / "python" ,
42+ "prefix_dir" : host_triple_dir / "prefix" ,
43+ }
3444
3545LOCAL_SETUP = CHECKOUT / "Modules" / "Setup.local"
3646LOCAL_SETUP_MARKER = b"# Generated by Tools/wasm/emscripten.py\n "
@@ -66,12 +76,17 @@ def updated_env(updates={}):
6676 return environment
6777
6878
69- def subdir (working_dir , * , clean_ok = False ):
70- """Decorator to change to a working directory."""
79+ def subdir (path_key , * , clean_ok = False ):
80+ """Decorator to change to a working directory.
81+
82+ path_key is a key into context.build_paths, used to resolve the working
83+ directory at call time.
84+ """
7185
7286 def decorator (func ):
7387 @functools .wraps (func )
7488 def wrapper (context ):
89+ working_dir = context .build_paths [path_key ]
7590 try :
7691 tput_output = subprocess .check_output (
7792 ["tput" , "cols" ], encoding = "utf-8"
@@ -128,20 +143,21 @@ def build_platform():
128143 return sysconfig .get_config_var ("BUILD_GNU_TYPE" )
129144
130145
131- def build_python_path ():
146+ def build_python_path (context ):
132147 """The path to the build Python binary."""
133- binary = NATIVE_BUILD_DIR / "python"
148+ native_build_dir = context .build_paths ["native_build_dir" ]
149+ binary = native_build_dir / "python"
134150 if not binary .is_file ():
135151 binary = binary .with_suffix (".exe" )
136152 if not binary .is_file ():
137153 raise FileNotFoundError (
138- f"Unable to find `python(.exe)` in { NATIVE_BUILD_DIR } "
154+ f"Unable to find `python(.exe)` in { native_build_dir } "
139155 )
140156
141157 return binary
142158
143159
144- @subdir (NATIVE_BUILD_DIR , clean_ok = True )
160+ @subdir ("native_build_dir" , clean_ok = True )
145161def configure_build_python (context , working_dir ):
146162 """Configure the build/host Python."""
147163 if LOCAL_SETUP .exists ():
@@ -157,12 +173,12 @@ def configure_build_python(context, working_dir):
157173 call (configure , quiet = context .quiet )
158174
159175
160- @subdir (NATIVE_BUILD_DIR )
176+ @subdir ("native_build_dir" )
161177def make_build_python (context , working_dir ):
162178 """Make/build the build Python."""
163179 call (["make" , "--jobs" , str (cpu_count ()), "all" ], quiet = context .quiet )
164180
165- binary = build_python_path ()
181+ binary = build_python_path (context )
166182 cmd = [
167183 binary ,
168184 "-c" ,
@@ -192,7 +208,7 @@ def download_and_unpack(working_dir: Path, url: str, expected_shasum: str):
192208 shutil .unpack_archive (tmp_file .name , working_dir )
193209
194210
195- @subdir (HOST_BUILD_DIR , clean_ok = True )
211+ @subdir ("host_build_dir" , clean_ok = True )
196212def make_emscripten_libffi (context , working_dir ):
197213 ver = "3.4.6"
198214 libffi_dir = working_dir / f"libffi-{ ver } "
@@ -204,13 +220,13 @@ def make_emscripten_libffi(context, working_dir):
204220 )
205221 call (
206222 [EMSCRIPTEN_DIR / "make_libffi.sh" ],
207- env = updated_env ({"PREFIX" : PREFIX_DIR } ),
223+ env = updated_env ({"PREFIX" : context . build_paths [ "prefix_dir" ]}, context . emsdk_cache ),
208224 cwd = libffi_dir ,
209225 quiet = context .quiet ,
210226 )
211227
212228
213- @subdir (HOST_BUILD_DIR , clean_ok = True )
229+ @subdir ("host_build_dir" , clean_ok = True )
214230def make_mpdec (context , working_dir ):
215231 ver = "4.0.1"
216232 mpdec_dir = working_dir / f"mpdecimal-{ ver } "
@@ -226,7 +242,7 @@ def make_mpdec(context, working_dir):
226242 mpdec_dir / "configure" ,
227243 "CFLAGS=-fPIC" ,
228244 "--prefix" ,
229- PREFIX_DIR ,
245+ context . build_paths [ "prefix_dir" ] ,
230246 "--disable-shared" ,
231247 ],
232248 cwd = mpdec_dir ,
@@ -239,14 +255,15 @@ def make_mpdec(context, working_dir):
239255 )
240256
241257
242- @subdir (HOST_DIR , clean_ok = True )
258+ @subdir ("host_dir" , clean_ok = True )
243259def configure_emscripten_python (context , working_dir ):
244260 """Configure the emscripten/host build."""
261+ paths = context .build_paths
245262 config_site = os .fsdecode (EMSCRIPTEN_DIR / "config.site-wasm32-emscripten" )
246263
247264 emscripten_build_dir = working_dir .relative_to (CHECKOUT )
248265
249- python_build_dir = NATIVE_BUILD_DIR / "build"
266+ python_build_dir = paths [ "native_build_dir" ] / "build"
250267 lib_dirs = list (python_build_dir .glob ("lib.*" ))
251268 assert len (lib_dirs ) == 1 , (
252269 f"Expected a single lib.* directory in { python_build_dir } "
@@ -272,13 +289,13 @@ def configure_emscripten_python(context, working_dir):
272289 capture_output = True ,
273290 )
274291 host_runner = res .stdout .strip ()
275- pkg_config_path_dir = (PREFIX_DIR / "lib/pkgconfig/" ).resolve ()
292+ pkg_config_path_dir = (paths [ "prefix_dir" ] / "lib/pkgconfig/" ).resolve ()
276293 env_additions = {
277294 "CONFIG_SITE" : config_site ,
278295 "HOSTRUNNER" : host_runner ,
279296 "EM_PKG_CONFIG_PATH" : str (pkg_config_path_dir ),
280297 }
281- build_python = os .fsdecode (build_python_path ())
298+ build_python = os .fsdecode (build_python_path (context ))
282299 configure = [
283300 "emconfigure" ,
284301 os .path .relpath (CHECKOUT / "configure" , working_dir ),
@@ -292,7 +309,7 @@ def configure_emscripten_python(context, working_dir):
292309 "--disable-ipv6" ,
293310 "--enable-big-digits=30" ,
294311 "--enable-wasm-dynamic-linking" ,
295- f"--prefix={ PREFIX_DIR } " ,
312+ f"--prefix={ paths [ 'prefix_dir' ] } " ,
296313 ]
297314 if pydebug :
298315 configure .append ("--with-pydebug" )
@@ -353,7 +370,7 @@ def configure_emscripten_python(context, working_dir):
353370 sys .stdout .flush ()
354371
355372
356- @subdir (HOST_DIR )
373+ @subdir ("host_dir" )
357374def make_emscripten_python (context , working_dir ):
358375 """Run `make` for the emscripten/host build."""
359376 call (
@@ -382,9 +399,10 @@ def build_all(context):
382399
383400def clean_contents (context ):
384401 """Delete all files created by this script."""
385- if CROSS_BUILD_DIR .exists ():
386- print (f"🧹 Deleting { CROSS_BUILD_DIR } ..." )
387- shutil .rmtree (CROSS_BUILD_DIR )
402+ host_triple_dir = context .build_paths ["host_triple_dir" ]
403+ if host_triple_dir .exists ():
404+ print (f"🧹 Deleting { host_triple_dir } ..." )
405+ shutil .rmtree (host_triple_dir )
388406
389407 if LOCAL_SETUP .exists ():
390408 with LOCAL_SETUP .open ("rb" ) as file :
@@ -439,6 +457,22 @@ def main():
439457 dest = "quiet" ,
440458 help = "Redirect output from subprocesses to a log file" ,
441459 )
460+ subcommand .add_argument (
461+ "--cross-build-dir" ,
462+ action = "store" ,
463+ default = None ,
464+ dest = "cross_build_dir" ,
465+ help = "Path to the cross-build directory "
466+ f"(default: { DEFAULT_CROSS_BUILD_DIR } )" ,
467+ )
468+ subcommand .add_argument (
469+ "--emsdk-cache" ,
470+ action = "store" ,
471+ default = None ,
472+ dest = "emsdk_cache" ,
473+ help = "Path to emsdk cache directory. If provided, validates that "
474+ "the required emscripten version is installed." ,
475+ )
442476 for subcommand in configure_build , configure_host :
443477 subcommand .add_argument (
444478 "--clean" ,
@@ -463,6 +497,12 @@ def main():
463497
464498 context = parser .parse_args ()
465499
500+ context .build_paths = get_build_paths (context .cross_build_dir )
501+
502+ if context .emsdk_cache :
503+ validate_emsdk_version (context .emsdk_cache )
504+ context .emsdk_cache = Path (context .emsdk_cache ).absolute ()
505+
466506 dispatch = {
467507 "make-libffi" : make_emscripten_libffi ,
468508 "make-mpdec" : make_mpdec ,
0 commit comments