def Generate(self, root_entry): # TODO(agrieve): Add an option to use interface jars and see if that speeds # things up at all. variables = {} java_dirs, excludes = self._GenJavaDirs(root_entry) java_dirs.extend( e.GeneratedJavaSubdir() for e in self._GetEntries(root_entry)) self.processed_java_dirs.update(java_dirs) java_dirs.sort() variables['java_dirs'] = self._Relativize(root_entry, java_dirs) variables['java_excludes'] = excludes variables['jni_libs'] = self._Relativize( root_entry, set(self._GenJniLibs(root_entry))) prebuilts = set( p for e in self._GetEntries(root_entry) for p in e.PrebuiltJars()) self.processed_prebuilts.update(prebuilts) variables['prebuilts'] = self._Relativize(root_entry, prebuilts) res_sources_files = _RebasePath( set(p for e in self._GetEntries(root_entry) for p in e.ResSources())) res_sources = [] for res_sources_file in res_sources_files: res_sources.extend(build_utils.ReadSourcesList(res_sources_file)) res_dirs = resource_utils.DeduceResourceDirsFromFileList(res_sources) # Do not add generated resources for the all module since it creates many # duplicates, and currently resources are only used for editing. self.processed_res_dirs.update(res_dirs) variables['res_dirs'] = self._Relativize(root_entry, res_dirs) if self.split_projects: deps = [_ProjectEntry.FromBuildConfigPath(p) for p in root_entry.Gradle()['dependent_android_projects']] variables['android_project_deps'] = [d.ProjectName() for d in deps] deps = [_ProjectEntry.FromBuildConfigPath(p) for p in root_entry.Gradle()['dependent_java_projects']] variables['java_project_deps'] = [d.ProjectName() for d in deps] return variables
def _ParseArgs(args): """Parses command line options. Returns: An options object as from argparse.ArgumentParser.parse_args() """ parser, input_opts, output_opts = resource_utils.ResourceArgsParser() input_opts.add_argument( '--res-sources-path', required=True, help='Path to a list of input resources for this target.') input_opts.add_argument( '--shared-resources', action='store_true', help='Make resources shareable by generating an onResourcesLoaded() ' 'method in the R.java source file.') input_opts.add_argument('--custom-package', help='Optional Java package for main R.java.') input_opts.add_argument( '--android-manifest', help='Optional AndroidManifest.xml path. Only used to extract a package ' 'name for R.java if a --custom-package is not provided.') output_opts.add_argument( '--resource-zip-out', help='Path to a zip archive containing all resources from ' '--resource-dirs, merged into a single directory tree.') output_opts.add_argument( '--srcjar-out', help='Path to .srcjar to contain the generated R.java.') output_opts.add_argument('--r-text-out', help='Path to store the generated R.txt file.') input_opts.add_argument('--strip-drawables', action="store_true", help='Remove drawables from the resources.') options = parser.parse_args(args) resource_utils.HandleCommonOptions(options) with open(options.res_sources_path) as f: options.sources = f.read().splitlines() options.resource_dirs = resource_utils.DeduceResourceDirsFromFileList( options.sources) return options
def _ParseArgs(args): """Parses command line options. Returns: An options object as from argparse.ArgumentParser.parse_args() """ parser = argparse.ArgumentParser(description=__doc__) build_utils.AddDepfileOption(parser) parser.add_argument( '--res-sources-path', required=True, help='Path to a list of input resources for this target.') parser.add_argument( '--r-text-in', help='Path to pre-existing R.txt. Its resource IDs override those found ' 'in the generated R.txt when generating R.java.') parser.add_argument( '--allow-missing-resources', action='store_true', help='Do not fail if some resources exist in the res/ dir but are not ' 'listed in the sources.') parser.add_argument( '--resource-zip-out', help='Path to a zip archive containing all resources from ' '--resource-dirs, merged into a single directory tree.') parser.add_argument('--r-text-out', help='Path to store the generated R.txt file.') parser.add_argument('--strip-drawables', action="store_true", help='Remove drawables from the resources.') options = parser.parse_args(args) with open(options.res_sources_path) as f: options.sources = f.read().splitlines() options.resource_dirs = resource_utils.DeduceResourceDirsFromFileList( options.sources) return options
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, 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), ] if config_path: cmd.extend(['--config', _RebasePath(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 = 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')