Index: build/android/gradle/generate_gradle.py |
diff --git a/build/android/gradle/generate_gradle.py b/build/android/gradle/generate_gradle.py |
index 2b529f0b6e30206e53bff013ffc1ac2b97945b84..8b6577839a005a3370b6d93cbdee63568aa1c730 100755 |
--- a/build/android/gradle/generate_gradle.py |
+++ b/build/android/gradle/generate_gradle.py |
@@ -117,39 +117,67 @@ def _QueryForAllGnTargets(output_dir): |
return ret |
+def _Dedup(entries): |
agrieve
2017/02/17 03:07:01
nit: this doesn't save any lines over just calling
Peter Wen
2017/02/17 16:37:26
Done.
|
+ """Return unique entries in iterable in the same order.""" |
+ return _DedupAgainst(entries, []) |
+ |
+ |
+def _DedupAgainst(entries, existing_entries): |
+ """Return unique entries in iterable given existing entries.""" |
agrieve
2017/02/17 03:07:01
You return a list, so I'd say that rather than ite
Peter Wen
2017/02/17 16:37:26
Done.
|
+ seen = set(existing_entries) |
+ unique_entries = [] |
+ for entry in entries: |
+ if entry not in seen: |
+ unique_entries.append(entry) |
+ seen.add(entry) |
+ return unique_entries |
+ |
+ |
class _ProjectEntry(object): |
"""Helper class for project entries.""" |
+ |
+ _cached_entries = {} |
+ |
def __init__(self, gn_target): |
- assert gn_target.startswith('//'), gn_target |
- if ':' not in gn_target: |
- gn_target = '%s:%s' % (gn_target, os.path.basename(gn_target)) |
+ # Use _ProjectEntry.FromGnTarget instead for caching. |
self._gn_target = gn_target |
self._build_config = None |
self._java_files = None |
+ self._all_entries = None |
self.android_test_entry = None |
@classmethod |
+ def FromGnTarget(cls, gn_target): |
+ assert gn_target.startswith('//'), gn_target |
+ if ':' not in gn_target: |
+ gn_target = '%s:%s' % (gn_target, os.path.basename(gn_target)) |
+ if gn_target not in cls._cached_entries: |
+ cls._cached_entries[gn_target] = cls(gn_target) |
+ return cls._cached_entries[gn_target] |
+ |
+ @classmethod |
def FromBuildConfigPath(cls, path): |
prefix = 'gen/' |
suffix = '.build_config' |
assert path.startswith(prefix) and path.endswith(suffix), path |
subdir = path[len(prefix):-len(suffix)] |
- return cls('//%s:%s' % (os.path.split(subdir))) |
+ gn_target = '//%s:%s' % (os.path.split(subdir)) |
+ return cls.FromGnTarget(gn_target) |
def __hash__(self): |
- return hash(self._gn_target) |
+ return hash(self.GnTarget()) |
agrieve
2017/02/17 03:07:01
nit: I think it's generally better to leave these
Peter Wen
2017/02/17 16:37:25
Done.
We don't do any checks/calculations in the
|
def __eq__(self, other): |
- return self._gn_target == other.GnTarget() |
+ return self.GnTarget() == other.GnTarget() |
def GnTarget(self): |
return self._gn_target |
def NinjaTarget(self): |
- return self._gn_target[2:] |
+ return self.GnTarget()[2:] |
def GnBuildConfigTarget(self): |
- return '%s__build_config' % self._gn_target |
+ return '%s__build_config' % self.GnTarget() |
def NinjaBuildConfigTarget(self): |
return '%s__build_config' % self.NinjaTarget() |
@@ -195,6 +223,26 @@ class _ProjectEntry(object): |
self._java_files = java_files |
return self._java_files |
+ def SubdirJavaFiles(self): |
agrieve
2017/02/17 03:07:01
Is this the same as saying generated java files? I
Peter Wen
2017/02/17 16:37:25
Done.
|
+ return [p for p in self.JavaFiles() if not p.startswith('..')] |
+ |
+ def PrebuiltJars(self): |
+ return self.Gradle()['dependent_prebuilt_jars'] |
+ |
+ def AllEntries(self): |
agrieve
2017/02/17 03:07:01
This is worth adding pydoc for.
Peter Wen
2017/02/17 16:37:26
Done.
|
+ if self._all_entries is None: |
+ logging.debug('Generating entries for %s', self.GnTarget()) |
+ deps = [_ProjectEntry.FromBuildConfigPath(p) |
+ for p in self.Gradle()['dependent_android_projects']] |
+ deps.extend([_ProjectEntry.FromBuildConfigPath(p) |
agrieve
2017/02/17 03:07:01
nit: shouldn't need the [].
Peter Wen
2017/02/17 16:37:26
Done.
|
+ for p in self.Gradle()['dependent_java_projects']]) |
+ all_entries = [] |
+ for dep in deps: |
+ all_entries += dep.AllEntries() |
agrieve
2017/02/17 03:07:01
I think it might be easier to follow if you de-dup
Peter Wen
2017/02/17 16:37:25
Switched to using sets instead, it's better to sor
|
+ all_entries.append(self) |
+ self._all_entries = _Dedup(all_entries) |
+ return self._all_entries |
+ |
class _ProjectContextGenerator(object): |
"""Helper class to generate gradle build files""" |
@@ -216,7 +264,7 @@ class _ProjectContextGenerator(object): |
return jni_libs |
def _GenJavaDirs(self, entry): |
- java_dirs, excludes = _CreateJavaSourceDir( |
+ java_dirs, excludes = _ComputeJavaSourceDirsAndExcludes( |
constants.GetOutDirectory(), entry.JavaFiles()) |
if self.Srcjars(entry): |
java_dirs.append( |
@@ -230,18 +278,19 @@ class _ProjectContextGenerator(object): |
return res_dirs |
def _GenCustomManifest(self, entry): |
- """Returns the path to the generated AndroidManifest.xml.""" |
- javac = entry.Javac() |
- resource_packages = javac['resource_packages'] |
- output_file = os.path.join( |
- self.EntryOutputDir(entry), 'AndroidManifest.xml') |
+ """Returns the path to the generated AndroidManifest.xml. |
+ Gradle uses package id from manifest when generating R.class. So, we need |
+ to generate a custom manifest if we let gradle process resources. We cannot |
+ simply set android.defaultConfig.applicationId because it is not supported |
+ for library targets.""" |
+ resource_packages = entry.Javac().get('resource_packages') |
if not resource_packages: |
- logging.error('Target ' + entry.GnTarget() + ' includes resources from ' |
+ logging.debug('Target ' + entry.GnTarget() + ' includes resources from ' |
'unknown package. Unable to process with gradle.') |
return _DEFAULT_ANDROID_MANIFEST_PATH |
elif len(resource_packages) > 1: |
- logging.error('Target ' + entry.GnTarget() + ' includes resources from ' |
+ logging.debug('Target ' + entry.GnTarget() + ' includes resources from ' |
'multiple packages. Unable to process with gradle.') |
return _DEFAULT_ANDROID_MANIFEST_PATH |
@@ -249,6 +298,8 @@ class _ProjectContextGenerator(object): |
variables['compile_sdk_version'] = self.build_vars['android_sdk_version'] |
variables['package'] = resource_packages[0] |
+ output_file = os.path.join( |
+ self.EntryOutputDir(entry), 'AndroidManifest.xml') |
data = self.jinja_processor.Render(_TemplatePath('manifest'), variables) |
_WriteFile(output_file, data) |
@@ -270,9 +321,8 @@ class _ProjectContextGenerator(object): |
generated_inputs = [] |
generated_inputs.extend(self.Srcjars(entry)) |
generated_inputs.extend(_RebasePath(entry.ResZips())) |
- generated_inputs.extend( |
- p for p in entry.JavaFiles() if not p.startswith('..')) |
- generated_inputs.extend(entry.Gradle()['dependent_prebuilt_jars']) |
+ generated_inputs.extend(entry.SubdirJavaFiles()) |
+ generated_inputs.extend(entry.PrebuiltJars()) |
return generated_inputs |
def Generate(self, entry): |
@@ -284,22 +334,14 @@ class _ProjectContextGenerator(object): |
variables['res_dirs'] = self._Relativize(entry, self._GenResDirs(entry)) |
android_manifest = entry.Gradle().get('android_manifest') |
if not android_manifest: |
- # Gradle uses package id from manifest when generating R.class. So, we |
- # need to generate a custom manifest if we let gradle process resources. |
- # We cannot simply set android.defaultConfig.applicationId because it is |
- # not supported for library targets. |
- if variables['res_dirs']: |
- android_manifest = self._GenCustomManifest(entry) |
- else: |
- android_manifest = _DEFAULT_ANDROID_MANIFEST_PATH |
+ android_manifest = self._GenCustomManifest(entry) |
variables['android_manifest'] = self._Relativize(entry, android_manifest) |
+ # TODO(agrieve): Add an option to use interface jars and see if that speeds |
+ # things up at all. |
+ variables['prebuilts'] = self._Relativize(entry, entry.PrebuiltJars()) |
deps = [_ProjectEntry.FromBuildConfigPath(p) |
for p in entry.Gradle()['dependent_android_projects']] |
variables['android_project_deps'] = [d.ProjectName() for d in deps] |
- # TODO(agrieve): Add an option to use interface jars and see if that speeds |
- # things up at all. |
- variables['prebuilts'] = self._Relativize( |
- entry, entry.Gradle()['dependent_prebuilt_jars']) |
deps = [_ProjectEntry.FromBuildConfigPath(p) |
for p in entry.Gradle()['dependent_java_projects']] |
variables['java_project_deps'] = [d.ProjectName() for d in deps] |
@@ -350,7 +392,7 @@ def _ComputeExcludeFilters(wanted_files, unwanted_files, parent_dir): |
return excludes |
-def _CreateJavaSourceDir(output_dir, java_files): |
+def _ComputeJavaSourceDirsAndExcludes(output_dir, java_files): |
"""Computes the list of java source directories and exclude patterns. |
1. Computes the root java source directories from the list of files. |
@@ -461,6 +503,10 @@ def _GenerateGradleFile(entry, generator, build_vars, jinja_processor): |
if entry.android_test_entry: |
variables['android_test'] = generator.Generate( |
entry.android_test_entry) |
+ for key, value in variables['android_test'].iteritems(): |
+ if isinstance(value, list): |
+ variables['android_test'][key] = _DedupAgainst( |
+ value, variables['main'][key]) |
return jinja_processor.Render( |
_TemplatePath(target_type.split('_')[0]), variables) |
@@ -600,7 +646,7 @@ def main(): |
targets = [re.sub(r'_junit_tests$', '_junit_tests__java_binary', t) |
for t in targets] |
- main_entries = [_ProjectEntry(t) for t in targets] |
+ main_entries = [_ProjectEntry.FromGnTarget(t) for t in targets] |
logging.warning('Building .build_config files...') |
_RunNinja(output_dir, [e.NinjaBuildConfigTarget() for e in main_entries]) |