Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(734)

Side by Side Diff: build/android/gradle/generate_gradle.py

Issue 2697313004: Android: Cache android studio project entries (Closed)
Patch Set: Remove dup add docs Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « build/android/gradle/android.jinja ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2016 The Chromium Authors. All rights reserved. 2 # Copyright 2016 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Generates an Android Studio project from a GN target.""" 6 """Generates an Android Studio project from a GN target."""
7 7
8 import argparse 8 import argparse
9 import codecs 9 import codecs
10 import glob 10 import glob
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
114 for line in ninja_output.splitlines(): 114 for line in ninja_output.splitlines():
115 ninja_target = line.rsplit(':', 1)[0] 115 ninja_target = line.rsplit(':', 1)[0]
116 # Ignore root aliases by ensure a : exists. 116 # Ignore root aliases by ensure a : exists.
117 if ':' in ninja_target and ninja_target.endswith('__build_config'): 117 if ':' in ninja_target and ninja_target.endswith('__build_config'):
118 ret.append('//' + ninja_target[:-SUFFIX_LEN]) 118 ret.append('//' + ninja_target[:-SUFFIX_LEN])
119 return ret 119 return ret
120 120
121 121
122 class _ProjectEntry(object): 122 class _ProjectEntry(object):
123 """Helper class for project entries.""" 123 """Helper class for project entries."""
124
125 _cached_entries = {}
126
124 def __init__(self, gn_target): 127 def __init__(self, gn_target):
128 # Use _ProjectEntry.FromGnTarget instead for caching.
129 self._gn_target = gn_target
130 self._build_config = None
131 self._java_files = None
132 self._all_entries = None
133 self.android_test_entry = None
134
135 @classmethod
136 def FromGnTarget(cls, gn_target):
125 assert gn_target.startswith('//'), gn_target 137 assert gn_target.startswith('//'), gn_target
126 if ':' not in gn_target: 138 if ':' not in gn_target:
127 gn_target = '%s:%s' % (gn_target, os.path.basename(gn_target)) 139 gn_target = '%s:%s' % (gn_target, os.path.basename(gn_target))
128 self._gn_target = gn_target 140 if gn_target not in cls._cached_entries:
129 self._build_config = None 141 cls._cached_entries[gn_target] = cls(gn_target)
130 self._java_files = None 142 return cls._cached_entries[gn_target]
131 self.android_test_entry = None
132 143
133 @classmethod 144 @classmethod
134 def FromBuildConfigPath(cls, path): 145 def FromBuildConfigPath(cls, path):
135 prefix = 'gen/' 146 prefix = 'gen/'
136 suffix = '.build_config' 147 suffix = '.build_config'
137 assert path.startswith(prefix) and path.endswith(suffix), path 148 assert path.startswith(prefix) and path.endswith(suffix), path
138 subdir = path[len(prefix):-len(suffix)] 149 subdir = path[len(prefix):-len(suffix)]
139 return cls('//%s:%s' % (os.path.split(subdir))) 150 gn_target = '//%s:%s' % (os.path.split(subdir))
151 return cls.FromGnTarget(gn_target)
140 152
141 def __hash__(self): 153 def __hash__(self):
142 return hash(self._gn_target) 154 return hash(self._gn_target)
143 155
144 def __eq__(self, other): 156 def __eq__(self, other):
145 return self._gn_target == other.GnTarget() 157 return self._gn_target == other.GnTarget()
146 158
147 def GnTarget(self): 159 def GnTarget(self):
148 return self._gn_target 160 return self._gn_target
149 161
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 def JavaFiles(self): 202 def JavaFiles(self):
191 if self._java_files is None: 203 if self._java_files is None:
192 java_sources_file = self.Gradle().get('java_sources_file') 204 java_sources_file = self.Gradle().get('java_sources_file')
193 java_files = [] 205 java_files = []
194 if java_sources_file: 206 if java_sources_file:
195 java_sources_file = _RebasePath(java_sources_file) 207 java_sources_file = _RebasePath(java_sources_file)
196 java_files = build_utils.ReadSourcesList(java_sources_file) 208 java_files = build_utils.ReadSourcesList(java_sources_file)
197 self._java_files = java_files 209 self._java_files = java_files
198 return self._java_files 210 return self._java_files
199 211
212 def GeneratedJavaFiles(self):
213 return [p for p in self.JavaFiles() if not p.startswith('..')]
214
215 def PrebuiltJars(self):
216 return self.Gradle()['dependent_prebuilt_jars']
217
218 def AllEntries(self):
219 """Returns a list of all entries that the current entry depends on.
220
221 This includes the entry itself to make iterating simpler."""
222 if self._all_entries is None:
223 logging.debug('Generating entries for %s', self.GnTarget())
224 deps = [_ProjectEntry.FromBuildConfigPath(p)
225 for p in self.Gradle()['dependent_android_projects']]
226 deps.extend(_ProjectEntry.FromBuildConfigPath(p)
227 for p in self.Gradle()['dependent_java_projects'])
228 all_entries = set()
229 for dep in deps:
230 all_entries.update(dep.AllEntries())
231 all_entries.add(self)
232 self._all_entries = list(all_entries)
233 return self._all_entries
234
200 235
201 class _ProjectContextGenerator(object): 236 class _ProjectContextGenerator(object):
202 """Helper class to generate gradle build files""" 237 """Helper class to generate gradle build files"""
203 def __init__(self, project_dir, build_vars, use_gradle_process_resources, 238 def __init__(self, project_dir, build_vars, use_gradle_process_resources,
204 jinja_processor): 239 jinja_processor):
205 self.project_dir = project_dir 240 self.project_dir = project_dir
206 self.build_vars = build_vars 241 self.build_vars = build_vars
207 self.use_gradle_process_resources = use_gradle_process_resources 242 self.use_gradle_process_resources = use_gradle_process_resources
208 self.jinja_processor = jinja_processor 243 self.jinja_processor = jinja_processor
209 244
210 def _GenJniLibs(self, entry): 245 def _GenJniLibs(self, entry):
211 native_section = entry.BuildConfig().get('native') 246 native_section = entry.BuildConfig().get('native')
212 if native_section: 247 if native_section:
213 jni_libs = _CreateJniLibsDir( 248 jni_libs = _CreateJniLibsDir(
214 constants.GetOutDirectory(), self.EntryOutputDir(entry), 249 constants.GetOutDirectory(), self.EntryOutputDir(entry),
215 native_section.get('libraries')) 250 native_section.get('libraries'))
216 else: 251 else:
217 jni_libs = [] 252 jni_libs = []
218 return jni_libs 253 return jni_libs
219 254
220 def _GenJavaDirs(self, entry): 255 def _GenJavaDirs(self, entry):
221 java_dirs, excludes = _CreateJavaSourceDir( 256 java_dirs, excludes = _ComputeJavaSourceDirsAndExcludes(
222 constants.GetOutDirectory(), entry.JavaFiles()) 257 constants.GetOutDirectory(), entry.JavaFiles())
223 if self.Srcjars(entry): 258 if self.Srcjars(entry):
224 java_dirs.append( 259 java_dirs.append(
225 os.path.join(self.EntryOutputDir(entry), _SRCJARS_SUBDIR)) 260 os.path.join(self.EntryOutputDir(entry), _SRCJARS_SUBDIR))
226 return java_dirs, excludes 261 return java_dirs, excludes
227 262
228 def _GenResDirs(self, entry): 263 def _GenResDirs(self, entry):
229 res_dirs = list(entry.DepsInfo().get('owned_resources_dirs', [])) 264 res_dirs = list(entry.DepsInfo().get('owned_resources_dirs', []))
230 if entry.ResZips(): 265 if entry.ResZips():
231 res_dirs.append(os.path.join(self.EntryOutputDir(entry), _RES_SUBDIR)) 266 res_dirs.append(os.path.join(self.EntryOutputDir(entry), _RES_SUBDIR))
232 return res_dirs 267 return res_dirs
233 268
234 def _GenCustomManifest(self, entry): 269 def _GenCustomManifest(self, entry):
235 """Returns the path to the generated AndroidManifest.xml.""" 270 """Returns the path to the generated AndroidManifest.xml.
236 javac = entry.Javac()
237 resource_packages = javac['resource_packages']
238 output_file = os.path.join(
239 self.EntryOutputDir(entry), 'AndroidManifest.xml')
240 271
272 Gradle uses package id from manifest when generating R.class. So, we need
273 to generate a custom manifest if we let gradle process resources. We cannot
274 simply set android.defaultConfig.applicationId because it is not supported
275 for library targets."""
276 resource_packages = entry.Javac().get('resource_packages')
241 if not resource_packages: 277 if not resource_packages:
242 logging.error('Target ' + entry.GnTarget() + ' includes resources from ' 278 logging.debug('Target ' + entry.GnTarget() + ' includes resources from '
243 'unknown package. Unable to process with gradle.') 279 'unknown package. Unable to process with gradle.')
244 return _DEFAULT_ANDROID_MANIFEST_PATH 280 return _DEFAULT_ANDROID_MANIFEST_PATH
245 elif len(resource_packages) > 1: 281 elif len(resource_packages) > 1:
246 logging.error('Target ' + entry.GnTarget() + ' includes resources from ' 282 logging.debug('Target ' + entry.GnTarget() + ' includes resources from '
247 'multiple packages. Unable to process with gradle.') 283 'multiple packages. Unable to process with gradle.')
248 return _DEFAULT_ANDROID_MANIFEST_PATH 284 return _DEFAULT_ANDROID_MANIFEST_PATH
249 285
250 variables = {} 286 variables = {}
251 variables['compile_sdk_version'] = self.build_vars['android_sdk_version'] 287 variables['compile_sdk_version'] = self.build_vars['android_sdk_version']
252 variables['package'] = resource_packages[0] 288 variables['package'] = resource_packages[0]
253 289
290 output_file = os.path.join(
291 self.EntryOutputDir(entry), 'AndroidManifest.xml')
254 data = self.jinja_processor.Render(_TemplatePath('manifest'), variables) 292 data = self.jinja_processor.Render(_TemplatePath('manifest'), variables)
255 _WriteFile(output_file, data) 293 _WriteFile(output_file, data)
256 294
257 return output_file 295 return output_file
258 296
259 def _Relativize(self, entry, paths): 297 def _Relativize(self, entry, paths):
260 return _RebasePath(paths, self.EntryOutputDir(entry)) 298 return _RebasePath(paths, self.EntryOutputDir(entry))
261 299
262 def EntryOutputDir(self, entry): 300 def EntryOutputDir(self, entry):
263 return os.path.join(self.project_dir, entry.GradleSubdir()) 301 return os.path.join(self.project_dir, entry.GradleSubdir())
264 302
265 def Srcjars(self, entry): 303 def Srcjars(self, entry):
266 srcjars = _RebasePath(entry.Gradle().get('bundled_srcjars', [])) 304 srcjars = _RebasePath(entry.Gradle().get('bundled_srcjars', []))
267 if not self.use_gradle_process_resources: 305 if not self.use_gradle_process_resources:
268 srcjars += _RebasePath(entry.BuildConfig()['javac']['srcjars']) 306 srcjars += _RebasePath(entry.BuildConfig()['javac']['srcjars'])
269 return srcjars 307 return srcjars
270 308
271 def GeneratedInputs(self, entry): 309 def GeneratedInputs(self, entry):
272 generated_inputs = [] 310 generated_inputs = []
273 generated_inputs.extend(self.Srcjars(entry)) 311 generated_inputs.extend(self.Srcjars(entry))
274 generated_inputs.extend(_RebasePath(entry.ResZips())) 312 generated_inputs.extend(_RebasePath(entry.ResZips()))
275 generated_inputs.extend( 313 generated_inputs.extend(entry.GeneratedJavaFiles())
276 p for p in entry.JavaFiles() if not p.startswith('..')) 314 generated_inputs.extend(entry.PrebuiltJars())
277 generated_inputs.extend(entry.Gradle()['dependent_prebuilt_jars'])
278 return generated_inputs 315 return generated_inputs
279 316
280 def Generate(self, entry): 317 def Generate(self, entry):
281 variables = {} 318 variables = {}
282 java_dirs, excludes = self._GenJavaDirs(entry) 319 java_dirs, excludes = self._GenJavaDirs(entry)
283 variables['java_dirs'] = self._Relativize(entry, java_dirs) 320 variables['java_dirs'] = self._Relativize(entry, java_dirs)
284 variables['java_excludes'] = excludes 321 variables['java_excludes'] = excludes
285 variables['jni_libs'] = self._Relativize(entry, self._GenJniLibs(entry)) 322 variables['jni_libs'] = self._Relativize(entry, self._GenJniLibs(entry))
286 variables['res_dirs'] = self._Relativize(entry, self._GenResDirs(entry)) 323 variables['res_dirs'] = self._Relativize(entry, self._GenResDirs(entry))
287 android_manifest = entry.Gradle().get('android_manifest') 324 android_manifest = entry.Gradle().get('android_manifest')
288 if not android_manifest: 325 if not android_manifest:
289 # Gradle uses package id from manifest when generating R.class. So, we 326 android_manifest = self._GenCustomManifest(entry)
290 # need to generate a custom manifest if we let gradle process resources.
291 # We cannot simply set android.defaultConfig.applicationId because it is
292 # not supported for library targets.
293 if variables['res_dirs']:
294 android_manifest = self._GenCustomManifest(entry)
295 else:
296 android_manifest = _DEFAULT_ANDROID_MANIFEST_PATH
297 variables['android_manifest'] = self._Relativize(entry, android_manifest) 327 variables['android_manifest'] = self._Relativize(entry, android_manifest)
328 # TODO(agrieve): Add an option to use interface jars and see if that speeds
329 # things up at all.
330 variables['prebuilts'] = self._Relativize(entry, entry.PrebuiltJars())
298 deps = [_ProjectEntry.FromBuildConfigPath(p) 331 deps = [_ProjectEntry.FromBuildConfigPath(p)
299 for p in entry.Gradle()['dependent_android_projects']] 332 for p in entry.Gradle()['dependent_android_projects']]
300 variables['android_project_deps'] = [d.ProjectName() for d in deps] 333 variables['android_project_deps'] = [d.ProjectName() for d in deps]
301 # TODO(agrieve): Add an option to use interface jars and see if that speeds
302 # things up at all.
303 variables['prebuilts'] = self._Relativize(
304 entry, entry.Gradle()['dependent_prebuilt_jars'])
305 deps = [_ProjectEntry.FromBuildConfigPath(p) 334 deps = [_ProjectEntry.FromBuildConfigPath(p)
306 for p in entry.Gradle()['dependent_java_projects']] 335 for p in entry.Gradle()['dependent_java_projects']]
307 variables['java_project_deps'] = [d.ProjectName() for d in deps] 336 variables['java_project_deps'] = [d.ProjectName() for d in deps]
308 return variables 337 return variables
309 338
310 339
311 def _ComputeJavaSourceDirs(java_files): 340 def _ComputeJavaSourceDirs(java_files):
312 """Returns a dictionary of source dirs with each given files in one.""" 341 """Returns a dictionary of source dirs with each given files in one."""
313 found_roots = {} 342 found_roots = {}
314 for path in java_files: 343 for path in java_files:
(...skipping 30 matching lines...) Expand all
345 found_files = set(glob.glob(target_exclude)) 374 found_files = set(glob.glob(target_exclude))
346 valid_files = found_files & files_to_include 375 valid_files = found_files & files_to_include
347 if valid_files: 376 if valid_files:
348 excludes.append(os.path.relpath(unwanted_file, parent_dir)) 377 excludes.append(os.path.relpath(unwanted_file, parent_dir))
349 else: 378 else:
350 excludes.append(os.path.relpath(target_exclude, parent_dir)) 379 excludes.append(os.path.relpath(target_exclude, parent_dir))
351 files_to_exclude -= found_files 380 files_to_exclude -= found_files
352 return excludes 381 return excludes
353 382
354 383
355 def _CreateJavaSourceDir(output_dir, java_files): 384 def _ComputeJavaSourceDirsAndExcludes(output_dir, java_files):
356 """Computes the list of java source directories and exclude patterns. 385 """Computes the list of java source directories and exclude patterns.
357 386
358 1. Computes the root java source directories from the list of files. 387 1. Computes the root java source directories from the list of files.
359 2. Compute exclude patterns that exclude all extra files only. 388 2. Compute exclude patterns that exclude all extra files only.
360 3. Returns the list of java source directories and exclude patterns. 389 3. Returns the list of java source directories and exclude patterns.
361 """ 390 """
362 java_dirs = [] 391 java_dirs = []
363 excludes = [] 392 excludes = []
364 if java_files: 393 if java_files:
365 java_files = _RebasePath(java_files) 394 java_files = _RebasePath(java_files)
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
460 build_vars['android_sdk_build_tools_version']) 489 build_vars['android_sdk_build_tools_version'])
461 variables['compile_sdk_version'] = build_vars['android_sdk_version'] 490 variables['compile_sdk_version'] = build_vars['android_sdk_version']
462 variables['main'] = generator.Generate(entry) 491 variables['main'] = generator.Generate(entry)
463 bootclasspath = gradle.get('bootclasspath') 492 bootclasspath = gradle.get('bootclasspath')
464 if bootclasspath: 493 if bootclasspath:
465 # Must use absolute path here. 494 # Must use absolute path here.
466 variables['bootclasspath'] = _RebasePath(bootclasspath) 495 variables['bootclasspath'] = _RebasePath(bootclasspath)
467 if entry.android_test_entry: 496 if entry.android_test_entry:
468 variables['android_test'] = generator.Generate( 497 variables['android_test'] = generator.Generate(
469 entry.android_test_entry) 498 entry.android_test_entry)
499 for key, value in variables['android_test'].iteritems():
500 if isinstance(value, list):
501 variables['android_test'][key] = list(
502 set(value) - set(variables['main'][key]))
470 503
471 return jinja_processor.Render( 504 return jinja_processor.Render(
472 _TemplatePath(target_type.split('_')[0]), variables) 505 _TemplatePath(target_type.split('_')[0]), variables)
473 506
474 507
475 def _GenerateRootGradle(jinja_processor): 508 def _GenerateRootGradle(jinja_processor):
476 """Returns the data for the root project's build.gradle.""" 509 """Returns the data for the root project's build.gradle."""
477 return jinja_processor.Render(_TemplatePath('root')) 510 return jinja_processor.Render(_TemplatePath('root'))
478 511
479 512
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
599 _RunNinja(constants.GetOutDirectory(), ['build.ninja']) 632 _RunNinja(constants.GetOutDirectory(), ['build.ninja'])
600 # Query ninja for all __build_config targets. 633 # Query ninja for all __build_config targets.
601 targets = _QueryForAllGnTargets(output_dir) 634 targets = _QueryForAllGnTargets(output_dir)
602 else: 635 else:
603 targets = args.targets or _DEFAULT_TARGETS 636 targets = args.targets or _DEFAULT_TARGETS
604 targets = [re.sub(r'_test_apk$', '_test_apk__apk', t) for t in targets] 637 targets = [re.sub(r'_test_apk$', '_test_apk__apk', t) for t in targets]
605 # TODO(wnwen): Utilize Gradle's test constructs for our junit tests? 638 # TODO(wnwen): Utilize Gradle's test constructs for our junit tests?
606 targets = [re.sub(r'_junit_tests$', '_junit_tests__java_binary', t) 639 targets = [re.sub(r'_junit_tests$', '_junit_tests__java_binary', t)
607 for t in targets] 640 for t in targets]
608 641
609 main_entries = [_ProjectEntry(t) for t in targets] 642 main_entries = [_ProjectEntry.FromGnTarget(t) for t in targets]
610 643
611 logging.warning('Building .build_config files...') 644 logging.warning('Building .build_config files...')
612 _RunNinja(output_dir, [e.NinjaBuildConfigTarget() for e in main_entries]) 645 _RunNinja(output_dir, [e.NinjaBuildConfigTarget() for e in main_entries])
613 646
614 # There are many unused libraries, so restrict to those that are actually used 647 # There are many unused libraries, so restrict to those that are actually used
615 # when using --all. 648 # when using --all.
616 if args.all: 649 if args.all:
617 main_entries = [e for e in main_entries if e.GetType() == 'android_apk'] 650 main_entries = [e for e in main_entries if e.GetType() == 'android_apk']
618 651
619 all_entries = _FindAllProjectEntries(main_entries) 652 all_entries = _FindAllProjectEntries(main_entries)
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
662 _ExtractZips(generator.project_dir, zip_tuples) 695 _ExtractZips(generator.project_dir, zip_tuples)
663 696
664 logging.warning('Project created! (%d subprojects)', len(project_entries)) 697 logging.warning('Project created! (%d subprojects)', len(project_entries))
665 logging.warning('Generated projects work best with Android Studio 2.2') 698 logging.warning('Generated projects work best with Android Studio 2.2')
666 logging.warning('For more tips: https://chromium.googlesource.com/chromium' 699 logging.warning('For more tips: https://chromium.googlesource.com/chromium'
667 '/src.git/+/master/docs/android_studio.md') 700 '/src.git/+/master/docs/android_studio.md')
668 701
669 702
670 if __name__ == '__main__': 703 if __name__ == '__main__':
671 main() 704 main()
OLDNEW
« no previous file with comments | « build/android/gradle/android.jinja ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698