blob: 8c75942ccf23c96d48b8f5b17f6bc56aa0c7bd05 [file] [log] [blame]
andrew@webrtc.org2442de12012-01-23 17:45:41 +00001# 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:34 +00008
kjellander7439f972016-12-05 22:47:46 -08009import json
kjellander@webrtc.orgaefe61a2014-12-08 13:00:30 +000010import os
kjellander@webrtc.org85759802013-10-22 16:47:40 +000011import re
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +000012import sys
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020013from collections import defaultdict
Oleh Prypin2f33a562017-10-04 20:17:54 +020014from contextlib import contextmanager
kjellander@webrtc.org85759802013-10-22 16:47:40 +000015
oprypin2aa463f2017-03-23 03:17:02 -070016# Files and directories that are *skipped* by cpplint in the presubmit script.
17CPPLINT_BLACKLIST = [
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018 'api/video_codecs/video_decoder.h',
19 'common_types.cc',
20 'common_types.h',
21 'examples/objc',
Amit Hilbuchce7032b2019-01-22 16:19:35 -080022 'media/base/stream_params.h',
23 'media/base/video_common.h',
24 'media/sctp/sctp_transport.cc',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020025 'modules/audio_coding',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020026 'modules/audio_device',
27 'modules/audio_processing',
28 'modules/desktop_capture',
29 'modules/include/module_common_types.h',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020030 'modules/utility',
31 'modules/video_capture',
Amit Hilbuchce7032b2019-01-22 16:19:35 -080032 'p2p/base/pseudo_tcp.cc',
33 'p2p/base/pseudo_tcp.h',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020034 'rtc_base',
35 'sdk/android/src/jni',
36 'sdk/objc',
37 'system_wrappers',
38 'test',
Henrik Kjellander90fd7d82017-05-09 08:30:10 +020039 'tools_webrtc',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020040 'voice_engine',
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +010041]
42
jbauchc4e3ead2016-02-19 00:25:55 -080043# 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 02:32:12 -070050# - whitespace/operators: Same as above (doesn't seem sufficient to eliminate
51# all move-related errors).
jbauchc4e3ead2016-02-19 00:25:55 -080052BLACKLIST_LINT_FILTERS = [
53 '-build/c++11',
kjellandere5a87a52016-04-27 02:32:12 -070054 '-whitespace/operators',
jbauchc4e3ead2016-02-19 00:25:55 -080055]
56
kjellanderfd595232015-12-04 02:44:09 -080057# 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-02 23:56:14 -080064NATIVE_API_DIRS = (
Karl Wibergef52d8b82017-10-25 13:20:03 +020065 'api', # All subdirectories of api/ are included as well.
Mirko Bonadeia4eeeff2018-01-11 13:16:52 +010066 'media/base',
67 'media/engine',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020068 'modules/audio_device/include',
69 'pc',
kjellanderdd705472016-06-09 11:17:27 -070070)
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020071
kjellanderdd705472016-06-09 11:17:27 -070072# 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 06:47:31 +020075 'common_audio/include',
76 'modules/audio_coding/include',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020077 'modules/audio_processing/include',
78 'modules/bitrate_controller/include',
79 'modules/congestion_controller/include',
80 'modules/include',
81 'modules/remote_bitrate_estimator/include',
82 'modules/rtp_rtcp/include',
83 'modules/rtp_rtcp/source',
84 'modules/utility/include',
85 'modules/video_coding/codecs/h264/include',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020086 'modules/video_coding/codecs/vp8/include',
87 'modules/video_coding/codecs/vp9/include',
88 'modules/video_coding/include',
89 'rtc_base',
90 'system_wrappers/include',
kjellander53047c92015-12-02 23:56:14 -080091)
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020092
Karl Wibergd4f01c12017-11-10 10:55:45 +010093# NOTE: The set of directories in API_DIRS should be the same as those
94# listed in the table in native-api.md.
kjellanderdd705472016-06-09 11:17:27 -070095API_DIRS = NATIVE_API_DIRS[:] + LEGACY_API_DIRS[:]
kjellander53047c92015-12-02 23:56:14 -080096
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020097# TARGET_RE matches a GN target, and extracts the target name and the contents.
98TARGET_RE = re.compile(r'(?P<indent>\s*)\w+\("(?P<target_name>\w+)"\) {'
99 r'(?P<target_contents>.*?)'
100 r'(?P=indent)}',
101 re.MULTILINE | re.DOTALL)
102
103# SOURCES_RE matches a block of sources inside a GN target.
104SOURCES_RE = re.compile(r'sources \+?= \[(?P<sources>.*?)\]',
105 re.MULTILINE | re.DOTALL)
106
107# FILE_PATH_RE matchies a file path.
108FILE_PATH_RE = re.compile(r'"(?P<file_path>(\w|\/)+)(?P<extension>\.\w+)"')
109
kjellander53047c92015-12-02 23:56:14 -0800110
Mirko Bonadeid8665442018-09-04 12:17:27 +0200111def FindSrcDirPath(starting_dir):
112 """Returns the abs path to the src/ dir of the project."""
113 src_dir = starting_dir
114 while os.path.basename(src_dir) != 'src':
115 src_dir = os.path.normpath(os.path.join(src_dir, os.pardir))
116 return src_dir
117
118
Oleh Prypin2f33a562017-10-04 20:17:54 +0200119@contextmanager
120def _AddToPath(*paths):
121 original_sys_path = sys.path
122 sys.path.extend(paths)
123 try:
124 yield
125 finally:
126 # Restore sys.path to what it was before.
127 sys.path = original_sys_path
ehmaldonado4fb97462017-01-30 05:27:22 -0800128
129
charujain9893e252017-09-14 13:33:22 +0200130def VerifyNativeApiHeadersListIsValid(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800131 """Ensures the list of native API header directories is up to date."""
132 non_existing_paths = []
133 native_api_full_paths = [
134 input_api.os_path.join(input_api.PresubmitLocalPath(),
kjellanderdd705472016-06-09 11:17:27 -0700135 *path.split('/')) for path in API_DIRS]
kjellander53047c92015-12-02 23:56:14 -0800136 for path in native_api_full_paths:
137 if not os.path.isdir(path):
138 non_existing_paths.append(path)
139 if non_existing_paths:
140 return [output_api.PresubmitError(
141 'Directories to native API headers have changed which has made the '
142 'list in PRESUBMIT.py outdated.\nPlease update it to the current '
143 'location of our native APIs.',
144 non_existing_paths)]
145 return []
146
Artem Titove92675b2018-05-22 10:21:27 +0200147
kjellanderc88b5d52017-04-05 06:42:43 -0700148API_CHANGE_MSG = """
kwibergeb133022016-04-07 07:41:48 -0700149You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 04:13:23 -0800150 1. Make compatible changes that don't break existing clients. Usually
151 this is done by keeping the existing method signatures unchanged.
152 2. Mark the old stuff as deprecated (see RTC_DEPRECATED macro).
kwibergeb133022016-04-07 07:41:48 -0700153 3. Create a timeline and plan for when the deprecated stuff will be
154 removed. (The amount of time we give users to change their code
155 should be informed by how much work it is for them. If they just
156 need to replace one name with another or something equally
157 simple, 1-2 weeks might be good; if they need to do serious work,
158 up to 3 months may be called for.)
159 4. Update/inform existing downstream code owners to stop using the
160 deprecated stuff. (Send announcements to
161 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
162 5. Remove the deprecated stuff, once the agreed-upon amount of time
163 has passed.
164Related files:
165"""
kjellander53047c92015-12-02 23:56:14 -0800166
Artem Titove92675b2018-05-22 10:21:27 +0200167
charujain9893e252017-09-14 13:33:22 +0200168def CheckNativeApiHeaderChanges(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800169 """Checks to remind proper changing of native APIs."""
170 files = []
Karl Wiberg6bfac032017-10-27 15:14:20 +0200171 source_file_filter = lambda x: input_api.FilterSourceFile(
172 x, white_list=[r'.+\.(gn|gni|h)$'])
173 for f in input_api.AffectedSourceFiles(source_file_filter):
174 for path in API_DIRS:
175 dn = os.path.dirname(f.LocalPath())
176 if path == 'api':
177 # Special case: Subdirectories included.
178 if dn == 'api' or dn.startswith('api/'):
179 files.append(f)
180 else:
181 # Normal case: Subdirectories not included.
182 if dn == path:
183 files.append(f)
kjellander53047c92015-12-02 23:56:14 -0800184
185 if files:
kjellanderc88b5d52017-04-05 06:42:43 -0700186 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
kjellander53047c92015-12-02 23:56:14 -0800187 return []
188
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100189
Artem Titova04d1402018-05-11 11:23:00 +0200190def CheckNoIOStreamInHeaders(input_api, output_api,
191 source_file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000192 """Checks to make sure no .h files include <iostream>."""
193 files = []
194 pattern = input_api.re.compile(r'^#include\s*<iostream>',
195 input_api.re.MULTILINE)
Artem Titova04d1402018-05-11 11:23:00 +0200196 file_filter = lambda x: (input_api.FilterSourceFile(x)
197 and source_file_filter(x))
198 for f in input_api.AffectedSourceFiles(file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000199 if not f.LocalPath().endswith('.h'):
200 continue
201 contents = input_api.ReadFile(f)
202 if pattern.search(contents):
203 files.append(f)
204
205 if len(files):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200206 return [output_api.PresubmitError(
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000207 'Do not #include <iostream> in header files, since it inserts static ' +
208 'initialization into every file including the header. Instead, ' +
209 '#include <ostream>. See http://crbug.com/94794',
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200210 files)]
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000211 return []
212
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000213
Artem Titova04d1402018-05-11 11:23:00 +0200214def CheckNoPragmaOnce(input_api, output_api,
215 source_file_filter):
kjellander6aeef742017-02-20 01:13:18 -0800216 """Make sure that banned functions are not used."""
217 files = []
218 pattern = input_api.re.compile(r'^#pragma\s+once',
219 input_api.re.MULTILINE)
Artem Titova04d1402018-05-11 11:23:00 +0200220 file_filter = lambda x: (input_api.FilterSourceFile(x)
221 and source_file_filter(x))
222 for f in input_api.AffectedSourceFiles(file_filter):
kjellander6aeef742017-02-20 01:13:18 -0800223 if not f.LocalPath().endswith('.h'):
224 continue
225 contents = input_api.ReadFile(f)
226 if pattern.search(contents):
227 files.append(f)
228
229 if files:
230 return [output_api.PresubmitError(
231 'Do not use #pragma once in header files.\n'
232 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
233 files)]
234 return []
235
236
Artem Titova04d1402018-05-11 11:23:00 +0200237def CheckNoFRIEND_TEST(input_api, output_api, # pylint: disable=invalid-name
238 source_file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000239 """Make sure that gtest's FRIEND_TEST() macro is not used, the
240 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
241 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
242 problems = []
243
Artem Titova04d1402018-05-11 11:23:00 +0200244 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
245 and source_file_filter(f))
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000246 for f in input_api.AffectedFiles(file_filter=file_filter):
247 for line_num, line in f.ChangedContents():
248 if 'FRIEND_TEST(' in line:
249 problems.append(' %s:%d' % (f.LocalPath(), line_num))
250
251 if not problems:
252 return []
253 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use '
254 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
255 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
256
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000257
charujain9893e252017-09-14 13:33:22 +0200258def IsLintBlacklisted(blacklist_paths, file_path):
oprypin2aa463f2017-03-23 03:17:02 -0700259 """ Checks if a file is blacklisted for lint check."""
260 for path in blacklist_paths:
261 if file_path == path or os.path.dirname(file_path).startswith(path):
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100262 return True
263 return False
264
265
charujain9893e252017-09-14 13:33:22 +0200266def CheckApprovedFilesLintClean(input_api, output_api,
Artem Titova04d1402018-05-11 11:23:00 +0200267 source_file_filter=None):
oprypin2aa463f2017-03-23 03:17:02 -0700268 """Checks that all new or non-blacklisted .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 13:33:22 +0200269 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000270 depot_tools/presubmit_canned_checks.py but has less filters and only checks
271 added files."""
272 result = []
273
274 # Initialize cpplint.
275 import cpplint
276 # Access to a protected member _XX of a client class
277 # pylint: disable=W0212
278 cpplint._cpplint_state.ResetErrorCounts()
279
jbauchc4e3ead2016-02-19 00:25:55 -0800280 lint_filters = cpplint._Filters()
281 lint_filters.extend(BLACKLIST_LINT_FILTERS)
282 cpplint._SetFilters(','.join(lint_filters))
283
oprypin2aa463f2017-03-23 03:17:02 -0700284 # Create a platform independent blacklist for cpplint.
285 blacklist_paths = [input_api.os_path.join(*path.split('/'))
286 for path in CPPLINT_BLACKLIST]
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100287
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000288 # Use the strictest verbosity level for cpplint.py (level 1) which is the
oprypin2aa463f2017-03-23 03:17:02 -0700289 # default when running cpplint.py from command line. To make it possible to
290 # work with not-yet-converted code, we're only applying it to new (or
291 # moved/renamed) files and files not listed in CPPLINT_BLACKLIST.
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000292 verbosity_level = 1
293 files = []
294 for f in input_api.AffectedSourceFiles(source_file_filter):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200295 # Note that moved/renamed files also count as added.
charujain9893e252017-09-14 13:33:22 +0200296 if f.Action() == 'A' or not IsLintBlacklisted(blacklist_paths,
Artem Titove92675b2018-05-22 10:21:27 +0200297 f.LocalPath()):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000298 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000299
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000300 for file_name in files:
301 cpplint.ProcessFile(file_name, verbosity_level)
302
303 if cpplint._cpplint_state.error_count > 0:
304 if input_api.is_committing:
oprypin8e58d652017-03-21 07:52:41 -0700305 res_type = output_api.PresubmitError
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000306 else:
307 res_type = output_api.PresubmitPromptWarning
308 result = [res_type('Changelist failed cpplint.py check.')]
309
310 return result
311
Artem Titove92675b2018-05-22 10:21:27 +0200312
charujain9893e252017-09-14 13:33:22 +0200313def CheckNoSourcesAbove(input_api, gn_files, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700314 # Disallow referencing source files with paths above the GN file location.
315 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
316 re.MULTILINE | re.DOTALL)
317 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
318 violating_gn_files = set()
319 violating_source_entries = []
320 for gn_file in gn_files:
321 contents = input_api.ReadFile(gn_file)
322 for source_block_match in source_pattern.finditer(contents):
323 # Find all source list entries starting with ../ in the source block
324 # (exclude overrides entries).
325 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
326 source_file = file_list_match.group(1)
327 if 'overrides/' not in source_file:
328 violating_source_entries.append(source_file)
329 violating_gn_files.add(gn_file)
330 if violating_gn_files:
331 return [output_api.PresubmitError(
332 'Referencing source files above the directory of the GN file is not '
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100333 'allowed. Please introduce new GN targets in the proper location '
334 'instead.\n'
ehmaldonado5b1ba082016-09-02 05:51:08 -0700335 'Invalid source entries:\n'
336 '%s\n'
337 'Violating GN files:' % '\n'.join(violating_source_entries),
338 items=violating_gn_files)]
339 return []
340
Artem Titove92675b2018-05-22 10:21:27 +0200341
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200342def CheckNoMixingSources(input_api, gn_files, output_api):
343 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
344
345 See bugs.webrtc.org/7743 for more context.
346 """
Artem Titove92675b2018-05-22 10:21:27 +0200347
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200348 def _MoreThanOneSourceUsed(*sources_lists):
349 sources_used = 0
350 for source_list in sources_lists:
351 if len(source_list):
352 sources_used += 1
353 return sources_used > 1
354
355 errors = defaultdict(lambda: [])
kjellander7439f972016-12-05 22:47:46 -0800356 for gn_file in gn_files:
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200357 gn_file_content = input_api.ReadFile(gn_file)
358 for target_match in TARGET_RE.finditer(gn_file_content):
359 # list_of_sources is a list of tuples of the form
360 # (c_files, cc_files, objc_files) that keeps track of all the sources
361 # defined in a target. A GN target can have more that on definition of
362 # sources (since it supports if/else statements).
363 # E.g.:
364 # rtc_static_library("foo") {
365 # if (is_win) {
366 # sources = [ "foo.cc" ]
367 # } else {
368 # sources = [ "foo.mm" ]
369 # }
370 # }
371 # This is allowed and the presubmit check should support this case.
372 list_of_sources = []
kjellander7439f972016-12-05 22:47:46 -0800373 c_files = []
374 cc_files = []
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200375 objc_files = []
376 target_name = target_match.group('target_name')
377 target_contents = target_match.group('target_contents')
378 for sources_match in SOURCES_RE.finditer(target_contents):
379 if '+=' not in sources_match.group(0):
380 if c_files or cc_files or objc_files:
381 list_of_sources.append((c_files, cc_files, objc_files))
382 c_files = []
383 cc_files = []
384 objc_files = []
385 for file_match in FILE_PATH_RE.finditer(sources_match.group(1)):
386 file_path = file_match.group('file_path')
387 extension = file_match.group('extension')
388 if extension == '.c':
389 c_files.append(file_path + extension)
390 if extension == '.cc':
391 cc_files.append(file_path + extension)
392 if extension in ['.m', '.mm']:
393 objc_files.append(file_path + extension)
394 list_of_sources.append((c_files, cc_files, objc_files))
395 for c_files_list, cc_files_list, objc_files_list in list_of_sources:
396 if _MoreThanOneSourceUsed(c_files_list, cc_files_list, objc_files_list):
397 all_sources = sorted(c_files_list + cc_files_list + objc_files_list)
398 errors[gn_file.LocalPath()].append((target_name, all_sources))
399 if errors:
kjellander7439f972016-12-05 22:47:46 -0800400 return [output_api.PresubmitError(
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200401 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
402 'Please create a separate target for each collection of sources.\n'
kjellander7439f972016-12-05 22:47:46 -0800403 'Mixed sources: \n'
404 '%s\n'
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200405 'Violating GN files:\n%s\n' % (json.dumps(errors, indent=2),
406 '\n'.join(errors.keys())))]
kjellander7439f972016-12-05 22:47:46 -0800407 return []
408
Artem Titove92675b2018-05-22 10:21:27 +0200409
charujain9893e252017-09-14 13:33:22 +0200410def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
ehmaldonado4fb97462017-01-30 05:27:22 -0800411 cwd = input_api.PresubmitLocalPath()
Oleh Prypin2f33a562017-10-04 20:17:54 +0200412 with _AddToPath(input_api.os_path.join(
413 cwd, 'tools_webrtc', 'presubmit_checks_lib')):
414 from check_package_boundaries import CheckPackageBoundaries
415 build_files = [os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files]
416 errors = CheckPackageBoundaries(cwd, build_files)[:5]
417 if errors:
ehmaldonado4fb97462017-01-30 05:27:22 -0800418 return [output_api.PresubmitError(
Oleh Prypin2f33a562017-10-04 20:17:54 +0200419 'There are package boundary violations in the following GN files:',
420 long_text='\n\n'.join(str(err) for err in errors))]
ehmaldonado4fb97462017-01-30 05:27:22 -0800421 return []
422
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100423
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200424def _ReportFileAndLine(filename, line_num):
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100425 """Default error formatter for _FindNewViolationsOfRule."""
426 return '%s (line %s)' % (filename, line_num)
427
428
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200429def CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api, output_api,
430 error_formatter=_ReportFileAndLine):
431 """Make sure that warning suppression flags are not added wihtout a reason."""
432 msg = ('Usage of //build/config/clang:extra_warnings is discouraged '
433 'in WebRTC.\n'
434 'If you are not adding this code (e.g. you are just moving '
435 'existing code) or you want to add an exception,\n'
436 'you can add a comment on the line that causes the problem:\n\n'
437 '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n'
438 '\n'
439 'Affected files:\n')
440 errors = [] # 2-element tuples with (file, line number)
441 clang_warn_re = input_api.re.compile(r'//build/config/clang:extra_warnings')
442 no_presubmit_re = input_api.re.compile(
443 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
444 for f in gn_files:
445 for line_num, line in f.ChangedContents():
446 if clang_warn_re.search(line) and not no_presubmit_re.search(line):
447 errors.append(error_formatter(f.LocalPath(), line_num))
448 if errors:
449 return [output_api.PresubmitError(msg, errors)]
450 return []
451
Mirko Bonadei9ce800d2019-02-05 16:48:13 +0100452
453def CheckNoTestCaseUsageIsAdded(input_api, output_api, source_file_filter,
454 error_formatter=_ReportFileAndLine):
455 error_msg = ('Usage of legacy GoogleTest API detected!\nPlease use the '
456 'new API: https://github.com/google/googletest/blob/master/'
457 'googletest/docs/primer.md#beware-of-the-nomenclature.\n'
458 'Affected files:\n')
459 errors = [] # 2-element tuples with (file, line number)
460 test_case_re = input_api.re.compile(r'TEST_CASE')
461 file_filter = lambda f: (source_file_filter(f)
462 and f.LocalPath().endswith('.cc'))
463 for f in input_api.AffectedSourceFiles(file_filter):
464 for line_num, line in f.ChangedContents():
465 if test_case_re.search(line):
466 errors.append(error_formatter(f.LocalPath(), line_num))
467 if errors:
468 return [output_api.PresubmitError(error_msg, errors)]
469 return []
470
471
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100472def CheckNoStreamUsageIsAdded(input_api, output_api,
Artem Titov739351d2018-05-11 12:21:36 +0200473 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200474 error_formatter=_ReportFileAndLine):
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100475 """Make sure that no more dependencies on stringstream are added."""
476 error_msg = ('Usage of <sstream>, <istream> and <ostream> in WebRTC is '
477 'deprecated.\n'
478 'This includes the following types:\n'
479 'std::istringstream, std::ostringstream, std::wistringstream, '
480 'std::wostringstream,\n'
481 'std::wstringstream, std::ostream, std::wostream, std::istream,'
482 'std::wistream,\n'
483 'std::iostream, std::wiostream.\n'
484 'If you are not adding this code (e.g. you are just moving '
485 'existing code),\n'
486 'you can add a comment on the line that causes the problem:\n\n'
487 '#include <sstream> // no-presubmit-check TODO(webrtc:8982)\n'
488 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n'
489 '\n'
Karl Wibergebd01e82018-03-14 15:08:39 +0100490 'If you are adding new code, consider using '
491 'rtc::SimpleStringBuilder\n'
492 '(in rtc_base/strings/string_builder.h).\n'
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100493 'Affected files:\n')
494 errors = [] # 2-element tuples with (file, line number)
495 include_re = input_api.re.compile(r'#include <(i|o|s)stream>')
496 usage_re = input_api.re.compile(r'std::(w|i|o|io|wi|wo|wio)(string)*stream')
497 no_presubmit_re = input_api.re.compile(
Jonas Olsson74395342018-04-03 12:22:07 +0200498 r'// no-presubmit-check TODO\(webrtc:8982\)')
Artem Titova04d1402018-05-11 11:23:00 +0200499 file_filter = lambda x: (input_api.FilterSourceFile(x)
500 and source_file_filter(x))
501 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei44ca9a32018-11-13 11:16:40 +0100502 # Usage of stringstream is allowed under examples/.
503 if f.LocalPath() == 'PRESUBMIT.py' or f.LocalPath().startswith('examples'):
Mirko Bonadeid2c83322018-03-19 10:31:47 +0000504 continue
505 for line_num, line in f.ChangedContents():
506 if ((include_re.search(line) or usage_re.search(line))
507 and not no_presubmit_re.search(line)):
508 errors.append(error_formatter(f.LocalPath(), line_num))
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100509 if errors:
510 return [output_api.PresubmitError(error_msg, errors)]
511 return []
512
Artem Titove92675b2018-05-22 10:21:27 +0200513
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200514def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
515 """Checks that public_deps is not used without a good reason."""
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100516 result = []
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200517 no_presubmit_check_re = input_api.re.compile(
518 r'# no-presubmit-check TODO\(webrtc:8603\)')
519 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
520 'because it doesn\'t map well to downstream build systems.\n'
521 'Used in: %s (line %d).\n'
522 'If you are not adding this code (e.g. you are just moving '
523 'existing code) or you have a good reason, you can add a '
524 'comment on the line that causes the problem:\n\n'
525 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100526 for affected_file in gn_files:
527 for (line_number, affected_line) in affected_file.ChangedContents():
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200528 if ('public_deps' in affected_line
529 and not no_presubmit_check_re.search(affected_line)):
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100530 result.append(
531 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
532 line_number)))
533 return result
534
Artem Titove92675b2018-05-22 10:21:27 +0200535
Patrik Höglund6f491062018-01-11 12:04:23 +0100536def CheckCheckIncludesIsNotUsed(gn_files, output_api):
537 result = []
538 error_msg = ('check_includes overrides are not allowed since it can cause '
539 'incorrect dependencies to form. It effectively means that your '
540 'module can include any .h file without depending on its '
541 'corresponding target. There are some exceptional cases when '
542 'this is allowed: if so, get approval from a .gn owner in the'
543 'root OWNERS file.\n'
544 'Used in: %s (line %d).')
545 for affected_file in gn_files:
546 for (line_number, affected_line) in affected_file.ChangedContents():
547 if 'check_includes' in affected_line:
548 result.append(
549 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
550 line_number)))
551 return result
552
Artem Titove92675b2018-05-22 10:21:27 +0200553
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200554def CheckGnChanges(input_api, output_api):
Artem Titova04d1402018-05-11 11:23:00 +0200555 file_filter = lambda x: (input_api.FilterSourceFile(
Oleh Prypinafe01652017-10-04 15:56:08 +0200556 x, white_list=(r'.+\.(gn|gni)$',),
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200557 black_list=(r'.*/presubmit_checks_lib/testdata/.*',)))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700558
559 gn_files = []
Artem Titova04d1402018-05-11 11:23:00 +0200560 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200561 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 05:51:08 -0700562
563 result = []
564 if gn_files:
charujain9893e252017-09-14 13:33:22 +0200565 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200566 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
567 result.extend(CheckNoPackageBoundaryViolations(input_api, gn_files,
568 output_api))
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200569 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api, output_api))
Patrik Höglund6f491062018-01-11 12:04:23 +0100570 result.extend(CheckCheckIncludesIsNotUsed(gn_files, output_api))
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200571 result.extend(CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api,
572 output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700573 return result
574
Artem Titove92675b2018-05-22 10:21:27 +0200575
Oleh Prypin920b6532017-10-05 11:28:51 +0200576def CheckGnGen(input_api, output_api):
577 """Runs `gn gen --check` with default args to detect mismatches between
578 #includes and dependencies in the BUILD.gn files, as well as general build
579 errors.
580 """
581 with _AddToPath(input_api.os_path.join(
582 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
583 from gn_check import RunGnCheck
Mirko Bonadeid8665442018-09-04 12:17:27 +0200584 errors = RunGnCheck(FindSrcDirPath(input_api.PresubmitLocalPath()))[:5]
Oleh Prypin920b6532017-10-05 11:28:51 +0200585 if errors:
586 return [output_api.PresubmitPromptWarning(
587 'Some #includes do not match the build dependency graph. Please run:\n'
588 ' gn gen --check <out_dir>',
589 long_text='\n\n'.join(errors))]
590 return []
591
Artem Titove92675b2018-05-22 10:21:27 +0200592
Artem Titova04d1402018-05-11 11:23:00 +0200593def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000594 """Runs checkdeps on #include statements added in this
595 change. Breaking - rules is an error, breaking ! rules is a
596 warning.
597 """
598 # Copied from Chromium's src/PRESUBMIT.py.
599
600 # We need to wait until we have an input_api object and use this
601 # roundabout construct to import checkdeps because this file is
602 # eval-ed and thus doesn't have __file__.
Mirko Bonadeid8665442018-09-04 12:17:27 +0200603 src_path = FindSrcDirPath(input_api.PresubmitLocalPath())
604 checkdeps_path = input_api.os_path.join(src_path, 'buildtools', 'checkdeps')
Oleh Prypin2f33a562017-10-04 20:17:54 +0200605 if not os.path.exists(checkdeps_path):
606 return [output_api.PresubmitError(
607 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
608 'download all the DEPS entries?' % checkdeps_path)]
609 with _AddToPath(checkdeps_path):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000610 import checkdeps
611 from cpp_checker import CppChecker
612 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000613
614 added_includes = []
Artem Titova04d1402018-05-11 11:23:00 +0200615 for f in input_api.AffectedFiles(file_filter=source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000616 if not CppChecker.IsCppFile(f.LocalPath()):
617 continue
618
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200619 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000620 added_includes.append([f.LocalPath(), changed_lines])
621
622 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
623
624 error_descriptions = []
625 warning_descriptions = []
626 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
627 added_includes):
628 description_with_path = '%s\n %s' % (path, rule_description)
629 if rule_type == Rule.DISALLOW:
630 error_descriptions.append(description_with_path)
631 else:
632 warning_descriptions.append(description_with_path)
633
634 results = []
635 if error_descriptions:
636 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 03:47:05 -0700637 'You added one or more #includes that violate checkdeps rules.\n'
638 'Check that the DEPS files in these locations contain valid rules.\n'
639 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
640 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000641 error_descriptions))
642 if warning_descriptions:
643 results.append(output_api.PresubmitPromptOrNotify(
644 'You added one or more #includes of files that are temporarily\n'
645 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 03:47:05 -0700646 '#include? See relevant DEPS file(s) for details and contacts.\n'
647 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
648 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000649 warning_descriptions))
650 return results
651
Artem Titove92675b2018-05-22 10:21:27 +0200652
charujain9893e252017-09-14 13:33:22 +0200653def CheckCommitMessageBugEntry(input_api, output_api):
654 """Check that bug entries are well-formed in commit message."""
655 bogus_bug_msg = (
Mirko Bonadei61880182017-10-12 15:12:35 +0200656 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
charujain9893e252017-09-14 13:33:22 +0200657 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.')
658 results = []
Mirko Bonadei61880182017-10-12 15:12:35 +0200659 for bug in input_api.change.BugsFromDescription():
charujain9893e252017-09-14 13:33:22 +0200660 bug = bug.strip()
661 if bug.lower() == 'none':
662 continue
charujain81a58c72017-09-25 13:25:45 +0200663 if 'b/' not in bug and ':' not in bug:
charujain9893e252017-09-14 13:33:22 +0200664 try:
665 if int(bug) > 100000:
666 # Rough indicator for current chromium bugs.
667 prefix_guess = 'chromium'
668 else:
669 prefix_guess = 'webrtc'
Mirko Bonadei61880182017-10-12 15:12:35 +0200670 results.append('Bug entry requires issue tracker prefix, e.g. %s:%s' %
charujain9893e252017-09-14 13:33:22 +0200671 (prefix_guess, bug))
672 except ValueError:
673 results.append(bogus_bug_msg % bug)
charujain81a58c72017-09-25 13:25:45 +0200674 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
charujain9893e252017-09-14 13:33:22 +0200675 results.append(bogus_bug_msg % bug)
676 return [output_api.PresubmitError(r) for r in results]
677
Artem Titove92675b2018-05-22 10:21:27 +0200678
charujain9893e252017-09-14 13:33:22 +0200679def CheckChangeHasBugField(input_api, output_api):
Mirko Bonadei61880182017-10-12 15:12:35 +0200680 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 08:11:16 -0700681
682 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 15:12:35 +0200683 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 08:11:16 -0700684 a bug reference.
Mirko Bonadei61880182017-10-12 15:12:35 +0200685
686 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
687 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 08:11:16 -0700688 """
Mirko Bonadei61880182017-10-12 15:12:35 +0200689 if input_api.change.BugsFromDescription():
kjellanderd1e26a92016-09-19 08:11:16 -0700690 return []
691 else:
692 return [output_api.PresubmitError(
Mirko Bonadei61880182017-10-12 15:12:35 +0200693 'The "Bug: [bug number]" footer is mandatory. Please create a bug and '
kjellanderd1e26a92016-09-19 08:11:16 -0700694 'reference it using either of:\n'
Mirko Bonadei61880182017-10-12 15:12:35 +0200695 ' * https://bugs.webrtc.org - reference it using Bug: webrtc:XXXX\n'
696 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000697
Artem Titove92675b2018-05-22 10:21:27 +0200698
Artem Titova04d1402018-05-11 11:23:00 +0200699def CheckJSONParseErrors(input_api, output_api, source_file_filter):
kjellander569cf942016-02-11 05:02:59 -0800700 """Check that JSON files do not contain syntax errors."""
701
702 def FilterFile(affected_file):
Artem Titova04d1402018-05-11 11:23:00 +0200703 return (input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
704 and source_file_filter(affected_file))
kjellander569cf942016-02-11 05:02:59 -0800705
706 def GetJSONParseError(input_api, filename):
707 try:
708 contents = input_api.ReadFile(filename)
709 input_api.json.loads(contents)
710 except ValueError as e:
711 return e
712 return None
713
714 results = []
715 for affected_file in input_api.AffectedFiles(
716 file_filter=FilterFile, include_deletes=False):
717 parse_error = GetJSONParseError(input_api,
718 affected_file.AbsoluteLocalPath())
719 if parse_error:
720 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
Artem Titove92675b2018-05-22 10:21:27 +0200721 (affected_file.LocalPath(),
722 parse_error)))
kjellander569cf942016-02-11 05:02:59 -0800723 return results
724
725
charujain9893e252017-09-14 13:33:22 +0200726def RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 06:42:43 -0700727 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200728 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
729
730 test_directories = [
Edward Lemur6d01f6d2017-09-14 17:02:01 +0200731 input_api.PresubmitLocalPath(),
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200732 Join('rtc_tools', 'py_event_log_analyzer'),
733 Join('rtc_tools'),
734 Join('audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 05:27:22 -0800735 ] + [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200736 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 05:27:22 -0800737 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200738 ]
739
740 tests = []
741 for directory in test_directories:
742 tests.extend(
743 input_api.canned_checks.GetUnitTestsInDirectory(
744 input_api,
745 output_api,
746 directory,
747 whitelist=[r'.+_test\.py$']))
748 return input_api.RunTests(tests, parallel=True)
749
750
Artem Titova04d1402018-05-11 11:23:00 +0200751def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
752 source_file_filter):
mbonadei38415b22017-04-07 05:38:01 -0700753 """Checks that the namespace google::protobuf has not been used."""
754 files = []
755 pattern = input_api.re.compile(r'google::protobuf')
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200756 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
Artem Titova04d1402018-05-11 11:23:00 +0200757 file_filter = lambda x: (input_api.FilterSourceFile(x)
758 and source_file_filter(x))
759 for f in input_api.AffectedSourceFiles(file_filter):
mbonadei38415b22017-04-07 05:38:01 -0700760 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
761 continue
762 contents = input_api.ReadFile(f)
763 if pattern.search(contents):
764 files.append(f)
765
766 if files:
767 return [output_api.PresubmitError(
768 'Please avoid to use namespace `google::protobuf` directly.\n'
769 'Add a using directive in `%s` and include that header instead.'
770 % proto_utils_path, files)]
771 return []
772
773
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200774def _LicenseHeader(input_api):
775 """Returns the license header regexp."""
776 # Accept any year number from 2003 to the current year
777 current_year = int(input_api.time.strftime('%Y'))
778 allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1)))
779 years_re = '(' + '|'.join(allowed_years) + ')'
780 license_header = (
781 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
782 r'All [Rr]ights [Rr]eserved\.\n'
783 r'.*?\n'
784 r'.*? Use of this source code is governed by a BSD-style license\n'
785 r'.*? that can be found in the LICENSE file in the root of the source\n'
786 r'.*? tree\. An additional intellectual property rights grant can be '
787 r'found\n'
788 r'.*? in the file PATENTS\. All contributing project authors may\n'
789 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
790 ) % {
791 'year': years_re,
792 }
793 return license_header
794
795
charujain9893e252017-09-14 13:33:22 +0200796def CommonChecks(input_api, output_api):
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000797 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34 +0000798 results = []
tkchin42f580e2015-11-26 23:18:23 -0800799 # Filter out files that are in objc or ios dirs from being cpplint-ed since
800 # they do not follow C++ lint rules.
801 black_list = input_api.DEFAULT_BLACK_LIST + (
802 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05 +0000803 r".*objc\.[hcm]+$",
tkchin42f580e2015-11-26 23:18:23 -0800804 )
805 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list)
charujain9893e252017-09-14 13:33:22 +0200806 results.extend(CheckApprovedFilesLintClean(
tkchin42f580e2015-11-26 23:18:23 -0800807 input_api, output_api, source_file_filter))
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200808 results.extend(input_api.canned_checks.CheckLicense(
809 input_api, output_api, _LicenseHeader(input_api)))
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000810 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100811 black_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200812 r'^build[\\\/].*\.py$',
813 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-12 22:43:38 -0700814 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 12:01:17 +0100815 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200816 r'^out.*[\\\/].*\.py$',
817 r'^testing[\\\/].*\.py$',
818 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100819 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 12:21:39 -0800820 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200821 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200822 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200823 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800824
nisse3d21e232016-09-02 03:07:06 -0700825 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200826 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
827 # we need to have different license checks in talk/ and webrtc/ directories.
828 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200829
tkchin3cd9a302016-06-08 12:40:28 -0700830 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
831 # ObjC subdirectories ObjC headers.
832 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100833 # Skip long-lines check for DEPS and GN files.
834 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
Artem Titova04d1402018-05-11 11:23:00 +0200835 # Also we will skip most checks for third_party directory.
Artem Titov42f0d782018-06-27 13:23:17 +0200836 third_party_filter_list = (r'^third_party[\\\/].+',)
tkchin3cd9a302016-06-08 12:40:28 -0700837 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
Artem Titova04d1402018-05-11 11:23:00 +0200838 black_list=build_file_filter_list + objc_filter_list +
839 third_party_filter_list)
tkchin3cd9a302016-06-08 12:40:28 -0700840 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
841 white_list=objc_filter_list)
Artem Titove92675b2018-05-22 10:21:27 +0200842 non_third_party_sources = lambda x: input_api.FilterSourceFile(x,
843 black_list=third_party_filter_list)
844
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000845 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 12:40:28 -0700846 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
847 results.extend(input_api.canned_checks.CheckLongLines(
848 input_api, output_api, maxlen=100,
849 source_file_filter=hundred_char_sources))
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000850 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
Artem Titova04d1402018-05-11 11:23:00 +0200851 input_api, output_api, source_file_filter=non_third_party_sources))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000852 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
Artem Titova04d1402018-05-11 11:23:00 +0200853 input_api, output_api, source_file_filter=non_third_party_sources))
kjellandere5dc62a2016-12-14 00:16:21 -0800854 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
Oleh Prypine0735142018-10-04 11:15:54 +0200855 input_api, output_api, bot_whitelist=[
856 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com'
857 ]))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000858 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
Artem Titova04d1402018-05-11 11:23:00 +0200859 input_api, output_api, source_file_filter=non_third_party_sources))
Yves Gerey87a93532018-06-20 15:51:49 +0200860 results.extend(input_api.canned_checks.CheckPatchFormatted(
861 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200862 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200863 results.extend(CheckNoIOStreamInHeaders(
864 input_api, output_api, source_file_filter=non_third_party_sources))
865 results.extend(CheckNoPragmaOnce(
866 input_api, output_api, source_file_filter=non_third_party_sources))
867 results.extend(CheckNoFRIEND_TEST(
868 input_api, output_api, source_file_filter=non_third_party_sources))
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200869 results.extend(CheckGnChanges(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200870 results.extend(CheckUnwantedDependencies(
871 input_api, output_api, source_file_filter=non_third_party_sources))
872 results.extend(CheckJSONParseErrors(
873 input_api, output_api, source_file_filter=non_third_party_sources))
charujain9893e252017-09-14 13:33:22 +0200874 results.extend(RunPythonTests(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200875 results.extend(CheckUsageOfGoogleProtobufNamespace(
876 input_api, output_api, source_file_filter=non_third_party_sources))
877 results.extend(CheckOrphanHeaders(
878 input_api, output_api, source_file_filter=non_third_party_sources))
879 results.extend(CheckNewlineAtTheEndOfProtoFiles(
880 input_api, output_api, source_file_filter=non_third_party_sources))
881 results.extend(CheckNoStreamUsageIsAdded(
Artem Titov739351d2018-05-11 12:21:36 +0200882 input_api, output_api, non_third_party_sources))
Mirko Bonadei9ce800d2019-02-05 16:48:13 +0100883 results.extend(CheckNoTestCaseUsageIsAdded(
884 input_api, output_api, non_third_party_sources))
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +0200885 results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200886 results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api))
tzika06bf852018-11-15 20:37:35 +0900887 results.extend(CheckAbslMemoryInclude(
888 input_api, output_api, non_third_party_sources))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200889 return results
890
891
892def CheckApiDepsFileIsUpToDate(input_api, output_api):
Mirko Bonadei90490372018-10-26 13:17:47 +0200893 """Check that 'include_rules' in api/DEPS is up to date.
894
895 The file api/DEPS must be kept up to date in order to avoid to avoid to
896 include internal header from WebRTC's api/ headers.
897
898 This check is focused on ensuring that 'include_rules' contains a deny
899 rule for each root level directory. More focused allow rules can be
900 added to 'specific_include_rules'.
901 """
Mirko Bonadeia418e672018-10-24 13:57:25 +0200902 results = []
903 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
904 with open(api_deps) as f:
905 deps_content = _ParseDeps(f.read())
906
907 include_rules = deps_content.get('include_rules', [])
Mirko Bonadeia418e672018-10-24 13:57:25 +0200908
Mirko Bonadei90490372018-10-26 13:17:47 +0200909 # Only check top level directories affected by the current CL.
910 dirs_to_check = set()
911 for f in input_api.AffectedFiles():
912 path_tokens = [t for t in f.LocalPath().split(os.sep) if t]
913 if len(path_tokens) > 1:
914 if (path_tokens[0] != 'api' and
915 os.path.isdir(os.path.join(input_api.PresubmitLocalPath(),
916 path_tokens[0]))):
917 dirs_to_check.add(path_tokens[0])
Mirko Bonadeia418e672018-10-24 13:57:25 +0200918
Mirko Bonadei90490372018-10-26 13:17:47 +0200919 missing_include_rules = set()
920 for p in dirs_to_check:
Mirko Bonadeia418e672018-10-24 13:57:25 +0200921 rule = '-%s' % p
922 if rule not in include_rules:
Mirko Bonadei90490372018-10-26 13:17:47 +0200923 missing_include_rules.add(rule)
924
Mirko Bonadeia418e672018-10-24 13:57:25 +0200925 if missing_include_rules:
Mirko Bonadei90490372018-10-26 13:17:47 +0200926 error_msg = [
927 'include_rules = [\n',
928 ' ...\n',
929 ]
Mirko Bonadeia418e672018-10-24 13:57:25 +0200930
Mirko Bonadei90490372018-10-26 13:17:47 +0200931 for r in sorted(missing_include_rules):
932 error_msg.append(' "%s",\n' % str(r))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200933
Mirko Bonadei90490372018-10-26 13:17:47 +0200934 error_msg.append(' ...\n')
935 error_msg.append(']\n')
936
Mirko Bonadeia418e672018-10-24 13:57:25 +0200937 results.append(output_api.PresubmitError(
Mirko Bonadei90490372018-10-26 13:17:47 +0200938 'New root level directory detected! WebRTC api/ headers should '
939 'not #include headers from \n'
940 'the new directory, so please update "include_rules" in file\n'
941 '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg))))
942
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000943 return results
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000944
tzika06bf852018-11-15 20:37:35 +0900945def CheckAbslMemoryInclude(input_api, output_api, source_file_filter):
946 pattern = input_api.re.compile(
947 r'^#include\s*"absl/memory/memory.h"', input_api.re.MULTILINE)
948 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
949 and source_file_filter(f))
950
951 files = []
952 for f in input_api.AffectedFiles(
953 include_deletes=False, file_filter=file_filter):
954 contents = input_api.ReadFile(f)
955 if pattern.search(contents):
956 continue
957 for _, line in f.ChangedContents():
958 if 'absl::make_unique' in line:
959 files.append(f)
960 break
961
962 if len(files):
963 return [output_api.PresubmitError(
964 'Please include "absl/memory/memory.h" header for'
965 ' absl::make_unique.\nThis header may or may not be included'
966 ' transitively depends on the C++ standard version.',
967 files)]
968 return []
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000969
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000970def CheckChangeOnUpload(input_api, output_api):
971 results = []
charujain9893e252017-09-14 13:33:22 +0200972 results.extend(CommonChecks(input_api, output_api))
Oleh Prypin920b6532017-10-05 11:28:51 +0200973 results.extend(CheckGnGen(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200974 results.extend(
975 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:34 +0000976 return results
977
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000978
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000979def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:24 +0000980 results = []
charujain9893e252017-09-14 13:33:22 +0200981 results.extend(CommonChecks(input_api, output_api))
982 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
Artem Titov42f0d782018-06-27 13:23:17 +0200983 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000984 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
985 input_api, output_api))
986 results.extend(input_api.canned_checks.CheckChangeHasDescription(
987 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200988 results.extend(CheckChangeHasBugField(input_api, output_api))
989 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:43 +0000990 results.extend(input_api.canned_checks.CheckTreeIsOpen(
991 input_api, output_api,
992 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:24 +0000993 return results
mbonadei74973ed2017-05-09 07:58:05 -0700994
995
Artem Titova04d1402018-05-11 11:23:00 +0200996def CheckOrphanHeaders(input_api, output_api, source_file_filter):
mbonadei74973ed2017-05-09 07:58:05 -0700997 # We need to wait until we have an input_api object and use this
998 # roundabout construct to import prebubmit_checks_lib because this file is
999 # eval-ed and thus doesn't have __file__.
Patrik Höglund2f3f7222017-12-19 11:08:56 +01001000 error_msg = """{} should be listed in {}."""
mbonadei74973ed2017-05-09 07:58:05 -07001001 results = []
Patrik Höglund7e60de22018-01-09 14:22:00 +01001002 orphan_blacklist = [
1003 os.path.join('tools_webrtc', 'ios', 'SDK'),
1004 ]
Oleh Prypin2f33a562017-10-04 20:17:54 +02001005 with _AddToPath(input_api.os_path.join(
1006 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
mbonadei74973ed2017-05-09 07:58:05 -07001007 from check_orphan_headers import GetBuildGnPathFromFilePath
1008 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 07:58:05 -07001009
Artem Titova04d1402018-05-11 11:23:00 +02001010 file_filter = lambda x: input_api.FilterSourceFile(
1011 x, black_list=orphan_blacklist) and source_file_filter(x)
1012 for f in input_api.AffectedSourceFiles(file_filter):
Patrik Höglund7e60de22018-01-09 14:22:00 +01001013 if f.LocalPath().endswith('.h'):
mbonadei74973ed2017-05-09 07:58:05 -07001014 file_path = os.path.abspath(f.LocalPath())
1015 root_dir = os.getcwd()
1016 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
1017 root_dir)
1018 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
1019 if not in_build_gn:
1020 results.append(output_api.PresubmitError(error_msg.format(
Patrik Höglund2f3f7222017-12-19 11:08:56 +01001021 f.LocalPath(), os.path.relpath(gn_file_path))))
mbonadei74973ed2017-05-09 07:58:05 -07001022 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001023
1024
Artem Titove92675b2018-05-22 10:21:27 +02001025def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api, source_file_filter):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001026 """Checks that all .proto files are terminated with a newline."""
1027 error_msg = 'File {} must end with exactly one newline.'
1028 results = []
Artem Titova04d1402018-05-11 11:23:00 +02001029 file_filter = lambda x: input_api.FilterSourceFile(
1030 x, white_list=(r'.+\.proto$',)) and source_file_filter(x)
1031 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001032 file_path = f.LocalPath()
1033 with open(file_path) as f:
1034 lines = f.readlines()
Mirko Bonadeia730c1c2017-09-18 11:33:13 +02001035 if len(lines) > 0 and not lines[-1].endswith('\n'):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001036 results.append(output_api.PresubmitError(error_msg.format(file_path)))
1037 return results
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001038
1039
1040def _ExtractAddRulesFromParsedDeps(parsed_deps):
1041 """Extract the rules that add dependencies from a parsed DEPS file.
1042
1043 Args:
1044 parsed_deps: the locals dictionary from evaluating the DEPS file."""
1045 add_rules = set()
1046 add_rules.update([
1047 rule[1:] for rule in parsed_deps.get('include_rules', [])
1048 if rule.startswith('+') or rule.startswith('!')
1049 ])
1050 for _, rules in parsed_deps.get('specific_include_rules',
1051 {}).iteritems():
1052 add_rules.update([
1053 rule[1:] for rule in rules
1054 if rule.startswith('+') or rule.startswith('!')
1055 ])
1056 return add_rules
1057
1058
1059def _ParseDeps(contents):
1060 """Simple helper for parsing DEPS files."""
1061 # Stubs for handling special syntax in the root DEPS file.
1062 class VarImpl(object):
1063
1064 def __init__(self, local_scope):
1065 self._local_scope = local_scope
1066
1067 def Lookup(self, var_name):
1068 """Implements the Var syntax."""
1069 try:
1070 return self._local_scope['vars'][var_name]
1071 except KeyError:
1072 raise Exception('Var is not defined: %s' % var_name)
1073
1074 local_scope = {}
1075 global_scope = {
1076 'Var': VarImpl(local_scope).Lookup,
1077 }
1078 exec contents in global_scope, local_scope
1079 return local_scope
1080
1081
1082def _CalculateAddedDeps(os_path, old_contents, new_contents):
1083 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
1084 a set of DEPS entries that we should look up.
1085
1086 For a directory (rather than a specific filename) we fake a path to
1087 a specific filename by adding /DEPS. This is chosen as a file that
1088 will seldom or never be subject to per-file include_rules.
1089 """
1090 # We ignore deps entries on auto-generated directories.
1091 auto_generated_dirs = ['grit', 'jni']
1092
1093 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1094 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
1095
1096 added_deps = new_deps.difference(old_deps)
1097
1098 results = set()
1099 for added_dep in added_deps:
1100 if added_dep.split('/')[0] in auto_generated_dirs:
1101 continue
1102 # Assume that a rule that ends in .h is a rule for a specific file.
1103 if added_dep.endswith('.h'):
1104 results.add(added_dep)
1105 else:
1106 results.add(os_path.join(added_dep, 'DEPS'))
1107 return results
1108
1109
1110def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
1111 """When a dependency prefixed with + is added to a DEPS file, we
1112 want to make sure that the change is reviewed by an OWNER of the
1113 target file or directory, to avoid layering violations from being
1114 introduced. This check verifies that this happens.
1115 """
1116 virtual_depended_on_files = set()
1117
1118 file_filter = lambda f: not input_api.re.match(
1119 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1120 for f in input_api.AffectedFiles(include_deletes=False,
1121 file_filter=file_filter):
1122 filename = input_api.os_path.basename(f.LocalPath())
1123 if filename == 'DEPS':
1124 virtual_depended_on_files.update(_CalculateAddedDeps(
1125 input_api.os_path,
1126 '\n'.join(f.OldContents()),
1127 '\n'.join(f.NewContents())))
1128
1129 if not virtual_depended_on_files:
1130 return []
1131
1132 if input_api.is_committing:
1133 if input_api.tbr:
1134 return [output_api.PresubmitNotifyResult(
1135 '--tbr was specified, skipping OWNERS check for DEPS additions')]
1136 if input_api.dry_run:
1137 return [output_api.PresubmitNotifyResult(
1138 'This is a dry run, skipping OWNERS check for DEPS additions')]
1139 if not input_api.change.issue:
1140 return [output_api.PresubmitError(
1141 "DEPS approval by OWNERS check failed: this change has "
1142 "no change number, so we can't check it for approvals.")]
1143 output = output_api.PresubmitError
1144 else:
1145 output = output_api.PresubmitNotifyResult
1146
1147 owners_db = input_api.owners_db
1148 owner_email, reviewers = (
1149 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1150 input_api,
1151 owners_db.email_regexp,
1152 approval_needed=input_api.is_committing))
1153
1154 owner_email = owner_email or input_api.change.author_email
1155
1156 reviewers_plus_owner = set(reviewers)
1157 if owner_email:
1158 reviewers_plus_owner.add(owner_email)
1159 missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
1160 reviewers_plus_owner)
1161
1162 # We strip the /DEPS part that was added by
1163 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1164 # directory.
1165 def StripDeps(path):
1166 start_deps = path.rfind('/DEPS')
1167 if start_deps != -1:
1168 return path[:start_deps]
1169 else:
1170 return path
1171 unapproved_dependencies = ["'+%s'," % StripDeps(path)
1172 for path in missing_files]
1173
1174 if unapproved_dependencies:
1175 output_list = [
1176 output('You need LGTM from owners of depends-on paths in DEPS that were '
1177 'modified in this CL:\n %s' %
1178 '\n '.join(sorted(unapproved_dependencies)))]
1179 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
1180 output_list.append(output(
1181 'Suggested missing target path OWNERS:\n %s' %
1182 '\n '.join(suggested_owners or [])))
1183 return output_list
1184
1185 return []