|
24 | 24 | CHECKOUT = EMSCRIPTEN_DIR.parent.parent.parent |
25 | 25 | EMSCRIPTEN_VERSION_FILE = EMSCRIPTEN_DIR / "emscripten_version.txt" |
26 | 26 |
|
27 | | -CROSS_BUILD_DIR = CHECKOUT / "cross-build" |
28 | | -NATIVE_BUILD_DIR = CROSS_BUILD_DIR / "build" |
| 27 | +DEFAULT_CROSS_BUILD_DIR = CHECKOUT / "cross-build" |
29 | 28 | HOST_TRIPLE = "wasm32-emscripten" |
30 | 29 |
|
31 | | -DOWNLOAD_DIR = CROSS_BUILD_DIR / HOST_TRIPLE / "build" |
32 | | -HOST_BUILD_DIR = CROSS_BUILD_DIR / HOST_TRIPLE / "build" |
33 | | -HOST_DIR = HOST_BUILD_DIR / "python" |
34 | | -PREFIX_DIR = CROSS_BUILD_DIR / HOST_TRIPLE / "prefix" |
| 30 | + |
| 31 | +def get_build_paths(cross_build_dir=None): |
| 32 | + """Compute all build paths from the given cross-build directory.""" |
| 33 | + if cross_build_dir is None: |
| 34 | + cross_build_dir = DEFAULT_CROSS_BUILD_DIR |
| 35 | + cross_build_dir = Path(cross_build_dir).absolute() |
| 36 | + host_triple_dir = cross_build_dir / HOST_TRIPLE |
| 37 | + return { |
| 38 | + "cross_build_dir": cross_build_dir, |
| 39 | + "native_build_dir": cross_build_dir / "build", |
| 40 | + "host_triple_dir": host_triple_dir, |
| 41 | + "host_build_dir": host_triple_dir / "build", |
| 42 | + "host_dir": host_triple_dir / "build" / "python", |
| 43 | + "prefix_dir": host_triple_dir / "prefix", |
| 44 | + } |
| 45 | + |
35 | 46 |
|
36 | 47 | LOCAL_SETUP = CHECKOUT / "Modules" / "Setup.local" |
37 | 48 | LOCAL_SETUP_MARKER = b"# Generated by Tools/wasm/emscripten.py\n" |
@@ -115,12 +126,17 @@ def updated_env(updates, emsdk_cache): |
115 | 126 | return environment |
116 | 127 |
|
117 | 128 |
|
118 | | -def subdir(working_dir, *, clean_ok=False): |
119 | | - """Decorator to change to a working directory.""" |
| 129 | +def subdir(path_key, *, clean_ok=False): |
| 130 | + """Decorator to change to a working directory. |
| 131 | +
|
| 132 | + path_key is a key into context.build_paths, used to resolve the working |
| 133 | + directory at call time. |
| 134 | + """ |
120 | 135 |
|
121 | 136 | def decorator(func): |
122 | 137 | @functools.wraps(func) |
123 | 138 | def wrapper(context): |
| 139 | + working_dir = context.build_paths[path_key] |
124 | 140 | try: |
125 | 141 | tput_output = subprocess.check_output( |
126 | 142 | ["tput", "cols"], encoding="utf-8" |
@@ -177,20 +193,21 @@ def build_platform(): |
177 | 193 | return sysconfig.get_config_var("BUILD_GNU_TYPE") |
178 | 194 |
|
179 | 195 |
|
180 | | -def build_python_path(): |
| 196 | +def build_python_path(context): |
181 | 197 | """The path to the build Python binary.""" |
182 | | - binary = NATIVE_BUILD_DIR / "python" |
| 198 | + native_build_dir = context.build_paths["native_build_dir"] |
| 199 | + binary = native_build_dir / "python" |
183 | 200 | if not binary.is_file(): |
184 | 201 | binary = binary.with_suffix(".exe") |
185 | 202 | if not binary.is_file(): |
186 | 203 | raise FileNotFoundError( |
187 | | - f"Unable to find `python(.exe)` in {NATIVE_BUILD_DIR}" |
| 204 | + f"Unable to find `python(.exe)` in {native_build_dir}" |
188 | 205 | ) |
189 | 206 |
|
190 | 207 | return binary |
191 | 208 |
|
192 | 209 |
|
193 | | -@subdir(NATIVE_BUILD_DIR, clean_ok=True) |
| 210 | +@subdir("native_build_dir", clean_ok=True) |
194 | 211 | def configure_build_python(context, working_dir): |
195 | 212 | """Configure the build/host Python.""" |
196 | 213 | if LOCAL_SETUP.exists(): |
@@ -206,12 +223,12 @@ def configure_build_python(context, working_dir): |
206 | 223 | call(configure, quiet=context.quiet) |
207 | 224 |
|
208 | 225 |
|
209 | | -@subdir(NATIVE_BUILD_DIR) |
| 226 | +@subdir("native_build_dir") |
210 | 227 | def make_build_python(context, working_dir): |
211 | 228 | """Make/build the build Python.""" |
212 | 229 | call(["make", "--jobs", str(cpu_count()), "all"], quiet=context.quiet) |
213 | 230 |
|
214 | | - binary = build_python_path() |
| 231 | + binary = build_python_path(context) |
215 | 232 | cmd = [ |
216 | 233 | binary, |
217 | 234 | "-c", |
@@ -241,7 +258,7 @@ def download_and_unpack(working_dir: Path, url: str, expected_shasum: str): |
241 | 258 | shutil.unpack_archive(tmp_file.name, working_dir) |
242 | 259 |
|
243 | 260 |
|
244 | | -@subdir(HOST_BUILD_DIR, clean_ok=True) |
| 261 | +@subdir("host_build_dir", clean_ok=True) |
245 | 262 | def make_emscripten_libffi(context, working_dir): |
246 | 263 | ver = "3.4.6" |
247 | 264 | libffi_dir = working_dir / f"libffi-{ver}" |
@@ -253,13 +270,15 @@ def make_emscripten_libffi(context, working_dir): |
253 | 270 | ) |
254 | 271 | call( |
255 | 272 | [EMSCRIPTEN_DIR / "make_libffi.sh"], |
256 | | - env=updated_env({"PREFIX": PREFIX_DIR}, context.emsdk_cache), |
| 273 | + env=updated_env( |
| 274 | + {"PREFIX": context.build_paths["prefix_dir"]}, context.emsdk_cache |
| 275 | + ), |
257 | 276 | cwd=libffi_dir, |
258 | 277 | quiet=context.quiet, |
259 | 278 | ) |
260 | 279 |
|
261 | 280 |
|
262 | | -@subdir(HOST_BUILD_DIR, clean_ok=True) |
| 281 | +@subdir("host_build_dir", clean_ok=True) |
263 | 282 | def make_mpdec(context, working_dir): |
264 | 283 | ver = "4.0.1" |
265 | 284 | mpdec_dir = working_dir / f"mpdecimal-{ver}" |
@@ -275,7 +294,7 @@ def make_mpdec(context, working_dir): |
275 | 294 | mpdec_dir / "configure", |
276 | 295 | "CFLAGS=-fPIC", |
277 | 296 | "--prefix", |
278 | | - PREFIX_DIR, |
| 297 | + context.build_paths["prefix_dir"], |
279 | 298 | "--disable-shared", |
280 | 299 | ], |
281 | 300 | cwd=mpdec_dir, |
@@ -289,14 +308,15 @@ def make_mpdec(context, working_dir): |
289 | 308 | ) |
290 | 309 |
|
291 | 310 |
|
292 | | -@subdir(HOST_DIR, clean_ok=True) |
| 311 | +@subdir("host_dir", clean_ok=True) |
293 | 312 | def configure_emscripten_python(context, working_dir): |
294 | 313 | """Configure the emscripten/host build.""" |
| 314 | + paths = context.build_paths |
295 | 315 | config_site = os.fsdecode(EMSCRIPTEN_DIR / "config.site-wasm32-emscripten") |
296 | 316 |
|
297 | 317 | emscripten_build_dir = working_dir.relative_to(CHECKOUT) |
298 | 318 |
|
299 | | - python_build_dir = NATIVE_BUILD_DIR / "build" |
| 319 | + python_build_dir = paths["native_build_dir"] / "build" |
300 | 320 | lib_dirs = list(python_build_dir.glob("lib.*")) |
301 | 321 | assert len(lib_dirs) == 1, ( |
302 | 322 | f"Expected a single lib.* directory in {python_build_dir}" |
@@ -322,13 +342,13 @@ def configure_emscripten_python(context, working_dir): |
322 | 342 | capture_output=True, |
323 | 343 | ) |
324 | 344 | host_runner = res.stdout.strip() |
325 | | - pkg_config_path_dir = (PREFIX_DIR / "lib/pkgconfig/").resolve() |
| 345 | + pkg_config_path_dir = (paths["prefix_dir"] / "lib/pkgconfig/").resolve() |
326 | 346 | env_additions = { |
327 | 347 | "CONFIG_SITE": config_site, |
328 | 348 | "HOSTRUNNER": host_runner, |
329 | 349 | "EM_PKG_CONFIG_PATH": str(pkg_config_path_dir), |
330 | 350 | } |
331 | | - build_python = os.fsdecode(build_python_path()) |
| 351 | + build_python = os.fsdecode(build_python_path(context)) |
332 | 352 | configure = [ |
333 | 353 | "emconfigure", |
334 | 354 | os.path.relpath(CHECKOUT / "configure", working_dir), |
@@ -342,7 +362,7 @@ def configure_emscripten_python(context, working_dir): |
342 | 362 | "--disable-ipv6", |
343 | 363 | "--enable-big-digits=30", |
344 | 364 | "--enable-wasm-dynamic-linking", |
345 | | - f"--prefix={PREFIX_DIR}", |
| 365 | + f"--prefix={paths['prefix_dir']}", |
346 | 366 | ] |
347 | 367 | if pydebug: |
348 | 368 | configure.append("--with-pydebug") |
@@ -403,7 +423,7 @@ def configure_emscripten_python(context, working_dir): |
403 | 423 | sys.stdout.flush() |
404 | 424 |
|
405 | 425 |
|
406 | | -@subdir(HOST_DIR) |
| 426 | +@subdir("host_dir") |
407 | 427 | def make_emscripten_python(context, working_dir): |
408 | 428 | """Run `make` for the emscripten/host build.""" |
409 | 429 | call( |
@@ -432,9 +452,17 @@ def build_all(context): |
432 | 452 |
|
433 | 453 | def clean_contents(context): |
434 | 454 | """Delete all files created by this script.""" |
435 | | - if CROSS_BUILD_DIR.exists(): |
436 | | - print(f"🧹 Deleting {CROSS_BUILD_DIR} ...") |
437 | | - shutil.rmtree(CROSS_BUILD_DIR) |
| 455 | + if context.target in {"all", "build"}: |
| 456 | + build_dir = context.build_paths["native_build_dir"] |
| 457 | + if build_dir.exists(): |
| 458 | + print(f"🧹 Deleting {build_dir} ...") |
| 459 | + shutil.rmtree(build_dir) |
| 460 | + |
| 461 | + if context.target in {"all", "host"}: |
| 462 | + host_triple_dir = context.build_paths["host_triple_dir"] |
| 463 | + if host_triple_dir.exists(): |
| 464 | + print(f"🧹 Deleting {host_triple_dir} ...") |
| 465 | + shutil.rmtree(host_triple_dir) |
438 | 466 |
|
439 | 467 | if LOCAL_SETUP.exists(): |
440 | 468 | with LOCAL_SETUP.open("rb") as file: |
@@ -472,6 +500,17 @@ def main(): |
472 | 500 | clean = subcommands.add_parser( |
473 | 501 | "clean", help="Delete files and directories created by this script" |
474 | 502 | ) |
| 503 | + clean.add_argument( |
| 504 | + "target", |
| 505 | + nargs="?", |
| 506 | + default="host", |
| 507 | + choices=["all", "host", "build"], |
| 508 | + help=( |
| 509 | + "What should be cleaned. 'build' for just the build platform, or " |
| 510 | + "'host' for the host platform, or 'all' for both. Defaults to 'host'." |
| 511 | + ), |
| 512 | + ) |
| 513 | + |
475 | 514 | for subcommand in ( |
476 | 515 | build, |
477 | 516 | configure_build, |
@@ -489,6 +528,14 @@ def main(): |
489 | 528 | dest="quiet", |
490 | 529 | help="Redirect output from subprocesses to a log file", |
491 | 530 | ) |
| 531 | + subcommand.add_argument( |
| 532 | + "--cross-build-dir", |
| 533 | + action="store", |
| 534 | + default=None, |
| 535 | + dest="cross_build_dir", |
| 536 | + help="Path to the cross-build directory " |
| 537 | + f"(default: {DEFAULT_CROSS_BUILD_DIR})", |
| 538 | + ) |
492 | 539 | subcommand.add_argument( |
493 | 540 | "--emsdk-cache", |
494 | 541 | action="store", |
@@ -521,6 +568,8 @@ def main(): |
521 | 568 |
|
522 | 569 | context = parser.parse_args() |
523 | 570 |
|
| 571 | + context.build_paths = get_build_paths(context.cross_build_dir) |
| 572 | + |
524 | 573 | if context.emsdk_cache: |
525 | 574 | validate_emsdk_version(context.emsdk_cache) |
526 | 575 | context.emsdk_cache = Path(context.emsdk_cache).absolute() |
|