def UpdateDependency(dependency_name, dependency_info, local_path, platform): bucket = dependency_info['cloud_storage_bucket'] folder = dependency_info['cloud_storage_base_folder'] # determine the hash ec, sha1sum_output = cmd_helper.GetCmdStatusAndOutput( ['sha1sum', local_path]) if ec: raise base_error.BaseError('Failed to determine SHA1 for %s: %s' % (local_path, sha1sum_output)) dependency_sha1 = sha1sum_output.split()[0] # upload remote_path = '%s_%s' % (dependency_name, dependency_sha1) gs_dest = 'gs://%s/%s/%s' % (bucket, folder, remote_path) ec, gsutil_output = cmd_helper.GetCmdStatusAndOutput( ['gsutil.py', 'cp', local_path, gs_dest]) if ec: raise base_error.BaseError('Failed to upload %s to %s: %s' % (remote_path, gs_dest, gsutil_output)) # update entry in json file_info = dependency_info['file_info'] if platform not in file_info: file_info[platform] = { 'cloud_storage_hash': '', # the user will need to manually update the download path after # uploading a previously unknown dependency. 'download_path': 'FIXME', } file_info[platform]['cloud_storage_hash'] = dependency_sha1
def _ParseManifestFromApk(apk_path): aapt_output = aapt.Dump('xmltree', apk_path, 'AndroidManifest.xml') parsed_manifest = {} node_stack = [parsed_manifest] indent = ' ' if aapt_output[0].startswith('N'): # if the first line is a namespace then the root manifest is indented, and # we need to add a dummy namespace node, then skip the first line (we dont # care about namespaces). node_stack.insert(0, {}) output_to_parse = aapt_output[1:] else: output_to_parse = aapt_output for line in output_to_parse: if len(line) == 0: continue # If namespaces are stripped, aapt still outputs the full url to the # namespace and appends it to the attribute names. line = line.replace('http://schemas.android.com/apk/res/android:', 'android:') indent_depth = 0 while line[(len(indent) * indent_depth):].startswith(indent): indent_depth += 1 # Pop the stack until the height of the stack is the same is the depth of # the current line within the tree. node_stack = node_stack[:indent_depth + 1] node = node_stack[-1] # Element nodes are a list of python dicts while attributes are just a dict. # This is because multiple elements, at the same depth of tree and the same # name, are all added to the same list keyed under the element name. m = _MANIFEST_ELEMENT_RE.match(line[len(indent) * indent_depth:]) if m: manifest_key = m.group(1) if manifest_key in node: node[manifest_key] += [{}] else: node[manifest_key] = [{}] node_stack += [node[manifest_key][-1]] continue m = _MANIFEST_ATTRIBUTE_RE.match(line[len(indent) * indent_depth:]) if m: manifest_key = m.group(1) if manifest_key in node: raise base_error.BaseError( "A single attribute should have one key and one value") else: node[manifest_key] = m.group(2) or m.group(3) continue return parsed_manifest
def GetInstrumentationName( self, default='android.test.InstrumentationTestRunner'): """Returns the name of the Instrumentation in the apk.""" all_instrumentations = self.GetAllInstrumentations(default=default) if len(all_instrumentations) != 1: raise base_error.BaseError( 'There is more than one instrumentation. Expected one.') else: return self._ResolveName(all_instrumentations[0]['android:name'])
def UpdateGivenDependency(dependencies, args): dep_name = args.name or os.path.basename(args.path) if not dep_name in dependencies.get('dependencies', {}): raise base_error.BaseError('Could not find dependency "%s" in %s' % (dep_name, args.dependencies_json)) UpdateDependency(dep_name, dependencies.get('dependencies', {}).get(dep_name, {}), args.path, args.platform) return dependencies
def BuildTargetsForCpu(targets, cpu, output_dir): logging.info('Building %s', cpu) gn_args = [ 'ffmpeg_branding="Chrome"', 'is_component_build=false', 'is_debug=false', 'proprietary_codecs=true', 'symbol_level=1', 'target_cpu="%s"' % cpu, 'target_os="android"', 'use_goma=true', ] cmd = ['gn', 'gen', '--args=%s' % (' '.join(gn_args)), output_dir] ec = cmd_helper.RunCmd(cmd) if ec: raise base_error.BaseError('%s failed with %d' % (cmd, ec)) ec = cmd_helper.RunCmd(['autoninja', '-C', output_dir] + targets) if ec: raise base_error.BaseError('building %s failed with %d' % (cpu, ec))
def ExtractApks(output_dir, apks_path, abis, locales, features, pixel_density, sdk_version, modules=None): """Extracts splits from APKS archive. Args: output_dir: Directory to extract splits into. apks_path: Path to APKS archive. abis: ABIs to support. locales: Locales to support. features: Device features to support. pixel_density: Pixel density to support. sdk_version: Target SDK version. modules: Extra modules to install. """ device_spec = { 'supportedAbis': abis, 'supportedLocales': ['%s-%s' % l for l in locales], 'deviceFeatures': features, 'screenDensity': pixel_density, 'sdkVersion': sdk_version, } with tempfile_ext.TemporaryFileName(suffix='.json') as device_spec_path: with open(device_spec_path, 'w') as f: json.dump(device_spec, f) cmd = [ 'java', '-jar', _bundletool_path.read(), 'extract-apks', '--apks=%s' % apks_path, '--device-spec=%s' % device_spec_path, '--output-dir=%s' % output_dir, ] if modules: cmd += ['--modules=%s' % ','.join(modules)] status, stdout, stderr = cmd_helper.GetCmdStatusOutputAndError(cmd) if status != 0: raise base_error.BaseError( 'Failed running {} with output\n{}\n{}'.format( ' '.join(cmd), stdout, stderr))
def GetAbis(self): """Returns a list of ABIs in the apk (empty list if no native code).""" # Use lib/* to determine the compatible ABIs. libs = set() for path in self._ListApkPaths(): path_tokens = path.split('/') if len(path_tokens) >= 2 and path_tokens[0] == 'lib': libs.add(path_tokens[1]) lib_to_abi = { abis.ARM: [abis.ARM, abis.ARM_64], abis.ARM_64: [abis.ARM_64], abis.X86: [abis.X86, abis.X86_64], abis.X86_64: [abis.X86_64] } try: output = set() for lib in libs: for abi in lib_to_abi[lib]: output.add(abi) return sorted(output) except KeyError: raise base_error.BaseError('Unexpected ABI in lib/* folder.')
def _ParseManifestFromApk(apk_path): aapt_output = aapt.Dump('xmltree', apk_path, 'AndroidManifest.xml') 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: manifest_key = m.group(1) if manifest_key in node: node[manifest_key] += [{}] else: node[manifest_key] = [{}] node_stack += [node[manifest_key][-1]] continue m = _MANIFEST_ATTRIBUTE_RE.match(line[len(indent) * indent_depth:]) if m: manifest_key = m.group(1) if manifest_key in node: raise base_error.BaseError( "A single attribute should have one key and one value") else: node[manifest_key] = m.group(2) or m.group(3) continue return parsed_manifest