15 from xml.dom.minidom import parseString
19 tool_base_dir='/var/tmp/tizen-dev-tool'
21 def updateOsceBuildRoot(arch):
22 os.environ['OSCE_BUILD_ROOT'] = tool_base_dir + '/build_root_%s' % arch
24 def getOsceBuildRoot():
25 return os.environ['OSCE_BUILD_ROOT']
27 def getOsceArchType(arch):
34 def getRepoInformation(repo_type, snapshot_base_url, arch):
36 information['repo_url'] = snapshot_base_url + '/repos/' + repo_type
37 information['bin'] = {}
38 information['bin']['db_file_name'] = repo_type + '.sqlite'
39 information['bin']['base_url'] = information['repo_url'] + '/' + arch + '/packages'
40 information['dbg'] = {}
41 information['dbg']['db_file_name'] = repo_type + '-dbg.sqlite'
42 information['dbg']['base_url'] = information['repo_url'] + '/' + arch + '/debug'
45 def guessReleaseTypeWithSnapshotID(snapshot_id):
46 key = snapshot_id.split('_')[0]
47 if key == 'SLPRelease':
51 elif key == 'tizen-SDK':
54 print 'Cannot guess release type with ' + snapshot_id
55 print 'Please update guessReleaseTypeWithSnapshotID() function in ' + this + ' script'
58 def getInformation(snapshot_id, release_type, arch):
60 information['snapshot_id'] = snapshot_id
62 release_type = guessReleaseTypeWithSnapshotID(snapshot_id)
63 information['release_type'] = release_type
64 information['base_dir'] = '%s/%s' % (snapshot_id, arch)
65 information['base_dir_abs'] = tool_base_dir + '/' + information['base_dir']
66 if not os.path.exists(information['base_dir_abs']):
67 os.makedirs(information['base_dir_abs'])
68 if release_type == 'SDK':
69 information['server'] = 'download.tizendev.org'
70 information['snapshot_base_url'] = 'https://%s/snapshots/SDK/common/%s' % (information['server'], information['snapshot_id'])
71 information['repo_type_list'] = ['tizen-base', 'tizen-main']
72 information['auth'] = { 'user': None, 'pass': None }
74 information['server'] = 'slp-build.sec.samsung.net'
75 information['snapshot_base_url'] = 'http://%s:8008/snapshots/%s/%s' % (information['server'], information['release_type'], information['snapshot_id'])
76 information['repo_type_list'] = ['slp-base', 'slp-system', 'slp-%s' % information['release_type']]
77 information['auth'] = None
79 for repo_type in information['repo_type_list']:
80 information[repo_type] = getRepoInformation(repo_type, information['snapshot_base_url'], arch)
83 def needAuth(information):
84 return information['auth']
86 def getUser(information):
87 if not information['auth']:
89 if not information['auth']['user']:
90 print information['server']
91 information['auth']['user'] = raw_input('user : ')
92 information['auth']['pass'] = getpass.getpass('password: ')
93 return information['auth']['user']
95 def getPass(information):
96 if not information['auth']:
98 if not information['auth']['user']:
99 print information['server']
100 information['auth']['user'] = raw_input('user : ')
101 information['auth']['pass'] = getpass.getpass('password: ')
102 return information['auth']['pass']
104 def getDBFilePath(information, repo_type, pkg_type):
105 return information['base_dir_abs'] + '/' + information[repo_type][pkg_type]['db_file_name']
107 def getDB(information, repo_type, pkg_type):
108 if needAuth(information):
109 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm()
110 authinfo.add_password(None, information['server'], getUser(information), getPass(information))
111 handler = urllib2.HTTPBasicAuthHandler(authinfo)
112 myopener = urllib2.build_opener(handler)
113 opened = urllib2.install_opener(myopener)
114 file = urllib2.urlopen(information[repo_type][pkg_type]['base_url'] + '/repodata/repomd.xml')
117 dom = parseString(data)
118 for element in dom.getElementsByTagName('data'):
119 if element.hasAttribute('type') and element.getAttribute('type') == 'primary_db':
120 db_bz2_url = information[repo_type][pkg_type]['base_url'] + '/' + element.getElementsByTagName('location')[0].getAttribute('href')
121 file = urllib2.urlopen(db_bz2_url)
124 file = open(getDBFilePath(information, repo_type, pkg_type), 'wb')
125 file.write(bz2.decompress(data))
130 def downloadPackages(information, repo_type, pkg_type, package_list):
131 conn = sqlite3.connect(getDBFilePath(information, repo_type, pkg_type))
134 for package in package_list:
135 c.execute('SELECT location_href, version, release, arch FROM packages WHERE name = "%s"' % package)
136 result = c.fetchone()
139 package_file_name = package + '-' + result[1] + '-' + result[2] + '.' + result[3] + '.rpm'
140 package_file_path = information['base_dir_abs'] + '/' + package_file_name
141 package_url = information[repo_type][pkg_type]['base_url'] + '/' + result[0]
143 if not os.path.isfile(package_file_path):
144 print 'download ' + package_file_name
145 if needAuth(information):
146 command = 'curl -o%s %s -u%s:%s -k' % (package_file_path, package_url, getUser(information), getPass(information))
148 command = 'curl -o%s %s -k' % (package_file_path, package_url)
149 subprocess.call(command.split())
151 if os.path.isfile(package_file_path):
152 current_dir = os.getcwd()
153 os.chdir(information['base_dir_abs'])
154 print 'extracting ' + package_file_name
155 pipe = subprocess.Popen(['rpm2cpio', package_file_name], stdout=subprocess.PIPE)
156 subprocess.Popen(['cpio', '-mid'], stdin=subprocess.PIPE).communicate(input=pipe.stdout.read())
157 os.chdir(current_dir)
159 downloaded |= set([package])
164 def getPackages(snapshot_id, release_type, arch, package_list):
165 information = getInformation(snapshot_id, release_type, arch)
167 for repo_type in information['repo_type_list']:
168 for pkg_type in ['bin', 'dbg']:
169 if not os.path.isfile(getDBFilePath(information, repo_type, pkg_type)):
170 if not getDB(information, repo_type, pkg_type):
171 print 'Failed to get DB'
173 package_list -= downloadPackages(information, repo_type, pkg_type, package_list)
174 if len(package_list) == 0:
177 print 'Cannot find packages : %r' % package_list
179 def commandGetPkgs(program, args):
180 parser = argparse.ArgumentParser(prog=program, description='Download packages')
181 parser.add_argument('snapshot_id', action='store')
182 parser.add_argument('package_name', nargs='+')
183 parser.add_argument('-a', '--arch', choices=['armv7l', 'ia32'], default='armv7l')
184 parser.add_argument('-t', '--type', choices=['standard', 'release', 'SDK'], dest='release_type', help='release type. If this is not specified, ' + this + 'tries to guess it with snapshot_id')
185 parsedArgs = parser.parse_args(args);
186 getPackages(parsedArgs.snapshot_id, parsedArgs.release_type, parsedArgs.arch, set(parsedArgs.package_name))
188 def getAllPackages(dbfile):
189 conn = sqlite3.connect(dbfile)
192 for row in c.execute('SELECT name, version, release, arch FROM packages'):
193 pkg_list |= set([row[0] + '-' + row[1] + '-' + row[2] + '.' + row[3] + '.rpm'])
197 def listPackages(snapshot_id, release_type, arch):
198 information = getInformation(snapshot_id, release_type, arch)
201 for repo_type in information['repo_type_list']:
202 for pkg_type in ['bin', 'dbg']:
203 dbfile = getDBFilePath(information, repo_type, pkg_type)
204 if not os.path.isfile(dbfile):
205 if not getDB(information, repo_type, pkg_type):
206 print 'Failed to get DB'
208 pkg_list |= getAllPackages(dbfile)
209 for pkg_name in sorted(pkg_list):
213 def commandPkgList(program, args):
214 parser = argparse.ArgumentParser(prog=program, description='Show package list')
215 parser.add_argument('snapshot_id', action='store')
216 parser.add_argument('-a', '--arch', choices=['armv7l', 'ia32'], default='armv7l')
217 parser.add_argument('-t', '--type', choices=['standard', 'release', 'SDK'], dest='release_type', help='release type. If this is not specified, ' + this + 'tries to guess it with snapshot_id')
218 parsedArgs = parser.parse_args(args);
219 listPackages(parsedArgs.snapshot_id, parsedArgs.release_type, parsedArgs.arch)
222 def GT_I8800_e4412(key_list):
223 if key_list[2] >= '20121017' and key_list[2] < '20121024':
226 return 'slp-release.sec.samsung.net', '/pub/GT-I8800_E4412_Release/release/GT-I8800_e4412_%s_1/information' % key_list[2], 'GT-I8800_e4412_%s_1.ks' % key_list[2]
228 def GT_I8800_e4412_cluster(key_list):
229 return 'slp-release.sec.samsung.net', '/pub/GT-I8800_E4412_Release/release/GT-I8800_e4412_%s_1/Redwood_Cluster' % key_list[3], 'GT-I8800_e4412_cluster_%s_1.ks' % key_list[3]
231 def GT_I8800_e4412v34_cluster(key_list):
232 return 'slp-release.sec.samsung.net', '/pub/GT-I8800_E4412_Release/release/GT-I8800_e4412_%s_1/Redwoodv34_Cluster' % key_list[3], 'GT-I8800_e4412v34_cluster_%s_1.ks' % key_list[3]
234 def getKsPathInfo(key_list):
236 search_dict['GT-I8800'] = {}
237 search_dict['GT-I8800']['e4412'] = {'default': GT_I8800_e4412}
238 search_dict['GT-I8800']['e4412']['cluster'] = GT_I8800_e4412_cluster
239 search_dict['GT-I8800']['e4412v34'] = {}
240 search_dict['GT-I8800']['e4412v34']['cluster'] = GT_I8800_e4412v34_cluster
244 if key not in search.keys():
245 if type(search['default']) is FunctionType:
246 return search['default'](key_list)
251 if type(search) is FunctionType:
252 return search(key_list)
256 def findSnapshotID(release_version):
257 ksPathInfo = getKsPathInfo(release_version.split('_'))
259 print 'Have no information of ' + release_version
260 print 'Please update search_dict in the ' + this + ' script'
263 ksdir = tool_base_dir + '/ks'
264 if not os.path.exists(ksdir):
267 ks_server = ksPathInfo[0]
268 ks_server_dir = ksPathInfo[1]
269 ks_file_name = ksPathInfo[2]
271 ks_path = ksdir + '/' + ks_file_name
272 if not os.path.isfile(ks_path):
273 ftp = ftplib.FTP(ks_server)
275 ks_user = raw_input('user : ')
276 ks_pass = getpass.getpass('password: ')
277 ftp.login(ks_user, ks_pass)
279 ftp.retrbinary('RETR %s/%s' % (ks_server_dir, ks_file_name), open(ks_path, 'wb').write)
282 print 'Failed to get %s:%s/%s' % (ks_server, ks_server_dir, ks_file_name)
283 print 'Maybe there is not such path in the server.'
284 print 'Please update search_dict in the ' + this + ' script'
287 for line in open(ks_path).readlines():
288 if line.startswith("repo "):
289 snapshot_id = line.split('/repos/')[0].split('/')[-1]
290 print 'SnapshotID: ' + snapshot_id
294 def commandFindSnapshotID(program, args):
295 parser = argparse.ArgumentParser(prog=program, description='Find repo id from release version')
296 parser.add_argument('release_version', action='store', help='release version of target binary or SDK')
297 parsedArgs = parser.parse_args(args);
298 return findSnapshotID(parsedArgs.release_version)
300 def __findProvides(information, repo_type, pkg_type, found_pkgs, file_path_list):
301 conn = sqlite3.connect(getDBFilePath(information, repo_type, pkg_type))
304 for file_path in file_path_list:
305 file_name = file_path.split('/')[-1]
307 if file_name.endswith('.so'):
310 file_token = file_name.split('.so.')
311 if len(file_token) is 2:
313 file_name = file_token[0] + '.so.' + file_token[1].split('.')[0]
316 for row in c.execute('select provides.name, packages.name from provides, packages where provides.name = "' + file_name + '" and provides.pkgKey == packages.pkgKey;'):
317 if row[0] not in found_pkgs.keys():
318 found_pkgs[row[0]] = set()
319 found_pkgs[row[0]] |= set([row[1]])
320 found_files |= set([file_path])
322 for row in c.execute('select files.name, packages.name from files, packages where files.name = "' + file_path + '" and files.pkgKey == packages.pkgKey;'):
323 if row[0] not in found_pkgs.keys():
324 found_pkgs[row[0]] = set()
325 found_pkgs[row[0]] |= set([row[1]])
326 found_files |= set([file_path])
330 def _findProvides(information, file_path_list):
333 for repo_type in information['repo_type_list']:
334 for pkg_type in ['bin', 'dbg']:
335 dbfile = getDBFilePath(information, repo_type, pkg_type)
336 if not os.path.isfile(dbfile):
337 if not getDB(information, repo_type, pkg_type):
338 print 'Failed to get DB'
340 found_files |= __findProvides(information, repo_type, pkg_type, found_pkgs, file_path_list)
341 return found_pkgs, found_files
344 def findProvides(snapshot_id, release_type, arch, file_path_list):
345 information = getInformation(snapshot_id, release_type, arch)
347 ret = _findProvides(information, file_path_list)
349 for key in sorted(ret[0].keys()):
350 for package_name in sorted(ret[0][key]):
351 print key + ' is provided by [' + package_name + ']'
353 file_path_list -= ret[1]
354 if len(file_path_list) > 0:
355 print 'Cannot find provider for %r' % sorted(file_path_list)
357 def commandProvides(program, args):
358 parser = argparse.ArgumentParser(prog=program, description='Find package that provides certain library')
359 parser.add_argument('snapshot_id', action='store')
360 parser.add_argument('file_path', nargs='+')
361 parser.add_argument('-a', '--arch', choices=['armv7l', 'ia32'], default='armv7l')
362 parser.add_argument('-t', '--type', choices=['standard', 'release', 'SDK'], dest='release_type', help='release type. If this is not specified, ' + this + 'tries to guess it with snapshot_id')
363 parsedArgs = parser.parse_args(args);
364 findProvides(parsedArgs.snapshot_id, parsedArgs.release_type, parsedArgs.arch, set(parsedArgs.file_path))
366 def findPID(parse_dict, line):
367 if parse_dict['FindPID']['finished']:
369 if line.startswith('Pid :') or line.startswith('pid :'):
370 parse_dict['FindPID']['finished'] = True
371 parse_dict['FindPID']['result'] = line.split()[-1]
373 def findExecPath(parse_dict, line):
374 if parse_dict['FindExecPath']['finished']:
376 if line.startswith('ExePath:'):
377 parse_dict['FindExecPath']['finished'] = True
378 parse_dict['FindExecPath']['result'] = line.split()[-1]
379 elif parse_dict['FindMapsStart']['finished'] and len(line.split()) is 4:
380 parse_dict['FindExecPath']['finished'] = True
381 parse_dict['FindExecPath']['result'] = line.split()[3]
383 def findMapsStart(parse_dict, line):
384 if parse_dict['FindMapsStart']['finished']:
386 if line.startswith('maps information'):
387 parse_dict['FindMapsStart']['finished'] = True
389 def findReleaseVersion(parse_dict, line):
390 if parse_dict['FindReleaseVersion']['finished']:
392 if line.startswith('Build='):
393 parse_dict['FindReleaseVersion']['finished'] = True
394 parse_dict['FindReleaseVersion']['result'] = line.split('=')[1].split(';')[0]
396 def findRelatedFiles(parse_dict, line):
397 if parse_dict['FindRelatedFiles']['finished']:
400 if line.startswith('end of maps information'):
401 parse_dict['FindRelatedFiles']['finished'] = True
403 line_tokens = line.split()
404 if parse_dict['FindMapsStart']['finished'] and len(line_tokens) is 4:
405 if line_tokens[3].startswith('['):
407 if parse_dict['FindRelatedFiles']['result'] is None:
408 parse_dict['FindRelatedFiles']['result'] = []
410 parse_dict['FindRelatedFiles']['result'].append(line_tokens[3])
412 def parseCSFile(cs_file):
414 parse_dict['FindPID'] = {'finished': False, 'function': findPID, 'result': None}
415 parse_dict['FindExecPath'] = {'finished': False, 'function': findExecPath, 'result': None}
416 parse_dict['FindMapsStart'] = {'finished': False, 'function': findMapsStart}
417 parse_dict['FindReleaseVersion'] = {'finished': False, 'function': findReleaseVersion, 'result': None}
418 parse_dict['FindRelatedFiles'] = {'finished': False, 'function': findRelatedFiles, 'result': None}
420 if not os.path.isfile(cs_file):
421 print cs_file + ' is not valid file'
424 for line in open(cs_file).readlines():
425 for key in parse_dict.keys():
426 parse_dict[key]['function'](parse_dict, line)
428 if not parse_dict['FindPID']['result']:
429 print 'Failed to get PID'
430 print 'Please update parse_dict in ' + this + ' script'
433 if not parse_dict['FindExecPath']['result']:
434 print 'Failed to get exec path'
435 print 'Please update parse_dict in ' + this + ' script'
438 if not parse_dict['FindReleaseVersion']['result']:
439 print 'Failed to get release version'
440 print 'Please update parse_dict in ' + this + ' script'
443 print 'Pid: ' + parse_dict['FindPID']['result']
444 print 'ExecPath: ' + parse_dict['FindExecPath']['result']
445 print 'ReleaseVersion: ' + parse_dict['FindReleaseVersion']['result']
446 #print 'RelatedFiles: %r' % parse_dict['FindRelatedFiles']['result']
448 return parse_dict['FindPID']['result'], parse_dict['FindExecPath']['result'], parse_dict['FindReleaseVersion']['result'], parse_dict['FindRelatedFiles']['result']
450 def commandCsInfo(program, args):
451 parser = argparse.ArgumentParser(prog=program)
452 parser.add_argument('cs_file', action='store')
453 parsedArgs = parser.parse_args(args);
454 return parseCSFile(parsedArgs.cs_file)
458 updateOsceBuildRoot(arch)
459 getbt_dir = tool_base_dir + '/getbt'
460 current_dir = os.getcwd()
462 subprocess.call(['osce', 'chroot', getOsceArchType(arch)])
463 os.chdir(current_dir)
465 def commandChroot(program, args):
466 parser = argparse.ArgumentParser(prog=program)
467 parser.add_argument('-a', '--arch', choices=['armv7l', 'ia32'], default='armv7l')
468 parsedArgs = parser.parse_args(args);
469 doChroot(parsedArgs.arch)
471 def getBackTrace(snapshot_id, release_type, arch, core_file, exec_path, force_copy, related_files):
472 if not os.path.isfile(core_file):
473 print core_file + ' is not valid file'
475 core_file_name = core_file.split('/')[-1]
477 information = getInformation(snapshot_id, release_type, arch)
479 if not os.path.isfile(information['base_dir_abs'] + exec_path):
480 print 'executive file is not exists : ' + information['base_dir_abs'] + exec_path
481 ret = _findProvides(information, [exec_path])
483 print 'please install ' + string.join(ret[0][exec_path]) + ' with below command'
484 print ' ' + this + ' getpkgs ' + snapshot_id + ' -a' + arch + ' ' + string.join(ret[0][exec_path])
486 print 'cannot find package that provides ' + exec_path
488 if len(related_files) > 0:
489 print '\nrelated packages from memory map'
490 ret = _findProvides(information, related_files)
492 for pkg_name_set in ret[0].values():
493 for pkg_name in pkg_name_set:
494 pkg_name_list.append(pkg_name)
495 print string.join(sorted(pkg_name_list))
498 getbt_dir = tool_base_dir + '/getbt'
499 if not os.path.exists(getbt_dir):
500 os.makedirs(getbt_dir)
502 current_dir = os.getcwd()
505 if not os.path.exists('.git'):
508 if not os.path.exists('packaging'):
509 os.makedirs('packaging')
511 updateOsceBuildRoot(arch)
513 package_file = 'packaging/webkit2-efl.spec'
514 if not os.path.exists(package_file):
520 'Group: System/Libraries',
522 'Source0: %{name}-%{version}.tar.gz',
524 'BuildRequires: gdb cmake python bison flex gperf perl gettext-tools vi',
527 'Browser Engine based on Webkit2 EFL (Shared Library)',
533 'echo Finished to create getbt environment',
535 file = open(package_file, 'w')
536 for line in spec_data:
541 if not os.path.exists(getOsceBuildRoot()):
542 subprocess.call(['osce', 'build', getOsceArchType(arch)])
543 os.chdir(current_dir)
545 osce_home_abuild = getOsceBuildRoot() + '/home/abuild'
546 subprocess.call(['sudo', 'mkdir', '-p', osce_home_abuild + '/' + snapshot_id])
548 if force_copy or not os.path.exists(osce_home_abuild + '/' + information['base_dir']):
549 print 'Coping snapshot data to build root'
550 subprocess.call(['sudo', 'cp', '-rfa', information['base_dir_abs'], osce_home_abuild + '/' + snapshot_id + '/'])
552 subprocess.call(['sudo', 'cp', core_file, osce_home_abuild])
554 gdb_sysroot = '/home/abuild/' + information['base_dir']
555 file = open('/tmp/cmd', 'w')
557 'exec-file ' + gdb_sysroot + exec_path,
558 'set sysroot ' + gdb_sysroot,
559 'set debug-file-directory ' + gdb_sysroot + '/usr/lib/debug',
560 'set substitute-path /usr/src ' + gdb_sysroot + '/usr/src',
561 'core-file ' + core_file_name,
564 for line in cmd_data:
569 subprocess.call(['sudo', 'cp', '/tmp/cmd', osce_home_abuild])
571 print '\n\n====================================================================='
572 print '- type \'gdb -x cmd\' to open ' + core_file_name + ' with gdb'
573 print '=====================================================================\n\n'
577 def commandGetBt(program, args):
578 parser = argparse.ArgumentParser(prog=program)
579 parser.add_argument('snapshot_id', action='store')
580 parser.add_argument('core_file', action='store')
581 parser.add_argument('exec_path', action='store')
582 parser.add_argument('-a', '--arch', choices=['armv7l', 'ia32'], default='armv7l')
583 parser.add_argument('-t', '--type', choices=['standard', 'release', 'SDK'], dest='release_type', help='release type. If this is not specified, ' + this + 'tries to guess it with snapshot_id')
584 parser.add_argument('-f', '--force', dest='force_copy', default=False, action='store_true', help='Copy snapshot data to the qemu forcely. This will take some time, but this need to be set when new packages are downloaded.')
585 parsedArgs = parser.parse_args(args);
586 getBackTrace(parsedArgs.snapshot_id, parsedArgs.release_type, parsedArgs.arch, parsedArgs.core_file, parsedArgs.exec_path, parsedArgs.force_copy, None)
589 def checkCrash(arch, core_file, cs_file, force_copy):
590 if not os.path.isfile(core_file):
591 print core_file + ' is not valid file'
593 result = parseCSFile(cs_file)
595 print 'Failed to parse cs file'
598 crash_pid = result[0]
599 exec_path = result[1]
600 release_version = result[2]
601 related_files = result[3]
603 core_file_name = 'core.' + crash_pid
604 if core_file.split('/')[-1] != core_file_name:
605 print 'core file is not matched with cs file'
608 snapshot_id = findSnapshotID(release_version)
610 print 'Failed to find snapshot ID with ' + release_version
613 getBackTrace(snapshot_id, None, arch, core_file, exec_path, force_copy, set(related_files))
616 def commandCrash(program, args):
617 parser = argparse.ArgumentParser(prog=program)
618 parser.add_argument('core_file', action='store')
619 parser.add_argument('cs_file', action='store')
620 parser.add_argument('-a', '--arch', choices=['armv7l', 'ia32'], default='armv7l')
621 parser.add_argument('-f', '--force', dest='force_copy', default=False, action='store_true', help='Copy snapshot data to the qemu forcely. This will take some time, but this need to be set when new packages are downloaded.')
622 parsedArgs = parser.parse_args(args);
623 checkCrash(parsedArgs.arch, parsedArgs.core_file, parsedArgs.cs_file, parsedArgs.force_copy)
627 commandMap['getpkgs'] = commandGetPkgs
628 commandMap['pkglist'] = commandPkgList
629 commandMap['findsnapshot'] = commandFindSnapshotID
630 commandMap['provides'] = commandProvides
631 commandMap['csinfo'] = commandCsInfo
632 commandMap['getbt'] = commandGetBt
633 commandMap['crash'] = commandCrash
634 commandMap['chroot'] = commandChroot
636 parser = argparse.ArgumentParser()
637 parser.add_argument('command', choices=commandMap.keys(), help='sub command. Type ' + this + ' <sub command> -h for help')
638 parser.add_argument('args', nargs=argparse.REMAINDER, help='arguments of command')
639 parsedArgs = parser.parse_args()
640 commandMap[parsedArgs.command](this + ' ' + parsedArgs.command, parsedArgs.args)
643 if __name__ == '__main__':