Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/env python
      2 # Copyright 2015 the V8 project 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 """
      7 Script to print potentially missing source dependencies based on the actual
      8 .h and .cc files in the source tree and which files are included in the gyp
      9 and gn files. The latter inclusion is overapproximated.
     10 
     11 TODO(machenbach): If two source files with the same name exist, but only one
     12 is referenced from a gyp/gn file, we won't necessarily detect it.
     13 """
     14 
     15 import itertools
     16 import re
     17 import os
     18 import subprocess
     19 import sys
     20 
     21 
     22 V8_BASE = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
     23 
     24 GYP_FILES = [
     25   os.path.join(V8_BASE, 'src', 'd8.gyp'),
     26   os.path.join(V8_BASE, 'src', 'v8.gyp'),
     27   os.path.join(V8_BASE, 'src', 'inspector', 'inspector.gypi'),
     28   os.path.join(V8_BASE, 'src', 'third_party', 'vtune', 'v8vtune.gyp'),
     29   os.path.join(V8_BASE, 'samples', 'samples.gyp'),
     30   os.path.join(V8_BASE, 'test', 'cctest', 'cctest.gyp'),
     31   os.path.join(V8_BASE, 'test', 'fuzzer', 'fuzzer.gyp'),
     32   os.path.join(V8_BASE, 'test', 'unittests', 'unittests.gyp'),
     33   os.path.join(V8_BASE, 'test', 'inspector', 'inspector.gyp'),
     34   os.path.join(V8_BASE, 'testing', 'gmock.gyp'),
     35   os.path.join(V8_BASE, 'testing', 'gtest.gyp'),
     36   os.path.join(V8_BASE, 'tools', 'parser-shell.gyp'),
     37 ]
     38 
     39 ALL_GYP_PREFIXES = [
     40   '..',
     41   'common',
     42   os.path.join('src', 'third_party', 'vtune'),
     43   'src',
     44   'samples',
     45   'testing',
     46   'tools',
     47   os.path.join('test', 'cctest'),
     48   os.path.join('test', 'common'),
     49   os.path.join('test', 'fuzzer'),
     50   os.path.join('test', 'unittests'),
     51   os.path.join('test', 'inspector'),
     52 ]
     53 
     54 GYP_UNSUPPORTED_FEATURES = [
     55   'gcmole',
     56 ]
     57 
     58 GN_FILES = [
     59   os.path.join(V8_BASE, 'BUILD.gn'),
     60   os.path.join(V8_BASE, 'build', 'secondary', 'testing', 'gmock', 'BUILD.gn'),
     61   os.path.join(V8_BASE, 'build', 'secondary', 'testing', 'gtest', 'BUILD.gn'),
     62   os.path.join(V8_BASE, 'src', 'inspector', 'BUILD.gn'),
     63   os.path.join(V8_BASE, 'test', 'cctest', 'BUILD.gn'),
     64   os.path.join(V8_BASE, 'test', 'unittests', 'BUILD.gn'),
     65   os.path.join(V8_BASE, 'test', 'inspector', 'BUILD.gn'),
     66   os.path.join(V8_BASE, 'tools', 'BUILD.gn'),
     67 ]
     68 
     69 GN_UNSUPPORTED_FEATURES = [
     70   'aix',
     71   'cygwin',
     72   'freebsd',
     73   'gcmole',
     74   'openbsd',
     75   'ppc',
     76   'qnx',
     77   'solaris',
     78   'vtune',
     79   'x87',
     80 ]
     81 
     82 ALL_GN_PREFIXES = [
     83   '..',
     84   os.path.join('src', 'inspector'),
     85   'src',
     86   'testing',
     87   os.path.join('test', 'cctest'),
     88   os.path.join('test', 'unittests'),
     89   os.path.join('test', 'inspector'),
     90 ]
     91 
     92 def pathsplit(path):
     93   return re.split('[/\\\\]', path)
     94 
     95 def path_no_prefix(path, prefixes):
     96   for prefix in prefixes:
     97     if path.startswith(prefix + os.sep):
     98       return path_no_prefix(path[len(prefix) + 1:], prefixes)
     99   return path
    100 
    101 
    102 def isources(prefixes):
    103   cmd = ['git', 'ls-tree', '-r', 'HEAD', '--full-name', '--name-only']
    104   for f in subprocess.check_output(cmd, universal_newlines=True).split('\n'):
    105     if not (f.endswith('.h') or f.endswith('.cc')):
    106       continue
    107     yield path_no_prefix(os.path.join(*pathsplit(f)), prefixes)
    108 
    109 
    110 def iflatten(obj):
    111   if isinstance(obj, dict):
    112     for value in obj.values():
    113       for i in iflatten(value):
    114         yield i
    115   elif isinstance(obj, list):
    116     for value in obj:
    117       for i in iflatten(value):
    118         yield i
    119   elif isinstance(obj, basestring):
    120     yield path_no_prefix(os.path.join(*pathsplit(obj)), ALL_GYP_PREFIXES)
    121 
    122 
    123 def iflatten_gyp_file(gyp_file):
    124   """Overaproximates all values in the gyp file.
    125 
    126   Iterates over all string values recursively. Removes '../' path prefixes.
    127   """
    128   with open(gyp_file) as f:
    129     return iflatten(eval(f.read()))
    130 
    131 
    132 def iflatten_gn_file(gn_file):
    133   """Overaproximates all values in the gn file.
    134 
    135   Iterates over all double quoted strings.
    136   """
    137   with open(gn_file) as f:
    138     for line in f.read().splitlines():
    139       match = re.match(r'.*"([^"]*)".*', line)
    140       if match:
    141         yield path_no_prefix(
    142             os.path.join(*pathsplit(match.group(1))), ALL_GN_PREFIXES)
    143 
    144 
    145 def icheck_values(values, prefixes):
    146   for source_file in isources(prefixes):
    147     if source_file not in values:
    148       yield source_file
    149 
    150 
    151 def missing_gyp_files():
    152   gyp_values = set(itertools.chain(
    153     *[iflatten_gyp_file(gyp_file) for gyp_file in GYP_FILES]
    154     ))
    155   gyp_files = sorted(icheck_values(gyp_values, ALL_GYP_PREFIXES))
    156   return filter(
    157       lambda x: not any(i in x for i in GYP_UNSUPPORTED_FEATURES), gyp_files)
    158 
    159 
    160 def missing_gn_files():
    161   gn_values = set(itertools.chain(
    162     *[iflatten_gn_file(gn_file) for gn_file in GN_FILES]
    163     ))
    164 
    165   gn_files = sorted(icheck_values(gn_values, ALL_GN_PREFIXES))
    166   return filter(
    167       lambda x: not any(i in x for i in GN_UNSUPPORTED_FEATURES), gn_files)
    168 
    169 
    170 def main():
    171   print "----------- Files not in gyp: ------------"
    172   for i in missing_gyp_files():
    173     print i
    174 
    175   print "\n----------- Files not in gn: -------------"
    176   for i in missing_gn_files():
    177     print i
    178   return 0
    179 
    180 if '__main__' == __name__:
    181   sys.exit(main())
    182