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

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

Issue 2708133002: Android: Android studio single module per target (Closed)
Patch Set: ) 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 | « no previous file | 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 collections
10 import glob 11 import glob
11 import logging 12 import logging
12 import os 13 import os
13 import re 14 import re
14 import shutil 15 import shutil
15 import subprocess 16 import subprocess
16 import sys 17 import sys
17 import zipfile 18 import zipfile
18 19
19 _BUILD_ANDROID = os.path.join(os.path.dirname(__file__), os.pardir) 20 _BUILD_ANDROID = os.path.join(os.path.dirname(__file__), os.pardir)
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 return self.BuildConfig()['gradle'] 191 return self.BuildConfig()['gradle']
191 192
192 def Javac(self): 193 def Javac(self):
193 return self.BuildConfig()['javac'] 194 return self.BuildConfig()['javac']
194 195
195 def GetType(self): 196 def GetType(self):
196 """Returns the target type from its .build_config.""" 197 """Returns the target type from its .build_config."""
197 return self.DepsInfo()['type'] 198 return self.DepsInfo()['type']
198 199
199 def ResZips(self): 200 def ResZips(self):
200 return self.DepsInfo().get('owned_resources_zips') 201 return self.DepsInfo().get('owned_resources_zips', [])
202
203 def ResDirs(self):
204 return self.DepsInfo().get('owned_resources_dirs', [])
201 205
202 def JavaFiles(self): 206 def JavaFiles(self):
203 if self._java_files is None: 207 if self._java_files is None:
204 java_sources_file = self.Gradle().get('java_sources_file') 208 java_sources_file = self.Gradle().get('java_sources_file')
205 java_files = [] 209 java_files = []
206 if java_sources_file: 210 if java_sources_file:
207 java_sources_file = _RebasePath(java_sources_file) 211 java_sources_file = _RebasePath(java_sources_file)
208 java_files = build_utils.ReadSourcesList(java_sources_file) 212 java_files = build_utils.ReadSourcesList(java_sources_file)
209 self._java_files = java_files 213 self._java_files = java_files
210 return self._java_files 214 return self._java_files
211 215
212 def GeneratedJavaFiles(self): 216 def GeneratedJavaFiles(self):
213 return [p for p in self.JavaFiles() if not p.startswith('..')] 217 return [p for p in self.JavaFiles() if not p.startswith('..')]
214 218
215 def PrebuiltJars(self): 219 def PrebuiltJars(self):
216 return self.Gradle()['dependent_prebuilt_jars'] 220 return self.Gradle().get('dependent_prebuilt_jars', [])
217 221
218 def AllEntries(self): 222 def AllEntries(self):
219 """Returns a list of all entries that the current entry depends on. 223 """Returns a list of all entries that the current entry depends on.
220 224
221 This includes the entry itself to make iterating simpler.""" 225 This includes the entry itself to make iterating simpler."""
222 if self._all_entries is None: 226 if self._all_entries is None:
223 logging.debug('Generating entries for %s', self.GnTarget()) 227 logging.debug('Generating entries for %s', self.GnTarget())
224 deps = [_ProjectEntry.FromBuildConfigPath(p) 228 deps = [_ProjectEntry.FromBuildConfigPath(p)
225 for p in self.Gradle()['dependent_android_projects']] 229 for p in self.Gradle()['dependent_android_projects']]
226 deps.extend(_ProjectEntry.FromBuildConfigPath(p) 230 deps.extend(_ProjectEntry.FromBuildConfigPath(p)
227 for p in self.Gradle()['dependent_java_projects']) 231 for p in self.Gradle()['dependent_java_projects'])
228 all_entries = set() 232 all_entries = set()
229 for dep in deps: 233 for dep in deps:
230 all_entries.update(dep.AllEntries()) 234 all_entries.update(dep.AllEntries())
231 all_entries.add(self) 235 all_entries.add(self)
232 self._all_entries = list(all_entries) 236 self._all_entries = list(all_entries)
233 return self._all_entries 237 return self._all_entries
234 238
235 239
236 class _ProjectContextGenerator(object): 240 class _ProjectContextGenerator(object):
237 """Helper class to generate gradle build files""" 241 """Helper class to generate gradle build files"""
238 def __init__(self, project_dir, build_vars, use_gradle_process_resources, 242 def __init__(self, project_dir, build_vars, use_gradle_process_resources,
239 jinja_processor): 243 jinja_processor, split_projects):
240 self.project_dir = project_dir 244 self.project_dir = project_dir
241 self.build_vars = build_vars 245 self.build_vars = build_vars
242 self.use_gradle_process_resources = use_gradle_process_resources 246 self.use_gradle_process_resources = use_gradle_process_resources
243 self.jinja_processor = jinja_processor 247 self.jinja_processor = jinja_processor
248 self.split_projects = split_projects
244 249
245 def _GenJniLibs(self, entry): 250 def _GenJniLibs(self, root_entry):
246 native_section = entry.BuildConfig().get('native') 251 libraries = []
247 if native_section: 252 for entry in self._GetEntries(root_entry):
248 jni_libs = _CreateJniLibsDir( 253 libraries += entry.BuildConfig().get('native', [])
249 constants.GetOutDirectory(), self.EntryOutputDir(entry), 254 if libraries:
250 native_section.get('libraries')) 255 return _CreateJniLibsDir(constants.GetOutDirectory(),
251 else: 256 self.EntryOutputDir(root_entry), libraries)
252 jni_libs = [] 257 return []
253 return jni_libs
254 258
255 def _GenJavaDirs(self, entry): 259 def _GenJavaDirs(self, root_entry):
260 java_files = []
261 for entry in self._GetEntries(root_entry):
262 java_files += entry.JavaFiles()
256 java_dirs, excludes = _ComputeJavaSourceDirsAndExcludes( 263 java_dirs, excludes = _ComputeJavaSourceDirsAndExcludes(
257 constants.GetOutDirectory(), entry.JavaFiles()) 264 constants.GetOutDirectory(), java_files)
258 if self.Srcjars(entry):
259 java_dirs.append(
260 os.path.join(self.EntryOutputDir(entry), _SRCJARS_SUBDIR))
261 return java_dirs, excludes 265 return java_dirs, excludes
262 266
263 def _GenResDirs(self, entry):
264 res_dirs = list(entry.DepsInfo().get('owned_resources_dirs', []))
265 if entry.ResZips():
266 res_dirs.append(os.path.join(self.EntryOutputDir(entry), _RES_SUBDIR))
267 return res_dirs
268
269 def _GenCustomManifest(self, entry): 267 def _GenCustomManifest(self, entry):
270 """Returns the path to the generated AndroidManifest.xml. 268 """Returns the path to the generated AndroidManifest.xml.
271 269
272 Gradle uses package id from manifest when generating R.class. So, we need 270 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 271 to generate a custom manifest if we let gradle process resources. We cannot
274 simply set android.defaultConfig.applicationId because it is not supported 272 simply set android.defaultConfig.applicationId because it is not supported
275 for library targets.""" 273 for library targets."""
276 resource_packages = entry.Javac().get('resource_packages') 274 resource_packages = entry.Javac().get('resource_packages')
277 if not resource_packages: 275 if not resource_packages:
278 logging.debug('Target ' + entry.GnTarget() + ' includes resources from ' 276 logging.debug('Target ' + entry.GnTarget() + ' includes resources from '
(...skipping 11 matching lines...) Expand all
290 output_file = os.path.join( 288 output_file = os.path.join(
291 self.EntryOutputDir(entry), 'AndroidManifest.xml') 289 self.EntryOutputDir(entry), 'AndroidManifest.xml')
292 data = self.jinja_processor.Render(_TemplatePath('manifest'), variables) 290 data = self.jinja_processor.Render(_TemplatePath('manifest'), variables)
293 _WriteFile(output_file, data) 291 _WriteFile(output_file, data)
294 292
295 return output_file 293 return output_file
296 294
297 def _Relativize(self, entry, paths): 295 def _Relativize(self, entry, paths):
298 return _RebasePath(paths, self.EntryOutputDir(entry)) 296 return _RebasePath(paths, self.EntryOutputDir(entry))
299 297
300 def EntryOutputDir(self, entry): 298 def _Srcjars(self, entry):
301 return os.path.join(self.project_dir, entry.GradleSubdir())
302
303 def Srcjars(self, entry):
304 srcjars = _RebasePath(entry.Gradle().get('bundled_srcjars', [])) 299 srcjars = _RebasePath(entry.Gradle().get('bundled_srcjars', []))
305 if not self.use_gradle_process_resources: 300 if not self.use_gradle_process_resources:
306 srcjars += _RebasePath(entry.BuildConfig()['javac']['srcjars']) 301 srcjars += _RebasePath(entry.BuildConfig()['javac']['srcjars'])
307 return srcjars 302 return srcjars
308 303
309 def GeneratedInputs(self, entry): 304 def _GetEntries(self, entry):
310 generated_inputs = [] 305 if self.split_projects:
311 generated_inputs.extend(self.Srcjars(entry)) 306 return [entry]
312 generated_inputs.extend(_RebasePath(entry.ResZips())) 307 return entry.AllEntries()
313 generated_inputs.extend(entry.GeneratedJavaFiles())
314 generated_inputs.extend(entry.PrebuiltJars())
315 return generated_inputs
316 308
317 def Generate(self, entry): 309 def EntryOutputDir(self, entry):
318 variables = {} 310 return os.path.join(self.project_dir, entry.GradleSubdir())
319 java_dirs, excludes = self._GenJavaDirs(entry) 311
320 variables['java_dirs'] = self._Relativize(entry, java_dirs) 312 def AllSrcjars(self, root_entry):
321 variables['java_excludes'] = excludes 313 srcjars = []
322 variables['jni_libs'] = self._Relativize(entry, self._GenJniLibs(entry)) 314 for entry in self._GetEntries(root_entry):
323 variables['res_dirs'] = self._Relativize(entry, self._GenResDirs(entry)) 315 srcjars += self._Srcjars(entry)
324 android_manifest = entry.Gradle().get('android_manifest') 316 return set(srcjars)
325 if not android_manifest: 317
326 android_manifest = self._GenCustomManifest(entry) 318 def AllResZips(self, root_entry):
327 variables['android_manifest'] = self._Relativize(entry, android_manifest) 319 res_zips = []
320 for entry in self._GetEntries(root_entry):
321 res_zips += entry.ResZips()
322 return set(_RebasePath(res_zips))
323
324 def GeneratedInputs(self, root_entry):
325 generated_inputs = set(self.AllResZips(root_entry))
326 generated_inputs.update(self.AllSrcjars(root_entry))
327 for entry in self._GetEntries(root_entry):
328 generated_inputs.update(entry.GeneratedJavaFiles())
329 generated_inputs.update(entry.PrebuiltJars())
330 return set(generated_inputs)
331
332 def Generate(self, root_entry):
328 # TODO(agrieve): Add an option to use interface jars and see if that speeds 333 # TODO(agrieve): Add an option to use interface jars and see if that speeds
329 # things up at all. 334 # things up at all.
330 variables['prebuilts'] = self._Relativize(entry, entry.PrebuiltJars()) 335 variables = collections.defaultdict(list)
agrieve 2017/02/21 17:52:05 nit: some variables can be non-lists, so using a d
331 deps = [_ProjectEntry.FromBuildConfigPath(p) 336 java_dirs, excludes = self._GenJavaDirs(root_entry)
332 for p in entry.Gradle()['dependent_android_projects']] 337 java_dirs.sort()
333 variables['android_project_deps'] = [d.ProjectName() for d in deps] 338 variables['java_dirs'] += self._Relativize(root_entry, java_dirs)
334 deps = [_ProjectEntry.FromBuildConfigPath(p) 339 variables['java_dirs'].append(_SRCJARS_SUBDIR)
335 for p in entry.Gradle()['dependent_java_projects']] 340 variables['java_excludes'] += excludes
336 variables['java_project_deps'] = [d.ProjectName() for d in deps] 341 variables['jni_libs'] += self._Relativize(
342 root_entry, set(self._GenJniLibs(root_entry)))
343 for entry in self._GetEntries(root_entry):
344 variables['prebuilts'] += entry.PrebuiltJars()
345 variables['res_dirs'] += entry.ResDirs()
346 variables['prebuilts'] = self._Relativize(
347 root_entry, set(variables['prebuilts']))
348 variables['res_dirs'] = self._Relativize(
349 root_entry, set(variables['res_dirs']))
350 variables['res_dirs'].append(_RES_SUBDIR)
351 android_manifest = root_entry.Gradle().get('android_manifest')
352 if not android_manifest:
353 android_manifest = self._GenCustomManifest(root_entry)
354 variables['android_manifest'] = self._Relativize(
355 root_entry, android_manifest)
356 if self.split_projects:
357 deps = [_ProjectEntry.FromBuildConfigPath(p)
358 for p in root_entry.Gradle()['dependent_android_projects']]
359 variables['android_project_deps'] = [d.ProjectName() for d in deps]
360 deps = [_ProjectEntry.FromBuildConfigPath(p)
361 for p in root_entry.Gradle()['dependent_java_projects']]
362 variables['java_project_deps'] = [d.ProjectName() for d in deps]
337 return variables 363 return variables
338 364
339 365
340 def _ComputeJavaSourceDirs(java_files): 366 def _ComputeJavaSourceDirs(java_files):
341 """Returns a dictionary of source dirs with each given files in one.""" 367 """Returns a dictionary of source dirs with each given files in one."""
342 found_roots = {} 368 found_roots = {}
343 for path in java_files: 369 for path in java_files:
344 path_root = path 370 path_root = path
345 # Recognize these tokens as top-level. 371 # Recognize these tokens as top-level.
346 while True: 372 while True:
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after
601 dest='targets', 627 dest='targets',
602 action='append', 628 action='append',
603 help='GN target to generate project for. ' 629 help='GN target to generate project for. '
604 'May be repeated.') 630 'May be repeated.')
605 parser.add_argument('--project-dir', 631 parser.add_argument('--project-dir',
606 help='Root of the output project.', 632 help='Root of the output project.',
607 default=os.path.join('$CHROMIUM_OUTPUT_DIR', 'gradle')) 633 default=os.path.join('$CHROMIUM_OUTPUT_DIR', 'gradle'))
608 parser.add_argument('--all', 634 parser.add_argument('--all',
609 action='store_true', 635 action='store_true',
610 help='Generate all java targets (slows down IDE)') 636 help='Generate all java targets (slows down IDE)')
611 parser.add_argument('--use-gradle-process-resources', 637 parser.add_argument('--use-gradle-process-resources',
agrieve 2017/02/21 17:52:05 I'm guessing that non-split projects will currentl
612 action='store_true', 638 action='store_true',
613 help='Have gradle generate R.java rather than ninja') 639 help='Have gradle generate R.java rather than ninja')
640 parser.add_argument('--split-projects',
641 action='store_true',
642 help='Split projects by their gn deps rather than '
643 'combining all the dependencies of each target')
614 args = parser.parse_args() 644 args = parser.parse_args()
615 if args.output_directory: 645 if args.output_directory:
616 constants.SetOutputDirectory(args.output_directory) 646 constants.SetOutputDirectory(args.output_directory)
617 constants.CheckOutputDirectory() 647 constants.CheckOutputDirectory()
618 output_dir = constants.GetOutDirectory() 648 output_dir = constants.GetOutDirectory()
619 devil_chromium.Initialize(output_directory=output_dir) 649 devil_chromium.Initialize(output_directory=output_dir)
620 run_tests_helper.SetLogLevel(args.verbose_count) 650 run_tests_helper.SetLogLevel(args.verbose_count)
621 651
622 _gradle_output_dir = os.path.abspath( 652 _gradle_output_dir = os.path.abspath(
623 args.project_dir.replace('$CHROMIUM_OUTPUT_DIR', output_dir)) 653 args.project_dir.replace('$CHROMIUM_OUTPUT_DIR', output_dir))
624 jinja_processor = jinja_template.JinjaProcessor(_FILE_DIR) 654 jinja_processor = jinja_template.JinjaProcessor(_FILE_DIR)
625 build_vars = _ReadBuildVars(output_dir) 655 build_vars = _ReadBuildVars(output_dir)
626 generator = _ProjectContextGenerator(_gradle_output_dir, build_vars, 656 generator = _ProjectContextGenerator(_gradle_output_dir, build_vars,
627 args.use_gradle_process_resources, jinja_processor) 657 args.use_gradle_process_resources, jinja_processor, args.split_projects)
628 logging.warning('Creating project at: %s', generator.project_dir) 658 logging.warning('Creating project at: %s', generator.project_dir)
629 659
630 if args.all: 660 if args.all:
631 # Run GN gen if necessary (faster than running "gn gen" in the no-op case). 661 # Run GN gen if necessary (faster than running "gn gen" in the no-op case).
632 _RunNinja(constants.GetOutDirectory(), ['build.ninja']) 662 _RunNinja(constants.GetOutDirectory(), ['build.ninja'])
633 # Query ninja for all __build_config targets. 663 # Query ninja for all __build_config targets.
634 targets = _QueryForAllGnTargets(output_dir) 664 targets = _QueryForAllGnTargets(output_dir)
635 else: 665 else:
636 targets = args.targets or _DEFAULT_TARGETS 666 targets = args.targets or _DEFAULT_TARGETS
637 targets = [re.sub(r'_test_apk$', '_test_apk__apk', t) for t in targets] 667 targets = [re.sub(r'_test_apk$', '_test_apk__apk', t) for t in targets]
638 # TODO(wnwen): Utilize Gradle's test constructs for our junit tests? 668 # TODO(wnwen): Utilize Gradle's test constructs for our junit tests?
639 targets = [re.sub(r'_junit_tests$', '_junit_tests__java_binary', t) 669 targets = [re.sub(r'_junit_tests$', '_junit_tests__java_binary', t)
640 for t in targets] 670 for t in targets]
641 671
642 main_entries = [_ProjectEntry.FromGnTarget(t) for t in targets] 672 main_entries = [_ProjectEntry.FromGnTarget(t) for t in targets]
643 673
644 logging.warning('Building .build_config files...') 674 logging.warning('Building .build_config files...')
645 _RunNinja(output_dir, [e.NinjaBuildConfigTarget() for e in main_entries]) 675 _RunNinja(output_dir, [e.NinjaBuildConfigTarget() for e in main_entries])
646 676
647 # There are many unused libraries, so restrict to those that are actually used 677 # There are many unused libraries, so restrict to those that are actually used
648 # when using --all. 678 # when using --all.
649 if args.all: 679 if args.all:
650 main_entries = [e for e in main_entries if e.GetType() == 'android_apk'] 680 main_entries = [e for e in main_entries if e.GetType() == 'android_apk']
651 681
652 all_entries = _FindAllProjectEntries(main_entries) 682 if args.split_projects:
653 logging.info('Found %d dependent build_config targets.', len(all_entries)) 683 main_entries = _FindAllProjectEntries(main_entries)
654 entries = _CombineTestEntries(all_entries) 684 logging.info('Found %d dependent build_config targets.', len(main_entries))
685 entries = _CombineTestEntries(main_entries)
655 logging.info('Creating %d projects for targets.', len(entries)) 686 logging.info('Creating %d projects for targets.', len(entries))
656 687
657 logging.warning('Writing .gradle files...') 688 logging.warning('Writing .gradle files...')
658 project_entries = [] 689 project_entries = []
659 zip_tuples = [] 690 zip_tuples = []
660 generated_inputs = [] 691 generated_inputs = []
661 for entry in entries: 692 for entry in entries:
662 if entry.GetType() not in ('android_apk', 'java_library', 'java_binary'): 693 if entry.GetType() not in ('android_apk', 'java_library', 'java_binary'):
663 continue 694 continue
664 695
665 data = _GenerateGradleFile(entry, generator, build_vars, jinja_processor) 696 data = _GenerateGradleFile(entry, generator, build_vars, jinja_processor)
666 if data: 697 if data:
667 project_entries.append(entry) 698 project_entries.append(entry)
668 # Build all paths references by .gradle that exist within output_dir. 699 # Build all paths references by .gradle that exist within output_dir.
669 generated_inputs.extend(generator.GeneratedInputs(entry)) 700 generated_inputs.extend(generator.GeneratedInputs(entry))
670 zip_tuples.extend( 701 zip_tuples.extend(
671 (s, os.path.join(generator.EntryOutputDir(entry), _SRCJARS_SUBDIR)) 702 (s, os.path.join(generator.EntryOutputDir(entry), _SRCJARS_SUBDIR))
672 for s in generator.Srcjars(entry)) 703 for s in generator.AllSrcjars(entry))
673 zip_tuples.extend( 704 zip_tuples.extend(
674 (s, os.path.join(generator.EntryOutputDir(entry), _RES_SUBDIR)) 705 (s, os.path.join(generator.EntryOutputDir(entry), _RES_SUBDIR))
675 for s in _RebasePath(entry.ResZips())) 706 for s in generator.AllResZips(entry))
676 _WriteFile( 707 _WriteFile(
677 os.path.join(generator.EntryOutputDir(entry), 'build.gradle'), data) 708 os.path.join(generator.EntryOutputDir(entry), 'build.gradle'), data)
678 709
679 _WriteFile(os.path.join(generator.project_dir, 'build.gradle'), 710 _WriteFile(os.path.join(generator.project_dir, 'build.gradle'),
680 _GenerateRootGradle(jinja_processor)) 711 _GenerateRootGradle(jinja_processor))
681 712
682 _WriteFile(os.path.join(generator.project_dir, 'settings.gradle'), 713 _WriteFile(os.path.join(generator.project_dir, 'settings.gradle'),
683 _GenerateSettingsGradle(project_entries)) 714 _GenerateSettingsGradle(project_entries))
684 715
685 sdk_path = _RebasePath(build_vars['android_sdk_root']) 716 sdk_path = _RebasePath(build_vars['android_sdk_root'])
686 _WriteFile(os.path.join(generator.project_dir, 'local.properties'), 717 _WriteFile(os.path.join(generator.project_dir, 'local.properties'),
687 _GenerateLocalProperties(sdk_path)) 718 _GenerateLocalProperties(sdk_path))
688 719
689 if generated_inputs: 720 if generated_inputs:
690 logging.warning('Building generated source files...') 721 logging.warning('Building generated source files...')
691 targets = _RebasePath(generated_inputs, output_dir) 722 targets = _RebasePath(generated_inputs, output_dir)
692 _RunNinja(output_dir, targets) 723 _RunNinja(output_dir, targets)
693 724
694 if zip_tuples: 725 if zip_tuples:
695 _ExtractZips(generator.project_dir, zip_tuples) 726 _ExtractZips(generator.project_dir, zip_tuples)
696 727
697 logging.warning('Project created! (%d subprojects)', len(project_entries)) 728 logging.warning('Project created! (%d subprojects)', len(project_entries))
698 logging.warning('Generated projects work best with Android Studio 2.2') 729 logging.warning('Generated projects work best with Android Studio 2.2')
699 logging.warning('For more tips: https://chromium.googlesource.com/chromium' 730 logging.warning('For more tips: https://chromium.googlesource.com/chromium'
700 '/src.git/+/master/docs/android_studio.md') 731 '/src.git/+/master/docs/android_studio.md')
701 732
702 733
703 if __name__ == '__main__': 734 if __name__ == '__main__':
704 main() 735 main()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698