1 #!/usr/bin/env python 2 # Copyright 2013 The Chromium Authors. All rights reserved. 3 # Use of this source code is governed by a BSD-style license that can be 4 # found in the LICENSE file. 5 6 ''' Verifies that builds of the embedded content_shell do not included 7 unnecessary dependencies.''' 8 9 import os 10 import re 11 import string 12 import subprocess 13 import sys 14 import optparse 15 16 kUndesiredLibraryList = [ 17 'libX11', 18 'libXau', 19 'libXcomposite', 20 'libXcursor', 21 'libXdamage', 22 'libXdmcp', 23 'libXext', 24 'libXfixes', 25 'libXi', 26 'libXrandr', 27 'libXrender', 28 'libXtst', 29 'libasound', 30 'libcairo', 31 'libdbus', 32 'libffi', 33 'libgconf', 34 'libgio', 35 'libglib', 36 'libgmodule', 37 'libgobject', 38 'libpango', 39 'libpcre', 40 'libpixman', 41 'libpng', 42 'libselinux', 43 'libudev', 44 'libxcb', 45 ] 46 47 kAllowedLibraryList = [ 48 # Toolchain libraries (gcc/glibc) 49 'ld-linux', 50 'libc', 51 'libdl', 52 'libgcc_s', 53 'libm', 54 'libpthread', 55 'libresolv', 56 'librt', 57 'libstdc++', 58 'linux-vdso', 59 60 # Needed for default ozone platforms 61 'libdrm', 62 63 # NSS & NSPR 64 'libnss3', 65 'libnssutil3', 66 'libnspr4', 67 'libplc4', 68 'libplds4', 69 'libsmime3', 70 71 # OpenSSL 72 'libcrypto', 73 74 # Miscellaneous 75 'libcap', 76 'libexpat', 77 'libfontconfig', 78 'libz', 79 ] 80 81 binary_target = 'content_shell' 82 83 def stdmsg(_final, errors): 84 if errors: 85 for message in errors: 86 print message 87 88 def bbmsg(final, errors): 89 if errors: 90 for message in errors: 91 print '@@@STEP_TEXT@%s@@@' % message 92 if final: 93 print '\n@@@STEP_%s@@@' % final 94 95 96 def _main(): 97 output = { 98 'message': lambda x: stdmsg(None, x), 99 'fail': lambda x: stdmsg('FAILED', x), 100 'warn': lambda x: stdmsg('WARNING', x), 101 'abend': lambda x: stdmsg('FAILED', x), 102 'ok': lambda x: stdmsg('SUCCESS', x), 103 'verbose': lambda x: None, 104 } 105 106 parser = optparse.OptionParser( 107 "usage: %prog -b <dir> --target <Debug|Release>") 108 parser.add_option("", "--annotate", dest='annotate', action='store_true', 109 default=False, help="include buildbot annotations in output") 110 parser.add_option("", "--noannotate", dest='annotate', action='store_false') 111 parser.add_option("-b", "--build-dir", 112 help="the location of the compiler output") 113 parser.add_option("--target", help="Debug or Release") 114 parser.add_option('-v', '--verbose', default=False, action='store_true') 115 116 options, args = parser.parse_args() 117 if args: 118 parser.usage() 119 return -1 120 121 # Bake target into build_dir. 122 if options.target and options.build_dir: 123 assert (options.target != 124 os.path.basename(os.path.dirname(options.build_dir))) 125 options.build_dir = os.path.join(os.path.abspath(options.build_dir), 126 options.target) 127 128 if options.build_dir != None: 129 build_dir = os.path.abspath(options.build_dir) 130 else: 131 build_dir = os.getcwd() 132 133 target = os.path.join(build_dir, binary_target) 134 135 if options.annotate: 136 output.update({ 137 'message': lambda x: bbmsg(None, x), 138 'fail': lambda x: bbmsg('FAILURE', x), 139 'warn': lambda x: bbmsg('WARNINGS', x), 140 'abend': lambda x: bbmsg('EXCEPTIONS', x), 141 'ok': lambda x: bbmsg(None, x), 142 }) 143 144 if options.verbose: 145 output['verbose'] = lambda x: stdmsg(None, x) 146 147 forbidden_regexp = re.compile(string.join(map(re.escape, 148 kUndesiredLibraryList), '|')) 149 mapping_regexp = re.compile(r"\s*([^/]*) => (.*)") 150 blessed_regexp = re.compile(r"(%s)[-0-9.]*\.so" % string.join(map(re.escape, 151 kAllowedLibraryList), '|')) 152 built_regexp = re.compile(re.escape(build_dir + os.sep)) 153 154 success = 0 155 warning = 0 156 157 p = subprocess.Popen(['ldd', target], stdout=subprocess.PIPE, 158 stderr=subprocess.PIPE) 159 out, err = p.communicate() 160 161 if err != '': 162 output['abend']([ 163 'Failed to execute ldd to analyze dependencies for ' + target + ':', 164 ' ' + err, 165 ]) 166 return 1 167 168 if out == '': 169 output['abend']([ 170 'No output to scan for forbidden dependencies.' 171 ]) 172 return 1 173 174 success = 1 175 deps = string.split(out, '\n') 176 for d in deps: 177 libmatch = mapping_regexp.match(d) 178 if libmatch: 179 lib = libmatch.group(1) 180 source = libmatch.group(2) 181 if forbidden_regexp.search(lib): 182 success = 0 183 output['message'](['Forbidden library: ' + lib]) 184 elif built_regexp.match(source): 185 output['verbose'](['Built library: ' + lib]) 186 elif blessed_regexp.match(lib): 187 output['verbose'](['Blessed library: ' + lib]) 188 else: 189 warning = 1 190 output['message'](['Unexpected library: ' + lib]) 191 192 if success == 1: 193 if warning == 1: 194 output['warn'](None) 195 else: 196 output['ok'](None) 197 return 0 198 else: 199 output['fail'](None) 200 return 1 201 202 if __name__ == "__main__": 203 # handle arguments... 204 # do something reasonable if not run with one... 205 sys.exit(_main()) 206