forked from opencv/opencv
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request opencv#18826 from Rightpoint:feature/colejd/build-…
…catalyst-xcframework Support XCFramework builds, Catalyst * Early work on xcframework support * Improve legibility * Somehow this works * Specify ABIs in a place where they won't get erased If you pass in the C/CXX flags from the Python script, they won't be respected. By doing it in the actual toolchain, the options are respected and Catalyst successfully links. * Clean up and push updates * Actually use Catalyst ABI Needed to specify EXE linker flags to get compiler tests to link to the Catalyst ABIs. * Clean up * Revert changes to common toolchain that don't matter * Try some things * Support Catalyst build in OSX scripts * Remove unnecessary iOS reference to AssetsLibrary framework * Getting closer * Try some things, port to Python 3 * Some additional fixes * Point Cmake Plist gen to osx directory for Catalyst targets * Remove dynamic lib references for Catalyst, copy iOS instead of macos * Add flag for building only specified archs, remove iOS catalyst refs * Add build-xcframework.sh * Update build-xcframework.sh * Add presumptive Apple Silicon support * Add arm64 iphonesimulator target * Fix xcframework build * Working on arm64 iOS simulator * Support 2.7 (replace run with check_output) * Correctly check output of uname_m against arch * Clean up * Use lipo for intermediate frameworks, add python script Remove unneeded __init__.py * Simplify python xcframework build script * Add --only-64-bit flag * Add --framework-name flag * Document * Commit to f-strings, improve console output * Add i386 to iphonesimulator platform in xcframework generator * Enable objc for non-Catalyst frameworks * Fix xcframework builder for paths with spaces * Use arch when specifying Catalyst build platform in build command * Fix incorrect settings for framework_name argparse configuration * Prefer underscores instead of hyphens in new flags * Move Catalyst flags to where they'll actually get used * Use --without=objc on Catalyst target for now * Remove get_or_create_folder and simplify logic * Remove unused import * Tighten up help text * Document * Move common functions into cv_build_utils * Improve documentation * Remove old build script * Add readme * Check for required CMake and Xcode versions * Clean up TODOs and re-enable `copy_samples()` Remove TODO Fixup * Add missing print_function import * Clarify CMake dependency documentation * Revert python2 change in gen_objc * Remove unnecessary builtins imports * Remove trailing whitespace * Avoid building Catalyst unless specified This makes Catalyst support a non-breaking change, though defaults should be specified when a breaking change is possible. * Prevent lipoing for the same archs on different platforms before build * Rename build-xcframework.py to build_xcframework.py * Check for duplicate archs more carefully * Prevent sample copying error when directory already exists This can happen when building multiple architectures for the same platform. * Simplify code for checking for default archs * Improve build_xcframework.py header text * Correctly resolve Python script paths * Parse only known args in ios/osx build_framework.py * Pass through uncaptured args in build_xcframework to osx/ios build * Fix typo * Fix typo * Fix unparameterized build path for intermediate frameworks * Fix dyanmic info.plist path for catalyst * Fix utf-8 Python 3 issue * Add dynamic flag to osx script * Rename platform to platforms, remove armv7s and i386 * Fix creation of dynamic framework on maccatalyst and macos * Update platforms/apple/readme.md * Add `macos_archs` flag and deprecate `archs` flag * Allow specification of archs when generating xcframework from terminal * Change xcframework platform argument names to match archs flag names * Remove platforms as a concept and shadow archs flags from ios/osx .py * Improve documentation * Fix building of objc module on Catalyst, excluding Swift * Clean up build folder logic a bit * Fix framework_name flag * Drop passthrough_args, use unknown_args instead * minor: coding style changes Co-authored-by: Chris Ballinger <[email protected]>
- Loading branch information
1 parent
19d825a
commit 85b0fb2
Showing
14 changed files
with
487 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
#!/usr/bin/env python3 | ||
""" | ||
This script builds OpenCV into an xcframework compatible with the platforms | ||
of your choice. Just run it and grab a snack; you'll be waiting a while. | ||
""" | ||
|
||
import sys, os, argparse, pathlib, traceback | ||
from cv_build_utils import execute, print_error, print_header, get_xcode_version, get_cmake_version | ||
|
||
if __name__ == "__main__": | ||
|
||
# Check for dependencies | ||
assert sys.version_info >= (3, 6), f"Python 3.6 or later is required! Current version is {sys.version_info}" | ||
# Need CMake 3.18.5/3.19 or later for a Silicon-related fix to building for the iOS Simulator. | ||
# See https://gitlab.kitware.com/cmake/cmake/-/issues/21425 for context. | ||
assert get_cmake_version() >= (3, 18, 5), f"CMake 3.18.5 or later is required. Current version is {get_cmake_version()}" | ||
# Need Xcode 12.2 for Apple Silicon support | ||
assert get_xcode_version() >= (12, 2), f"Xcode 12.2 command line tools or later are required! Current version is {get_xcode_version()}. \ | ||
Run xcode-select to switch if you have multiple Xcode installs." | ||
|
||
# Parse arguments | ||
description = """ | ||
This script builds OpenCV into an xcframework supporting the Apple platforms of your choice. | ||
""" | ||
epilog = """ | ||
Any arguments that are not recognized by this script are passed through to the ios/osx build_framework.py scripts. | ||
""" | ||
parser = argparse.ArgumentParser(description=description, epilog=epilog) | ||
parser.add_argument('out', metavar='OUTDIR', help='The directory where the xcframework will be created') | ||
parser.add_argument('--framework_name', default='opencv2', help='Name of OpenCV xcframework (default: opencv2, will change to OpenCV in future version)') | ||
parser.add_argument('--iphoneos_archs', default=None, help='select iPhoneOS target ARCHS. Default is "armv7,arm64"') | ||
parser.add_argument('--iphonesimulator_archs', default=None, help='select iPhoneSimulator target ARCHS. Default is "x86_64,arm64"') | ||
parser.add_argument('--macos_archs', default=None, help='Select MacOS ARCHS. Default is "x86_64,arm64"') | ||
parser.add_argument('--catalyst_archs', default=None, help='Select Catalyst ARCHS. Default is "x86_64,arm64"') | ||
parser.add_argument('--build_only_specified_archs', default=False, action='store_true', help='if enabled, only directly specified archs are built and defaults are ignored') | ||
|
||
args, unknown_args = parser.parse_known_args() | ||
if unknown_args: | ||
print(f"The following args are not recognized by this script and will be passed through to the ios/osx build_framework.py scripts: {unknown_args}") | ||
|
||
# Parse architectures from args | ||
iphoneos_archs = args.iphoneos_archs | ||
if not iphoneos_archs and not args.build_only_specified_archs: | ||
# Supply defaults | ||
iphoneos_archs = "armv7,arm64" | ||
print(f'Using iPhoneOS ARCHS={iphoneos_archs}') | ||
|
||
iphonesimulator_archs = args.iphonesimulator_archs | ||
if not iphonesimulator_archs and not args.build_only_specified_archs: | ||
# Supply defaults | ||
iphonesimulator_archs = "x86_64,arm64" | ||
print(f'Using iPhoneSimulator ARCHS={iphonesimulator_archs}') | ||
|
||
macos_archs = args.macos_archs | ||
if not macos_archs and not args.build_only_specified_archs: | ||
# Supply defaults | ||
macos_archs = "x86_64,arm64" | ||
print(f'Using MacOS ARCHS={macos_archs}') | ||
|
||
catalyst_archs = args.macos_archs | ||
if not catalyst_archs and not args.build_only_specified_archs: | ||
# Supply defaults | ||
catalyst_archs = "x86_64,arm64" | ||
print(f'Using Catalyst ARCHS={catalyst_archs}') | ||
|
||
# Build phase | ||
|
||
try: | ||
# Build .frameworks for each platform | ||
osx_script_path = os.path.abspath(os.path.abspath(os.path.dirname(__file__))+'/../osx/build_framework.py') | ||
ios_script_path = os.path.abspath(os.path.abspath(os.path.dirname(__file__))+'/../ios/build_framework.py') | ||
|
||
build_folders = [] | ||
|
||
def get_or_create_build_folder(base_dir, platform): | ||
build_folder = f"./{base_dir}/{platform}".replace(" ", "\\ ") # Escape spaces in output path | ||
pathlib.Path(build_folder).mkdir(parents=True, exist_ok=True) | ||
return build_folder | ||
|
||
if iphoneos_archs: | ||
build_folder = get_or_create_build_folder(args.out, "iphoneos") | ||
build_folders.append(build_folder) | ||
command = ["python3", ios_script_path, "--iphoneos_archs", iphoneos_archs, "--framework_name", args.framework_name, "--build_only_specified_archs", build_folder] + unknown_args | ||
print_header("Building iPhoneOS frameworks") | ||
print(command) | ||
execute(command, cwd=os.getcwd()) | ||
if iphonesimulator_archs: | ||
build_folder = get_or_create_build_folder(args.out, "iphonesimulator") | ||
build_folders.append(build_folder) | ||
command = ["python3", ios_script_path, "--iphonesimulator_archs", iphonesimulator_archs, "--framework_name", args.framework_name, "--build_only_specified_archs", build_folder] + unknown_args | ||
print_header("Building iPhoneSimulator frameworks") | ||
execute(command, cwd=os.getcwd()) | ||
if macos_archs: | ||
build_folder = get_or_create_build_folder(args.out, "macos") | ||
build_folders.append(build_folder) | ||
command = ["python3", osx_script_path, "--macos_archs", macos_archs, "--framework_name", args.framework_name, "--build_only_specified_archs", build_folder] + unknown_args | ||
print_header("Building MacOS frameworks") | ||
execute(command, cwd=os.getcwd()) | ||
if catalyst_archs: | ||
build_folder = get_or_create_build_folder(args.out, "catalyst") | ||
build_folders.append(build_folder) | ||
command = ["python3", osx_script_path, "--catalyst_archs", catalyst_archs, "--framework_name", args.framework_name, "--build_only_specified_archs", build_folder] + unknown_args | ||
print_header("Building Catalyst frameworks") | ||
execute(command, cwd=os.getcwd()) | ||
|
||
# Put all the built .frameworks together into a .xcframework | ||
print_header("Building xcframework") | ||
xcframework_build_command = [ | ||
"xcodebuild", | ||
"-create-xcframework", | ||
"-output", | ||
f"{args.out}/{args.framework_name}.xcframework", | ||
] | ||
for folder in build_folders: | ||
xcframework_build_command += ["-framework", f"{folder}/{args.framework_name}.framework"] | ||
execute(xcframework_build_command, cwd=os.getcwd()) | ||
|
||
print("") | ||
print_header(f"Finished building {args.out}/{args.framework_name}.xcframework") | ||
except Exception as e: | ||
print_error(e) | ||
traceback.print_exc(file=sys.stderr) | ||
sys.exit(1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
#!/usr/bin/env python | ||
""" | ||
Common utilities. These should be compatible with Python 2 and 3. | ||
""" | ||
|
||
from __future__ import print_function | ||
import sys, re | ||
from subprocess import check_call, check_output, CalledProcessError | ||
|
||
def execute(cmd, cwd = None): | ||
print("Executing: %s in %s" % (cmd, cwd), file=sys.stderr) | ||
print('Executing: ' + ' '.join(cmd)) | ||
retcode = check_call(cmd, cwd = cwd) | ||
if retcode != 0: | ||
raise Exception("Child returned:", retcode) | ||
|
||
def print_header(text): | ||
print("="*60) | ||
print(text) | ||
print("="*60) | ||
|
||
def print_error(text): | ||
print("="*60, file=sys.stderr) | ||
print("ERROR: %s" % text, file=sys.stderr) | ||
print("="*60, file=sys.stderr) | ||
|
||
def get_xcode_major(): | ||
ret = check_output(["xcodebuild", "-version"]).decode('utf-8') | ||
m = re.match(r'Xcode\s+(\d+)\..*', ret, flags=re.IGNORECASE) | ||
if m: | ||
return int(m.group(1)) | ||
else: | ||
raise Exception("Failed to parse Xcode version") | ||
|
||
def get_xcode_version(): | ||
""" | ||
Returns the major and minor version of the current Xcode | ||
command line tools as a tuple of (major, minor) | ||
""" | ||
ret = check_output(["xcodebuild", "-version"]).decode('utf-8') | ||
m = re.match(r'Xcode\s+(\d+)\.(\d+)', ret, flags=re.IGNORECASE) | ||
if m: | ||
return (int(m.group(1)), int(m.group(2))) | ||
else: | ||
raise Exception("Failed to parse Xcode version") | ||
|
||
def get_xcode_setting(var, projectdir): | ||
ret = check_output(["xcodebuild", "-showBuildSettings"], cwd = projectdir).decode('utf-8') | ||
m = re.search("\s" + var + " = (.*)", ret) | ||
if m: | ||
return m.group(1) | ||
else: | ||
raise Exception("Failed to parse Xcode settings") | ||
|
||
def get_cmake_version(): | ||
""" | ||
Returns the major and minor version of the current CMake | ||
command line tools as a tuple of (major, minor, revision) | ||
""" | ||
ret = check_output(["cmake", "--version"]).decode('utf-8') | ||
m = re.match(r'cmake\sversion\s+(\d+)\.(\d+).(\d+)', ret, flags=re.IGNORECASE) | ||
if m: | ||
return (int(m.group(1)), int(m.group(2)), int(m.group(3))) | ||
else: | ||
raise Exception("Failed to parse CMake version") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Building for Apple Platforms | ||
|
||
build_xcframework.py creates an xcframework supporting a variety of Apple platforms. | ||
|
||
You'll need the following to run these steps: | ||
- MacOS 10.15 or later | ||
- Python 3.6 or later | ||
- CMake 3.18.5/3.19.0 or later (make sure the `cmake` command is available on your PATH) | ||
- Xcode 12.2 or later (and its command line tools) | ||
|
||
You can then run build_xcframework.py, as below: | ||
``` | ||
cd ~/<my_working_directory> | ||
python opencv/platforms/apple/build_xcframework.py ./build_xcframework | ||
``` | ||
|
||
Grab a coffee, because you'll be here for a while. By default this builds OpenCV for 8 architectures across 4 platforms: | ||
|
||
- iOS (`--iphoneos_archs`): arm64, armv7 | ||
- iOS Simulator (`--iphonesimulator_archs`): x86_64, arm64 | ||
- macOS (`--macos_archs`): x86_64, arm64 | ||
- Mac Catalyst (`--catalyst_archs`): x86_64, arm64 | ||
|
||
If everything's fine, you will eventually get `opencv2.xcframework` in the output directory. | ||
|
||
The script has some configuration options to exclude platforms and architectures you don't want to build for. Use the `--help` flag for more information. | ||
|
||
## Examples | ||
|
||
You may override the defaults by specifying a value for any of the `*_archs` flags. For example, if you want to build for arm64 on every platform, you can do this: | ||
|
||
``` | ||
python build_xcframework.py somedir --iphoneos_archs arm64 --iphonesimulator_archs arm64 --macos_archs arm64 --catalyst_archs arm64 | ||
``` | ||
|
||
If you want to build only for certain platforms, you can supply the `--build_only_specified_archs` flag, which makes the script build only the archs you directly ask for. For example, to build only for Catalyst, you can do this: | ||
|
||
``` | ||
python build_xcframework.py somedir --catalyst_archs x86_64,arm64 --build_only_specified_archs | ||
``` |
Empty file.
Oops, something went wrong.