def _ParseManifestFromApk(apk_path): aapt_cmd = [_AAPT_PATH, 'dump', 'xmltree', apk_path, 'AndroidManifest.xml'] aapt_output = cmd_helper.GetCmdOutput(aapt_cmd).split('\n') parsed_manifest = {} node_stack = [parsed_manifest] indent = ' ' for line in aapt_output[1:]: if len(line) == 0: continue indent_depth = 0 while line[(len(indent) * indent_depth):].startswith(indent): indent_depth += 1 node_stack = node_stack[:indent_depth] node = node_stack[-1] m = _MANIFEST_ELEMENT_RE.match(line[len(indent) * indent_depth:]) if m: if not m.group(1) in node: node[m.group(1)] = {} node_stack += [node[m.group(1)]] continue m = _MANIFEST_ATTRIBUTE_RE.match(line[len(indent) * indent_depth:]) if m: if not m.group(1) in node: node[m.group(1)] = [] node[m.group(1)].append(m.group(2)) continue return parsed_manifest
def _get_svn_revision(self, in_directory): """Returns the git/svn revision for the given directory. Args: in_directory: The directory relative to src. """ def _is_git_directory(in_directory): """Returns true if the given directory is in a git repository. Args: in_directory: The directory path to be tested. """ if os.path.exists(os.path.join(in_directory, '.git')): return True parent = os.path.dirname(in_directory) if parent == constants.DIR_SOURCE_ROOT or parent == in_directory: return False return _is_git_directory(parent) in_directory = os.path.join(constants.DIR_SOURCE_ROOT, in_directory) if not os.path.exists(os.path.join(in_directory, '.svn')): if _is_git_directory(in_directory): return repo_utils.GetGitHeadSHA1(in_directory) else: return '' output = cmd_helper.GetCmdOutput(['svn', 'info', '--xml'], cwd=in_directory) try: dom = xml.dom.minidom.parseString(output) return dom.getElementsByTagName('entry')[0].getAttribute('revision') except xml.parsers.expat.ExpatError: return '' return ''
def _GetChromeClasses(): path = constants.GetOutDirectory() cmd = 'find %s -name "*.class"' % path out = cmd_helper.GetCmdOutput(shlex.split(cmd)) if not out: print 'No classes found in %s' % path return out
def TakeScreenshot(self, identifier_mark): """Takes a screen shot from current specified device. Args: identifier_mark: A string to identify the screen shot DebugInfo will take. It will be part of filename of the screen shot. Empty string is acceptable. Returns: Returns the file name on the host of the screenshot if successful, None otherwise. """ assert isinstance(identifier_mark, str) screenshot_path = os.path.join(os.getenv('ANDROID_HOST_OUT', ''), 'bin', 'screenshot2') if not os.path.exists(screenshot_path): logging.error('Failed to take screen shot from device %s', self.device) return None shot_path = os.path.join(self.log_dir, ''.join([self._GeneratePrefixName(), identifier_mark, '_screenshot.png'])) re_success = re.compile(re.escape('Success.'), re.MULTILINE) if re_success.findall(cmd_helper.GetCmdOutput([screenshot_path, '-s', self.device, shot_path])): logging.info('Successfully took a screen shot to %s', shot_path) return shot_path logging.error('Failed to take screen shot from device %s', self.device) return None
def lsusb(): """Call lsusb and return the parsed output.""" lsusb_raw_output = cmd_helper.GetCmdOutput(['lsusb', '-v']) device = None devices = [] depth_stack = [] for line in lsusb_raw_output.splitlines(): if not line: if device: devices.append(device) device = None continue if not device: m = _LSUSB_BUS_DEVICE_RE.match(line) if m: device = { 'bus': m.group(1), 'device': m.group(2) } depth_stack = [device] continue indent_match = _INDENTATION_RE.match(line) if not indent_match: continue depth = 1 + len(indent_match.group(1)) / 2 if depth > len(depth_stack): logging.error('lsusb parsing error: unexpected indentation: "%s"', line) continue while depth < len(depth_stack): depth_stack.pop() cur = depth_stack[-1] m = _LSUSB_GROUP_RE.match(line) if m: new_group = {} cur[m.group(1)] = new_group depth_stack.append(new_group) continue m = _LSUSB_ENTRY_RE.match(line) if m: new_entry = { '_value': m.group(2), '_desc': m.group(3), } cur[m.group(1)] = new_entry depth_stack.append(new_entry) continue logging.error('lsusb parsing error: unrecognized line: "%s"', line) if device: devices.append(device) return devices
def _RunATraceCommand(self, command): # We use a separate interface to adb because the one from AndroidCommands # isn't re-entrant. device = ['-s', self._adb.GetDevice()] if self._adb.GetDevice() else [] cmd = ['adb'] + device + ['shell', 'atrace', '--%s' % command] + \ _SYSTRACE_OPTIONS + self._categories return cmd_helper.GetCmdOutput(cmd)
def _RunAdbShellCommand(self, command): # We use a separate interface to adb because the one from AndroidCommands # isn't re-entrant. # TODO(jbudorick) Look at providing a way to unhandroll this once the # adb rewrite has fully landed. device_param = (['-s', str(self._device)] if str(self._device) else []) cmd = ['adb'] + device_param + ['shell'] + command return cmd_helper.GetCmdOutput(cmd)
def GetGitHeadSHA1(in_directory): """Returns the git hash tag for the given directory. Args: in_directory: The directory where git is to be run. """ command_line = ['git', 'log', '-1', '--pretty=format:%H'] output = cmd_helper.GetCmdOutput(command_line, cwd=in_directory) return output[0:40]
def _RunATraceCommand(self, command): # TODO(jbudorick) can this be made work with DeviceUtils? # We use a separate interface to adb because the one from AndroidCommands # isn't re-entrant. device_param = (['-s', self._device.old_interface.GetDevice()] if self._device.old_interface.GetDevice() else []) cmd = ['adb'] + device_param + ['shell', 'atrace', '--%s' % command] + \ _SYSTRACE_OPTIONS + self._categories return cmd_helper.GetCmdOutput(cmd)
def _GetChromeClasses(release_version): version = 'Debug' if release_version: version = 'Release' path = os.path.join(constants.DIR_SOURCE_ROOT, 'out', version) cmd = 'find %s -name "*.class"' % path out = cmd_helper.GetCmdOutput(shlex.split(cmd)) if not out: print 'No classes found in %s' % path return out
def GetPackageNameForApk(apk_path): """Returns the package name of the apk file.""" aapt_output = cmd_helper.GetCmdOutput( ['aapt', 'dump', 'badging', apk_path]).split('\n') package_name_re = re.compile(r'package: .*name=\'(\S*)\'') for line in aapt_output: m = package_name_re.match(line) if m: return m.group(1) raise Exception('Failed to determine package name of %s' % apk_path)
def GetPackageName(apk_path): """Returns the package name of the apk.""" aapt = os.path.join(constants.ANDROID_SDK_TOOLS, 'aapt') aapt_output = cmd_helper.GetCmdOutput([aapt, 'dump', 'badging', apk_path]).split('\n') package_name_re = re.compile(r'package: .*name=\'(\S*)\'') for line in aapt_output: m = package_name_re.match(line) if m: return m.group(1) raise Exception('Failed to determine package name of %s' % apk_path)
def _KillPendingServers(): for retry in range(5): for server in ['lighttpd', 'web-page-replay']: pids = cmd_helper.GetCmdOutput(['pgrep', '-f', server]) pids = [pid.strip() for pid in pids.split('\n') if pid.strip()] for pid in pids: try: logging.warning('Killing %s %s', server, pid) os.kill(int(pid), signal.SIGQUIT) except Exception as e: logging.warning('Failed killing %s %s %s', server, pid, e)
def _GetChromeJars(release_version): version = 'Debug' if release_version: version = 'Release' path = os.path.join(constants.DIR_SOURCE_ROOT, 'out', version, 'lib.java') cmd = 'find %s -name "*.jar"' % path out = cmd_helper.GetCmdOutput(shlex.split(cmd)) out = [p for p in out.splitlines() if not p.endswith('.dex.jar')] if not out: print 'No classes found in %s' % path return ' '.join(out)
def Run(exclude, classes_to_analyze, auxiliary_classes, output_file, findbug_args, jars): """Run FindBugs. Args: exclude: the exclude xml file, refer to FindBugs's -exclude command option. classes_to_analyze: the list of classes need to analyze, refer to FindBug's -onlyAnalyze command line option. auxiliary_classes: the classes help to analyze, refer to FindBug's -auxclasspath command line option. output_file: An optional path to dump XML results to. findbug_args: A list of addtional command line options to pass to Findbugs. """ # TODO(jbudorick): Get this from the build system. system_classes = [ os.path.join(constants.ANDROID_SDK_ROOT, 'platforms', 'android-%s' % constants.ANDROID_SDK_VERSION, 'android.jar') ] system_classes.extend( os.path.abspath(classes) for classes in auxiliary_classes or []) cmd = [ 'java', '-classpath', '%s:' % _FINDBUGS_JAR, '-Xmx%dm' % _FINDBUGS_MAX_HEAP, '-Dfindbugs.home="%s"' % _FINDBUGS_HOME, '-jar', _FINDBUGS_JAR, '-textui', '-sortByClass', '-pluginList', _FINDBUGS_PLUGIN_PATH, '-xml:withMessages' ] if system_classes: cmd.extend(['-auxclasspath', ':'.join(system_classes)]) if classes_to_analyze: cmd.extend(['-onlyAnalyze', classes_to_analyze]) if exclude: cmd.extend(['-exclude', os.path.abspath(exclude)]) if output_file: cmd.extend(['-output', output_file]) if findbug_args: cmd.extend(findbug_args) cmd.extend(os.path.abspath(j) for j in jars or []) if output_file: cmd_helper.RunCmd(cmd) results_doc = xml.dom.minidom.parse(output_file) else: raw_out = cmd_helper.GetCmdOutput(cmd) results_doc = xml.dom.minidom.parseString(raw_out) current_warnings_set = _ParseXmlResults(results_doc) return (' '.join(cmd), current_warnings_set)
def DeleteAllTempAVDs(): """Delete all temporary AVDs which are created for tests. If the test exits abnormally and some temporary AVDs created when testing may be left in the system. Clean these AVDs. """ avds = android_commands.GetAVDs() if not avds: return for avd_name in avds: if 'run_tests_avd' in avd_name: cmd = ['android', '-s', 'delete', 'avd', '--name', avd_name] cmd_helper.GetCmdOutput(cmd) logging.info('Delete AVD %s' % avd_name)
def GetPackageName(self): """Returns the package name of the apk.""" if self._package_name: return self._package_name aapt_cmd = [_AAPT_PATH, 'dump', 'badging', self._apk_path] aapt_output = cmd_helper.GetCmdOutput(aapt_cmd).split('\n') package_name_re = re.compile(r'package: .*name=\'(\S*)\'') for line in aapt_output: m = package_name_re.match(line) if m: self._package_name = m.group(1) return self._package_name raise Exception('Failed to determine package name of %s' % self._apk_path)
def CalculateHostMd5Sums(paths): """Calculates the MD5 sum value for all items in |paths|. Args: paths: A list of host paths to md5sum. Returns: A list of named tuples with 'hash' and 'path' attributes. """ if isinstance(paths, basestring): paths = [paths] out = cmd_helper.GetCmdOutput( [os.path.join(constants.GetOutDirectory(), 'md5sum_bin_host')] + [p for p in paths]) return [HashAndPath(*l.split(None, 1)) for l in out.splitlines()]
def _CopyTemplateFilesAndClearDir(self): """Copy files needed to build a new apk. Uses rsync to avoid unnecessary io. This call also clears outstanding files in the directory. """ srcdir = os.path.abspath(os.path.dirname(__file__)) destdir = self._output_directory if not os.path.exists(destdir): os.makedirs(destdir) elif not '/out/' in destdir: raise Exception('Unbelievable output directory; bailing for safety') logging.warning('rsync %s --> %s', self._SOURCE_FILES, destdir) logging.info(cmd_helper.GetCmdOutput( ['rsync', '-aRv', '--delete', '--exclude', '.svn'] + self._SOURCE_FILES + [destdir], cwd=srcdir))
def GetAVDs(): """Returns a list of Android Virtual Devices. Returns: A list containing the configured AVDs. """ lines = cmd_helper.GetCmdOutput([ os.path.join(constants.ANDROID_SDK_ROOT, 'tools', 'android'), 'list', 'avd']).splitlines() avds = [] for line in lines: if 'Name:' not in line: continue key, value = (s.strip() for s in line.split(':', 1)) if key == 'Name': avds.append(value) return avds
def _KillAllEmulators(): """Kill all running emulators that look like ones we started. There are odd 'sticky' cases where there can be no emulator process running but a device slot is taken. A little bot trouble and and we're out of room forever. """ emulators = android_commands.GetEmulators() if not emulators: return for emu_name in emulators: cmd_helper.GetCmdOutput(['adb', '-s', emu_name, 'emu', 'kill']) logging.info('Emulator killing is async; give a few seconds for all to die.') for i in range(5): if not android_commands.GetEmulators(): return time.sleep(1)
def _get_svn_revision(self, in_directory): """Returns the git/svn revision for the given directory. Args: in_directory: The directory relative to src. """ def _is_git_directory(in_directory): """Returns true if the given directory is in a git repository. Args: in_directory: The directory path to be tested. """ if os.path.exists(os.path.join(in_directory, '.git')): return True parent = os.path.dirname(in_directory) if parent == constants.CHROME_DIR or parent == in_directory: return False return _is_git_directory(parent) def _get_git_revision(in_directory): """Returns the git hash tag for the given directory. Args: in_directory: The directory where git is to be run. """ command_line = ['git', 'log', '-1', '--pretty=format:%H'] output = cmd_helper.GetCmdOutput(command_line, cwd=in_directory) return output[0:40] in_directory = os.path.join(constants.CHROME_DIR, in_directory) if not os.path.exists(os.path.join(in_directory, '.svn')): if _is_git_directory(in_directory): return _get_git_revision(in_directory) else: return '' output = cmd_helper.GetCmdOutput(['svn', 'info', '--xml'], cwd=in_directory) try: dom = xml.dom.minidom.parseString(output) return dom.getElementsByTagName('entry')[0].getAttribute( 'revision') except xml.parsers.expat.ExpatError: return '' return ''
def CalculateHostMd5Sums(paths): """Calculates the MD5 sum value for all items in |paths|. Args: paths: A list of host paths to md5sum. Returns: A dict mapping paths to their respective md5sum checksums. """ if isinstance(paths, basestring): paths = [paths] md5sum_bin_host_path = os.path.join(constants.GetOutDirectory(), 'md5sum_bin_host') if not os.path.exists(md5sum_bin_host_path): raise IOError('File not built: %s' % md5sum_bin_host_path) out = cmd_helper.GetCmdOutput([md5sum_bin_host_path] + [p for p in paths]) return _ParseMd5SumOutput(out.splitlines())
def IsHostPortUsed(host_port): """Checks whether the specified host port is used or not. Uses -n -P to inhibit the conversion of host/port numbers to host/port names. Args: host_port: Port on host we want to check. Returns: True if the port on host is already used, otherwise returns False. """ port_info = '(\*)|(127\.0\.0\.1)|(localhost):%d' % host_port # TODO(jnd): Find a better way to filter the port. Note that connecting to the # socket and closing it would leave it in the TIME_WAIT state. Setting # SO_LINGER on it and then closing it makes the Python HTTP server crash. re_port = re.compile(port_info, re.MULTILINE) if re_port.search(cmd_helper.GetCmdOutput(['lsof', '-nPi:%d' % host_port])): return True return False
def _KillPendingServers(): for retry in range(5): for server in ['lighttpd', 'web-page-replay']: pids = cmd_helper.GetCmdOutput(['pgrep', '-f', server]) pids = [pid.strip() for pid in pids.split('\n') if pid.strip()] for pid in pids: try: logging.warning('Killing %s %s', server, pid) os.kill(int(pid), signal.SIGQUIT) except Exception as e: logging.warning('Failed killing %s %s %s', server, pid, e) # Restart the adb server with taskset to set a single CPU affinity. cmd_helper.RunCmd(['adb', 'kill-server']) cmd_helper.RunCmd(['taskset', '-c', '0', 'adb', 'start-server']) cmd_helper.RunCmd(['taskset', '-c', '0', 'adb', 'root']) i = 1 while not android_commands.GetAttachedDevices(): time.sleep(i) i *= 2 if i > 10: break
def main(argv): # ANDROID_SDK_ROOT needs to be set to the location of the SDK used to launch # the emulator to find the system images upon launch. emulator_sdk = os.path.join(constants.EMULATOR_SDK_ROOT, 'sdk') os.environ['ANDROID_SDK_ROOT'] = emulator_sdk opt_parser = optparse.OptionParser(description='AVD script.') opt_parser.add_option( '--name', help='Optinaly, name of existing AVD to ' 'launch. If not specified, new AVD\'s will be created') opt_parser.add_option('-n', '--num', dest='emulator_count', help='Number of emulators to launch (default is 1).', type='int', default='1') opt_parser.add_option( '--abi', default='x86', help='Platform of emulators to launch (x86 default).') opt_parser.add_option( '--api-level', dest='api_level', help='API level for the image, e.g. 19 for Android 4.4', type='int', default=constants.ANDROID_SDK_VERSION) options, _ = opt_parser.parse_args(argv[1:]) logging.basicConfig(level=logging.INFO, format='# %(asctime)-15s: %(message)s') logging.root.setLevel(logging.INFO) # Check if KVM is enabled for x86 AVD's and check for x86 system images. # TODO(andrewhayden) Since we can fix all of these with install_emulator_deps # why don't we just run it? if options.abi == 'x86': if not install_emulator_deps.CheckKVM(): logging.critical( 'ERROR: KVM must be enabled in BIOS, and installed. ' 'Enable KVM in BIOS and run install_emulator_deps.py') return 1 elif not install_emulator_deps.CheckX86Image(options.api_level): logging.critical( 'ERROR: System image for x86 AVD not installed. Run ' 'install_emulator_deps.py') return 1 if not install_emulator_deps.CheckSDK(): logging.critical('ERROR: Emulator SDK not installed. Run ' 'install_emulator_deps.py.') return 1 # If AVD is specified, check that the SDK has the required target. If not, # check that the SDK has the desired target for the temporary AVD's. api_level = options.api_level if options.name: android = os.path.join(constants.EMULATOR_SDK_ROOT, 'sdk', 'tools', 'android') avds_output = cmd_helper.GetCmdOutput([android, 'list', 'avd']) names = re.findall('Name: (\w+)', avds_output) api_levels = re.findall('API level (\d+)', avds_output) try: avd_index = names.index(options.name) except ValueError: logging.critical('ERROR: Specified AVD %s does not exist.' % options.name) return 1 api_level = int(api_levels[avd_index]) if not install_emulator_deps.CheckSDKPlatform(api_level): logging.critical( 'ERROR: Emulator SDK missing required target for API %d. ' 'Run install_emulator_deps.py.') return 1 if options.name: emulator.LaunchEmulator(options.name, options.abi) else: emulator.LaunchTempEmulators(options.emulator_count, options.abi, options.api_level, True)
def _GetProguardData(self): proguard_output = cmd_helper.GetCmdOutput([ self._PROGUARD_PATH, '-injars', self._jar_path, '-dontshrink', '-dontoptimize', '-dontobfuscate', '-dontpreverify', '-dump', ]).split('\n') clazz = None method = None annotation = None has_value = False qualified_method = None for line in proguard_output: m = self._PROGUARD_CLASS_RE.match(line) if m: clazz = m.group(1).replace('/', '.') # Change package delim. annotation = None continue m = self._PROGUARD_METHOD_RE.match(line) if m: method = m.group(1) annotation = None qualified_method = clazz + '#' + method if method.startswith('test') and clazz.endswith('Test'): self._test_methods += [qualified_method] continue if not qualified_method: # Ignore non-method annotations. continue m = self._PROGUARD_ANNOTATION_RE.match(line) if m: annotation = m.group(1).split('/')[ -1] # Ignore the annotation package. self._annotation_map[qualified_method].append(annotation) has_value = False continue if annotation: if not has_value: m = self._PROGUARD_ANNOTATION_CONST_RE.match(line) if m: has_value = True else: m = self._PROGUARD_ANNOTATION_VALUE_RE.match(line) if m: value = m.group(1) self._annotation_map[qualified_method].append( annotation + ':' + value) has_value = False logging.info('Storing proguard output to %s', self._pickled_proguard_name) d = { 'VERSION': PICKLE_FORMAT_VERSION, 'ANNOTATION_MAP': self._annotation_map, 'TEST_METHODS': self._test_methods } with open(self._pickled_proguard_name, 'w') as f: f.write(pickle.dumps(d))
def testSingleQuote_doExpand(self): test_string = 'hello $TEST_VAR' cmd = 'TEST_VAR=world; echo %s' % cmd_helper.DoubleQuote(test_string) self.assertEquals('hello world', cmd_helper.GetCmdOutput(cmd, shell=True).rstrip())