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 logging | 11 import logging |
11 import os | 12 import os |
12 import re | 13 import re |
13 import shutil | 14 import shutil |
14 import subprocess | 15 import subprocess |
15 import sys | 16 import sys |
16 import zipfile | 17 import zipfile |
17 | 18 |
18 _BUILD_ANDROID = os.path.join(os.path.dirname(__file__), os.pardir) | 19 _BUILD_ANDROID = os.path.join(os.path.dirname(__file__), os.pardir) |
19 sys.path.append(_BUILD_ANDROID) | 20 sys.path.append(_BUILD_ANDROID) |
20 import devil_chromium | 21 import devil_chromium |
21 from devil.utils import run_tests_helper | 22 from devil.utils import run_tests_helper |
22 from pylib import constants | 23 from pylib import constants |
23 from pylib.constants import host_paths | 24 from pylib.constants import host_paths |
24 | 25 |
25 sys.path.append(os.path.join(_BUILD_ANDROID, 'gyp')) | 26 sys.path.append(os.path.join(_BUILD_ANDROID, 'gyp')) |
26 import jinja_template | 27 import jinja_template |
27 from util import build_utils | 28 from util import build_utils |
28 | 29 |
29 | 30 |
30 _DEFAULT_ANDROID_MANIFEST_PATH = os.path.join( | 31 _DEFAULT_ANDROID_MANIFEST_PATH = os.path.join( |
31 host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'AndroidManifest.xml') | 32 host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'AndroidManifest.xml') |
32 _FILE_DIR = os.path.dirname(__file__) | 33 _FILE_DIR = os.path.dirname(__file__) |
33 _JAVA_SUBDIR = 'symlinked-java' | |
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 | 37 |
38 _DEFAULT_TARGETS = [ | 38 _DEFAULT_TARGETS = [ |
39 # TODO(agrieve): Requires alternate android.jar to compile. | 39 # TODO(agrieve): Requires alternate android.jar to compile. |
40 # '//android_webview:system_webview_apk', | 40 # '//android_webview:system_webview_apk', |
41 '//android_webview/test:android_webview_apk', | 41 '//android_webview/test:android_webview_apk', |
42 '//android_webview/test:android_webview_test_apk', | 42 '//android_webview/test:android_webview_test_apk', |
43 '//base:base_junit_tests', | 43 '//base:base_junit_tests', |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
197 native_section = entry.BuildConfig().get('native') | 197 native_section = entry.BuildConfig().get('native') |
198 if native_section: | 198 if native_section: |
199 jni_libs = _CreateJniLibsDir( | 199 jni_libs = _CreateJniLibsDir( |
200 constants.GetOutDirectory(), self.EntryOutputDir(entry), | 200 constants.GetOutDirectory(), self.EntryOutputDir(entry), |
201 native_section.get('libraries')) | 201 native_section.get('libraries')) |
202 else: | 202 else: |
203 jni_libs = [] | 203 jni_libs = [] |
204 return jni_libs | 204 return jni_libs |
205 | 205 |
206 def _GenJavaDirs(self, entry): | 206 def _GenJavaDirs(self, entry): |
207 java_dirs = _CreateJavaSourceDir( | 207 java_dirs, excludes = _CreateJavaSourceDir( |
208 constants.GetOutDirectory(), self.EntryOutputDir(entry), | 208 constants.GetOutDirectory(), entry.JavaFiles()) |
209 entry.JavaFiles()) | |
210 if self.Srcjars(entry): | 209 if self.Srcjars(entry): |
211 java_dirs.append( | 210 java_dirs.append( |
212 os.path.join(self.EntryOutputDir(entry), _SRCJARS_SUBDIR)) | 211 os.path.join(self.EntryOutputDir(entry), _SRCJARS_SUBDIR)) |
213 return java_dirs | 212 return java_dirs, excludes |
214 | 213 |
215 def _Relativize(self, entry, paths): | 214 def _Relativize(self, entry, paths): |
216 return _RebasePath(paths, self.EntryOutputDir(entry)) | 215 return _RebasePath(paths, self.EntryOutputDir(entry)) |
217 | 216 |
218 def EntryOutputDir(self, entry): | 217 def EntryOutputDir(self, entry): |
219 return os.path.join(self.project_dir, entry.GradleSubdir()) | 218 return os.path.join(self.project_dir, entry.GradleSubdir()) |
220 | 219 |
221 def Srcjars(self, entry): | 220 def Srcjars(self, entry): |
222 srcjars = _RebasePath(entry.Gradle().get('bundled_srcjars', [])) | 221 srcjars = _RebasePath(entry.Gradle().get('bundled_srcjars', [])) |
223 if not self.use_gradle_process_resources: | 222 if not self.use_gradle_process_resources: |
224 srcjars += _RebasePath(entry.BuildConfig()['javac']['srcjars']) | 223 srcjars += _RebasePath(entry.BuildConfig()['javac']['srcjars']) |
225 return srcjars | 224 return srcjars |
226 | 225 |
227 def GeneratedInputs(self, entry): | 226 def GeneratedInputs(self, entry): |
228 generated_inputs = [] | 227 generated_inputs = [] |
229 generated_inputs.extend(self.Srcjars(entry)) | 228 generated_inputs.extend(self.Srcjars(entry)) |
230 generated_inputs.extend( | 229 generated_inputs.extend( |
231 p for p in entry.JavaFiles() if not p.startswith('..')) | 230 p for p in entry.JavaFiles() if not p.startswith('..')) |
232 generated_inputs.extend(entry.Gradle()['dependent_prebuilt_jars']) | 231 generated_inputs.extend(entry.Gradle()['dependent_prebuilt_jars']) |
233 return generated_inputs | 232 return generated_inputs |
234 | 233 |
235 def Generate(self, entry): | 234 def Generate(self, entry): |
236 variables = {} | 235 variables = {} |
237 android_test_manifest = entry.Gradle().get( | 236 android_test_manifest = entry.Gradle().get( |
238 'android_manifest', _DEFAULT_ANDROID_MANIFEST_PATH) | 237 'android_manifest', _DEFAULT_ANDROID_MANIFEST_PATH) |
239 variables['android_manifest'] = self._Relativize( | 238 variables['android_manifest'] = self._Relativize( |
240 entry, android_test_manifest) | 239 entry, android_test_manifest) |
241 variables['java_dirs'] = self._Relativize(entry, self._GenJavaDirs(entry)) | 240 java_dirs, excludes = self._GenJavaDirs(entry) |
241 variables['java_dirs'] = self._Relativize(entry, java_dirs) | |
242 variables['java_excludes'] = excludes | |
242 variables['jni_libs'] = self._Relativize(entry, self._GenJniLibs(entry)) | 243 variables['jni_libs'] = self._Relativize(entry, self._GenJniLibs(entry)) |
243 deps = [_ProjectEntry.FromBuildConfigPath(p) | 244 deps = [_ProjectEntry.FromBuildConfigPath(p) |
244 for p in entry.Gradle()['dependent_android_projects']] | 245 for p in entry.Gradle()['dependent_android_projects']] |
245 variables['android_project_deps'] = [d.ProjectName() for d in deps] | 246 variables['android_project_deps'] = [d.ProjectName() for d in deps] |
246 # TODO(agrieve): Add an option to use interface jars and see if that speeds | 247 # TODO(agrieve): Add an option to use interface jars and see if that speeds |
247 # things up at all. | 248 # things up at all. |
248 variables['prebuilts'] = self._Relativize( | 249 variables['prebuilts'] = self._Relativize( |
249 entry, entry.Gradle()['dependent_prebuilt_jars']) | 250 entry, entry.Gradle()['dependent_prebuilt_jars']) |
250 deps = [_ProjectEntry.FromBuildConfigPath(p) | 251 deps = [_ProjectEntry.FromBuildConfigPath(p) |
251 for p in entry.Gradle()['dependent_java_projects']] | 252 for p in entry.Gradle()['dependent_java_projects']] |
252 variables['java_project_deps'] = [d.ProjectName() for d in deps] | 253 variables['java_project_deps'] = [d.ProjectName() for d in deps] |
253 return variables | 254 return variables |
254 | 255 |
255 | 256 |
256 def _ComputeJavaSourceDirs(java_files): | 257 def _ComputeJavaSourceDirs(java_files): |
257 """Returns the list of source directories for the given files.""" | 258 """Returns a dictionary of source dirs with each given files in one.""" |
258 found_roots = set() | 259 found_roots = {} |
259 for path in java_files: | 260 for path in java_files: |
260 path_root = path | 261 path_root = path |
261 # Recognize these tokens as top-level. | 262 # Recognize these tokens as top-level. |
262 while True: | 263 while True: |
263 path_root = os.path.dirname(path_root) | 264 path_root = os.path.dirname(path_root) |
264 basename = os.path.basename(path_root) | 265 basename = os.path.basename(path_root) |
265 assert basename, 'Failed to find source dir for ' + path | 266 assert basename, 'Failed to find source dir for ' + path |
266 if basename in ('java', 'src'): | 267 if basename in ('java', 'src'): |
267 break | 268 break |
268 if basename in ('javax', 'org', 'com'): | 269 if basename in ('javax', 'org', 'com'): |
269 path_root = os.path.dirname(path_root) | 270 path_root = os.path.dirname(path_root) |
270 break | 271 break |
271 found_roots.add(path_root) | 272 if path_root not in found_roots: |
272 return list(found_roots) | 273 found_roots[path_root] = [] |
274 found_roots[path_root].append(path) | |
275 return found_roots | |
273 | 276 |
274 | 277 |
275 def _CreateRelativeSymlink(target_path, link_path): | 278 def _CreateRelativeSymlink(target_path, link_path): |
276 link_dir = os.path.dirname(link_path) | 279 link_dir = os.path.dirname(link_path) |
277 relpath = os.path.relpath(target_path, link_dir) | 280 relpath = os.path.relpath(target_path, link_dir) |
278 logging.debug('Creating symlink %s -> %s', link_path, relpath) | 281 logging.debug('Creating symlink %s -> %s', link_path, relpath) |
279 os.symlink(relpath, link_path) | 282 os.symlink(relpath, link_path) |
280 | 283 |
281 | 284 |
282 def _CreateSymlinkTree(entry_output_dir, symlink_dir, desired_files, | 285 def _CreateSymlinkTree(entry_output_dir, symlink_dir, desired_files, |
283 parent_dirs): | 286 parent_dirs): |
284 """Creates a directory tree of symlinks to the given files. | 287 """Creates a directory tree of symlinks to the given files. |
285 | 288 |
286 The idea here is to replicate a directory tree while leaving out files within | 289 The idea here is to replicate a directory tree while leaving out files within |
287 it not listed by |desired_files|. | 290 it not listed by |desired_files|. |
288 """ | 291 """ |
289 assert _IsSubpathOf(symlink_dir, entry_output_dir) | 292 assert _IsSubpathOf(symlink_dir, entry_output_dir) |
290 | 293 |
291 for target_path in desired_files: | 294 for target_path in desired_files: |
292 prefix = next(d for d in parent_dirs if target_path.startswith(d)) | 295 prefix = next(d for d in parent_dirs if target_path.startswith(d)) |
293 subpath = os.path.relpath(target_path, prefix) | 296 subpath = os.path.relpath(target_path, prefix) |
294 symlinked_path = os.path.join(symlink_dir, subpath) | 297 symlinked_path = os.path.join(symlink_dir, subpath) |
295 symlinked_dir = os.path.dirname(symlinked_path) | 298 symlinked_dir = os.path.dirname(symlinked_path) |
296 if not os.path.exists(symlinked_dir): | 299 if not os.path.exists(symlinked_dir): |
297 os.makedirs(symlinked_dir) | 300 os.makedirs(symlinked_dir) |
298 _CreateRelativeSymlink(target_path, symlinked_path) | 301 _CreateRelativeSymlink(target_path, symlinked_path) |
299 | 302 |
300 | 303 |
301 def _CreateJavaSourceDir(output_dir, entry_output_dir, java_files): | 304 def _CreateJavaSourceDir(output_dir, java_files): |
302 """Computes and constructs when necessary the list of java source directories. | 305 """Computes the list of java source directories and exclude patterns. |
303 | 306 |
304 1. Computes the root java source directories from the list of files. | 307 1. Computes the root java source directories from the list of files. |
305 2. Determines whether there are any .java files in them that are not included | 308 2. Compute exclude patterns that exclude all extra files only. |
306 in |java_files|. | 309 3. Returns the list of java source directories and exclude patterns. |
307 3. If not, returns the list of java source directories. If so, constructs a | |
308 tree of symlinks within |entry_output_dir| of all files in |java_files|. | |
309 """ | 310 """ |
310 java_dirs = [] | 311 java_dirs = [] |
312 excludes = [] | |
311 if java_files: | 313 if java_files: |
312 java_files = _RebasePath(java_files) | 314 java_files = _RebasePath(java_files) |
313 java_dirs = _ComputeJavaSourceDirs(java_files) | 315 computed_dirs = _ComputeJavaSourceDirs(java_files) |
316 java_dirs = computed_dirs.keys() | |
317 all_found_java_files = set() | |
314 | 318 |
315 found_java_files = build_utils.FindInDirectories(java_dirs, '*.java') | 319 for directory, files in computed_dirs.iteritems(): |
316 unwanted_java_files = set(found_java_files) - set(java_files) | 320 found_java_files = build_utils.FindInDirectory(directory, '*.java') |
317 missing_java_files = set(java_files) - set(found_java_files) | 321 all_found_java_files.update(found_java_files) |
322 unwanted_java_files = set(found_java_files) - set(files) | |
323 | |
324 if unwanted_java_files: | |
325 logging.debug('Target requires .java symlinks: %s, %s', | |
agrieve
2017/02/09 10:00:38
nit: update log message to not say symlinks
Peter Wen
2017/02/09 14:38:28
Done.
| |
326 directory, files) | |
327 # Shorten exclude list by globbing if possible. | |
agrieve
2017/02/09 10:00:38
Might be worth pulling this out into a helper func
Peter Wen
2017/02/09 14:38:28
Done.
| |
328 while unwanted_java_files: | |
329 unwanted_file = unwanted_java_files.pop() | |
330 target_exclude = os.path.join( | |
331 os.path.dirname(unwanted_file), '*.java') | |
332 found_files = glob.glob(target_exclude) | |
333 valid_files = set(found_files) & set(files) | |
334 if valid_files: | |
335 excludes.append(os.path.relpath(unwanted_file, directory)) | |
336 else: | |
337 excludes.append(os.path.relpath(target_exclude, directory)) | |
338 unwanted_java_files -= set(found_files) | |
agrieve
2017/02/09 10:00:38
you convert found_files to a set twice. Maybe just
Peter Wen
2017/02/09 14:38:28
Done.
| |
339 | |
340 missing_java_files = set(java_files) - set(all_found_java_files) | |
318 # Warn only about non-generated files that are missing. | 341 # Warn only about non-generated files that are missing. |
319 missing_java_files = [p for p in missing_java_files | 342 missing_java_files = [p for p in missing_java_files |
320 if not p.startswith(output_dir)] | 343 if not p.startswith(output_dir)] |
344 if missing_java_files: | |
345 logging.warning( | |
346 'Some java files were not found: %s', missing_java_files) | |
321 | 347 |
322 symlink_dir = os.path.join(entry_output_dir, _JAVA_SUBDIR) | 348 return java_dirs, excludes |
323 shutil.rmtree(symlink_dir, True) | |
324 | |
325 if unwanted_java_files: | |
326 logging.debug('Target requires .java symlinks: %s', entry_output_dir) | |
327 _CreateSymlinkTree(entry_output_dir, symlink_dir, java_files, java_dirs) | |
328 java_dirs = [symlink_dir] | |
329 | |
330 if missing_java_files: | |
331 logging.warning('Some java files were not found: %s', missing_java_files) | |
332 | |
333 return java_dirs | |
334 | 349 |
335 | 350 |
336 def _CreateJniLibsDir(output_dir, entry_output_dir, so_files): | 351 def _CreateJniLibsDir(output_dir, entry_output_dir, so_files): |
337 """Creates directory with symlinked .so files if necessary. | 352 """Creates directory with symlinked .so files if necessary. |
338 | 353 |
339 Returns list of JNI libs directories.""" | 354 Returns list of JNI libs directories.""" |
340 | 355 |
341 if so_files: | 356 if so_files: |
342 symlink_dir = os.path.join(entry_output_dir, _JNI_LIBS_SUBDIR) | 357 symlink_dir = os.path.join(entry_output_dir, _JNI_LIBS_SUBDIR) |
343 shutil.rmtree(symlink_dir, True) | 358 shutil.rmtree(symlink_dir, True) |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
590 _ExtractSrcjars(generator.project_dir, srcjar_tuples) | 605 _ExtractSrcjars(generator.project_dir, srcjar_tuples) |
591 | 606 |
592 logging.warning('Project created! (%d subprojects)', len(project_entries)) | 607 logging.warning('Project created! (%d subprojects)', len(project_entries)) |
593 logging.warning('Generated projects work best with Android Studio 2.2') | 608 logging.warning('Generated projects work best with Android Studio 2.2') |
594 logging.warning('For more tips: https://chromium.googlesource.com/chromium' | 609 logging.warning('For more tips: https://chromium.googlesource.com/chromium' |
595 '/src.git/+/master/docs/android_studio.md') | 610 '/src.git/+/master/docs/android_studio.md') |
596 | 611 |
597 | 612 |
598 if __name__ == '__main__': | 613 if __name__ == '__main__': |
599 main() | 614 main() |
OLD | NEW |