blob: 840af3ecb9843dea017ce34282b388d9f70577b2 [file] [log] [blame]
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from recipe_engine import post_process
DEPS = [
'chromium',
'chromium_android',
'chromium_checkout',
'chromium_tests',
'depot_tools/bot_update',
'depot_tools/gclient',
'depot_tools/gerrit',
'depot_tools/tryserver',
'filter',
'recipe_engine/context',
'recipe_engine/file',
'recipe_engine/json',
'recipe_engine/path',
'recipe_engine/platform',
'recipe_engine/properties',
'recipe_engine/python',
'recipe_engine/step',
]
_ANALYZE_TARGETS = [
'//chrome/android:monochrome_public_apk',
'//tools/binary_size:binary_size_trybot_py',
]
_COMPILE_TARGETS = [
'monochrome_public_apk',
]
_APK_NAME = 'MonochromePublic.apk'
_PATCH_FIXED_BUILD_STEP_NAME = (
'Not measuring binary size because build is broken without patch.')
_FOOTER_PRESENT_STEP_NAME = (
'Not measuring binary size because Binary-Size justification was provided.')
def RunSteps(api):
assert api.tryserver.is_tryserver
with api.chromium.chromium_layout():
api.gclient.set_config('chromium')
api.gclient.apply_config('android')
api.chromium.set_config('chromium')
api.chromium.apply_config('mb')
api.chromium_android.set_config('base_config')
revision_info = api.gerrit.get_revision_info(
api.properties['patch_gerrit_url'],
api.properties['patch_issue'],
api.properties['patch_set'])
author = revision_info['commit']['author']['email']
# get_footer returns a list of footer values.
size_footers = api.tryserver.get_footer(
'Binary-Size', patch_text=revision_info['commit']['message'])
# Short-circuit early so that the bot is fast when disabled via header.
# Although the bot is also meant to test compiles of official builds,
# headers are generally only added when a previous job compiles fine and
# fails with the "You need to add the header" message.
if size_footers:
api.python.succeeding_step(_FOOTER_PRESENT_STEP_NAME, '')
return
suffix = ' (with patch)'
bot_update_step = api.bot_update.ensure_checkout(suffix=suffix, patch=True)
api.chromium.runhooks(name='runhooks' + suffix)
affected_files = api.chromium_checkout.get_files_affected_by_patch()
if not api.filter.analyze(affected_files, _ANALYZE_TARGETS, None,
'trybot_analyze_config.json')[0]:
return
api.chromium.ensure_goma()
with_results_dir = _BuildAndMeasure(api, True)
with api.context(cwd=api.chromium_checkout.working_dir):
api.bot_update.deapply_patch(bot_update_step)
with api.context(cwd=api.path['checkout']):
suffix = ' (without patch)'
try:
api.chromium.runhooks(name='runhooks' + suffix)
without_results_dir = _BuildAndMeasure(api, False)
except api.step.StepFailure:
api.python.succeeding_step(_PATCH_FIXED_BUILD_STEP_NAME, '')
return
# Re-apply patch so that the diff scripts can be tested via tryjobs.
# We could build without-patch first to avoid having to apply the patch
# twice, but it's nicer to fail fast when the patch does not compile.
suffix = ' (with patch again)'
api.bot_update.ensure_checkout(suffix=suffix, patch=True)
api.chromium.runhooks(name='runhooks' + suffix)
with api.context(cwd=api.path['checkout']):
resource_sizes_diff_path, diff_summary_str = _ResourceSizesDiff(
api, without_results_dir, with_results_dir)
_SupersizeDiff(api, without_results_dir, with_results_dir)
_CheckForUnexpectedIncrease(
api, resource_sizes_diff_path, author, diff_summary_str)
def _BuildAndMeasure(api, with_patch):
suffix = ' (with patch)' if with_patch else ' (without patch)'
results_basename = 'with_patch' if with_patch else 'without_patch'
api.chromium_tests.run_mb_and_compile(_COMPILE_TARGETS, None, suffix)
results_dir = api.chromium.output_dir.join(results_basename)
api.file.ensure_directory('mkdir ' + results_basename, results_dir)
apk_path = api.chromium_android.apk_path(_APK_NAME)
# Can't use api.chromium_android.resource_sizes() without it trying to upload
# the results.
api.python(
'resource_sizes ({}){}'.format(api.path.basename(apk_path), suffix),
api.chromium_android.c.resource_sizes, [
str(apk_path),
'--chartjson',
'--output-dir', results_dir,
'--chromium-output-directory', api.chromium.output_dir,
])
api.json.read(
'resource_sizes result{}'.format(suffix),
results_dir.join('results-chart.json'))
size_path = results_dir.join(_APK_NAME + '.size')
api.chromium_android.supersize_archive(
apk_path, size_path, step_suffix=suffix)
return results_dir
def _SupersizeDiff(api, without_results_dir, with_results_dir):
diagnose_bloat = api.path['checkout'].join(
'tools', 'binary_size', 'diagnose_bloat.py')
diff_output_path = api.chromium.output_dir.join('supersize_diff.txt')
api.python('Supersize diff', diagnose_bloat, [
'diff',
'--apk-name', _APK_NAME,
'--diff-type', 'native',
'--before-dir', without_results_dir,
'--after-dir', with_results_dir,
'--diff-output', diff_output_path,
])
diff_text = api.file.read_text('Show Supersize Diff', diff_output_path)
read_step_result = api.step.active_result
read_step_result.presentation.step_text = '(Look here for detailed breakdown)'
read_step_result.presentation.logs['>>> Show Diff <<<'] = (
diff_text.splitlines())
def _ResourceSizesDiff(api, without_results_dir, with_results_dir):
diagnose_bloat = api.path['checkout'].join(
'tools', 'binary_size', 'diagnose_bloat.py')
diff_output_path = api.chromium.output_dir.join('resource_sizes_diff.txt')
api.python('resource_sizes diff', diagnose_bloat, [
'diff',
'--apk-name', _APK_NAME,
'--diff-type', 'sizes',
'--before-dir', without_results_dir,
'--after-dir', with_results_dir,
'--diff-output', diff_output_path,
])
diff_text = api.file.read_text(
'Show Resource Sizes Diff', diff_output_path,
test_data='MonochromePublic.apk_Specifics normalized apk size=-5\n')
diff_lines = diff_text.splitlines()
diff_summary_str = 'Normalized apk size: ' + diff_lines[-1].partition('=')[2]
read_step_result = api.step.active_result
read_step_result.presentation.step_text = '(Look here for high-level metrics)'
read_step_result.presentation.logs['>>> Show Diff <<<'] = diff_lines
return diff_output_path, diff_summary_str
def _CheckForUnexpectedIncrease(api, resource_sizes_diff_path, author,
diff_summary_str):
checker_script = api.path['checkout'].join(
'tools', 'binary_size', 'trybot_commit_size_checker.py')
step_result = api.python('check for undocumented increase', checker_script, [
'--author', author,
'--resource-sizes-diff', resource_sizes_diff_path,
])
step_result.presentation.step_text = '({})'.format(diff_summary_str)
def GenTests(api):
def props(name, size_footer=False, **kwargs):
kwargs.setdefault('path_config', 'kitchen')
kwargs['revision'] = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
revision_info = {
'_number': 1,
'commit': {
'author': {
'email': 'foo@bar.com',
},
'message': 'message',
}
}
footer_json = {}
if size_footer:
footer_json['Binary-Size'] = ['Totally worth it.']
return (
api.test(name) +
api.properties.tryserver(
build_config='Release',
mastername='tryserver.chromium.android',
buildername='android_binary_size',
patch_set=1,
**kwargs) +
api.platform('linux', 64) +
api.override_step_data(
'gerrit changes',
api.json.output([{
'revisions': {
kwargs['revision']: revision_info
}
}])) +
api.override_step_data('parse description',
api.json.output(footer_json))
)
def override_analyze(no_changes=False):
"""Overrides analyze step data so that targets get compiled."""
return api.override_step_data(
'analyze',
api.json.output({
'status': 'Found dependency',
'compile_targets': _ANALYZE_TARGETS,
'test_targets': [] if no_changes else _COMPILE_TARGETS}))
yield (
props('noop_because_of_size_footer', size_footer=True) +
api.post_process(post_process.MustRun, _FOOTER_PRESENT_STEP_NAME) +
api.post_process(post_process.DropExpectation)
)
yield (
props('noop_because_of_analyze') +
override_analyze(no_changes=True) +
api.post_process(post_process.MustRun, 'analyze') +
api.post_process(post_process.DoesNotRunRE, r'.*build') +
api.post_process(post_process.DropExpectation)
)
yield (
props('patch_fixes_build') +
override_analyze() +
api.override_step_data('compile (without patch)', retcode=1) +
api.post_process(post_process.MustRun, _PATCH_FIXED_BUILD_STEP_NAME) +
api.post_process(post_process.DropExpectation)
)
yield (
props('normal_build') +
override_analyze()
)