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 'libresolv', 43 'libselinux', 44 'libudev', 45 'libxcb', 46 ] 47 48 kAllowedLibraryList = [ 49 # Toolchain libraries (gcc/glibc) 50 'ld-linux', 51 'libc', 52 'libdl', 53 'libgcc_s', 54 'libm', 55 'libpthread', 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 # Miscellaneous 72 'libcap', 73 'libexpat', 74 'libfontconfig', 75 'libz', 76 ] 77 78 binary_target = 'content_shell' 79 80 def stdmsg(_final, errors): 81 if errors: 82 for message in errors: 83 print message 84 85 def bbmsg(final, errors): 86 if errors: 87 for message in errors: 88 print '@@@STEP_TEXT@%s@@@' % message 89 if final: 90 print '\n@@@STEP_%s@@@' % final 91 92 93 def _main(): 94 output = { 95 'message': lambda x: stdmsg(None, x), 96 'fail': lambda x: stdmsg('FAILED', x), 97 'warn': lambda x: stdmsg('WARNING', x), 98 'abend': lambda x: stdmsg('FAILED', x), 99 'ok': lambda x: stdmsg('SUCCESS', x), 100 'verbose': lambda x: None, 101 } 102 103 parser = optparse.OptionParser( 104 "usage: %prog -b <dir> --target <Debug|Release>") 105 parser.add_option("", "--annotate", dest='annotate', action='store_true', 106 default=False, help="include buildbot annotations in output") 107 parser.add_option("", "--noannotate", dest='annotate', action='store_false') 108 parser.add_option("-b", "--build-dir", 109 help="the location of the compiler output") 110 parser.add_option("--target", help="Debug or Release") 111 parser.add_option('-v', '--verbose', default=False, action='store_true') 112 113 options, args = parser.parse_args() 114 if args: 115 parser.usage() 116 return -1 117 118 # Bake target into build_dir. 119 if options.target and options.build_dir: 120 assert (options.target != 121 os.path.basename(os.path.dirname(options.build_dir))) 122 options.build_dir = os.path.join(os.path.abspath(options.build_dir), 123 options.target) 124 125 if options.build_dir != None: 126 build_dir = os.path.abspath(options.build_dir) 127 else: 128 build_dir = os.getcwd() 129 130 target = os.path.join(build_dir, binary_target) 131 132 if options.annotate: 133 output.update({ 134 'message': lambda x: bbmsg(None, x), 135 'fail': lambda x: bbmsg('FAILURE', x), 136 'warn': lambda x: bbmsg('WARNINGS', x), 137 'abend': lambda x: bbmsg('EXCEPTIONS', x), 138 'ok': lambda x: bbmsg(None, x), 139 }) 140 141 if options.verbose: 142 output['verbose'] = lambda x: stdmsg(None, x) 143 144 forbidden_regexp = re.compile(string.join(map(re.escape, 145 kUndesiredLibraryList), '|')) 146 mapping_regexp = re.compile(r"\s*([^/]*) => (.*)") 147 blessed_regexp = re.compile(r"(%s)[-0-9.]*\.so" % string.join(map(re.escape, 148 kAllowedLibraryList), '|')) 149 built_regexp = re.compile(re.escape(build_dir + os.sep)) 150 151 success = 0 152 warning = 0 153 154 p = subprocess.Popen(['ldd', target], stdout=subprocess.PIPE, 155 stderr=subprocess.PIPE) 156 out, err = p.communicate() 157 158 if err != '': 159 output['abend']([ 160 'Failed to execute ldd to analyze dependencies for ' + target + ':', 161 ' ' + err, 162 ]) 163 return 1 164 165 if out == '': 166 output['abend']([ 167 'No output to scan for forbidden dependencies.' 168 ]) 169 return 1 170 171 success = 1 172 deps = string.split(out, '\n') 173 for d in deps: 174 libmatch = mapping_regexp.match(d) 175 if libmatch: 176 lib = libmatch.group(1) 177 source = libmatch.group(2) 178 if forbidden_regexp.search(lib): 179 success = 0 180 output['message'](['Forbidden library: ' + lib]) 181 elif built_regexp.match(source): 182 output['verbose'](['Built library: ' + lib]) 183 elif blessed_regexp.match(lib): 184 output['verbose'](['Blessed library: ' + lib]) 185 else: 186 warning = 1 187 output['message'](['Unexpected library: ' + lib]) 188 189 if success == 1: 190 if warning == 1: 191 output['warn'](None) 192 else: 193 output['ok'](None) 194 return 0 195 else: 196 output['fail'](None) 197 return 1 198 199 if __name__ == "__main__": 200 # handle arguments... 201 # do something reasonable if not run with one... 202 sys.exit(_main()) 203