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 |