File: build_xcframework.py

package info (click to toggle)
opencv 4.5.1%2Bdfsg-5
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 268,248 kB
  • sloc: cpp: 969,170; xml: 682,525; python: 36,732; lisp: 30,170; java: 25,155; ansic: 7,927; javascript: 5,643; objc: 2,041; sh: 935; cs: 601; perl: 494; makefile: 145
file content (133 lines) | stat: -rwxr-xr-x 7,246 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/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, contextlib, shutil
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), "Python 3.6 or later is required! Current version is {}".format(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), "CMake 3.18.5 or later is required. Current version is {}".format(get_cmake_version())
    # Need Xcode 12.2 for Apple Silicon support
    assert get_xcode_version() >= (12, 2), \
        "Xcode 12.2 command line tools or later are required! Current version is {}. ".format(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('-o', '--out', metavar='OUTDIR', help='<Required> The directory where the xcframework will be created', required=True)
    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("The following args are not recognized by this script and will be passed through to the ios/osx build_framework.py scripts: {}".format(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('Using iPhoneOS ARCHS={}'.format(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('Using iPhoneSimulator ARCHS={}'.format(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('Using MacOS ARCHS={}'.format(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('Using Catalyst ARCHS={}'.format(catalyst_archs))

    # Build phase

    try:
        # Phase 1: 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 = "{}/{}".format(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, build_folder, "--iphoneos_archs", iphoneos_archs, "--framework_name", args.framework_name, "--build_only_specified_archs"] + 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, build_folder, "--iphonesimulator_archs", iphonesimulator_archs, "--framework_name", args.framework_name, "--build_only_specified_archs"] + 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, build_folder, "--macos_archs", macos_archs, "--framework_name", args.framework_name, "--build_only_specified_archs"] + 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, build_folder, "--catalyst_archs", catalyst_archs, "--framework_name", args.framework_name, "--build_only_specified_archs"] + unknown_args
            print_header("Building Catalyst frameworks")
            execute(command, cwd=os.getcwd())

        # Phase 2: put all the built .frameworks together into a .xcframework

        xcframework_path = "{}/{}.xcframework".format(args.out, args.framework_name)
        print_header("Building {}".format(xcframework_path))

        # Remove the xcframework if it exists, otherwise the existing
        # file will cause the xcodebuild command to fail.
        with contextlib.suppress(FileNotFoundError):
            shutil.rmtree(xcframework_path)
            print("Removed existing xcframework at {}".format(xcframework_path))

        xcframework_build_command = [
            "xcodebuild",
            "-create-xcframework",
            "-output",
            xcframework_path,
        ]
        for folder in build_folders:
            xcframework_build_command += ["-framework", "{}/{}.framework".format(folder, args.framework_name)]
        execute(xcframework_build_command, cwd=os.getcwd())

        print("")
        print_header("Finished building {}".format(xcframework_path))
    except Exception as e:
        print_error(e)
        traceback.print_exc(file=sys.stderr)
        sys.exit(1)