Home | History | Annotate | Download | only in ios
      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