• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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