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

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

Issue 2682183002: Android: Replace symlinks with gradle filters (Closed)
Patch Set: Fix per review 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 | « build/android/gradle/android.jinja ('k') | docs/android_studio.md » ('j') | 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 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
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
276
277
278 def _ComputeExcludeFilters(wanted_files, unwanted_files, parent_dir):
279 """Returns exclude patters to exclude unwanted files but keep wanted files.
280
281 - Shortens exclude list by globbing if possible.
282 - Exclude patterns are relative paths from the parent directory.
283 """
284 excludes = []
285 files_to_include = set(wanted_files)
286 files_to_exclude = set(unwanted_files)
287 while files_to_exclude:
288 unwanted_file = files_to_exclude.pop()
289 target_exclude = os.path.join(
290 os.path.dirname(unwanted_file), '*.java')
291 found_files = set(glob.glob(target_exclude))
292 valid_files = found_files & files_to_include
293 if valid_files:
294 excludes.append(os.path.relpath(unwanted_file, parent_dir))
295 else:
296 excludes.append(os.path.relpath(target_exclude, parent_dir))
297 files_to_exclude -= found_files
298 return excludes
299
300
301 def _CreateJavaSourceDir(output_dir, java_files):
302 """Computes the list of java source directories and exclude patterns.
303
304 1. Computes the root java source directories from the list of files.
305 2. Compute exclude patterns that exclude all extra files only.
306 3. Returns the list of java source directories and exclude patterns.
307 """
308 java_dirs = []
309 excludes = []
310 if java_files:
311 java_files = _RebasePath(java_files)
312 computed_dirs = _ComputeJavaSourceDirs(java_files)
313 java_dirs = computed_dirs.keys()
314 all_found_java_files = set()
315
316 for directory, files in computed_dirs.iteritems():
317 found_java_files = build_utils.FindInDirectory(directory, '*.java')
318 all_found_java_files.update(found_java_files)
319 unwanted_java_files = set(found_java_files) - set(files)
320 if unwanted_java_files:
321 logging.debug('Directory requires excludes: %s', directory)
322 excludes.extend(
323 _ComputeExcludeFilters(files, unwanted_java_files, directory))
324
325 missing_java_files = set(java_files) - all_found_java_files
326 # Warn only about non-generated files that are missing.
327 missing_java_files = [p for p in missing_java_files
328 if not p.startswith(output_dir)]
329 if missing_java_files:
330 logging.warning(
331 'Some java files were not found: %s', missing_java_files)
332
333 return java_dirs, excludes
273 334
274 335
275 def _CreateRelativeSymlink(target_path, link_path): 336 def _CreateRelativeSymlink(target_path, link_path):
276 link_dir = os.path.dirname(link_path) 337 link_dir = os.path.dirname(link_path)
277 relpath = os.path.relpath(target_path, link_dir) 338 relpath = os.path.relpath(target_path, link_dir)
278 logging.debug('Creating symlink %s -> %s', link_path, relpath) 339 logging.debug('Creating symlink %s -> %s', link_path, relpath)
279 os.symlink(relpath, link_path) 340 os.symlink(relpath, link_path)
280 341
281 342
282 def _CreateSymlinkTree(entry_output_dir, symlink_dir, desired_files,
283 parent_dirs):
284 """Creates a directory tree of symlinks to the given files.
285
286 The idea here is to replicate a directory tree while leaving out files within
287 it not listed by |desired_files|.
288 """
289 assert _IsSubpathOf(symlink_dir, entry_output_dir)
290
291 for target_path in desired_files:
292 prefix = next(d for d in parent_dirs if target_path.startswith(d))
293 subpath = os.path.relpath(target_path, prefix)
294 symlinked_path = os.path.join(symlink_dir, subpath)
295 symlinked_dir = os.path.dirname(symlinked_path)
296 if not os.path.exists(symlinked_dir):
297 os.makedirs(symlinked_dir)
298 _CreateRelativeSymlink(target_path, symlinked_path)
299
300
301 def _CreateJavaSourceDir(output_dir, entry_output_dir, java_files):
302 """Computes and constructs when necessary the list of java source directories.
303
304 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
306 in |java_files|.
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 java_dirs = []
311 if java_files:
312 java_files = _RebasePath(java_files)
313 java_dirs = _ComputeJavaSourceDirs(java_files)
314
315 found_java_files = build_utils.FindInDirectories(java_dirs, '*.java')
316 unwanted_java_files = set(found_java_files) - set(java_files)
317 missing_java_files = set(java_files) - set(found_java_files)
318 # Warn only about non-generated files that are missing.
319 missing_java_files = [p for p in missing_java_files
320 if not p.startswith(output_dir)]
321
322 symlink_dir = os.path.join(entry_output_dir, _JAVA_SUBDIR)
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
335
336 def _CreateJniLibsDir(output_dir, entry_output_dir, so_files): 343 def _CreateJniLibsDir(output_dir, entry_output_dir, so_files):
337 """Creates directory with symlinked .so files if necessary. 344 """Creates directory with symlinked .so files if necessary.
338 345
339 Returns list of JNI libs directories.""" 346 Returns list of JNI libs directories."""
340 347
341 if so_files: 348 if so_files:
342 symlink_dir = os.path.join(entry_output_dir, _JNI_LIBS_SUBDIR) 349 symlink_dir = os.path.join(entry_output_dir, _JNI_LIBS_SUBDIR)
343 shutil.rmtree(symlink_dir, True) 350 shutil.rmtree(symlink_dir, True)
344 abi_dir = os.path.join(symlink_dir, _ARMEABI_SUBDIR) 351 abi_dir = os.path.join(symlink_dir, _ARMEABI_SUBDIR)
345 if not os.path.exists(abi_dir): 352 if not os.path.exists(abi_dir):
(...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after
590 _ExtractSrcjars(generator.project_dir, srcjar_tuples) 597 _ExtractSrcjars(generator.project_dir, srcjar_tuples)
591 598
592 logging.warning('Project created! (%d subprojects)', len(project_entries)) 599 logging.warning('Project created! (%d subprojects)', len(project_entries))
593 logging.warning('Generated projects work best with Android Studio 2.2') 600 logging.warning('Generated projects work best with Android Studio 2.2')
594 logging.warning('For more tips: https://chromium.googlesource.com/chromium' 601 logging.warning('For more tips: https://chromium.googlesource.com/chromium'
595 '/src.git/+/master/docs/android_studio.md') 602 '/src.git/+/master/docs/android_studio.md')
596 603
597 604
598 if __name__ == '__main__': 605 if __name__ == '__main__':
599 main() 606 main()
OLDNEW
« no previous file with comments | « build/android/gradle/android.jinja ('k') | docs/android_studio.md » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698