verify_source_deps.py 4.88 KB
Newer Older
1 2 3 4 5 6 7 8 9
#!/usr/bin/env python
# Copyright 2015 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""
Script to print potentially missing source dependencies based on the actual
.h and .cc files in the source tree and which files are included in the gyp
and gn files. The latter inclusion is overapproximated.
10

11 12
TODO(machenbach): If two source files with the same name exist, but only one
is referenced from a gyp/gn file, we won't necessarily detect it.
13 14 15 16 17
"""

import itertools
import re
import os
18
import subprocess
19
import sys
20 21 22 23 24 25


V8_BASE = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))

GYP_FILES = [
  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 35
  os.path.join(V8_BASE, 'testing', 'gmock.gyp'),
  os.path.join(V8_BASE, 'testing', 'gtest.gyp'),
36 37 38
  os.path.join(V8_BASE, 'tools', 'parser-shell.gyp'),
]

39 40 41 42 43 44 45 46 47 48 49 50
ALL_GYP_PREFIXES = [
  '..',
  'common',
  os.path.join('src', 'third_party', 'vtune'),
  'src',
  'samples',
  'testing',
  'tools',
  os.path.join('test', 'cctest'),
  os.path.join('test', 'common'),
  os.path.join('test', 'fuzzer'),
  os.path.join('test', 'unittests'),
51
  os.path.join('test', 'inspector'),
52 53 54 55 56 57
]

GYP_UNSUPPORTED_FEATURES = [
  'gcmole',
]

58 59
GN_FILES = [
  os.path.join(V8_BASE, 'BUILD.gn'),
60 61
  os.path.join(V8_BASE, 'build', 'secondary', 'testing', 'gmock', 'BUILD.gn'),
  os.path.join(V8_BASE, 'build', 'secondary', 'testing', 'gtest', 'BUILD.gn'),
62 63 64
  os.path.join(V8_BASE, 'src', 'inspector', 'BUILD.gn'),
  os.path.join(V8_BASE, 'test', 'cctest', 'BUILD.gn'),
  os.path.join(V8_BASE, 'test', 'unittests', 'BUILD.gn'),
65
  os.path.join(V8_BASE, 'test', 'inspector', 'BUILD.gn'),
66 67 68 69 70 71 72
  os.path.join(V8_BASE, 'tools', 'BUILD.gn'),
]

GN_UNSUPPORTED_FEATURES = [
  'aix',
  'cygwin',
  'freebsd',
73
  'gcmole',
74 75 76 77 78 79 80 81
  'openbsd',
  'ppc',
  'qnx',
  'solaris',
  'vtune',
  'x87',
]

82 83 84 85
ALL_GN_PREFIXES = [
  '..',
  os.path.join('src', 'inspector'),
  'src',
86
  'testing',
87 88
  os.path.join('test', 'cctest'),
  os.path.join('test', 'unittests'),
89
  os.path.join('test', 'inspector'),
90 91 92 93 94 95 96 97 98
]

def pathsplit(path):
  return re.split('[/\\\\]', path)

def path_no_prefix(path, prefixes):
  for prefix in prefixes:
    if path.startswith(prefix + os.sep):
      return path_no_prefix(path[len(prefix) + 1:], prefixes)
99
  return path
100 101


102 103 104 105 106 107
def isources(prefixes):
  cmd = ['git', 'ls-tree', '-r', 'HEAD', '--full-name', '--name-only']
  for f in subprocess.check_output(cmd, universal_newlines=True).split('\n'):
    if not (f.endswith('.h') or f.endswith('.cc')):
      continue
    yield path_no_prefix(os.path.join(*pathsplit(f)), prefixes)
108 109 110 111 112 113 114 115 116 117 118 119


def iflatten(obj):
  if isinstance(obj, dict):
    for value in obj.values():
      for i in iflatten(value):
        yield i
  elif isinstance(obj, list):
    for value in obj:
      for i in iflatten(value):
        yield i
  elif isinstance(obj, basestring):
120
    yield path_no_prefix(os.path.join(*pathsplit(obj)), ALL_GYP_PREFIXES)
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140


def iflatten_gyp_file(gyp_file):
  """Overaproximates all values in the gyp file.

  Iterates over all string values recursively. Removes '../' path prefixes.
  """
  with open(gyp_file) as f:
    return iflatten(eval(f.read()))


def iflatten_gn_file(gn_file):
  """Overaproximates all values in the gn file.

  Iterates over all double quoted strings.
  """
  with open(gn_file) as f:
    for line in f.read().splitlines():
      match = re.match(r'.*"([^"]*)".*', line)
      if match:
141 142
        yield path_no_prefix(
            os.path.join(*pathsplit(match.group(1))), ALL_GN_PREFIXES)
143 144


145 146
def icheck_values(values, prefixes):
  for source_file in isources(prefixes):
147 148 149 150
    if source_file not in values:
      yield source_file


151 152 153 154
def missing_gyp_files():
  gyp_values = set(itertools.chain(
    *[iflatten_gyp_file(gyp_file) for gyp_file in GYP_FILES]
    ))
155 156 157
  gyp_files = sorted(icheck_values(gyp_values, ALL_GYP_PREFIXES))
  return filter(
      lambda x: not any(i in x for i in GYP_UNSUPPORTED_FEATURES), gyp_files)
158

159

160 161 162 163
def missing_gn_files():
  gn_values = set(itertools.chain(
    *[iflatten_gn_file(gn_file) for gn_file in GN_FILES]
    ))
164

165
  gn_files = sorted(icheck_values(gn_values, ALL_GN_PREFIXES))
166 167
  return filter(
      lambda x: not any(i in x for i in GN_UNSUPPORTED_FEATURES), gn_files)
168

169 170 171 172 173 174 175 176

def main():
  print "----------- Files not in gyp: ------------"
  for i in missing_gyp_files():
    print i

  print "\n----------- Files not in gn: -------------"
  for i in missing_gn_files():
177
    print i
178 179 180 181
  return 0

if '__main__' == __name__:
  sys.exit(main())