blob: b5d1e1103741fec6e8e31d8a70dd1d22f173e4d3 [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 Bonadeid2c83322018-03-19 10:31:47 +0000484 if f.LocalPath() == 'PRESUBMIT.py':
485 continue
486 for line_num, line in f.ChangedContents():
487 if ((include_re.search(line) or usage_re.search(line))
488 and not no_presubmit_re.search(line)):
489 errors.append(error_formatter(f.LocalPath(), line_num))
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100490 if errors:
491 return [output_api.PresubmitError(error_msg, errors)]
492 return []
493
Artem Titove92675b2018-05-22 10:21:27 +0200494
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200495def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
496 """Checks that public_deps is not used without a good reason."""
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100497 result = []
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200498 no_presubmit_check_re = input_api.re.compile(
499 r'# no-presubmit-check TODO\(webrtc:8603\)')
500 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
501 'because it doesn\'t map well to downstream build systems.\n'
502 'Used in: %s (line %d).\n'
503 'If you are not adding this code (e.g. you are just moving '
504 'existing code) or you have a good reason, you can add a '
505 'comment on the line that causes the problem:\n\n'
506 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100507 for affected_file in gn_files:
508 for (line_number, affected_line) in affected_file.ChangedContents():
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200509 if ('public_deps' in affected_line
510 and not no_presubmit_check_re.search(affected_line)):
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100511 result.append(
512 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
513 line_number)))
514 return result
515
Artem Titove92675b2018-05-22 10:21:27 +0200516
Patrik Höglund6f491062018-01-11 12:04:23 +0100517def CheckCheckIncludesIsNotUsed(gn_files, output_api):
518 result = []
519 error_msg = ('check_includes overrides are not allowed since it can cause '
520 'incorrect dependencies to form. It effectively means that your '
521 'module can include any .h file without depending on its '
522 'corresponding target. There are some exceptional cases when '
523 'this is allowed: if so, get approval from a .gn owner in the'
524 'root OWNERS file.\n'
525 'Used in: %s (line %d).')
526 for affected_file in gn_files:
527 for (line_number, affected_line) in affected_file.ChangedContents():
528 if 'check_includes' in affected_line:
529 result.append(
530 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
531 line_number)))
532 return result
533
Artem Titove92675b2018-05-22 10:21:27 +0200534
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200535def CheckGnChanges(input_api, output_api):
Artem Titova04d1402018-05-11 11:23:00 +0200536 file_filter = lambda x: (input_api.FilterSourceFile(
Oleh Prypinafe01652017-10-04 15:56:08 +0200537 x, white_list=(r'.+\.(gn|gni)$',),
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200538 black_list=(r'.*/presubmit_checks_lib/testdata/.*',)))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700539
540 gn_files = []
Artem Titova04d1402018-05-11 11:23:00 +0200541 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200542 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 05:51:08 -0700543
544 result = []
545 if gn_files:
charujain9893e252017-09-14 13:33:22 +0200546 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200547 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
548 result.extend(CheckNoPackageBoundaryViolations(input_api, gn_files,
549 output_api))
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200550 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api, output_api))
Patrik Höglund6f491062018-01-11 12:04:23 +0100551 result.extend(CheckCheckIncludesIsNotUsed(gn_files, output_api))
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200552 result.extend(CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api,
553 output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700554 return result
555
Artem Titove92675b2018-05-22 10:21:27 +0200556
Oleh Prypin920b6532017-10-05 11:28:51 +0200557def CheckGnGen(input_api, output_api):
558 """Runs `gn gen --check` with default args to detect mismatches between
559 #includes and dependencies in the BUILD.gn files, as well as general build
560 errors.
561 """
562 with _AddToPath(input_api.os_path.join(
563 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
564 from gn_check import RunGnCheck
Mirko Bonadeid8665442018-09-04 12:17:27 +0200565 errors = RunGnCheck(FindSrcDirPath(input_api.PresubmitLocalPath()))[:5]
Oleh Prypin920b6532017-10-05 11:28:51 +0200566 if errors:
567 return [output_api.PresubmitPromptWarning(
568 'Some #includes do not match the build dependency graph. Please run:\n'
569 ' gn gen --check <out_dir>',
570 long_text='\n\n'.join(errors))]
571 return []
572
Artem Titove92675b2018-05-22 10:21:27 +0200573
Artem Titova04d1402018-05-11 11:23:00 +0200574def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000575 """Runs checkdeps on #include statements added in this
576 change. Breaking - rules is an error, breaking ! rules is a
577 warning.
578 """
579 # Copied from Chromium's src/PRESUBMIT.py.
580
581 # We need to wait until we have an input_api object and use this
582 # roundabout construct to import checkdeps because this file is
583 # eval-ed and thus doesn't have __file__.
Mirko Bonadeid8665442018-09-04 12:17:27 +0200584 src_path = FindSrcDirPath(input_api.PresubmitLocalPath())
585 checkdeps_path = input_api.os_path.join(src_path, 'buildtools', 'checkdeps')
Oleh Prypin2f33a562017-10-04 20:17:54 +0200586 if not os.path.exists(checkdeps_path):
587 return [output_api.PresubmitError(
588 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
589 'download all the DEPS entries?' % checkdeps_path)]
590 with _AddToPath(checkdeps_path):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000591 import checkdeps
592 from cpp_checker import CppChecker
593 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000594
595 added_includes = []
Artem Titova04d1402018-05-11 11:23:00 +0200596 for f in input_api.AffectedFiles(file_filter=source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000597 if not CppChecker.IsCppFile(f.LocalPath()):
598 continue
599
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200600 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000601 added_includes.append([f.LocalPath(), changed_lines])
602
603 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
604
605 error_descriptions = []
606 warning_descriptions = []
607 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
608 added_includes):
609 description_with_path = '%s\n %s' % (path, rule_description)
610 if rule_type == Rule.DISALLOW:
611 error_descriptions.append(description_with_path)
612 else:
613 warning_descriptions.append(description_with_path)
614
615 results = []
616 if error_descriptions:
617 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 03:47:05 -0700618 'You added one or more #includes that violate checkdeps rules.\n'
619 'Check that the DEPS files in these locations contain valid rules.\n'
620 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
621 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000622 error_descriptions))
623 if warning_descriptions:
624 results.append(output_api.PresubmitPromptOrNotify(
625 'You added one or more #includes of files that are temporarily\n'
626 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 03:47:05 -0700627 '#include? See relevant DEPS file(s) for details and contacts.\n'
628 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
629 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000630 warning_descriptions))
631 return results
632
Artem Titove92675b2018-05-22 10:21:27 +0200633
charujain9893e252017-09-14 13:33:22 +0200634def CheckCommitMessageBugEntry(input_api, output_api):
635 """Check that bug entries are well-formed in commit message."""
636 bogus_bug_msg = (
Mirko Bonadei61880182017-10-12 15:12:35 +0200637 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
charujain9893e252017-09-14 13:33:22 +0200638 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.')
639 results = []
Mirko Bonadei61880182017-10-12 15:12:35 +0200640 for bug in input_api.change.BugsFromDescription():
charujain9893e252017-09-14 13:33:22 +0200641 bug = bug.strip()
642 if bug.lower() == 'none':
643 continue
charujain81a58c72017-09-25 13:25:45 +0200644 if 'b/' not in bug and ':' not in bug:
charujain9893e252017-09-14 13:33:22 +0200645 try:
646 if int(bug) > 100000:
647 # Rough indicator for current chromium bugs.
648 prefix_guess = 'chromium'
649 else:
650 prefix_guess = 'webrtc'
Mirko Bonadei61880182017-10-12 15:12:35 +0200651 results.append('Bug entry requires issue tracker prefix, e.g. %s:%s' %
charujain9893e252017-09-14 13:33:22 +0200652 (prefix_guess, bug))
653 except ValueError:
654 results.append(bogus_bug_msg % bug)
charujain81a58c72017-09-25 13:25:45 +0200655 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
charujain9893e252017-09-14 13:33:22 +0200656 results.append(bogus_bug_msg % bug)
657 return [output_api.PresubmitError(r) for r in results]
658
Artem Titove92675b2018-05-22 10:21:27 +0200659
charujain9893e252017-09-14 13:33:22 +0200660def CheckChangeHasBugField(input_api, output_api):
Mirko Bonadei61880182017-10-12 15:12:35 +0200661 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 08:11:16 -0700662
663 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 15:12:35 +0200664 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 08:11:16 -0700665 a bug reference.
Mirko Bonadei61880182017-10-12 15:12:35 +0200666
667 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
668 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 08:11:16 -0700669 """
Mirko Bonadei61880182017-10-12 15:12:35 +0200670 if input_api.change.BugsFromDescription():
kjellanderd1e26a92016-09-19 08:11:16 -0700671 return []
672 else:
673 return [output_api.PresubmitError(
Mirko Bonadei61880182017-10-12 15:12:35 +0200674 'The "Bug: [bug number]" footer is mandatory. Please create a bug and '
kjellanderd1e26a92016-09-19 08:11:16 -0700675 'reference it using either of:\n'
Mirko Bonadei61880182017-10-12 15:12:35 +0200676 ' * https://bugs.webrtc.org - reference it using Bug: webrtc:XXXX\n'
677 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000678
Artem Titove92675b2018-05-22 10:21:27 +0200679
Artem Titova04d1402018-05-11 11:23:00 +0200680def CheckJSONParseErrors(input_api, output_api, source_file_filter):
kjellander569cf942016-02-11 05:02:59 -0800681 """Check that JSON files do not contain syntax errors."""
682
683 def FilterFile(affected_file):
Artem Titova04d1402018-05-11 11:23:00 +0200684 return (input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
685 and source_file_filter(affected_file))
kjellander569cf942016-02-11 05:02:59 -0800686
687 def GetJSONParseError(input_api, filename):
688 try:
689 contents = input_api.ReadFile(filename)
690 input_api.json.loads(contents)
691 except ValueError as e:
692 return e
693 return None
694
695 results = []
696 for affected_file in input_api.AffectedFiles(
697 file_filter=FilterFile, include_deletes=False):
698 parse_error = GetJSONParseError(input_api,
699 affected_file.AbsoluteLocalPath())
700 if parse_error:
701 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
Artem Titove92675b2018-05-22 10:21:27 +0200702 (affected_file.LocalPath(),
703 parse_error)))
kjellander569cf942016-02-11 05:02:59 -0800704 return results
705
706
charujain9893e252017-09-14 13:33:22 +0200707def RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 06:42:43 -0700708 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200709 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
710
711 test_directories = [
Edward Lemur6d01f6d2017-09-14 17:02:01 +0200712 input_api.PresubmitLocalPath(),
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200713 Join('rtc_tools', 'py_event_log_analyzer'),
714 Join('rtc_tools'),
715 Join('audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 05:27:22 -0800716 ] + [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200717 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 05:27:22 -0800718 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200719 ]
720
721 tests = []
722 for directory in test_directories:
723 tests.extend(
724 input_api.canned_checks.GetUnitTestsInDirectory(
725 input_api,
726 output_api,
727 directory,
728 whitelist=[r'.+_test\.py$']))
729 return input_api.RunTests(tests, parallel=True)
730
731
Artem Titova04d1402018-05-11 11:23:00 +0200732def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
733 source_file_filter):
mbonadei38415b22017-04-07 05:38:01 -0700734 """Checks that the namespace google::protobuf has not been used."""
735 files = []
736 pattern = input_api.re.compile(r'google::protobuf')
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200737 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
Artem Titova04d1402018-05-11 11:23:00 +0200738 file_filter = lambda x: (input_api.FilterSourceFile(x)
739 and source_file_filter(x))
740 for f in input_api.AffectedSourceFiles(file_filter):
mbonadei38415b22017-04-07 05:38:01 -0700741 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
742 continue
743 contents = input_api.ReadFile(f)
744 if pattern.search(contents):
745 files.append(f)
746
747 if files:
748 return [output_api.PresubmitError(
749 'Please avoid to use namespace `google::protobuf` directly.\n'
750 'Add a using directive in `%s` and include that header instead.'
751 % proto_utils_path, files)]
752 return []
753
754
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200755def _LicenseHeader(input_api):
756 """Returns the license header regexp."""
757 # Accept any year number from 2003 to the current year
758 current_year = int(input_api.time.strftime('%Y'))
759 allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1)))
760 years_re = '(' + '|'.join(allowed_years) + ')'
761 license_header = (
762 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
763 r'All [Rr]ights [Rr]eserved\.\n'
764 r'.*?\n'
765 r'.*? Use of this source code is governed by a BSD-style license\n'
766 r'.*? that can be found in the LICENSE file in the root of the source\n'
767 r'.*? tree\. An additional intellectual property rights grant can be '
768 r'found\n'
769 r'.*? in the file PATENTS\. All contributing project authors may\n'
770 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
771 ) % {
772 'year': years_re,
773 }
774 return license_header
775
776
charujain9893e252017-09-14 13:33:22 +0200777def CommonChecks(input_api, output_api):
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000778 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34 +0000779 results = []
tkchin42f580e2015-11-26 23:18:23 -0800780 # Filter out files that are in objc or ios dirs from being cpplint-ed since
781 # they do not follow C++ lint rules.
782 black_list = input_api.DEFAULT_BLACK_LIST + (
783 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05 +0000784 r".*objc\.[hcm]+$",
tkchin42f580e2015-11-26 23:18:23 -0800785 )
786 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list)
charujain9893e252017-09-14 13:33:22 +0200787 results.extend(CheckApprovedFilesLintClean(
tkchin42f580e2015-11-26 23:18:23 -0800788 input_api, output_api, source_file_filter))
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200789 results.extend(input_api.canned_checks.CheckLicense(
790 input_api, output_api, _LicenseHeader(input_api)))
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000791 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100792 black_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200793 r'^build[\\\/].*\.py$',
794 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-12 22:43:38 -0700795 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 12:01:17 +0100796 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200797 r'^out.*[\\\/].*\.py$',
798 r'^testing[\\\/].*\.py$',
799 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100800 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 12:21:39 -0800801 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200802 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200803 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200804 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800805
nisse3d21e232016-09-02 03:07:06 -0700806 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200807 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
808 # we need to have different license checks in talk/ and webrtc/ directories.
809 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200810
tkchin3cd9a302016-06-08 12:40:28 -0700811 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
812 # ObjC subdirectories ObjC headers.
813 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100814 # Skip long-lines check for DEPS and GN files.
815 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
Artem Titova04d1402018-05-11 11:23:00 +0200816 # Also we will skip most checks for third_party directory.
Artem Titov42f0d782018-06-27 13:23:17 +0200817 third_party_filter_list = (r'^third_party[\\\/].+',)
tkchin3cd9a302016-06-08 12:40:28 -0700818 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
Artem Titova04d1402018-05-11 11:23:00 +0200819 black_list=build_file_filter_list + objc_filter_list +
820 third_party_filter_list)
tkchin3cd9a302016-06-08 12:40:28 -0700821 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
822 white_list=objc_filter_list)
Artem Titove92675b2018-05-22 10:21:27 +0200823 non_third_party_sources = lambda x: input_api.FilterSourceFile(x,
824 black_list=third_party_filter_list)
825
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000826 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 12:40:28 -0700827 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
828 results.extend(input_api.canned_checks.CheckLongLines(
829 input_api, output_api, maxlen=100,
830 source_file_filter=hundred_char_sources))
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000831 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
Artem Titova04d1402018-05-11 11:23:00 +0200832 input_api, output_api, source_file_filter=non_third_party_sources))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000833 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
Artem Titova04d1402018-05-11 11:23:00 +0200834 input_api, output_api, source_file_filter=non_third_party_sources))
kjellandere5dc62a2016-12-14 00:16:21 -0800835 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
Oleh Prypine0735142018-10-04 11:15:54 +0200836 input_api, output_api, bot_whitelist=[
837 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com'
838 ]))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000839 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
Artem Titova04d1402018-05-11 11:23:00 +0200840 input_api, output_api, source_file_filter=non_third_party_sources))
Yves Gerey87a93532018-06-20 15:51:49 +0200841 results.extend(input_api.canned_checks.CheckPatchFormatted(
842 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200843 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200844 results.extend(CheckNoIOStreamInHeaders(
845 input_api, output_api, source_file_filter=non_third_party_sources))
846 results.extend(CheckNoPragmaOnce(
847 input_api, output_api, source_file_filter=non_third_party_sources))
848 results.extend(CheckNoFRIEND_TEST(
849 input_api, output_api, source_file_filter=non_third_party_sources))
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200850 results.extend(CheckGnChanges(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200851 results.extend(CheckUnwantedDependencies(
852 input_api, output_api, source_file_filter=non_third_party_sources))
853 results.extend(CheckJSONParseErrors(
854 input_api, output_api, source_file_filter=non_third_party_sources))
charujain9893e252017-09-14 13:33:22 +0200855 results.extend(RunPythonTests(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200856 results.extend(CheckUsageOfGoogleProtobufNamespace(
857 input_api, output_api, source_file_filter=non_third_party_sources))
858 results.extend(CheckOrphanHeaders(
859 input_api, output_api, source_file_filter=non_third_party_sources))
860 results.extend(CheckNewlineAtTheEndOfProtoFiles(
861 input_api, output_api, source_file_filter=non_third_party_sources))
862 results.extend(CheckNoStreamUsageIsAdded(
Artem Titov739351d2018-05-11 12:21:36 +0200863 input_api, output_api, non_third_party_sources))
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +0200864 results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200865 results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api))
866 return results
867
868
869def CheckApiDepsFileIsUpToDate(input_api, output_api):
870 results = []
871 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
872 with open(api_deps) as f:
873 deps_content = _ParseDeps(f.read())
874
875 include_rules = deps_content.get('include_rules', [])
876 specific_include_rules = deps_content.get('specific_include_rules', [])
877 cc_include_rules = specific_include_rules.get(r'.*\.cc', [])
878
879 top_level_files = [f for f in os.listdir(input_api.PresubmitLocalPath())
880 if f != 'api' and not f.startswith('.')]
881 top_level_dirs = []
882 for f in top_level_files:
883 if os.path.isdir(os.path.join(input_api.PresubmitLocalPath(), f)):
884 top_level_dirs.append(f)
885
886 missing_include_rules = []
887 for p in top_level_dirs:
888 rule = '-%s' % p
889 if rule not in include_rules:
890 missing_include_rules.append(rule)
891 if missing_include_rules:
892 results.append(output_api.PresubmitError(
893 'Please add the following lines to `include_rules` in file\n'
894 '%s:\n%s' % (api_deps, str(missing_include_rules))))
895
896 missing_cc_include_rules = []
897 non_webrtc_dirs = [
898 'base',
899 'build',
900 'build_overrides',
901 'buildtools',
902 'data',
903 'infra',
904 'out',
905 'resources',
906 'testing',
907 'style-guide',
908 ]
909 webrtc_top_level_dirs = [d for d in top_level_dirs
910 if d not in non_webrtc_dirs]
911
912 for p in webrtc_top_level_dirs:
913 rule = '+%s' % p
914 if rule not in cc_include_rules:
915 missing_cc_include_rules.append(rule)
916 if missing_cc_include_rules:
917 results.append(output_api.PresubmitError(
918 r'Please add the following lines to the `.*\.cc` rule under '
919 '`specific_include_rules` in file\n'
920 '%s:\n%s' % (api_deps, str(missing_cc_include_rules))))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000921 return results
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000922
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000923
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000924def CheckChangeOnUpload(input_api, output_api):
925 results = []
charujain9893e252017-09-14 13:33:22 +0200926 results.extend(CommonChecks(input_api, output_api))
Oleh Prypin920b6532017-10-05 11:28:51 +0200927 results.extend(CheckGnGen(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200928 results.extend(
929 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:34 +0000930 return results
931
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000932
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000933def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:24 +0000934 results = []
charujain9893e252017-09-14 13:33:22 +0200935 results.extend(CommonChecks(input_api, output_api))
936 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
Artem Titov42f0d782018-06-27 13:23:17 +0200937 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000938 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
939 input_api, output_api))
940 results.extend(input_api.canned_checks.CheckChangeHasDescription(
941 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200942 results.extend(CheckChangeHasBugField(input_api, output_api))
943 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:43 +0000944 results.extend(input_api.canned_checks.CheckTreeIsOpen(
945 input_api, output_api,
946 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:24 +0000947 return results
mbonadei74973ed2017-05-09 07:58:05 -0700948
949
Artem Titova04d1402018-05-11 11:23:00 +0200950def CheckOrphanHeaders(input_api, output_api, source_file_filter):
mbonadei74973ed2017-05-09 07:58:05 -0700951 # We need to wait until we have an input_api object and use this
952 # roundabout construct to import prebubmit_checks_lib because this file is
953 # eval-ed and thus doesn't have __file__.
Patrik Höglund2f3f7222017-12-19 11:08:56 +0100954 error_msg = """{} should be listed in {}."""
mbonadei74973ed2017-05-09 07:58:05 -0700955 results = []
Patrik Höglund7e60de22018-01-09 14:22:00 +0100956 orphan_blacklist = [
957 os.path.join('tools_webrtc', 'ios', 'SDK'),
958 ]
Oleh Prypin2f33a562017-10-04 20:17:54 +0200959 with _AddToPath(input_api.os_path.join(
960 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
mbonadei74973ed2017-05-09 07:58:05 -0700961 from check_orphan_headers import GetBuildGnPathFromFilePath
962 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 07:58:05 -0700963
Artem Titova04d1402018-05-11 11:23:00 +0200964 file_filter = lambda x: input_api.FilterSourceFile(
965 x, black_list=orphan_blacklist) and source_file_filter(x)
966 for f in input_api.AffectedSourceFiles(file_filter):
Patrik Höglund7e60de22018-01-09 14:22:00 +0100967 if f.LocalPath().endswith('.h'):
mbonadei74973ed2017-05-09 07:58:05 -0700968 file_path = os.path.abspath(f.LocalPath())
969 root_dir = os.getcwd()
970 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
971 root_dir)
972 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
973 if not in_build_gn:
974 results.append(output_api.PresubmitError(error_msg.format(
Patrik Höglund2f3f7222017-12-19 11:08:56 +0100975 f.LocalPath(), os.path.relpath(gn_file_path))))
mbonadei74973ed2017-05-09 07:58:05 -0700976 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200977
978
Artem Titove92675b2018-05-22 10:21:27 +0200979def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api, source_file_filter):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200980 """Checks that all .proto files are terminated with a newline."""
981 error_msg = 'File {} must end with exactly one newline.'
982 results = []
Artem Titova04d1402018-05-11 11:23:00 +0200983 file_filter = lambda x: input_api.FilterSourceFile(
984 x, white_list=(r'.+\.proto$',)) and source_file_filter(x)
985 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200986 file_path = f.LocalPath()
987 with open(file_path) as f:
988 lines = f.readlines()
Mirko Bonadeia730c1c2017-09-18 11:33:13 +0200989 if len(lines) > 0 and not lines[-1].endswith('\n'):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200990 results.append(output_api.PresubmitError(error_msg.format(file_path)))
991 return results
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +0200992
993
994def _ExtractAddRulesFromParsedDeps(parsed_deps):
995 """Extract the rules that add dependencies from a parsed DEPS file.
996
997 Args:
998 parsed_deps: the locals dictionary from evaluating the DEPS file."""
999 add_rules = set()
1000 add_rules.update([
1001 rule[1:] for rule in parsed_deps.get('include_rules', [])
1002 if rule.startswith('+') or rule.startswith('!')
1003 ])
1004 for _, rules in parsed_deps.get('specific_include_rules',
1005 {}).iteritems():
1006 add_rules.update([
1007 rule[1:] for rule in rules
1008 if rule.startswith('+') or rule.startswith('!')
1009 ])
1010 return add_rules
1011
1012
1013def _ParseDeps(contents):
1014 """Simple helper for parsing DEPS files."""
1015 # Stubs for handling special syntax in the root DEPS file.
1016 class VarImpl(object):
1017
1018 def __init__(self, local_scope):
1019 self._local_scope = local_scope
1020
1021 def Lookup(self, var_name):
1022 """Implements the Var syntax."""
1023 try:
1024 return self._local_scope['vars'][var_name]
1025 except KeyError:
1026 raise Exception('Var is not defined: %s' % var_name)
1027
1028 local_scope = {}
1029 global_scope = {
1030 'Var': VarImpl(local_scope).Lookup,
1031 }
1032 exec contents in global_scope, local_scope
1033 return local_scope
1034
1035
1036def _CalculateAddedDeps(os_path, old_contents, new_contents):
1037 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
1038 a set of DEPS entries that we should look up.
1039
1040 For a directory (rather than a specific filename) we fake a path to
1041 a specific filename by adding /DEPS. This is chosen as a file that
1042 will seldom or never be subject to per-file include_rules.
1043 """
1044 # We ignore deps entries on auto-generated directories.
1045 auto_generated_dirs = ['grit', 'jni']
1046
1047 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1048 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
1049
1050 added_deps = new_deps.difference(old_deps)
1051
1052 results = set()
1053 for added_dep in added_deps:
1054 if added_dep.split('/')[0] in auto_generated_dirs:
1055 continue
1056 # Assume that a rule that ends in .h is a rule for a specific file.
1057 if added_dep.endswith('.h'):
1058 results.add(added_dep)
1059 else:
1060 results.add(os_path.join(added_dep, 'DEPS'))
1061 return results
1062
1063
1064def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
1065 """When a dependency prefixed with + is added to a DEPS file, we
1066 want to make sure that the change is reviewed by an OWNER of the
1067 target file or directory, to avoid layering violations from being
1068 introduced. This check verifies that this happens.
1069 """
1070 virtual_depended_on_files = set()
1071
1072 file_filter = lambda f: not input_api.re.match(
1073 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1074 for f in input_api.AffectedFiles(include_deletes=False,
1075 file_filter=file_filter):
1076 filename = input_api.os_path.basename(f.LocalPath())
1077 if filename == 'DEPS':
1078 virtual_depended_on_files.update(_CalculateAddedDeps(
1079 input_api.os_path,
1080 '\n'.join(f.OldContents()),
1081 '\n'.join(f.NewContents())))
1082
1083 if not virtual_depended_on_files:
1084 return []
1085
1086 if input_api.is_committing:
1087 if input_api.tbr:
1088 return [output_api.PresubmitNotifyResult(
1089 '--tbr was specified, skipping OWNERS check for DEPS additions')]
1090 if input_api.dry_run:
1091 return [output_api.PresubmitNotifyResult(
1092 'This is a dry run, skipping OWNERS check for DEPS additions')]
1093 if not input_api.change.issue:
1094 return [output_api.PresubmitError(
1095 "DEPS approval by OWNERS check failed: this change has "
1096 "no change number, so we can't check it for approvals.")]
1097 output = output_api.PresubmitError
1098 else:
1099 output = output_api.PresubmitNotifyResult
1100
1101 owners_db = input_api.owners_db
1102 owner_email, reviewers = (
1103 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1104 input_api,
1105 owners_db.email_regexp,
1106 approval_needed=input_api.is_committing))
1107
1108 owner_email = owner_email or input_api.change.author_email
1109
1110 reviewers_plus_owner = set(reviewers)
1111 if owner_email:
1112 reviewers_plus_owner.add(owner_email)
1113 missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
1114 reviewers_plus_owner)
1115
1116 # We strip the /DEPS part that was added by
1117 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1118 # directory.
1119 def StripDeps(path):
1120 start_deps = path.rfind('/DEPS')
1121 if start_deps != -1:
1122 return path[:start_deps]
1123 else:
1124 return path
1125 unapproved_dependencies = ["'+%s'," % StripDeps(path)
1126 for path in missing_files]
1127
1128 if unapproved_dependencies:
1129 output_list = [
1130 output('You need LGTM from owners of depends-on paths in DEPS that were '
1131 'modified in this CL:\n %s' %
1132 '\n '.join(sorted(unapproved_dependencies)))]
1133 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
1134 output_list.append(output(
1135 'Suggested missing target path OWNERS:\n %s' %
1136 '\n '.join(suggested_owners or [])))
1137 return output_list
1138
1139 return []