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""" 7Script 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 9and gn files. The latter inclusion is overapproximated. 10 11TODO(machenbach): If two source files with the same name exist, but only one 12is referenced from a gyp/gn file, we won't necessarily detect it. 13""" 14 15import itertools 16import re 17import os 18import subprocess 19import sys 20 21 22V8_BASE = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 23 24GYP_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 39ALL_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 54GYP_UNSUPPORTED_FEATURES = [ 55 'gcmole', 56] 57 58GN_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 69GN_UNSUPPORTED_FEATURES = [ 70 'aix', 71 'cygwin', 72 'freebsd', 73 'gcmole', 74 'openbsd', 75 'ppc', 76 'qnx', 77 'solaris', 78 'vtune', 79 'x87', 80] 81 82ALL_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 92def pathsplit(path): 93 return re.split('[/\\\\]', path) 94 95def 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 102def 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 110def 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 123def 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 132def 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 145def icheck_values(values, prefixes): 146 for source_file in isources(prefixes): 147 if source_file not in values: 148 yield source_file 149 150 151def 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 160def 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 170def 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 180if '__main__' == __name__: 181 sys.exit(main()) 182