1#!/usr/bin/env python
2"""
3The script builds OpenCV.framework for iOS.
4The built framework is universal, it can be used to build app and run it on either iOS simulator or real device.
5
6Usage:
7    ./build_framework.py <outputdir>
8
9By cmake conventions (and especially if you work with OpenCV repository),
10the output dir should not be a subdirectory of OpenCV source tree.
11
12Script 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
23The script should handle minor OpenCV updates efficiently
24- it does not recompile the library from scratch each time.
25However, opencv2.framework directory is erased and recreated on each run.
26"""
27
28import glob, re, os, os.path, shutil, string, sys
29
30def build_opencv(srcroot, buildroot, target, arch):
31    "builds OpenCV for device or simulator"
32
33    builddir = os.path.join(buildroot, target + '-' + arch)
34    if not os.path.isdir(builddir):
35        os.makedirs(builddir)
36    currdir = os.getcwd()
37    os.chdir(builddir)
38    # for some reason, if you do not specify CMAKE_BUILD_TYPE, it puts libs to "RELEASE" rather than "Release"
39    cmakeargs = ("-GXcode " +
40                "-DCMAKE_BUILD_TYPE=Release " +
41                "-DBUILD_SHARED_LIBS=OFF " +
42                "-DBUILD_DOCS=OFF " +
43                "-DBUILD_EXAMPLES=OFF " +
44                "-DBUILD_TESTS=OFF " +
45                "-DBUILD_PERF_TESTS=OFF " +
46                "-DBUILD_opencv_apps=OFF " +
47                "-DBUILD_opencv_world=ON " +
48                "-DBUILD_opencv_matlab=OFF " +
49                "-DWITH_TIFF=OFF -DBUILD_TIFF=OFF " +
50                "-DWITH_JASPER=OFF -DBUILD_JASPER=OFF " +
51                "-DWITH_WEBP=OFF -DBUILD_WEBP=OFF " +
52                "-DWITH_OPENEXR=OFF -DBUILD_OPENEXR=OFF " +
53                "-DWITH_IPP=OFF -DWITH_IPP_A=OFF " +
54                "-DCMAKE_C_FLAGS=\"-Wno-implicit-function-declaration\" " +
55                "-DCMAKE_INSTALL_PREFIX=install")
56    # if cmake cache exists, just rerun cmake to update OpenCV.xproj if necessary
57    if os.path.isfile(os.path.join(builddir, "CMakeCache.txt")):
58        os.system("cmake %s ." % (cmakeargs,))
59    else:
60        os.system("cmake %s %s" % (cmakeargs, srcroot))
61
62    for wlib in [builddir + "/modules/world/UninstalledProducts/libopencv_world.a",
63                 builddir + "/lib/Release/libopencv_world.a"]:
64        if os.path.isfile(wlib):
65            os.remove(wlib)
66
67    os.system("xcodebuild -parallelizeTargets ARCHS=%s -jobs 2 -sdk %s -configuration Release -target ALL_BUILD" % (arch, target.lower()))
68    os.system("xcodebuild ARCHS=%s -sdk %s -configuration Release -target install install" % (arch, target.lower()))
69    os.chdir(currdir)
70
71def put_framework_together(srcroot, dstroot):
72    "constructs the framework directory after all the targets are built"
73
74    # find the list of targets (basically, ["iPhoneOS", "iPhoneSimulator"])
75    targetlist = glob.glob(os.path.join(dstroot, "build", "*"))
76    targetlist = [os.path.basename(t) for t in targetlist]
77
78    # set the current dir to the dst root
79    currdir = os.getcwd()
80    framework_dir = dstroot + "/opencv2.framework"
81    if os.path.isdir(framework_dir):
82        shutil.rmtree(framework_dir)
83    os.makedirs(framework_dir)
84    os.chdir(framework_dir)
85
86    # form the directory tree
87    dstdir = "Versions/A"
88    os.makedirs(dstdir + "/Resources")
89
90    tdir0 = "../build/" + targetlist[0]
91    # copy headers
92    shutil.copytree(tdir0 + "/install/include/opencv2", dstdir + "/Headers")
93
94    # make universal static lib
95    wlist = " ".join(["../build/" + t + "/lib/Release/libopencv_world.a" for t in targetlist])
96    os.system("lipo -create " + wlist + " -o " + dstdir + "/opencv2")
97
98    # copy Info.plist
99    shutil.copyfile(tdir0 + "/osx/Info.plist", dstdir + "/Resources/Info.plist")
100
101    # make symbolic links
102    os.symlink("A", "Versions/Current")
103    os.symlink("Versions/Current/Headers", "Headers")
104    os.symlink("Versions/Current/Resources", "Resources")
105    os.symlink("Versions/Current/opencv2", "opencv2")
106
107
108def build_framework(srcroot, dstroot):
109    "main function to do all the work"
110
111    targets = ["MacOSX", "MacOSX" ]
112    archs   = ["x86_64", "i386"   ]
113    for i in range(len(targets)):
114        build_opencv(srcroot, os.path.join(dstroot, "build"), targets[i], archs[i])
115
116    put_framework_together(srcroot, dstroot)
117
118
119if __name__ == "__main__":
120    if len(sys.argv) != 2:
121        print "Usage:\n\t./build_framework.py <outputdir>\n\n"
122        sys.exit(0)
123
124    build_framework(os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "../..")), os.path.abspath(sys.argv[1]))
125