Build OpenCV Contrib as a .xcframework, for iOS, on a M1

We needed an OpenCV Contrib module in an iOS app, using Swift Package Manager. I thought the hardest part would be using OpenCV. How wrong I was.

Build OpenCV Contrib as a .xcframework, for iOS, on a M1
Photo by Andrea De Santis / Unsplash

Context

We needed an OpenCV Contrib module in an iOS app. When reviewing options, it was decided we would expose a Swift package, wrapping an Objective-C wrapper, that would itself be the one importing OpenCV.

This meant exposing OpenCV as a Swift Package Manager binary target. Which requires us to build a .xcframework.

Goal

Build a .xcframework that we can run on both OS devices and Intel & Apple M1 Mac iPhone / iPad Simulators. Why a .xcframework and not simply a .framework? Because while Swift Package Manager can import binaries, it can only import .xcframework ones, not .framework ones.

Requirements

  • cmake (installable using brew) // TODO: add link
  • python3  (because python2.7 is obsolete)  (installable using brew)
  • opencv source  (via git clone https://github.com/opencv/opencv.git)
  • opencv_contrib source (via git clone https://github.com/opencv/opencv_contrib.git)

Building

Build OpenCV using the proper flags (outfile, contrib, iphoneos arch, iphonesimulator archs):

python3 opencv/platforms/apple/build_xcframework.py --out ./opencv-build --contrib opencv_contrib --iphoneos_archs armv7,armv7s,arm64 --iphonesimulator_archs arm64 --build_only_specified_archs
python3 opencv/platforms/apple/build_xcframework.py --out ./opencv-build --contrib opencv_contrib --iphoneos_archs armv7,armv7s,arm64 --iphonesimulator_archs x86_64,arm64 --dynamic --build_only_specified_archs

Where the --contrib parameter's value is the local path to the opencv_contrib source.

💡
I tried building it for both `x86_64` and `arm64` simulators, but it simply wouldn't build, probably because my M1 Mac has an `arm64` CPU architecture. An Intel Mac (or an M1 one running Rosetta) may not be able to build for the `arm64` simulator. I will look into that in the future.

Go make yourself a nice and warm cup of tea 🫖 (because I currently am writing this in the UK 🇬🇧 )

With a dash of whisky in it 🥃, because I'm more specifically in Scotland 🏴󠁧󠁢󠁳󠁣󠁴󠁿 at the moment

Wait

... Wait

... Wait some more

Your tea should be ready now, more or less

Wait

... ... Wait

Wait

Wait some more

Wait

You can now carefully sip your tea

cd /Users/path/opencv-build/iphonesimulator/build/build-arm64-iphonesimulator/modules/objc_bindings_generator/ios/gen
    export LANG\=en_US.US-ASCII
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -x objective-c++ -target arm64-apple-ios9.0-simulator -fmessage-length\=122 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit\=0 -fcolor-diagnostics -Wno-trigraphs -fpascal-strings -O1 -Wno-missing-field-initializers -Wno-missing-prototypes -Wno-return-type -Wno-implicit-atomic-properties -Wno-objc-interface-ivars -Wno-arc-repeated-use-of-weak -Wno-non-virtual-dtor -Wno-overloaded-virtual -Wno-exit-time-destructors -Wno-missing-braces -Wparentheses -Wswitch -Wno-unused-function -Wno-unused-label -Wno-unused-parameter -Wno-unused-variable -Wunused-value -Wno-emp

Sip some tea.

Wait.

Did you know there are over 3000 varieties of tea out there? I certainly didn't. Until I looked it up to give you some random, vaguely appropriate, fun fact.

A girl gathering leaves
Photo by 蔡 嘉宇 / Unsplash

Go buy yourself some nice biscuits to go with your tea

-o /Users/path/opencv-build/iphonesimulator/build/build-arm64-iphonesimulator/modules/objc/framework_build/opencv2.build/Release-iphonesimulator/opencv2.build/Objects-normal/arm64/AffineFeature.o

CompileC /Users/path/opencv-build/iphonesimulator/build/build-arm64-iphonesimulator/modules/objc/framework_build/opencv2.build/Release-iphonesimulator/opencv2.build/Objects-normal/arm64/AdaptiveManifoldFilter.o /Users/path/opencv-build/build-arm64-iphonesimulator/modules/objc_bindings_generator/ios/gen/objc/ximgproc/AdaptiveManifoldFilter.mm normal arm64 objective-c++ com.apple.compilers.llvm.clang.1_0.compiler (in target 'opencv2' from project 'opencv2')
    cd /Users/path/opencv-build/iphonesimulator/build/build-arm64-iphonesimulator/modules/objc_bindings_generator/ios/gen
    export LANG\=en_US.US-ASCII
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -x objective-c++ -target arm64-apple-ios9.0-simulator

Sip your tea some more, now with biscuits

Wait

... Wait

Put the cup of tea down...

Executing: ['cmake', '-DBUILD_TYPE=Release', '-DCMAKE_INSTALL_PREFIX=/Users/username/path/opencv-build/iphonesimulator/build/build-arm64-iphonesimulator/install', '-P', 'cmake_install.cmake'] in /Users/path/opencv-build/iphonesimulator/build/build-arm64-iphonesimulator/modules/objc/framework_build
Executing: cmake -DBUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/Users/path/opencv-build/iphonesimulator/build/build-arm64-iphonesimulator/install -P cmake_install.cmake
-- Install configuration: "Release"

... lay back in your chair...

... and do a 360!

Turn around some more!

via GIPHY

Oh. That got creepy.

Oh, it's done building! Congrats!

============================================================
Finished building ./opencv-build/opencv2.xcframework
============================================================

That wasn't so hard now, was it? Now you can use OpenCV contrib modules in your app! Isn't that nice! Next, how to import it into our projects as a binary target using Swift Package Manager!

Resources: