1 #!/usr/bin/env python 2 """ 3 The script builds OpenCV.framework for iOS. 4 The built framework is universal, it can be used to build app and run it on either iOS simulator or real device. 5 6 Usage: 7 ./build_framework.py <outputdir> 8 9 By cmake conventions (and especially if you work with OpenCV repository), 10 the output dir should not be a subdirectory of OpenCV source tree. 11 12 Script will create <outputdir>, if it's missing, and a few its subdirectories: 13 14 <outputdir> 15 build/ 16 iPhoneOS-*/ 17 [cmake-generated build tree for an iOS device target] 18 iPhoneSimulator-*/ 19 [cmake-generated build tree for iOS simulator] 20 opencv2.framework/ 21 [the framework content] 22 23 The script should handle minor OpenCV updates efficiently 24 - it does not recompile the library from scratch each time. 25 However, opencv2.framework directory is erased and recreated on each run. 26 """ 27 28 import glob, re, os, os.path, shutil, string, sys, exceptions, subprocess, argparse 29 30 opencv_contrib_path = None 31 32 def execute(cmd): 33 try: 34 print >>sys.stderr, "Executing:", cmd 35 retcode = subprocess.call(cmd, shell=True) 36 if retcode < 0: 37 raise Exception("Child was terminated by signal:", -retcode) 38 elif retcode > 0: 39 raise Exception("Child returned:", retcode) 40 except OSError as e: 41 raise Exception("Execution failed:", e) 42 43 def build_opencv(srcroot, buildroot, target, arch): 44 "builds OpenCV for device or simulator" 45 46 builddir = os.path.join(buildroot, target + '-' + arch) 47 if not os.path.isdir(builddir): 48 os.makedirs(builddir) 49 currdir = os.getcwd() 50 os.chdir(builddir) 51 # for some reason, if you do not specify CMAKE_BUILD_TYPE, it puts libs to "RELEASE" rather than "Release" 52 cmakeargs = ("-GXcode " + 53 "-DCMAKE_BUILD_TYPE=Release " + 54 "-DCMAKE_TOOLCHAIN_FILE=%s/platforms/ios/cmake/Toolchains/Toolchain-%s_Xcode.cmake " + 55 "-DCMAKE_C_FLAGS=\"-Wno-implicit-function-declaration\" " + 56 "-DCMAKE_INSTALL_PREFIX=install") % (srcroot, target) 57 58 if arch.startswith("armv"): 59 cmakeargs += " -DENABLE_NEON=ON" 60 61 if opencv_contrib_path is not None: 62 cmakeargs += " -DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=ON -DOPENCV_EXTRA_MODULES_PATH=%s -DBUILD_opencv_contrib_world=ON" % opencv_contrib_path 63 build_target = "opencv_contrib_world" 64 libname = "libopencv_contrib_world.a" 65 else: 66 cmakeargs += " -DBUILD_opencv_world=ON" 67 build_target = "ALL_BUILD" 68 libname = "libopencv_world.a" 69 70 # if cmake cache exists, just rerun cmake to update OpenCV.xcodeproj if necessary 71 if os.path.isfile(os.path.join(builddir, "CMakeCache.txt")): 72 execute("cmake %s ." % (cmakeargs,)) 73 else: 74 execute("cmake %s %s" % (cmakeargs, srcroot)) 75 76 for wlib in [builddir + "/modules/world/UninstalledProducts/" + libname, 77 builddir + "/lib/Release/" + libname]: 78 if os.path.isfile(wlib): 79 os.remove(wlib) 80 81 execute("xcodebuild IPHONEOS_DEPLOYMENT_TARGET=6.0 -parallelizeTargets ARCHS=%s -jobs 8 -sdk %s -configuration Release -target %s" % (arch, target.lower(), build_target)) 82 execute("xcodebuild IPHONEOS_DEPLOYMENT_TARGET=6.0 ARCHS=%s -sdk %s -configuration Release -target install install" % (arch, target.lower())) 83 os.chdir(currdir) 84 85 def put_framework_together(srcroot, dstroot): 86 "constructs the framework directory after all the targets are built" 87 88 name = "opencv2" if opencv_contrib_path is None else "opencv2_contrib" 89 libname = "libopencv_world.a" if opencv_contrib_path is None else "libopencv_contrib_world.a" 90 91 # find the list of targets (basically, ["iPhoneOS", "iPhoneSimulator"]) 92 targetlist = glob.glob(os.path.join(dstroot, "build", "*")) 93 targetlist = [os.path.basename(t) for t in targetlist] 94 95 # set the current dir to the dst root 96 currdir = os.getcwd() 97 framework_dir = dstroot + "/%s.framework" % name 98 if os.path.isdir(framework_dir): 99 shutil.rmtree(framework_dir) 100 os.makedirs(framework_dir) 101 os.chdir(framework_dir) 102 103 # form the directory tree 104 dstdir = "Versions/A" 105 os.makedirs(dstdir + "/Resources") 106 107 tdir0 = "../build/" + targetlist[0] 108 # copy headers 109 shutil.copytree(tdir0 + "/install/include/opencv2", dstdir + "/Headers") 110 111 # make universal static lib 112 wlist = " ".join(["../build/" + t + "/lib/Release/" + libname for t in targetlist]) 113 execute("lipo -create " + wlist + " -o " + dstdir + "/%s" % name) 114 115 # copy Info.plist 116 shutil.copyfile(tdir0 + "/ios/Info.plist", dstdir + "/Resources/Info.plist") 117 118 # make symbolic links 119 os.symlink("A", "Versions/Current") 120 os.symlink("Versions/Current/Headers", "Headers") 121 os.symlink("Versions/Current/Resources", "Resources") 122 os.symlink("Versions/Current/%s" % name, name) 123 124 125 def build_framework(srcroot, dstroot): 126 "main function to do all the work" 127 128 targets = [("armv7", "iPhoneOS"), 129 ("armv7s", "iPhoneOS"), 130 ("arm64", "iPhoneOS"), 131 ("i386", "iPhoneSimulator"), 132 ("x86_64", "iPhoneSimulator")] 133 for t in targets: 134 build_opencv(srcroot, os.path.join(dstroot, "build"), t[1], t[0]) 135 136 put_framework_together(srcroot, dstroot) 137 138 139 if __name__ == "__main__": 140 parser = argparse.ArgumentParser(description='The script builds OpenCV.framework for iOS.') 141 parser.add_argument('outputdir', nargs=1, help='folder to put built framework') 142 parser.add_argument('--contrib', help="folder with opencv_contrib repository") 143 args = parser.parse_args() 144 145 # path to OpenCV main repository - hardcoded ../.. 146 opencv_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "../..")) 147 print "OpenCV:", opencv_path 148 149 # path to OpenCV_contrib repository, can be empty - global variable 150 if hasattr(args, "contrib") and args.contrib is not None: 151 if os.path.isdir(args.contrib + "/modules"): 152 opencv_contrib_path = os.path.abspath(args.contrib + "/modules") 153 print "Contrib:", opencv_contrib_path 154 else: 155 print "Note: contrib repository is bad: modules subfolder not found" 156 157 # result path - folder where framework will be located 158 output_path = os.path.abspath(args.outputdir[0]) 159 print "Output:", output_path 160 161 try: 162 build_framework(opencv_path, output_path) 163 except Exception as e: 164 print >>sys.stderr, e 165 sys.exit(1) 166