blob: 5b4677dcd9c5922866899b09a8baedc8bbc6d0a8 [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',
Steve Antone78bcb92017-10-31 09:53:08 -070022 'media/base/streamparams.h',
23 'media/base/videocommon.h',
Steve Antone78bcb92017-10-31 09:53:08 -070024 'media/sctp/sctptransport.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',
Steve Anton6c38cc72017-11-29 10:25:58 -080032 'p2p/base/pseudotcp.cc',
33 'p2p/base/pseudotcp.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 Bonadeia51bbd82018-03-08 16:15:45 +0100452def CheckNoStreamUsageIsAdded(input_api, output_api,
Artem Titov739351d2018-05-11 12:21:36 +0200453 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200454 error_formatter=_ReportFileAndLine):
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100455 """Make sure that no more dependencies on stringstream are added."""
456 error_msg = ('Usage of <sstream>, <istream> and <ostream> in WebRTC is '
457 'deprecated.\n'
458 'This includes the following types:\n'
459 'std::istringstream, std::ostringstream, std::wistringstream, '
460 'std::wostringstream,\n'
461 'std::wstringstream, std::ostream, std::wostream, std::istream,'
462 'std::wistream,\n'
463 'std::iostream, std::wiostream.\n'
464 'If you are not adding this code (e.g. you are just moving '
465 'existing code),\n'
466 'you can add a comment on the line that causes the problem:\n\n'
467 '#include <sstream> // no-presubmit-check TODO(webrtc:8982)\n'
468 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n'
469 '\n'
Karl Wibergebd01e82018-03-14 15:08:39 +0100470 'If you are adding new code, consider using '
471 'rtc::SimpleStringBuilder\n'
472 '(in rtc_base/strings/string_builder.h).\n'
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100473 'Affected files:\n')
474 errors = [] # 2-element tuples with (file, line number)
475 include_re = input_api.re.compile(r'#include <(i|o|s)stream>')
476 usage_re = input_api.re.compile(r'std::(w|i|o|io|wi|wo|wio)(string)*stream')
477 no_presubmit_re = input_api.re.compile(
Jonas Olsson74395342018-04-03 12:22:07 +0200478 r'// no-presubmit-check TODO\(webrtc:8982\)')
Artem Titova04d1402018-05-11 11:23:00 +0200479 file_filter = lambda x: (input_api.FilterSourceFile(x)
480 and source_file_filter(x))
481 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei44ca9a32018-11-13 11:16:40 +0100482 # Usage of stringstream is allowed under examples/.
483 if f.LocalPath() == 'PRESUBMIT.py' or f.LocalPath().startswith('examples'):
Mirko Bonadeid2c83322018-03-19 10:31:47 +0000484 continue
485 for line_num, line in f.ChangedContents():
486 if ((include_re.search(line) or usage_re.search(line))
487 and not no_presubmit_re.search(line)):
488 errors.append(error_formatter(f.LocalPath(), line_num))
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100489 if errors:
490 return [output_api.PresubmitError(error_msg, errors)]
491 return []
492
Artem Titove92675b2018-05-22 10:21:27 +0200493
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200494def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
495 """Checks that public_deps is not used without a good reason."""
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100496 result = []
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200497 no_presubmit_check_re = input_api.re.compile(
498 r'# no-presubmit-check TODO\(webrtc:8603\)')
499 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
500 'because it doesn\'t map well to downstream build systems.\n'
501 'Used in: %s (line %d).\n'
502 'If you are not adding this code (e.g. you are just moving '
503 'existing code) or you have a good reason, you can add a '
504 'comment on the line that causes the problem:\n\n'
505 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100506 for affected_file in gn_files:
507 for (line_number, affected_line) in affected_file.ChangedContents():
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200508 if ('public_deps' in affected_line
509 and not no_presubmit_check_re.search(affected_line)):
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100510 result.append(
511 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
512 line_number)))
513 return result
514
Artem Titove92675b2018-05-22 10:21:27 +0200515
Patrik Höglund6f491062018-01-11 12:04:23 +0100516def CheckCheckIncludesIsNotUsed(gn_files, output_api):
517 result = []
518 error_msg = ('check_includes overrides are not allowed since it can cause '
519 'incorrect dependencies to form. It effectively means that your '
520 'module can include any .h file without depending on its '
521 'corresponding target. There are some exceptional cases when '
522 'this is allowed: if so, get approval from a .gn owner in the'
523 'root OWNERS file.\n'
524 'Used in: %s (line %d).')
525 for affected_file in gn_files:
526 for (line_number, affected_line) in affected_file.ChangedContents():
527 if 'check_includes' in affected_line:
528 result.append(
529 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
530 line_number)))
531 return result
532
Artem Titove92675b2018-05-22 10:21:27 +0200533
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200534def CheckGnChanges(input_api, output_api):
Artem Titova04d1402018-05-11 11:23:00 +0200535 file_filter = lambda x: (input_api.FilterSourceFile(
Oleh Prypinafe01652017-10-04 15:56:08 +0200536 x, white_list=(r'.+\.(gn|gni)$',),
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200537 black_list=(r'.*/presubmit_checks_lib/testdata/.*',)))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700538
539 gn_files = []
Artem Titova04d1402018-05-11 11:23:00 +0200540 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200541 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 05:51:08 -0700542
543 result = []
544 if gn_files:
charujain9893e252017-09-14 13:33:22 +0200545 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200546 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
547 result.extend(CheckNoPackageBoundaryViolations(input_api, gn_files,
548 output_api))
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200549 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api, output_api))
Patrik Höglund6f491062018-01-11 12:04:23 +0100550 result.extend(CheckCheckIncludesIsNotUsed(gn_files, output_api))
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200551 result.extend(CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api,
552 output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700553 return result
554
Artem Titove92675b2018-05-22 10:21:27 +0200555
Oleh Prypin920b6532017-10-05 11:28:51 +0200556def CheckGnGen(input_api, output_api):
557 """Runs `gn gen --check` with default args to detect mismatches between
558 #includes and dependencies in the BUILD.gn files, as well as general build
559 errors.
560 """
561 with _AddToPath(input_api.os_path.join(
562 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
563 from gn_check import RunGnCheck
Mirko Bonadeid8665442018-09-04 12:17:27 +0200564 errors = RunGnCheck(FindSrcDirPath(input_api.PresubmitLocalPath()))[:5]
Oleh Prypin920b6532017-10-05 11:28:51 +0200565 if errors:
566 return [output_api.PresubmitPromptWarning(
567 'Some #includes do not match the build dependency graph. Please run:\n'
568 ' gn gen --check <out_dir>',
569 long_text='\n\n'.join(errors))]
570 return []
571
Artem Titove92675b2018-05-22 10:21:27 +0200572
Artem Titova04d1402018-05-11 11:23:00 +0200573def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000574 """Runs checkdeps on #include statements added in this
575 change. Breaking - rules is an error, breaking ! rules is a
576 warning.
577 """
578 # Copied from Chromium's src/PRESUBMIT.py.
579
580 # We need to wait until we have an input_api object and use this
581 # roundabout construct to import checkdeps because this file is
582 # eval-ed and thus doesn't have __file__.
Mirko Bonadeid8665442018-09-04 12:17:27 +0200583 src_path = FindSrcDirPath(input_api.PresubmitLocalPath())
584 checkdeps_path = input_api.os_path.join(src_path, 'buildtools', 'checkdeps')
Oleh Prypin2f33a562017-10-04 20:17:54 +0200585 if not os.path.exists(checkdeps_path):
586 return [output_api.PresubmitError(
587 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
588 'download all the DEPS entries?' % checkdeps_path)]
589 with _AddToPath(checkdeps_path):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000590 import checkdeps
591 from cpp_checker import CppChecker
592 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000593
594 added_includes = []
Artem Titova04d1402018-05-11 11:23:00 +0200595 for f in input_api.AffectedFiles(file_filter=source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000596 if not CppChecker.IsCppFile(f.LocalPath()):
597 continue
598
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200599 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000600 added_includes.append([f.LocalPath(), changed_lines])
601
602 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
603
604 error_descriptions = []
605 warning_descriptions = []
606 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
607 added_includes):
608 description_with_path = '%s\n %s' % (path, rule_description)
609 if rule_type == Rule.DISALLOW:
610 error_descriptions.append(description_with_path)
611 else:
612 warning_descriptions.append(description_with_path)
613
614 results = []
615 if error_descriptions:
616 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 03:47:05 -0700617 'You added one or more #includes that violate checkdeps rules.\n'
618 'Check that the DEPS files in these locations contain valid rules.\n'
619 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
620 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000621 error_descriptions))
622 if warning_descriptions:
623 results.append(output_api.PresubmitPromptOrNotify(
624 'You added one or more #includes of files that are temporarily\n'
625 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 03:47:05 -0700626 '#include? See relevant DEPS file(s) for details and contacts.\n'
627 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
628 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000629 warning_descriptions))
630 return results
631
Artem Titove92675b2018-05-22 10:21:27 +0200632
charujain9893e252017-09-14 13:33:22 +0200633def CheckCommitMessageBugEntry(input_api, output_api):
634 """Check that bug entries are well-formed in commit message."""
635 bogus_bug_msg = (
Mirko Bonadei61880182017-10-12 15:12:35 +0200636 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
charujain9893e252017-09-14 13:33:22 +0200637 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.')
638 results = []
Mirko Bonadei61880182017-10-12 15:12:35 +0200639 for bug in input_api.change.BugsFromDescription():
charujain9893e252017-09-14 13:33:22 +0200640 bug = bug.strip()
641 if bug.lower() == 'none':
642 continue
charujain81a58c72017-09-25 13:25:45 +0200643 if 'b/' not in bug and ':' not in bug:
charujain9893e252017-09-14 13:33:22 +0200644 try:
645 if int(bug) > 100000:
646 # Rough indicator for current chromium bugs.
647 prefix_guess = 'chromium'
648 else:
649 prefix_guess = 'webrtc'
Mirko Bonadei61880182017-10-12 15:12:35 +0200650 results.append('Bug entry requires issue tracker prefix, e.g. %s:%s' %
charujain9893e252017-09-14 13:33:22 +0200651 (prefix_guess, bug))
652 except ValueError:
653 results.append(bogus_bug_msg % bug)
charujain81a58c72017-09-25 13:25:45 +0200654 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
charujain9893e252017-09-14 13:33:22 +0200655 results.append(bogus_bug_msg % bug)
656 return [output_api.PresubmitError(r) for r in results]
657
Artem Titove92675b2018-05-22 10:21:27 +0200658
charujain9893e252017-09-14 13:33:22 +0200659def CheckChangeHasBugField(input_api, output_api):
Mirko Bonadei61880182017-10-12 15:12:35 +0200660 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 08:11:16 -0700661
662 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 15:12:35 +0200663 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 08:11:16 -0700664 a bug reference.
Mirko Bonadei61880182017-10-12 15:12:35 +0200665
666 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
667 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 08:11:16 -0700668 """
Mirko Bonadei61880182017-10-12 15:12:35 +0200669 if input_api.change.BugsFromDescription():
kjellanderd1e26a92016-09-19 08:11:16 -0700670 return []
671 else:
672 return [output_api.PresubmitError(
Mirko Bonadei61880182017-10-12 15:12:35 +0200673 'The "Bug: [bug number]" footer is mandatory. Please create a bug and '
kjellanderd1e26a92016-09-19 08:11:16 -0700674 'reference it using either of:\n'
Mirko Bonadei61880182017-10-12 15:12:35 +0200675 ' * https://bugs.webrtc.org - reference it using Bug: webrtc:XXXX\n'
676 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000677
Artem Titove92675b2018-05-22 10:21:27 +0200678
Artem Titova04d1402018-05-11 11:23:00 +0200679def CheckJSONParseErrors(input_api, output_api, source_file_filter):
kjellander569cf942016-02-11 05:02:59 -0800680 """Check that JSON files do not contain syntax errors."""
681
682 def FilterFile(affected_file):
Artem Titova04d1402018-05-11 11:23:00 +0200683 return (input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
684 and source_file_filter(affected_file))
kjellander569cf942016-02-11 05:02:59 -0800685
686 def GetJSONParseError(input_api, filename):
687 try:
688 contents = input_api.ReadFile(filename)
689 input_api.json.loads(contents)
690 except ValueError as e:
691 return e
692 return None
693
694 results = []
695 for affected_file in input_api.AffectedFiles(
696 file_filter=FilterFile, include_deletes=False):
697 parse_error = GetJSONParseError(input_api,
698 affected_file.AbsoluteLocalPath())
699 if parse_error:
700 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
Artem Titove92675b2018-05-22 10:21:27 +0200701 (affected_file.LocalPath(),
702 parse_error)))
kjellander569cf942016-02-11 05:02:59 -0800703 return results
704
705
charujain9893e252017-09-14 13:33:22 +0200706def RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 06:42:43 -0700707 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200708 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
709
710 test_directories = [
Edward Lemur6d01f6d2017-09-14 17:02:01 +0200711 input_api.PresubmitLocalPath(),
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200712 Join('rtc_tools', 'py_event_log_analyzer'),
713 Join('rtc_tools'),
714 Join('audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 05:27:22 -0800715 ] + [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200716 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 05:27:22 -0800717 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200718 ]
719
720 tests = []
721 for directory in test_directories:
722 tests.extend(
723 input_api.canned_checks.GetUnitTestsInDirectory(
724 input_api,
725 output_api,
726 directory,
727 whitelist=[r'.+_test\.py$']))
728 return input_api.RunTests(tests, parallel=True)
729
730
Artem Titova04d1402018-05-11 11:23:00 +0200731def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
732 source_file_filter):
mbonadei38415b22017-04-07 05:38:01 -0700733 """Checks that the namespace google::protobuf has not been used."""
734 files = []
735 pattern = input_api.re.compile(r'google::protobuf')
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200736 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
Artem Titova04d1402018-05-11 11:23:00 +0200737 file_filter = lambda x: (input_api.FilterSourceFile(x)
738 and source_file_filter(x))
739 for f in input_api.AffectedSourceFiles(file_filter):
mbonadei38415b22017-04-07 05:38:01 -0700740 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
741 continue
742 contents = input_api.ReadFile(f)
743 if pattern.search(contents):
744 files.append(f)
745
746 if files:
747 return [output_api.PresubmitError(
748 'Please avoid to use namespace `google::protobuf` directly.\n'
749 'Add a using directive in `%s` and include that header instead.'
750 % proto_utils_path, files)]
751 return []
752
753
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200754def _LicenseHeader(input_api):
755 """Returns the license header regexp."""
756 # Accept any year number from 2003 to the current year
757 current_year = int(input_api.time.strftime('%Y'))
758 allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1)))
759 years_re = '(' + '|'.join(allowed_years) + ')'
760 license_header = (
761 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
762 r'All [Rr]ights [Rr]eserved\.\n'
763 r'.*?\n'
764 r'.*? Use of this source code is governed by a BSD-style license\n'
765 r'.*? that can be found in the LICENSE file in the root of the source\n'
766 r'.*? tree\. An additional intellectual property rights grant can be '
767 r'found\n'
768 r'.*? in the file PATENTS\. All contributing project authors may\n'
769 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
770 ) % {
771 'year': years_re,
772 }
773 return license_header
774
775
charujain9893e252017-09-14 13:33:22 +0200776def CommonChecks(input_api, output_api):
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000777 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34 +0000778 results = []
tkchin42f580e2015-11-26 23:18:23 -0800779 # Filter out files that are in objc or ios dirs from being cpplint-ed since
780 # they do not follow C++ lint rules.
781 black_list = input_api.DEFAULT_BLACK_LIST + (
782 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05 +0000783 r".*objc\.[hcm]+$",
tkchin42f580e2015-11-26 23:18:23 -0800784 )
785 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list)
charujain9893e252017-09-14 13:33:22 +0200786 results.extend(CheckApprovedFilesLintClean(
tkchin42f580e2015-11-26 23:18:23 -0800787 input_api, output_api, source_file_filter))
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200788 results.extend(input_api.canned_checks.CheckLicense(
789 input_api, output_api, _LicenseHeader(input_api)))
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000790 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100791 black_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200792 r'^build[\\\/].*\.py$',
793 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-12 22:43:38 -0700794 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 12:01:17 +0100795 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200796 r'^out.*[\\\/].*\.py$',
797 r'^testing[\\\/].*\.py$',
798 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100799 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 12:21:39 -0800800 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200801 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200802 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200803 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800804
nisse3d21e232016-09-02 03:07:06 -0700805 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200806 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
807 # we need to have different license checks in talk/ and webrtc/ directories.
808 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200809
tkchin3cd9a302016-06-08 12:40:28 -0700810 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
811 # ObjC subdirectories ObjC headers.
812 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100813 # Skip long-lines check for DEPS and GN files.
814 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
Artem Titova04d1402018-05-11 11:23:00 +0200815 # Also we will skip most checks for third_party directory.
Artem Titov42f0d782018-06-27 13:23:17 +0200816 third_party_filter_list = (r'^third_party[\\\/].+',)
tkchin3cd9a302016-06-08 12:40:28 -0700817 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
Artem Titova04d1402018-05-11 11:23:00 +0200818 black_list=build_file_filter_list + objc_filter_list +
819 third_party_filter_list)
tkchin3cd9a302016-06-08 12:40:28 -0700820 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
821 white_list=objc_filter_list)
Artem Titove92675b2018-05-22 10:21:27 +0200822 non_third_party_sources = lambda x: input_api.FilterSourceFile(x,
823 black_list=third_party_filter_list)
824
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000825 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 12:40:28 -0700826 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
827 results.extend(input_api.canned_checks.CheckLongLines(
828 input_api, output_api, maxlen=100,
829 source_file_filter=hundred_char_sources))
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000830 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
Artem Titova04d1402018-05-11 11:23:00 +0200831 input_api, output_api, source_file_filter=non_third_party_sources))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000832 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
Artem Titova04d1402018-05-11 11:23:00 +0200833 input_api, output_api, source_file_filter=non_third_party_sources))
kjellandere5dc62a2016-12-14 00:16:21 -0800834 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
Oleh Prypine0735142018-10-04 11:15:54 +0200835 input_api, output_api, bot_whitelist=[
836 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com'
837 ]))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000838 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
Artem Titova04d1402018-05-11 11:23:00 +0200839 input_api, output_api, source_file_filter=non_third_party_sources))
Yves Gerey87a93532018-06-20 15:51:49 +0200840 results.extend(input_api.canned_checks.CheckPatchFormatted(
841 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200842 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200843 results.extend(CheckNoIOStreamInHeaders(
844 input_api, output_api, source_file_filter=non_third_party_sources))
845 results.extend(CheckNoPragmaOnce(
846 input_api, output_api, source_file_filter=non_third_party_sources))
847 results.extend(CheckNoFRIEND_TEST(
848 input_api, output_api, source_file_filter=non_third_party_sources))
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200849 results.extend(CheckGnChanges(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200850 results.extend(CheckUnwantedDependencies(
851 input_api, output_api, source_file_filter=non_third_party_sources))
852 results.extend(CheckJSONParseErrors(
853 input_api, output_api, source_file_filter=non_third_party_sources))
charujain9893e252017-09-14 13:33:22 +0200854 results.extend(RunPythonTests(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200855 results.extend(CheckUsageOfGoogleProtobufNamespace(
856 input_api, output_api, source_file_filter=non_third_party_sources))
857 results.extend(CheckOrphanHeaders(
858 input_api, output_api, source_file_filter=non_third_party_sources))
859 results.extend(CheckNewlineAtTheEndOfProtoFiles(
860 input_api, output_api, source_file_filter=non_third_party_sources))
861 results.extend(CheckNoStreamUsageIsAdded(
Artem Titov739351d2018-05-11 12:21:36 +0200862 input_api, output_api, non_third_party_sources))
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +0200863 results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200864 results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api))
tzika06bf852018-11-15 20:37:35 +0900865 results.extend(CheckAbslMemoryInclude(
866 input_api, output_api, non_third_party_sources))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200867 return results
868
869
870def CheckApiDepsFileIsUpToDate(input_api, output_api):
Mirko Bonadei90490372018-10-26 13:17:47 +0200871 """Check that 'include_rules' in api/DEPS is up to date.
872
873 The file api/DEPS must be kept up to date in order to avoid to avoid to
874 include internal header from WebRTC's api/ headers.
875
876 This check is focused on ensuring that 'include_rules' contains a deny
877 rule for each root level directory. More focused allow rules can be
878 added to 'specific_include_rules'.
879 """
Mirko Bonadeia418e672018-10-24 13:57:25 +0200880 results = []
881 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
882 with open(api_deps) as f:
883 deps_content = _ParseDeps(f.read())
884
885 include_rules = deps_content.get('include_rules', [])
Mirko Bonadeia418e672018-10-24 13:57:25 +0200886
Mirko Bonadei90490372018-10-26 13:17:47 +0200887 # Only check top level directories affected by the current CL.
888 dirs_to_check = set()
889 for f in input_api.AffectedFiles():
890 path_tokens = [t for t in f.LocalPath().split(os.sep) if t]
891 if len(path_tokens) > 1:
892 if (path_tokens[0] != 'api' and
893 os.path.isdir(os.path.join(input_api.PresubmitLocalPath(),
894 path_tokens[0]))):
895 dirs_to_check.add(path_tokens[0])
Mirko Bonadeia418e672018-10-24 13:57:25 +0200896
Mirko Bonadei90490372018-10-26 13:17:47 +0200897 missing_include_rules = set()
898 for p in dirs_to_check:
Mirko Bonadeia418e672018-10-24 13:57:25 +0200899 rule = '-%s' % p
900 if rule not in include_rules:
Mirko Bonadei90490372018-10-26 13:17:47 +0200901 missing_include_rules.add(rule)
902
Mirko Bonadeia418e672018-10-24 13:57:25 +0200903 if missing_include_rules:
Mirko Bonadei90490372018-10-26 13:17:47 +0200904 error_msg = [
905 'include_rules = [\n',
906 ' ...\n',
907 ]
Mirko Bonadeia418e672018-10-24 13:57:25 +0200908
Mirko Bonadei90490372018-10-26 13:17:47 +0200909 for r in sorted(missing_include_rules):
910 error_msg.append(' "%s",\n' % str(r))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200911
Mirko Bonadei90490372018-10-26 13:17:47 +0200912 error_msg.append(' ...\n')
913 error_msg.append(']\n')
914
Mirko Bonadeia418e672018-10-24 13:57:25 +0200915 results.append(output_api.PresubmitError(
Mirko Bonadei90490372018-10-26 13:17:47 +0200916 'New root level directory detected! WebRTC api/ headers should '
917 'not #include headers from \n'
918 'the new directory, so please update "include_rules" in file\n'
919 '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg))))
920
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000921 return results
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000922
tzika06bf852018-11-15 20:37:35 +0900923def CheckAbslMemoryInclude(input_api, output_api, source_file_filter):
924 pattern = input_api.re.compile(
925 r'^#include\s*"absl/memory/memory.h"', input_api.re.MULTILINE)
926 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
927 and source_file_filter(f))
928
929 files = []
930 for f in input_api.AffectedFiles(
931 include_deletes=False, file_filter=file_filter):
932 contents = input_api.ReadFile(f)
933 if pattern.search(contents):
934 continue
935 for _, line in f.ChangedContents():
936 if 'absl::make_unique' in line:
937 files.append(f)
938 break
939
940 if len(files):
941 return [output_api.PresubmitError(
942 'Please include "absl/memory/memory.h" header for'
943 ' absl::make_unique.\nThis header may or may not be included'
944 ' transitively depends on the C++ standard version.',
945 files)]
946 return []
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000947
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000948def CheckChangeOnUpload(input_api, output_api):
949 results = []
charujain9893e252017-09-14 13:33:22 +0200950 results.extend(CommonChecks(input_api, output_api))
Oleh Prypin920b6532017-10-05 11:28:51 +0200951 results.extend(CheckGnGen(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200952 results.extend(
953 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:34 +0000954 return results
955
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000956
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000957def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:24 +0000958 results = []
charujain9893e252017-09-14 13:33:22 +0200959 results.extend(CommonChecks(input_api, output_api))
960 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
Artem Titov42f0d782018-06-27 13:23:17 +0200961 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000962 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
963 input_api, output_api))
964 results.extend(input_api.canned_checks.CheckChangeHasDescription(
965 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200966 results.extend(CheckChangeHasBugField(input_api, output_api))
967 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:43 +0000968 results.extend(input_api.canned_checks.CheckTreeIsOpen(
969 input_api, output_api,
970 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:24 +0000971 return results
mbonadei74973ed2017-05-09 07:58:05 -0700972
973
Artem Titova04d1402018-05-11 11:23:00 +0200974def CheckOrphanHeaders(input_api, output_api, source_file_filter):
mbonadei74973ed2017-05-09 07:58:05 -0700975 # We need to wait until we have an input_api object and use this
976 # roundabout construct to import prebubmit_checks_lib because this file is
977 # eval-ed and thus doesn't have __file__.
Patrik Höglund2f3f7222017-12-19 11:08:56 +0100978 error_msg = """{} should be listed in {}."""
mbonadei74973ed2017-05-09 07:58:05 -0700979 results = []
Patrik Höglund7e60de22018-01-09 14:22:00 +0100980 orphan_blacklist = [
981 os.path.join('tools_webrtc', 'ios', 'SDK'),
982 ]
Oleh Prypin2f33a562017-10-04 20:17:54 +0200983 with _AddToPath(input_api.os_path.join(
984 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
mbonadei74973ed2017-05-09 07:58:05 -0700985 from check_orphan_headers import GetBuildGnPathFromFilePath
986 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 07:58:05 -0700987
Artem Titova04d1402018-05-11 11:23:00 +0200988 file_filter = lambda x: input_api.FilterSourceFile(
989 x, black_list=orphan_blacklist) and source_file_filter(x)
990 for f in input_api.AffectedSourceFiles(file_filter):
Patrik Höglund7e60de22018-01-09 14:22:00 +0100991 if f.LocalPath().endswith('.h'):
mbonadei74973ed2017-05-09 07:58:05 -0700992 file_path = os.path.abspath(f.LocalPath())
993 root_dir = os.getcwd()
994 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
995 root_dir)
996 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
997 if not in_build_gn:
998 results.append(output_api.PresubmitError(error_msg.format(
Patrik Höglund2f3f7222017-12-19 11:08:56 +0100999 f.LocalPath(), os.path.relpath(gn_file_path))))
mbonadei74973ed2017-05-09 07:58:05 -07001000 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001001
1002
Artem Titove92675b2018-05-22 10:21:27 +02001003def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api, source_file_filter):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001004 """Checks that all .proto files are terminated with a newline."""
1005 error_msg = 'File {} must end with exactly one newline.'
1006 results = []
Artem Titova04d1402018-05-11 11:23:00 +02001007 file_filter = lambda x: input_api.FilterSourceFile(
1008 x, white_list=(r'.+\.proto$',)) and source_file_filter(x)
1009 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001010 file_path = f.LocalPath()
1011 with open(file_path) as f:
1012 lines = f.readlines()
Mirko Bonadeia730c1c2017-09-18 11:33:13 +02001013 if len(lines) > 0 and not lines[-1].endswith('\n'):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001014 results.append(output_api.PresubmitError(error_msg.format(file_path)))
1015 return results
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001016
1017
1018def _ExtractAddRulesFromParsedDeps(parsed_deps):
1019 """Extract the rules that add dependencies from a parsed DEPS file.
1020
1021 Args:
1022 parsed_deps: the locals dictionary from evaluating the DEPS file."""
1023 add_rules = set()
1024 add_rules.update([
1025 rule[1:] for rule in parsed_deps.get('include_rules', [])
1026 if rule.startswith('+') or rule.startswith('!')
1027 ])
1028 for _, rules in parsed_deps.get('specific_include_rules',
1029 {}).iteritems():
1030 add_rules.update([
1031 rule[1:] for rule in rules
1032 if rule.startswith('+') or rule.startswith('!')
1033 ])
1034 return add_rules
1035
1036
1037def _ParseDeps(contents):
1038 """Simple helper for parsing DEPS files."""
1039 # Stubs for handling special syntax in the root DEPS file.
1040 class VarImpl(object):
1041
1042 def __init__(self, local_scope):
1043 self._local_scope = local_scope
1044
1045 def Lookup(self, var_name):
1046 """Implements the Var syntax."""
1047 try:
1048 return self._local_scope['vars'][var_name]
1049 except KeyError:
1050 raise Exception('Var is not defined: %s' % var_name)
1051
1052 local_scope = {}
1053 global_scope = {
1054 'Var': VarImpl(local_scope).Lookup,
1055 }
1056 exec contents in global_scope, local_scope
1057 return local_scope
1058
1059
1060def _CalculateAddedDeps(os_path, old_contents, new_contents):
1061 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
1062 a set of DEPS entries that we should look up.
1063
1064 For a directory (rather than a specific filename) we fake a path to
1065 a specific filename by adding /DEPS. This is chosen as a file that
1066 will seldom or never be subject to per-file include_rules.
1067 """
1068 # We ignore deps entries on auto-generated directories.
1069 auto_generated_dirs = ['grit', 'jni']
1070
1071 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1072 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
1073
1074 added_deps = new_deps.difference(old_deps)
1075
1076 results = set()
1077 for added_dep in added_deps:
1078 if added_dep.split('/')[0] in auto_generated_dirs:
1079 continue
1080 # Assume that a rule that ends in .h is a rule for a specific file.
1081 if added_dep.endswith('.h'):
1082 results.add(added_dep)
1083 else:
1084 results.add(os_path.join(added_dep, 'DEPS'))
1085 return results
1086
1087
1088def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
1089 """When a dependency prefixed with + is added to a DEPS file, we
1090 want to make sure that the change is reviewed by an OWNER of the
1091 target file or directory, to avoid layering violations from being
1092 introduced. This check verifies that this happens.
1093 """
1094 virtual_depended_on_files = set()
1095
1096 file_filter = lambda f: not input_api.re.match(
1097 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1098 for f in input_api.AffectedFiles(include_deletes=False,
1099 file_filter=file_filter):
1100 filename = input_api.os_path.basename(f.LocalPath())
1101 if filename == 'DEPS':
1102 virtual_depended_on_files.update(_CalculateAddedDeps(
1103 input_api.os_path,
1104 '\n'.join(f.OldContents()),
1105 '\n'.join(f.NewContents())))
1106
1107 if not virtual_depended_on_files:
1108 return []
1109
1110 if input_api.is_committing:
1111 if input_api.tbr:
1112 return [output_api.PresubmitNotifyResult(
1113 '--tbr was specified, skipping OWNERS check for DEPS additions')]
1114 if input_api.dry_run:
1115 return [output_api.PresubmitNotifyResult(
1116 'This is a dry run, skipping OWNERS check for DEPS additions')]
1117 if not input_api.change.issue:
1118 return [output_api.PresubmitError(
1119 "DEPS approval by OWNERS check failed: this change has "
1120 "no change number, so we can't check it for approvals.")]
1121 output = output_api.PresubmitError
1122 else:
1123 output = output_api.PresubmitNotifyResult
1124
1125 owners_db = input_api.owners_db
1126 owner_email, reviewers = (
1127 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1128 input_api,
1129 owners_db.email_regexp,
1130 approval_needed=input_api.is_committing))
1131
1132 owner_email = owner_email or input_api.change.author_email
1133
1134 reviewers_plus_owner = set(reviewers)
1135 if owner_email:
1136 reviewers_plus_owner.add(owner_email)
1137 missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
1138 reviewers_plus_owner)
1139
1140 # We strip the /DEPS part that was added by
1141 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1142 # directory.
1143 def StripDeps(path):
1144 start_deps = path.rfind('/DEPS')
1145 if start_deps != -1:
1146 return path[:start_deps]
1147 else:
1148 return path
1149 unapproved_dependencies = ["'+%s'," % StripDeps(path)
1150 for path in missing_files]
1151
1152 if unapproved_dependencies:
1153 output_list = [
1154 output('You need LGTM from owners of depends-on paths in DEPS that were '
1155 'modified in this CL:\n %s' %
1156 '\n '.join(sorted(unapproved_dependencies)))]
1157 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
1158 output_list.append(output(
1159 'Suggested missing target path OWNERS:\n %s' %
1160 '\n '.join(suggested_owners or [])))
1161 return output_list
1162
1163 return []