|
18 | 18 | import platform |
19 | 19 | import shlex |
20 | 20 | import subprocess |
| 21 | +import shutil |
21 | 22 | from typing import Optional, List, Dict |
22 | 23 |
|
23 | 24 |
|
@@ -498,3 +499,115 @@ def GetTBBPatches(context): |
498 | 499 | f"-target arm64-apple-ios{version}-simulator")) |
499 | 500 |
|
500 | 501 | return target_config_patches, clang_config_patches |
| 502 | + |
| 503 | + |
| 504 | +def BuildXCFramework(root, targets, args): |
| 505 | + if TARGET_UNIVERSAL in targets: |
| 506 | + targets.extend([TARGET_ARM64, TARGET_X86]) |
| 507 | + targets.remove(TARGET_UNIVERSAL) |
| 508 | + if TARGET_NATIVE in targets: |
| 509 | + targets.remove(TARGET_NATIVE) |
| 510 | + targets.append(GetHostArch()) |
| 511 | + |
| 512 | + targets = set(targets) |
| 513 | + print(f"Building {len(targets)} targets...") |
| 514 | + shared_sources = os.path.join(root, "shared_sources") |
| 515 | + os.makedirs(shared_sources, exist_ok=True) |
| 516 | + |
| 517 | + do_lipo = TARGET_ARM64 in targets and TARGET_X86 in targets |
| 518 | + |
| 519 | + build_command = os.path.join(os.path.dirname(os.path.abspath(__file__)), "build_usd.py") |
| 520 | + frameworks = [] |
| 521 | + to_lipo = [] |
| 522 | + for target in targets: |
| 523 | + print(f"Building {target}...") |
| 524 | + install_dir = os.path.join(root, "builds", target) |
| 525 | + target_src_dir = os.path.join(install_dir, "src") |
| 526 | + os.makedirs(target_src_dir, exist_ok=True) |
| 527 | + framework = os.path.join(install_dir, "frameworks/OpenUSD.framework") |
| 528 | + if do_lipo and target in (TARGET_X86, TARGET_ARM64): |
| 529 | + to_lipo.append(framework) |
| 530 | + else: |
| 531 | + frameworks.append(framework) |
| 532 | + |
| 533 | + # Copy the shared sources over to save time |
| 534 | + for src in os.listdir(shared_sources): |
| 535 | + shared_src = os.path.join(shared_sources, src) |
| 536 | + target_src = os.path.join(target_src_dir, src) |
| 537 | + shutil.copy2(shared_src, target_src) |
| 538 | + |
| 539 | + target_args = [sys.executable, build_command, install_dir, "--build-target", target, "--build-apple-framework"] |
| 540 | + target_args.extend(args) |
| 541 | + try: |
| 542 | + subprocess.check_call(target_args) |
| 543 | + except: |
| 544 | + raise RuntimeError(f"Failed to build {target} using {' '.join(target_args)}") |
| 545 | + |
| 546 | + # Copy the unshared sources back as needed |
| 547 | + # We copy the zips in case there are any patches involved |
| 548 | + for src in os.listdir(target_src_dir): |
| 549 | + target_src_path = os.path.join(target_src_dir, src) |
| 550 | + shared_src_path = os.path.join(shared_sources, src) |
| 551 | + if not os.path.exists(shared_src_path) and os.path.isfile(target_src_path): |
| 552 | + shutil.copy2(target_src_path, shared_src_path) |
| 553 | + |
| 554 | + assert os.path.exists(framework) |
| 555 | + |
| 556 | + if do_lipo: |
| 557 | + print("Combining Mac framework architectures") |
| 558 | + assert (len(to_lipo) == 2) |
| 559 | + |
| 560 | + fat_dir = os.path.join(root, "builds/fat") |
| 561 | + if os.path.exists(fat_dir): |
| 562 | + shutil.rmtree(fat_dir) |
| 563 | + |
| 564 | + fat_framework = os.path.join(fat_dir, "OpenUSD.framework") |
| 565 | + subprocess.check_call(["ditto", to_lipo[0], fat_framework]) # Ditto copies more metadata than shutil does |
| 566 | + |
| 567 | + dylib_a = os.path.join(to_lipo[0], "Versions/A/OpenUSD") |
| 568 | + dylib_b = os.path.join(to_lipo[1], "Versions/A/OpenUSD") |
| 569 | + dylib_dest = os.path.join(fat_framework, "Versions/A/OpenUSD") |
| 570 | + subprocess.check_call(["lipo", dylib_a, dylib_b, "-create", "-output", dylib_dest]) |
| 571 | + frameworks.append(fat_framework) |
| 572 | + |
| 573 | + print("Creating XCFramework") |
| 574 | + xcframework_dir = os.path.join(root, "xcframework") |
| 575 | + if os.path.exists(xcframework_dir): |
| 576 | + shutil.rmtree(xcframework_dir) |
| 577 | + os.makedirs(xcframework_dir, exist_ok=True) |
| 578 | + xcframework_path = os.path.join(xcframework_dir, "OpenUSD.xcframework") |
| 579 | + command = ["xcodebuild", "-create-xcframework", "-output", xcframework_path] |
| 580 | + for framework in frameworks: |
| 581 | + command.extend(["-framework", framework]) |
| 582 | + |
| 583 | + try: |
| 584 | + subprocess.check_call(command) |
| 585 | + except: |
| 586 | + raise RuntimeError(f"Failed to create XCFramework using {' '.join(command)}") |
| 587 | + |
| 588 | + print("Success! Add the OpenUSD.xcframework to your Xcode Project.") |
| 589 | + |
| 590 | + |
| 591 | +def main(): |
| 592 | + import argparse |
| 593 | + parser = argparse.ArgumentParser(description="A set of command line utilities for building on Apple Platforms") |
| 594 | + subparsers = parser.add_subparsers(dest="command", required=True) |
| 595 | + |
| 596 | + xcframework = subparsers.add_parser("xcframework", |
| 597 | + description="Build multiple framework targets together as a single xcframework") |
| 598 | + xcframework.add_argument("install_dir", type=str, |
| 599 | + help="Directory where the XCFramework will be installed") |
| 600 | + xcframework.add_argument("--build-targets", nargs="+", help="The list of targets to build.", |
| 601 | + choices=GetBuildTargets(), |
| 602 | + default=GetBuildTargets()) |
| 603 | + |
| 604 | + args, unknown = parser.parse_known_args() |
| 605 | + command = args.command |
| 606 | + if command == "xcframework": |
| 607 | + BuildXCFramework(args.install_dir, args.build_targets, unknown) |
| 608 | + else: |
| 609 | + raise RuntimeError(f"Unknown command: {command}") |
| 610 | + |
| 611 | + |
| 612 | +if __name__ == '__main__': |
| 613 | + main() |
0 commit comments