diff --git a/cmake/OpenCVGenInfoPlist.cmake b/cmake/OpenCVGenInfoPlist.cmake index 2b78ae1e53..6dbdc5b9e7 100644 --- a/cmake/OpenCVGenInfoPlist.cmake +++ b/cmake/OpenCVGenInfoPlist.cmake @@ -2,8 +2,13 @@ set(OPENCV_APPLE_BUNDLE_NAME "OpenCV") set(OPENCV_APPLE_BUNDLE_ID "org.opencv") if(IOS) - configure_file("${OpenCV_SOURCE_DIR}/platforms/ios/Info.plist.in" - "${CMAKE_BINARY_DIR}/ios/Info.plist") + if (APPLE_FRAMEWORK AND BUILD_SHARED_LIBS) + configure_file("${OpenCV_SOURCE_DIR}/platforms/ios/Info.Dynamic.plist.in" + "${CMAKE_BINARY_DIR}/ios/Info.plist") + else() + configure_file("${OpenCV_SOURCE_DIR}/platforms/ios/Info.plist.in" + "${CMAKE_BINARY_DIR}/ios/Info.plist") + endif() elseif(APPLE) configure_file("${OpenCV_SOURCE_DIR}/platforms/osx/Info.plist.in" "${CMAKE_BINARY_DIR}/osx/Info.plist") diff --git a/cmake/OpenCVUtils.cmake b/cmake/OpenCVUtils.cmake index cdf257d5fe..7a0e448f77 100644 --- a/cmake/OpenCVUtils.cmake +++ b/cmake/OpenCVUtils.cmake @@ -668,7 +668,11 @@ endfunction() # add install command function(ocv_install_target) - install(TARGETS ${ARGN}) + if(APPLE_FRAMEWORK AND BUILD_SHARED_LIBS) + install(TARGETS ${ARGN} FRAMEWORK DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH}) + else() + install(TARGETS ${ARGN}) + endif() set(isPackage 0) unset(__package) @@ -931,6 +935,29 @@ function(ocv_add_library target) unset(sources) endif() + if(APPLE_FRAMEWORK AND BUILD_SHARED_LIBS) + message(STATUS "Setting Apple target properties for ${target}") + + set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG 1) + + set_target_properties(${target} PROPERTIES + FRAMEWORK TRUE + MACOSX_FRAMEWORK_IDENTIFIER org.opencv + MACOSX_FRAMEWORK_INFO_PLIST ${CMAKE_BINARY_DIR}/ios/Info.plist + # "current version" in semantic format in Mach-O binary file + VERSION ${OPENCV_LIBVERSION} + # "compatibility version" in semantic format in Mach-O binary file + SOVERSION ${OPENCV_LIBVERSION} + INSTALL_RPATH "" + INSTALL_NAME_DIR "@rpath" + BUILD_WITH_INSTALL_RPATH 1 + LIBRARY_OUTPUT_NAME "opencv2" + XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2" + #PUBLIC_HEADER "${OPENCV_CONFIG_FILE_INCLUDE_DIR}/cvconfig.h" + #XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer" + ) + endif() + _ocv_append_target_includes(${target}) endfunction() diff --git a/modules/videoio/CMakeLists.txt b/modules/videoio/CMakeLists.txt index fb64894899..8a5b9d7a36 100644 --- a/modules/videoio/CMakeLists.txt +++ b/modules/videoio/CMakeLists.txt @@ -208,7 +208,11 @@ if(IOS) ${CMAKE_CURRENT_LIST_DIR}/src/cap_ios_abstract_camera.mm ${CMAKE_CURRENT_LIST_DIR}/src/cap_ios_photo_camera.mm ${CMAKE_CURRENT_LIST_DIR}/src/cap_ios_video_camera.mm) + list(APPEND VIDEOIO_LIBRARIES "-framework Accelerate" "-framework AVFoundation" "-framework CoreGraphics" "-framework CoreImage" "-framework CoreMedia" "-framework CoreVideo" "-framework QuartzCore" "-framework AssetsLibrary") + if(APPLE_FRAMEWORK AND BUILD_SHARED_LIBS) + list(APPEND VIDEOIO_LIBRARIES "-framework UIKit") + endif() endif() if(WIN32) diff --git a/modules/world/CMakeLists.txt b/modules/world/CMakeLists.txt index db8928d69c..1a77245df2 100644 --- a/modules/world/CMakeLists.txt +++ b/modules/world/CMakeLists.txt @@ -2,7 +2,7 @@ set(the_description "All the selected OpenCV modules in a single binary") set(OPENCV_MODULE_IS_PART_OF_WORLD FALSE) set(BUILD_opencv_world_INIT OFF) -if(APPLE_FRAMEWORK OR NOT BUILD_SHARED_LIBS) +if(NOT BUILD_SHARED_LIBS) set(OPENCV_MODULE_TYPE STATIC) set(OPENCV_WORLD_FLAGS_PROPERTY STATIC_LIBRARY_FLAGS) else() diff --git a/platforms/ios/Info.Dynamic.plist.in b/platforms/ios/Info.Dynamic.plist.in new file mode 100644 index 0000000000..4ff68cca04 --- /dev/null +++ b/platforms/ios/Info.Dynamic.plist.in @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + opencv2 + CFBundleName + ${OPENCV_APPLE_BUNDLE_NAME} + CFBundleIdentifier + ${OPENCV_APPLE_BUNDLE_ID} + CFBundleVersion + ${OPENCV_LIBVERSION} + CFBundleShortVersionString + ${OPENCV_LIBVERSION} + CFBundleSignature + ???? + CFBundlePackageType + FMWK + CFBundleSupportedPlatforms + + iPhoneOS + + MinimumOSVersion + 8.0 + + \ No newline at end of file diff --git a/platforms/ios/build_framework.py b/platforms/ios/build_framework.py index f8886b6f93..2770a5a102 100644 --- a/platforms/ios/build_framework.py +++ b/platforms/ios/build_framework.py @@ -23,6 +23,8 @@ Script will create , if it's missing, and a few its subdirectories: The script should handle minor OpenCV updates efficiently - it does not recompile the library from scratch each time. However, opencv2.framework directory is erased and recreated on each run. + +Adding --dynamic parameter will build opencv2.framework as App Store dynamic framework. Only iOS 8+ versions are supported. """ from __future__ import print_function @@ -43,7 +45,7 @@ def getXCodeMajor(): return 0 class Builder: - def __init__(self, opencv, contrib, exclude, targets): + def __init__(self, opencv, contrib, dynamic, bitcodedisabled, exclude, targets): self.opencv = os.path.abspath(opencv) self.contrib = None if contrib: @@ -52,11 +54,18 @@ class Builder: self.contrib = os.path.abspath(modpath) else: print("Note: contrib repository is bad - modules subfolder not found", file=sys.stderr) + self.dynamic = dynamic + self.bitcodedisabled = bitcodedisabled self.exclude = exclude self.targets = targets def getBD(self, parent, t): - res = os.path.join(parent, '%s-%s' % t) + + if len(t[0]) == 1: + res = os.path.join(parent, 'build-%s-%s' % (t[0][0].lower(), t[1].lower())) + else: + res = os.path.join(parent, 'build-%s' % t[1].lower()) + if not os.path.isdir(res): os.makedirs(res) return os.path.abspath(res) @@ -70,17 +79,32 @@ class Builder: xcode_ver = getXCodeMajor() - for t in self.targets: + if self.dynamic: + alltargets = self.targets + else: + # if we are building a static library, we must build each architecture separately + alltargets = [] + + for t in self.targets: + for at in t[0]: + current = ( [at], t[1] ) + + alltargets.append(current) + + for t in alltargets: mainBD = self.getBD(mainWD, t) dirs.append(mainBD) + cmake_flags = [] if self.contrib: cmake_flags.append("-DOPENCV_EXTRA_MODULES_PATH=%s" % self.contrib) - if xcode_ver >= 7 and t[1] == 'iPhoneOS': + if xcode_ver >= 7 and t[1] == 'iPhoneOS' and self.bitcodedisabled == False: cmake_flags.append("-DCMAKE_C_FLAGS=-fembed-bitcode") cmake_flags.append("-DCMAKE_CXX_FLAGS=-fembed-bitcode") self.buildOne(t[0], t[1], mainBD, cmake_flags) - self.mergeLibs(mainBD) + + if self.dynamic == False: + self.mergeLibs(mainBD) self.makeFramework(outdir, dirs) def build(self, outdir): @@ -97,13 +121,26 @@ class Builder: return None def getCMakeArgs(self, arch, target): - args = [ - "cmake", - "-GXcode", - "-DAPPLE_FRAMEWORK=ON", - "-DCMAKE_INSTALL_PREFIX=install", - "-DCMAKE_BUILD_TYPE=Release", - ] + + if self.dynamic: + args = [ + "cmake", + "-GXcode", + "-DAPPLE_FRAMEWORK=ON", + "-DCMAKE_INSTALL_PREFIX=install", + "-DCMAKE_BUILD_TYPE=Release", + "-DBUILD_SHARED_LIBS=ON", + "-DCMAKE_MACOSX_BUNDLE=ON", + "-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=NO", + ] + else: + args = [ + "cmake", + "-GXcode", + "-DAPPLE_FRAMEWORK=ON", + "-DCMAKE_INSTALL_PREFIX=install", + "-DCMAKE_BUILD_TYPE=Release", + ] if len(self.exclude) > 0: args += ["-DBUILD_opencv_world=OFF"] @@ -111,16 +148,38 @@ class Builder: return args - def getBuildCommand(self, arch, target): - buildcmd = [ - "xcodebuild", - "IPHONEOS_DEPLOYMENT_TARGET=6.0", - "ARCHS=%s" % arch, - "-sdk", target.lower(), - "-configuration", "Release", - "-parallelizeTargets", - "-jobs", "4" - ] + def getBuildCommand(self, archs, target): + + if self.dynamic: + buildcmd = [ + "xcodebuild", + "IPHONEOS_DEPLOYMENT_TARGET=8.0", + "ONLY_ACTIVE_ARCH=NO", + ] + + for arch in archs: + buildcmd.append("-arch") + buildcmd.append(arch.lower()) + + buildcmd += [ + "-sdk", target.lower(), + "-configuration", "Release", + "-parallelizeTargets", + "-jobs", "4", + "-target","ALL_BUILD", + ] + else: + arch = ";".join(archs) + buildcmd = [ + "xcodebuild", + "IPHONEOS_DEPLOYMENT_TARGET=6.0", + "ARCHS=%s" % arch, + "-sdk", target.lower(), + "-configuration", "Release", + "-parallelizeTargets", + "-jobs", "4" + ] + return buildcmd def getInfoPlist(self, builddirs): @@ -131,11 +190,12 @@ class Builder: toolchain = self.getToolchain(arch, target) cmakecmd = self.getCMakeArgs(arch, target) + \ (["-DCMAKE_TOOLCHAIN_FILE=%s" % toolchain] if toolchain is not None else []) - if arch.startswith("armv") or arch.startswith("arm64"): + if target.lower().startswith("iphoneos"): cmakecmd.append("-DENABLE_NEON=ON") cmakecmd.append(self.opencv) cmakecmd.extend(cmakeargs) execute(cmakecmd, cwd = builddir) + # Clean and build clean_dir = os.path.join(builddir, "install") if os.path.isdir(clean_dir): @@ -153,7 +213,6 @@ class Builder: def makeFramework(self, outdir, builddirs): name = "opencv2" - libname = "libopencv_merged.a" # set the current dir to the dst root framework_dir = os.path.join(outdir, "%s.framework" % name) @@ -161,7 +220,12 @@ class Builder: shutil.rmtree(framework_dir) os.makedirs(framework_dir) - dstdir = os.path.join(framework_dir, "Versions", "A") + if self.dynamic: + dstdir = framework_dir + libname = "opencv2.framework/opencv2" + else: + dstdir = os.path.join(framework_dir, "Versions", "A") + libname = "libopencv_merged.a" # copy headers from one of build folders shutil.copytree(os.path.join(builddirs[0], "install", "include", "opencv2"), os.path.join(dstdir, "Headers")) @@ -174,22 +238,27 @@ class Builder: print("Creating universal library from:\n\t%s" % "\n\t".join(libs), file=sys.stderr) execute(lipocmd) - # copy Info.plist - resdir = os.path.join(dstdir, "Resources") - os.makedirs(resdir) - shutil.copyfile(self.getInfoPlist(builddirs), os.path.join(resdir, "Info.plist")) + # dynamic framework has different structure, just copy the Plist directly + if self.dynamic: + resdir = dstdir + shutil.copyfile(self.getInfoPlist(builddirs), os.path.join(resdir, "Info.plist")) + else: + # copy Info.plist + resdir = os.path.join(dstdir, "Resources") + os.makedirs(resdir) + shutil.copyfile(self.getInfoPlist(builddirs), os.path.join(resdir, "Info.plist")) - # make symbolic links - links = [ - (["A"], ["Versions", "Current"]), - (["Versions", "Current", "Headers"], ["Headers"]), - (["Versions", "Current", "Resources"], ["Resources"]), - (["Versions", "Current", name], [name]) - ] - for l in links: - s = os.path.join(*l[0]) - d = os.path.join(framework_dir, *l[1]) - os.symlink(s, d) + # make symbolic links + links = [ + (["A"], ["Versions", "Current"]), + (["Versions", "Current", "Headers"], ["Headers"]), + (["Versions", "Current", "Resources"], ["Resources"]), + (["Versions", "Current", name], [name]) + ] + for l in links: + s = os.path.join(*l[0]) + d = os.path.join(framework_dir, *l[1]) + os.symlink(s, d) class iOSBuilder(Builder): @@ -198,6 +267,8 @@ class iOSBuilder(Builder): return toolchain def getCMakeArgs(self, arch, target): + arch = ";".join(arch) + args = Builder.getCMakeArgs(self, arch, target) args = args + [ '-DIOS_ARCH=%s' % arch @@ -212,18 +283,16 @@ if __name__ == "__main__": 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)') args = parser.parse_args() - b = iOSBuilder(args.opencv, args.contrib, args.without, + b = iOSBuilder(args.opencv, args.contrib, args.dynamic, args.bitcodedisabled, args.without, [ - ("armv7", "iPhoneOS"), - ("arm64", "iPhoneOS"), + (["armv7", "arm64"], "iPhoneOS"), ] if os.environ.get('BUILD_PRECOMMIT', None) else [ - ("armv7", "iPhoneOS"), - ("armv7s", "iPhoneOS"), - ("arm64", "iPhoneOS"), - ("i386", "iPhoneSimulator"), - ("x86_64", "iPhoneSimulator"), + (["armv7", "armv7s", "arm64"], "iPhoneOS"), + (["i386", "x86_64"], "iPhoneSimulator"), ]) b.build(args.out) diff --git a/platforms/ios/cmake/Modules/Platform/iOS.cmake b/platforms/ios/cmake/Modules/Platform/iOS.cmake index 63cf1d89e7..6915adfbc7 100644 --- a/platforms/ios/cmake/Modules/Platform/iOS.cmake +++ b/platforms/ios/cmake/Modules/Platform/iOS.cmake @@ -38,6 +38,14 @@ set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") +# Additional flags for dynamic framework +if (APPLE_FRAMEWORK AND BUILD_SHARED_LIBS) + set (CMAKE_MODULE_LINKER_FLAGS "-rpath @executable_path/Frameworks -rpath @loader_path/Frameworks") + set (CMAKE_SHARED_LINKER_FLAGS "-rpath @executable_path/Frameworks -rpath @loader_path/Frameworks") + set (CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG 1) + set (CMAKE_INSTALL_NAME_DIR "@rpath") +endif() + # Hidden visibilty is required for cxx on iOS set (no_warn "-Wno-unused-function -Wno-overloaded-virtual") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${no_warn}") @@ -144,7 +152,6 @@ set (CMAKE_C_CREATE_MACOSX_FRAMEWORK set (CMAKE_CXX_CREATE_MACOSX_FRAMEWORK " -o -install_name ") - # Add the install directory of the running cmake to the search directories # CMAKE_ROOT is CMAKE_INSTALL_PREFIX/share/cmake, so we need to go two levels up get_filename_component (_CMAKE_INSTALL_DIR "${CMAKE_ROOT}" PATH) diff --git a/platforms/ios/cmake/Toolchains/common-ios-toolchain.cmake b/platforms/ios/cmake/Toolchains/common-ios-toolchain.cmake index 24dab91ff6..44ad57ce74 100644 --- a/platforms/ios/cmake/Toolchains/common-ios-toolchain.cmake +++ b/platforms/ios/cmake/Toolchains/common-ios-toolchain.cmake @@ -86,24 +86,52 @@ endif() set(CMAKE_MACOSX_BUNDLE YES) set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") -set(CMAKE_OSX_ARCHITECTURES "${IOS_ARCH}" CACHE INTERNAL "Build architecture for iOS" FORCE) +if(APPLE_FRAMEWORK AND NOT BUILD_SHARED_LIBS) + set(CMAKE_OSX_ARCHITECTURES "${IOS_ARCH}" CACHE INTERNAL "Build architecture for iOS" FORCE) +endif() if(NOT __IN_TRY_COMPILE) set(_xcodebuild_wrapper "${CMAKE_BINARY_DIR}/xcodebuild_wrapper") if(NOT CMAKE_MAKE_PROGRAM STREQUAL _xcodebuild_wrapper) - set(_xcodebuild_wrapper_tmp "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/xcodebuild_wrapper") - file(WRITE "${_xcodebuild_wrapper_tmp}" "#!/bin/sh + if(APPLE_FRAMEWORK AND BUILD_SHARED_LIBS) + set(_xcodebuild_wrapper_tmp "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/xcodebuild_wrapper") + file(WRITE "${_xcodebuild_wrapper_tmp}" "#!/bin/sh +${CMAKE_MAKE_PROGRAM} IPHONEOS_DEPLOYMENT_TARGET=8.0 -sdk ${CMAKE_OSX_SYSROOT} \$*") + # Make executable + file(COPY "${_xcodebuild_wrapper_tmp}" DESTINATION ${CMAKE_BINARY_DIR} FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + set(CMAKE_MAKE_PROGRAM "${_xcodebuild_wrapper}" CACHE INTERNAL "" FORCE) + else() + set(_xcodebuild_wrapper_tmp "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/xcodebuild_wrapper") + file(WRITE "${_xcodebuild_wrapper_tmp}" "#!/bin/sh ${CMAKE_MAKE_PROGRAM} IPHONEOS_DEPLOYMENT_TARGET=6.0 ARCHS=${IOS_ARCH} -sdk ${CMAKE_OSX_SYSROOT} \$*") - # Make executable - file(COPY "${_xcodebuild_wrapper_tmp}" DESTINATION ${CMAKE_BINARY_DIR} FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - set(CMAKE_MAKE_PROGRAM "${_xcodebuild_wrapper}" CACHE INTERNAL "" FORCE) + # Make executable + file(COPY "${_xcodebuild_wrapper_tmp}" DESTINATION ${CMAKE_BINARY_DIR} FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + set(CMAKE_MAKE_PROGRAM "${_xcodebuild_wrapper}" CACHE INTERNAL "" FORCE) + endif() endif() endif() # Standard settings set(CMAKE_SYSTEM_NAME iOS) -set(CMAKE_SYSTEM_VERSION 6.0) -set(CMAKE_SYSTEM_PROCESSOR "${IOS_ARCH}") + +# Apple Framework settings +if(APPLE_FRAMEWORK AND BUILD_SHARED_LIBS) + set(CMAKE_SYSTEM_VERSION 8.0) + set(CMAKE_C_SIZEOF_DATA_PTR 4) + set(CMAKE_CXX_SIZEOF_DATA_PTR 4) +else() + set(CMAKE_SYSTEM_VERSION 6.0) + set(CMAKE_SYSTEM_PROCESSOR "${IOS_ARCH}") + + if(AARCH64 OR X86_64) + set(CMAKE_C_SIZEOF_DATA_PTR 8) + set(CMAKE_CXX_SIZEOF_DATA_PTR 8) + else() + set(CMAKE_C_SIZEOF_DATA_PTR 4) + set(CMAKE_CXX_SIZEOF_DATA_PTR 4) + endif() +endif() + # Include extra modules for the iOS platform files set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/cmake/Modules") @@ -112,13 +140,6 @@ include(CMakeForceCompiler) #CMAKE_FORCE_C_COMPILER (clang GNU) #CMAKE_FORCE_CXX_COMPILER (clang++ GNU) -if(AARCH64 OR X86_64) - set(CMAKE_C_SIZEOF_DATA_PTR 8) - set(CMAKE_CXX_SIZEOF_DATA_PTR 8) -else() - set(CMAKE_C_SIZEOF_DATA_PTR 4) - set(CMAKE_CXX_SIZEOF_DATA_PTR 4) -endif() set(CMAKE_C_HAS_ISYSROOT 1) set(CMAKE_CXX_HAS_ISYSROOT 1) set(CMAKE_C_COMPILER_ABI ELF) @@ -134,4 +155,4 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -toolchain_save_config(IOS_ARCH) +toolchain_save_config(IOS_ARCH) \ No newline at end of file