def _OnStaleMd5(options): with resource_utils.BuildContext() as build: if options.sources: _CheckAllFilesListed(options.sources, options.resource_dirs) if options.r_text_in: r_txt_path = options.r_text_in else: # Extract dependencies to resolve @foo/type references into # dependent packages. dep_subdirs = resource_utils.ExtractDeps( options.dependencies_res_zips, build.deps_dir) _GenerateRTxt(options, dep_subdirs, build.gen_dir) r_txt_path = build.r_txt_path if options.r_text_out: shutil.copyfile(r_txt_path, options.r_text_out) if options.srcjar_out: package = options.custom_package if not package and options.android_manifest: _, manifest_node, _ = manifest_utils.ParseManifest( options.android_manifest) package = manifest_utils.GetPackage(manifest_node) # Don't create a .java file for the current resource target when no # package name was provided (either by manifest or build rules). if package: # All resource IDs should be non-final here, but the # onResourcesLoaded() method should only be generated if # --shared-resources is used. rjava_build_options = resource_utils.RJavaBuildOptions() rjava_build_options.ExportAllResources() rjava_build_options.ExportAllStyleables() if options.shared_resources: rjava_build_options.GenerateOnResourcesLoaded() # Not passing in custom_root_package_name or parent to keep # file names unique. resource_utils.CreateRJavaFiles(build.srcjar_dir, package, r_txt_path, options.extra_res_packages, options.extra_r_text_files, rjava_build_options, options.srcjar_out) build_utils.ZipDir(options.srcjar_out, build.srcjar_dir) if options.resource_zip_out: ignore_pattern = resource_utils.AAPT_IGNORE_PATTERN if options.strip_drawables: ignore_pattern += ':*drawable*' _ZipResources(options.resource_dirs, options.resource_zip_out, ignore_pattern)
def _GenerateAndroidManifest(original_manifest_path, extra_manifest_paths, min_sdk_version, android_sdk_version): # Set minSdkVersion in the manifest to the correct value. doc, manifest, app_node = manifest_utils.ParseManifest(original_manifest_path) # TODO(crbug.com/1126301): Should this be done using manifest merging? # Add anything in the application node of the extra manifests to the main # manifest to prevent unused resource errors. for path in extra_manifest_paths: _, _, extra_app_node = manifest_utils.ParseManifest(path) for node in extra_app_node: app_node.append(node) uses_sdk = manifest.find('./uses-sdk') if uses_sdk is None: uses_sdk = ElementTree.Element('uses-sdk') manifest.insert(0, uses_sdk) uses_sdk.set('{%s}minSdkVersion' % manifest_utils.ANDROID_NAMESPACE, min_sdk_version) uses_sdk.set('{%s}targetSdkVersion' % manifest_utils.ANDROID_NAMESPACE, android_sdk_version) return doc
def _GenerateAndroidManifest(original_manifest_path, min_sdk_version, manifest_package=None): # Set minSdkVersion and package in the manifest to the correct values. doc, manifest, _ = manifest_utils.ParseManifest(original_manifest_path) uses_sdk = manifest.find('./uses-sdk') if uses_sdk is None: uses_sdk = ElementTree.Element('uses-sdk') manifest.insert(0, uses_sdk) uses_sdk.set('{%s}minSdkVersion' % manifest_utils.ANDROID_NAMESPACE, min_sdk_version) if manifest_package: manifest.set('package', manifest_package) return doc
def _ProcessManifest(manifest_path, allow_uses_sdk): """Patches an Android manifest to always include the 'tools' namespace declaration, as it is not propagated by the manifest merger from the SDK. See https://issuetracker.google.com/issues/63411481 """ doc, manifest, _ = manifest_utils.ParseManifest(manifest_path) package = manifest_utils.GetPackage(manifest) if not allow_uses_sdk: manifest_utils.AssertNoUsesSdk(manifest) tmp_prefix = os.path.basename(manifest_path) with tempfile.NamedTemporaryFile(prefix=tmp_prefix) as patched_manifest: manifest_utils.SaveManifest(doc, patched_manifest.name) yield patched_manifest.name, package
def _ProcessManifest(manifest_path, min_sdk_version, target_sdk_version, max_sdk_version, manifest_package): """Patches an Android manifest's package and performs assertions to ensure correctness for the manifest. """ doc, manifest, _ = manifest_utils.ParseManifest(manifest_path) manifest_utils.AssertUsesSdk(manifest, min_sdk_version, target_sdk_version, max_sdk_version) assert manifest_utils.GetPackage(manifest) or manifest_package, \ 'Must set manifest package in GN or in AndroidManifest.xml' manifest_utils.AssertPackage(manifest, manifest_package) if manifest_package: manifest.set('package', manifest_package) tmp_prefix = os.path.basename(manifest_path) with tempfile.NamedTemporaryFile(prefix=tmp_prefix) as patched_manifest: manifest_utils.SaveManifest(doc, patched_manifest.name) yield patched_manifest.name, manifest_utils.GetPackage(manifest)
def _ProcessManifest(manifest_path, min_sdk_version, target_sdk_version, manifest_package): """Patches an Android manifest to always include the 'tools' namespace declaration, as it is not propagated by the manifest merger from the SDK. See https://issuetracker.google.com/issues/63411481 """ doc, manifest, _ = manifest_utils.ParseManifest(manifest_path) manifest_utils.AssertUsesSdk(manifest, min_sdk_version, target_sdk_version) assert manifest_utils.GetPackage(manifest) or manifest_package, \ 'Must set manifest package in GN or in AndroidManifest.xml' manifest_utils.AssertPackage(manifest, manifest_package) if manifest_package: manifest.set('package', manifest_package) tmp_prefix = os.path.basename(manifest_path) with tempfile.NamedTemporaryFile(prefix=tmp_prefix) as patched_manifest: manifest_utils.SaveManifest(doc, patched_manifest.name) yield patched_manifest.name, manifest_utils.GetPackage(manifest)
def _GenerateAndroidManifest(original_manifest_path, min_sdk_version, android_sdk_version): # Set minSdkVersion in the manifest to the correct value. doc, manifest, app_node = manifest_utils.ParseManifest(original_manifest_path) if app_node.find( '{%s}allowBackup' % manifest_utils.ANDROID_NAMESPACE) is None: # Assume no backup is intended, appeases AllowBackup lint check and keeping # it working for manifests that do define android:allowBackup. app_node.set('{%s}allowBackup' % manifest_utils.ANDROID_NAMESPACE, 'false') uses_sdk = manifest.find('./uses-sdk') if uses_sdk is None: uses_sdk = ElementTree.Element('uses-sdk') manifest.insert(0, uses_sdk) uses_sdk.set('{%s}minSdkVersion' % manifest_utils.ANDROID_NAMESPACE, min_sdk_version) uses_sdk.set('{%s}targetSdkVersion' % manifest_utils.ANDROID_NAMESPACE, android_sdk_version) return doc
def _ProcessManifest(path, arsc_package_name, disable_isolated_processes): doc, manifest_node, app_node = manifest_utils.ParseManifest(path) # Ensure the manifest package matches that of the apk's arsc package # So that resource references resolve correctly. The actual manifest # package name is set via --rename-manifest-package. manifest_node.set('package', arsc_package_name) # Pylint for some reason things app_node is an int. # pylint: disable=no-member real_app_class = app_node.get(_AddNamespace('name'), _DEFAULT_APPLICATION_CLASS) app_node.set(_AddNamespace('name'), _INCREMENTAL_APP_NAME) # pylint: enable=no-member _CreateMetaData(app_node, _META_DATA_APP_NAME, real_app_class) # Seems to be a bug in ElementTree, as doc.find() doesn't work here. instrumentation_nodes = doc.findall('instrumentation') assert len(instrumentation_nodes) <= 2, ( 'Need to update incremental install to support >2 <instrumentation> tags' ) for i, instrumentation_node in enumerate(instrumentation_nodes): real_instrumentation_class = instrumentation_node.get( _AddNamespace('name')) instrumentation_node.set(_AddNamespace('name'), _INCREMENTAL_INSTRUMENTATION_CLASSES[i]) _CreateMetaData(app_node, _META_DATA_INSTRUMENTATION_NAMES[i], real_instrumentation_class) ret = ElementTree.tostring(doc.getroot(), encoding='UTF-8') # Disable check for page-aligned native libraries. ret = ret.replace(b'extractNativeLibs="false"', b'extractNativeLibs="true"') if disable_isolated_processes: ret = ret.replace(b'isolatedProcess="true"', b'isolatedProcess="false"') return ret
def _FixManifest(options, temp_dir): """Fix the APK's AndroidManifest.xml. This adds any missing namespaces for 'android' and 'tools', and sets certains elements like 'platformBuildVersionCode' or 'android:debuggable' depending on the content of |options|. Args: options: The command-line arguments tuple. temp_dir: A temporary directory where the fixed manifest will be written to. Returns: Tuple of: * Manifest path within |temp_dir|. * Original package_name. """ def maybe_extract_version(j): try: return resource_utils.ExtractBinaryManifestValues( options.aapt2_path, j) except build_utils.CalledProcessError: return None android_sdk_jars = [ j for j in options.include_resources if os.path.basename(j) in ('android.jar', 'android_system.jar') ] extract_all = [maybe_extract_version(j) for j in android_sdk_jars] successful_extractions = [x for x in extract_all if x] if len(successful_extractions) == 0: raise Exception('Unable to find android SDK jar among candidates: %s' % ', '.join(android_sdk_jars)) elif len(successful_extractions) > 1: raise Exception( 'Found multiple android SDK jars among candidates: %s' % ', '.join(android_sdk_jars)) version_code, version_name = successful_extractions.pop()[:2] debug_manifest_path = os.path.join(temp_dir, 'AndroidManifest.xml') doc, manifest_node, app_node = manifest_utils.ParseManifest( options.android_manifest) manifest_utils.AssertUsesSdk(manifest_node, options.min_sdk_version, options.target_sdk_version) # We explicitly check that maxSdkVersion is set in the manifest since we don't # add it later like minSdkVersion and targetSdkVersion. manifest_utils.AssertUsesSdk(manifest_node, max_sdk_version=options.max_sdk_version, fail_if_not_exist=True) manifest_utils.AssertPackage(manifest_node, options.manifest_package) manifest_node.set('platformBuildVersionCode', version_code) manifest_node.set('platformBuildVersionName', version_name) orig_package = manifest_node.get('package') if options.arsc_package_name: manifest_node.set('package', options.arsc_package_name) if options.debuggable: app_node.set( '{%s}%s' % (manifest_utils.ANDROID_NAMESPACE, 'debuggable'), 'true') manifest_utils.SaveManifest(doc, debug_manifest_path) return debug_manifest_path, orig_package
def _FixManifest(options, temp_dir, extra_manifest=None): """Fix the APK's AndroidManifest.xml. This adds any missing namespaces for 'android' and 'tools', and sets certains elements like 'platformBuildVersionCode' or 'android:debuggable' depending on the content of |options|. Args: options: The command-line arguments tuple. temp_dir: A temporary directory where the fixed manifest will be written to. extra_manifest: Path to an AndroidManifest.xml file which will get merged into the application node of the base manifest. Returns: Tuple of: * Manifest path within |temp_dir|. * Original package_name. """ def maybe_extract_version(j): try: return resource_utils.ExtractBinaryManifestValues( options.aapt2_path, j) except build_utils.CalledProcessError: return None android_sdk_jars = [ j for j in options.include_resources if os.path.basename(j) in ('android.jar', 'android_system.jar') ] extract_all = [maybe_extract_version(j) for j in android_sdk_jars] successful_extractions = [x for x in extract_all if x] if len(successful_extractions) == 0: raise Exception('Unable to find android SDK jar among candidates: %s' % ', '.join(android_sdk_jars)) elif len(successful_extractions) > 1: raise Exception( 'Found multiple android SDK jars among candidates: %s' % ', '.join(android_sdk_jars)) version_code, version_name = successful_extractions.pop()[:2] debug_manifest_path = os.path.join(temp_dir, 'AndroidManifest.xml') doc, manifest_node, app_node = manifest_utils.ParseManifest( options.android_manifest) if extra_manifest: _, extra_manifest_node, extra_app_node = manifest_utils.ParseManifest( extra_manifest) for node in extra_app_node: app_node.append(node) for node in extra_manifest_node: # DFM manifests have a bunch of tags we don't care about inside # <manifest>, so only take <queries>. if node.tag == 'queries': manifest_node.append(node) manifest_utils.AssertUsesSdk(manifest_node, options.min_sdk_version, options.target_sdk_version) # We explicitly check that maxSdkVersion is set in the manifest since we don't # add it later like minSdkVersion and targetSdkVersion. manifest_utils.AssertUsesSdk(manifest_node, max_sdk_version=options.max_sdk_version, fail_if_not_exist=True) manifest_utils.AssertPackage(manifest_node, options.manifest_package) manifest_node.set('platformBuildVersionCode', version_code) manifest_node.set('platformBuildVersionName', version_name) orig_package = manifest_node.get('package') if options.arsc_package_name: manifest_node.set('package', options.arsc_package_name) if options.debuggable: app_node.set( '{%s}%s' % (manifest_utils.ANDROID_NAMESPACE, 'debuggable'), 'true') if options.uses_split: uses_split = ElementTree.SubElement(manifest_node, 'uses-split') uses_split.set('{%s}name' % manifest_utils.ANDROID_NAMESPACE, options.uses_split) # Make sure the min-sdk condition is not less than the min-sdk of the bundle. for min_sdk_node in manifest_node.iter('{%s}min-sdk' % manifest_utils.DIST_NAMESPACE): dist_value = '{%s}value' % manifest_utils.DIST_NAMESPACE if int(min_sdk_node.get(dist_value)) < int(options.min_sdk_version): min_sdk_node.set(dist_value, options.min_sdk_version) manifest_utils.SaveManifest(doc, debug_manifest_path) return debug_manifest_path, orig_package
def _OnStaleMd5(lint_path, config_path, processed_config_path, manifest_path, result_path, product_dir, sources, jar_path, cache_dir, android_sdk_version, srcjars, min_sdk_version, manifest_package, resource_sources, disable=None, classpath=None, can_fail_build=False, include_unexpected=False, silent=False): def _RebasePath(path): """Returns relative path to top-level src dir. Args: path: A path relative to cwd. """ ret = os.path.relpath(os.path.abspath(path), build_utils.DIR_SOURCE_ROOT) # If it's outside of src/, just use abspath. if ret.startswith('..'): ret = os.path.abspath(path) return ret def _ProcessConfigFile(): if not config_path or not processed_config_path: return if not build_utils.IsTimeStale(processed_config_path, [config_path]): return with open(config_path, 'rb') as f: content = f.read().replace( 'PRODUCT_DIR', _RebasePath(product_dir)) with open(processed_config_path, 'wb') as f: f.write(content) def _ProcessResultFile(): with open(result_path, 'rb') as f: content = f.read().replace( _RebasePath(product_dir), 'PRODUCT_DIR') with open(result_path, 'wb') as f: f.write(content) def _ParseAndShowResultFile(): dom = minidom.parse(result_path) issues = dom.getElementsByTagName('issue') if not silent: print(file=sys.stderr) for issue in issues: issue_id = issue.attributes['id'].value message = issue.attributes['message'].value location_elem = issue.getElementsByTagName('location')[0] path = location_elem.attributes['file'].value line = location_elem.getAttribute('line') if line: error = '%s:%s %s: %s [warning]' % (path, line, message, issue_id) else: # Issues in class files don't have a line number. error = '%s %s: %s [warning]' % (path, message, issue_id) print(error.encode('utf-8'), file=sys.stderr) for attr in ['errorLine1', 'errorLine2']: error_line = issue.getAttribute(attr) if error_line: print(error_line.encode('utf-8'), file=sys.stderr) return len(issues) with build_utils.TempDir() as temp_dir: _ProcessConfigFile() cmd = [ _RebasePath(lint_path), '-Werror', '--exitcode', '--showall', '--xml', _RebasePath(result_path), ] if jar_path: # --classpath is just for .class files for this one target. cmd.extend(['--classpath', _RebasePath(jar_path)]) if processed_config_path: cmd.extend(['--config', _RebasePath(processed_config_path)]) tmp_dir_counter = [0] def _NewTempSubdir(prefix, append_digit=True): # Helper function to create a new sub directory based on the number of # subdirs created earlier. if append_digit: tmp_dir_counter[0] += 1 prefix += str(tmp_dir_counter[0]) new_dir = os.path.join(temp_dir, prefix) os.makedirs(new_dir) return new_dir resource_dirs = [] for resource_source in resource_sources: if os.path.isdir(resource_source): resource_dirs.append(resource_source) else: # This is a zip file with generated resources (e. g. strings from GRD). # Extract it to temporary folder. resource_dir = _NewTempSubdir(resource_source, append_digit=False) resource_dirs.append(resource_dir) build_utils.ExtractAll(resource_source, path=resource_dir) for resource_dir in resource_dirs: cmd.extend(['--resources', _RebasePath(resource_dir)]) if classpath: # --libraries is the classpath (excluding active target). cp = ':'.join(_RebasePath(p) for p in classpath) cmd.extend(['--libraries', cp]) # There may be multiple source files with the same basename (but in # different directories). It is difficult to determine what part of the path # corresponds to the java package, and so instead just link the source files # into temporary directories (creating a new one whenever there is a name # conflict). def PathInDir(d, src): subpath = os.path.join(d, _RebasePath(src)) subdir = os.path.dirname(subpath) if not os.path.exists(subdir): os.makedirs(subdir) return subpath src_dirs = [] for src in sources: src_dir = None for d in src_dirs: if not os.path.exists(PathInDir(d, src)): src_dir = d break if not src_dir: src_dir = _NewTempSubdir('SRC_ROOT') src_dirs.append(src_dir) cmd.extend(['--sources', _RebasePath(src_dir)]) # In cases where the build dir is outside of the src dir, this can # result in trying to symlink a file to itself for this file: # gen/components/version_info/android/java/org/chromium/ # components/version_info/VersionConstants.java src = os.path.abspath(src) dst = PathInDir(src_dir, src) if src == dst: continue os.symlink(src, dst) if srcjars: srcjar_paths = build_utils.ParseGnList(srcjars) if srcjar_paths: srcjar_dir = _NewTempSubdir('SRC_ROOT') cmd.extend(['--sources', _RebasePath(srcjar_dir)]) for srcjar in srcjar_paths: build_utils.ExtractAll(srcjar, path=srcjar_dir) if disable: cmd.extend(['--disable', ','.join(disable)]) project_dir = _NewTempSubdir('SRC_ROOT') if android_sdk_version: # Create dummy project.properies file in a temporary "project" directory. # It is the only way to add Android SDK to the Lint's classpath. Proper # classpath is necessary for most source-level checks. with open(os.path.join(project_dir, 'project.properties'), 'w') \ as propfile: print('target=android-{}'.format(android_sdk_version), file=propfile) # Put the manifest in a temporary directory in order to avoid lint detecting # sibling res/ and src/ directories (which should be pass explicitly if they # are to be included). if not manifest_path: manifest_path = os.path.join( build_utils.DIR_SOURCE_ROOT, 'build', 'android', 'AndroidManifest.xml') lint_manifest_path = os.path.join(project_dir, 'AndroidManifest.xml') shutil.copyfile(os.path.abspath(manifest_path), lint_manifest_path) # Check that minSdkVersion and package is correct and add it to the manifest # in case it does not exist. doc, manifest, _ = manifest_utils.ParseManifest(lint_manifest_path) manifest_utils.AssertUsesSdk(manifest, min_sdk_version) manifest_utils.AssertPackage(manifest, manifest_package) uses_sdk = manifest.find('./uses-sdk') if uses_sdk is None: uses_sdk = ElementTree.Element('uses-sdk') manifest.insert(0, uses_sdk) uses_sdk.set('{%s}minSdkVersion' % manifest_utils.ANDROID_NAMESPACE, min_sdk_version) if manifest_package: manifest.set('package', manifest_package) manifest_utils.SaveManifest(doc, lint_manifest_path) cmd.append(project_dir) if os.path.exists(result_path): os.remove(result_path) env = os.environ.copy() stderr_filter = None if cache_dir: env['_JAVA_OPTIONS'] = '-Duser.home=%s' % _RebasePath(cache_dir) # When _JAVA_OPTIONS is set, java prints to stderr: # Picked up _JAVA_OPTIONS: ... # # We drop all lines that contain _JAVA_OPTIONS from the output stderr_filter = lambda l: re.sub(r'.*_JAVA_OPTIONS.*\n?', '', l) def fail_func(returncode, stderr): if returncode != 0: return True if (include_unexpected and 'Unexpected failure during lint analysis' in stderr): return True return False try: build_utils.CheckOutput(cmd, cwd=build_utils.DIR_SOURCE_ROOT, env=env or None, stderr_filter=stderr_filter, fail_func=fail_func) except build_utils.CalledProcessError: # There is a problem with lint usage if not os.path.exists(result_path): raise # Sometimes produces empty (almost) files: if os.path.getsize(result_path) < 10: if can_fail_build: raise elif not silent: traceback.print_exc() return # There are actual lint issues try: num_issues = _ParseAndShowResultFile() except Exception: # pylint: disable=broad-except if not silent: print('Lint created unparseable xml file...') print('File contents:') with open(result_path) as f: print(f.read()) if can_fail_build: traceback.print_exc() if can_fail_build: raise else: return _ProcessResultFile() if num_issues == 0 and include_unexpected: msg = 'Please refer to output above for unexpected lint failures.\n' else: msg = ('\nLint found %d new issues.\n' ' - For full explanation, please refer to %s\n' ' - For more information about lint and how to fix lint issues,' ' please refer to %s\n' % (num_issues, _RebasePath(result_path), _LINT_MD_URL)) if not silent: print(msg, file=sys.stderr) if can_fail_build: raise Exception('Lint failed.')
def main(argv): argv = build_utils.ExpandFileArgs(argv) parser = argparse.ArgumentParser(description=__doc__) build_utils.AddDepfileOption(parser) parser.add_argument('--build-vars', help='Path to GN build vars file', required=True) parser.add_argument('--root-manifest', help='Root manifest which to merge into', required=True) parser.add_argument('--output', help='Output manifest path', required=True) parser.add_argument('--extras', help='GN list of additional manifest to merge') parser.add_argument('--min-sdk-version', required=True, help='android:minSdkVersion for merging.') parser.add_argument('--target-sdk-version', required=True, help='android:targetSdkVersion for merging.') parser.add_argument('--max-sdk-version', help='android:maxSdkVersion for merging.') parser.add_argument('--manifest-package', help='Package name of the merged AndroidManifest.xml.') args = parser.parse_args(argv) classpath = _BuildManifestMergerClasspath( build_utils.ReadBuildVars(args.build_vars)) with build_utils.AtomicOutput(args.output) as output: cmd = [ build_utils.JAVA_PATH, '-cp', classpath, _MANIFEST_MERGER_MAIN_CLASS, '--out', output.name, '--property', 'MIN_SDK_VERSION=' + args.min_sdk_version, '--property', 'TARGET_SDK_VERSION=' + args.target_sdk_version, ] if args.max_sdk_version: cmd += [ '--property', 'MAX_SDK_VERSION=' + args.max_sdk_version, ] extras = build_utils.ParseGnList(args.extras) if extras: cmd += ['--libs', ':'.join(extras)] with _ProcessManifest(args.root_manifest, args.min_sdk_version, args.target_sdk_version, args.max_sdk_version, args.manifest_package) as tup: root_manifest, package = tup cmd += [ '--main', root_manifest, '--property', 'PACKAGE=' + package, ] build_utils.CheckOutput( cmd, # https://issuetracker.google.com/issues/63514300: # The merger doesn't set a nonzero exit code for failures. fail_func=lambda returncode, stderr: returncode != 0 or build_utils.IsTimeStale(output.name, [root_manifest] + extras)) # Check for correct output. _, manifest, _ = manifest_utils.ParseManifest(output.name) manifest_utils.AssertUsesSdk(manifest, args.min_sdk_version, args.target_sdk_version) manifest_utils.AssertPackage(manifest, package) if args.depfile: inputs = extras + classpath.split(':') build_utils.WriteDepfile(args.depfile, args.output, inputs=inputs, add_pydeps=False)
def main(argv): argv = build_utils.ExpandFileArgs(argv) parser = argparse.ArgumentParser(description=__doc__) build_utils.AddDepfileOption(parser) parser.add_argument('--build-vars', help='Path to GN build vars file', required=True) parser.add_argument('--root-manifest', help='Root manifest which to merge into', required=True) parser.add_argument('--output', help='Output manifest path', required=True) parser.add_argument('--extras', help='GN list of additional manifest to merge') parser.add_argument('--min-sdk-version', required=True, help='android:minSdkVersion for merging.') parser.add_argument('--target-sdk-version', required=True, help='android:targetSdkVersion for merging.') parser.add_argument( '--allow-uses-sdk', action='store_true', help='Use only for third party code. ' 'Don\'t fail if input manifest contains a <uses-sdk> element.') args = parser.parse_args(argv) classpath = _BuildManifestMergerClasspath( build_utils.ReadBuildVars(args.build_vars)) with build_utils.AtomicOutput(args.output) as output: cmd = [ 'java', '-cp', classpath, _MANIFEST_MERGER_MAIN_CLASS, '--out', output.name, ] extras = build_utils.ParseGnList(args.extras) if extras: cmd += ['--libs', ':'.join(extras)] with _ProcessManifest(args.root_manifest, args.allow_uses_sdk) as tup: root_manifest, package = tup cmd += [ '--main', root_manifest, '--property', 'PACKAGE=' + package, '--property', 'MIN_SDK_VERSION=' + args.min_sdk_version, '--property', 'TARGET_SDK_VERSION=' + args.target_sdk_version, ] build_utils.CheckOutput( cmd, # https://issuetracker.google.com/issues/63514300: # The merger doesn't set a nonzero exit code for failures. fail_func=lambda returncode, stderr: returncode != 0 or build_utils.IsTimeStale(output.name, [root_manifest] + extras)) # Subsequent build system steps expect uses-sdk tag does not exist. # Therefore, check it has the expected attribute values and remove it. doc, manifest, _ = manifest_utils.ParseManifest(output.name) uses_sdk = manifest.find('./uses-sdk') assert uses_sdk.get( '{%s}minSdkVersion' % manifest_utils.ANDROID_NAMESPACE) == args.min_sdk_version assert uses_sdk.get( '{%s}targetSdkVersion' % manifest_utils.ANDROID_NAMESPACE) == args.target_sdk_version manifest.remove(uses_sdk) manifest_utils.SaveManifest(doc, output.name) if args.depfile: inputs = extras + classpath.split(':') build_utils.WriteDepfile(args.depfile, args.output, inputs=inputs, add_pydeps=False)
def _RunLint(lint_path, config_path, manifest_path, result_path, product_dir, sources, cache_dir, android_sdk_version, srcjars, min_sdk_version, manifest_package, resource_sources, resource_zips, android_sdk_root, testonly_target=False, can_fail_build=False, include_unexpected=False, silent=False): logging.info('Lint starting') def _RebasePath(path): """Returns relative path to top-level src dir. Args: path: A path relative to cwd. """ ret = os.path.relpath(os.path.abspath(path), build_utils.DIR_SOURCE_ROOT) # If it's outside of src/, just use abspath. if ret.startswith('..'): ret = os.path.abspath(path) return ret def _ProcessResultFile(): with open(result_path, 'rb') as f: content = f.read().replace(_RebasePath(product_dir), 'PRODUCT_DIR') with open(result_path, 'wb') as f: f.write(content) def _ParseAndShowResultFile(): dom = minidom.parse(result_path) issues = dom.getElementsByTagName('issue') if not silent: print(file=sys.stderr) for issue in issues: issue_id = issue.attributes['id'].value message = issue.attributes['message'].value location_elem = issue.getElementsByTagName('location')[0] path = location_elem.attributes['file'].value line = location_elem.getAttribute('line') error = '%s:%s %s: %s [warning]' % (path, line, message, issue_id) print(error.encode('utf-8'), file=sys.stderr) for attr in ['errorLine1', 'errorLine2']: error_line = issue.getAttribute(attr) if error_line: print(error_line.encode('utf-8'), file=sys.stderr) return len(issues) with build_utils.TempDir() as temp_dir: cmd = [ _RebasePath(lint_path), '-Werror', '--exitcode', '--showall', '--xml', _RebasePath(result_path), # An explicit sdk root needs to be specified since we have an extra # intermediate 'lastest' directory under cmdline-tools which prevents # lint from automatically deducing the location of the sdk. The sdk is # required for many checks (e.g. NewApi). Lint also requires absolute # paths. '--sdk-home', os.path.abspath(android_sdk_root), ] if config_path: cmd.extend(['--config', _RebasePath(config_path)]) if testonly_target: cmd.extend(['--disable', ','.join(_DISABLED_FOR_TESTS)]) tmp_dir_counter = [0] def _NewTempSubdir(prefix, append_digit=True): # Helper function to create a new sub directory based on the number of # subdirs created earlier. if append_digit: tmp_dir_counter[0] += 1 prefix += str(tmp_dir_counter[0]) new_dir = os.path.join(temp_dir, prefix) os.makedirs(new_dir) return new_dir resource_dirs = resource_utils.DeduceResourceDirsFromFileList( resource_sources) # These are zip files with generated resources (e. g. strings from GRD). for resource_zip in resource_zips: resource_dir = _NewTempSubdir(resource_zip, append_digit=False) resource_dirs.append(resource_dir) build_utils.ExtractAll(resource_zip, path=resource_dir) for resource_dir in resource_dirs: cmd.extend(['--resources', _RebasePath(resource_dir)]) # There may be multiple source files with the same basename (but in # different directories). It is difficult to determine what part of the path # corresponds to the java package, and so instead just link the source files # into temporary directories (creating a new one whenever there is a name # conflict). def PathInDir(d, src): subpath = os.path.join(d, _RebasePath(src)) subdir = os.path.dirname(subpath) if not os.path.exists(subdir): os.makedirs(subdir) return subpath src_dirs = [] for src in sources: src_dir = None for d in src_dirs: if not os.path.exists(PathInDir(d, src)): src_dir = d break if not src_dir: src_dir = _NewTempSubdir('SRC_ROOT') src_dirs.append(src_dir) cmd.extend(['--sources', _RebasePath(src_dir)]) # In cases where the build dir is outside of the src dir, this can # result in trying to symlink a file to itself for this file: # gen/components/version_info/android/java/org/chromium/ # components/version_info/VersionConstants.java src = os.path.abspath(src) dst = PathInDir(src_dir, src) if src == dst: continue os.symlink(src, dst) if srcjars: srcjar_dir = _NewTempSubdir('GENERATED_SRC_ROOT', append_digit=False) cmd.extend(['--sources', _RebasePath(srcjar_dir)]) for srcjar in srcjars: # We choose to allow srcjars that contain java files which have the # same package and name to clobber each other. This happens for # generated files like BuildConfig.java. It is generated for # targets like base_build_config_gen as well as targets like # chrome_modern_public_base_bundle_module__build_config_srcjar. # Although we could extract each srcjar to a separate folder, that # slows down some invocations of lint by 20 seconds or more. # TODO(wnwen): Switch lint.py to generate a project.xml file which # supports srcjar inputs by default. build_utils.ExtractAll(srcjar, path=srcjar_dir, no_clobber=False) project_dir = _NewTempSubdir('PROJECT_ROOT', append_digit=False) if android_sdk_version: # Create dummy project.properies file in a temporary "project" directory. # It is the only way to add Android SDK to the Lint's classpath. Proper # classpath is necessary for most source-level checks. with open(os.path.join(project_dir, 'project.properties'), 'w') \ as propfile: print('target=android-{}'.format(android_sdk_version), file=propfile) # Put the manifest in a temporary directory in order to avoid lint detecting # sibling res/ and src/ directories (which should be pass explicitly if they # are to be included). if not manifest_path: manifest_path = os.path.join(build_utils.DIR_SOURCE_ROOT, 'build', 'android', 'AndroidManifest.xml') lint_manifest_path = os.path.join(project_dir, 'AndroidManifest.xml') shutil.copyfile(os.path.abspath(manifest_path), lint_manifest_path) # Check that minSdkVersion and package is correct and add it to the manifest # in case it does not exist. doc, manifest, _ = manifest_utils.ParseManifest(lint_manifest_path) manifest_utils.AssertUsesSdk(manifest, min_sdk_version) manifest_utils.AssertPackage(manifest, manifest_package) uses_sdk = manifest.find('./uses-sdk') if uses_sdk is None: uses_sdk = ElementTree.Element('uses-sdk') manifest.insert(0, uses_sdk) uses_sdk.set('{%s}minSdkVersion' % manifest_utils.ANDROID_NAMESPACE, min_sdk_version) if manifest_package: manifest.set('package', manifest_package) manifest_utils.SaveManifest(doc, lint_manifest_path) cmd.append(project_dir) if os.path.exists(result_path): os.remove(result_path) env = os.environ.copy() stderr_filter = build_utils.FilterReflectiveAccessJavaWarnings if cache_dir: env['_JAVA_OPTIONS'] = '-Duser.home=%s' % _RebasePath(cache_dir) # When _JAVA_OPTIONS is set, java prints to stderr: # Picked up _JAVA_OPTIONS: ... # # We drop all lines that contain _JAVA_OPTIONS from the output stderr_filter = lambda l: re.sub( r'.*_JAVA_OPTIONS.*\n?', '', build_utils.FilterReflectiveAccessJavaWarnings(l)) def fail_func(returncode, stderr): if returncode != 0: return True if (include_unexpected and 'Unexpected failure during lint analysis' in stderr): return True return False try: env['JAVA_HOME'] = os.path.relpath(build_utils.JAVA_HOME, build_utils.DIR_SOURCE_ROOT) logging.debug('Lint command %s', cmd) start = time.time() build_utils.CheckOutput(cmd, cwd=build_utils.DIR_SOURCE_ROOT, env=env or None, stderr_filter=stderr_filter, fail_func=fail_func) end = time.time() - start logging.info('Lint command took %ss', end) except build_utils.CalledProcessError: # There is a problem with lint usage if not os.path.exists(result_path): raise # Sometimes produces empty (almost) files: if os.path.getsize(result_path) < 10: if can_fail_build: raise elif not silent: traceback.print_exc() return # There are actual lint issues try: num_issues = _ParseAndShowResultFile() except Exception: # pylint: disable=broad-except if not silent: print('Lint created unparseable xml file...') print('File contents:') with open(result_path) as f: print(f.read()) if can_fail_build: traceback.print_exc() if can_fail_build: raise else: return _ProcessResultFile() if num_issues == 0 and include_unexpected: msg = 'Please refer to output above for unexpected lint failures.\n' else: msg = ( '\nLint found %d new issues.\n' ' - For full explanation, please refer to %s\n' ' - For more information about lint and how to fix lint issues,' ' please refer to %s\n' % (num_issues, _RebasePath(result_path), _LINT_MD_URL)) if not silent: print(msg, file=sys.stderr) if can_fail_build: raise Exception('Lint failed.') logging.info('Lint completed')