| 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 logging | 10 import logging | 
| (...skipping 18 matching lines...) Expand all  Loading... | 
| 29 | 29 | 
| 30 _DEFAULT_ANDROID_MANIFEST_PATH = os.path.join( | 30 _DEFAULT_ANDROID_MANIFEST_PATH = os.path.join( | 
| 31     host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'AndroidManifest.xml') | 31     host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'AndroidManifest.xml') | 
| 32 _JINJA_TEMPLATE_PATH = os.path.join( | 32 _JINJA_TEMPLATE_PATH = os.path.join( | 
| 33     os.path.dirname(__file__), 'build.gradle.jinja') | 33     os.path.dirname(__file__), 'build.gradle.jinja') | 
| 34 | 34 | 
| 35 _JAVA_SUBDIR = 'symlinked-java' | 35 _JAVA_SUBDIR = 'symlinked-java' | 
| 36 _SRCJARS_SUBDIR = 'extracted-srcjars' | 36 _SRCJARS_SUBDIR = 'extracted-srcjars' | 
| 37 | 37 | 
| 38 _DEFAULT_TARGETS = [ | 38 _DEFAULT_TARGETS = [ | 
| 39     '//android_webview:system_webview_apk', | 39     # TODO(agrieve): Requires alternate android.jar to compile. | 
|  | 40     # '//android_webview:system_webview_apk', | 
| 40     '//android_webview/test:android_webview_apk', | 41     '//android_webview/test:android_webview_apk', | 
| 41     '//android_webview/test:android_webview_test_apk', | 42     '//android_webview/test:android_webview_test_apk', | 
|  | 43     '//base:base_junit_tests', | 
|  | 44     '//chrome/android:chrome_junit_tests', | 
| 42     '//chrome/android:chrome_public_apk', | 45     '//chrome/android:chrome_public_apk', | 
| 43     '//chrome/android:chrome_public_test_apk', | 46     '//chrome/android:chrome_public_test_apk', | 
| 44     '//chrome/android:chrome_sync_shell_apk', | 47     '//chrome/android:chrome_sync_shell_apk', | 
| 45     '//chrome/android:chrome_sync_shell_test_apk', | 48     '//chrome/android:chrome_sync_shell_test_apk', | 
|  | 49     '//content/public/android:content_junit_tests', | 
|  | 50     '//content/shell/android:content_shell_apk', | 
| 46 ] | 51 ] | 
| 47 | 52 | 
| 48 def _RebasePath(path_or_list, new_cwd=None, old_cwd=None): | 53 def _RebasePath(path_or_list, new_cwd=None, old_cwd=None): | 
| 49   """Makes the given path(s) relative to new_cwd, or absolute if not specified. | 54   """Makes the given path(s) relative to new_cwd, or absolute if not specified. | 
| 50 | 55 | 
| 51   If new_cwd is not specified, absolute paths are returned. | 56   If new_cwd is not specified, absolute paths are returned. | 
| 52   If old_cwd is not specified, constants.GetOutDirectory() is assumed. | 57   If old_cwd is not specified, constants.GetOutDirectory() is assumed. | 
| 53   """ | 58   """ | 
| 54   if not isinstance(path_or_list, basestring): | 59   if not isinstance(path_or_list, basestring): | 
| 55     return [_RebasePath(p, new_cwd, old_cwd) for p in path_or_list] | 60     return [_RebasePath(p, new_cwd, old_cwd) for p in path_or_list] | 
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 158     """Returns the target type from its .build_config.""" | 163     """Returns the target type from its .build_config.""" | 
| 159     return self.BuildConfig()['deps_info']['type'] | 164     return self.BuildConfig()['deps_info']['type'] | 
| 160 | 165 | 
| 161 | 166 | 
| 162 def _ComputeJavaSourceDirs(java_files): | 167 def _ComputeJavaSourceDirs(java_files): | 
| 163   """Returns the list of source directories for the given files.""" | 168   """Returns the list of source directories for the given files.""" | 
| 164   found_roots = set() | 169   found_roots = set() | 
| 165   for path in java_files: | 170   for path in java_files: | 
| 166     path_root = path | 171     path_root = path | 
| 167     # Recognize these tokens as top-level. | 172     # Recognize these tokens as top-level. | 
| 168     while os.path.basename(path_root) not in ('javax', 'org', 'com', 'src'): | 173     while True: | 
| 169       assert path_root, 'Failed to find source dir for ' + path |  | 
| 170       path_root = os.path.dirname(path_root) | 174       path_root = os.path.dirname(path_root) | 
| 171     # Assume that if we've hit "src", the we're at the root. | 175       basename = os.path.basename(path_root) | 
| 172     if os.path.basename(path_root) != 'src': | 176       assert basename, 'Failed to find source dir for ' + path | 
| 173       path_root = os.path.dirname(path_root) | 177       if basename in ('java', 'src'): | 
|  | 178         break | 
|  | 179       if basename in ('javax', 'org', 'com'): | 
|  | 180         path_root = os.path.dirname(path_root) | 
|  | 181         break | 
| 174     found_roots.add(path_root) | 182     found_roots.add(path_root) | 
| 175   return list(found_roots) | 183   return list(found_roots) | 
| 176 | 184 | 
| 177 | 185 | 
| 178 def _CreateSymlinkTree(entry_output_dir, symlink_dir, desired_files, | 186 def _CreateSymlinkTree(entry_output_dir, symlink_dir, desired_files, | 
| 179                        parent_dirs): | 187                        parent_dirs): | 
| 180   """Creates a directory tree of symlinks to the given files. | 188   """Creates a directory tree of symlinks to the given files. | 
| 181 | 189 | 
| 182   The idea here is to replicate a directory tree while leaving out files within | 190   The idea here is to replicate a directory tree while leaving out files within | 
| 183   it not listed by |desired_files|. | 191   it not listed by |desired_files|. | 
| 184   """ | 192   """ | 
| 185   assert _IsSubpathOf(symlink_dir, entry_output_dir) | 193   assert _IsSubpathOf(symlink_dir, entry_output_dir) | 
| 186   if os.path.exists(symlink_dir): |  | 
| 187     shutil.rmtree(symlink_dir) |  | 
| 188 | 194 | 
| 189   for target_path in desired_files: | 195   for target_path in desired_files: | 
| 190     prefix = next(d for d in parent_dirs if target_path.startswith(d)) | 196     prefix = next(d for d in parent_dirs if target_path.startswith(d)) | 
| 191     subpath = os.path.relpath(target_path, prefix) | 197     subpath = os.path.relpath(target_path, prefix) | 
| 192     symlinked_path = os.path.join(symlink_dir, subpath) | 198     symlinked_path = os.path.join(symlink_dir, subpath) | 
| 193     symlinked_dir = os.path.dirname(symlinked_path) | 199     symlinked_dir = os.path.dirname(symlinked_path) | 
| 194     if not os.path.exists(symlinked_dir): | 200     if not os.path.exists(symlinked_dir): | 
| 195       os.makedirs(symlinked_dir) | 201       os.makedirs(symlinked_dir) | 
| 196     relpath = os.path.relpath(target_path, symlinked_dir) | 202     relpath = os.path.relpath(target_path, symlinked_dir) | 
| 197     logging.debug('Creating symlink %s -> %s', symlinked_path, relpath) | 203     logging.debug('Creating symlink %s -> %s', symlinked_path, relpath) | 
| (...skipping 13 matching lines...) Expand all  Loading... | 
| 211   if java_files: | 217   if java_files: | 
| 212     java_files = _RebasePath(java_files) | 218     java_files = _RebasePath(java_files) | 
| 213     java_dirs = _ComputeJavaSourceDirs(java_files) | 219     java_dirs = _ComputeJavaSourceDirs(java_files) | 
| 214 | 220 | 
| 215     found_java_files = build_utils.FindInDirectories(java_dirs, '*.java') | 221     found_java_files = build_utils.FindInDirectories(java_dirs, '*.java') | 
| 216     unwanted_java_files = set(found_java_files) - set(java_files) | 222     unwanted_java_files = set(found_java_files) - set(java_files) | 
| 217     missing_java_files = set(java_files) - set(found_java_files) | 223     missing_java_files = set(java_files) - set(found_java_files) | 
| 218     # Warn only about non-generated files that are missing. | 224     # Warn only about non-generated files that are missing. | 
| 219     missing_java_files = [p for p in missing_java_files | 225     missing_java_files = [p for p in missing_java_files | 
| 220                           if not p.startswith(output_dir)] | 226                           if not p.startswith(output_dir)] | 
|  | 227 | 
|  | 228     symlink_dir = os.path.join(entry_output_dir, _JAVA_SUBDIR) | 
|  | 229     shutil.rmtree(symlink_dir, True) | 
|  | 230 | 
| 221     if unwanted_java_files: | 231     if unwanted_java_files: | 
| 222       logging.debug('Target requires .java symlinks: %s', entry_output_dir) | 232       logging.debug('Target requires .java symlinks: %s', entry_output_dir) | 
| 223       symlink_dir = os.path.join(entry_output_dir, _JAVA_SUBDIR) |  | 
| 224       _CreateSymlinkTree(entry_output_dir, symlink_dir, java_files, java_dirs) | 233       _CreateSymlinkTree(entry_output_dir, symlink_dir, java_files, java_dirs) | 
| 225       java_dirs = [symlink_dir] | 234       java_dirs = [symlink_dir] | 
| 226 | 235 | 
| 227     if missing_java_files: | 236     if missing_java_files: | 
| 228       logging.warning('Some java files were not found: %s', missing_java_files) | 237       logging.warning('Some java files were not found: %s', missing_java_files) | 
| 229 | 238 | 
| 230   return java_dirs | 239   return java_dirs | 
| 231 | 240 | 
| 232 | 241 | 
| 233 def _GenerateLocalProperties(sdk_dir): | 242 def _GenerateLocalProperties(sdk_dir): | 
| 234   """Returns the data for project.properties as a string.""" | 243   """Returns the data for project.properties as a string.""" | 
| 235   return '\n'.join([ | 244   return '\n'.join([ | 
| 236       '# Generated by //build/android/gradle/generate_gradle.py', | 245       '# Generated by //build/android/gradle/generate_gradle.py', | 
| 237       'sdk.dir=%s' % sdk_dir, | 246       'sdk.dir=%s' % sdk_dir, | 
| 238       '']) | 247       '']) | 
| 239 | 248 | 
| 240 | 249 | 
| 241 def _GenerateGradleFile(build_config, build_vars, java_dirs, relativize, | 250 def _GenerateGradleFile(build_config, build_vars, java_dirs, relativize, | 
| 242                         use_gradle_process_resources, jinja_processor): | 251                         use_gradle_process_resources, jinja_processor): | 
| 243   """Returns the data for a project's build.gradle.""" | 252   """Returns the data for a project's build.gradle.""" | 
| 244   deps_info = build_config['deps_info'] | 253   deps_info = build_config['deps_info'] | 
| 245   gradle = build_config['gradle'] | 254   gradle = build_config['gradle'] | 
| 246 | 255 | 
|  | 256   variables = { | 
|  | 257       'sourceSetName': 'main', | 
|  | 258       'depCompileName': 'compile', | 
|  | 259   } | 
| 247   if deps_info['type'] == 'android_apk': | 260   if deps_info['type'] == 'android_apk': | 
| 248     target_type = 'android_apk' | 261     target_type = 'android_apk' | 
| 249   elif deps_info['type'] == 'java_library' and not deps_info['is_prebuilt']: | 262   elif deps_info['type'] == 'java_library': | 
|  | 263     if deps_info['is_prebuilt'] or deps_info['gradle_treat_as_prebuilt']: | 
|  | 264       return None | 
|  | 265 | 
| 250     if deps_info['requires_android']: | 266     if deps_info['requires_android']: | 
| 251       target_type = 'android_library' | 267       target_type = 'android_library' | 
| 252     else: | 268     else: | 
| 253       target_type = 'java_library' | 269       target_type = 'java_library' | 
|  | 270   elif deps_info['type'] == 'java_binary': | 
|  | 271     if gradle['main_class'] == 'org.chromium.testing.local.JunitTestMain': | 
|  | 272       target_type = 'android_junit' | 
|  | 273       variables['sourceSetName'] = 'test' | 
|  | 274       variables['depCompileName'] = 'testCompile' | 
|  | 275     else: | 
|  | 276       target_type = 'java_binary' | 
|  | 277       variables['main_class'] = gradle['main_class'] | 
| 254   else: | 278   else: | 
| 255     return None | 279     return None | 
| 256 | 280 | 
| 257   variables = {} | 281   variables['target_name'] = os.path.splitext(deps_info['name'])[0] | 
| 258   variables['template_type'] = target_type | 282   variables['template_type'] = target_type | 
| 259   variables['use_gradle_process_resources'] = use_gradle_process_resources | 283   variables['use_gradle_process_resources'] = use_gradle_process_resources | 
| 260   variables['build_tools_version'] = ( | 284   variables['build_tools_version'] = ( | 
| 261       build_vars['android_sdk_build_tools_version']) | 285       build_vars['android_sdk_build_tools_version']) | 
| 262   variables['compile_sdk_version'] = build_vars['android_sdk_version'] | 286   variables['compile_sdk_version'] = build_vars['android_sdk_version'] | 
| 263   android_manifest = gradle.get('android_manifest', | 287   android_manifest = gradle.get('android_manifest', | 
| 264                                 _DEFAULT_ANDROID_MANIFEST_PATH) | 288                                 _DEFAULT_ANDROID_MANIFEST_PATH) | 
| 265   variables['android_manifest'] = relativize(android_manifest) | 289   variables['android_manifest'] = relativize(android_manifest) | 
| 266   variables['java_dirs'] = relativize(java_dirs) | 290   variables['java_dirs'] = relativize(java_dirs) | 
|  | 291   # TODO(agrieve): Add an option to use interface jars and see if that speeds | 
|  | 292   # things up at all. | 
| 267   variables['prebuilts'] = relativize(gradle['dependent_prebuilt_jars']) | 293   variables['prebuilts'] = relativize(gradle['dependent_prebuilt_jars']) | 
| 268   deps = [_ProjectEntry.FromBuildConfigPath(p) | 294   deps = [_ProjectEntry.FromBuildConfigPath(p) | 
| 269           for p in gradle['dependent_android_projects']] | 295           for p in gradle['dependent_android_projects']] | 
| 270 | 296 | 
| 271   variables['android_project_deps'] = [d.ProjectName() for d in deps] | 297   variables['android_project_deps'] = [d.ProjectName() for d in deps] | 
| 272   deps = [_ProjectEntry.FromBuildConfigPath(p) | 298   deps = [_ProjectEntry.FromBuildConfigPath(p) | 
| 273           for p in gradle['dependent_java_projects']] | 299           for p in gradle['dependent_java_projects']] | 
| 274   variables['java_project_deps'] = [d.ProjectName() for d in deps] | 300   variables['java_project_deps'] = [d.ProjectName() for d in deps] | 
| 275 | 301 | 
| 276   return jinja_processor.Render(_JINJA_TEMPLATE_PATH, variables) | 302   return jinja_processor.Render(_JINJA_TEMPLATE_PATH, variables) | 
| (...skipping 20 matching lines...) Expand all  Loading... | 
| 297     lines.append('project(":%s").projectDir = new File(settingsDir, "%s")' % | 323     lines.append('project(":%s").projectDir = new File(settingsDir, "%s")' % | 
| 298                  (entry.ProjectName(), entry.GradleSubdir())) | 324                  (entry.ProjectName(), entry.GradleSubdir())) | 
| 299   return '\n'.join(lines) | 325   return '\n'.join(lines) | 
| 300 | 326 | 
| 301 | 327 | 
| 302 def _ExtractSrcjars(entry_output_dir, srcjar_tuples): | 328 def _ExtractSrcjars(entry_output_dir, srcjar_tuples): | 
| 303   """Extracts all srcjars to the directory given by the tuples.""" | 329   """Extracts all srcjars to the directory given by the tuples.""" | 
| 304   extracted_paths = set(s[1] for s in srcjar_tuples) | 330   extracted_paths = set(s[1] for s in srcjar_tuples) | 
| 305   for extracted_path in extracted_paths: | 331   for extracted_path in extracted_paths: | 
| 306     assert _IsSubpathOf(extracted_path, entry_output_dir) | 332     assert _IsSubpathOf(extracted_path, entry_output_dir) | 
| 307     if os.path.exists(extracted_path): | 333     shutil.rmtree(extracted_path, True) | 
| 308       shutil.rmtree(extracted_path) |  | 
| 309 | 334 | 
| 310   for srcjar_path, extracted_path in srcjar_tuples: | 335   for srcjar_path, extracted_path in srcjar_tuples: | 
| 311     logging.info('Extracting %s to %s', srcjar_path, extracted_path) | 336     logging.info('Extracting %s to %s', srcjar_path, extracted_path) | 
| 312     with zipfile.ZipFile(srcjar_path) as z: | 337     with zipfile.ZipFile(srcjar_path) as z: | 
| 313       z.extractall(extracted_path) | 338       z.extractall(extracted_path) | 
| 314 | 339 | 
| 315 | 340 | 
| 316 def _FindAllProjectEntries(main_entries): | 341 def _FindAllProjectEntries(main_entries): | 
| 317   """Returns the list of all _ProjectEntry instances given the root project.""" | 342   """Returns the list of all _ProjectEntry instances given the root project.""" | 
| 318   found = set() | 343   found = set() | 
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 368   if args.all: | 393   if args.all: | 
| 369     # Run GN gen if necessary (faster than running "gn gen" in the no-op case). | 394     # Run GN gen if necessary (faster than running "gn gen" in the no-op case). | 
| 370     _RunNinja(output_dir, ['build.ninja']) | 395     _RunNinja(output_dir, ['build.ninja']) | 
| 371     # Query ninja for all __build_config targets. | 396     # Query ninja for all __build_config targets. | 
| 372     targets = _QueryForAllGnTargets(output_dir) | 397     targets = _QueryForAllGnTargets(output_dir) | 
| 373   else: | 398   else: | 
| 374     targets = args.targets or _DEFAULT_TARGETS | 399     targets = args.targets or _DEFAULT_TARGETS | 
| 375     # TODO(agrieve): See if it makes sense to utilize Gradle's test constructs | 400     # TODO(agrieve): See if it makes sense to utilize Gradle's test constructs | 
| 376     # for our instrumentation tests. | 401     # for our instrumentation tests. | 
| 377     targets = [re.sub(r'_test_apk$', '_test_apk__apk', t) for t in targets] | 402     targets = [re.sub(r'_test_apk$', '_test_apk__apk', t) for t in targets] | 
|  | 403     targets = [re.sub(r'_junit_tests$', '_junit_tests__java_binary', t) | 
|  | 404                for t in targets] | 
| 378 | 405 | 
| 379   main_entries = [_ProjectEntry(t) for t in targets] | 406   main_entries = [_ProjectEntry(t) for t in targets] | 
| 380 | 407 | 
| 381   logging.warning('Building .build_config files...') | 408   logging.warning('Building .build_config files...') | 
| 382   _RunNinja(output_dir, [e.NinjaBuildConfigTarget() for e in main_entries]) | 409   _RunNinja(output_dir, [e.NinjaBuildConfigTarget() for e in main_entries]) | 
| 383 | 410 | 
| 384   # There are many unused libraries, so restrict to those that are actually used | 411   # There are many unused libraries, so restrict to those that are actually used | 
| 385   # when using --all. | 412   # when using --all. | 
| 386   if args.all: | 413   if args.all: | 
| 387     main_entries = [e for e in main_entries if e.GetType() == 'android_apk'] | 414     main_entries = [e for e in main_entries if e.GetType() == 'android_apk'] | 
| 388 | 415 | 
| 389   all_entries = _FindAllProjectEntries(main_entries) | 416   all_entries = _FindAllProjectEntries(main_entries) | 
| 390   logging.info('Found %d dependent build_config targets.', len(all_entries)) | 417   logging.info('Found %d dependent build_config targets.', len(all_entries)) | 
| 391 | 418 | 
| 392   logging.warning('Writing .gradle files...') | 419   logging.warning('Writing .gradle files...') | 
| 393   jinja_processor = jinja_template.JinjaProcessor(host_paths.DIR_SOURCE_ROOT) | 420   jinja_processor = jinja_template.JinjaProcessor(host_paths.DIR_SOURCE_ROOT) | 
| 394   build_vars = _ReadBuildVars(output_dir) | 421   build_vars = _ReadBuildVars(output_dir) | 
| 395   project_entries = [] | 422   project_entries = [] | 
| 396   srcjar_tuples = [] | 423   srcjar_tuples = [] | 
| 397   generated_inputs = [] | 424   generated_inputs = [] | 
| 398   for entry in all_entries: | 425   for entry in all_entries: | 
| 399     if entry.GetType() not in ('android_apk', 'java_library'): | 426     if entry.GetType() not in ('android_apk', 'java_library', 'java_binary'): | 
| 400       continue | 427       continue | 
| 401 | 428 | 
| 402     entry_output_dir = os.path.join(gradle_output_dir, entry.GradleSubdir()) | 429     entry_output_dir = os.path.join(gradle_output_dir, entry.GradleSubdir()) | 
| 403     relativize = lambda x, d=entry_output_dir: _RebasePath(x, d) | 430     relativize = lambda x, d=entry_output_dir: _RebasePath(x, d) | 
| 404     build_config = entry.BuildConfig() | 431     build_config = entry.BuildConfig() | 
| 405 | 432 | 
| 406     srcjars = _RebasePath(build_config['gradle'].get('bundled_srcjars', [])) | 433     srcjars = _RebasePath(build_config['gradle'].get('bundled_srcjars', [])) | 
| 407     if not args.use_gradle_process_resources: | 434     if not args.use_gradle_process_resources: | 
| 408       srcjars += _RebasePath(build_config['javac']['srcjars']) | 435       srcjars += _RebasePath(build_config['javac']['srcjars']) | 
| 409 | 436 | 
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 450     _ExtractSrcjars(gradle_output_dir, srcjar_tuples) | 477     _ExtractSrcjars(gradle_output_dir, srcjar_tuples) | 
| 451 | 478 | 
| 452   logging.warning('Project created! (%d subprojects)', len(project_entries)) | 479   logging.warning('Project created! (%d subprojects)', len(project_entries)) | 
| 453   logging.warning('Generated projects work best with Android Studio 2.2') | 480   logging.warning('Generated projects work best with Android Studio 2.2') | 
| 454   logging.warning('For more tips: https://chromium.googlesource.com/chromium' | 481   logging.warning('For more tips: https://chromium.googlesource.com/chromium' | 
| 455                   '/src.git/+/master/docs/android_studio.md') | 482                   '/src.git/+/master/docs/android_studio.md') | 
| 456 | 483 | 
| 457 | 484 | 
| 458 if __name__ == '__main__': | 485 if __name__ == '__main__': | 
| 459   main() | 486   main() | 
| OLD | NEW | 
|---|