1#!/usr/bin/env python2.7 2# 3# Copyright 2018 gRPC authors. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import argparse 18import glob 19import multiprocessing 20import os 21import shutil 22import subprocess 23import sys 24from parse_link_map import parse_link_map 25 26sys.path.append( 27 os.path.join(os.path.dirname(sys.argv[0]), '..', '..', 'run_tests', 28 'python_utils')) 29import check_on_pr 30 31# Only show diff 1KB or greater 32diff_threshold = 1000 33 34size_labels = ('Core', 'ObjC', 'BoringSSL', 'Protobuf', 'Total') 35 36argp = argparse.ArgumentParser( 37 description='Binary size diff of gRPC Objective-C sample') 38 39argp.add_argument('-d', 40 '--diff_base', 41 type=str, 42 help='Commit or branch to compare the current one to') 43 44args = argp.parse_args() 45 46 47def dir_size(dir): 48 total = 0 49 for dirpath, dirnames, filenames in os.walk(dir): 50 for f in filenames: 51 fp = os.path.join(dirpath, f) 52 total += os.stat(fp).st_size 53 return total 54 55 56def get_size(where, frameworks): 57 build_dir = 'src/objective-c/examples/Sample/Build/Build-%s/' % where 58 if not frameworks: 59 link_map_filename = 'Build/Intermediates.noindex/Sample.build/Release-iphoneos/Sample.build/Sample-LinkMap-normal-arm64.txt' 60 return parse_link_map(build_dir + link_map_filename) 61 else: 62 framework_dir = 'Build/Products/Release-iphoneos/Sample.app/Frameworks/' 63 boringssl_size = dir_size(build_dir + framework_dir + 64 'openssl.framework') 65 core_size = dir_size(build_dir + framework_dir + 'grpc.framework') 66 objc_size = dir_size(build_dir + framework_dir + 'GRPCClient.framework') + \ 67 dir_size(build_dir + framework_dir + 'RxLibrary.framework') + \ 68 dir_size(build_dir + framework_dir + 'ProtoRPC.framework') 69 protobuf_size = dir_size(build_dir + framework_dir + 70 'Protobuf.framework') 71 app_size = dir_size(build_dir + 72 'Build/Products/Release-iphoneos/Sample.app') 73 return core_size, objc_size, boringssl_size, protobuf_size, app_size 74 75 76def build(where, frameworks): 77 subprocess.check_call(['make', 'clean']) 78 shutil.rmtree('src/objective-c/examples/Sample/Build/Build-%s' % where, 79 ignore_errors=True) 80 subprocess.check_call( 81 'CONFIG=opt EXAMPLE_PATH=src/objective-c/examples/Sample SCHEME=Sample FRAMEWORKS=%s ./build_one_example.sh' 82 % ('YES' if frameworks else 'NO'), 83 shell=True, 84 cwd='src/objective-c/tests') 85 os.rename('src/objective-c/examples/Sample/Build/Build', 86 'src/objective-c/examples/Sample/Build/Build-%s' % where) 87 88 89text = 'Objective-C binary sizes\n' 90for frameworks in [False, True]: 91 build('new', frameworks) 92 new_size = get_size('new', frameworks) 93 old_size = None 94 95 if args.diff_base: 96 old = 'old' 97 where_am_i = subprocess.check_output( 98 ['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip() 99 subprocess.check_call(['git', 'checkout', '--', '.']) 100 subprocess.check_call(['git', 'checkout', args.diff_base]) 101 subprocess.check_call(['git', 'submodule', 'update', '--force']) 102 try: 103 build('old', frameworks) 104 old_size = get_size('old', frameworks) 105 finally: 106 subprocess.check_call(['git', 'checkout', '--', '.']) 107 subprocess.check_call(['git', 'checkout', where_am_i]) 108 subprocess.check_call(['git', 'submodule', 'update', '--force']) 109 110 text += ('***************FRAMEWORKS****************\n' 111 if frameworks else '*****************STATIC******************\n') 112 row_format = "{:>10}{:>15}{:>15}" + '\n' 113 text += row_format.format('New size', '', 'Old size') 114 if old_size == None: 115 for i in range(0, len(size_labels)): 116 text += ('\n' if i == len(size_labels) - 117 1 else '') + row_format.format('{:,}'.format(new_size[i]), 118 size_labels[i], '') 119 else: 120 has_diff = False 121 for i in range(0, len(size_labels) - 1): 122 if abs(new_size[i] - old_size[i]) < diff_threshold: 123 continue 124 if new_size[i] > old_size[i]: 125 diff_sign = ' (>)' 126 else: 127 diff_sign = ' (<)' 128 has_diff = True 129 text += row_format.format('{:,}'.format(new_size[i]), 130 size_labels[i] + diff_sign, 131 '{:,}'.format(old_size[i])) 132 i = len(size_labels) - 1 133 if new_size[i] > old_size[i]: 134 diff_sign = ' (>)' 135 elif new_size[i] < old_size[i]: 136 diff_sign = ' (<)' 137 else: 138 diff_sign = ' (=)' 139 text += ('\n' if has_diff else '') + row_format.format( 140 '{:,}'.format(new_size[i]), size_labels[i] + diff_sign, 141 '{:,}'.format(old_size[i])) 142 if not has_diff: 143 text += '\n No significant differences in binary sizes\n' 144 text += '\n' 145 146print text 147 148check_on_pr.check_on_pr('Binary Size', '```\n%s\n```' % text) 149