1#!/usr/bin/env python 2 3import os, sys, subprocess, argparse, shutil, glob, re, multiprocessing 4import logging as log 5 6SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 7 8class Fail(Exception): 9 def __init__(self, text=None): 10 self.t = text 11 def __str__(self): 12 return "ERROR" if self.t is None else self.t 13 14def execute(cmd, shell=False): 15 try: 16 log.info("Executing: %s" % cmd) 17 env = os.environ.copy() 18 env['VERBOSE'] = '1' 19 retcode = subprocess.call(cmd, shell=shell, env=env) 20 if retcode < 0: 21 raise Fail("Child was terminated by signal: %s" % -retcode) 22 elif retcode > 0: 23 raise Fail("Child returned: %s" % retcode) 24 except OSError as e: 25 raise Fail("Execution failed: %d / %s" % (e.errno, e.strerror)) 26 27def rm_one(d): 28 d = os.path.abspath(d) 29 if os.path.exists(d): 30 if os.path.isdir(d): 31 log.info("Removing dir: %s", d) 32 shutil.rmtree(d) 33 elif os.path.isfile(d): 34 log.info("Removing file: %s", d) 35 os.remove(d) 36 37def check_dir(d, create=False, clean=False): 38 d = os.path.abspath(d) 39 log.info("Check dir %s (create: %s, clean: %s)", d, create, clean) 40 if os.path.exists(d): 41 if not os.path.isdir(d): 42 raise Fail("Not a directory: %s" % d) 43 if clean: 44 for x in glob.glob(os.path.join(d, "*")): 45 rm_one(x) 46 else: 47 if create: 48 os.makedirs(d) 49 return d 50 51def check_file(d): 52 d = os.path.abspath(d) 53 if os.path.exists(d): 54 if os.path.isfile(d): 55 return True 56 else: 57 return False 58 return False 59 60def find_file(name, path): 61 for root, dirs, files in os.walk(path): 62 if name in files: 63 return os.path.join(root, name) 64 65class Builder: 66 def __init__(self, options): 67 self.options = options 68 self.build_dir = check_dir(options.build_dir, create=True) 69 self.opencv_dir = check_dir(options.opencv_dir) 70 self.emscripten_dir = check_dir(options.emscripten_dir) 71 72 def get_toolchain_file(self): 73 return os.path.join(self.emscripten_dir, "cmake", "Modules", "Platform", "Emscripten.cmake") 74 75 def clean_build_dir(self): 76 for d in ["CMakeCache.txt", "CMakeFiles/", "bin/", "libs/", "lib/", "modules"]: 77 rm_one(d) 78 79 def get_cmake_cmd(self): 80 cmd = [ 81 "cmake", 82 "-DPYTHON_DEFAULT_EXECUTABLE=%s" % sys.executable, 83 "-DENABLE_PIC=FALSE", # To workaround emscripten upstream backend issue https://github.com/emscripten-core/emscripten/issues/8761 84 "-DCMAKE_BUILD_TYPE=Release", 85 "-DCMAKE_TOOLCHAIN_FILE='%s'" % self.get_toolchain_file(), 86 "-DCPU_BASELINE=''", 87 "-DCPU_DISPATCH=''", 88 "-DCV_TRACE=OFF", 89 "-DBUILD_SHARED_LIBS=OFF", 90 "-DWITH_1394=OFF", 91 "-DWITH_ADE=OFF", 92 "-DWITH_VTK=OFF", 93 "-DWITH_EIGEN=OFF", 94 "-DWITH_FFMPEG=OFF", 95 "-DWITH_GSTREAMER=OFF", 96 "-DWITH_GTK=OFF", 97 "-DWITH_GTK_2_X=OFF", 98 "-DWITH_IPP=OFF", 99 "-DWITH_JASPER=OFF", 100 "-DWITH_JPEG=OFF", 101 "-DWITH_WEBP=OFF", 102 "-DWITH_OPENEXR=OFF", 103 "-DWITH_OPENGL=OFF", 104 "-DWITH_OPENVX=OFF", 105 "-DWITH_OPENNI=OFF", 106 "-DWITH_OPENNI2=OFF", 107 "-DWITH_PNG=OFF", 108 "-DWITH_TBB=OFF", 109 "-DWITH_TIFF=OFF", 110 "-DWITH_V4L=OFF", 111 "-DWITH_OPENCL=OFF", 112 "-DWITH_OPENCL_SVM=OFF", 113 "-DWITH_OPENCLAMDFFT=OFF", 114 "-DWITH_OPENCLAMDBLAS=OFF", 115 "-DWITH_GPHOTO2=OFF", 116 "-DWITH_LAPACK=OFF", 117 "-DWITH_ITT=OFF", 118 "-DWITH_QUIRC=ON", 119 "-DBUILD_ZLIB=ON", 120 "-DBUILD_opencv_apps=OFF", 121 "-DBUILD_opencv_calib3d=ON", 122 "-DBUILD_opencv_dnn=ON", 123 "-DBUILD_opencv_features2d=ON", 124 "-DBUILD_opencv_flann=ON", # No bindings provided. This module is used as a dependency for other modules. 125 "-DBUILD_opencv_gapi=OFF", 126 "-DBUILD_opencv_ml=OFF", 127 "-DBUILD_opencv_photo=ON", 128 "-DBUILD_opencv_imgcodecs=OFF", 129 "-DBUILD_opencv_shape=OFF", 130 "-DBUILD_opencv_videoio=OFF", 131 "-DBUILD_opencv_videostab=OFF", 132 "-DBUILD_opencv_highgui=OFF", 133 "-DBUILD_opencv_superres=OFF", 134 "-DBUILD_opencv_stitching=OFF", 135 "-DBUILD_opencv_java=OFF", 136 "-DBUILD_opencv_js=ON", 137 "-DBUILD_opencv_python2=OFF", 138 "-DBUILD_opencv_python3=OFF", 139 "-DBUILD_EXAMPLES=OFF", 140 "-DBUILD_PACKAGE=OFF", 141 "-DBUILD_TESTS=OFF", 142 "-DBUILD_PERF_TESTS=OFF"] 143 if self.options.cmake_option: 144 cmd += self.options.cmake_option 145 if self.options.build_doc: 146 cmd.append("-DBUILD_DOCS=ON") 147 else: 148 cmd.append("-DBUILD_DOCS=OFF") 149 150 if self.options.threads: 151 cmd.append("-DWITH_PTHREADS_PF=ON") 152 else: 153 cmd.append("-DWITH_PTHREADS_PF=OFF") 154 155 if self.options.simd: 156 cmd.append("-DCV_ENABLE_INTRINSICS=ON") 157 else: 158 cmd.append("-DCV_ENABLE_INTRINSICS=OFF") 159 160 if self.options.build_wasm_intrin_test: 161 cmd.append("-DBUILD_WASM_INTRIN_TESTS=ON") 162 else: 163 cmd.append("-DBUILD_WASM_INTRIN_TESTS=OFF") 164 165 flags = self.get_build_flags() 166 if flags: 167 cmd += ["-DCMAKE_C_FLAGS='%s'" % flags, 168 "-DCMAKE_CXX_FLAGS='%s'" % flags] 169 return cmd 170 171 def get_build_flags(self): 172 flags = "" 173 if self.options.build_wasm: 174 flags += "-s WASM=1 " 175 elif self.options.disable_wasm: 176 flags += "-s WASM=0 " 177 if self.options.threads: 178 flags += "-s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=4 " 179 else: 180 flags += "-s USE_PTHREADS=0 " 181 if self.options.enable_exception: 182 flags += "-s DISABLE_EXCEPTION_CATCHING=0 " 183 if self.options.simd: 184 flags += "-msimd128 " 185 if self.options.build_flags: 186 flags += self.options.build_flags 187 return flags 188 189 def config(self): 190 cmd = self.get_cmake_cmd() 191 cmd.append(self.opencv_dir) 192 execute(cmd) 193 194 def build_opencvjs(self): 195 execute(["make", "-j", str(multiprocessing.cpu_count()), "opencv.js"]) 196 197 def build_test(self): 198 execute(["make", "-j", str(multiprocessing.cpu_count()), "opencv_js_test"]) 199 200 def build_perf(self): 201 execute(["make", "-j", str(multiprocessing.cpu_count()), "opencv_js_perf"]) 202 203 def build_doc(self): 204 execute(["make", "-j", str(multiprocessing.cpu_count()), "doxygen"]) 205 206 def build_loader(self): 207 execute(["make", "-j", str(multiprocessing.cpu_count()), "opencv_js_loader"]) 208 209 210#=================================================================================================== 211 212if __name__ == "__main__": 213 log.basicConfig(format='%(message)s', level=log.DEBUG) 214 215 opencv_dir = os.path.abspath(os.path.join(SCRIPT_DIR, '../..')) 216 emscripten_dir = None 217 if "EMSCRIPTEN" in os.environ: 218 emscripten_dir = os.environ["EMSCRIPTEN"] 219 else: 220 log.warning("EMSCRIPTEN environment variable is not available. Please properly activate Emscripten SDK and consider using 'emcmake' launcher") 221 222 parser = argparse.ArgumentParser(description='Build OpenCV.js by Emscripten') 223 parser.add_argument("build_dir", help="Building directory (and output)") 224 parser.add_argument('--opencv_dir', default=opencv_dir, help='Opencv source directory (default is "../.." relative to script location)') 225 parser.add_argument('--emscripten_dir', default=emscripten_dir, help="Path to Emscripten to use for build (deprecated in favor of 'emcmake' launcher)") 226 parser.add_argument('--build_wasm', action="store_true", help="Build OpenCV.js in WebAssembly format") 227 parser.add_argument('--disable_wasm', action="store_true", help="Build OpenCV.js in Asm.js format") 228 parser.add_argument('--threads', action="store_true", help="Build OpenCV.js with threads optimization") 229 parser.add_argument('--simd', action="store_true", help="Build OpenCV.js with SIMD optimization") 230 parser.add_argument('--build_test', action="store_true", help="Build tests") 231 parser.add_argument('--build_perf', action="store_true", help="Build performance tests") 232 parser.add_argument('--build_doc', action="store_true", help="Build tutorials") 233 parser.add_argument('--build_loader', action="store_true", help="Build OpenCV.js loader") 234 parser.add_argument('--clean_build_dir', action="store_true", help="Clean build dir") 235 parser.add_argument('--skip_config', action="store_true", help="Skip cmake config") 236 parser.add_argument('--config_only', action="store_true", help="Only do cmake config") 237 parser.add_argument('--enable_exception', action="store_true", help="Enable exception handling") 238 # Use flag --cmake option="-D...=ON" only for one argument, if you would add more changes write new cmake_option flags 239 parser.add_argument('--cmake_option', action='append', help="Append CMake options") 240 # Use flag --build_flags="-s USE_PTHREADS=0 -Os" for one and more arguments as in the example 241 parser.add_argument('--build_flags', help="Append Emscripten build options") 242 parser.add_argument('--build_wasm_intrin_test', default=False, action="store_true", help="Build WASM intrin tests") 243 # Write a path to modify file like argument of this flag 244 parser.add_argument('--config', default=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'opencv_js.config.py'), 245 help="Specify configuration file with own list of exported into JS functions") 246 247 args = parser.parse_args() 248 249 log.debug("Args: %s", args) 250 251 os.environ["OPENCV_JS_WHITELIST"] = os.path.abspath(args.config) 252 253 if 'EMMAKEN_JUST_CONFIGURE' in os.environ: 254 del os.environ['EMMAKEN_JUST_CONFIGURE'] # avoid linker errors with NODERAWFS message then using 'emcmake' launcher 255 256 if args.emscripten_dir is None: 257 log.error("Cannot get Emscripten path, please use 'emcmake' launcher or specify it either by EMSCRIPTEN environment variable or --emscripten_dir option.") 258 sys.exit(-1) 259 260 builder = Builder(args) 261 262 os.chdir(builder.build_dir) 263 264 if args.clean_build_dir: 265 log.info("=====") 266 log.info("===== Clean build dir %s", builder.build_dir) 267 log.info("=====") 268 builder.clean_build_dir() 269 270 if not args.skip_config: 271 target = "default target" 272 if args.build_wasm: 273 target = "wasm" 274 elif args.disable_wasm: 275 target = "asm.js" 276 log.info("=====") 277 log.info("===== Config OpenCV.js build for %s" % target) 278 log.info("=====") 279 builder.config() 280 281 if args.config_only: 282 sys.exit(0) 283 284 log.info("=====") 285 log.info("===== Building OpenCV.js") 286 log.info("=====") 287 builder.build_opencvjs() 288 289 if args.build_test: 290 log.info("=====") 291 log.info("===== Building OpenCV.js tests") 292 log.info("=====") 293 builder.build_test() 294 295 if args.build_perf: 296 log.info("=====") 297 log.info("===== Building OpenCV.js performance tests") 298 log.info("=====") 299 builder.build_perf() 300 301 if args.build_doc: 302 log.info("=====") 303 log.info("===== Building OpenCV.js tutorials") 304 log.info("=====") 305 builder.build_doc() 306 307 if args.build_loader: 308 log.info("=====") 309 log.info("===== Building OpenCV.js loader") 310 log.info("=====") 311 builder.build_loader() 312 313 log.info("=====") 314 log.info("===== Build finished") 315 log.info("=====") 316 317 opencvjs_path = os.path.join(builder.build_dir, "bin", "opencv.js") 318 if check_file(opencvjs_path): 319 log.info("OpenCV.js location: %s", opencvjs_path) 320 321 if args.build_test: 322 opencvjs_test_path = os.path.join(builder.build_dir, "bin", "tests.html") 323 if check_file(opencvjs_test_path): 324 log.info("OpenCV.js tests location: %s", opencvjs_test_path) 325 326 if args.build_perf: 327 opencvjs_perf_path = os.path.join(builder.build_dir, "bin", "perf") 328 opencvjs_perf_base_path = os.path.join(builder.build_dir, "bin", "perf", "base.js") 329 if check_file(opencvjs_perf_base_path): 330 log.info("OpenCV.js performance tests location: %s", opencvjs_perf_path) 331 332 if args.build_doc: 333 opencvjs_tutorial_path = find_file("tutorial_js_root.html", os.path.join(builder.build_dir, "doc", "doxygen", "html")) 334 if check_file(opencvjs_tutorial_path): 335 log.info("OpenCV.js tutorials location: %s", opencvjs_tutorial_path) 336 337 if args.build_loader: 338 opencvjs_loader_path = os.path.join(builder.build_dir, "bin", "loader.js") 339 if check_file(opencvjs_loader_path): 340 log.info("OpenCV.js loader location: %s", opencvjs_loader_path) 341