1#!/usr/bin/env python 2 3import sys, os, platform, xml, re, tempfile, glob, datetime, getpass, shutil 4from optparse import OptionParser 5from subprocess import Popen, PIPE 6 7hostos = os.name # 'nt', 'posix' 8hostmachine = platform.machine() # 'x86', 'AMD64', 'x86_64' 9 10errorCode = 0 11 12SIMD_DETECTION_PROGRAM=""" 13#if __SSE5__ 14# error SSE5 15#endif 16#if __AVX2__ 17# error AVX2 18#endif 19#if __AVX__ 20# error AVX 21#endif 22#if __SSE4_2__ 23# error SSE4.2 24#endif 25#if __SSE4_1__ 26# error SSE4.1 27#endif 28#if __SSSE3__ 29# error SSSE3 30#endif 31#if __SSE3__ 32# error SSE3 33#endif 34#if __AES__ 35# error AES 36#endif 37#if __SSE2__ 38# error SSE2 39#endif 40#if __SSE__ 41# error SSE 42#endif 43#if __3dNOW__ 44# error 3dNOW 45#endif 46#if __MMX__ 47# error MMX 48#endif 49#if __ARM_NEON__ 50# error NEON 51#endif 52#error NOSIMD 53""" 54 55parse_patterns = ( 56 {'name': "has_perf_tests", 'default': "OFF", 'pattern': re.compile("^BUILD_PERF_TESTS:BOOL=(ON)$")}, 57 {'name': "has_accuracy_tests", 'default': "OFF", 'pattern': re.compile("^BUILD_TESTS:BOOL=(ON)$")}, 58 {'name': "cmake_home", 'default': None, 'pattern': re.compile("^CMAKE_HOME_DIRECTORY:INTERNAL=(.+)$")}, 59 {'name': "opencv_home", 'default': None, 'pattern': re.compile("^OpenCV_SOURCE_DIR:STATIC=(.+)$")}, 60 {'name': "tests_dir", 'default': None, 'pattern': re.compile("^EXECUTABLE_OUTPUT_PATH:PATH=(.+)$")}, 61 {'name': "build_type", 'default': "Release", 'pattern': re.compile("^CMAKE_BUILD_TYPE:STRING=(.*)$")}, 62 {'name': "svnversion_path", 'default': None, 'pattern': re.compile("^SVNVERSION_PATH:FILEPATH=(.*)$")}, 63 {'name': "git_executable", 'default': None, 'pattern': re.compile("^GIT_EXECUTABLE:FILEPATH=(.*)$")}, 64 {'name': "cxx_flags", 'default': "", 'pattern': re.compile("^CMAKE_CXX_FLAGS:STRING=(.*)$")}, 65 {'name': "cxx_flags_debug", 'default': "", 'pattern': re.compile("^CMAKE_CXX_FLAGS_DEBUG:STRING=(.*)$")}, 66 {'name': "cxx_flags_release", 'default': "", 'pattern': re.compile("^CMAKE_CXX_FLAGS_RELEASE:STRING=(.*)$")}, 67 {'name': "opencv_cxx_flags", 'default': "", 'pattern': re.compile("^OPENCV_EXTRA_C_FLAGS:INTERNAL=(.*)$")}, 68 {'name': "opencv_cxx_flags_debug", 'default': "", 'pattern': re.compile("^OPENCV_EXTRA_C_FLAGS_DEBUG:INTERNAL=(.*)$")}, 69 {'name': "opencv_cxx_flags_release", 'default': "", 'pattern': re.compile("^OPENCV_EXTRA_C_FLAGS_RELEASE:INTERNAL=(.*)$")}, 70 {'name': "cxx_flags_android", 'default': None, 'pattern': re.compile("^ANDROID_CXX_FLAGS:INTERNAL=(.*)$")}, 71 {'name': "ndk_path", 'default': None, 'pattern': re.compile("^(?:ANDROID_NDK|ANDROID_STANDALONE_TOOLCHAIN)?:PATH=(.*)$")}, 72 {'name': "android_abi", 'default': None, 'pattern': re.compile("^ANDROID_ABI:STRING=(.*)$")}, 73 {'name': "android_executable", 'default': None, 'pattern': re.compile("^ANDROID_EXECUTABLE:FILEPATH=(.*android.*)$")}, 74 {'name': "ant_executable", 'default': None, 'pattern': re.compile("^ANT_EXECUTABLE:FILEPATH=(.*ant.*)$")}, 75 {'name': "java_test_binary_dir", 'default': None, 'pattern': re.compile("^opencv_test_java_BINARY_DIR:STATIC=(.*)$")}, 76 {'name': "is_x64", 'default': "OFF", 'pattern': re.compile("^CUDA_64_BIT_DEVICE_CODE:BOOL=(ON)$")},#ugly( 77 {'name': "cmake_generator", 'default': None, 'pattern': re.compile("^CMAKE_GENERATOR:INTERNAL=(.+)$")}, 78 {'name': "cxx_compiler", 'default': None, 'pattern': re.compile("^CMAKE_CXX_COMPILER:FILEPATH=(.+)$")}, 79 {'name': "cxx_compiler_arg1", 'default': None, 'pattern': re.compile("^CMAKE_CXX_COMPILER_ARG1:[A-Z]+=(.+)$")}, 80 {'name': "with_cuda", 'default': "OFF", 'pattern': re.compile("^WITH_CUDA:BOOL=(ON)$")}, 81 {'name': "cuda_library", 'default': None, 'pattern': re.compile("^CUDA_CUDA_LIBRARY:FILEPATH=(.+)$")}, 82 {'name': "core_dependencies", 'default': None, 'pattern': re.compile("^opencv_core_LIB_DEPENDS:STATIC=(.+)$")}, 83) 84 85def query_yes_no(stdout, question, default="yes"): 86 valid = {"yes":True, "y":True, "ye":True, "no":False, "n":False} 87 if default == None: 88 prompt = " [y/n] " 89 elif default == "yes": 90 prompt = " [Y/n] " 91 elif default == "no": 92 prompt = " [y/N] " 93 else: 94 raise ValueError("invalid default answer: '%s'" % default) 95 96 while True: 97 stdout.write(os.linesep + question + prompt) 98 choice = raw_input().lower() 99 if default is not None and choice == '': 100 return valid[default] 101 elif choice in valid: 102 return valid[choice] 103 else: 104 stdout.write("Please respond with 'yes' or 'no' "\ 105 "(or 'y' or 'n').\n") 106 107def getRunningProcessExePathByName_win32(name): 108 from ctypes import windll, POINTER, pointer, Structure, sizeof 109 from ctypes import c_long , c_int , c_uint , c_char , c_ubyte , c_char_p , c_void_p 110 111 class PROCESSENTRY32(Structure): 112 _fields_ = [ ( 'dwSize' , c_uint ) , 113 ( 'cntUsage' , c_uint) , 114 ( 'th32ProcessID' , c_uint) , 115 ( 'th32DefaultHeapID' , c_uint) , 116 ( 'th32ModuleID' , c_uint) , 117 ( 'cntThreads' , c_uint) , 118 ( 'th32ParentProcessID' , c_uint) , 119 ( 'pcPriClassBase' , c_long) , 120 ( 'dwFlags' , c_uint) , 121 ( 'szExeFile' , c_char * 260 ) , 122 ( 'th32MemoryBase' , c_long) , 123 ( 'th32AccessKey' , c_long ) ] 124 125 class MODULEENTRY32(Structure): 126 _fields_ = [ ( 'dwSize' , c_long ) , 127 ( 'th32ModuleID' , c_long ), 128 ( 'th32ProcessID' , c_long ), 129 ( 'GlblcntUsage' , c_long ), 130 ( 'ProccntUsage' , c_long ) , 131 ( 'modBaseAddr' , c_long ) , 132 ( 'modBaseSize' , c_long ) , 133 ( 'hModule' , c_void_p ) , 134 ( 'szModule' , c_char * 256 ), 135 ( 'szExePath' , c_char * 260 ) ] 136 137 TH32CS_SNAPPROCESS = 2 138 TH32CS_SNAPMODULE = 0x00000008 139 140 ## CreateToolhelp32Snapshot 141 CreateToolhelp32Snapshot= windll.kernel32.CreateToolhelp32Snapshot 142 CreateToolhelp32Snapshot.reltype = c_long 143 CreateToolhelp32Snapshot.argtypes = [ c_int , c_int ] 144 ## Process32First 145 Process32First = windll.kernel32.Process32First 146 Process32First.argtypes = [ c_void_p , POINTER( PROCESSENTRY32 ) ] 147 Process32First.rettype = c_int 148 ## Process32Next 149 Process32Next = windll.kernel32.Process32Next 150 Process32Next.argtypes = [ c_void_p , POINTER(PROCESSENTRY32) ] 151 Process32Next.rettype = c_int 152 ## CloseHandle 153 CloseHandle = windll.kernel32.CloseHandle 154 CloseHandle.argtypes = [ c_void_p ] 155 CloseHandle.rettype = c_int 156 ## Module32First 157 Module32First = windll.kernel32.Module32First 158 Module32First.argtypes = [ c_void_p , POINTER(MODULEENTRY32) ] 159 Module32First.rettype = c_int 160 161 hProcessSnap = c_void_p(0) 162 hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS , 0 ) 163 164 pe32 = PROCESSENTRY32() 165 pe32.dwSize = sizeof( PROCESSENTRY32 ) 166 ret = Process32First( hProcessSnap , pointer( pe32 ) ) 167 path = None 168 169 while ret : 170 if name + ".exe" == pe32.szExeFile: 171 hModuleSnap = c_void_p(0) 172 me32 = MODULEENTRY32() 173 me32.dwSize = sizeof( MODULEENTRY32 ) 174 hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, pe32.th32ProcessID ) 175 176 ret = Module32First( hModuleSnap, pointer(me32) ) 177 path = me32.szExePath 178 CloseHandle( hModuleSnap ) 179 if path: 180 break 181 ret = Process32Next( hProcessSnap, pointer(pe32) ) 182 CloseHandle( hProcessSnap ) 183 return path 184 185def getRunningProcessExePathByName_posix(name): 186 pids= [pid for pid in os.listdir('/proc') if pid.isdigit()] 187 for pid in pids: 188 try: 189 path = os.readlink(os.path.join('/proc', pid, 'exe')) 190 if path and path.endswith(name): 191 return path 192 except: 193 pass 194 195def getRunningProcessExePathByName(name): 196 try: 197 if hostos == "nt": 198 return getRunningProcessExePathByName_win32(name) 199 elif hostos == "posix": 200 return getRunningProcessExePathByName_posix(name) 201 else: 202 return None 203 except: 204 return None 205 206class TestSuite(object): 207 def __init__(self, options, path = None): 208 self.options = options 209 self.path = path 210 self.error = None 211 self.setUp = None 212 self.tearDown = None 213 self.adb = None 214 self.targetos = None 215 self.nameprefix = "opencv_" + self.options.mode + "_" 216 for p in parse_patterns: 217 setattr(self, p["name"], p["default"]) 218 219 if self.path: 220 cachefile = open(os.path.join(self.path, "CMakeCache.txt"), "rt") 221 try: 222 for l in cachefile.readlines(): 223 ll = l.strip() 224 if not ll or ll.startswith("#"): 225 continue 226 for p in parse_patterns: 227 match = p["pattern"].match(ll) 228 if match: 229 value = match.groups()[0] 230 if value and not value.endswith("-NOTFOUND"): 231 setattr(self, p["name"], value) 232 except: 233 pass 234 cachefile.close() 235 236 # detect target platform 237 if self.android_executable or self.android_abi or self.ndk_path: 238 self.targetos = "android" 239 else: 240 self.targetos = hostos 241 242 self.initialize() 243 244 def initialize(self): 245 # fix empty tests dir 246 if not self.tests_dir: 247 self.tests_dir = self.path 248 self.tests_dir = os.path.normpath(self.tests_dir) 249 250 # compute path to adb 251 if self.android_executable: 252 self.adb = os.path.join(os.path.dirname(os.path.dirname(self.android_executable)), ("platform-tools/adb","platform-tools/adb.exe")[hostos == 'nt']) 253 if not os.path.isfile(self.adb) or not os.access(self.adb, os.X_OK): 254 self.adb = None 255 else: 256 self.adb = None 257 258 if self.targetos == "android": 259 # fix adb tool location 260 if not self.adb: 261 self.adb = getRunningProcessExePathByName("adb") 262 if not self.adb: 263 self.adb = "adb" 264 if self.options.adb_serial: 265 self.adb = [self.adb, "-s", self.options.adb_serial] 266 else: 267 self.adb = [self.adb] 268 try: 269 output = Popen(self.adb + ["shell", "ls"], stdout=PIPE, stderr=PIPE).communicate() 270 except OSError: 271 self.adb = [] 272 # remember current device serial. Needed if another device is connected while this script runs 273 if self.adb and not self.options.adb_serial: 274 adb_res = self.runAdb("devices") 275 if not adb_res: 276 self.error = "Could not run adb command: %s (for %s)" % (self.error, self.path) 277 self.adb = [] 278 else: 279 # assume here that device name may consists of any characters except newline 280 connected_devices = re.findall(r"^[^\n]+[ \t]+device\r?$", adb_res, re.MULTILINE) 281 if not connected_devices: 282 self.error = "Android device not found" 283 self.adb = [] 284 elif len(connected_devices) != 1: 285 self.error = "Too many (%s) devices are connected. Please specify single device using --serial option:\n\n" % (len(connected_devices)) + adb_res 286 self.adb = [] 287 else: 288 self.options.adb_serial = connected_devices[0].split("\t")[0] 289 self.adb = self.adb + ["-s", self.options.adb_serial] 290 if self.adb: 291 # construct name for aapt tool 292 self.aapt = [os.path.join(os.path.dirname(self.adb[0]), ("aapt","aapt.exe")[hostos == 'nt'])] 293 if not os.path.isfile(self.aapt[0]): 294 # it's moved in SDK r22 295 sdk_dir = os.path.dirname( os.path.dirname(self.adb[0]) ) 296 aapt_fn = ("aapt", "aapt.exe")[hostos == 'nt'] 297 for r, ds, fs in os.walk( os.path.join(sdk_dir, 'build-tools') ): 298 if aapt_fn in fs: 299 self.aapt = [ os.path.join(r, aapt_fn) ] 300 break 301 else: 302 self.error = "Can't find '%s' tool!" % aapt_fn 303 304 # fix has_perf_tests param 305 self.has_perf_tests = self.has_perf_tests == "ON" 306 self.has_accuracy_tests = self.has_accuracy_tests == "ON" 307 # fix is_x64 flag 308 self.is_x64 = self.is_x64 == "ON" 309 if not self.is_x64 and ("X64" in "%s %s %s" % (self.cxx_flags, self.cxx_flags_release, self.cxx_flags_debug) or "Win64" in self.cmake_generator): 310 self.is_x64 = True 311 312 # fix test path 313 if "Visual Studio" in self.cmake_generator: 314 if self.options.configuration: 315 self.tests_dir = os.path.join(self.tests_dir, self.options.configuration) 316 else: 317 self.tests_dir = os.path.join(self.tests_dir, self.build_type) 318 elif not self.is_x64 and self.cxx_compiler: 319 #one more attempt to detect x64 compiler 320 try: 321 compiler = [self.cxx_compiler] 322 if self.cxx_compiler_arg1: 323 compiler.append(self.cxx_compiler_arg1) 324 output = Popen(compiler + ["-v"], stdout=PIPE, stderr=PIPE).communicate() 325 if not output[0] and "x86_64" in output[1]: 326 self.is_x64 = True 327 except OSError: 328 pass 329 330 # detect target arch 331 if self.targetos == "android": 332 if "armeabi-v7a" in self.android_abi: 333 self.targetarch = "armv7a" 334 elif "armeabi-v6" in self.android_abi: 335 self.targetarch = "armv6" 336 elif "armeabi" in self.android_abi: 337 self.targetarch = "armv5te" 338 elif "x86" in self.android_abi: 339 self.targetarch = "x86" 340 elif "mips" in self.android_abi: 341 self.targetarch = "mips" 342 else: 343 self.targetarch = "ARM" 344 elif self.is_x64 and hostmachine in ["AMD64", "x86_64"]: 345 self.targetarch = "x64" 346 elif hostmachine in ["x86", "AMD64", "x86_64"]: 347 self.targetarch = "x86" 348 else: 349 self.targetarch = "unknown" 350 351 # fix CUDA attributes 352 self.with_cuda = self.with_cuda == "ON" 353 if self.cuda_library and self.cuda_library.endswith("-NOTFOUND"): 354 self.cuda_library = None 355 self.has_cuda = self.with_cuda and self.cuda_library and self.targetarch in ["x86", "x64"] 356 357 self.hardware = None 358 359 self.cmake_home_vcver = self.getVCVersion(self.cmake_home) 360 if self.opencv_home == self.cmake_home: 361 self.opencv_home_vcver = self.cmake_home_vcver 362 else: 363 self.opencv_home_vcver = self.getVCVersion(self.opencv_home) 364 365 self.tests = self.getAvailableTestApps() 366 367 def getVCVersion(self, root_path): 368 if not root_path: 369 return None 370 if os.path.isdir(os.path.join(root_path, ".svn")): 371 return self.getSvnVersion(root_path) 372 elif os.path.isdir(os.path.join(root_path, ".git")): 373 return self.getGitHash(root_path) 374 return None 375 376 def getGitHash(self, path): 377 if not path or not self.git_executable: 378 return None 379 try: 380 output = Popen([self.git_executable, "rev-parse", "--short", "HEAD"], stdout=PIPE, stderr=PIPE, cwd = path).communicate() 381 if not output[1]: 382 return output[0].strip() 383 else: 384 return None 385 except OSError: 386 return None 387 388 def getSvnVersion(self, path): 389 if not path: 390 val = None 391 elif not self.svnversion_path and hostos == 'nt': 392 val = self.tryGetSvnVersionWithTortoise(path) 393 else: 394 svnversion = self.svnversion_path 395 if not svnversion: 396 svnversion = "svnversion" 397 try: 398 output = Popen([svnversion, "-n", path], stdout=PIPE, stderr=PIPE).communicate() 399 if not output[1]: 400 val = output[0] 401 else: 402 val = None 403 except OSError: 404 val = None 405 if val: 406 val = val.replace(" ", "_") 407 return val 408 409 def tryGetSvnVersionWithTortoise(self, path): 410 try: 411 wcrev = "SubWCRev.exe" 412 dir = tempfile.mkdtemp() 413 #print dir 414 tmpfilename = os.path.join(dir, "svn.tmp") 415 tmpfilename2 = os.path.join(dir, "svn_out.tmp") 416 tmpfile = open(tmpfilename, "w") 417 tmpfile.write("$WCRANGE$$WCMODS?M:$") 418 tmpfile.close(); 419 output = Popen([wcrev, path, tmpfilename, tmpfilename2, "-f"], stdout=PIPE, stderr=PIPE).communicate() 420 if "is not a working copy" in output[0]: 421 version = "exported" 422 else: 423 tmpfile = open(tmpfilename2, "r") 424 version = tmpfile.read() 425 tmpfile.close() 426 return version 427 except: 428 return None 429 finally: 430 if dir: 431 shutil.rmtree(dir) 432 433 def isTest(self, fullpath): 434 if not os.path.isfile(fullpath): 435 return False 436 if self.targetos == "nt" and not fullpath.endswith(".exe"): 437 return False 438 if hostos == self.targetos: 439 return os.access(fullpath, os.X_OK) 440 if self.targetos == "android" and fullpath.endswith(".apk"): 441 return True 442 return True 443 444 def getAvailableTestApps(self): 445 if self.tests_dir and os.path.isdir(self.tests_dir): 446 files = glob.glob(os.path.join(self.tests_dir, self.nameprefix + "*")) 447 files = [f for f in files if self.isTest(f)] 448 if self.ant_executable and self.java_test_binary_dir: 449 files.append("java") 450 return files 451 return [] 452 453 def getLogName(self, app, timestamp): 454 app = os.path.basename(app) 455 if app.endswith(".exe"): 456 if app.endswith("d.exe"): 457 app = app[:-5] 458 else: 459 app = app[:-4] 460 if app.startswith(self.nameprefix): 461 app = app[len(self.nameprefix):] 462 463 if self.cmake_home_vcver: 464 if self.cmake_home_vcver == self.opencv_home_vcver: 465 rev = self.cmake_home_vcver 466 elif self.opencv_home_vcver: 467 rev = self.cmake_home_vcver + "-" + self.opencv_home_vcver 468 else: 469 rev = self.cmake_home_vcver 470 else: 471 rev = None 472 if rev: 473 rev = rev.replace(":","to") 474 else: 475 rev = "" 476 477 if self.options.useLongNames: 478 if not rev: 479 rev = "unknown" 480 tstamp = timestamp.strftime("%Y%m%d-%H%M%S") 481 482 features = [] 483 #OS 484 _os = "" 485 if self.targetos == "android": 486 _os = "Android" + self.runAdb("shell", "getprop ro.build.version.release").strip() 487 else: 488 mv = platform.mac_ver() 489 if mv[0]: 490 _os = "Darwin" + mv[0] 491 else: 492 wv = platform.win32_ver() 493 if wv[0]: 494 _os = "Windows" + wv[0] 495 else: 496 lv = platform.linux_distribution() 497 if lv[0]: 498 _os = lv[0] + lv[1] 499 else: 500 _os = self.targetos 501 features.append(_os) 502 503 #HW(x86, x64, ARMv7a) 504 if self.targetarch: 505 features.append(self.targetarch) 506 507 #TBB 508 if ";tbb;" in self.core_dependencies: 509 features.append("TBB") 510 511 #CUDA 512 if self.has_cuda: 513 #TODO: determine compute capability 514 features.append("CUDA") 515 516 #SIMD 517 compiler_output = "" 518 try: 519 tmpfile = tempfile.mkstemp(suffix=".cpp", text = True) 520 fd = os.fdopen(tmpfile[0], "w+b") 521 fd.write(SIMD_DETECTION_PROGRAM) 522 fd.close(); 523 options = [self.cxx_compiler] 524 if self.cxx_compiler_arg1: 525 options.append(self.cxx_compiler_arg1) 526 cxx_flags = self.cxx_flags + " " + self.cxx_flags_release + " " + self.opencv_cxx_flags + " " + self.opencv_cxx_flags_release 527 if self.targetos == "android" and self.cxx_flags_android: 528 cxx_flags = self.cxx_flags_android + " " + cxx_flags 529 530 prev_option = None 531 for opt in cxx_flags.split(" "): 532 if opt.count('\"') % 2 == 1: 533 if prev_option is None: 534 prev_option = opt 535 else: 536 options.append(prev_option + " " + opt) 537 prev_option = None 538 elif prev_option is None: 539 options.append(opt) 540 else: 541 prev_option = prev_option + " " + opt 542 options.append(tmpfile[1]) 543 output = Popen(options, stdout=PIPE, stderr=PIPE).communicate() 544 compiler_output = output[1] 545 os.remove(tmpfile[1]) 546 except OSError: 547 pass 548 if compiler_output: 549 m = re.search("#error\W+(\w+)", compiler_output) 550 if m: 551 features.append(m.group(1)) 552 553 #fin 554 return "%s__%s__%s__%s.xml" % (app, rev, tstamp, "_".join(features)) 555 else: 556 if rev: 557 rev = rev + "_" 558 if self.hardware: 559 hw = str(self.hardware).replace(" ", "_") + "_" 560 elif self.has_cuda: 561 hw = "CUDA_" 562 else: 563 hw = "" 564 tstamp = timestamp.strftime("%Y%m%d-%H%M%S") 565 lname = "%s_%s_%s_%s%s%s.xml" % (app, self.targetos, self.targetarch, hw, rev, tstamp) 566 lname = str.replace(lname, '(', '_') 567 lname = str.replace(lname, ')', '_') 568 return lname 569 570 def getTest(self, name): 571 # full path 572 if self.isTest(name): 573 return name 574 575 # name only 576 fullname = os.path.join(self.tests_dir, name) 577 if self.isTest(fullname): 578 return fullname 579 580 # name without extension 581 fullname += ".exe" 582 if self.isTest(fullname): 583 return fullname 584 if self.targetos == "android": 585 fullname += ".apk" 586 if self.isTest(fullname): 587 return fullname 588 589 # short name for OpenCV tests 590 for t in self.tests: 591 if t == name: 592 return t 593 fname = os.path.basename(t) 594 if fname == name: 595 return t 596 if fname.endswith(".exe") or (self.targetos == "android" and fname.endswith(".apk")): 597 fname = fname[:-4] 598 if fname == name: 599 return t 600 if self.options.configuration == "Debug" and fname == name + 'd': 601 return t 602 if fname.startswith(self.nameprefix): 603 fname = fname[len(self.nameprefix):] 604 if fname == name: 605 return t 606 if self.options.configuration == "Debug" and fname == name + 'd': 607 return t 608 return None 609 610 def runAdb(self, *args): 611 cmd = self.adb[:] 612 cmd.extend(args) 613 try: 614 output = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate() 615 if not output[1]: 616 return output[0] 617 self.error = output[1] 618 except OSError: 619 pass 620 return None 621 622 def isRunnable(self): 623 if self.error: 624 return False 625 if self.targetarch == "x64" and hostmachine == "x86": 626 self.error = "Target architecture is incompatible with current platform (at %s)" % self.path 627 return False 628 if self.targetos == "android": 629 if not self.adb: 630 self.error = "Could not find adb executable (for %s)" % self.path 631 return False 632 if "armeabi-v7a" in self.android_abi: 633 adb_res = self.runAdb("shell", "cat /proc/cpuinfo") 634 if not adb_res: 635 self.error = "Could not get info about Android platform: %s (for %s)" % (self.error, self.path) 636 return False 637 if "ARMv7" not in adb_res: 638 self.error = "Android device does not support ARMv7 commands, but tests are built for armeabi-v7a (for %s)" % self.path 639 return False 640 if "NEON" in self.android_abi and "neon" not in adb_res: 641 self.error = "Android device has no NEON, but tests are built for %s (for %s)" % (self.android_abi, self.path) 642 return False 643 hw = re.search(r"^Hardware[ \t]*:[ \t]*(.*?)$", adb_res, re.MULTILINE) 644 if hw: 645 self.hardware = hw.groups()[0].strip() 646 return True 647 648 def runTest(self, path, workingDir, _stdout, _stderr, args = []): 649 global errorCode 650 651 if self.error: 652 return 653 args = args[:] 654 timestamp = datetime.datetime.now() 655 logfile = self.getLogName(path, timestamp) 656 exe = os.path.abspath(path) 657 658 userlog = [a for a in args if a.startswith("--gtest_output=")] 659 if len(userlog) == 0: 660 args.append("--gtest_output=xml:" + logfile) 661 else: 662 logfile = userlog[0][userlog[0].find(":")+1:] 663 664 if self.targetos == "android" and exe.endswith(".apk"): 665 print "Run java tests:", exe 666 try: 667 # get package info 668 output = Popen(self.aapt + ["dump", "xmltree", exe, "AndroidManifest.xml"], stdout=PIPE, stderr=_stderr).communicate() 669 if not output[0]: 670 print >> _stderr, "fail to dump manifest from", exe 671 return 672 tags = re.split(r"[ ]+E: ", output[0]) 673 # get package name 674 manifest_tag = [t for t in tags if t.startswith("manifest ")] 675 if not manifest_tag: 676 print >> _stderr, "fail to read package name from", exe 677 return 678 pkg_name = re.search(r"^[ ]+A: package=\"(?P<pkg>.*?)\" \(Raw: \"(?P=pkg)\"\)\r?$", manifest_tag[0], flags=re.MULTILINE).group("pkg") 679 # get test instrumentation info 680 instrumentation_tag = [t for t in tags if t.startswith("instrumentation ")] 681 if not instrumentation_tag: 682 print >> _stderr, "can not find instrumentation detials in", exe 683 return 684 pkg_runner = re.search(r"^[ ]+A: android:name\(0x[0-9a-f]{8}\)=\"(?P<runner>.*?)\" \(Raw: \"(?P=runner)\"\)\r?$", instrumentation_tag[0], flags=re.MULTILINE).group("runner") 685 pkg_target = re.search(r"^[ ]+A: android:targetPackage\(0x[0-9a-f]{8}\)=\"(?P<pkg>.*?)\" \(Raw: \"(?P=pkg)\"\)\r?$", instrumentation_tag[0], flags=re.MULTILINE).group("pkg") 686 if not pkg_name or not pkg_runner or not pkg_target: 687 print >> _stderr, "can not find instrumentation detials in", exe 688 return 689 if self.options.junit_package: 690 if self.options.junit_package.startswith("."): 691 pkg_target += self.options.junit_package 692 else: 693 pkg_target = self.options.junit_package 694 # uninstall previously installed package 695 print >> _stderr, "Uninstalling old", pkg_name, "from device..." 696 Popen(self.adb + ["uninstall", pkg_name], stdout=PIPE, stderr=_stderr).communicate() 697 print >> _stderr, "Installing new", exe, "to device...", 698 output = Popen(self.adb + ["install", exe], stdout=PIPE, stderr=PIPE).communicate() 699 if output[0] and output[0].strip().endswith("Success"): 700 print >> _stderr, "Success" 701 else: 702 print >> _stderr, "Failure" 703 print >> _stderr, "Failed to install", exe, "to device" 704 return 705 print >> _stderr, "Running jUnit tests for ", pkg_target 706 if self.setUp: 707 self.setUp() 708 Popen(self.adb + ["shell", "am instrument -w -e package " + pkg_target + " " + pkg_name + "/" + pkg_runner], stdout=_stdout, stderr=_stderr).wait() 709 if self.tearDown: 710 self.tearDown() 711 except OSError: 712 pass 713 return 714 elif self.targetos == "android": 715 hostlogpath = "" 716 usercolor = [a for a in args if a.startswith("--gtest_color=")] 717 if len(usercolor) == 0 and _stdout.isatty() and hostos != "nt": 718 args.append("--gtest_color=yes") 719 try: 720 tempdir = "/data/local/tmp/" 721 andoidcwd = tempdir + getpass.getuser().replace(" ","") + "_" + self.options.mode +"/" 722 exename = os.path.basename(exe) 723 androidexe = andoidcwd + exename 724 # upload 725 _stderr.write("Uploading... ") 726 output = Popen(self.adb + ["push", exe, androidexe], stdout=_stdout, stderr=_stderr).wait() 727 if output != 0: 728 print >> _stderr, "adb finishes unexpectedly with error code", output 729 return 730 # chmod 731 output = Popen(self.adb + ["shell", "chmod 777 " + androidexe], stdout=_stdout, stderr=_stderr).wait() 732 if output != 0: 733 print >> _stderr, "adb finishes unexpectedly with error code", output 734 return 735 # run 736 if self.options.help: 737 command = exename + " --help" 738 else: 739 command = exename + " " + " ".join(args) 740 print >> _stderr, "Run command:", command 741 if self.setUp: 742 self.setUp() 743 env = self.options.android_env.copy() 744 env['OPENCV_TEST_DATA_PATH'] = self.options.test_data_path 745 if self.options.android_propagate_opencv_env: 746 for k, v in os.environ.items(): 747 if k.startswith('OPENCV') and not k in env: 748 env[k] = v 749 print >> _stderr, "Android environment variables: \n", '\n'.join([' %s=%s' % (k, v) for k, v in env.items()]) 750 commandPrefix = ''.join(['export %s=%s && ' % (k, v) for k, v in env.items()]) 751 Popen(self.adb + ["shell", commandPrefix + "cd " + andoidcwd + "&& ./" + command], stdout=_stdout, stderr=_stderr).wait() 752 if self.tearDown: 753 self.tearDown() 754 # try get log 755 if not self.options.help: 756 #_stderr.write("Pull log... ") 757 hostlogpath = os.path.join(workingDir, logfile) 758 output = Popen(self.adb + ["pull", andoidcwd + logfile, hostlogpath], stdout=_stdout, stderr=PIPE).wait() 759 if output != 0: 760 print >> _stderr, "adb finishes unexpectedly with error code", output 761 return 762 #rm log 763 Popen(self.adb + ["shell", "rm " + andoidcwd + logfile], stdout=PIPE, stderr=PIPE).wait() 764 765 # clean temporary files 766 Popen(self.adb + ["shell", "rm " + tempdir + "__opencv_temp.*"], stdout=PIPE, stderr=PIPE).wait() 767 except OSError: 768 pass 769 if os.path.isfile(hostlogpath): 770 return hostlogpath 771 return None 772 elif path == "java": 773 cmd = [self.ant_executable, 774 "-Dopencv.build.type=" 775 + (self.options.configuration if self.options.configuration else self.build_type), 776 "buildAndTest"] 777 778 print >> _stderr, "Run command:", " ".join(cmd) 779 try: 780 errorCode = Popen(cmd, stdout=_stdout, stderr=_stderr, cwd = self.java_test_binary_dir + "/.build").wait() 781 except: 782 print "Unexpected error:", sys.exc_info()[0] 783 784 return None 785 else: 786 cmd = [exe] 787 if self.options.help: 788 cmd.append("--help") 789 else: 790 cmd.extend(args) 791 792 orig_temp_path = os.environ.get('OPENCV_TEMP_PATH') 793 temp_path = tempfile.mkdtemp(prefix="__opencv_temp.", dir=orig_temp_path or None) 794 os.environ['OPENCV_TEMP_PATH'] = temp_path 795 796 print >> _stderr, "Run command:", " ".join(cmd) 797 try: 798 errorCode = Popen(cmd, stdout=_stdout, stderr=_stderr, cwd = workingDir).wait() 799 except: 800 print "Unexpected error:", sys.exc_info()[0] 801 802 # clean temporary files 803 if orig_temp_path: 804 os.environ['OPENCV_TEMP_PATH'] = orig_temp_path 805 else: 806 del os.environ['OPENCV_TEMP_PATH'] 807 808 try: 809 shutil.rmtree(temp_path) 810 pass 811 except: 812 pass 813 814 logpath = os.path.join(workingDir, logfile) 815 if os.path.isfile(logpath): 816 return logpath 817 return None 818 819 def runTests(self, tests, _stdout, _stderr, workingDir, args = []): 820 if not self.isRunnable(): 821 print >> _stderr, "Error:", self.error 822 if self.error: 823 return [] 824 if self.adb and self.targetos == "android": 825 print "adb command:", " ".join(self.adb) 826 if not tests: 827 tests = self.tests 828 logs = [] 829 for test in tests: 830 t = self.getTest(test) 831 if t: 832 logfile = self.runTest(t, workingDir, _stdout, _stderr, args) 833 if logfile: 834 logs.append(os.path.relpath(logfile, ".")) 835 else: 836 print >> _stderr, "Error: Test \"%s\" is not found in %s" % (test, self.tests_dir) 837 return logs 838 839def getRunArgs(args): 840 run_args = [] 841 for path in args: 842 path = os.path.abspath(path) 843 while (True): 844 if os.path.isdir(path) and os.path.isfile(os.path.join(path, "CMakeCache.txt")): 845 run_args.append(path) 846 break 847 npath = os.path.dirname(path) 848 if npath == path: 849 break 850 path = npath 851 return run_args 852 853if hostos == "nt": 854 def moveTests(instance, destination): 855 src = os.path.dirname(instance.tests_dir) 856 # new binaries path 857 newBinPath = os.path.join(destination, "bin") 858 859 try: 860 # copy binaries and CMakeCache.txt to the specified destination 861 shutil.copytree(src, newBinPath) 862 shutil.copy(os.path.join(instance.path, "CMakeCache.txt"), os.path.join(destination, "CMakeCache.txt")) 863 except Exception, e: 864 print "Copying error occurred:", str(e) 865 exit(e.errno) 866 867 # pattern of CMakeCache.txt string to be replaced 868 replacePattern = re.compile("EXECUTABLE_OUTPUT_PATH:PATH=(.+)") 869 870 with open(os.path.join(destination, "CMakeCache.txt"), "r") as cachefile: 871 try: 872 cachedata = cachefile.read() 873 if hostos == 'nt': 874 # fix path slashes on nt systems 875 newBinPath = re.sub(r"\\", r"/", newBinPath) 876 # replace old binaries path in CMakeCache.txt 877 cachedata = re.sub(re.search(replacePattern, cachedata).group(1), newBinPath, cachedata) 878 except Exception, e: 879 print "Reading error occurred:", str(e) 880 exit(e.errno) 881 882 with open(os.path.join(destination, "CMakeCache.txt"), "w") as cachefile: 883 try: 884 cachefile.write(cachedata) 885 except Exception, e: 886 print "Writing error occurred:", str(e) 887 exit(e.errno) 888 exit() 889 890if __name__ == "__main__": 891 test_args = [a for a in sys.argv if a.startswith("--perf_") or a.startswith("--gtest_")] 892 argv = [a for a in sys.argv if not(a.startswith("--perf_") or a.startswith("--gtest_"))] 893 894 parser = OptionParser(usage="run.py [options] [build_path]", description="Note: build_path is required if running not from CMake build directory") 895 parser.add_option("-t", "--tests", dest="tests", help="comma-separated list of modules to test", metavar="SUITS", default="") 896 if hostos == "nt": 897 parser.add_option("-m", "--move_tests", dest="move", help="location to move current tests build", metavar="PATH", default="") 898 parser.add_option("-w", "--cwd", dest="cwd", help="working directory for tests", metavar="PATH", default=".") 899 parser.add_option("-a", "--accuracy", dest="accuracy", help="look for accuracy tests instead of performance tests", action="store_true", default=False) 900 parser.add_option("-l", "--longname", dest="useLongNames", action="store_true", help="generate log files with long names", default=False) 901 parser.add_option("", "--android_test_data_path", dest="test_data_path", help="OPENCV_TEST_DATA_PATH for Android run", metavar="PATH", default="/sdcard/opencv_testdata/") 902 parser.add_option("", "--android_env", dest="android_env_array", help="Environment variable for Android run (NAME=VALUE)", action='append') 903 parser.add_option("", "--android_propagate_opencv_env", dest="android_propagate_opencv_env", help="Propagate OPENCV* environment variables for Android run", action="store_true", default=False) 904 parser.add_option("", "--configuration", dest="configuration", help="force Debug or Release configuration", metavar="CFG", default="") 905 parser.add_option("", "--serial", dest="adb_serial", help="Android: directs command to the USB device or emulator with the given serial number", metavar="serial number", default="") 906 parser.add_option("", "--package", dest="junit_package", help="Android: run jUnit tests for specified package", metavar="package", default="") 907 parser.add_option("", "--help-tests", dest="help", help="Show help for test executable", action="store_true", default=False) 908 parser.add_option("", "--check", dest="check", help="Shortcut for '--perf_min_samples=1 --perf_force_samples=1'", action="store_true", default=False) 909 parser.add_option("", "--list", dest="list", help="List available tests", action="store_true", default=False) 910 911 (options, args) = parser.parse_args(argv) 912 913 if options.accuracy: 914 options.mode = "test" 915 else: 916 options.mode = "perf" 917 918 run_args = getRunArgs(args[1:] or ['.']) 919 920 if len(run_args) == 0: 921 print >> sys.stderr, "Usage:", os.path.basename(sys.argv[0]), "[options] [build_path]" 922 print >> sys.stderr, "Please specify build_path or run script from CMake build directory" 923 exit(1) 924 925 options.android_env = {} 926 if options.android_env_array: 927 for entry in options.android_env_array: 928 k, v = entry.split("=", 1) 929 options.android_env[k] = v 930 931 tests = [s.strip() for s in options.tests.split(",") if s] 932 933 if len(tests) != 1 or len(run_args) != 1: 934 # remove --gtest_output from params 935 test_args = [a for a in test_args if not a.startswith("--gtest_output=")] 936 937 if options.check: 938 if not [a for a in test_args if a.startswith("--perf_min_samples=")] : 939 test_args.extend(["--perf_min_samples=1"]) 940 if not [a for a in test_args if a.startswith("--perf_force_samples=")] : 941 test_args.extend(["--perf_force_samples=1"]) 942 if not [a for a in test_args if a.startswith("--perf_verify_sanity")] : 943 test_args.extend(["--perf_verify_sanity"]) 944 945 logs = [] 946 test_list = [] 947 for path in run_args: 948 suite = TestSuite(options, path) 949 950 if hostos == "nt": 951 if(options.move): 952 moveTests(suite, options.move) 953 #print vars(suite),"\n" 954 if options.list: 955 test_list.extend(suite.tests) 956 else: 957 logs.extend(suite.runTests(tests, sys.stdout, sys.stderr, options.cwd, test_args)) 958 959 if options.list: 960 print os.linesep.join(test_list) or "No tests found" 961 962 if logs: 963 print >> sys.stderr, "Collected: ", " ".join(logs) 964 965 if errorCode != 0: 966 print "Error code: ", errorCode, (" (0x%x)" % (errorCode & 0xffffffff)) 967 exit(errorCode) 968