blob: 34b5adf777838390aa588e986513f520a2e305cf [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',
24 'media/engine/fakewebrtcdeviceinfo.h',
25 'media/sctp/sctptransport.cc',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020026 'modules/audio_coding',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020027 'modules/audio_device',
28 'modules/audio_processing',
29 'modules/desktop_capture',
30 'modules/include/module_common_types.h',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020031 'modules/utility',
32 'modules/video_capture',
Steve Anton6c38cc72017-11-29 10:25:58 -080033 'p2p/base/pseudotcp.cc',
34 'p2p/base/pseudotcp.h',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020035 'rtc_base',
36 'sdk/android/src/jni',
37 'sdk/objc',
38 'system_wrappers',
39 'test',
Henrik Kjellander90fd7d82017-05-09 08:30:10 +020040 'tools_webrtc',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020041 'voice_engine',
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +010042]
43
jbauchc4e3ead2016-02-19 00:25:55 -080044# These filters will always be removed, even if the caller specifies a filter
45# set, as they are problematic or broken in some way.
46#
47# Justifications for each filter:
48# - build/c++11 : Rvalue ref checks are unreliable (false positives),
49# include file and feature blacklists are
50# google3-specific.
kjellandere5a87a52016-04-27 02:32:12 -070051# - whitespace/operators: Same as above (doesn't seem sufficient to eliminate
52# all move-related errors).
jbauchc4e3ead2016-02-19 00:25:55 -080053BLACKLIST_LINT_FILTERS = [
54 '-build/c++11',
kjellandere5a87a52016-04-27 02:32:12 -070055 '-whitespace/operators',
jbauchc4e3ead2016-02-19 00:25:55 -080056]
57
kjellanderfd595232015-12-04 02:44:09 -080058# List of directories of "supported" native APIs. That means changes to headers
59# will be done in a compatible way following this scheme:
60# 1. Non-breaking changes are made.
61# 2. The old APIs as marked as deprecated (with comments).
62# 3. Deprecation is announced to discuss-webrtc@googlegroups.com and
63# webrtc-users@google.com (internal list).
64# 4. (later) The deprecated APIs are removed.
kjellander53047c92015-12-02 23:56:14 -080065NATIVE_API_DIRS = (
Karl Wibergef52d8b82017-10-25 13:20:03 +020066 'api', # All subdirectories of api/ are included as well.
Mirko Bonadeia4eeeff2018-01-11 13:16:52 +010067 'media/base',
68 'media/engine',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020069 'modules/audio_device/include',
70 'pc',
kjellanderdd705472016-06-09 11:17:27 -070071)
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020072
kjellanderdd705472016-06-09 11:17:27 -070073# These directories should not be used but are maintained only to avoid breaking
74# some legacy downstream code.
75LEGACY_API_DIRS = (
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020076 'common_audio/include',
77 'modules/audio_coding/include',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020078 'modules/audio_processing/include',
79 'modules/bitrate_controller/include',
80 'modules/congestion_controller/include',
81 'modules/include',
82 'modules/remote_bitrate_estimator/include',
83 'modules/rtp_rtcp/include',
84 'modules/rtp_rtcp/source',
85 'modules/utility/include',
86 'modules/video_coding/codecs/h264/include',
87 'modules/video_coding/codecs/i420/include',
88 'modules/video_coding/codecs/vp8/include',
89 'modules/video_coding/codecs/vp9/include',
90 'modules/video_coding/include',
91 'rtc_base',
92 'system_wrappers/include',
kjellander53047c92015-12-02 23:56:14 -080093)
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020094
Karl Wibergd4f01c12017-11-10 10:55:45 +010095# NOTE: The set of directories in API_DIRS should be the same as those
96# listed in the table in native-api.md.
kjellanderdd705472016-06-09 11:17:27 -070097API_DIRS = NATIVE_API_DIRS[:] + LEGACY_API_DIRS[:]
kjellander53047c92015-12-02 23:56:14 -080098
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020099# TARGET_RE matches a GN target, and extracts the target name and the contents.
100TARGET_RE = re.compile(r'(?P<indent>\s*)\w+\("(?P<target_name>\w+)"\) {'
101 r'(?P<target_contents>.*?)'
102 r'(?P=indent)}',
103 re.MULTILINE | re.DOTALL)
104
105# SOURCES_RE matches a block of sources inside a GN target.
106SOURCES_RE = re.compile(r'sources \+?= \[(?P<sources>.*?)\]',
107 re.MULTILINE | re.DOTALL)
108
109# FILE_PATH_RE matchies a file path.
110FILE_PATH_RE = re.compile(r'"(?P<file_path>(\w|\/)+)(?P<extension>\.\w+)"')
111
kjellander53047c92015-12-02 23:56:14 -0800112
Mirko Bonadeid8665442018-09-04 12:17:27 +0200113def FindSrcDirPath(starting_dir):
114 """Returns the abs path to the src/ dir of the project."""
115 src_dir = starting_dir
116 while os.path.basename(src_dir) != 'src':
117 src_dir = os.path.normpath(os.path.join(src_dir, os.pardir))
118 return src_dir
119
120
Oleh Prypin2f33a562017-10-04 20:17:54 +0200121@contextmanager
122def _AddToPath(*paths):
123 original_sys_path = sys.path
124 sys.path.extend(paths)
125 try:
126 yield
127 finally:
128 # Restore sys.path to what it was before.
129 sys.path = original_sys_path
ehmaldonado4fb97462017-01-30 05:27:22 -0800130
131
charujain9893e252017-09-14 13:33:22 +0200132def VerifyNativeApiHeadersListIsValid(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800133 """Ensures the list of native API header directories is up to date."""
134 non_existing_paths = []
135 native_api_full_paths = [
136 input_api.os_path.join(input_api.PresubmitLocalPath(),
kjellanderdd705472016-06-09 11:17:27 -0700137 *path.split('/')) for path in API_DIRS]
kjellander53047c92015-12-02 23:56:14 -0800138 for path in native_api_full_paths:
139 if not os.path.isdir(path):
140 non_existing_paths.append(path)
141 if non_existing_paths:
142 return [output_api.PresubmitError(
143 'Directories to native API headers have changed which has made the '
144 'list in PRESUBMIT.py outdated.\nPlease update it to the current '
145 'location of our native APIs.',
146 non_existing_paths)]
147 return []
148
Artem Titove92675b2018-05-22 10:21:27 +0200149
kjellanderc88b5d52017-04-05 06:42:43 -0700150API_CHANGE_MSG = """
kwibergeb133022016-04-07 07:41:48 -0700151You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 04:13:23 -0800152 1. Make compatible changes that don't break existing clients. Usually
153 this is done by keeping the existing method signatures unchanged.
154 2. Mark the old stuff as deprecated (see RTC_DEPRECATED macro).
kwibergeb133022016-04-07 07:41:48 -0700155 3. Create a timeline and plan for when the deprecated stuff will be
156 removed. (The amount of time we give users to change their code
157 should be informed by how much work it is for them. If they just
158 need to replace one name with another or something equally
159 simple, 1-2 weeks might be good; if they need to do serious work,
160 up to 3 months may be called for.)
161 4. Update/inform existing downstream code owners to stop using the
162 deprecated stuff. (Send announcements to
163 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
164 5. Remove the deprecated stuff, once the agreed-upon amount of time
165 has passed.
166Related files:
167"""
kjellander53047c92015-12-02 23:56:14 -0800168
Artem Titove92675b2018-05-22 10:21:27 +0200169
charujain9893e252017-09-14 13:33:22 +0200170def CheckNativeApiHeaderChanges(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800171 """Checks to remind proper changing of native APIs."""
172 files = []
Karl Wiberg6bfac032017-10-27 15:14:20 +0200173 source_file_filter = lambda x: input_api.FilterSourceFile(
174 x, white_list=[r'.+\.(gn|gni|h)$'])
175 for f in input_api.AffectedSourceFiles(source_file_filter):
176 for path in API_DIRS:
177 dn = os.path.dirname(f.LocalPath())
178 if path == 'api':
179 # Special case: Subdirectories included.
180 if dn == 'api' or dn.startswith('api/'):
181 files.append(f)
182 else:
183 # Normal case: Subdirectories not included.
184 if dn == path:
185 files.append(f)
kjellander53047c92015-12-02 23:56:14 -0800186
187 if files:
kjellanderc88b5d52017-04-05 06:42:43 -0700188 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
kjellander53047c92015-12-02 23:56:14 -0800189 return []
190
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100191
Artem Titova04d1402018-05-11 11:23:00 +0200192def CheckNoIOStreamInHeaders(input_api, output_api,
193 source_file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000194 """Checks to make sure no .h files include <iostream>."""
195 files = []
196 pattern = input_api.re.compile(r'^#include\s*<iostream>',
197 input_api.re.MULTILINE)
Artem Titova04d1402018-05-11 11:23:00 +0200198 file_filter = lambda x: (input_api.FilterSourceFile(x)
199 and source_file_filter(x))
200 for f in input_api.AffectedSourceFiles(file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000201 if not f.LocalPath().endswith('.h'):
202 continue
203 contents = input_api.ReadFile(f)
204 if pattern.search(contents):
205 files.append(f)
206
207 if len(files):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200208 return [output_api.PresubmitError(
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000209 'Do not #include <iostream> in header files, since it inserts static ' +
210 'initialization into every file including the header. Instead, ' +
211 '#include <ostream>. See http://crbug.com/94794',
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200212 files)]
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000213 return []
214
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000215
Artem Titova04d1402018-05-11 11:23:00 +0200216def CheckNoPragmaOnce(input_api, output_api,
217 source_file_filter):
kjellander6aeef742017-02-20 01:13:18 -0800218 """Make sure that banned functions are not used."""
219 files = []
220 pattern = input_api.re.compile(r'^#pragma\s+once',
221 input_api.re.MULTILINE)
Artem Titova04d1402018-05-11 11:23:00 +0200222 file_filter = lambda x: (input_api.FilterSourceFile(x)
223 and source_file_filter(x))
224 for f in input_api.AffectedSourceFiles(file_filter):
kjellander6aeef742017-02-20 01:13:18 -0800225 if not f.LocalPath().endswith('.h'):
226 continue
227 contents = input_api.ReadFile(f)
228 if pattern.search(contents):
229 files.append(f)
230
231 if files:
232 return [output_api.PresubmitError(
233 'Do not use #pragma once in header files.\n'
234 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
235 files)]
236 return []
237
238
Artem Titova04d1402018-05-11 11:23:00 +0200239def CheckNoFRIEND_TEST(input_api, output_api, # pylint: disable=invalid-name
240 source_file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000241 """Make sure that gtest's FRIEND_TEST() macro is not used, the
242 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
243 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
244 problems = []
245
Artem Titova04d1402018-05-11 11:23:00 +0200246 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
247 and source_file_filter(f))
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000248 for f in input_api.AffectedFiles(file_filter=file_filter):
249 for line_num, line in f.ChangedContents():
250 if 'FRIEND_TEST(' in line:
251 problems.append(' %s:%d' % (f.LocalPath(), line_num))
252
253 if not problems:
254 return []
255 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use '
256 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
257 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
258
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000259
charujain9893e252017-09-14 13:33:22 +0200260def IsLintBlacklisted(blacklist_paths, file_path):
oprypin2aa463f2017-03-23 03:17:02 -0700261 """ Checks if a file is blacklisted for lint check."""
262 for path in blacklist_paths:
263 if file_path == path or os.path.dirname(file_path).startswith(path):
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100264 return True
265 return False
266
267
charujain9893e252017-09-14 13:33:22 +0200268def CheckApprovedFilesLintClean(input_api, output_api,
Artem Titova04d1402018-05-11 11:23:00 +0200269 source_file_filter=None):
oprypin2aa463f2017-03-23 03:17:02 -0700270 """Checks that all new or non-blacklisted .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 13:33:22 +0200271 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000272 depot_tools/presubmit_canned_checks.py but has less filters and only checks
273 added files."""
274 result = []
275
276 # Initialize cpplint.
277 import cpplint
278 # Access to a protected member _XX of a client class
279 # pylint: disable=W0212
280 cpplint._cpplint_state.ResetErrorCounts()
281
jbauchc4e3ead2016-02-19 00:25:55 -0800282 lint_filters = cpplint._Filters()
283 lint_filters.extend(BLACKLIST_LINT_FILTERS)
284 cpplint._SetFilters(','.join(lint_filters))
285
oprypin2aa463f2017-03-23 03:17:02 -0700286 # Create a platform independent blacklist for cpplint.
287 blacklist_paths = [input_api.os_path.join(*path.split('/'))
288 for path in CPPLINT_BLACKLIST]
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100289
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000290 # Use the strictest verbosity level for cpplint.py (level 1) which is the
oprypin2aa463f2017-03-23 03:17:02 -0700291 # default when running cpplint.py from command line. To make it possible to
292 # work with not-yet-converted code, we're only applying it to new (or
293 # moved/renamed) files and files not listed in CPPLINT_BLACKLIST.
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000294 verbosity_level = 1
295 files = []
296 for f in input_api.AffectedSourceFiles(source_file_filter):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200297 # Note that moved/renamed files also count as added.
charujain9893e252017-09-14 13:33:22 +0200298 if f.Action() == 'A' or not IsLintBlacklisted(blacklist_paths,
Artem Titove92675b2018-05-22 10:21:27 +0200299 f.LocalPath()):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000300 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000301
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000302 for file_name in files:
303 cpplint.ProcessFile(file_name, verbosity_level)
304
305 if cpplint._cpplint_state.error_count > 0:
306 if input_api.is_committing:
oprypin8e58d652017-03-21 07:52:41 -0700307 res_type = output_api.PresubmitError
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000308 else:
309 res_type = output_api.PresubmitPromptWarning
310 result = [res_type('Changelist failed cpplint.py check.')]
311
312 return result
313
Artem Titove92675b2018-05-22 10:21:27 +0200314
charujain9893e252017-09-14 13:33:22 +0200315def CheckNoSourcesAbove(input_api, gn_files, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700316 # Disallow referencing source files with paths above the GN file location.
317 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
318 re.MULTILINE | re.DOTALL)
319 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
320 violating_gn_files = set()
321 violating_source_entries = []
322 for gn_file in gn_files:
323 contents = input_api.ReadFile(gn_file)
324 for source_block_match in source_pattern.finditer(contents):
325 # Find all source list entries starting with ../ in the source block
326 # (exclude overrides entries).
327 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
328 source_file = file_list_match.group(1)
329 if 'overrides/' not in source_file:
330 violating_source_entries.append(source_file)
331 violating_gn_files.add(gn_file)
332 if violating_gn_files:
333 return [output_api.PresubmitError(
334 'Referencing source files above the directory of the GN file is not '
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100335 'allowed. Please introduce new GN targets in the proper location '
336 'instead.\n'
ehmaldonado5b1ba082016-09-02 05:51:08 -0700337 'Invalid source entries:\n'
338 '%s\n'
339 'Violating GN files:' % '\n'.join(violating_source_entries),
340 items=violating_gn_files)]
341 return []
342
Artem Titove92675b2018-05-22 10:21:27 +0200343
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200344def CheckNoMixingSources(input_api, gn_files, output_api):
345 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
346
347 See bugs.webrtc.org/7743 for more context.
348 """
Artem Titove92675b2018-05-22 10:21:27 +0200349
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200350 def _MoreThanOneSourceUsed(*sources_lists):
351 sources_used = 0
352 for source_list in sources_lists:
353 if len(source_list):
354 sources_used += 1
355 return sources_used > 1
356
357 errors = defaultdict(lambda: [])
kjellander7439f972016-12-05 22:47:46 -0800358 for gn_file in gn_files:
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200359 gn_file_content = input_api.ReadFile(gn_file)
360 for target_match in TARGET_RE.finditer(gn_file_content):
361 # list_of_sources is a list of tuples of the form
362 # (c_files, cc_files, objc_files) that keeps track of all the sources
363 # defined in a target. A GN target can have more that on definition of
364 # sources (since it supports if/else statements).
365 # E.g.:
366 # rtc_static_library("foo") {
367 # if (is_win) {
368 # sources = [ "foo.cc" ]
369 # } else {
370 # sources = [ "foo.mm" ]
371 # }
372 # }
373 # This is allowed and the presubmit check should support this case.
374 list_of_sources = []
kjellander7439f972016-12-05 22:47:46 -0800375 c_files = []
376 cc_files = []
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200377 objc_files = []
378 target_name = target_match.group('target_name')
379 target_contents = target_match.group('target_contents')
380 for sources_match in SOURCES_RE.finditer(target_contents):
381 if '+=' not in sources_match.group(0):
382 if c_files or cc_files or objc_files:
383 list_of_sources.append((c_files, cc_files, objc_files))
384 c_files = []
385 cc_files = []
386 objc_files = []
387 for file_match in FILE_PATH_RE.finditer(sources_match.group(1)):
388 file_path = file_match.group('file_path')
389 extension = file_match.group('extension')
390 if extension == '.c':
391 c_files.append(file_path + extension)
392 if extension == '.cc':
393 cc_files.append(file_path + extension)
394 if extension in ['.m', '.mm']:
395 objc_files.append(file_path + extension)
396 list_of_sources.append((c_files, cc_files, objc_files))
397 for c_files_list, cc_files_list, objc_files_list in list_of_sources:
398 if _MoreThanOneSourceUsed(c_files_list, cc_files_list, objc_files_list):
399 all_sources = sorted(c_files_list + cc_files_list + objc_files_list)
400 errors[gn_file.LocalPath()].append((target_name, all_sources))
401 if errors:
kjellander7439f972016-12-05 22:47:46 -0800402 return [output_api.PresubmitError(
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200403 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
404 'Please create a separate target for each collection of sources.\n'
kjellander7439f972016-12-05 22:47:46 -0800405 'Mixed sources: \n'
406 '%s\n'
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200407 'Violating GN files:\n%s\n' % (json.dumps(errors, indent=2),
408 '\n'.join(errors.keys())))]
kjellander7439f972016-12-05 22:47:46 -0800409 return []
410
Artem Titove92675b2018-05-22 10:21:27 +0200411
charujain9893e252017-09-14 13:33:22 +0200412def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
ehmaldonado4fb97462017-01-30 05:27:22 -0800413 cwd = input_api.PresubmitLocalPath()
Oleh Prypin2f33a562017-10-04 20:17:54 +0200414 with _AddToPath(input_api.os_path.join(
415 cwd, 'tools_webrtc', 'presubmit_checks_lib')):
416 from check_package_boundaries import CheckPackageBoundaries
417 build_files = [os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files]
418 errors = CheckPackageBoundaries(cwd, build_files)[:5]
419 if errors:
ehmaldonado4fb97462017-01-30 05:27:22 -0800420 return [output_api.PresubmitError(
Oleh Prypin2f33a562017-10-04 20:17:54 +0200421 'There are package boundary violations in the following GN files:',
422 long_text='\n\n'.join(str(err) for err in errors))]
ehmaldonado4fb97462017-01-30 05:27:22 -0800423 return []
424
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100425
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200426def _ReportFileAndLine(filename, line_num):
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100427 """Default error formatter for _FindNewViolationsOfRule."""
428 return '%s (line %s)' % (filename, line_num)
429
430
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200431def CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api, output_api,
432 error_formatter=_ReportFileAndLine):
433 """Make sure that warning suppression flags are not added wihtout a reason."""
434 msg = ('Usage of //build/config/clang:extra_warnings is discouraged '
435 'in WebRTC.\n'
436 'If you are not adding this code (e.g. you are just moving '
437 'existing code) or you want to add an exception,\n'
438 'you can add a comment on the line that causes the problem:\n\n'
439 '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n'
440 '\n'
441 'Affected files:\n')
442 errors = [] # 2-element tuples with (file, line number)
443 clang_warn_re = input_api.re.compile(r'//build/config/clang:extra_warnings')
444 no_presubmit_re = input_api.re.compile(
445 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
446 for f in gn_files:
447 for line_num, line in f.ChangedContents():
448 if clang_warn_re.search(line) and not no_presubmit_re.search(line):
449 errors.append(error_formatter(f.LocalPath(), line_num))
450 if errors:
451 return [output_api.PresubmitError(msg, errors)]
452 return []
453
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100454def CheckNoStreamUsageIsAdded(input_api, output_api,
Artem Titov739351d2018-05-11 12:21:36 +0200455 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200456 error_formatter=_ReportFileAndLine):
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100457 """Make sure that no more dependencies on stringstream are added."""
458 error_msg = ('Usage of <sstream>, <istream> and <ostream> in WebRTC is '
459 'deprecated.\n'
460 'This includes the following types:\n'
461 'std::istringstream, std::ostringstream, std::wistringstream, '
462 'std::wostringstream,\n'
463 'std::wstringstream, std::ostream, std::wostream, std::istream,'
464 'std::wistream,\n'
465 'std::iostream, std::wiostream.\n'
466 'If you are not adding this code (e.g. you are just moving '
467 'existing code),\n'
468 'you can add a comment on the line that causes the problem:\n\n'
469 '#include <sstream> // no-presubmit-check TODO(webrtc:8982)\n'
470 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n'
471 '\n'
Karl Wibergebd01e82018-03-14 15:08:39 +0100472 'If you are adding new code, consider using '
473 'rtc::SimpleStringBuilder\n'
474 '(in rtc_base/strings/string_builder.h).\n'
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100475 'Affected files:\n')
476 errors = [] # 2-element tuples with (file, line number)
477 include_re = input_api.re.compile(r'#include <(i|o|s)stream>')
478 usage_re = input_api.re.compile(r'std::(w|i|o|io|wi|wo|wio)(string)*stream')
479 no_presubmit_re = input_api.re.compile(
Jonas Olsson74395342018-04-03 12:22:07 +0200480 r'// no-presubmit-check TODO\(webrtc:8982\)')
Artem Titova04d1402018-05-11 11:23:00 +0200481 file_filter = lambda x: (input_api.FilterSourceFile(x)
482 and source_file_filter(x))
483 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei44ca9a32018-11-13 11:16:40 +0100484 # Usage of stringstream is allowed under examples/.
485 if f.LocalPath() == 'PRESUBMIT.py' or f.LocalPath().startswith('examples'):
Mirko Bonadeid2c83322018-03-19 10:31:47 +0000486 continue
487 for line_num, line in f.ChangedContents():
488 if ((include_re.search(line) or usage_re.search(line))
489 and not no_presubmit_re.search(line)):
490 errors.append(error_formatter(f.LocalPath(), line_num))
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100491 if errors:
492 return [output_api.PresubmitError(error_msg, errors)]
493 return []
494
Artem Titove92675b2018-05-22 10:21:27 +0200495
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200496def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
497 """Checks that public_deps is not used without a good reason."""
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100498 result = []
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200499 no_presubmit_check_re = input_api.re.compile(
500 r'# no-presubmit-check TODO\(webrtc:8603\)')
501 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
502 'because it doesn\'t map well to downstream build systems.\n'
503 'Used in: %s (line %d).\n'
504 'If you are not adding this code (e.g. you are just moving '
505 'existing code) or you have a good reason, you can add a '
506 'comment on the line that causes the problem:\n\n'
507 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100508 for affected_file in gn_files:
509 for (line_number, affected_line) in affected_file.ChangedContents():
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200510 if ('public_deps' in affected_line
511 and not no_presubmit_check_re.search(affected_line)):
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100512 result.append(
513 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
514 line_number)))
515 return result
516
Artem Titove92675b2018-05-22 10:21:27 +0200517
Patrik Höglund6f491062018-01-11 12:04:23 +0100518def CheckCheckIncludesIsNotUsed(gn_files, output_api):
519 result = []
520 error_msg = ('check_includes overrides are not allowed since it can cause '
521 'incorrect dependencies to form. It effectively means that your '
522 'module can include any .h file without depending on its '
523 'corresponding target. There are some exceptional cases when '
524 'this is allowed: if so, get approval from a .gn owner in the'
525 'root OWNERS file.\n'
526 'Used in: %s (line %d).')
527 for affected_file in gn_files:
528 for (line_number, affected_line) in affected_file.ChangedContents():
529 if 'check_includes' in affected_line:
530 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
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200536def CheckGnChanges(input_api, output_api):
Artem Titova04d1402018-05-11 11:23:00 +0200537 file_filter = lambda x: (input_api.FilterSourceFile(
Oleh Prypinafe01652017-10-04 15:56:08 +0200538 x, white_list=(r'.+\.(gn|gni)$',),
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200539 black_list=(r'.*/presubmit_checks_lib/testdata/.*',)))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700540
541 gn_files = []
Artem Titova04d1402018-05-11 11:23:00 +0200542 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200543 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 05:51:08 -0700544
545 result = []
546 if gn_files:
charujain9893e252017-09-14 13:33:22 +0200547 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200548 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
549 result.extend(CheckNoPackageBoundaryViolations(input_api, gn_files,
550 output_api))
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200551 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api, output_api))
Patrik Höglund6f491062018-01-11 12:04:23 +0100552 result.extend(CheckCheckIncludesIsNotUsed(gn_files, output_api))
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200553 result.extend(CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api,
554 output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700555 return result
556
Artem Titove92675b2018-05-22 10:21:27 +0200557
Oleh Prypin920b6532017-10-05 11:28:51 +0200558def CheckGnGen(input_api, output_api):
559 """Runs `gn gen --check` with default args to detect mismatches between
560 #includes and dependencies in the BUILD.gn files, as well as general build
561 errors.
562 """
563 with _AddToPath(input_api.os_path.join(
564 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
565 from gn_check import RunGnCheck
Mirko Bonadeid8665442018-09-04 12:17:27 +0200566 errors = RunGnCheck(FindSrcDirPath(input_api.PresubmitLocalPath()))[:5]
Oleh Prypin920b6532017-10-05 11:28:51 +0200567 if errors:
568 return [output_api.PresubmitPromptWarning(
569 'Some #includes do not match the build dependency graph. Please run:\n'
570 ' gn gen --check <out_dir>',
571 long_text='\n\n'.join(errors))]
572 return []
573
Artem Titove92675b2018-05-22 10:21:27 +0200574
Artem Titova04d1402018-05-11 11:23:00 +0200575def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000576 """Runs checkdeps on #include statements added in this
577 change. Breaking - rules is an error, breaking ! rules is a
578 warning.
579 """
580 # Copied from Chromium's src/PRESUBMIT.py.
581
582 # We need to wait until we have an input_api object and use this
583 # roundabout construct to import checkdeps because this file is
584 # eval-ed and thus doesn't have __file__.
Mirko Bonadeid8665442018-09-04 12:17:27 +0200585 src_path = FindSrcDirPath(input_api.PresubmitLocalPath())
586 checkdeps_path = input_api.os_path.join(src_path, 'buildtools', 'checkdeps')
Oleh Prypin2f33a562017-10-04 20:17:54 +0200587 if not os.path.exists(checkdeps_path):
588 return [output_api.PresubmitError(
589 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
590 'download all the DEPS entries?' % checkdeps_path)]
591 with _AddToPath(checkdeps_path):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000592 import checkdeps
593 from cpp_checker import CppChecker
594 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000595
596 added_includes = []
Artem Titova04d1402018-05-11 11:23:00 +0200597 for f in input_api.AffectedFiles(file_filter=source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000598 if not CppChecker.IsCppFile(f.LocalPath()):
599 continue
600
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200601 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000602 added_includes.append([f.LocalPath(), changed_lines])
603
604 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
605
606 error_descriptions = []
607 warning_descriptions = []
608 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
609 added_includes):
610 description_with_path = '%s\n %s' % (path, rule_description)
611 if rule_type == Rule.DISALLOW:
612 error_descriptions.append(description_with_path)
613 else:
614 warning_descriptions.append(description_with_path)
615
616 results = []
617 if error_descriptions:
618 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 03:47:05 -0700619 'You added one or more #includes that violate checkdeps rules.\n'
620 'Check that the DEPS files in these locations contain valid rules.\n'
621 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
622 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000623 error_descriptions))
624 if warning_descriptions:
625 results.append(output_api.PresubmitPromptOrNotify(
626 'You added one or more #includes of files that are temporarily\n'
627 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 03:47:05 -0700628 '#include? See relevant DEPS file(s) for details and contacts.\n'
629 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
630 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000631 warning_descriptions))
632 return results
633
Artem Titove92675b2018-05-22 10:21:27 +0200634
charujain9893e252017-09-14 13:33:22 +0200635def CheckCommitMessageBugEntry(input_api, output_api):
636 """Check that bug entries are well-formed in commit message."""
637 bogus_bug_msg = (
Mirko Bonadei61880182017-10-12 15:12:35 +0200638 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
charujain9893e252017-09-14 13:33:22 +0200639 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.')
640 results = []
Mirko Bonadei61880182017-10-12 15:12:35 +0200641 for bug in input_api.change.BugsFromDescription():
charujain9893e252017-09-14 13:33:22 +0200642 bug = bug.strip()
643 if bug.lower() == 'none':
644 continue
charujain81a58c72017-09-25 13:25:45 +0200645 if 'b/' not in bug and ':' not in bug:
charujain9893e252017-09-14 13:33:22 +0200646 try:
647 if int(bug) > 100000:
648 # Rough indicator for current chromium bugs.
649 prefix_guess = 'chromium'
650 else:
651 prefix_guess = 'webrtc'
Mirko Bonadei61880182017-10-12 15:12:35 +0200652 results.append('Bug entry requires issue tracker prefix, e.g. %s:%s' %
charujain9893e252017-09-14 13:33:22 +0200653 (prefix_guess, bug))
654 except ValueError:
655 results.append(bogus_bug_msg % bug)
charujain81a58c72017-09-25 13:25:45 +0200656 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
charujain9893e252017-09-14 13:33:22 +0200657 results.append(bogus_bug_msg % bug)
658 return [output_api.PresubmitError(r) for r in results]
659
Artem Titove92675b2018-05-22 10:21:27 +0200660
charujain9893e252017-09-14 13:33:22 +0200661def CheckChangeHasBugField(input_api, output_api):
Mirko Bonadei61880182017-10-12 15:12:35 +0200662 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 08:11:16 -0700663
664 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 15:12:35 +0200665 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 08:11:16 -0700666 a bug reference.
Mirko Bonadei61880182017-10-12 15:12:35 +0200667
668 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
669 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 08:11:16 -0700670 """
Mirko Bonadei61880182017-10-12 15:12:35 +0200671 if input_api.change.BugsFromDescription():
kjellanderd1e26a92016-09-19 08:11:16 -0700672 return []
673 else:
674 return [output_api.PresubmitError(
Mirko Bonadei61880182017-10-12 15:12:35 +0200675 'The "Bug: [bug number]" footer is mandatory. Please create a bug and '
kjellanderd1e26a92016-09-19 08:11:16 -0700676 'reference it using either of:\n'
Mirko Bonadei61880182017-10-12 15:12:35 +0200677 ' * https://bugs.webrtc.org - reference it using Bug: webrtc:XXXX\n'
678 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000679
Artem Titove92675b2018-05-22 10:21:27 +0200680
Artem Titova04d1402018-05-11 11:23:00 +0200681def CheckJSONParseErrors(input_api, output_api, source_file_filter):
kjellander569cf942016-02-11 05:02:59 -0800682 """Check that JSON files do not contain syntax errors."""
683
684 def FilterFile(affected_file):
Artem Titova04d1402018-05-11 11:23:00 +0200685 return (input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
686 and source_file_filter(affected_file))
kjellander569cf942016-02-11 05:02:59 -0800687
688 def GetJSONParseError(input_api, filename):
689 try:
690 contents = input_api.ReadFile(filename)
691 input_api.json.loads(contents)
692 except ValueError as e:
693 return e
694 return None
695
696 results = []
697 for affected_file in input_api.AffectedFiles(
698 file_filter=FilterFile, include_deletes=False):
699 parse_error = GetJSONParseError(input_api,
700 affected_file.AbsoluteLocalPath())
701 if parse_error:
702 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
Artem Titove92675b2018-05-22 10:21:27 +0200703 (affected_file.LocalPath(),
704 parse_error)))
kjellander569cf942016-02-11 05:02:59 -0800705 return results
706
707
charujain9893e252017-09-14 13:33:22 +0200708def RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 06:42:43 -0700709 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200710 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
711
712 test_directories = [
Edward Lemur6d01f6d2017-09-14 17:02:01 +0200713 input_api.PresubmitLocalPath(),
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200714 Join('rtc_tools', 'py_event_log_analyzer'),
715 Join('rtc_tools'),
716 Join('audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 05:27:22 -0800717 ] + [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200718 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 05:27:22 -0800719 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200720 ]
721
722 tests = []
723 for directory in test_directories:
724 tests.extend(
725 input_api.canned_checks.GetUnitTestsInDirectory(
726 input_api,
727 output_api,
728 directory,
729 whitelist=[r'.+_test\.py$']))
730 return input_api.RunTests(tests, parallel=True)
731
732
Artem Titova04d1402018-05-11 11:23:00 +0200733def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
734 source_file_filter):
mbonadei38415b22017-04-07 05:38:01 -0700735 """Checks that the namespace google::protobuf has not been used."""
736 files = []
737 pattern = input_api.re.compile(r'google::protobuf')
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200738 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
Artem Titova04d1402018-05-11 11:23:00 +0200739 file_filter = lambda x: (input_api.FilterSourceFile(x)
740 and source_file_filter(x))
741 for f in input_api.AffectedSourceFiles(file_filter):
mbonadei38415b22017-04-07 05:38:01 -0700742 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
743 continue
744 contents = input_api.ReadFile(f)
745 if pattern.search(contents):
746 files.append(f)
747
748 if files:
749 return [output_api.PresubmitError(
750 'Please avoid to use namespace `google::protobuf` directly.\n'
751 'Add a using directive in `%s` and include that header instead.'
752 % proto_utils_path, files)]
753 return []
754
755
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200756def _LicenseHeader(input_api):
757 """Returns the license header regexp."""
758 # Accept any year number from 2003 to the current year
759 current_year = int(input_api.time.strftime('%Y'))
760 allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1)))
761 years_re = '(' + '|'.join(allowed_years) + ')'
762 license_header = (
763 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
764 r'All [Rr]ights [Rr]eserved\.\n'
765 r'.*?\n'
766 r'.*? Use of this source code is governed by a BSD-style license\n'
767 r'.*? that can be found in the LICENSE file in the root of the source\n'
768 r'.*? tree\. An additional intellectual property rights grant can be '
769 r'found\n'
770 r'.*? in the file PATENTS\. All contributing project authors may\n'
771 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
772 ) % {
773 'year': years_re,
774 }
775 return license_header
776
777
charujain9893e252017-09-14 13:33:22 +0200778def CommonChecks(input_api, output_api):
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000779 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34 +0000780 results = []
tkchin42f580e2015-11-26 23:18:23 -0800781 # Filter out files that are in objc or ios dirs from being cpplint-ed since
782 # they do not follow C++ lint rules.
783 black_list = input_api.DEFAULT_BLACK_LIST + (
784 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05 +0000785 r".*objc\.[hcm]+$",
tkchin42f580e2015-11-26 23:18:23 -0800786 )
787 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list)
charujain9893e252017-09-14 13:33:22 +0200788 results.extend(CheckApprovedFilesLintClean(
tkchin42f580e2015-11-26 23:18:23 -0800789 input_api, output_api, source_file_filter))
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200790 results.extend(input_api.canned_checks.CheckLicense(
791 input_api, output_api, _LicenseHeader(input_api)))
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000792 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100793 black_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200794 r'^build[\\\/].*\.py$',
795 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-12 22:43:38 -0700796 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 12:01:17 +0100797 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200798 r'^out.*[\\\/].*\.py$',
799 r'^testing[\\\/].*\.py$',
800 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100801 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 12:21:39 -0800802 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200803 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200804 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200805 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800806
nisse3d21e232016-09-02 03:07:06 -0700807 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200808 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
809 # we need to have different license checks in talk/ and webrtc/ directories.
810 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200811
tkchin3cd9a302016-06-08 12:40:28 -0700812 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
813 # ObjC subdirectories ObjC headers.
814 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100815 # Skip long-lines check for DEPS and GN files.
816 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
Artem Titova04d1402018-05-11 11:23:00 +0200817 # Also we will skip most checks for third_party directory.
Artem Titov42f0d782018-06-27 13:23:17 +0200818 third_party_filter_list = (r'^third_party[\\\/].+',)
tkchin3cd9a302016-06-08 12:40:28 -0700819 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
Artem Titova04d1402018-05-11 11:23:00 +0200820 black_list=build_file_filter_list + objc_filter_list +
821 third_party_filter_list)
tkchin3cd9a302016-06-08 12:40:28 -0700822 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
823 white_list=objc_filter_list)
Artem Titove92675b2018-05-22 10:21:27 +0200824 non_third_party_sources = lambda x: input_api.FilterSourceFile(x,
825 black_list=third_party_filter_list)
826
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000827 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 12:40:28 -0700828 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
829 results.extend(input_api.canned_checks.CheckLongLines(
830 input_api, output_api, maxlen=100,
831 source_file_filter=hundred_char_sources))
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000832 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
Artem Titova04d1402018-05-11 11:23:00 +0200833 input_api, output_api, source_file_filter=non_third_party_sources))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000834 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
Artem Titova04d1402018-05-11 11:23:00 +0200835 input_api, output_api, source_file_filter=non_third_party_sources))
kjellandere5dc62a2016-12-14 00:16:21 -0800836 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
Oleh Prypine0735142018-10-04 11:15:54 +0200837 input_api, output_api, bot_whitelist=[
838 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com'
839 ]))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000840 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
Artem Titova04d1402018-05-11 11:23:00 +0200841 input_api, output_api, source_file_filter=non_third_party_sources))
Yves Gerey87a93532018-06-20 15:51:49 +0200842 results.extend(input_api.canned_checks.CheckPatchFormatted(
843 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200844 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200845 results.extend(CheckNoIOStreamInHeaders(
846 input_api, output_api, source_file_filter=non_third_party_sources))
847 results.extend(CheckNoPragmaOnce(
848 input_api, output_api, source_file_filter=non_third_party_sources))
849 results.extend(CheckNoFRIEND_TEST(
850 input_api, output_api, source_file_filter=non_third_party_sources))
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200851 results.extend(CheckGnChanges(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200852 results.extend(CheckUnwantedDependencies(
853 input_api, output_api, source_file_filter=non_third_party_sources))
854 results.extend(CheckJSONParseErrors(
855 input_api, output_api, source_file_filter=non_third_party_sources))
charujain9893e252017-09-14 13:33:22 +0200856 results.extend(RunPythonTests(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200857 results.extend(CheckUsageOfGoogleProtobufNamespace(
858 input_api, output_api, source_file_filter=non_third_party_sources))
859 results.extend(CheckOrphanHeaders(
860 input_api, output_api, source_file_filter=non_third_party_sources))
861 results.extend(CheckNewlineAtTheEndOfProtoFiles(
862 input_api, output_api, source_file_filter=non_third_party_sources))
863 results.extend(CheckNoStreamUsageIsAdded(
Artem Titov739351d2018-05-11 12:21:36 +0200864 input_api, output_api, non_third_party_sources))
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +0200865 results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200866 results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api))
tzika06bf852018-11-15 20:37:35 +0900867 results.extend(CheckAbslMemoryInclude(
868 input_api, output_api, non_third_party_sources))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200869 return results
870
871
872def CheckApiDepsFileIsUpToDate(input_api, output_api):
Mirko Bonadei90490372018-10-26 13:17:47 +0200873 """Check that 'include_rules' in api/DEPS is up to date.
874
875 The file api/DEPS must be kept up to date in order to avoid to avoid to
876 include internal header from WebRTC's api/ headers.
877
878 This check is focused on ensuring that 'include_rules' contains a deny
879 rule for each root level directory. More focused allow rules can be
880 added to 'specific_include_rules'.
881 """
Mirko Bonadeia418e672018-10-24 13:57:25 +0200882 results = []
883 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
884 with open(api_deps) as f:
885 deps_content = _ParseDeps(f.read())
886
887 include_rules = deps_content.get('include_rules', [])
Mirko Bonadeia418e672018-10-24 13:57:25 +0200888
Mirko Bonadei90490372018-10-26 13:17:47 +0200889 # Only check top level directories affected by the current CL.
890 dirs_to_check = set()
891 for f in input_api.AffectedFiles():
892 path_tokens = [t for t in f.LocalPath().split(os.sep) if t]
893 if len(path_tokens) > 1:
894 if (path_tokens[0] != 'api' and
895 os.path.isdir(os.path.join(input_api.PresubmitLocalPath(),
896 path_tokens[0]))):
897 dirs_to_check.add(path_tokens[0])
Mirko Bonadeia418e672018-10-24 13:57:25 +0200898
Mirko Bonadei90490372018-10-26 13:17:47 +0200899 missing_include_rules = set()
900 for p in dirs_to_check:
Mirko Bonadeia418e672018-10-24 13:57:25 +0200901 rule = '-%s' % p
902 if rule not in include_rules:
Mirko Bonadei90490372018-10-26 13:17:47 +0200903 missing_include_rules.add(rule)
904
Mirko Bonadeia418e672018-10-24 13:57:25 +0200905 if missing_include_rules:
Mirko Bonadei90490372018-10-26 13:17:47 +0200906 error_msg = [
907 'include_rules = [\n',
908 ' ...\n',
909 ]
Mirko Bonadeia418e672018-10-24 13:57:25 +0200910
Mirko Bonadei90490372018-10-26 13:17:47 +0200911 for r in sorted(missing_include_rules):
912 error_msg.append(' "%s",\n' % str(r))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200913
Mirko Bonadei90490372018-10-26 13:17:47 +0200914 error_msg.append(' ...\n')
915 error_msg.append(']\n')
916
Mirko Bonadeia418e672018-10-24 13:57:25 +0200917 results.append(output_api.PresubmitError(
Mirko Bonadei90490372018-10-26 13:17:47 +0200918 'New root level directory detected! WebRTC api/ headers should '
919 'not #include headers from \n'
920 'the new directory, so please update "include_rules" in file\n'
921 '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg))))
922
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000923 return results
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000924
tzika06bf852018-11-15 20:37:35 +0900925def CheckAbslMemoryInclude(input_api, output_api, source_file_filter):
926 pattern = input_api.re.compile(
927 r'^#include\s*"absl/memory/memory.h"', input_api.re.MULTILINE)
928 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
929 and source_file_filter(f))
930
931 files = []
932 for f in input_api.AffectedFiles(
933 include_deletes=False, file_filter=file_filter):
934 contents = input_api.ReadFile(f)
935 if pattern.search(contents):
936 continue
937 for _, line in f.ChangedContents():
938 if 'absl::make_unique' in line:
939 files.append(f)
940 break
941
942 if len(files):
943 return [output_api.PresubmitError(
944 'Please include "absl/memory/memory.h" header for'
945 ' absl::make_unique.\nThis header may or may not be included'
946 ' transitively depends on the C++ standard version.',
947 files)]
948 return []
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000949
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000950def CheckChangeOnUpload(input_api, output_api):
951 results = []
charujain9893e252017-09-14 13:33:22 +0200952 results.extend(CommonChecks(input_api, output_api))
Oleh Prypin920b6532017-10-05 11:28:51 +0200953 results.extend(CheckGnGen(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200954 results.extend(
955 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:34 +0000956 return results
957
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000958
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000959def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:24 +0000960 results = []
charujain9893e252017-09-14 13:33:22 +0200961 results.extend(CommonChecks(input_api, output_api))
962 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
Artem Titov42f0d782018-06-27 13:23:17 +0200963 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000964 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
965 input_api, output_api))
966 results.extend(input_api.canned_checks.CheckChangeHasDescription(
967 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200968 results.extend(CheckChangeHasBugField(input_api, output_api))
969 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:43 +0000970 results.extend(input_api.canned_checks.CheckTreeIsOpen(
971 input_api, output_api,
972 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:24 +0000973 return results
mbonadei74973ed2017-05-09 07:58:05 -0700974
975
Artem Titova04d1402018-05-11 11:23:00 +0200976def CheckOrphanHeaders(input_api, output_api, source_file_filter):
mbonadei74973ed2017-05-09 07:58:05 -0700977 # We need to wait until we have an input_api object and use this
978 # roundabout construct to import prebubmit_checks_lib because this file is
979 # eval-ed and thus doesn't have __file__.
Patrik Höglund2f3f7222017-12-19 11:08:56 +0100980 error_msg = """{} should be listed in {}."""
mbonadei74973ed2017-05-09 07:58:05 -0700981 results = []
Patrik Höglund7e60de22018-01-09 14:22:00 +0100982 orphan_blacklist = [
983 os.path.join('tools_webrtc', 'ios', 'SDK'),
984 ]
Oleh Prypin2f33a562017-10-04 20:17:54 +0200985 with _AddToPath(input_api.os_path.join(
986 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
mbonadei74973ed2017-05-09 07:58:05 -0700987 from check_orphan_headers import GetBuildGnPathFromFilePath
988 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 07:58:05 -0700989
Artem Titova04d1402018-05-11 11:23:00 +0200990 file_filter = lambda x: input_api.FilterSourceFile(
991 x, black_list=orphan_blacklist) and source_file_filter(x)
992 for f in input_api.AffectedSourceFiles(file_filter):
Patrik Höglund7e60de22018-01-09 14:22:00 +0100993 if f.LocalPath().endswith('.h'):
mbonadei74973ed2017-05-09 07:58:05 -0700994 file_path = os.path.abspath(f.LocalPath())
995 root_dir = os.getcwd()
996 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
997 root_dir)
998 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
999 if not in_build_gn:
1000 results.append(output_api.PresubmitError(error_msg.format(
Patrik Höglund2f3f7222017-12-19 11:08:56 +01001001 f.LocalPath(), os.path.relpath(gn_file_path))))
mbonadei74973ed2017-05-09 07:58:05 -07001002 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001003
1004
Artem Titove92675b2018-05-22 10:21:27 +02001005def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api, source_file_filter):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001006 """Checks that all .proto files are terminated with a newline."""
1007 error_msg = 'File {} must end with exactly one newline.'
1008 results = []
Artem Titova04d1402018-05-11 11:23:00 +02001009 file_filter = lambda x: input_api.FilterSourceFile(
1010 x, white_list=(r'.+\.proto$',)) and source_file_filter(x)
1011 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001012 file_path = f.LocalPath()
1013 with open(file_path) as f:
1014 lines = f.readlines()
Mirko Bonadeia730c1c2017-09-18 11:33:13 +02001015 if len(lines) > 0 and not lines[-1].endswith('\n'):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001016 results.append(output_api.PresubmitError(error_msg.format(file_path)))
1017 return results
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001018
1019
1020def _ExtractAddRulesFromParsedDeps(parsed_deps):
1021 """Extract the rules that add dependencies from a parsed DEPS file.
1022
1023 Args:
1024 parsed_deps: the locals dictionary from evaluating the DEPS file."""
1025 add_rules = set()
1026 add_rules.update([
1027 rule[1:] for rule in parsed_deps.get('include_rules', [])
1028 if rule.startswith('+') or rule.startswith('!')
1029 ])
1030 for _, rules in parsed_deps.get('specific_include_rules',
1031 {}).iteritems():
1032 add_rules.update([
1033 rule[1:] for rule in rules
1034 if rule.startswith('+') or rule.startswith('!')
1035 ])
1036 return add_rules
1037
1038
1039def _ParseDeps(contents):
1040 """Simple helper for parsing DEPS files."""
1041 # Stubs for handling special syntax in the root DEPS file.
1042 class VarImpl(object):
1043
1044 def __init__(self, local_scope):
1045 self._local_scope = local_scope
1046
1047 def Lookup(self, var_name):
1048 """Implements the Var syntax."""
1049 try:
1050 return self._local_scope['vars'][var_name]
1051 except KeyError:
1052 raise Exception('Var is not defined: %s' % var_name)
1053
1054 local_scope = {}
1055 global_scope = {
1056 'Var': VarImpl(local_scope).Lookup,
1057 }
1058 exec contents in global_scope, local_scope
1059 return local_scope
1060
1061
1062def _CalculateAddedDeps(os_path, old_contents, new_contents):
1063 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
1064 a set of DEPS entries that we should look up.
1065
1066 For a directory (rather than a specific filename) we fake a path to
1067 a specific filename by adding /DEPS. This is chosen as a file that
1068 will seldom or never be subject to per-file include_rules.
1069 """
1070 # We ignore deps entries on auto-generated directories.
1071 auto_generated_dirs = ['grit', 'jni']
1072
1073 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1074 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
1075
1076 added_deps = new_deps.difference(old_deps)
1077
1078 results = set()
1079 for added_dep in added_deps:
1080 if added_dep.split('/')[0] in auto_generated_dirs:
1081 continue
1082 # Assume that a rule that ends in .h is a rule for a specific file.
1083 if added_dep.endswith('.h'):
1084 results.add(added_dep)
1085 else:
1086 results.add(os_path.join(added_dep, 'DEPS'))
1087 return results
1088
1089
1090def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
1091 """When a dependency prefixed with + is added to a DEPS file, we
1092 want to make sure that the change is reviewed by an OWNER of the
1093 target file or directory, to avoid layering violations from being
1094 introduced. This check verifies that this happens.
1095 """
1096 virtual_depended_on_files = set()
1097
1098 file_filter = lambda f: not input_api.re.match(
1099 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1100 for f in input_api.AffectedFiles(include_deletes=False,
1101 file_filter=file_filter):
1102 filename = input_api.os_path.basename(f.LocalPath())
1103 if filename == 'DEPS':
1104 virtual_depended_on_files.update(_CalculateAddedDeps(
1105 input_api.os_path,
1106 '\n'.join(f.OldContents()),
1107 '\n'.join(f.NewContents())))
1108
1109 if not virtual_depended_on_files:
1110 return []
1111
1112 if input_api.is_committing:
1113 if input_api.tbr:
1114 return [output_api.PresubmitNotifyResult(
1115 '--tbr was specified, skipping OWNERS check for DEPS additions')]
1116 if input_api.dry_run:
1117 return [output_api.PresubmitNotifyResult(
1118 'This is a dry run, skipping OWNERS check for DEPS additions')]
1119 if not input_api.change.issue:
1120 return [output_api.PresubmitError(
1121 "DEPS approval by OWNERS check failed: this change has "
1122 "no change number, so we can't check it for approvals.")]
1123 output = output_api.PresubmitError
1124 else:
1125 output = output_api.PresubmitNotifyResult
1126
1127 owners_db = input_api.owners_db
1128 owner_email, reviewers = (
1129 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1130 input_api,
1131 owners_db.email_regexp,
1132 approval_needed=input_api.is_committing))
1133
1134 owner_email = owner_email or input_api.change.author_email
1135
1136 reviewers_plus_owner = set(reviewers)
1137 if owner_email:
1138 reviewers_plus_owner.add(owner_email)
1139 missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
1140 reviewers_plus_owner)
1141
1142 # We strip the /DEPS part that was added by
1143 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1144 # directory.
1145 def StripDeps(path):
1146 start_deps = path.rfind('/DEPS')
1147 if start_deps != -1:
1148 return path[:start_deps]
1149 else:
1150 return path
1151 unapproved_dependencies = ["'+%s'," % StripDeps(path)
1152 for path in missing_files]
1153
1154 if unapproved_dependencies:
1155 output_list = [
1156 output('You need LGTM from owners of depends-on paths in DEPS that were '
1157 'modified in this CL:\n %s' %
1158 '\n '.join(sorted(unapproved_dependencies)))]
1159 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
1160 output_list.append(output(
1161 'Suggested missing target path OWNERS:\n %s' %
1162 '\n '.join(suggested_owners or [])))
1163 return output_list
1164
1165 return []