[SyncFS] Build indexes from FileTracker entries on disk.
[chromium-blink-merge.git] / native_client_sdk / src / build_tools / verify_ppapi.py
blob8a51e3c28811364120e795bf0b5e6a9cb3bba04e
1 #!/usr/bin/env python
2 # Copyright (c) 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.
6 """Helper script for PPAPI's PRESUBMIT.py to detect if additions or removals of
7 PPAPI interfaces have been propagated to the Native Client libraries (.dsc
8 files).
10 For example, if a user adds "ppapi/c/foo.h", we check that the interface has
11 been added to "native_client_sdk/src/libraries/ppapi/library.dsc".
12 """
14 import optparse
15 import os
16 import sys
18 from build_paths import PPAPI_DIR, SRC_DIR, SDK_LIBRARY_DIR
19 import parse_dsc
22 class VerifyException(Exception):
23 def __init__(self, lib_path, expected, unexpected):
24 self.expected = expected
25 self.unexpected = unexpected
27 msg = 'In %s:\n' % lib_path
28 if expected:
29 msg += ' these files are missing and should be added:\n'
30 for filename in sorted(expected):
31 msg += ' %s\n' % filename
32 if unexpected:
33 msg += ' these files no longer exist and should be removed:\n'
34 for filename in sorted(unexpected):
35 msg += ' %s\n' % filename
37 Exception.__init__(self, msg)
40 def PartitionFiles(filenames):
41 c_filenames = set()
42 cpp_filenames = set()
43 private_filenames = set()
45 for filename in filenames:
46 if os.path.splitext(filename)[1] not in ('.cc', '.h'):
47 continue
49 parts = filename.split(os.sep)
50 if 'private' in filename:
51 if 'flash' in filename:
52 continue
53 private_filenames.add(filename)
54 elif parts[0:2] == ['ppapi', 'c']:
55 if len(parts) >= 2 and parts[2] in ('documentation', 'trusted'):
56 continue
57 c_filenames.add(filename)
58 elif (parts[0:2] == ['ppapi', 'cpp'] or
59 parts[0:2] == ['ppapi', 'utility']):
60 if len(parts) >= 2 and parts[2] in ('documentation', 'trusted'):
61 continue
62 cpp_filenames.add(filename)
63 else:
64 continue
66 return {
67 'ppapi': c_filenames,
68 'ppapi_cpp': cpp_filenames,
69 'ppapi_cpp_private': private_filenames
73 def GetDirectoryList(directory_path, relative_to):
74 result = []
75 for root, _, files in os.walk(directory_path):
76 rel_root = os.path.relpath(root, relative_to)
77 if rel_root == '.':
78 rel_root = ''
79 for base_name in files:
80 result.append(os.path.join(rel_root, base_name))
81 return result
84 def GetDscSourcesAndHeaders(dsc):
85 result = []
86 for headers_info in dsc.get('HEADERS', []):
87 result.extend(headers_info['FILES'])
88 for targets_info in dsc.get('TARGETS', []):
89 result.extend(targets_info['SOURCES'])
90 return result
93 def GetChangedAndRemovedFilenames(modified_filenames, directory_list):
94 changed = set()
95 removed = set()
96 directory_list_set = set(directory_list)
97 for filename in modified_filenames:
98 if filename in directory_list_set:
99 # We can't know if a file was added (that would require knowing the
100 # previous state of the working directory). Instead, we assume that a
101 # changed file may have been added, and check it accordingly.
102 changed.add(filename)
103 else:
104 removed.add(filename)
105 return changed, removed
108 def GetDscFilenameFromLibraryName(lib_name):
109 return os.path.join(SDK_LIBRARY_DIR, lib_name, 'library.dsc')
112 def Verify(dsc_filename, dsc_sources_and_headers, changed_filenames,
113 removed_filenames):
114 expected_filenames = set()
115 unexpected_filenames = set()
117 for filename in changed_filenames:
118 basename = os.path.basename(filename)
119 if basename not in dsc_sources_and_headers:
120 expected_filenames.add(filename)
122 for filename in removed_filenames:
123 basename = os.path.basename(filename)
124 if basename in dsc_sources_and_headers:
125 unexpected_filenames.add(filename)
127 if expected_filenames or unexpected_filenames:
128 raise VerifyException(dsc_filename, expected_filenames,
129 unexpected_filenames)
132 def VerifyOrPrintError(dsc_filename, dsc_sources_and_headers, changed_filenames,
133 removed_filenames, is_private=False):
134 try:
135 Verify(dsc_filename, dsc_sources_and_headers, changed_filenames,
136 removed_filenames)
137 except VerifyException as e:
138 should_fail = True
139 if is_private and e.expected:
140 # For ppapi_cpp_private, we don't fail if there are expected filenames...
141 # we may not want to include them. We still want to fail if there are
142 # unexpected filenames, though.
143 sys.stderr.write('>>> WARNING: private interface files changed. '
144 'Should they be added to the Native Client SDK? <<<\n')
145 if not e.unexpected:
146 should_fail = False
147 sys.stderr.write(str(e) + '\n')
148 if should_fail:
149 return False
150 return True
153 def main(args):
154 usage = '%prog <file>...'
155 description = __doc__
156 parser = optparse.OptionParser(usage=usage, description=description)
157 args = parser.parse_args(args)[1]
158 if not args:
159 parser.error('Expected a PPAPI header or source file.')
161 retval = 0
162 lib_files = PartitionFiles(args)
163 directory_list = GetDirectoryList(PPAPI_DIR, relative_to=SRC_DIR)
164 for lib_name, filenames in lib_files.iteritems():
165 if not filenames:
166 continue
168 changed_filenames, removed_filenames = \
169 GetChangedAndRemovedFilenames(filenames, directory_list)
171 dsc_filename = GetDscFilenameFromLibraryName(lib_name)
172 dsc = parse_dsc.LoadProject(dsc_filename)
173 dsc_sources_and_headers = GetDscSourcesAndHeaders(dsc)
175 # Use the relative path to the .dsc to make the error messages shorter.
176 rel_dsc_filename = os.path.relpath(dsc_filename, SRC_DIR)
177 is_private = lib_name == 'ppapi_cpp_private'
178 if not VerifyOrPrintError(rel_dsc_filename, dsc_sources_and_headers,
179 changed_filenames, removed_filenames,
180 is_private=is_private):
181 retval = 1
182 return retval
185 if __name__ == '__main__':
186 sys.exit(main(sys.argv[1:]))