CatHand Blog

アプリ開発やMac弄り

OpenCVを含んだiOSプロジェクトをApple Silicon macでビルドできるようにする

MacBook Air (M1) の開発環境を構築してます。

www.cathand.app

OpenCVを含んだiOSシミュレータ向けビルドが通らない問題ですが、どうもlipoコマンドでは同じアーキテクチャのデバイス向けとシミュレータ向けのバイナリを混在できないようで、それらを混在させるためにはxcframeworkを使う必要があるようです。

現状ではOpenCVのxcframeworkは無いみたいなので、ソースからビルドします。

OpenCV公式からソースをダウンロードして展開します。今回は3.4.6を使いたいので3.4.6で説明します。

https://opencv.org/releases

platforms/ios/build_framework.py の300行目あたりにビルド設定している部分があります。

if __name__ == "__main__":
    folder = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "../.."))
    parser = argparse.ArgumentParser(description='The script builds OpenCV.framework for iOS.')
    parser.add_argument('out', metavar='OUTDIR', help='folder to put built framework')
    parser.add_argument('--opencv', metavar='DIR', default=folder, help='folder with opencv repository (default is "../.." relative to script location)')
    parser.add_argument('--contrib', metavar='DIR', default=None, help='folder with opencv_contrib repository (default is "None" - build only main framework)')
    parser.add_argument('--without', metavar='MODULE', default=[], action='append', help='OpenCV modules to exclude from the framework')
    parser.add_argument('--dynamic', default=False, action='store_true', help='build dynamic framework (default is "False" - builds static framework)')
    parser.add_argument('--disable-bitcode', default=False, dest='bitcodedisabled', action='store_true', help='disable bitcode (enabled by default)')
    parser.add_argument('--iphoneos_deployment_target', default=os.environ.get('IPHONEOS_DEPLOYMENT_TARGET', IPHONEOS_DEPLOYMENT_TARGET), help='specify IPHONEOS_DEPLOYMENT_TARGET')
    parser.add_argument('--iphoneos_archs', default='armv7,armv7s,arm64', help='select iPhoneOS target ARCHS')
    parser.add_argument('--iphonesimulator_archs', default='i386,x86_64', help='select iPhoneSimulator target ARCHS')
    parser.add_argument('--enable_nonfree', default=False, dest='enablenonfree', action='store_true', help='enable non-free modules (disabled by default)')
    args = parser.parse_args()

    os.environ['IPHONEOS_DEPLOYMENT_TARGET'] = args.iphoneos_deployment_target
    print('Using IPHONEOS_DEPLOYMENT_TARGET=' + os.environ['IPHONEOS_DEPLOYMENT_TARGET'])
    iphoneos_archs = args.iphoneos_archs.split(',')
    print('Using iPhoneOS ARCHS=' + str(iphoneos_archs))
    iphonesimulator_archs = args.iphonesimulator_archs.split(',')
    print('Using iPhoneSimulator ARCHS=' + str(iphonesimulator_archs))

    b = iOSBuilder(args.opencv, args.contrib, args.dynamic, args.bitcodedisabled, args.without, args.enablenonfree,
        [
            (iphoneos_archs, "iPhoneOS"),
        ] if os.environ.get('BUILD_PRECOMMIT', None) else
        [
            (iphoneos_archs, "iPhoneOS"),
            (iphonesimulator_archs, "iPhoneSimulator"),
        ])
    b.build(args.out)

今回は、

  • 実機向けframeworkを作成
  • シミュレータ向けframeworkを作成
  • 実機向けframeworkとシミュレータ向けframeworkを合わせてxcframeworkを作成

という手順でやります。

まず、実機向けビルドですが、

    b = iOSBuilder(args.opencv, args.contrib, args.dynamic, args.bitcodedisabled, args.without, args.enablenonfree,
        [
            (iphoneos_archs, "iPhoneOS"),
        ] if os.environ.get('BUILD_PRECOMMIT', None) else
        [
            (iphoneos_archs, "iPhoneOS"),
            (iphonesimulator_archs, "iPhoneSimulator"),
        ])

ここを

    b = iOSBuilder(args.opencv, args.contrib, args.dynamic, args.bitcodedisabled, args.without, args.enablenonfree,
        [
            (iphoneos_archs, "iPhoneOS"),
        ])

と書き換えて以下のコマンドを実行します。

python platforms/ios/build_framework.py iphoneos

次にシミュレータビルドですが、先ほどのところを

    b = iOSBuilder(args.opencv, args.contrib, args.dynamic, args.bitcodedisabled, args.without, args.enablenonfree,
        [
            (iphonesimulator_archs, "iPhoneSimulator"),
        ])

として、さらに

    parser.add_argument('--iphonesimulator_archs', default='i386,x86_64', help='select iPhoneSimulator target ARCHS')

の部分に arm64 を追加します。

    parser.add_argument('--iphonesimulator_archs', default='i386,x86_64,arm64', help='select iPhoneSimulator target ARCHS')

そして

python platforms/ios/build_framework.py iphonesimulator

を実行します。

これで、 iphoneos/opencv2.frameworkiphonesimulator/opencv2.framework ができたので、あとはこれを合体させます。

xcodebuild -create-xcframework -output opencv2.xcframework -framework iphoneos/opencv2.framework -framework iphonesimulator/opencv2.framework

これでできた opencv2.xcframework を使うと、シミュレータでビルド・実行できるようになります。