Skip to content

Commit d952340

Browse files
dgovilpixar-oss
authored andcommitted
Enable building as an XCFramework
This change enables OpenUSD to be built as an [XCFramework](https://developer.apple.com/documentation/xcode/creating-a-multi-platform-binary-framework-bundle) which are a multi platform framework, and simplifies creation of singular codebases that can target each of the supported Apple platforms. From a design perspective, it adds a command line argument to apple_utils.py which calls the build_usd.py per each target. `python OpenUSD/build_scripts/apple_utils.py xcframework /path/to/my_usd_install_dir` Closes PixarAnimationStudios#3715 (Internal change: 2418019)
1 parent d20c525 commit d952340

3 files changed

Lines changed: 120 additions & 1 deletion

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,12 @@ It is recommended to set it to `Embed and Sign`.
191191
To setup headers, configure the Xcode `SYSTEM_HEADER_SEARCH_PATHS` to add the path to your headers. e.g
192192
`$(SRCROOT)/OpenUSD.framework/Headers` if the framework exists in your projects root.
193193

194+
OpenUSD also supports building a combined XCFramework of multiple targets.
195+
This command takes an optional list of targets to build, but will otherwise build all supported platforms.
196+
197+
```
198+
> python OpenUSD/build_scripts/apple_utils.py xcframework /path/to/my_usd_install_dir
199+
```
194200

195201
##### Windows:
196202

build_scripts/apple_utils.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import platform
1919
import shlex
2020
import subprocess
21+
import shutil
2122
from typing import Optional, List, Dict
2223

2324

@@ -498,3 +499,115 @@ def GetTBBPatches(context):
498499
f"-target arm64-apple-ios{version}-simulator"))
499500

500501
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()

cmake/macros/Private.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ endfunction()
721721
function(_pxr_install_rpath rpathRef NAME)
722722
if (PXR_BUILD_APPLE_FRAMEWORK)
723723
# Apple Frameworks already fix the install path at the end
724-
# so this maks things faster and reduces duplication errors
724+
# so this makes things faster and reduces duplication errors
725725
return()
726726
endif()
727727
# Get and remove the origin.

0 commit comments

Comments
 (0)