blob: 99835ccf29606c604ff23c8a3de2e77f9f501d80 [file] [log] [blame]
andrew@webrtc.org2442de12012-01-23 17:45:411# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
2#
3# Use of this source code is governed by a BSD-style license
4# that can be found in the LICENSE file in the root of the source
5# tree. An additional intellectual property rights grant can be found
6# in the file PATENTS. All contributing project authors may
7# be found in the AUTHORS file in the root of the source tree.
niklase@google.comda159d62011-05-30 11:51:348
kjellander7439f972016-12-06 06:47:469import json
kjellander@webrtc.orgaefe61a2014-12-08 13:00:3010import os
kjellander@webrtc.org85759802013-10-22 16:47:4011import re
kjellander@webrtc.org3bd41562014-09-01 11:06:3712import sys
Mirko Bonadei4dc4e252017-09-19 11:49:1613from collections import defaultdict
Oleh Prypin2f33a562017-10-04 18:17:5414from contextlib import contextmanager
kjellander@webrtc.org85759802013-10-22 16:47:4015
oprypin2aa463f2017-03-23 10:17:0216# Files and directories that are *skipped* by cpplint in the presubmit script.
17CPPLINT_BLACKLIST = [
Mirko Bonadei92ea95e2017-09-15 04:47:3118 'api/video_codecs/video_decoder.h',
19 'common_types.cc',
20 'common_types.h',
21 'examples/objc',
Amit Hilbuchce7032b2019-01-23 00:19:3522 'media/base/stream_params.h',
23 'media/base/video_common.h',
24 'media/sctp/sctp_transport.cc',
Mirko Bonadei92ea95e2017-09-15 04:47:3125 'modules/audio_coding',
Mirko Bonadei92ea95e2017-09-15 04:47:3126 'modules/audio_device',
27 'modules/audio_processing',
28 'modules/desktop_capture',
29 'modules/include/module_common_types.h',
Mirko Bonadei92ea95e2017-09-15 04:47:3130 'modules/utility',
31 'modules/video_capture',
Amit Hilbuchce7032b2019-01-23 00:19:3532 'p2p/base/pseudo_tcp.cc',
33 'p2p/base/pseudo_tcp.h',
Mirko Bonadei92ea95e2017-09-15 04:47:3134 'rtc_base',
35 'sdk/android/src/jni',
36 'sdk/objc',
37 'system_wrappers',
38 'test',
Henrik Kjellander90fd7d82017-05-09 06:30:1039 'tools_webrtc',
Mirko Bonadei92ea95e2017-09-15 04:47:3140 'voice_engine',
kjellander@webrtc.org0fcaf992015-11-26 14:24:5241]
42
jbauchc4e3ead2016-02-19 08:25:5543# These filters will always be removed, even if the caller specifies a filter
44# set, as they are problematic or broken in some way.
45#
46# Justifications for each filter:
47# - build/c++11 : Rvalue ref checks are unreliable (false positives),
48# include file and feature blacklists are
49# google3-specific.
kjellandere5a87a52016-04-27 09:32:1250# - whitespace/operators: Same as above (doesn't seem sufficient to eliminate
51# all move-related errors).
jbauchc4e3ead2016-02-19 08:25:5552BLACKLIST_LINT_FILTERS = [
53 '-build/c++11',
kjellandere5a87a52016-04-27 09:32:1254 '-whitespace/operators',
jbauchc4e3ead2016-02-19 08:25:5555]
56
kjellanderfd595232015-12-04 10:44:0957# List of directories of "supported" native APIs. That means changes to headers
58# will be done in a compatible way following this scheme:
59# 1. Non-breaking changes are made.
60# 2. The old APIs as marked as deprecated (with comments).
61# 3. Deprecation is announced to discuss-webrtc@googlegroups.com and
62# webrtc-users@google.com (internal list).
63# 4. (later) The deprecated APIs are removed.
kjellander53047c92015-12-03 07:56:1464NATIVE_API_DIRS = (
Karl Wibergef52d8b82017-10-25 11:20:0365 'api', # All subdirectories of api/ are included as well.
Mirko Bonadeia4eeeff2018-01-11 12:16:5266 'media/base',
67 'media/engine',
Mirko Bonadei92ea95e2017-09-15 04:47:3168 'modules/audio_device/include',
69 'pc',
kjellanderdd705472016-06-09 18:17:2770)
Mirko Bonadei4dc4e252017-09-19 11:49:1671
kjellanderdd705472016-06-09 18:17:2772# These directories should not be used but are maintained only to avoid breaking
73# some legacy downstream code.
74LEGACY_API_DIRS = (
Mirko Bonadei92ea95e2017-09-15 04:47:3175 'common_audio/include',
76 'modules/audio_coding/include',
Mirko Bonadei92ea95e2017-09-15 04:47:3177 'modules/audio_processing/include',
Mirko Bonadei92ea95e2017-09-15 04:47:3178 'modules/congestion_controller/include',
79 'modules/include',
80 'modules/remote_bitrate_estimator/include',
81 'modules/rtp_rtcp/include',
82 'modules/rtp_rtcp/source',
83 'modules/utility/include',
84 'modules/video_coding/codecs/h264/include',
Mirko Bonadei92ea95e2017-09-15 04:47:3185 'modules/video_coding/codecs/vp8/include',
86 'modules/video_coding/codecs/vp9/include',
87 'modules/video_coding/include',
88 'rtc_base',
89 'system_wrappers/include',
kjellander53047c92015-12-03 07:56:1490)
Mirko Bonadei4dc4e252017-09-19 11:49:1691
Karl Wibergd4f01c12017-11-10 09:55:4592# NOTE: The set of directories in API_DIRS should be the same as those
93# listed in the table in native-api.md.
kjellanderdd705472016-06-09 18:17:2794API_DIRS = NATIVE_API_DIRS[:] + LEGACY_API_DIRS[:]
kjellander53047c92015-12-03 07:56:1495
Mirko Bonadei4dc4e252017-09-19 11:49:1696# TARGET_RE matches a GN target, and extracts the target name and the contents.
97TARGET_RE = re.compile(r'(?P<indent>\s*)\w+\("(?P<target_name>\w+)"\) {'
98 r'(?P<target_contents>.*?)'
99 r'(?P=indent)}',
100 re.MULTILINE | re.DOTALL)
101
102# SOURCES_RE matches a block of sources inside a GN target.
103SOURCES_RE = re.compile(r'sources \+?= \[(?P<sources>.*?)\]',
104 re.MULTILINE | re.DOTALL)
105
106# FILE_PATH_RE matchies a file path.
107FILE_PATH_RE = re.compile(r'"(?P<file_path>(\w|\/)+)(?P<extension>\.\w+)"')
108
kjellander53047c92015-12-03 07:56:14109
Mirko Bonadeid8665442018-09-04 10:17:27110def FindSrcDirPath(starting_dir):
111 """Returns the abs path to the src/ dir of the project."""
112 src_dir = starting_dir
113 while os.path.basename(src_dir) != 'src':
114 src_dir = os.path.normpath(os.path.join(src_dir, os.pardir))
115 return src_dir
116
117
Oleh Prypin2f33a562017-10-04 18:17:54118@contextmanager
119def _AddToPath(*paths):
120 original_sys_path = sys.path
121 sys.path.extend(paths)
122 try:
123 yield
124 finally:
125 # Restore sys.path to what it was before.
126 sys.path = original_sys_path
ehmaldonado4fb97462017-01-30 13:27:22127
128
charujain9893e252017-09-14 11:33:22129def VerifyNativeApiHeadersListIsValid(input_api, output_api):
kjellander53047c92015-12-03 07:56:14130 """Ensures the list of native API header directories is up to date."""
131 non_existing_paths = []
132 native_api_full_paths = [
133 input_api.os_path.join(input_api.PresubmitLocalPath(),
kjellanderdd705472016-06-09 18:17:27134 *path.split('/')) for path in API_DIRS]
kjellander53047c92015-12-03 07:56:14135 for path in native_api_full_paths:
136 if not os.path.isdir(path):
137 non_existing_paths.append(path)
138 if non_existing_paths:
139 return [output_api.PresubmitError(
140 'Directories to native API headers have changed which has made the '
141 'list in PRESUBMIT.py outdated.\nPlease update it to the current '
142 'location of our native APIs.',
143 non_existing_paths)]
144 return []
145
Artem Titove92675b2018-05-22 08:21:27146
kjellanderc88b5d52017-04-05 13:42:43147API_CHANGE_MSG = """
kwibergeb133022016-04-07 14:41:48148You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 12:13:23149 1. Make compatible changes that don't break existing clients. Usually
150 this is done by keeping the existing method signatures unchanged.
151 2. Mark the old stuff as deprecated (see RTC_DEPRECATED macro).
kwibergeb133022016-04-07 14:41:48152 3. Create a timeline and plan for when the deprecated stuff will be
153 removed. (The amount of time we give users to change their code
154 should be informed by how much work it is for them. If they just
155 need to replace one name with another or something equally
156 simple, 1-2 weeks might be good; if they need to do serious work,
157 up to 3 months may be called for.)
158 4. Update/inform existing downstream code owners to stop using the
159 deprecated stuff. (Send announcements to
160 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
161 5. Remove the deprecated stuff, once the agreed-upon amount of time
162 has passed.
163Related files:
164"""
kjellander53047c92015-12-03 07:56:14165
Artem Titove92675b2018-05-22 08:21:27166
charujain9893e252017-09-14 11:33:22167def CheckNativeApiHeaderChanges(input_api, output_api):
kjellander53047c92015-12-03 07:56:14168 """Checks to remind proper changing of native APIs."""
169 files = []
Karl Wiberg6bfac032017-10-27 13:14:20170 source_file_filter = lambda x: input_api.FilterSourceFile(
171 x, white_list=[r'.+\.(gn|gni|h)$'])
172 for f in input_api.AffectedSourceFiles(source_file_filter):
173 for path in API_DIRS:
174 dn = os.path.dirname(f.LocalPath())
175 if path == 'api':
176 # Special case: Subdirectories included.
177 if dn == 'api' or dn.startswith('api/'):
Niels Möller1d201852019-06-26 10:58:27178 files.append(f.LocalPath())
Karl Wiberg6bfac032017-10-27 13:14:20179 else:
180 # Normal case: Subdirectories not included.
181 if dn == path:
Niels Möller1d201852019-06-26 10:58:27182 files.append(f.LocalPath())
kjellander53047c92015-12-03 07:56:14183
184 if files:
kjellanderc88b5d52017-04-05 13:42:43185 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
kjellander53047c92015-12-03 07:56:14186 return []
187
kjellander@webrtc.org0fcaf992015-11-26 14:24:52188
Artem Titova04d1402018-05-11 09:23:00189def CheckNoIOStreamInHeaders(input_api, output_api,
190 source_file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46191 """Checks to make sure no .h files include <iostream>."""
192 files = []
193 pattern = input_api.re.compile(r'^#include\s*<iostream>',
194 input_api.re.MULTILINE)
Artem Titova04d1402018-05-11 09:23:00195 file_filter = lambda x: (input_api.FilterSourceFile(x)
196 and source_file_filter(x))
197 for f in input_api.AffectedSourceFiles(file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46198 if not f.LocalPath().endswith('.h'):
199 continue
200 contents = input_api.ReadFile(f)
201 if pattern.search(contents):
202 files.append(f)
203
204 if len(files):
Henrik Kjellander57e5fd22015-05-25 10:55:39205 return [output_api.PresubmitError(
kjellander@webrtc.org51198f12012-02-21 17:53:46206 'Do not #include <iostream> in header files, since it inserts static ' +
207 'initialization into every file including the header. Instead, ' +
208 '#include <ostream>. See http://crbug.com/94794',
Henrik Kjellander57e5fd22015-05-25 10:55:39209 files)]
kjellander@webrtc.org51198f12012-02-21 17:53:46210 return []
211
kjellander@webrtc.orge4158642014-08-06 09:11:18212
Artem Titova04d1402018-05-11 09:23:00213def CheckNoPragmaOnce(input_api, output_api,
214 source_file_filter):
kjellander6aeef742017-02-20 09:13:18215 """Make sure that banned functions are not used."""
216 files = []
217 pattern = input_api.re.compile(r'^#pragma\s+once',
218 input_api.re.MULTILINE)
Artem Titova04d1402018-05-11 09:23:00219 file_filter = lambda x: (input_api.FilterSourceFile(x)
220 and source_file_filter(x))
221 for f in input_api.AffectedSourceFiles(file_filter):
kjellander6aeef742017-02-20 09:13:18222 if not f.LocalPath().endswith('.h'):
223 continue
224 contents = input_api.ReadFile(f)
225 if pattern.search(contents):
226 files.append(f)
227
228 if files:
229 return [output_api.PresubmitError(
230 'Do not use #pragma once in header files.\n'
231 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
232 files)]
233 return []
234
235
Artem Titova04d1402018-05-11 09:23:00236def CheckNoFRIEND_TEST(input_api, output_api, # pylint: disable=invalid-name
237 source_file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46238 """Make sure that gtest's FRIEND_TEST() macro is not used, the
239 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
240 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
241 problems = []
242
Artem Titova04d1402018-05-11 09:23:00243 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
244 and source_file_filter(f))
kjellander@webrtc.org51198f12012-02-21 17:53:46245 for f in input_api.AffectedFiles(file_filter=file_filter):
246 for line_num, line in f.ChangedContents():
247 if 'FRIEND_TEST(' in line:
248 problems.append(' %s:%d' % (f.LocalPath(), line_num))
249
250 if not problems:
251 return []
252 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use '
253 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
254 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
255
kjellander@webrtc.orge4158642014-08-06 09:11:18256
charujain9893e252017-09-14 11:33:22257def IsLintBlacklisted(blacklist_paths, file_path):
oprypin2aa463f2017-03-23 10:17:02258 """ Checks if a file is blacklisted for lint check."""
259 for path in blacklist_paths:
260 if file_path == path or os.path.dirname(file_path).startswith(path):
kjellander@webrtc.org0fcaf992015-11-26 14:24:52261 return True
262 return False
263
264
charujain9893e252017-09-14 11:33:22265def CheckApprovedFilesLintClean(input_api, output_api,
Artem Titova04d1402018-05-11 09:23:00266 source_file_filter=None):
oprypin2aa463f2017-03-23 10:17:02267 """Checks that all new or non-blacklisted .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 11:33:22268 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46269 depot_tools/presubmit_canned_checks.py but has less filters and only checks
270 added files."""
271 result = []
272
273 # Initialize cpplint.
274 import cpplint
275 # Access to a protected member _XX of a client class
276 # pylint: disable=W0212
277 cpplint._cpplint_state.ResetErrorCounts()
278
jbauchc4e3ead2016-02-19 08:25:55279 lint_filters = cpplint._Filters()
280 lint_filters.extend(BLACKLIST_LINT_FILTERS)
281 cpplint._SetFilters(','.join(lint_filters))
282
oprypin2aa463f2017-03-23 10:17:02283 # Create a platform independent blacklist for cpplint.
284 blacklist_paths = [input_api.os_path.join(*path.split('/'))
285 for path in CPPLINT_BLACKLIST]
kjellander@webrtc.org0fcaf992015-11-26 14:24:52286
kjellander@webrtc.org51198f12012-02-21 17:53:46287 # Use the strictest verbosity level for cpplint.py (level 1) which is the
oprypin2aa463f2017-03-23 10:17:02288 # default when running cpplint.py from command line. To make it possible to
289 # work with not-yet-converted code, we're only applying it to new (or
290 # moved/renamed) files and files not listed in CPPLINT_BLACKLIST.
kjellander@webrtc.org51198f12012-02-21 17:53:46291 verbosity_level = 1
292 files = []
293 for f in input_api.AffectedSourceFiles(source_file_filter):
Henrik Kjellander57e5fd22015-05-25 10:55:39294 # Note that moved/renamed files also count as added.
charujain9893e252017-09-14 11:33:22295 if f.Action() == 'A' or not IsLintBlacklisted(blacklist_paths,
Artem Titove92675b2018-05-22 08:21:27296 f.LocalPath()):
kjellander@webrtc.org51198f12012-02-21 17:53:46297 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23298
kjellander@webrtc.org51198f12012-02-21 17:53:46299 for file_name in files:
300 cpplint.ProcessFile(file_name, verbosity_level)
301
302 if cpplint._cpplint_state.error_count > 0:
303 if input_api.is_committing:
oprypin8e58d652017-03-21 14:52:41304 res_type = output_api.PresubmitError
kjellander@webrtc.org51198f12012-02-21 17:53:46305 else:
306 res_type = output_api.PresubmitPromptWarning
307 result = [res_type('Changelist failed cpplint.py check.')]
308
309 return result
310
Artem Titove92675b2018-05-22 08:21:27311
charujain9893e252017-09-14 11:33:22312def CheckNoSourcesAbove(input_api, gn_files, output_api):
ehmaldonado5b1ba082016-09-02 12:51:08313 # Disallow referencing source files with paths above the GN file location.
314 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
315 re.MULTILINE | re.DOTALL)
316 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
317 violating_gn_files = set()
318 violating_source_entries = []
319 for gn_file in gn_files:
320 contents = input_api.ReadFile(gn_file)
321 for source_block_match in source_pattern.finditer(contents):
322 # Find all source list entries starting with ../ in the source block
323 # (exclude overrides entries).
324 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
325 source_file = file_list_match.group(1)
326 if 'overrides/' not in source_file:
327 violating_source_entries.append(source_file)
328 violating_gn_files.add(gn_file)
329 if violating_gn_files:
330 return [output_api.PresubmitError(
331 'Referencing source files above the directory of the GN file is not '
Henrik Kjellanderb4af3d62016-11-16 19:11:29332 'allowed. Please introduce new GN targets in the proper location '
333 'instead.\n'
ehmaldonado5b1ba082016-09-02 12:51:08334 'Invalid source entries:\n'
335 '%s\n'
336 'Violating GN files:' % '\n'.join(violating_source_entries),
337 items=violating_gn_files)]
338 return []
339
Artem Titove92675b2018-05-22 08:21:27340
Mirko Bonadei4dc4e252017-09-19 11:49:16341def CheckNoMixingSources(input_api, gn_files, output_api):
342 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
343
344 See bugs.webrtc.org/7743 for more context.
345 """
Artem Titove92675b2018-05-22 08:21:27346
Mirko Bonadei4dc4e252017-09-19 11:49:16347 def _MoreThanOneSourceUsed(*sources_lists):
348 sources_used = 0
349 for source_list in sources_lists:
350 if len(source_list):
351 sources_used += 1
352 return sources_used > 1
353
354 errors = defaultdict(lambda: [])
kjellander7439f972016-12-06 06:47:46355 for gn_file in gn_files:
Mirko Bonadei4dc4e252017-09-19 11:49:16356 gn_file_content = input_api.ReadFile(gn_file)
357 for target_match in TARGET_RE.finditer(gn_file_content):
358 # list_of_sources is a list of tuples of the form
359 # (c_files, cc_files, objc_files) that keeps track of all the sources
360 # defined in a target. A GN target can have more that on definition of
361 # sources (since it supports if/else statements).
362 # E.g.:
363 # rtc_static_library("foo") {
364 # if (is_win) {
365 # sources = [ "foo.cc" ]
366 # } else {
367 # sources = [ "foo.mm" ]
368 # }
369 # }
370 # This is allowed and the presubmit check should support this case.
371 list_of_sources = []
kjellander7439f972016-12-06 06:47:46372 c_files = []
373 cc_files = []
Mirko Bonadei4dc4e252017-09-19 11:49:16374 objc_files = []
375 target_name = target_match.group('target_name')
376 target_contents = target_match.group('target_contents')
377 for sources_match in SOURCES_RE.finditer(target_contents):
378 if '+=' not in sources_match.group(0):
379 if c_files or cc_files or objc_files:
380 list_of_sources.append((c_files, cc_files, objc_files))
381 c_files = []
382 cc_files = []
383 objc_files = []
384 for file_match in FILE_PATH_RE.finditer(sources_match.group(1)):
385 file_path = file_match.group('file_path')
386 extension = file_match.group('extension')
387 if extension == '.c':
388 c_files.append(file_path + extension)
389 if extension == '.cc':
390 cc_files.append(file_path + extension)
391 if extension in ['.m', '.mm']:
392 objc_files.append(file_path + extension)
393 list_of_sources.append((c_files, cc_files, objc_files))
394 for c_files_list, cc_files_list, objc_files_list in list_of_sources:
395 if _MoreThanOneSourceUsed(c_files_list, cc_files_list, objc_files_list):
396 all_sources = sorted(c_files_list + cc_files_list + objc_files_list)
397 errors[gn_file.LocalPath()].append((target_name, all_sources))
398 if errors:
kjellander7439f972016-12-06 06:47:46399 return [output_api.PresubmitError(
Mirko Bonadei4dc4e252017-09-19 11:49:16400 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
401 'Please create a separate target for each collection of sources.\n'
kjellander7439f972016-12-06 06:47:46402 'Mixed sources: \n'
403 '%s\n'
Mirko Bonadei4dc4e252017-09-19 11:49:16404 'Violating GN files:\n%s\n' % (json.dumps(errors, indent=2),
405 '\n'.join(errors.keys())))]
kjellander7439f972016-12-06 06:47:46406 return []
407
Artem Titove92675b2018-05-22 08:21:27408
charujain9893e252017-09-14 11:33:22409def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
ehmaldonado4fb97462017-01-30 13:27:22410 cwd = input_api.PresubmitLocalPath()
Oleh Prypin2f33a562017-10-04 18:17:54411 with _AddToPath(input_api.os_path.join(
412 cwd, 'tools_webrtc', 'presubmit_checks_lib')):
413 from check_package_boundaries import CheckPackageBoundaries
414 build_files = [os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files]
415 errors = CheckPackageBoundaries(cwd, build_files)[:5]
416 if errors:
ehmaldonado4fb97462017-01-30 13:27:22417 return [output_api.PresubmitError(
Oleh Prypin2f33a562017-10-04 18:17:54418 'There are package boundary violations in the following GN files:',
419 long_text='\n\n'.join(str(err) for err in errors))]
ehmaldonado4fb97462017-01-30 13:27:22420 return []
421
Mirko Bonadeia51bbd82018-03-08 15:15:45422
Mirko Bonadeif0e0d752018-07-04 06:48:18423def _ReportFileAndLine(filename, line_num):
Mirko Bonadeia51bbd82018-03-08 15:15:45424 """Default error formatter for _FindNewViolationsOfRule."""
425 return '%s (line %s)' % (filename, line_num)
426
427
Mirko Bonadeif0e0d752018-07-04 06:48:18428def CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api, output_api,
429 error_formatter=_ReportFileAndLine):
430 """Make sure that warning suppression flags are not added wihtout a reason."""
431 msg = ('Usage of //build/config/clang:extra_warnings is discouraged '
432 'in WebRTC.\n'
433 'If you are not adding this code (e.g. you are just moving '
434 'existing code) or you want to add an exception,\n'
435 'you can add a comment on the line that causes the problem:\n\n'
436 '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n'
437 '\n'
438 'Affected files:\n')
439 errors = [] # 2-element tuples with (file, line number)
440 clang_warn_re = input_api.re.compile(r'//build/config/clang:extra_warnings')
441 no_presubmit_re = input_api.re.compile(
442 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
443 for f in gn_files:
444 for line_num, line in f.ChangedContents():
445 if clang_warn_re.search(line) and not no_presubmit_re.search(line):
446 errors.append(error_formatter(f.LocalPath(), line_num))
447 if errors:
448 return [output_api.PresubmitError(msg, errors)]
449 return []
450
Mirko Bonadei9ce800d2019-02-05 15:48:13451
452def CheckNoTestCaseUsageIsAdded(input_api, output_api, source_file_filter,
453 error_formatter=_ReportFileAndLine):
454 error_msg = ('Usage of legacy GoogleTest API detected!\nPlease use the '
455 'new API: https://github.com/google/googletest/blob/master/'
456 'googletest/docs/primer.md#beware-of-the-nomenclature.\n'
457 'Affected files:\n')
458 errors = [] # 2-element tuples with (file, line number)
459 test_case_re = input_api.re.compile(r'TEST_CASE')
460 file_filter = lambda f: (source_file_filter(f)
461 and f.LocalPath().endswith('.cc'))
462 for f in input_api.AffectedSourceFiles(file_filter):
463 for line_num, line in f.ChangedContents():
464 if test_case_re.search(line):
465 errors.append(error_formatter(f.LocalPath(), line_num))
466 if errors:
467 return [output_api.PresubmitError(error_msg, errors)]
468 return []
469
470
Mirko Bonadeia51bbd82018-03-08 15:15:45471def CheckNoStreamUsageIsAdded(input_api, output_api,
Artem Titov739351d2018-05-11 10:21:36472 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 06:48:18473 error_formatter=_ReportFileAndLine):
Mirko Bonadeia51bbd82018-03-08 15:15:45474 """Make sure that no more dependencies on stringstream are added."""
475 error_msg = ('Usage of <sstream>, <istream> and <ostream> in WebRTC is '
476 'deprecated.\n'
477 'This includes the following types:\n'
478 'std::istringstream, std::ostringstream, std::wistringstream, '
479 'std::wostringstream,\n'
480 'std::wstringstream, std::ostream, std::wostream, std::istream,'
481 'std::wistream,\n'
482 'std::iostream, std::wiostream.\n'
483 'If you are not adding this code (e.g. you are just moving '
484 'existing code),\n'
485 'you can add a comment on the line that causes the problem:\n\n'
486 '#include <sstream> // no-presubmit-check TODO(webrtc:8982)\n'
487 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n'
488 '\n'
Karl Wibergebd01e82018-03-14 14:08:39489 'If you are adding new code, consider using '
490 'rtc::SimpleStringBuilder\n'
491 '(in rtc_base/strings/string_builder.h).\n'
Mirko Bonadeia51bbd82018-03-08 15:15:45492 'Affected files:\n')
493 errors = [] # 2-element tuples with (file, line number)
494 include_re = input_api.re.compile(r'#include <(i|o|s)stream>')
495 usage_re = input_api.re.compile(r'std::(w|i|o|io|wi|wo|wio)(string)*stream')
496 no_presubmit_re = input_api.re.compile(
Jonas Olsson74395342018-04-03 10:22:07497 r'// no-presubmit-check TODO\(webrtc:8982\)')
Artem Titova04d1402018-05-11 09:23:00498 file_filter = lambda x: (input_api.FilterSourceFile(x)
499 and source_file_filter(x))
Mirko Bonadei571791a2019-05-07 12:08:05500
501 def _IsException(file_path):
502 is_test = any(file_path.endswith(x) for x in ['_test.cc', '_tests.cc',
503 '_unittest.cc',
504 '_unittests.cc'])
505 return file_path.startswith('examples') or is_test
506
Artem Titova04d1402018-05-11 09:23:00507 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei571791a2019-05-07 12:08:05508 # Usage of stringstream is allowed under examples/ and in tests.
509 if f.LocalPath() == 'PRESUBMIT.py' or _IsException(f.LocalPath()):
Mirko Bonadeid2c83322018-03-19 10:31:47510 continue
511 for line_num, line in f.ChangedContents():
512 if ((include_re.search(line) or usage_re.search(line))
513 and not no_presubmit_re.search(line)):
514 errors.append(error_formatter(f.LocalPath(), line_num))
Mirko Bonadeia51bbd82018-03-08 15:15:45515 if errors:
516 return [output_api.PresubmitError(error_msg, errors)]
517 return []
518
Artem Titove92675b2018-05-22 08:21:27519
Mirko Bonadeia05d47e2018-05-09 09:03:38520def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
521 """Checks that public_deps is not used without a good reason."""
Mirko Bonadei5c1ad592017-12-12 10:52:27522 result = []
Mirko Bonadeia05d47e2018-05-09 09:03:38523 no_presubmit_check_re = input_api.re.compile(
524 r'# no-presubmit-check TODO\(webrtc:8603\)')
525 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
526 'because it doesn\'t map well to downstream build systems.\n'
527 'Used in: %s (line %d).\n'
528 'If you are not adding this code (e.g. you are just moving '
529 'existing code) or you have a good reason, you can add a '
530 'comment on the line that causes the problem:\n\n'
531 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
Mirko Bonadei5c1ad592017-12-12 10:52:27532 for affected_file in gn_files:
533 for (line_number, affected_line) in affected_file.ChangedContents():
Mirko Bonadeia05d47e2018-05-09 09:03:38534 if ('public_deps' in affected_line
535 and not no_presubmit_check_re.search(affected_line)):
Mirko Bonadei5c1ad592017-12-12 10:52:27536 result.append(
537 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
538 line_number)))
539 return result
540
Artem Titove92675b2018-05-22 08:21:27541
Patrik Höglund6f491062018-01-11 11:04:23542def CheckCheckIncludesIsNotUsed(gn_files, output_api):
543 result = []
544 error_msg = ('check_includes overrides are not allowed since it can cause '
545 'incorrect dependencies to form. It effectively means that your '
546 'module can include any .h file without depending on its '
547 'corresponding target. There are some exceptional cases when '
548 'this is allowed: if so, get approval from a .gn owner in the'
549 'root OWNERS file.\n'
550 'Used in: %s (line %d).')
551 for affected_file in gn_files:
552 for (line_number, affected_line) in affected_file.ChangedContents():
553 if 'check_includes' in affected_line:
554 result.append(
555 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
556 line_number)))
557 return result
558
Artem Titove92675b2018-05-22 08:21:27559
Mirko Bonadeif0e0d752018-07-04 06:48:18560def CheckGnChanges(input_api, output_api):
Artem Titova04d1402018-05-11 09:23:00561 file_filter = lambda x: (input_api.FilterSourceFile(
Oleh Prypinafe01652017-10-04 13:56:08562 x, white_list=(r'.+\.(gn|gni)$',),
Mirko Bonadeif0e0d752018-07-04 06:48:18563 black_list=(r'.*/presubmit_checks_lib/testdata/.*',)))
ehmaldonado5b1ba082016-09-02 12:51:08564
565 gn_files = []
Artem Titova04d1402018-05-11 09:23:00566 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei92ea95e2017-09-15 04:47:31567 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 12:51:08568
569 result = []
570 if gn_files:
charujain9893e252017-09-14 11:33:22571 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
Mirko Bonadei4dc4e252017-09-19 11:49:16572 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
573 result.extend(CheckNoPackageBoundaryViolations(input_api, gn_files,
574 output_api))
Mirko Bonadeia05d47e2018-05-09 09:03:38575 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api, output_api))
Patrik Höglund6f491062018-01-11 11:04:23576 result.extend(CheckCheckIncludesIsNotUsed(gn_files, output_api))
Mirko Bonadeif0e0d752018-07-04 06:48:18577 result.extend(CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api,
578 output_api))
ehmaldonado5b1ba082016-09-02 12:51:08579 return result
580
Artem Titove92675b2018-05-22 08:21:27581
Oleh Prypin920b6532017-10-05 09:28:51582def CheckGnGen(input_api, output_api):
583 """Runs `gn gen --check` with default args to detect mismatches between
584 #includes and dependencies in the BUILD.gn files, as well as general build
585 errors.
586 """
587 with _AddToPath(input_api.os_path.join(
588 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
Yves Gerey546ee612019-02-26 16:04:16589 from build_helpers import RunGnCheck
Mirko Bonadeid8665442018-09-04 10:17:27590 errors = RunGnCheck(FindSrcDirPath(input_api.PresubmitLocalPath()))[:5]
Oleh Prypin920b6532017-10-05 09:28:51591 if errors:
592 return [output_api.PresubmitPromptWarning(
593 'Some #includes do not match the build dependency graph. Please run:\n'
594 ' gn gen --check <out_dir>',
595 long_text='\n\n'.join(errors))]
596 return []
597
Artem Titove92675b2018-05-22 08:21:27598
Artem Titova04d1402018-05-11 09:23:00599def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37600 """Runs checkdeps on #include statements added in this
601 change. Breaking - rules is an error, breaking ! rules is a
602 warning.
603 """
604 # Copied from Chromium's src/PRESUBMIT.py.
605
606 # We need to wait until we have an input_api object and use this
607 # roundabout construct to import checkdeps because this file is
608 # eval-ed and thus doesn't have __file__.
Mirko Bonadeid8665442018-09-04 10:17:27609 src_path = FindSrcDirPath(input_api.PresubmitLocalPath())
610 checkdeps_path = input_api.os_path.join(src_path, 'buildtools', 'checkdeps')
Oleh Prypin2f33a562017-10-04 18:17:54611 if not os.path.exists(checkdeps_path):
612 return [output_api.PresubmitError(
613 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
614 'download all the DEPS entries?' % checkdeps_path)]
615 with _AddToPath(checkdeps_path):
kjellander@webrtc.org3bd41562014-09-01 11:06:37616 import checkdeps
617 from cpp_checker import CppChecker
618 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37619
620 added_includes = []
Artem Titova04d1402018-05-11 09:23:00621 for f in input_api.AffectedFiles(file_filter=source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37622 if not CppChecker.IsCppFile(f.LocalPath()):
623 continue
624
Henrik Kjellander57e5fd22015-05-25 10:55:39625 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37626 added_includes.append([f.LocalPath(), changed_lines])
627
628 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
629
630 error_descriptions = []
631 warning_descriptions = []
632 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
633 added_includes):
634 description_with_path = '%s\n %s' % (path, rule_description)
635 if rule_type == Rule.DISALLOW:
636 error_descriptions.append(description_with_path)
637 else:
638 warning_descriptions.append(description_with_path)
639
640 results = []
641 if error_descriptions:
642 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 10:47:05643 'You added one or more #includes that violate checkdeps rules.\n'
644 'Check that the DEPS files in these locations contain valid rules.\n'
645 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
646 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37647 error_descriptions))
648 if warning_descriptions:
649 results.append(output_api.PresubmitPromptOrNotify(
650 'You added one or more #includes of files that are temporarily\n'
651 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 10:47:05652 '#include? See relevant DEPS file(s) for details and contacts.\n'
653 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
654 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37655 warning_descriptions))
656 return results
657
Artem Titove92675b2018-05-22 08:21:27658
charujain9893e252017-09-14 11:33:22659def CheckCommitMessageBugEntry(input_api, output_api):
660 """Check that bug entries are well-formed in commit message."""
661 bogus_bug_msg = (
Mirko Bonadei61880182017-10-12 13:12:35662 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
charujain9893e252017-09-14 11:33:22663 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.')
664 results = []
Mirko Bonadei61880182017-10-12 13:12:35665 for bug in input_api.change.BugsFromDescription():
charujain9893e252017-09-14 11:33:22666 bug = bug.strip()
667 if bug.lower() == 'none':
668 continue
charujain81a58c72017-09-25 11:25:45669 if 'b/' not in bug and ':' not in bug:
charujain9893e252017-09-14 11:33:22670 try:
671 if int(bug) > 100000:
672 # Rough indicator for current chromium bugs.
673 prefix_guess = 'chromium'
674 else:
675 prefix_guess = 'webrtc'
Mirko Bonadei61880182017-10-12 13:12:35676 results.append('Bug entry requires issue tracker prefix, e.g. %s:%s' %
charujain9893e252017-09-14 11:33:22677 (prefix_guess, bug))
678 except ValueError:
679 results.append(bogus_bug_msg % bug)
charujain81a58c72017-09-25 11:25:45680 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
charujain9893e252017-09-14 11:33:22681 results.append(bogus_bug_msg % bug)
682 return [output_api.PresubmitError(r) for r in results]
683
Artem Titove92675b2018-05-22 08:21:27684
charujain9893e252017-09-14 11:33:22685def CheckChangeHasBugField(input_api, output_api):
Mirko Bonadei61880182017-10-12 13:12:35686 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 15:11:16687
688 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 13:12:35689 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 15:11:16690 a bug reference.
Mirko Bonadei61880182017-10-12 13:12:35691
692 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
693 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 15:11:16694 """
Mirko Bonadei61880182017-10-12 13:12:35695 if input_api.change.BugsFromDescription():
kjellanderd1e26a92016-09-19 15:11:16696 return []
697 else:
698 return [output_api.PresubmitError(
Mirko Bonadei61880182017-10-12 13:12:35699 'The "Bug: [bug number]" footer is mandatory. Please create a bug and '
kjellanderd1e26a92016-09-19 15:11:16700 'reference it using either of:\n'
Mirko Bonadei61880182017-10-12 13:12:35701 ' * https://bugs.webrtc.org - reference it using Bug: webrtc:XXXX\n'
702 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18703
Artem Titove92675b2018-05-22 08:21:27704
Artem Titova04d1402018-05-11 09:23:00705def CheckJSONParseErrors(input_api, output_api, source_file_filter):
kjellander569cf942016-02-11 13:02:59706 """Check that JSON files do not contain syntax errors."""
707
708 def FilterFile(affected_file):
Artem Titova04d1402018-05-11 09:23:00709 return (input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
710 and source_file_filter(affected_file))
kjellander569cf942016-02-11 13:02:59711
712 def GetJSONParseError(input_api, filename):
713 try:
714 contents = input_api.ReadFile(filename)
715 input_api.json.loads(contents)
716 except ValueError as e:
717 return e
718 return None
719
720 results = []
721 for affected_file in input_api.AffectedFiles(
722 file_filter=FilterFile, include_deletes=False):
723 parse_error = GetJSONParseError(input_api,
724 affected_file.AbsoluteLocalPath())
725 if parse_error:
726 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
Artem Titove92675b2018-05-22 08:21:27727 (affected_file.LocalPath(),
728 parse_error)))
kjellander569cf942016-02-11 13:02:59729 return results
730
731
charujain9893e252017-09-14 11:33:22732def RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 13:42:43733 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 17:52:05734 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
735
736 test_directories = [
Edward Lemur6d01f6d2017-09-14 15:02:01737 input_api.PresubmitLocalPath(),
Mirko Bonadei92ea95e2017-09-15 04:47:31738 Join('rtc_tools', 'py_event_log_analyzer'),
Mirko Bonadei92ea95e2017-09-15 04:47:31739 Join('audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 13:27:22740 ] + [
Henrik Kjellander90fd7d82017-05-09 06:30:10741 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 13:27:22742 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 17:52:05743 ]
744
745 tests = []
746 for directory in test_directories:
747 tests.extend(
748 input_api.canned_checks.GetUnitTestsInDirectory(
749 input_api,
750 output_api,
751 directory,
752 whitelist=[r'.+_test\.py$']))
753 return input_api.RunTests(tests, parallel=True)
754
755
Artem Titova04d1402018-05-11 09:23:00756def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
757 source_file_filter):
mbonadei38415b22017-04-07 12:38:01758 """Checks that the namespace google::protobuf has not been used."""
759 files = []
760 pattern = input_api.re.compile(r'google::protobuf')
Mirko Bonadei92ea95e2017-09-15 04:47:31761 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
Artem Titova04d1402018-05-11 09:23:00762 file_filter = lambda x: (input_api.FilterSourceFile(x)
763 and source_file_filter(x))
764 for f in input_api.AffectedSourceFiles(file_filter):
mbonadei38415b22017-04-07 12:38:01765 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
766 continue
767 contents = input_api.ReadFile(f)
768 if pattern.search(contents):
769 files.append(f)
770
771 if files:
772 return [output_api.PresubmitError(
773 'Please avoid to use namespace `google::protobuf` directly.\n'
774 'Add a using directive in `%s` and include that header instead.'
775 % proto_utils_path, files)]
776 return []
777
778
Mirko Bonadei92ea95e2017-09-15 04:47:31779def _LicenseHeader(input_api):
780 """Returns the license header regexp."""
781 # Accept any year number from 2003 to the current year
782 current_year = int(input_api.time.strftime('%Y'))
783 allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1)))
784 years_re = '(' + '|'.join(allowed_years) + ')'
785 license_header = (
786 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
787 r'All [Rr]ights [Rr]eserved\.\n'
788 r'.*?\n'
789 r'.*? Use of this source code is governed by a BSD-style license\n'
790 r'.*? that can be found in the LICENSE file in the root of the source\n'
791 r'.*? tree\. An additional intellectual property rights grant can be '
792 r'found\n'
793 r'.*? in the file PATENTS\. All contributing project authors may\n'
794 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
795 ) % {
796 'year': years_re,
797 }
798 return license_header
799
800
charujain9893e252017-09-14 11:33:22801def CommonChecks(input_api, output_api):
andrew@webrtc.org53df1362012-01-26 21:24:23802 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34803 results = []
tkchin42f580e2015-11-27 07:18:23804 # Filter out files that are in objc or ios dirs from being cpplint-ed since
805 # they do not follow C++ lint rules.
806 black_list = input_api.DEFAULT_BLACK_LIST + (
807 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05808 r".*objc\.[hcm]+$",
tkchin42f580e2015-11-27 07:18:23809 )
810 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list)
charujain9893e252017-09-14 11:33:22811 results.extend(CheckApprovedFilesLintClean(
tkchin42f580e2015-11-27 07:18:23812 input_api, output_api, source_file_filter))
Mirko Bonadei92ea95e2017-09-15 04:47:31813 results.extend(input_api.canned_checks.CheckLicense(
814 input_api, output_api, _LicenseHeader(input_api)))
phoglund@webrtc.org5d3713932013-03-07 09:59:43815 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
kjellander@webrtc.org177567c2016-12-22 09:40:28816 black_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 11:10:04817 r'^build[\\\/].*\.py$',
818 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-13 05:43:38819 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 11:01:17820 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 11:10:04821 r'^out.*[\\\/].*\.py$',
822 r'^testing[\\\/].*\.py$',
823 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 09:40:28824 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 20:21:39825 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 06:30:10826 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 11:10:04827 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 10:55:39828 pylintrc='pylintrc'))
kjellander569cf942016-02-11 13:02:59829
nisse3d21e232016-09-02 10:07:06830 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 10:55:39831 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
832 # we need to have different license checks in talk/ and webrtc/ directories.
833 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 06:03:56834
tkchin3cd9a302016-06-08 19:40:28835 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
836 # ObjC subdirectories ObjC headers.
837 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 19:11:29838 # Skip long-lines check for DEPS and GN files.
839 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
Artem Titova04d1402018-05-11 09:23:00840 # Also we will skip most checks for third_party directory.
Artem Titov42f0d782018-06-27 11:23:17841 third_party_filter_list = (r'^third_party[\\\/].+',)
tkchin3cd9a302016-06-08 19:40:28842 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
Artem Titova04d1402018-05-11 09:23:00843 black_list=build_file_filter_list + objc_filter_list +
844 third_party_filter_list)
tkchin3cd9a302016-06-08 19:40:28845 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
846 white_list=objc_filter_list)
Artem Titove92675b2018-05-22 08:21:27847 non_third_party_sources = lambda x: input_api.FilterSourceFile(x,
848 black_list=third_party_filter_list)
849
andrew@webrtc.org2442de12012-01-23 17:45:41850 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 19:40:28851 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
852 results.extend(input_api.canned_checks.CheckLongLines(
853 input_api, output_api, maxlen=100,
854 source_file_filter=hundred_char_sources))
andrew@webrtc.org2442de12012-01-23 17:45:41855 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
Artem Titova04d1402018-05-11 09:23:00856 input_api, output_api, source_file_filter=non_third_party_sources))
andrew@webrtc.org53df1362012-01-26 21:24:23857 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
Artem Titova04d1402018-05-11 09:23:00858 input_api, output_api, source_file_filter=non_third_party_sources))
kjellandere5dc62a2016-12-14 08:16:21859 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
Oleh Prypine0735142018-10-04 09:15:54860 input_api, output_api, bot_whitelist=[
861 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com'
862 ]))
andrew@webrtc.org53df1362012-01-26 21:24:23863 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
Artem Titova04d1402018-05-11 09:23:00864 input_api, output_api, source_file_filter=non_third_party_sources))
Yves Gerey87a93532018-06-20 13:51:49865 results.extend(input_api.canned_checks.CheckPatchFormatted(
866 input_api, output_api))
charujain9893e252017-09-14 11:33:22867 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
Artem Titova04d1402018-05-11 09:23:00868 results.extend(CheckNoIOStreamInHeaders(
869 input_api, output_api, source_file_filter=non_third_party_sources))
870 results.extend(CheckNoPragmaOnce(
871 input_api, output_api, source_file_filter=non_third_party_sources))
872 results.extend(CheckNoFRIEND_TEST(
873 input_api, output_api, source_file_filter=non_third_party_sources))
Mirko Bonadeif0e0d752018-07-04 06:48:18874 results.extend(CheckGnChanges(input_api, output_api))
Artem Titova04d1402018-05-11 09:23:00875 results.extend(CheckUnwantedDependencies(
876 input_api, output_api, source_file_filter=non_third_party_sources))
877 results.extend(CheckJSONParseErrors(
878 input_api, output_api, source_file_filter=non_third_party_sources))
charujain9893e252017-09-14 11:33:22879 results.extend(RunPythonTests(input_api, output_api))
Artem Titova04d1402018-05-11 09:23:00880 results.extend(CheckUsageOfGoogleProtobufNamespace(
881 input_api, output_api, source_file_filter=non_third_party_sources))
882 results.extend(CheckOrphanHeaders(
883 input_api, output_api, source_file_filter=non_third_party_sources))
884 results.extend(CheckNewlineAtTheEndOfProtoFiles(
885 input_api, output_api, source_file_filter=non_third_party_sources))
886 results.extend(CheckNoStreamUsageIsAdded(
Artem Titov739351d2018-05-11 10:21:36887 input_api, output_api, non_third_party_sources))
Mirko Bonadei9ce800d2019-02-05 15:48:13888 results.extend(CheckNoTestCaseUsageIsAdded(
889 input_api, output_api, non_third_party_sources))
Mirko Bonadei7e4ee6e2018-09-28 09:45:23890 results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api))
Mirko Bonadeia418e672018-10-24 11:57:25891 results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api))
tzika06bf852018-11-15 11:37:35892 results.extend(CheckAbslMemoryInclude(
893 input_api, output_api, non_third_party_sources))
Mirko Bonadei9fa8ef12019-09-17 17:14:13894 results.extend(CheckBannedAbslMakeUnique(
895 input_api, output_api, non_third_party_sources))
Mirko Bonadeia418e672018-10-24 11:57:25896 return results
897
898
899def CheckApiDepsFileIsUpToDate(input_api, output_api):
Mirko Bonadei90490372018-10-26 11:17:47900 """Check that 'include_rules' in api/DEPS is up to date.
901
902 The file api/DEPS must be kept up to date in order to avoid to avoid to
903 include internal header from WebRTC's api/ headers.
904
905 This check is focused on ensuring that 'include_rules' contains a deny
906 rule for each root level directory. More focused allow rules can be
907 added to 'specific_include_rules'.
908 """
Mirko Bonadeia418e672018-10-24 11:57:25909 results = []
910 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
911 with open(api_deps) as f:
912 deps_content = _ParseDeps(f.read())
913
914 include_rules = deps_content.get('include_rules', [])
Mirko Bonadei01e97ae2019-09-05 12:36:42915 dirs_to_skip = set(['api', 'docs'])
Mirko Bonadeia418e672018-10-24 11:57:25916
Mirko Bonadei90490372018-10-26 11:17:47917 # Only check top level directories affected by the current CL.
918 dirs_to_check = set()
919 for f in input_api.AffectedFiles():
920 path_tokens = [t for t in f.LocalPath().split(os.sep) if t]
921 if len(path_tokens) > 1:
Mirko Bonadei01e97ae2019-09-05 12:36:42922 if (path_tokens[0] not in dirs_to_skip and
Mirko Bonadei90490372018-10-26 11:17:47923 os.path.isdir(os.path.join(input_api.PresubmitLocalPath(),
924 path_tokens[0]))):
925 dirs_to_check.add(path_tokens[0])
Mirko Bonadeia418e672018-10-24 11:57:25926
Mirko Bonadei90490372018-10-26 11:17:47927 missing_include_rules = set()
928 for p in dirs_to_check:
Mirko Bonadeia418e672018-10-24 11:57:25929 rule = '-%s' % p
930 if rule not in include_rules:
Mirko Bonadei90490372018-10-26 11:17:47931 missing_include_rules.add(rule)
932
Mirko Bonadeia418e672018-10-24 11:57:25933 if missing_include_rules:
Mirko Bonadei90490372018-10-26 11:17:47934 error_msg = [
935 'include_rules = [\n',
936 ' ...\n',
937 ]
Mirko Bonadeia418e672018-10-24 11:57:25938
Mirko Bonadei90490372018-10-26 11:17:47939 for r in sorted(missing_include_rules):
940 error_msg.append(' "%s",\n' % str(r))
Mirko Bonadeia418e672018-10-24 11:57:25941
Mirko Bonadei90490372018-10-26 11:17:47942 error_msg.append(' ...\n')
943 error_msg.append(']\n')
944
Mirko Bonadeia418e672018-10-24 11:57:25945 results.append(output_api.PresubmitError(
Mirko Bonadei90490372018-10-26 11:17:47946 'New root level directory detected! WebRTC api/ headers should '
947 'not #include headers from \n'
948 'the new directory, so please update "include_rules" in file\n'
949 '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg))))
950
andrew@webrtc.org53df1362012-01-26 21:24:23951 return results
andrew@webrtc.org2442de12012-01-23 17:45:41952
Mirko Bonadei9fa8ef12019-09-17 17:14:13953def CheckBannedAbslMakeUnique(input_api, output_api, source_file_filter):
954 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
955 and source_file_filter(f))
956
957 files = []
958 for f in input_api.AffectedFiles(
959 include_deletes=False, file_filter=file_filter):
960 for _, line in f.ChangedContents():
961 if 'absl::make_unique' in line:
962 files.append(f)
963 break
964
965 if len(files):
966 return [output_api.PresubmitError(
967 'Please use std::make_unique instead of absl::make_unique.\n'
968 'Affected files:',
969 files)]
970 return []
971
tzika06bf852018-11-15 11:37:35972def CheckAbslMemoryInclude(input_api, output_api, source_file_filter):
973 pattern = input_api.re.compile(
974 r'^#include\s*"absl/memory/memory.h"', input_api.re.MULTILINE)
975 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
976 and source_file_filter(f))
977
978 files = []
979 for f in input_api.AffectedFiles(
980 include_deletes=False, file_filter=file_filter):
981 contents = input_api.ReadFile(f)
982 if pattern.search(contents):
983 continue
984 for _, line in f.ChangedContents():
Mirko Bonadei9fa8ef12019-09-17 17:14:13985 if 'absl::WrapUnique' in line:
tzika06bf852018-11-15 11:37:35986 files.append(f)
987 break
988
989 if len(files):
990 return [output_api.PresubmitError(
Mirko Bonadei9fa8ef12019-09-17 17:14:13991 'Please include "absl/memory/memory.h" header for absl::WrapUnique.\n'
992 'This header may or may not be included transitively depending on the '
993 'C++ standard version.',
tzika06bf852018-11-15 11:37:35994 files)]
995 return []
kjellander@webrtc.orge4158642014-08-06 09:11:18996
andrew@webrtc.org53df1362012-01-26 21:24:23997def CheckChangeOnUpload(input_api, output_api):
998 results = []
charujain9893e252017-09-14 11:33:22999 results.extend(CommonChecks(input_api, output_api))
Oleh Prypin920b6532017-10-05 09:28:511000 results.extend(CheckGnGen(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 10:55:391001 results.extend(
1002 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:341003 return results
1004
kjellander@webrtc.orge4158642014-08-06 09:11:181005
andrew@webrtc.org2442de12012-01-23 17:45:411006def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:241007 results = []
charujain9893e252017-09-14 11:33:221008 results.extend(CommonChecks(input_api, output_api))
1009 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
Artem Titov42f0d782018-06-27 11:23:171010 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:231011 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
1012 input_api, output_api))
1013 results.extend(input_api.canned_checks.CheckChangeHasDescription(
1014 input_api, output_api))
charujain9893e252017-09-14 11:33:221015 results.extend(CheckChangeHasBugField(input_api, output_api))
1016 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:431017 results.extend(input_api.canned_checks.CheckTreeIsOpen(
1018 input_api, output_api,
1019 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:241020 return results
mbonadei74973ed2017-05-09 14:58:051021
1022
Artem Titova04d1402018-05-11 09:23:001023def CheckOrphanHeaders(input_api, output_api, source_file_filter):
mbonadei74973ed2017-05-09 14:58:051024 # We need to wait until we have an input_api object and use this
1025 # roundabout construct to import prebubmit_checks_lib because this file is
1026 # eval-ed and thus doesn't have __file__.
Patrik Höglund2f3f7222017-12-19 10:08:561027 error_msg = """{} should be listed in {}."""
mbonadei74973ed2017-05-09 14:58:051028 results = []
Patrik Höglund7e60de22018-01-09 13:22:001029 orphan_blacklist = [
1030 os.path.join('tools_webrtc', 'ios', 'SDK'),
1031 ]
Oleh Prypin2f33a562017-10-04 18:17:541032 with _AddToPath(input_api.os_path.join(
1033 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
mbonadei74973ed2017-05-09 14:58:051034 from check_orphan_headers import GetBuildGnPathFromFilePath
1035 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 14:58:051036
Artem Titova04d1402018-05-11 09:23:001037 file_filter = lambda x: input_api.FilterSourceFile(
1038 x, black_list=orphan_blacklist) and source_file_filter(x)
1039 for f in input_api.AffectedSourceFiles(file_filter):
Patrik Höglund7e60de22018-01-09 13:22:001040 if f.LocalPath().endswith('.h'):
mbonadei74973ed2017-05-09 14:58:051041 file_path = os.path.abspath(f.LocalPath())
1042 root_dir = os.getcwd()
1043 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
1044 root_dir)
1045 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
1046 if not in_build_gn:
1047 results.append(output_api.PresubmitError(error_msg.format(
Patrik Höglund2f3f7222017-12-19 10:08:561048 f.LocalPath(), os.path.relpath(gn_file_path))))
mbonadei74973ed2017-05-09 14:58:051049 return results
Mirko Bonadei960fd5b2017-06-29 12:59:361050
1051
Artem Titove92675b2018-05-22 08:21:271052def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api, source_file_filter):
Mirko Bonadei960fd5b2017-06-29 12:59:361053 """Checks that all .proto files are terminated with a newline."""
1054 error_msg = 'File {} must end with exactly one newline.'
1055 results = []
Artem Titova04d1402018-05-11 09:23:001056 file_filter = lambda x: input_api.FilterSourceFile(
1057 x, white_list=(r'.+\.proto$',)) and source_file_filter(x)
1058 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei960fd5b2017-06-29 12:59:361059 file_path = f.LocalPath()
1060 with open(file_path) as f:
1061 lines = f.readlines()
Mirko Bonadeia730c1c2017-09-18 09:33:131062 if len(lines) > 0 and not lines[-1].endswith('\n'):
Mirko Bonadei960fd5b2017-06-29 12:59:361063 results.append(output_api.PresubmitError(error_msg.format(file_path)))
1064 return results
Mirko Bonadei7e4ee6e2018-09-28 09:45:231065
1066
1067def _ExtractAddRulesFromParsedDeps(parsed_deps):
1068 """Extract the rules that add dependencies from a parsed DEPS file.
1069
1070 Args:
1071 parsed_deps: the locals dictionary from evaluating the DEPS file."""
1072 add_rules = set()
1073 add_rules.update([
1074 rule[1:] for rule in parsed_deps.get('include_rules', [])
1075 if rule.startswith('+') or rule.startswith('!')
1076 ])
1077 for _, rules in parsed_deps.get('specific_include_rules',
1078 {}).iteritems():
1079 add_rules.update([
1080 rule[1:] for rule in rules
1081 if rule.startswith('+') or rule.startswith('!')
1082 ])
1083 return add_rules
1084
1085
1086def _ParseDeps(contents):
1087 """Simple helper for parsing DEPS files."""
1088 # Stubs for handling special syntax in the root DEPS file.
1089 class VarImpl(object):
1090
1091 def __init__(self, local_scope):
1092 self._local_scope = local_scope
1093
1094 def Lookup(self, var_name):
1095 """Implements the Var syntax."""
1096 try:
1097 return self._local_scope['vars'][var_name]
1098 except KeyError:
1099 raise Exception('Var is not defined: %s' % var_name)
1100
1101 local_scope = {}
1102 global_scope = {
1103 'Var': VarImpl(local_scope).Lookup,
1104 }
1105 exec contents in global_scope, local_scope
1106 return local_scope
1107
1108
1109def _CalculateAddedDeps(os_path, old_contents, new_contents):
1110 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
1111 a set of DEPS entries that we should look up.
1112
1113 For a directory (rather than a specific filename) we fake a path to
1114 a specific filename by adding /DEPS. This is chosen as a file that
1115 will seldom or never be subject to per-file include_rules.
1116 """
1117 # We ignore deps entries on auto-generated directories.
1118 auto_generated_dirs = ['grit', 'jni']
1119
1120 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1121 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
1122
1123 added_deps = new_deps.difference(old_deps)
1124
1125 results = set()
1126 for added_dep in added_deps:
1127 if added_dep.split('/')[0] in auto_generated_dirs:
1128 continue
1129 # Assume that a rule that ends in .h is a rule for a specific file.
1130 if added_dep.endswith('.h'):
1131 results.add(added_dep)
1132 else:
1133 results.add(os_path.join(added_dep, 'DEPS'))
1134 return results
1135
1136
1137def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
1138 """When a dependency prefixed with + is added to a DEPS file, we
1139 want to make sure that the change is reviewed by an OWNER of the
1140 target file or directory, to avoid layering violations from being
1141 introduced. This check verifies that this happens.
1142 """
1143 virtual_depended_on_files = set()
1144
1145 file_filter = lambda f: not input_api.re.match(
1146 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1147 for f in input_api.AffectedFiles(include_deletes=False,
1148 file_filter=file_filter):
1149 filename = input_api.os_path.basename(f.LocalPath())
1150 if filename == 'DEPS':
1151 virtual_depended_on_files.update(_CalculateAddedDeps(
1152 input_api.os_path,
1153 '\n'.join(f.OldContents()),
1154 '\n'.join(f.NewContents())))
1155
1156 if not virtual_depended_on_files:
1157 return []
1158
1159 if input_api.is_committing:
1160 if input_api.tbr:
1161 return [output_api.PresubmitNotifyResult(
1162 '--tbr was specified, skipping OWNERS check for DEPS additions')]
1163 if input_api.dry_run:
1164 return [output_api.PresubmitNotifyResult(
1165 'This is a dry run, skipping OWNERS check for DEPS additions')]
1166 if not input_api.change.issue:
1167 return [output_api.PresubmitError(
1168 "DEPS approval by OWNERS check failed: this change has "
1169 "no change number, so we can't check it for approvals.")]
1170 output = output_api.PresubmitError
1171 else:
1172 output = output_api.PresubmitNotifyResult
1173
1174 owners_db = input_api.owners_db
1175 owner_email, reviewers = (
1176 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1177 input_api,
1178 owners_db.email_regexp,
1179 approval_needed=input_api.is_committing))
1180
1181 owner_email = owner_email or input_api.change.author_email
1182
1183 reviewers_plus_owner = set(reviewers)
1184 if owner_email:
1185 reviewers_plus_owner.add(owner_email)
1186 missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
1187 reviewers_plus_owner)
1188
1189 # We strip the /DEPS part that was added by
1190 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1191 # directory.
1192 def StripDeps(path):
1193 start_deps = path.rfind('/DEPS')
1194 if start_deps != -1:
1195 return path[:start_deps]
1196 else:
1197 return path
1198 unapproved_dependencies = ["'+%s'," % StripDeps(path)
1199 for path in missing_files]
1200
1201 if unapproved_dependencies:
1202 output_list = [
1203 output('You need LGTM from owners of depends-on paths in DEPS that were '
1204 'modified in this CL:\n %s' %
1205 '\n '.join(sorted(unapproved_dependencies)))]
1206 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
1207 output_list.append(output(
1208 'Suggested missing target path OWNERS:\n %s' %
1209 '\n '.join(suggested_owners or [])))
1210 return output_list
1211
1212 return []