blob: b5d4534c0b89defcbd195f3e2a32788bef25e6ab [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.
Mirko Bonadeifc17a782020-06-30 14:31:37 +020017CPPLINT_EXCEPTIONS = [
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018 'api/video_codecs/video_decoder.h',
19 'common_types.cc',
20 'common_types.h',
21 'examples/objc',
Amit Hilbuchce7032b2019-01-22 16:19:35 -080022 'media/base/stream_params.h',
23 'media/base/video_common.h',
24 'media/sctp/sctp_transport.cc',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020025 'modules/audio_coding',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020026 'modules/audio_device',
27 'modules/audio_processing',
28 'modules/desktop_capture',
29 'modules/include/module_common_types.h',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020030 'modules/utility',
31 'modules/video_capture',
Amit Hilbuchce7032b2019-01-22 16:19:35 -080032 'p2p/base/pseudo_tcp.cc',
33 'p2p/base/pseudo_tcp.h',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020034 'rtc_base',
35 'sdk/android/src/jni',
36 'sdk/objc',
37 'system_wrappers',
38 'test',
Henrik Kjellander90fd7d82017-05-09 08:30:10 +020039 'tools_webrtc',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020040 'voice_engine',
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +010041]
42
jbauchc4e3ead2016-02-19 00:25:55 -080043# These filters will always be removed, even if the caller specifies a filter
44# set, as they are problematic or broken in some way.
45#
46# Justifications for each filter:
47# - build/c++11 : Rvalue ref checks are unreliable (false positives),
Mirko Bonadeifc17a782020-06-30 14:31:37 +020048# include file and feature blocklists are
jbauchc4e3ead2016-02-19 00:25:55 -080049# google3-specific.
Mirko Bonadeie92e2862020-05-29 15:23:09 +020050# - runtime/references : Mutable references are not banned by the Google
51# C++ style guide anymore (starting from May 2020).
kjellandere5a87a52016-04-27 02:32:12 -070052# - whitespace/operators: Same as above (doesn't seem sufficient to eliminate
53# all move-related errors).
Mirko Bonadeifc17a782020-06-30 14:31:37 +020054DISABLED_LINT_FILTERS = [
jbauchc4e3ead2016-02-19 00:25:55 -080055 '-build/c++11',
Mirko Bonadeie92e2862020-05-29 15:23:09 +020056 '-runtime/references',
kjellandere5a87a52016-04-27 02:32:12 -070057 '-whitespace/operators',
jbauchc4e3ead2016-02-19 00:25:55 -080058]
59
kjellanderfd595232015-12-04 02:44:09 -080060# List of directories of "supported" native APIs. That means changes to headers
61# will be done in a compatible way following this scheme:
62# 1. Non-breaking changes are made.
63# 2. The old APIs as marked as deprecated (with comments).
64# 3. Deprecation is announced to discuss-webrtc@googlegroups.com and
65# webrtc-users@google.com (internal list).
66# 4. (later) The deprecated APIs are removed.
kjellander53047c92015-12-02 23:56:14 -080067NATIVE_API_DIRS = (
Karl Wibergef52d8b82017-10-25 13:20:03 +020068 'api', # All subdirectories of api/ are included as well.
Mirko Bonadeia4eeeff2018-01-11 13:16:52 +010069 'media/base',
70 'media/engine',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020071 'modules/audio_device/include',
72 'pc',
kjellanderdd705472016-06-09 11:17:27 -070073)
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020074
kjellanderdd705472016-06-09 11:17:27 -070075# These directories should not be used but are maintained only to avoid breaking
76# some legacy downstream code.
77LEGACY_API_DIRS = (
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020078 'common_audio/include',
79 'modules/audio_coding/include',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020080 'modules/audio_processing/include',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020081 'modules/congestion_controller/include',
82 'modules/include',
83 'modules/remote_bitrate_estimator/include',
84 'modules/rtp_rtcp/include',
85 'modules/rtp_rtcp/source',
86 'modules/utility/include',
87 'modules/video_coding/codecs/h264/include',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020088 '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.
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200100TARGET_RE = re.compile(
101 r'(?P<indent>\s*)(?P<target_type>\w+)\("(?P<target_name>\w+)"\) {'
102 r'(?P<target_contents>.*?)'
103 r'(?P=indent)}',
104 re.MULTILINE | re.DOTALL)
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200105
106# SOURCES_RE matches a block of sources inside a GN target.
107SOURCES_RE = re.compile(r'sources \+?= \[(?P<sources>.*?)\]',
108 re.MULTILINE | re.DOTALL)
109
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200110# DEPS_RE matches a block of sources inside a GN target.
111DEPS_RE = re.compile(r'\bdeps \+?= \[(?P<deps>.*?)\]',
112 re.MULTILINE | re.DOTALL)
113
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200114# FILE_PATH_RE matchies a file path.
115FILE_PATH_RE = re.compile(r'"(?P<file_path>(\w|\/)+)(?P<extension>\.\w+)"')
116
kjellander53047c92015-12-02 23:56:14 -0800117
Mirko Bonadeid8665442018-09-04 12:17:27 +0200118def FindSrcDirPath(starting_dir):
119 """Returns the abs path to the src/ dir of the project."""
120 src_dir = starting_dir
121 while os.path.basename(src_dir) != 'src':
122 src_dir = os.path.normpath(os.path.join(src_dir, os.pardir))
123 return src_dir
124
125
Oleh Prypin2f33a562017-10-04 20:17:54 +0200126@contextmanager
127def _AddToPath(*paths):
128 original_sys_path = sys.path
129 sys.path.extend(paths)
130 try:
131 yield
132 finally:
133 # Restore sys.path to what it was before.
134 sys.path = original_sys_path
ehmaldonado4fb97462017-01-30 05:27:22 -0800135
136
charujain9893e252017-09-14 13:33:22 +0200137def VerifyNativeApiHeadersListIsValid(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800138 """Ensures the list of native API header directories is up to date."""
139 non_existing_paths = []
140 native_api_full_paths = [
141 input_api.os_path.join(input_api.PresubmitLocalPath(),
kjellanderdd705472016-06-09 11:17:27 -0700142 *path.split('/')) for path in API_DIRS]
kjellander53047c92015-12-02 23:56:14 -0800143 for path in native_api_full_paths:
144 if not os.path.isdir(path):
145 non_existing_paths.append(path)
146 if non_existing_paths:
147 return [output_api.PresubmitError(
148 'Directories to native API headers have changed which has made the '
149 'list in PRESUBMIT.py outdated.\nPlease update it to the current '
150 'location of our native APIs.',
151 non_existing_paths)]
152 return []
153
Artem Titove92675b2018-05-22 10:21:27 +0200154
kjellanderc88b5d52017-04-05 06:42:43 -0700155API_CHANGE_MSG = """
kwibergeb133022016-04-07 07:41:48 -0700156You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 04:13:23 -0800157 1. Make compatible changes that don't break existing clients. Usually
158 this is done by keeping the existing method signatures unchanged.
159 2. Mark the old stuff as deprecated (see RTC_DEPRECATED macro).
kwibergeb133022016-04-07 07:41:48 -0700160 3. Create a timeline and plan for when the deprecated stuff will be
161 removed. (The amount of time we give users to change their code
162 should be informed by how much work it is for them. If they just
163 need to replace one name with another or something equally
164 simple, 1-2 weeks might be good; if they need to do serious work,
165 up to 3 months may be called for.)
166 4. Update/inform existing downstream code owners to stop using the
167 deprecated stuff. (Send announcements to
168 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
169 5. Remove the deprecated stuff, once the agreed-upon amount of time
170 has passed.
171Related files:
172"""
kjellander53047c92015-12-02 23:56:14 -0800173
Artem Titove92675b2018-05-22 10:21:27 +0200174
charujain9893e252017-09-14 13:33:22 +0200175def CheckNativeApiHeaderChanges(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800176 """Checks to remind proper changing of native APIs."""
177 files = []
Karl Wiberg6bfac032017-10-27 15:14:20 +0200178 source_file_filter = lambda x: input_api.FilterSourceFile(
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200179 x, allow_list=[r'.+\.(gn|gni|h)$'])
Karl Wiberg6bfac032017-10-27 15:14:20 +0200180 for f in input_api.AffectedSourceFiles(source_file_filter):
181 for path in API_DIRS:
182 dn = os.path.dirname(f.LocalPath())
183 if path == 'api':
184 # Special case: Subdirectories included.
185 if dn == 'api' or dn.startswith('api/'):
Niels Möller1d201852019-06-26 12:58:27 +0200186 files.append(f.LocalPath())
Karl Wiberg6bfac032017-10-27 15:14:20 +0200187 else:
188 # Normal case: Subdirectories not included.
189 if dn == path:
Niels Möller1d201852019-06-26 12:58:27 +0200190 files.append(f.LocalPath())
kjellander53047c92015-12-02 23:56:14 -0800191
192 if files:
kjellanderc88b5d52017-04-05 06:42:43 -0700193 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
kjellander53047c92015-12-02 23:56:14 -0800194 return []
195
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100196
Artem Titova04d1402018-05-11 11:23:00 +0200197def CheckNoIOStreamInHeaders(input_api, output_api,
198 source_file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000199 """Checks to make sure no .h files include <iostream>."""
200 files = []
201 pattern = input_api.re.compile(r'^#include\s*<iostream>',
202 input_api.re.MULTILINE)
Artem Titova04d1402018-05-11 11:23:00 +0200203 file_filter = lambda x: (input_api.FilterSourceFile(x)
204 and source_file_filter(x))
205 for f in input_api.AffectedSourceFiles(file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000206 if not f.LocalPath().endswith('.h'):
207 continue
208 contents = input_api.ReadFile(f)
209 if pattern.search(contents):
210 files.append(f)
211
212 if len(files):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200213 return [output_api.PresubmitError(
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000214 'Do not #include <iostream> in header files, since it inserts static ' +
215 'initialization into every file including the header. Instead, ' +
216 '#include <ostream>. See http://crbug.com/94794',
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200217 files)]
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000218 return []
219
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000220
Artem Titova04d1402018-05-11 11:23:00 +0200221def CheckNoPragmaOnce(input_api, output_api,
222 source_file_filter):
kjellander6aeef742017-02-20 01:13:18 -0800223 """Make sure that banned functions are not used."""
224 files = []
225 pattern = input_api.re.compile(r'^#pragma\s+once',
226 input_api.re.MULTILINE)
Artem Titova04d1402018-05-11 11:23:00 +0200227 file_filter = lambda x: (input_api.FilterSourceFile(x)
228 and source_file_filter(x))
229 for f in input_api.AffectedSourceFiles(file_filter):
kjellander6aeef742017-02-20 01:13:18 -0800230 if not f.LocalPath().endswith('.h'):
231 continue
232 contents = input_api.ReadFile(f)
233 if pattern.search(contents):
234 files.append(f)
235
236 if files:
237 return [output_api.PresubmitError(
238 'Do not use #pragma once in header files.\n'
239 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
240 files)]
241 return []
242
243
Artem Titova04d1402018-05-11 11:23:00 +0200244def CheckNoFRIEND_TEST(input_api, output_api, # pylint: disable=invalid-name
245 source_file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000246 """Make sure that gtest's FRIEND_TEST() macro is not used, the
247 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
248 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
249 problems = []
250
Artem Titova04d1402018-05-11 11:23:00 +0200251 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
252 and source_file_filter(f))
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000253 for f in input_api.AffectedFiles(file_filter=file_filter):
254 for line_num, line in f.ChangedContents():
255 if 'FRIEND_TEST(' in line:
256 problems.append(' %s:%d' % (f.LocalPath(), line_num))
257
258 if not problems:
259 return []
260 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use '
261 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
262 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
263
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000264
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200265def IsLintDisabled(disabled_paths, file_path):
266 """ Checks if a file is disabled for lint check."""
267 for path in disabled_paths:
oprypin2aa463f2017-03-23 03:17:02 -0700268 if file_path == path or os.path.dirname(file_path).startswith(path):
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100269 return True
270 return False
271
272
charujain9893e252017-09-14 13:33:22 +0200273def CheckApprovedFilesLintClean(input_api, output_api,
Artem Titova04d1402018-05-11 11:23:00 +0200274 source_file_filter=None):
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200275 """Checks that all new or non-exempt .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 13:33:22 +0200276 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000277 depot_tools/presubmit_canned_checks.py but has less filters and only checks
278 added files."""
279 result = []
280
281 # Initialize cpplint.
282 import cpplint
283 # Access to a protected member _XX of a client class
284 # pylint: disable=W0212
285 cpplint._cpplint_state.ResetErrorCounts()
286
jbauchc4e3ead2016-02-19 00:25:55 -0800287 lint_filters = cpplint._Filters()
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200288 lint_filters.extend(DISABLED_LINT_FILTERS)
jbauchc4e3ead2016-02-19 00:25:55 -0800289 cpplint._SetFilters(','.join(lint_filters))
290
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200291 # Create a platform independent exempt list for cpplint.
292 disabled_paths = [input_api.os_path.join(*path.split('/'))
293 for path in CPPLINT_EXCEPTIONS]
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100294
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000295 # Use the strictest verbosity level for cpplint.py (level 1) which is the
oprypin2aa463f2017-03-23 03:17:02 -0700296 # default when running cpplint.py from command line. To make it possible to
297 # work with not-yet-converted code, we're only applying it to new (or
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200298 # moved/renamed) files and files not listed in CPPLINT_EXCEPTIONS.
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000299 verbosity_level = 1
300 files = []
301 for f in input_api.AffectedSourceFiles(source_file_filter):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200302 # Note that moved/renamed files also count as added.
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200303 if f.Action() == 'A' or not IsLintDisabled(disabled_paths,
Artem Titove92675b2018-05-22 10:21:27 +0200304 f.LocalPath()):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000305 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000306
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000307 for file_name in files:
308 cpplint.ProcessFile(file_name, verbosity_level)
309
310 if cpplint._cpplint_state.error_count > 0:
311 if input_api.is_committing:
oprypin8e58d652017-03-21 07:52:41 -0700312 res_type = output_api.PresubmitError
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000313 else:
314 res_type = output_api.PresubmitPromptWarning
315 result = [res_type('Changelist failed cpplint.py check.')]
316
317 return result
318
Artem Titove92675b2018-05-22 10:21:27 +0200319
charujain9893e252017-09-14 13:33:22 +0200320def CheckNoSourcesAbove(input_api, gn_files, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700321 # Disallow referencing source files with paths above the GN file location.
322 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
323 re.MULTILINE | re.DOTALL)
324 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
325 violating_gn_files = set()
326 violating_source_entries = []
327 for gn_file in gn_files:
328 contents = input_api.ReadFile(gn_file)
329 for source_block_match in source_pattern.finditer(contents):
330 # Find all source list entries starting with ../ in the source block
331 # (exclude overrides entries).
332 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
333 source_file = file_list_match.group(1)
334 if 'overrides/' not in source_file:
335 violating_source_entries.append(source_file)
336 violating_gn_files.add(gn_file)
337 if violating_gn_files:
338 return [output_api.PresubmitError(
339 'Referencing source files above the directory of the GN file is not '
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100340 'allowed. Please introduce new GN targets in the proper location '
341 'instead.\n'
ehmaldonado5b1ba082016-09-02 05:51:08 -0700342 'Invalid source entries:\n'
343 '%s\n'
344 'Violating GN files:' % '\n'.join(violating_source_entries),
345 items=violating_gn_files)]
346 return []
347
Artem Titove92675b2018-05-22 10:21:27 +0200348
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200349def CheckAbseilDependencies(input_api, gn_files, output_api):
350 """Checks that Abseil dependencies are declared in `absl_deps`."""
351 absl_re = re.compile(r'third_party/abseil-cpp', re.MULTILINE | re.DOTALL)
352 target_types_to_check = [
353 'rtc_library',
354 'rtc_source_set',
Mirko Bonadei96115cf2020-06-23 23:39:56 +0200355 'rtc_static_library',
356 'webrtc_fuzzer_test',
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200357 ]
358 error_msg = ('Abseil dependencies in target "%s" (file: %s) '
359 'should be moved to the "absl_deps" parameter.')
360 errors = []
361
362 for gn_file in gn_files:
363 gn_file_content = input_api.ReadFile(gn_file)
364 for target_match in TARGET_RE.finditer(gn_file_content):
365 target_type = target_match.group('target_type')
366 target_name = target_match.group('target_name')
367 target_contents = target_match.group('target_contents')
368 if target_type in target_types_to_check:
369 for deps_match in DEPS_RE.finditer(target_contents):
370 deps = deps_match.group('deps').splitlines()
371 for dep in deps:
372 if re.search(absl_re, dep):
373 errors.append(
374 output_api.PresubmitError(error_msg % (target_name,
375 gn_file.LocalPath())))
376 break # no need to warn more than once per target
377 return errors
378
379
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200380def CheckNoMixingSources(input_api, gn_files, output_api):
381 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
382
383 See bugs.webrtc.org/7743 for more context.
384 """
Artem Titove92675b2018-05-22 10:21:27 +0200385
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200386 def _MoreThanOneSourceUsed(*sources_lists):
387 sources_used = 0
388 for source_list in sources_lists:
389 if len(source_list):
390 sources_used += 1
391 return sources_used > 1
392
393 errors = defaultdict(lambda: [])
kjellander7439f972016-12-05 22:47:46 -0800394 for gn_file in gn_files:
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200395 gn_file_content = input_api.ReadFile(gn_file)
396 for target_match in TARGET_RE.finditer(gn_file_content):
397 # list_of_sources is a list of tuples of the form
398 # (c_files, cc_files, objc_files) that keeps track of all the sources
399 # defined in a target. A GN target can have more that on definition of
400 # sources (since it supports if/else statements).
401 # E.g.:
402 # rtc_static_library("foo") {
403 # if (is_win) {
404 # sources = [ "foo.cc" ]
405 # } else {
406 # sources = [ "foo.mm" ]
407 # }
408 # }
409 # This is allowed and the presubmit check should support this case.
410 list_of_sources = []
kjellander7439f972016-12-05 22:47:46 -0800411 c_files = []
412 cc_files = []
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200413 objc_files = []
414 target_name = target_match.group('target_name')
415 target_contents = target_match.group('target_contents')
416 for sources_match in SOURCES_RE.finditer(target_contents):
417 if '+=' not in sources_match.group(0):
418 if c_files or cc_files or objc_files:
419 list_of_sources.append((c_files, cc_files, objc_files))
420 c_files = []
421 cc_files = []
422 objc_files = []
423 for file_match in FILE_PATH_RE.finditer(sources_match.group(1)):
424 file_path = file_match.group('file_path')
425 extension = file_match.group('extension')
426 if extension == '.c':
427 c_files.append(file_path + extension)
428 if extension == '.cc':
429 cc_files.append(file_path + extension)
430 if extension in ['.m', '.mm']:
431 objc_files.append(file_path + extension)
432 list_of_sources.append((c_files, cc_files, objc_files))
433 for c_files_list, cc_files_list, objc_files_list in list_of_sources:
434 if _MoreThanOneSourceUsed(c_files_list, cc_files_list, objc_files_list):
435 all_sources = sorted(c_files_list + cc_files_list + objc_files_list)
436 errors[gn_file.LocalPath()].append((target_name, all_sources))
437 if errors:
kjellander7439f972016-12-05 22:47:46 -0800438 return [output_api.PresubmitError(
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200439 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
440 'Please create a separate target for each collection of sources.\n'
kjellander7439f972016-12-05 22:47:46 -0800441 'Mixed sources: \n'
442 '%s\n'
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200443 'Violating GN files:\n%s\n' % (json.dumps(errors, indent=2),
444 '\n'.join(errors.keys())))]
kjellander7439f972016-12-05 22:47:46 -0800445 return []
446
Artem Titove92675b2018-05-22 10:21:27 +0200447
charujain9893e252017-09-14 13:33:22 +0200448def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
ehmaldonado4fb97462017-01-30 05:27:22 -0800449 cwd = input_api.PresubmitLocalPath()
Oleh Prypin2f33a562017-10-04 20:17:54 +0200450 with _AddToPath(input_api.os_path.join(
451 cwd, 'tools_webrtc', 'presubmit_checks_lib')):
452 from check_package_boundaries import CheckPackageBoundaries
453 build_files = [os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files]
454 errors = CheckPackageBoundaries(cwd, build_files)[:5]
455 if errors:
ehmaldonado4fb97462017-01-30 05:27:22 -0800456 return [output_api.PresubmitError(
Oleh Prypin2f33a562017-10-04 20:17:54 +0200457 'There are package boundary violations in the following GN files:',
458 long_text='\n\n'.join(str(err) for err in errors))]
ehmaldonado4fb97462017-01-30 05:27:22 -0800459 return []
460
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100461
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200462def _ReportFileAndLine(filename, line_num):
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100463 """Default error formatter for _FindNewViolationsOfRule."""
464 return '%s (line %s)' % (filename, line_num)
465
466
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200467def CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api, output_api,
468 error_formatter=_ReportFileAndLine):
469 """Make sure that warning suppression flags are not added wihtout a reason."""
470 msg = ('Usage of //build/config/clang:extra_warnings is discouraged '
471 'in WebRTC.\n'
472 'If you are not adding this code (e.g. you are just moving '
473 'existing code) or you want to add an exception,\n'
474 'you can add a comment on the line that causes the problem:\n\n'
475 '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n'
476 '\n'
477 'Affected files:\n')
478 errors = [] # 2-element tuples with (file, line number)
479 clang_warn_re = input_api.re.compile(r'//build/config/clang:extra_warnings')
480 no_presubmit_re = input_api.re.compile(
481 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
482 for f in gn_files:
483 for line_num, line in f.ChangedContents():
484 if clang_warn_re.search(line) and not no_presubmit_re.search(line):
485 errors.append(error_formatter(f.LocalPath(), line_num))
486 if errors:
487 return [output_api.PresubmitError(msg, errors)]
488 return []
489
Mirko Bonadei9ce800d2019-02-05 16:48:13 +0100490
491def CheckNoTestCaseUsageIsAdded(input_api, output_api, source_file_filter,
492 error_formatter=_ReportFileAndLine):
493 error_msg = ('Usage of legacy GoogleTest API detected!\nPlease use the '
494 'new API: https://github.com/google/googletest/blob/master/'
495 'googletest/docs/primer.md#beware-of-the-nomenclature.\n'
496 'Affected files:\n')
497 errors = [] # 2-element tuples with (file, line number)
498 test_case_re = input_api.re.compile(r'TEST_CASE')
499 file_filter = lambda f: (source_file_filter(f)
500 and f.LocalPath().endswith('.cc'))
501 for f in input_api.AffectedSourceFiles(file_filter):
502 for line_num, line in f.ChangedContents():
503 if test_case_re.search(line):
504 errors.append(error_formatter(f.LocalPath(), line_num))
505 if errors:
506 return [output_api.PresubmitError(error_msg, errors)]
507 return []
508
509
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100510def CheckNoStreamUsageIsAdded(input_api, output_api,
Artem Titov739351d2018-05-11 12:21:36 +0200511 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200512 error_formatter=_ReportFileAndLine):
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100513 """Make sure that no more dependencies on stringstream are added."""
514 error_msg = ('Usage of <sstream>, <istream> and <ostream> in WebRTC is '
515 'deprecated.\n'
516 'This includes the following types:\n'
517 'std::istringstream, std::ostringstream, std::wistringstream, '
518 'std::wostringstream,\n'
519 'std::wstringstream, std::ostream, std::wostream, std::istream,'
520 'std::wistream,\n'
521 'std::iostream, std::wiostream.\n'
522 'If you are not adding this code (e.g. you are just moving '
523 'existing code),\n'
524 'you can add a comment on the line that causes the problem:\n\n'
525 '#include <sstream> // no-presubmit-check TODO(webrtc:8982)\n'
526 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n'
527 '\n'
Karl Wibergebd01e82018-03-14 15:08:39 +0100528 'If you are adding new code, consider using '
529 'rtc::SimpleStringBuilder\n'
530 '(in rtc_base/strings/string_builder.h).\n'
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100531 'Affected files:\n')
532 errors = [] # 2-element tuples with (file, line number)
533 include_re = input_api.re.compile(r'#include <(i|o|s)stream>')
534 usage_re = input_api.re.compile(r'std::(w|i|o|io|wi|wo|wio)(string)*stream')
535 no_presubmit_re = input_api.re.compile(
Jonas Olsson74395342018-04-03 12:22:07 +0200536 r'// no-presubmit-check TODO\(webrtc:8982\)')
Artem Titova04d1402018-05-11 11:23:00 +0200537 file_filter = lambda x: (input_api.FilterSourceFile(x)
538 and source_file_filter(x))
Mirko Bonadei571791a2019-05-07 14:08:05 +0200539
540 def _IsException(file_path):
541 is_test = any(file_path.endswith(x) for x in ['_test.cc', '_tests.cc',
542 '_unittest.cc',
543 '_unittests.cc'])
Patrik Höglund2ea27962020-01-13 15:10:40 +0100544 return (file_path.startswith('examples') or
545 file_path.startswith('test') or
546 is_test)
547
Mirko Bonadei571791a2019-05-07 14:08:05 +0200548
Artem Titova04d1402018-05-11 11:23:00 +0200549 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei571791a2019-05-07 14:08:05 +0200550 # Usage of stringstream is allowed under examples/ and in tests.
551 if f.LocalPath() == 'PRESUBMIT.py' or _IsException(f.LocalPath()):
Mirko Bonadeid2c83322018-03-19 10:31:47 +0000552 continue
553 for line_num, line in f.ChangedContents():
554 if ((include_re.search(line) or usage_re.search(line))
555 and not no_presubmit_re.search(line)):
556 errors.append(error_formatter(f.LocalPath(), line_num))
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100557 if errors:
558 return [output_api.PresubmitError(error_msg, errors)]
559 return []
560
Artem Titove92675b2018-05-22 10:21:27 +0200561
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200562def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
563 """Checks that public_deps is not used without a good reason."""
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100564 result = []
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200565 no_presubmit_check_re = input_api.re.compile(
Joe Chen0b3a6e32019-12-26 23:01:42 -0800566 r'# no-presubmit-check TODO\(webrtc:\d+\)')
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200567 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
568 'because it doesn\'t map well to downstream build systems.\n'
569 'Used in: %s (line %d).\n'
570 'If you are not adding this code (e.g. you are just moving '
Patrik Höglund81c7a602020-01-30 11:32:33 +0100571 'existing code) or you have a good reason, you can add this '
572 'comment (verbatim) on the line that causes the problem:\n\n'
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200573 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100574 for affected_file in gn_files:
575 for (line_number, affected_line) in affected_file.ChangedContents():
Patrik Höglund81c7a602020-01-30 11:32:33 +0100576 if 'public_deps' in affected_line:
577 surpressed = no_presubmit_check_re.search(affected_line)
578 if not surpressed:
579 result.append(
580 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
581 line_number)))
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100582 return result
583
Artem Titove92675b2018-05-22 10:21:27 +0200584
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700585def CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api):
Patrik Höglund6f491062018-01-11 12:04:23 +0100586 result = []
587 error_msg = ('check_includes overrides are not allowed since it can cause '
588 'incorrect dependencies to form. It effectively means that your '
589 'module can include any .h file without depending on its '
590 'corresponding target. There are some exceptional cases when '
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700591 'this is allowed: if so, get approval from a .gn owner in the '
Patrik Höglund6f491062018-01-11 12:04:23 +0100592 'root OWNERS file.\n'
593 'Used in: %s (line %d).')
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700594 no_presubmit_re = input_api.re.compile(
595 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
Patrik Höglund6f491062018-01-11 12:04:23 +0100596 for affected_file in gn_files:
597 for (line_number, affected_line) in affected_file.ChangedContents():
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700598 if ('check_includes' in affected_line
599 and not no_presubmit_re.search(affected_line)):
Patrik Höglund6f491062018-01-11 12:04:23 +0100600 result.append(
601 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
602 line_number)))
603 return result
604
Artem Titove92675b2018-05-22 10:21:27 +0200605
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200606def CheckGnChanges(input_api, output_api):
Artem Titova04d1402018-05-11 11:23:00 +0200607 file_filter = lambda x: (input_api.FilterSourceFile(
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200608 x, allow_list=(r'.+\.(gn|gni)$',),
609 block_list=(r'.*/presubmit_checks_lib/testdata/.*',)))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700610
611 gn_files = []
Artem Titova04d1402018-05-11 11:23:00 +0200612 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200613 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 05:51:08 -0700614
615 result = []
616 if gn_files:
charujain9893e252017-09-14 13:33:22 +0200617 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200618 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200619 result.extend(CheckAbseilDependencies(input_api, gn_files, output_api))
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200620 result.extend(CheckNoPackageBoundaryViolations(input_api, gn_files,
621 output_api))
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200622 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api, output_api))
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700623 result.extend(CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api))
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200624 result.extend(CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api,
625 output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700626 return result
627
Artem Titove92675b2018-05-22 10:21:27 +0200628
Oleh Prypin920b6532017-10-05 11:28:51 +0200629def CheckGnGen(input_api, output_api):
630 """Runs `gn gen --check` with default args to detect mismatches between
631 #includes and dependencies in the BUILD.gn files, as well as general build
632 errors.
633 """
634 with _AddToPath(input_api.os_path.join(
635 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
Yves Gerey546ee612019-02-26 17:04:16 +0100636 from build_helpers import RunGnCheck
Mirko Bonadeid8665442018-09-04 12:17:27 +0200637 errors = RunGnCheck(FindSrcDirPath(input_api.PresubmitLocalPath()))[:5]
Oleh Prypin920b6532017-10-05 11:28:51 +0200638 if errors:
639 return [output_api.PresubmitPromptWarning(
640 'Some #includes do not match the build dependency graph. Please run:\n'
641 ' gn gen --check <out_dir>',
642 long_text='\n\n'.join(errors))]
643 return []
644
Artem Titove92675b2018-05-22 10:21:27 +0200645
Artem Titova04d1402018-05-11 11:23:00 +0200646def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000647 """Runs checkdeps on #include statements added in this
648 change. Breaking - rules is an error, breaking ! rules is a
649 warning.
650 """
651 # Copied from Chromium's src/PRESUBMIT.py.
652
653 # We need to wait until we have an input_api object and use this
654 # roundabout construct to import checkdeps because this file is
655 # eval-ed and thus doesn't have __file__.
Mirko Bonadeid8665442018-09-04 12:17:27 +0200656 src_path = FindSrcDirPath(input_api.PresubmitLocalPath())
657 checkdeps_path = input_api.os_path.join(src_path, 'buildtools', 'checkdeps')
Oleh Prypin2f33a562017-10-04 20:17:54 +0200658 if not os.path.exists(checkdeps_path):
659 return [output_api.PresubmitError(
660 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
661 'download all the DEPS entries?' % checkdeps_path)]
662 with _AddToPath(checkdeps_path):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000663 import checkdeps
664 from cpp_checker import CppChecker
665 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000666
667 added_includes = []
Artem Titova04d1402018-05-11 11:23:00 +0200668 for f in input_api.AffectedFiles(file_filter=source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000669 if not CppChecker.IsCppFile(f.LocalPath()):
670 continue
671
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200672 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000673 added_includes.append([f.LocalPath(), changed_lines])
674
675 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
676
677 error_descriptions = []
678 warning_descriptions = []
679 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
680 added_includes):
681 description_with_path = '%s\n %s' % (path, rule_description)
682 if rule_type == Rule.DISALLOW:
683 error_descriptions.append(description_with_path)
684 else:
685 warning_descriptions.append(description_with_path)
686
687 results = []
688 if error_descriptions:
689 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 03:47:05 -0700690 'You added one or more #includes that violate checkdeps rules.\n'
691 'Check that the DEPS files in these locations contain valid rules.\n'
692 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
693 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000694 error_descriptions))
695 if warning_descriptions:
696 results.append(output_api.PresubmitPromptOrNotify(
697 'You added one or more #includes of files that are temporarily\n'
698 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 03:47:05 -0700699 '#include? See relevant DEPS file(s) for details and contacts.\n'
700 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
701 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000702 warning_descriptions))
703 return results
704
Artem Titove92675b2018-05-22 10:21:27 +0200705
charujain9893e252017-09-14 13:33:22 +0200706def CheckCommitMessageBugEntry(input_api, output_api):
707 """Check that bug entries are well-formed in commit message."""
708 bogus_bug_msg = (
Mirko Bonadei61880182017-10-12 15:12:35 +0200709 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
charujain9893e252017-09-14 13:33:22 +0200710 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.')
711 results = []
Mirko Bonadei61880182017-10-12 15:12:35 +0200712 for bug in input_api.change.BugsFromDescription():
charujain9893e252017-09-14 13:33:22 +0200713 bug = bug.strip()
714 if bug.lower() == 'none':
715 continue
charujain81a58c72017-09-25 13:25:45 +0200716 if 'b/' not in bug and ':' not in bug:
charujain9893e252017-09-14 13:33:22 +0200717 try:
718 if int(bug) > 100000:
719 # Rough indicator for current chromium bugs.
720 prefix_guess = 'chromium'
721 else:
722 prefix_guess = 'webrtc'
Mirko Bonadei61880182017-10-12 15:12:35 +0200723 results.append('Bug entry requires issue tracker prefix, e.g. %s:%s' %
charujain9893e252017-09-14 13:33:22 +0200724 (prefix_guess, bug))
725 except ValueError:
726 results.append(bogus_bug_msg % bug)
charujain81a58c72017-09-25 13:25:45 +0200727 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
charujain9893e252017-09-14 13:33:22 +0200728 results.append(bogus_bug_msg % bug)
729 return [output_api.PresubmitError(r) for r in results]
730
Artem Titove92675b2018-05-22 10:21:27 +0200731
charujain9893e252017-09-14 13:33:22 +0200732def CheckChangeHasBugField(input_api, output_api):
Mirko Bonadei61880182017-10-12 15:12:35 +0200733 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 08:11:16 -0700734
735 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 15:12:35 +0200736 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 08:11:16 -0700737 a bug reference.
Mirko Bonadei61880182017-10-12 15:12:35 +0200738
739 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
740 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 08:11:16 -0700741 """
Mirko Bonadei61880182017-10-12 15:12:35 +0200742 if input_api.change.BugsFromDescription():
kjellanderd1e26a92016-09-19 08:11:16 -0700743 return []
744 else:
745 return [output_api.PresubmitError(
Mirko Bonadei61880182017-10-12 15:12:35 +0200746 'The "Bug: [bug number]" footer is mandatory. Please create a bug and '
kjellanderd1e26a92016-09-19 08:11:16 -0700747 'reference it using either of:\n'
Mirko Bonadei61880182017-10-12 15:12:35 +0200748 ' * https://bugs.webrtc.org - reference it using Bug: webrtc:XXXX\n'
749 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000750
Artem Titove92675b2018-05-22 10:21:27 +0200751
Artem Titova04d1402018-05-11 11:23:00 +0200752def CheckJSONParseErrors(input_api, output_api, source_file_filter):
kjellander569cf942016-02-11 05:02:59 -0800753 """Check that JSON files do not contain syntax errors."""
754
755 def FilterFile(affected_file):
Artem Titova04d1402018-05-11 11:23:00 +0200756 return (input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
757 and source_file_filter(affected_file))
kjellander569cf942016-02-11 05:02:59 -0800758
759 def GetJSONParseError(input_api, filename):
760 try:
761 contents = input_api.ReadFile(filename)
762 input_api.json.loads(contents)
763 except ValueError as e:
764 return e
765 return None
766
767 results = []
768 for affected_file in input_api.AffectedFiles(
769 file_filter=FilterFile, include_deletes=False):
770 parse_error = GetJSONParseError(input_api,
771 affected_file.AbsoluteLocalPath())
772 if parse_error:
773 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
Artem Titove92675b2018-05-22 10:21:27 +0200774 (affected_file.LocalPath(),
775 parse_error)))
kjellander569cf942016-02-11 05:02:59 -0800776 return results
777
778
charujain9893e252017-09-14 13:33:22 +0200779def RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 06:42:43 -0700780 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200781 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
782
783 test_directories = [
Edward Lemur6d01f6d2017-09-14 17:02:01 +0200784 input_api.PresubmitLocalPath(),
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200785 Join('rtc_tools', 'py_event_log_analyzer'),
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200786 Join('audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 05:27:22 -0800787 ] + [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200788 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 05:27:22 -0800789 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200790 ]
791
792 tests = []
793 for directory in test_directories:
794 tests.extend(
795 input_api.canned_checks.GetUnitTestsInDirectory(
796 input_api,
797 output_api,
798 directory,
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200799 allowlist=[r'.+_test\.py$']))
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200800 return input_api.RunTests(tests, parallel=True)
801
802
Artem Titova04d1402018-05-11 11:23:00 +0200803def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
804 source_file_filter):
mbonadei38415b22017-04-07 05:38:01 -0700805 """Checks that the namespace google::protobuf has not been used."""
806 files = []
807 pattern = input_api.re.compile(r'google::protobuf')
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200808 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
Artem Titova04d1402018-05-11 11:23:00 +0200809 file_filter = lambda x: (input_api.FilterSourceFile(x)
810 and source_file_filter(x))
811 for f in input_api.AffectedSourceFiles(file_filter):
mbonadei38415b22017-04-07 05:38:01 -0700812 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
813 continue
814 contents = input_api.ReadFile(f)
815 if pattern.search(contents):
816 files.append(f)
817
818 if files:
819 return [output_api.PresubmitError(
820 'Please avoid to use namespace `google::protobuf` directly.\n'
821 'Add a using directive in `%s` and include that header instead.'
822 % proto_utils_path, files)]
823 return []
824
825
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200826def _LicenseHeader(input_api):
827 """Returns the license header regexp."""
828 # Accept any year number from 2003 to the current year
829 current_year = int(input_api.time.strftime('%Y'))
830 allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1)))
831 years_re = '(' + '|'.join(allowed_years) + ')'
832 license_header = (
833 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
834 r'All [Rr]ights [Rr]eserved\.\n'
835 r'.*?\n'
836 r'.*? Use of this source code is governed by a BSD-style license\n'
837 r'.*? that can be found in the LICENSE file in the root of the source\n'
838 r'.*? tree\. An additional intellectual property rights grant can be '
839 r'found\n'
840 r'.*? in the file PATENTS\. All contributing project authors may\n'
841 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
842 ) % {
843 'year': years_re,
844 }
845 return license_header
846
847
charujain9893e252017-09-14 13:33:22 +0200848def CommonChecks(input_api, output_api):
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000849 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34 +0000850 results = []
tkchin42f580e2015-11-26 23:18:23 -0800851 # Filter out files that are in objc or ios dirs from being cpplint-ed since
852 # they do not follow C++ lint rules.
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200853 exception_list = input_api.DEFAULT_BLACK_LIST + (
tkchin42f580e2015-11-26 23:18:23 -0800854 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05 +0000855 r".*objc\.[hcm]+$",
tkchin42f580e2015-11-26 23:18:23 -0800856 )
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200857 source_file_filter = lambda x: input_api.FilterSourceFile(x, None,
858 exception_list)
charujain9893e252017-09-14 13:33:22 +0200859 results.extend(CheckApprovedFilesLintClean(
tkchin42f580e2015-11-26 23:18:23 -0800860 input_api, output_api, source_file_filter))
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200861 results.extend(input_api.canned_checks.CheckLicense(
862 input_api, output_api, _LicenseHeader(input_api)))
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000863 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200864 block_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200865 r'^build[\\\/].*\.py$',
866 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-12 22:43:38 -0700867 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 12:01:17 +0100868 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200869 r'^out.*[\\\/].*\.py$',
870 r'^testing[\\\/].*\.py$',
871 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100872 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 12:21:39 -0800873 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200874 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200875 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200876 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800877
nisse3d21e232016-09-02 03:07:06 -0700878 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200879 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
880 # we need to have different license checks in talk/ and webrtc/ directories.
881 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200882
tkchin3cd9a302016-06-08 12:40:28 -0700883 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
884 # ObjC subdirectories ObjC headers.
885 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100886 # Skip long-lines check for DEPS and GN files.
887 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
Artem Titova04d1402018-05-11 11:23:00 +0200888 # Also we will skip most checks for third_party directory.
Artem Titov42f0d782018-06-27 13:23:17 +0200889 third_party_filter_list = (r'^third_party[\\\/].+',)
tkchin3cd9a302016-06-08 12:40:28 -0700890 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200891 block_list=build_file_filter_list + objc_filter_list +
Artem Titova04d1402018-05-11 11:23:00 +0200892 third_party_filter_list)
tkchin3cd9a302016-06-08 12:40:28 -0700893 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200894 allow_list=objc_filter_list)
Artem Titove92675b2018-05-22 10:21:27 +0200895 non_third_party_sources = lambda x: input_api.FilterSourceFile(x,
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200896 block_list=third_party_filter_list)
Artem Titove92675b2018-05-22 10:21:27 +0200897
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000898 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 12:40:28 -0700899 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
900 results.extend(input_api.canned_checks.CheckLongLines(
901 input_api, output_api, maxlen=100,
902 source_file_filter=hundred_char_sources))
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000903 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
Artem Titova04d1402018-05-11 11:23:00 +0200904 input_api, output_api, source_file_filter=non_third_party_sources))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000905 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
Artem Titova04d1402018-05-11 11:23:00 +0200906 input_api, output_api, source_file_filter=non_third_party_sources))
kjellandere5dc62a2016-12-14 00:16:21 -0800907 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
Oleh Prypine0735142018-10-04 11:15:54 +0200908 input_api, output_api, bot_whitelist=[
909 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com'
910 ]))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000911 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
Artem Titova04d1402018-05-11 11:23:00 +0200912 input_api, output_api, source_file_filter=non_third_party_sources))
Yves Gerey87a93532018-06-20 15:51:49 +0200913 results.extend(input_api.canned_checks.CheckPatchFormatted(
914 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200915 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200916 results.extend(CheckNoIOStreamInHeaders(
917 input_api, output_api, source_file_filter=non_third_party_sources))
918 results.extend(CheckNoPragmaOnce(
919 input_api, output_api, source_file_filter=non_third_party_sources))
920 results.extend(CheckNoFRIEND_TEST(
921 input_api, output_api, source_file_filter=non_third_party_sources))
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200922 results.extend(CheckGnChanges(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200923 results.extend(CheckUnwantedDependencies(
924 input_api, output_api, source_file_filter=non_third_party_sources))
925 results.extend(CheckJSONParseErrors(
926 input_api, output_api, source_file_filter=non_third_party_sources))
charujain9893e252017-09-14 13:33:22 +0200927 results.extend(RunPythonTests(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200928 results.extend(CheckUsageOfGoogleProtobufNamespace(
929 input_api, output_api, source_file_filter=non_third_party_sources))
930 results.extend(CheckOrphanHeaders(
931 input_api, output_api, source_file_filter=non_third_party_sources))
932 results.extend(CheckNewlineAtTheEndOfProtoFiles(
933 input_api, output_api, source_file_filter=non_third_party_sources))
934 results.extend(CheckNoStreamUsageIsAdded(
Artem Titov739351d2018-05-11 12:21:36 +0200935 input_api, output_api, non_third_party_sources))
Mirko Bonadei9ce800d2019-02-05 16:48:13 +0100936 results.extend(CheckNoTestCaseUsageIsAdded(
937 input_api, output_api, non_third_party_sources))
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +0200938 results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200939 results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api))
tzika06bf852018-11-15 20:37:35 +0900940 results.extend(CheckAbslMemoryInclude(
941 input_api, output_api, non_third_party_sources))
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +0200942 results.extend(CheckBannedAbslMakeUnique(
943 input_api, output_api, non_third_party_sources))
Mirko Bonadeid74c0e62020-07-16 21:57:01 +0200944 results.extend(CheckObjcApiSymbols(
945 input_api, output_api, non_third_party_sources))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200946 return results
947
948
949def CheckApiDepsFileIsUpToDate(input_api, output_api):
Mirko Bonadei90490372018-10-26 13:17:47 +0200950 """Check that 'include_rules' in api/DEPS is up to date.
951
952 The file api/DEPS must be kept up to date in order to avoid to avoid to
953 include internal header from WebRTC's api/ headers.
954
955 This check is focused on ensuring that 'include_rules' contains a deny
956 rule for each root level directory. More focused allow rules can be
957 added to 'specific_include_rules'.
958 """
Mirko Bonadeia418e672018-10-24 13:57:25 +0200959 results = []
960 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
961 with open(api_deps) as f:
962 deps_content = _ParseDeps(f.read())
963
964 include_rules = deps_content.get('include_rules', [])
Mirko Bonadei01e97ae2019-09-05 14:36:42 +0200965 dirs_to_skip = set(['api', 'docs'])
Mirko Bonadeia418e672018-10-24 13:57:25 +0200966
Mirko Bonadei90490372018-10-26 13:17:47 +0200967 # Only check top level directories affected by the current CL.
968 dirs_to_check = set()
969 for f in input_api.AffectedFiles():
970 path_tokens = [t for t in f.LocalPath().split(os.sep) if t]
971 if len(path_tokens) > 1:
Mirko Bonadei01e97ae2019-09-05 14:36:42 +0200972 if (path_tokens[0] not in dirs_to_skip and
Mirko Bonadei90490372018-10-26 13:17:47 +0200973 os.path.isdir(os.path.join(input_api.PresubmitLocalPath(),
974 path_tokens[0]))):
975 dirs_to_check.add(path_tokens[0])
Mirko Bonadeia418e672018-10-24 13:57:25 +0200976
Mirko Bonadei90490372018-10-26 13:17:47 +0200977 missing_include_rules = set()
978 for p in dirs_to_check:
Mirko Bonadeia418e672018-10-24 13:57:25 +0200979 rule = '-%s' % p
980 if rule not in include_rules:
Mirko Bonadei90490372018-10-26 13:17:47 +0200981 missing_include_rules.add(rule)
982
Mirko Bonadeia418e672018-10-24 13:57:25 +0200983 if missing_include_rules:
Mirko Bonadei90490372018-10-26 13:17:47 +0200984 error_msg = [
985 'include_rules = [\n',
986 ' ...\n',
987 ]
Mirko Bonadeia418e672018-10-24 13:57:25 +0200988
Mirko Bonadei90490372018-10-26 13:17:47 +0200989 for r in sorted(missing_include_rules):
990 error_msg.append(' "%s",\n' % str(r))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200991
Mirko Bonadei90490372018-10-26 13:17:47 +0200992 error_msg.append(' ...\n')
993 error_msg.append(']\n')
994
Mirko Bonadeia418e672018-10-24 13:57:25 +0200995 results.append(output_api.PresubmitError(
Mirko Bonadei90490372018-10-26 13:17:47 +0200996 'New root level directory detected! WebRTC api/ headers should '
997 'not #include headers from \n'
998 'the new directory, so please update "include_rules" in file\n'
999 '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg))))
1000
andrew@webrtc.org53df1362012-01-26 21:24:23 +00001001 return results
andrew@webrtc.org2442de12012-01-23 17:45:41 +00001002
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001003def CheckBannedAbslMakeUnique(input_api, output_api, source_file_filter):
1004 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
1005 and source_file_filter(f))
1006
1007 files = []
1008 for f in input_api.AffectedFiles(
1009 include_deletes=False, file_filter=file_filter):
1010 for _, line in f.ChangedContents():
1011 if 'absl::make_unique' in line:
1012 files.append(f)
1013 break
1014
1015 if len(files):
1016 return [output_api.PresubmitError(
1017 'Please use std::make_unique instead of absl::make_unique.\n'
1018 'Affected files:',
1019 files)]
1020 return []
1021
Mirko Bonadeid74c0e62020-07-16 21:57:01 +02001022def CheckObjcApiSymbols(input_api, output_api, source_file_filter):
1023 rtc_objc_export = re.compile(r'RTC_OBJC_EXPORT(.|\n){26}',
1024 re.MULTILINE | re.DOTALL)
1025 file_filter = lambda f: (f.LocalPath().endswith(('.h'))
1026 and source_file_filter(f))
1027
1028 files = []
1029 file_filter = lambda x: (input_api.FilterSourceFile(x)
1030 and source_file_filter(x))
1031 for f in input_api.AffectedSourceFiles(file_filter):
1032 if not f.LocalPath().endswith('.h') or not 'sdk/objc' in f.LocalPath():
1033 continue
1034 contents = input_api.ReadFile(f)
1035 for match in rtc_objc_export.finditer(contents):
1036 export_block = match.group(0)
1037 if 'RTC_OBJC_TYPE' not in export_block:
1038 files.append(f.LocalPath())
1039
1040 if len(files):
1041 return [output_api.PresubmitError(
1042 'RTC_OBJC_EXPORT types must be wrapped into an RTC_OBJC_TYPE() ' +
1043 'macro.\n\n' +
1044 'For example:\n' +
1045 'RTC_OBJC_EXPORT @protocol RTC_OBJC_TYPE(RtcFoo)\n\n' +
1046 'RTC_OBJC_EXPORT @interface RTC_OBJC_TYPE(RtcFoo)\n\n' +
1047 'Please fix the following files:',
1048 files)]
1049 return []
1050
tzika06bf852018-11-15 20:37:35 +09001051def CheckAbslMemoryInclude(input_api, output_api, source_file_filter):
1052 pattern = input_api.re.compile(
1053 r'^#include\s*"absl/memory/memory.h"', input_api.re.MULTILINE)
1054 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
1055 and source_file_filter(f))
1056
1057 files = []
1058 for f in input_api.AffectedFiles(
1059 include_deletes=False, file_filter=file_filter):
1060 contents = input_api.ReadFile(f)
1061 if pattern.search(contents):
1062 continue
1063 for _, line in f.ChangedContents():
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001064 if 'absl::WrapUnique' in line:
tzika06bf852018-11-15 20:37:35 +09001065 files.append(f)
1066 break
1067
1068 if len(files):
1069 return [output_api.PresubmitError(
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001070 'Please include "absl/memory/memory.h" header for absl::WrapUnique.\n'
1071 'This header may or may not be included transitively depending on the '
1072 'C++ standard version.',
tzika06bf852018-11-15 20:37:35 +09001073 files)]
1074 return []
kjellander@webrtc.orge4158642014-08-06 09:11:18 +00001075
andrew@webrtc.org53df1362012-01-26 21:24:23 +00001076def CheckChangeOnUpload(input_api, output_api):
1077 results = []
charujain9893e252017-09-14 13:33:22 +02001078 results.extend(CommonChecks(input_api, output_api))
Oleh Prypin920b6532017-10-05 11:28:51 +02001079 results.extend(CheckGnGen(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 12:55:39 +02001080 results.extend(
1081 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:34 +00001082 return results
1083
kjellander@webrtc.orge4158642014-08-06 09:11:18 +00001084
andrew@webrtc.org2442de12012-01-23 17:45:41 +00001085def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:24 +00001086 results = []
charujain9893e252017-09-14 13:33:22 +02001087 results.extend(CommonChecks(input_api, output_api))
1088 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
Artem Titov42f0d782018-06-27 13:23:17 +02001089 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +00001090 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
1091 input_api, output_api))
1092 results.extend(input_api.canned_checks.CheckChangeHasDescription(
1093 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +02001094 results.extend(CheckChangeHasBugField(input_api, output_api))
1095 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:43 +00001096 results.extend(input_api.canned_checks.CheckTreeIsOpen(
1097 input_api, output_api,
1098 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:24 +00001099 return results
mbonadei74973ed2017-05-09 07:58:05 -07001100
1101
Artem Titova04d1402018-05-11 11:23:00 +02001102def CheckOrphanHeaders(input_api, output_api, source_file_filter):
mbonadei74973ed2017-05-09 07:58:05 -07001103 # We need to wait until we have an input_api object and use this
1104 # roundabout construct to import prebubmit_checks_lib because this file is
1105 # eval-ed and thus doesn't have __file__.
Patrik Höglund2f3f7222017-12-19 11:08:56 +01001106 error_msg = """{} should be listed in {}."""
mbonadei74973ed2017-05-09 07:58:05 -07001107 results = []
Mirko Bonadeifc17a782020-06-30 14:31:37 +02001108 exempt_paths = [
Patrik Höglund7e60de22018-01-09 14:22:00 +01001109 os.path.join('tools_webrtc', 'ios', 'SDK'),
1110 ]
Oleh Prypin2f33a562017-10-04 20:17:54 +02001111 with _AddToPath(input_api.os_path.join(
1112 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
mbonadei74973ed2017-05-09 07:58:05 -07001113 from check_orphan_headers import GetBuildGnPathFromFilePath
1114 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 07:58:05 -07001115
Artem Titova04d1402018-05-11 11:23:00 +02001116 file_filter = lambda x: input_api.FilterSourceFile(
Mirko Bonadeifc17a782020-06-30 14:31:37 +02001117 x, block_list=exempt_paths) and source_file_filter(x)
Artem Titova04d1402018-05-11 11:23:00 +02001118 for f in input_api.AffectedSourceFiles(file_filter):
Patrik Höglund7e60de22018-01-09 14:22:00 +01001119 if f.LocalPath().endswith('.h'):
mbonadei74973ed2017-05-09 07:58:05 -07001120 file_path = os.path.abspath(f.LocalPath())
1121 root_dir = os.getcwd()
1122 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
1123 root_dir)
1124 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
1125 if not in_build_gn:
1126 results.append(output_api.PresubmitError(error_msg.format(
Patrik Höglund2f3f7222017-12-19 11:08:56 +01001127 f.LocalPath(), os.path.relpath(gn_file_path))))
mbonadei74973ed2017-05-09 07:58:05 -07001128 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001129
1130
Artem Titove92675b2018-05-22 10:21:27 +02001131def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api, source_file_filter):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001132 """Checks that all .proto files are terminated with a newline."""
1133 error_msg = 'File {} must end with exactly one newline.'
1134 results = []
Artem Titova04d1402018-05-11 11:23:00 +02001135 file_filter = lambda x: input_api.FilterSourceFile(
Mirko Bonadeifc17a782020-06-30 14:31:37 +02001136 x, allow_list=(r'.+\.proto$',)) and source_file_filter(x)
Artem Titova04d1402018-05-11 11:23:00 +02001137 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001138 file_path = f.LocalPath()
1139 with open(file_path) as f:
1140 lines = f.readlines()
Mirko Bonadeia730c1c2017-09-18 11:33:13 +02001141 if len(lines) > 0 and not lines[-1].endswith('\n'):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001142 results.append(output_api.PresubmitError(error_msg.format(file_path)))
1143 return results
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001144
1145
1146def _ExtractAddRulesFromParsedDeps(parsed_deps):
1147 """Extract the rules that add dependencies from a parsed DEPS file.
1148
1149 Args:
1150 parsed_deps: the locals dictionary from evaluating the DEPS file."""
1151 add_rules = set()
1152 add_rules.update([
1153 rule[1:] for rule in parsed_deps.get('include_rules', [])
1154 if rule.startswith('+') or rule.startswith('!')
1155 ])
1156 for _, rules in parsed_deps.get('specific_include_rules',
1157 {}).iteritems():
1158 add_rules.update([
1159 rule[1:] for rule in rules
1160 if rule.startswith('+') or rule.startswith('!')
1161 ])
1162 return add_rules
1163
1164
1165def _ParseDeps(contents):
1166 """Simple helper for parsing DEPS files."""
1167 # Stubs for handling special syntax in the root DEPS file.
1168 class VarImpl(object):
1169
1170 def __init__(self, local_scope):
1171 self._local_scope = local_scope
1172
1173 def Lookup(self, var_name):
1174 """Implements the Var syntax."""
1175 try:
1176 return self._local_scope['vars'][var_name]
1177 except KeyError:
1178 raise Exception('Var is not defined: %s' % var_name)
1179
1180 local_scope = {}
1181 global_scope = {
1182 'Var': VarImpl(local_scope).Lookup,
1183 }
1184 exec contents in global_scope, local_scope
1185 return local_scope
1186
1187
1188def _CalculateAddedDeps(os_path, old_contents, new_contents):
1189 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
1190 a set of DEPS entries that we should look up.
1191
1192 For a directory (rather than a specific filename) we fake a path to
1193 a specific filename by adding /DEPS. This is chosen as a file that
1194 will seldom or never be subject to per-file include_rules.
1195 """
1196 # We ignore deps entries on auto-generated directories.
1197 auto_generated_dirs = ['grit', 'jni']
1198
1199 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1200 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
1201
1202 added_deps = new_deps.difference(old_deps)
1203
1204 results = set()
1205 for added_dep in added_deps:
1206 if added_dep.split('/')[0] in auto_generated_dirs:
1207 continue
1208 # Assume that a rule that ends in .h is a rule for a specific file.
1209 if added_dep.endswith('.h'):
1210 results.add(added_dep)
1211 else:
1212 results.add(os_path.join(added_dep, 'DEPS'))
1213 return results
1214
1215
1216def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
1217 """When a dependency prefixed with + is added to a DEPS file, we
1218 want to make sure that the change is reviewed by an OWNER of the
1219 target file or directory, to avoid layering violations from being
1220 introduced. This check verifies that this happens.
1221 """
1222 virtual_depended_on_files = set()
1223
1224 file_filter = lambda f: not input_api.re.match(
1225 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1226 for f in input_api.AffectedFiles(include_deletes=False,
1227 file_filter=file_filter):
1228 filename = input_api.os_path.basename(f.LocalPath())
1229 if filename == 'DEPS':
1230 virtual_depended_on_files.update(_CalculateAddedDeps(
1231 input_api.os_path,
1232 '\n'.join(f.OldContents()),
1233 '\n'.join(f.NewContents())))
1234
1235 if not virtual_depended_on_files:
1236 return []
1237
1238 if input_api.is_committing:
1239 if input_api.tbr:
1240 return [output_api.PresubmitNotifyResult(
1241 '--tbr was specified, skipping OWNERS check for DEPS additions')]
1242 if input_api.dry_run:
1243 return [output_api.PresubmitNotifyResult(
1244 'This is a dry run, skipping OWNERS check for DEPS additions')]
1245 if not input_api.change.issue:
1246 return [output_api.PresubmitError(
1247 "DEPS approval by OWNERS check failed: this change has "
1248 "no change number, so we can't check it for approvals.")]
1249 output = output_api.PresubmitError
1250 else:
1251 output = output_api.PresubmitNotifyResult
1252
1253 owners_db = input_api.owners_db
1254 owner_email, reviewers = (
1255 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1256 input_api,
1257 owners_db.email_regexp,
1258 approval_needed=input_api.is_committing))
1259
1260 owner_email = owner_email or input_api.change.author_email
1261
1262 reviewers_plus_owner = set(reviewers)
1263 if owner_email:
1264 reviewers_plus_owner.add(owner_email)
1265 missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
1266 reviewers_plus_owner)
1267
1268 # We strip the /DEPS part that was added by
1269 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1270 # directory.
1271 def StripDeps(path):
1272 start_deps = path.rfind('/DEPS')
1273 if start_deps != -1:
1274 return path[:start_deps]
1275 else:
1276 return path
1277 unapproved_dependencies = ["'+%s'," % StripDeps(path)
1278 for path in missing_files]
1279
1280 if unapproved_dependencies:
1281 output_list = [
1282 output('You need LGTM from owners of depends-on paths in DEPS that were '
1283 'modified in this CL:\n %s' %
1284 '\n '.join(sorted(unapproved_dependencies)))]
1285 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
1286 output_list.append(output(
1287 'Suggested missing target path OWNERS:\n %s' %
1288 '\n '.join(suggested_owners or [])))
1289 return output_list
1290
1291 return []