Home | History | Annotate | Download | only in check_ecs_deps
      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