[MediaRouter] Update MR-2-Extension's PostMessage to return boolean.
[chromium-blink-merge.git] / native_client_sdk / src / build_tools / verify_ppapi.py
blobf1c36c38dceda65c02f4e43eac51308b5e712d11
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 argparse
15 import os
16 import sys
18 from build_paths import PPAPI_DIR, SRC_DIR, SDK_LIBRARY_DIR
19 import parse_dsc
22 # Add a file to this list if it should not be added to a .dsc file; i.e. if it
23 # should not be included in the Native Client SDK. This will silence the
24 # presubmit warning.
26 # Some examples of files that should not be added to the SDK are: Dev and
27 # Private interfaces that are either not available to NaCl plugins or are only
28 # available to Flash or other privileged plugins.
29 IGNORED_FILES = set([
30 'pp_video_dev.h'
34 class VerifyException(Exception):
35 def __init__(self, lib_path, expected, unexpected):
36 self.expected = expected
37 self.unexpected = unexpected
39 msg = 'In %s:\n' % lib_path
40 if expected:
41 msg += ' these files are missing and should be added:\n'
42 for filename in sorted(expected):
43 msg += ' %s\n' % filename
44 if unexpected:
45 msg += ' these files no longer exist and should be removed:\n'
46 for filename in sorted(unexpected):
47 msg += ' %s\n' % filename
49 Exception.__init__(self, msg)
52 def PartitionFiles(filenames):
53 c_filenames = set()
54 cpp_filenames = set()
55 private_filenames = set()
57 for filename in filenames:
58 if os.path.splitext(filename)[1] not in ('.cc', '.h'):
59 continue
61 parts = filename.split(os.sep)
62 basename = os.path.basename(filename)
63 if basename in IGNORED_FILES:
64 continue
66 if 'private' in filename:
67 if 'flash' in filename:
68 continue
69 private_filenames.add(filename)
70 elif parts[0:2] == ['ppapi', 'c']:
71 if len(parts) >= 2 and parts[2] in ('documentation', 'trusted'):
72 continue
73 c_filenames.add(filename)
74 elif (parts[0:2] == ['ppapi', 'cpp'] or
75 parts[0:2] == ['ppapi', 'utility']):
76 if len(parts) >= 2 and parts[2] in ('documentation', 'trusted'):
77 continue
78 cpp_filenames.add(filename)
79 else:
80 continue
82 return {
83 'ppapi': c_filenames,
84 'ppapi_cpp': cpp_filenames,
85 'ppapi_cpp_private': private_filenames
89 def GetDirectoryList(directory_path, relative_to):
90 result = []
91 for root, _, files in os.walk(directory_path):
92 rel_root = os.path.relpath(root, relative_to)
93 if rel_root == '.':
94 rel_root = ''
95 for base_name in files:
96 result.append(os.path.join(rel_root, base_name))
97 return result
100 def GetDscSourcesAndHeaders(dsc):
101 result = []
102 for headers_info in dsc.get('HEADERS', []):
103 result.extend(headers_info['FILES'])
104 for targets_info in dsc.get('TARGETS', []):
105 result.extend(targets_info['SOURCES'])
106 return result
109 def GetChangedAndRemovedFilenames(modified_filenames, directory_list):
110 changed = set()
111 removed = set()
112 directory_list_set = set(directory_list)
113 for filename in modified_filenames:
114 if filename in directory_list_set:
115 # We can't know if a file was added (that would require knowing the
116 # previous state of the working directory). Instead, we assume that a
117 # changed file may have been added, and check it accordingly.
118 changed.add(filename)
119 else:
120 removed.add(filename)
121 return changed, removed
124 def GetDscFilenameFromLibraryName(lib_name):
125 return os.path.join(SDK_LIBRARY_DIR, lib_name, 'library.dsc')
128 def Verify(dsc_filename, dsc_sources_and_headers, changed_filenames,
129 removed_filenames):
130 expected_filenames = set()
131 unexpected_filenames = set()
133 for filename in changed_filenames:
134 basename = os.path.basename(filename)
135 if basename not in dsc_sources_and_headers:
136 expected_filenames.add(filename)
138 for filename in removed_filenames:
139 basename = os.path.basename(filename)
140 if basename in dsc_sources_and_headers:
141 unexpected_filenames.add(filename)
143 if expected_filenames or unexpected_filenames:
144 raise VerifyException(dsc_filename, expected_filenames,
145 unexpected_filenames)
148 def VerifyOrPrintError(dsc_filename, dsc_sources_and_headers, changed_filenames,
149 removed_filenames, is_private=False):
150 try:
151 Verify(dsc_filename, dsc_sources_and_headers, changed_filenames,
152 removed_filenames)
153 except VerifyException as e:
154 should_fail = True
155 if is_private and e.expected:
156 # For ppapi_cpp_private, we don't fail if there are expected filenames...
157 # we may not want to include them. We still want to fail if there are
158 # unexpected filenames, though.
159 sys.stderr.write('>>> WARNING: private interface files changed. '
160 'Should they be added to the Native Client SDK? <<<\n')
161 if not e.unexpected:
162 should_fail = False
163 sys.stderr.write(str(e) + '\n')
164 if should_fail:
165 return False
166 return True
169 def main(args):
170 parser = argparse.ArgumentParser(description=__doc__)
171 parser.add_argument('sources', nargs='+')
172 options = parser.parse_args(args)
174 retval = 0
175 lib_files = PartitionFiles(options.sources)
176 directory_list = GetDirectoryList(PPAPI_DIR, relative_to=SRC_DIR)
177 for lib_name, filenames in lib_files.iteritems():
178 if not filenames:
179 continue
181 changed_filenames, removed_filenames = \
182 GetChangedAndRemovedFilenames(filenames, directory_list)
184 dsc_filename = GetDscFilenameFromLibraryName(lib_name)
185 dsc = parse_dsc.LoadProject(dsc_filename)
186 dsc_sources_and_headers = GetDscSourcesAndHeaders(dsc)
188 # Use the relative path to the .dsc to make the error messages shorter.
189 rel_dsc_filename = os.path.relpath(dsc_filename, SRC_DIR)
190 is_private = lib_name == 'ppapi_cpp_private'
191 if not VerifyOrPrintError(rel_dsc_filename, dsc_sources_and_headers,
192 changed_filenames, removed_filenames,
193 is_private=is_private):
194 retval = 1
195 return retval
198 if __name__ == '__main__':
199 sys.exit(main(sys.argv[1:]))