Android: Default no longer builds generated files

* Switch Android Studio's generate_gradle.py script to no longer build
  all the generated files by default. This reduces runtime for a clean
  build from 10 minutes to 20 seconds. Add a `--full` flag to explicitly
  build these generated files, which should not be necessary on a
  regular basis.

* Most of the benefit from generated files being present is fewer red
  underlines in Android Studio. Many red underlines are from lacking a
  R.java file, which takes a long time to generate.

* Fix bug so generate_gradle.py works even right after `gn clean`.

* Allow users to specify `-j` so generate_gradle.py works without goma.

* Speed up generating the `all` module (which is the default) by not
  creating each sub-project's build.gradle file when generating the
  information that it needs.

Bug: 799954,620034
Change-Id: Ie3c6ea0cdba198aede001332d64bec2eccffbe13
Reviewed-on: https://chromium-review.googlesource.com/1031052
Reviewed-by: Eric Stevenson <estevenson@chromium.org>
Commit-Queue: Peter Wen <wnwen@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#554838}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: ccbb2419379c8eb34bfc2bf262c5d29b995116c0
This commit is contained in:
Peter Wen 2018-04-30 19:13:55 +00:00 коммит произвёл Commit Bot
Родитель 343b7396c7
Коммит bd7145a538
1 изменённых файлов: 70 добавлений и 63 удалений

Просмотреть файл

@ -108,12 +108,12 @@ def _ReadPropertiesFile(path):
return dict(l.rstrip().split('=', 1) for l in f if '=' in l) return dict(l.rstrip().split('=', 1) for l in f if '=' in l)
def _RunNinja(output_dir, args): def _RunNinja(output_dir, args, j):
cmd = [ cmd = [
os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'ninja'), os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'ninja'),
'-C', '-C',
output_dir, output_dir,
'-j1000', '-j{}'.format(j),
] ]
cmd.extend(args) cmd.extend(args)
logging.info('Running: %r', cmd) logging.info('Running: %r', cmd)
@ -270,7 +270,7 @@ class _ProjectEntry(object):
class _ProjectContextGenerator(object): class _ProjectContextGenerator(object):
"""Helper class to generate gradle build files""" """Helper class to generate gradle build files"""
def __init__(self, project_dir, build_vars, use_gradle_process_resources, def __init__(self, project_dir, build_vars, use_gradle_process_resources,
jinja_processor, split_projects, channel): jinja_processor, split_projects, channel):
self.project_dir = project_dir self.project_dir = project_dir
self.build_vars = build_vars self.build_vars = build_vars
self.use_gradle_process_resources = use_gradle_process_resources self.use_gradle_process_resources = use_gradle_process_resources
@ -356,12 +356,21 @@ class _ProjectContextGenerator(object):
return set(_RebasePath(res_zips)) return set(_RebasePath(res_zips))
def GeneratedInputs(self, root_entry): def GeneratedInputs(self, root_entry):
generated_inputs = set(self.AllResZips(root_entry)) generated_inputs = self.AllResZips(root_entry)
generated_inputs.update(self.AllSrcjars(root_entry)) generated_inputs.update(self.AllSrcjars(root_entry))
for entry in self._GetEntries(root_entry): for entry in self._GetEntries(root_entry):
generated_inputs.update(entry.GeneratedJavaFiles()) generated_inputs.update(entry.GeneratedJavaFiles())
generated_inputs.update(entry.PrebuiltJars()) generated_inputs.update(entry.PrebuiltJars())
return set(generated_inputs) return generated_inputs
def GeneratedZips(self, root_entry):
entry_output_dir = self.EntryOutputDir(root_entry)
tuples = []
tuples.extend((s, os.path.join(entry_output_dir, _SRCJARS_SUBDIR))
for s in self.AllSrcjars(root_entry))
tuples.extend((s, os.path.join(entry_output_dir, _RES_SUBDIR))
for s in self.AllResZips(root_entry))
return tuples
def Generate(self, root_entry): def Generate(self, root_entry):
# TODO(agrieve): Add an option to use interface jars and see if that speeds # TODO(agrieve): Add an option to use interface jars and see if that speeds
@ -623,7 +632,7 @@ def _GenerateRootGradle(jinja_processor, channel):
return jinja_processor.Render(_TemplatePath('root'), {'channel': channel}) return jinja_processor.Render(_TemplatePath('root'), {'channel': channel})
def _GenerateSettingsGradle(project_entries, add_all_module): def _GenerateSettingsGradle(project_entries):
"""Returns the data for settings.gradle.""" """Returns the data for settings.gradle."""
project_name = os.path.basename(os.path.dirname(host_paths.DIR_SOURCE_ROOT)) project_name = os.path.basename(os.path.dirname(host_paths.DIR_SOURCE_ROOT))
lines = [] lines = []
@ -631,17 +640,11 @@ def _GenerateSettingsGradle(project_entries, add_all_module):
lines.append('rootProject.name = "%s"' % project_name) lines.append('rootProject.name = "%s"' % project_name)
lines.append('rootProject.projectDir = settingsDir') lines.append('rootProject.projectDir = settingsDir')
lines.append('') lines.append('')
for name, subdir in project_entries:
if add_all_module:
lines.append('include ":{0}"'.format(_MODULE_ALL))
lines.append(
'project(":{0}").projectDir = new File(settingsDir, "{0}")'.format(
_MODULE_ALL))
for entry in project_entries:
# Example target: android_webview:android_webview_java__build_config # Example target: android_webview:android_webview_java__build_config
lines.append('include ":%s"' % entry.ProjectName()) lines.append('include ":%s"' % name)
lines.append('project(":%s").projectDir = new File(settingsDir, "%s")' % lines.append('project(":%s").projectDir = new File(settingsDir, "%s")' %
(entry.ProjectName(), entry.GradleSubdir())) (name, subdir))
return '\n'.join(lines) return '\n'.join(lines)
@ -741,6 +744,12 @@ def main():
action='store_true', action='store_true',
help='Split projects by their gn deps rather than ' help='Split projects by their gn deps rather than '
'combining all the dependencies of each target') 'combining all the dependencies of each target')
parser.add_argument('--full',
action='store_true',
help='Generate R.java and other ninja-generated files')
parser.add_argument('-j',
default=1000,
help='Value for number of parallel jobs for ninja')
version_group = parser.add_mutually_exclusive_group() version_group = parser.add_mutually_exclusive_group()
version_group.add_argument('--beta', version_group.add_argument('--beta',
action='store_true', action='store_true',
@ -781,21 +790,7 @@ def main():
_gradle_output_dir = os.path.abspath( _gradle_output_dir = os.path.abspath(
args.project_dir.replace('$CHROMIUM_OUTPUT_DIR', output_dir)) args.project_dir.replace('$CHROMIUM_OUTPUT_DIR', output_dir))
jinja_processor = jinja_template.JinjaProcessor(_FILE_DIR) logging.warning('Creating project at: %s', _gradle_output_dir)
build_vars = _ReadPropertiesFile(os.path.join(output_dir, 'build_vars.txt'))
source_properties = _ReadPropertiesFile(
_RebasePath(os.path.join(build_vars['android_sdk_build_tools'],
'source.properties')))
if args.beta:
channel = 'beta'
elif args.canary:
channel = 'canary'
else:
channel = 'stable'
generator = _ProjectContextGenerator(_gradle_output_dir, build_vars,
args.use_gradle_process_resources, jinja_processor, args.split_projects,
channel)
logging.warning('Creating project at: %s', generator.project_dir)
# Generate for "all targets" by default when not using --split-projects (too # Generate for "all targets" by default when not using --split-projects (too
# slow), and when no --target has been explicitly set. "all targets" means all # slow), and when no --target has been explicitly set. "all targets" means all
@ -809,17 +804,30 @@ def main():
if args.all: if args.all:
# Run GN gen if necessary (faster than running "gn gen" in the no-op case). # Run GN gen if necessary (faster than running "gn gen" in the no-op case).
_RunNinja(constants.GetOutDirectory(), ['build.ninja']) _RunNinja(constants.GetOutDirectory(), ['build.ninja'], args.j)
# Query ninja for all __build_config targets. # Query ninja for all __build_config targets.
targets = _QueryForAllGnTargets(output_dir) targets = _QueryForAllGnTargets(output_dir)
else: else:
targets = [re.sub(r'_test_apk$', '_test_apk__apk', t) targets = [re.sub(r'_test_apk$', '_test_apk__apk', t)
for t in targets_from_args] for t in targets_from_args]
build_vars = _ReadPropertiesFile(os.path.join(output_dir, 'build_vars.txt'))
jinja_processor = jinja_template.JinjaProcessor(_FILE_DIR)
if args.beta:
channel = 'beta'
elif args.canary:
channel = 'canary'
else:
channel = 'stable'
generator = _ProjectContextGenerator(_gradle_output_dir, build_vars,
args.use_gradle_process_resources, jinja_processor, args.split_projects,
channel)
main_entries = [_ProjectEntry.FromGnTarget(t) for t in targets] main_entries = [_ProjectEntry.FromGnTarget(t) for t in targets]
logging.warning('Building .build_config files...') logging.warning('Building .build_config files...')
_RunNinja(output_dir, [e.NinjaBuildConfigTarget() for e in main_entries]) _RunNinja(
output_dir, [e.NinjaBuildConfigTarget() for e in main_entries], args.j)
if args.all: if args.all:
# There are many unused libraries, so restrict to those that are actually # There are many unused libraries, so restrict to those that are actually
@ -837,41 +845,33 @@ def main():
entries = [e for e in _CombineTestEntries(main_entries) if e.IsValid()] entries = [e for e in _CombineTestEntries(main_entries) if e.IsValid()]
logging.info('Creating %d projects for targets.', len(entries)) logging.info('Creating %d projects for targets.', len(entries))
logging.warning('Writing .gradle files...')
source_properties = _ReadPropertiesFile(
_RebasePath(os.path.join(build_vars['android_sdk_build_tools'],
'source.properties')))
project_entries = []
# When only one entry will be generated we want it to have a valid # When only one entry will be generated we want it to have a valid
# build.gradle file with its own AndroidManifest. # build.gradle file with its own AndroidManifest.
add_all_module = not args.split_projects and len(entries) > 1 add_all_module = not args.split_projects and len(entries) > 1
logging.warning('Writing .gradle files...')
project_entries = []
zip_tuples = []
generated_inputs = []
for entry in entries: for entry in entries:
data = _GenerateGradleFile(entry, generator, build_vars, source_properties, data = _GenerateGradleFile(
jinja_processor) entry, generator, build_vars, source_properties, jinja_processor)
if data: if data and add_all_module:
# Build all paths references by .gradle that exist within output_dir. project_entries.append((entry.ProjectName(), entry.GradleSubdir()))
generated_inputs.extend(generator.GeneratedInputs(entry))
zip_tuples.extend(
(s, os.path.join(generator.EntryOutputDir(entry), _SRCJARS_SUBDIR))
for s in generator.AllSrcjars(entry))
zip_tuples.extend(
(s, os.path.join(generator.EntryOutputDir(entry), _RES_SUBDIR))
for s in generator.AllResZips(entry))
if not add_all_module:
project_entries.append(entry)
_WriteFile( _WriteFile(
os.path.join(generator.EntryOutputDir(entry), _GRADLE_BUILD_FILE), os.path.join(generator.EntryOutputDir(entry), _GRADLE_BUILD_FILE),
data) data)
if add_all_module: if add_all_module:
_GenerateModuleAll(_gradle_output_dir, generator, build_vars, project_entries.append((_MODULE_ALL, _MODULE_ALL))
source_properties, jinja_processor) _GenerateModuleAll(
_gradle_output_dir, generator, build_vars, source_properties,
jinja_processor)
_WriteFile(os.path.join(generator.project_dir, _GRADLE_BUILD_FILE), _WriteFile(os.path.join(generator.project_dir, _GRADLE_BUILD_FILE),
_GenerateRootGradle(jinja_processor, channel)) _GenerateRootGradle(jinja_processor, channel))
_WriteFile(os.path.join(generator.project_dir, 'settings.gradle'), _WriteFile(os.path.join(generator.project_dir, 'settings.gradle'),
_GenerateSettingsGradle(project_entries, add_all_module)) _GenerateSettingsGradle(project_entries))
if args.sdk != "AndroidStudioCurrent": if args.sdk != "AndroidStudioCurrent":
if args.sdk_path: if args.sdk_path:
@ -883,16 +883,23 @@ def main():
_WriteFile(os.path.join(generator.project_dir, 'local.properties'), _WriteFile(os.path.join(generator.project_dir, 'local.properties'),
_GenerateLocalProperties(sdk_path)) _GenerateLocalProperties(sdk_path))
if generated_inputs: if args.full:
logging.warning('Building generated source files...') zip_tuples = []
targets = _RebasePath(generated_inputs, output_dir) generated_inputs = set()
_RunNinja(output_dir, targets) for entry in entries:
# Build all paths references by .gradle that exist within output_dir.
generated_inputs.update(generator.GeneratedInputs(entry))
zip_tuples.extend(generator.GeneratedZips(entry))
if generated_inputs:
logging.warning('Building generated source files...')
targets = _RebasePath(generated_inputs, output_dir)
_RunNinja(output_dir, targets, args.j)
if zip_tuples:
_ExtractZips(generator.project_dir, zip_tuples)
if zip_tuples: logging.warning('Generated projects for Android Studio %s', channel)
_ExtractZips(generator.project_dir, zip_tuples) if not args.full:
logging.warning('Run with --full flag to update generated files (slow)')
logging.warning('Project created!')
logging.warning('Generated projects work with Android Studio %s', channel)
logging.warning('For more tips: https://chromium.googlesource.com/chromium' logging.warning('For more tips: https://chromium.googlesource.com/chromium'
'/src.git/+/master/docs/android_studio.md') '/src.git/+/master/docs/android_studio.md')