#!/usr/bin/env vpython3
#
# Copyright 2021 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.
'''Implements Chrome-Fuchsia package binary size differ.'''
import argparse
import collections
import copy
import json
import logging
import math
import os
import re
import shutil
import subprocess
import sys
import tempfile
import time
import traceback
import uuid
from common import GetHostToolPathFromPlatform, GetHostArchFromPlatform
from common import SDK_ROOT, DIR_SOURCE_ROOT
from binary_sizes import ReadPackageSizesJson
from binary_sizes import PACKAGES_SIZES_FILE
# Eng is not responsible for changes that cause "reasonable growth" if the
# uncompressed binary size does not grow.
# First-warning will fail the test if the uncompressed and compressed size
# grow, while always-fail will fail the test regardless of uncompressed growth
# (solely based on compressed growth).
_FIRST_WARNING_DELTA_BYTES = 12 * 1024 # 12 KiB
_ALWAYS_FAIL_DELTA_BYTES = 100 * 1024 # 100 KiB
_TRYBOT_DOC = 'https://chromium.googlesource.com/chromium/src/+/main/docs/speed/binary_size/fuchsia_binary_size_trybot.md'
def ComputePackageDiffs(before_sizes_file, after_sizes_file):
'''Computes difference between after and before diff, for each package.'''
before_sizes = ReadPackageSizesJson(before_sizes_file)
after_sizes = ReadPackageSizesJson(after_sizes_file)
assert before_sizes.keys() == after_sizes.keys(), (
'Package files cannot'
' be compared with different packages: '
'{} vs {}'.format(before_sizes.keys(), after_sizes.keys()))
growth = {'compressed': {}, 'uncompressed': {}}
status_code = 0
summary = ''
for package_name in before_sizes:
growth['compressed'][package_name] = (after_sizes[package_name].compressed -
before_sizes[package_name].compressed)
growth['uncompressed'][package_name] = (
after_sizes[package_name].uncompressed -
before_sizes[package_name].uncompressed)
# Developers are only responsible if uncompressed increases.
if ((growth['compressed'][package_name] >= _FIRST_WARNING_DELTA_BYTES
and growth['uncompressed'][package_name] > 0)
# However, if compressed growth is unusually large, fail always.
or growth['compressed'][package_name] >= _ALWAYS_FAIL_DELTA_BYTES):
if not summary:
summary = ('Size check failed! The following package(s) are affected:'
'
')
status_code = 1
summary += (('- {} (compressed) grew by {} bytes (uncompressed growth:'
' {} bytes).
').format(
package_name, growth['compressed'][package_name],
growth['uncompressed'][package_name]))
growth['status_code'] = status_code
summary += ('
See the following document for more information about'
' this trybot:
{}'.format(_TRYBOT_DOC))
growth['summary'] = summary
# TODO(crbug.com/1266085): Investigate using these fields.
growth['archive_filenames'] = []
growth['links'] = []
return growth
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'--before-dir',
type=os.path.realpath,
required=True,
help='Location of the build without the patch',
)
parser.add_argument(
'--after-dir',
type=os.path.realpath,
required=True,
help='Location of the build with the patch',
)
parser.add_argument(
'--results-path',
type=os.path.realpath,
required=True,
help='Output path for the trybot result .json file',
)
parser.add_argument('--verbose',
'-v',
action='store_true',
help='Enable verbose output')
args = parser.parse_args()
if args.verbose:
print('Fuchsia binary sizes')
print('Working directory', os.getcwd())
print('Args:')
for var in vars(args):
print(' {}: {}'.format(var, getattr(args, var) or ''))
if not os.path.isdir(args.before_dir) or not os.path.isdir(args.after_dir):
raise Exception(
'Could not find build output directory "{}" or "{}".'.format(
args.before_dir, args.after_dir))
test_name = 'sizes'
before_sizes_file = os.path.join(args.before_dir, test_name,
PACKAGES_SIZES_FILE)
after_sizes_file = os.path.join(args.after_dir, test_name,
PACKAGES_SIZES_FILE)
if not os.path.isfile(before_sizes_file):
raise Exception(
'Could not find before sizes file: "{}"'.format(before_sizes_file))
if not os.path.isfile(after_sizes_file):
raise Exception(
'Could not find after sizes file: "{}"'.format(after_sizes_file))
test_completed = False
try:
growth = ComputePackageDiffs(before_sizes_file, after_sizes_file)
test_completed = True
with open(args.results_path, 'wt') as results_file:
json.dump(growth, results_file)
except:
_, value, trace = sys.exc_info()
traceback.print_tb(trace)
print(str(value))
finally:
return 0 if test_completed else 1
if __name__ == '__main__':
sys.exit(main())