OLD | NEW |
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 16 matching lines...) Expand all Loading... |
27 import jinja_template | 27 import jinja_template |
28 from util import build_utils | 28 from util import build_utils |
29 | 29 |
30 | 30 |
31 _DEFAULT_ANDROID_MANIFEST_PATH = os.path.join( | 31 _DEFAULT_ANDROID_MANIFEST_PATH = os.path.join( |
32 host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'AndroidManifest.xml') | 32 host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'AndroidManifest.xml') |
33 _FILE_DIR = os.path.dirname(__file__) | 33 _FILE_DIR = os.path.dirname(__file__) |
34 _SRCJARS_SUBDIR = 'extracted-srcjars' | 34 _SRCJARS_SUBDIR = 'extracted-srcjars' |
35 _JNI_LIBS_SUBDIR = 'symlinked-libs' | 35 _JNI_LIBS_SUBDIR = 'symlinked-libs' |
36 _ARMEABI_SUBDIR = 'armeabi' | 36 _ARMEABI_SUBDIR = 'armeabi' |
| 37 _RES_SUBDIR = 'extracted-res' |
37 | 38 |
38 _DEFAULT_TARGETS = [ | 39 _DEFAULT_TARGETS = [ |
39 # TODO(agrieve): Requires alternate android.jar to compile. | 40 # TODO(agrieve): Requires alternate android.jar to compile. |
40 # '//android_webview:system_webview_apk', | 41 # '//android_webview:system_webview_apk', |
41 '//android_webview/test:android_webview_apk', | 42 '//android_webview/test:android_webview_apk', |
42 '//android_webview/test:android_webview_test_apk', | 43 '//android_webview/test:android_webview_test_apk', |
43 '//base:base_junit_tests', | 44 '//base:base_junit_tests', |
44 '//chrome/android:chrome_junit_tests', | 45 '//chrome/android:chrome_junit_tests', |
45 '//chrome/android:chrome_public_apk', | 46 '//chrome/android:chrome_public_apk', |
46 '//chrome/android:chrome_public_test_apk', | 47 '//chrome/android:chrome_public_test_apk', |
47 '//chrome/android:chrome_sync_shell_apk', | 48 '//chrome/android:chrome_sync_shell_apk', |
48 '//chrome/android:chrome_sync_shell_test_apk', | 49 '//chrome/android:chrome_sync_shell_test_apk', |
49 '//content/public/android:content_junit_tests', | 50 '//content/public/android:content_junit_tests', |
50 '//content/shell/android:content_shell_apk', | 51 '//content/shell/android:content_shell_apk', |
51 ] | 52 ] |
52 | 53 |
53 | 54 |
54 def _TemplatePath(name): | 55 def _TemplatePath(name): |
55 return os.path.join(_FILE_DIR, '{}.jinja'.format(name)) | 56 return os.path.join(_FILE_DIR, '{}.jinja'.format(name)) |
56 | 57 |
57 | 58 |
58 def _RebasePath(path_or_list, new_cwd=None, old_cwd=None): | 59 def _RebasePath(path_or_list, new_cwd=None, old_cwd=None): |
59 """Makes the given path(s) relative to new_cwd, or absolute if not specified. | 60 """Makes the given path(s) relative to new_cwd, or absolute if not specified. |
60 | 61 |
61 If new_cwd is not specified, absolute paths are returned. | 62 If new_cwd is not specified, absolute paths are returned. |
62 If old_cwd is not specified, constants.GetOutDirectory() is assumed. | 63 If old_cwd is not specified, constants.GetOutDirectory() is assumed. |
63 """ | 64 """ |
| 65 if path_or_list is None: |
| 66 return [] |
64 if not isinstance(path_or_list, basestring): | 67 if not isinstance(path_or_list, basestring): |
65 return [_RebasePath(p, new_cwd, old_cwd) for p in path_or_list] | 68 return [_RebasePath(p, new_cwd, old_cwd) for p in path_or_list] |
66 if old_cwd is None: | 69 if old_cwd is None: |
67 old_cwd = constants.GetOutDirectory() | 70 old_cwd = constants.GetOutDirectory() |
68 old_cwd = os.path.abspath(old_cwd) | 71 old_cwd = os.path.abspath(old_cwd) |
69 if new_cwd: | 72 if new_cwd: |
70 new_cwd = os.path.abspath(new_cwd) | 73 new_cwd = os.path.abspath(new_cwd) |
71 return os.path.relpath(os.path.join(old_cwd, path_or_list), new_cwd) | 74 return os.path.relpath(os.path.join(old_cwd, path_or_list), new_cwd) |
72 return os.path.abspath(os.path.join(old_cwd, path_or_list)) | 75 return os.path.abspath(os.path.join(old_cwd, path_or_list)) |
73 | 76 |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
169 def DepsInfo(self): | 172 def DepsInfo(self): |
170 return self.BuildConfig()['deps_info'] | 173 return self.BuildConfig()['deps_info'] |
171 | 174 |
172 def Gradle(self): | 175 def Gradle(self): |
173 return self.BuildConfig()['gradle'] | 176 return self.BuildConfig()['gradle'] |
174 | 177 |
175 def GetType(self): | 178 def GetType(self): |
176 """Returns the target type from its .build_config.""" | 179 """Returns the target type from its .build_config.""" |
177 return self.DepsInfo()['type'] | 180 return self.DepsInfo()['type'] |
178 | 181 |
| 182 def ResZips(self): |
| 183 return self.DepsInfo().get('owned_resources_zips') |
| 184 |
179 def JavaFiles(self): | 185 def JavaFiles(self): |
180 if self._java_files is None: | 186 if self._java_files is None: |
181 java_sources_file = self.Gradle().get('java_sources_file') | 187 java_sources_file = self.Gradle().get('java_sources_file') |
182 java_files = [] | 188 java_files = [] |
183 if java_sources_file: | 189 if java_sources_file: |
184 java_sources_file = _RebasePath(java_sources_file) | 190 java_sources_file = _RebasePath(java_sources_file) |
185 java_files = build_utils.ReadSourcesList(java_sources_file) | 191 java_files = build_utils.ReadSourcesList(java_sources_file) |
186 self._java_files = java_files | 192 self._java_files = java_files |
187 return self._java_files | 193 return self._java_files |
188 | 194 |
(...skipping 15 matching lines...) Expand all Loading... |
204 return jni_libs | 210 return jni_libs |
205 | 211 |
206 def _GenJavaDirs(self, entry): | 212 def _GenJavaDirs(self, entry): |
207 java_dirs, excludes = _CreateJavaSourceDir( | 213 java_dirs, excludes = _CreateJavaSourceDir( |
208 constants.GetOutDirectory(), entry.JavaFiles()) | 214 constants.GetOutDirectory(), entry.JavaFiles()) |
209 if self.Srcjars(entry): | 215 if self.Srcjars(entry): |
210 java_dirs.append( | 216 java_dirs.append( |
211 os.path.join(self.EntryOutputDir(entry), _SRCJARS_SUBDIR)) | 217 os.path.join(self.EntryOutputDir(entry), _SRCJARS_SUBDIR)) |
212 return java_dirs, excludes | 218 return java_dirs, excludes |
213 | 219 |
| 220 def _GenResDirs(self, entry): |
| 221 res_dirs = list(entry.DepsInfo().get('owned_resources_dirs', [])) |
| 222 if entry.ResZips(): |
| 223 res_dirs.append(os.path.join(self.EntryOutputDir(entry), _RES_SUBDIR)) |
| 224 return res_dirs |
| 225 |
214 def _Relativize(self, entry, paths): | 226 def _Relativize(self, entry, paths): |
215 return _RebasePath(paths, self.EntryOutputDir(entry)) | 227 return _RebasePath(paths, self.EntryOutputDir(entry)) |
216 | 228 |
217 def EntryOutputDir(self, entry): | 229 def EntryOutputDir(self, entry): |
218 return os.path.join(self.project_dir, entry.GradleSubdir()) | 230 return os.path.join(self.project_dir, entry.GradleSubdir()) |
219 | 231 |
220 def Srcjars(self, entry): | 232 def Srcjars(self, entry): |
221 srcjars = _RebasePath(entry.Gradle().get('bundled_srcjars', [])) | 233 srcjars = _RebasePath(entry.Gradle().get('bundled_srcjars', [])) |
222 if not self.use_gradle_process_resources: | 234 if not self.use_gradle_process_resources: |
223 srcjars += _RebasePath(entry.BuildConfig()['javac']['srcjars']) | 235 srcjars += _RebasePath(entry.BuildConfig()['javac']['srcjars']) |
224 return srcjars | 236 return srcjars |
225 | 237 |
226 def GeneratedInputs(self, entry): | 238 def GeneratedInputs(self, entry): |
227 generated_inputs = [] | 239 generated_inputs = [] |
228 generated_inputs.extend(self.Srcjars(entry)) | 240 generated_inputs.extend(self.Srcjars(entry)) |
| 241 generated_inputs.extend(_RebasePath(entry.ResZips())) |
229 generated_inputs.extend( | 242 generated_inputs.extend( |
230 p for p in entry.JavaFiles() if not p.startswith('..')) | 243 p for p in entry.JavaFiles() if not p.startswith('..')) |
231 generated_inputs.extend(entry.Gradle()['dependent_prebuilt_jars']) | 244 generated_inputs.extend(entry.Gradle()['dependent_prebuilt_jars']) |
232 return generated_inputs | 245 return generated_inputs |
233 | 246 |
234 def Generate(self, entry): | 247 def Generate(self, entry): |
235 variables = {} | 248 variables = {} |
236 android_test_manifest = entry.Gradle().get( | 249 android_test_manifest = entry.Gradle().get( |
237 'android_manifest', _DEFAULT_ANDROID_MANIFEST_PATH) | 250 'android_manifest', _DEFAULT_ANDROID_MANIFEST_PATH) |
238 variables['android_manifest'] = self._Relativize( | 251 variables['android_manifest'] = self._Relativize( |
239 entry, android_test_manifest) | 252 entry, android_test_manifest) |
240 java_dirs, excludes = self._GenJavaDirs(entry) | 253 java_dirs, excludes = self._GenJavaDirs(entry) |
241 variables['java_dirs'] = self._Relativize(entry, java_dirs) | 254 variables['java_dirs'] = self._Relativize(entry, java_dirs) |
242 variables['java_excludes'] = excludes | 255 variables['java_excludes'] = excludes |
243 variables['jni_libs'] = self._Relativize(entry, self._GenJniLibs(entry)) | 256 variables['jni_libs'] = self._Relativize(entry, self._GenJniLibs(entry)) |
| 257 variables['res_dirs'] = self._Relativize(entry, self._GenResDirs(entry)) |
244 deps = [_ProjectEntry.FromBuildConfigPath(p) | 258 deps = [_ProjectEntry.FromBuildConfigPath(p) |
245 for p in entry.Gradle()['dependent_android_projects']] | 259 for p in entry.Gradle()['dependent_android_projects']] |
246 variables['android_project_deps'] = [d.ProjectName() for d in deps] | 260 variables['android_project_deps'] = [d.ProjectName() for d in deps] |
247 # TODO(agrieve): Add an option to use interface jars and see if that speeds | 261 # TODO(agrieve): Add an option to use interface jars and see if that speeds |
248 # things up at all. | 262 # things up at all. |
249 variables['prebuilts'] = self._Relativize( | 263 variables['prebuilts'] = self._Relativize( |
250 entry, entry.Gradle()['dependent_prebuilt_jars']) | 264 entry, entry.Gradle()['dependent_prebuilt_jars']) |
251 deps = [_ProjectEntry.FromBuildConfigPath(p) | 265 deps = [_ProjectEntry.FromBuildConfigPath(p) |
252 for p in entry.Gradle()['dependent_java_projects']] | 266 for p in entry.Gradle()['dependent_java_projects']] |
253 variables['java_project_deps'] = [d.ProjectName() for d in deps] | 267 variables['java_project_deps'] = [d.ProjectName() for d in deps] |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
429 lines.append('') | 443 lines.append('') |
430 | 444 |
431 for entry in project_entries: | 445 for entry in project_entries: |
432 # Example target: android_webview:android_webview_java__build_config | 446 # Example target: android_webview:android_webview_java__build_config |
433 lines.append('include ":%s"' % entry.ProjectName()) | 447 lines.append('include ":%s"' % entry.ProjectName()) |
434 lines.append('project(":%s").projectDir = new File(settingsDir, "%s")' % | 448 lines.append('project(":%s").projectDir = new File(settingsDir, "%s")' % |
435 (entry.ProjectName(), entry.GradleSubdir())) | 449 (entry.ProjectName(), entry.GradleSubdir())) |
436 return '\n'.join(lines) | 450 return '\n'.join(lines) |
437 | 451 |
438 | 452 |
439 def _ExtractSrcjars(entry_output_dir, srcjar_tuples): | 453 def _ExtractFile(zip_path, extracted_path): |
| 454 logging.info('Extracting %s to %s', zip_path, extracted_path) |
| 455 with zipfile.ZipFile(zip_path) as z: |
| 456 z.extractall(extracted_path) |
| 457 |
| 458 |
| 459 def _ExtractZips(entry_output_dir, zip_tuples): |
440 """Extracts all srcjars to the directory given by the tuples.""" | 460 """Extracts all srcjars to the directory given by the tuples.""" |
441 extracted_paths = set(s[1] for s in srcjar_tuples) | 461 extracted_paths = set(s[1] for s in zip_tuples) |
442 for extracted_path in extracted_paths: | 462 for extracted_path in extracted_paths: |
443 assert _IsSubpathOf(extracted_path, entry_output_dir) | 463 assert _IsSubpathOf(extracted_path, entry_output_dir) |
444 shutil.rmtree(extracted_path, True) | 464 shutil.rmtree(extracted_path, True) |
445 | 465 |
446 for srcjar_path, extracted_path in srcjar_tuples: | 466 for zip_path, extracted_path in zip_tuples: |
447 logging.info('Extracting %s to %s', srcjar_path, extracted_path) | 467 _ExtractFile(zip_path, extracted_path) |
448 with zipfile.ZipFile(srcjar_path) as z: | |
449 z.extractall(extracted_path) | |
450 | 468 |
451 | 469 |
452 def _FindAllProjectEntries(main_entries): | 470 def _FindAllProjectEntries(main_entries): |
453 """Returns the list of all _ProjectEntry instances given the root project.""" | 471 """Returns the list of all _ProjectEntry instances given the root project.""" |
454 found = set() | 472 found = set() |
455 to_scan = list(main_entries) | 473 to_scan = list(main_entries) |
456 while to_scan: | 474 while to_scan: |
457 cur_entry = to_scan.pop() | 475 cur_entry = to_scan.pop() |
458 if cur_entry in found: | 476 if cur_entry in found: |
459 continue | 477 continue |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
554 | 572 |
555 all_entries = _FindAllProjectEntries(main_entries) | 573 all_entries = _FindAllProjectEntries(main_entries) |
556 logging.info('Found %d dependent build_config targets.', len(all_entries)) | 574 logging.info('Found %d dependent build_config targets.', len(all_entries)) |
557 entries = _CombineTestEntries(all_entries) | 575 entries = _CombineTestEntries(all_entries) |
558 logging.info('Creating %d projects for targets.', len(entries)) | 576 logging.info('Creating %d projects for targets.', len(entries)) |
559 | 577 |
560 logging.warning('Writing .gradle files...') | 578 logging.warning('Writing .gradle files...') |
561 jinja_processor = jinja_template.JinjaProcessor(_FILE_DIR) | 579 jinja_processor = jinja_template.JinjaProcessor(_FILE_DIR) |
562 build_vars = _ReadBuildVars(output_dir) | 580 build_vars = _ReadBuildVars(output_dir) |
563 project_entries = [] | 581 project_entries = [] |
564 srcjar_tuples = [] | 582 zip_tuples = [] |
565 generated_inputs = [] | 583 generated_inputs = [] |
566 for entry in entries: | 584 for entry in entries: |
567 if entry.GetType() not in ('android_apk', 'java_library', 'java_binary'): | 585 if entry.GetType() not in ('android_apk', 'java_library', 'java_binary'): |
568 continue | 586 continue |
569 | 587 |
570 data = _GenerateGradleFile(entry, generator, build_vars, jinja_processor) | 588 data = _GenerateGradleFile(entry, generator, build_vars, jinja_processor) |
571 if data: | 589 if data: |
572 project_entries.append(entry) | 590 project_entries.append(entry) |
573 # Build all paths references by .gradle that exist within output_dir. | 591 # Build all paths references by .gradle that exist within output_dir. |
574 generated_inputs.extend(generator.GeneratedInputs(entry)) | 592 generated_inputs.extend(generator.GeneratedInputs(entry)) |
575 srcjar_tuples.extend( | 593 zip_tuples.extend( |
576 (s, os.path.join(generator.EntryOutputDir(entry), _SRCJARS_SUBDIR)) | 594 (s, os.path.join(generator.EntryOutputDir(entry), _SRCJARS_SUBDIR)) |
577 for s in generator.Srcjars(entry)) | 595 for s in generator.Srcjars(entry)) |
| 596 zip_tuples.extend( |
| 597 (s, os.path.join(generator.EntryOutputDir(entry), _RES_SUBDIR)) |
| 598 for s in _RebasePath(entry.ResZips())) |
578 _WriteFile( | 599 _WriteFile( |
579 os.path.join(generator.EntryOutputDir(entry), 'build.gradle'), data) | 600 os.path.join(generator.EntryOutputDir(entry), 'build.gradle'), data) |
580 | 601 |
581 _WriteFile(os.path.join(generator.project_dir, 'build.gradle'), | 602 _WriteFile(os.path.join(generator.project_dir, 'build.gradle'), |
582 _GenerateRootGradle(jinja_processor)) | 603 _GenerateRootGradle(jinja_processor)) |
583 | 604 |
584 _WriteFile(os.path.join(generator.project_dir, 'settings.gradle'), | 605 _WriteFile(os.path.join(generator.project_dir, 'settings.gradle'), |
585 _GenerateSettingsGradle(project_entries)) | 606 _GenerateSettingsGradle(project_entries)) |
586 | 607 |
587 sdk_path = _RebasePath(build_vars['android_sdk_root']) | 608 sdk_path = _RebasePath(build_vars['android_sdk_root']) |
588 _WriteFile(os.path.join(generator.project_dir, 'local.properties'), | 609 _WriteFile(os.path.join(generator.project_dir, 'local.properties'), |
589 _GenerateLocalProperties(sdk_path)) | 610 _GenerateLocalProperties(sdk_path)) |
590 | 611 |
591 if generated_inputs: | 612 if generated_inputs: |
592 logging.warning('Building generated source files...') | 613 logging.warning('Building generated source files...') |
593 targets = _RebasePath(generated_inputs, output_dir) | 614 targets = _RebasePath(generated_inputs, output_dir) |
594 _RunNinja(output_dir, targets) | 615 _RunNinja(output_dir, targets) |
595 | 616 |
596 if srcjar_tuples: | 617 if zip_tuples: |
597 _ExtractSrcjars(generator.project_dir, srcjar_tuples) | 618 _ExtractZips(generator.project_dir, zip_tuples) |
598 | 619 |
599 logging.warning('Project created! (%d subprojects)', len(project_entries)) | 620 logging.warning('Project created! (%d subprojects)', len(project_entries)) |
600 logging.warning('Generated projects work best with Android Studio 2.2') | 621 logging.warning('Generated projects work best with Android Studio 2.2') |
601 logging.warning('For more tips: https://chromium.googlesource.com/chromium' | 622 logging.warning('For more tips: https://chromium.googlesource.com/chromium' |
602 '/src.git/+/master/docs/android_studio.md') | 623 '/src.git/+/master/docs/android_studio.md') |
603 | 624 |
604 | 625 |
605 if __name__ == '__main__': | 626 if __name__ == '__main__': |
606 main() | 627 main() |
OLD | NEW |