Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 54 additions & 22 deletions tools/ci_build/github/apple/build_apple_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ def _get_framework_binary_path(framework_dir):
return os.path.join(framework_dir, "onnxruntime")


def _get_framework_headers_path(framework_dir):
versioned_headers_path = os.path.join(framework_dir, "Versions", "A", "Headers")
if os.path.exists(versioned_headers_path):
return versioned_headers_path

return os.path.join(framework_dir, "Headers")


# Build fat framework for all archs of a single sysroot
# For example, arm64 and x86_64 for iphonesimulator
def _build_for_apple_sysroot(
Expand Down Expand Up @@ -105,20 +113,30 @@ def _build_for_apple_sysroot(
if not info_plist_path:
info_plist_path = os.path.join(build_dir_current_arch, build_config, "Info.plist")
framework_info_path = os.path.join(build_dir_current_arch, build_config, "framework_info.json")
headers = glob.glob(os.path.join(framework_dir, "Headers", "*.h"))
headers = glob.glob(os.path.join(_get_framework_headers_path(framework_dir), "*.h"))

# manually create the fat framework
framework_dir = os.path.join(intermediates_dir, "frameworks", sysroot, "onnxruntime.framework")
# remove the existing framework if any
if os.path.exists(framework_dir):
shutil.rmtree(framework_dir)
pathlib.Path(framework_dir).mkdir(parents=True, exist_ok=True)
bundle_root = os.path.join(intermediates_dir, "frameworks", sysroot)
framework_dir = os.path.join(bundle_root, "onnxruntime.framework")
static_library_dir = os.path.join(bundle_root, "onnxruntime")
if build_dynamic_framework:
if sysroot == "macosx" or sysroot == "macabi":
fat_binary_output_path = os.path.join(framework_dir, "Versions", "A", "onnxruntime")
else:
fat_binary_output_path = os.path.join(framework_dir, "onnxruntime")
else:
fat_binary_output_path = os.path.join(static_library_dir, "libonnxruntime.a")

if os.path.exists(bundle_root):
shutil.rmtree(bundle_root)
pathlib.Path(bundle_root).mkdir(parents=True, exist_ok=True)
if build_dynamic_framework:
pathlib.Path(framework_dir).mkdir(parents=True, exist_ok=True)

# copy the Info.plist, framework_info.json, and header files

# macos requires different framework structure:
# https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html
if sysroot == "macosx" or sysroot == "macabi":
if build_dynamic_framework and (sysroot == "macosx" or sysroot == "macabi"):
# create headers and resources directory
header_dir = os.path.join(framework_dir, "Versions", "A", "Headers")
resource_dir = os.path.join(framework_dir, "Versions", "A", "Resources")
Expand All @@ -134,7 +152,7 @@ def _build_for_apple_sysroot(
# use lipo to create a fat ort library
lipo_command = ["lipo", "-create"]
lipo_command += ort_libs
lipo_command += ["-output", os.path.join(framework_dir, "Versions", "A", "onnxruntime")]
lipo_command += ["-output", fat_binary_output_path]
subprocess.run(lipo_command, shell=False, check=True)

# create the symbolic link
Expand All @@ -148,9 +166,14 @@ def _build_for_apple_sysroot(
pathlib.Path(os.path.join(framework_dir, "onnxruntime")).symlink_to("Versions/Current/onnxruntime")

else:
shutil.copy(info_plist_path, framework_dir)
shutil.copy(framework_info_path, os.path.dirname(framework_dir))
header_dir = os.path.join(framework_dir, "Headers")
if build_dynamic_framework:
shutil.copy(info_plist_path, framework_dir)
header_dir = os.path.join(framework_dir, "Headers")
else:
header_dir = os.path.join(static_library_dir, "Headers")
pathlib.Path(static_library_dir).mkdir(parents=True, exist_ok=True)

shutil.copy(framework_info_path, bundle_root)
pathlib.Path(header_dir).mkdir(parents=True, exist_ok=True)

for _header in headers:
Expand All @@ -159,10 +182,15 @@ def _build_for_apple_sysroot(
# use lipo to create a fat ort library
lipo_command = ["lipo", "-create"]
lipo_command += ort_libs
lipo_command += ["-output", os.path.join(framework_dir, "onnxruntime")]
lipo_command += ["-output", fat_binary_output_path]
subprocess.run(lipo_command, shell=False, check=True)

return framework_dir
return {
"artifact_path": framework_dir if build_dynamic_framework else fat_binary_output_path,
"framework_info_path": os.path.join(bundle_root, "framework_info.json"),
"headers_path": header_dir if build_dynamic_framework else os.path.join(static_library_dir, "Headers"),
"is_framework": build_dynamic_framework,
}


def _merge_framework_info_files(files, output_file):
Expand All @@ -188,7 +216,7 @@ def _build_package(args):
build_config = args.config

# build framework for individual sysroot
framework_dirs = []
build_artifacts = []
framework_info_files_to_merge = []
public_headers_path = ""
for sysroot in build_settings["build_osx_archs"]:
Expand All @@ -205,22 +233,21 @@ def _build_package(args):
if args.path_to_protoc_exe is not None:
base_build_command += ["--path_to_protoc_exe=" + str(args.path_to_protoc_exe.resolve())]

framework_dir = _build_for_apple_sysroot(
build_artifact = _build_for_apple_sysroot(
build_config,
intermediates_dir,
base_build_command,
sysroot,
build_settings["build_osx_archs"][sysroot],
args.build_dynamic_framework,
)
framework_dirs.append(framework_dir)
build_artifacts.append(build_artifact)

curr_framework_info_path = os.path.join(os.path.dirname(framework_dir), "framework_info.json")
framework_info_files_to_merge.append(curr_framework_info_path)
framework_info_files_to_merge.append(build_artifact["framework_info_path"])

# headers for each sysroot are the same, pick one of them
if not public_headers_path:
public_headers_path = os.path.join(os.path.dirname(framework_dir), "onnxruntime.framework", "Headers")
public_headers_path = build_artifact["headers_path"]

# create the folder for xcframework and copy the LICENSE and framework_info.json file
xcframework_dir = os.path.join(build_dir, "framework_out")
Expand All @@ -236,8 +263,13 @@ def _build_package(args):

# Assemble the final xcframework
build_xcframework_cmd = ["xcrun", "xcodebuild", "-create-xcframework", "-output", xcframework_path]
for framework_dir in framework_dirs:
build_xcframework_cmd.extend(["-framework", framework_dir])
for build_artifact in build_artifacts:
if build_artifact["is_framework"]:
build_xcframework_cmd.extend(["-framework", build_artifact["artifact_path"]])
else:
build_xcframework_cmd.extend(
["-library", build_artifact["artifact_path"], "-headers", build_artifact["headers_path"]]
)

subprocess.run(build_xcframework_cmd, shell=False, check=True, cwd=REPO_DIR)

Expand Down
Loading